]> git.proxmox.com Git - ceph.git/blob - ceph/src/global/global_init.cc
update sources to 12.2.7
[ceph.git] / ceph / src / global / global_init.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2011 New Dream Network
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include "common/ceph_argparse.h"
16 #include "common/code_environment.h"
17 #include "common/config.h"
18 #include "common/debug.h"
19 #include "common/errno.h"
20 #include "common/signal.h"
21 #include "common/version.h"
22 #include "erasure-code/ErasureCodePlugin.h"
23 #include "global/global_context.h"
24 #include "global/global_init.h"
25 #include "global/pidfile.h"
26 #include "global/signal_handler.h"
27 #include "include/compat.h"
28 #include "include/str_list.h"
29 #include "common/admin_socket.h"
30
31 #include <pwd.h>
32 #include <grp.h>
33 #include <errno.h>
34
35 #ifdef HAVE_SYS_PRCTL_H
36 #include <sys/prctl.h>
37 #endif
38
39 #define dout_context g_ceph_context
40 #define dout_subsys ceph_subsys_
41
42 static void global_init_set_globals(CephContext *cct)
43 {
44 g_ceph_context = cct;
45 g_conf = cct->_conf;
46 }
47
48 static void output_ceph_version()
49 {
50 char buf[1024];
51 snprintf(buf, sizeof(buf), "%s, process %s, pid %d",
52 pretty_version_to_str().c_str(),
53 get_process_name_cpp().c_str(), getpid());
54 generic_dout(0) << buf << dendl;
55 }
56
57 static const char* c_str_or_null(const std::string &str)
58 {
59 if (str.empty())
60 return NULL;
61 return str.c_str();
62 }
63
64 static int chown_path(const std::string &pathname, const uid_t owner, const gid_t group,
65 const std::string &uid_str, const std::string &gid_str)
66 {
67 const char *pathname_cstr = c_str_or_null(pathname);
68
69 if (!pathname_cstr) {
70 return 0;
71 }
72
73 int r = ::chown(pathname_cstr, owner, group);
74
75 if (r < 0) {
76 r = -errno;
77 cerr << "warning: unable to chown() " << pathname << " as "
78 << uid_str << ":" << gid_str << ": " << cpp_strerror(r) << std::endl;
79 }
80
81 return r;
82 }
83
84 void global_pre_init(std::vector < const char * > *alt_def_args,
85 std::vector < const char* >& args,
86 uint32_t module_type, code_environment_t code_env,
87 int flags)
88 {
89 std::string conf_file_list;
90 std::string cluster = "";
91 CephInitParameters iparams = ceph_argparse_early_args(args, module_type,
92 &cluster, &conf_file_list);
93 CephContext *cct = common_preinit(iparams, code_env, flags);
94 cct->_conf->cluster = cluster;
95 global_init_set_globals(cct);
96 md_config_t *conf = cct->_conf;
97
98 if (alt_def_args)
99 conf->parse_argv(*alt_def_args); // alternative default args
100
101 int ret = conf->parse_config_files(c_str_or_null(conf_file_list),
102 &cerr, flags);
103 if (ret == -EDOM) {
104 dout_emergency("global_init: error parsing config file.\n");
105 _exit(1);
106 }
107 else if (ret == -ENOENT) {
108 if (!(flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)) {
109 if (conf_file_list.length()) {
110 ostringstream oss;
111 oss << "global_init: unable to open config file from search list "
112 << conf_file_list << "\n";
113 dout_emergency(oss.str());
114 _exit(1);
115 } else {
116 derr << "did not load config file, using default settings." << dendl;
117 }
118 }
119 }
120 else if (ret) {
121 dout_emergency("global_init: error reading config file.\n");
122 _exit(1);
123 }
124
125 conf->parse_env(); // environment variables override
126
127 conf->parse_argv(args); // argv override
128
129 // Now we're ready to complain about config file parse errors
130 g_conf->complain_about_parse_errors(g_ceph_context);
131 }
132
133 boost::intrusive_ptr<CephContext>
134 global_init(std::vector < const char * > *alt_def_args,
135 std::vector < const char* >& args,
136 uint32_t module_type, code_environment_t code_env,
137 int flags,
138 const char *data_dir_option, bool run_pre_init)
139 {
140 // Ensure we're not calling the global init functions multiple times.
141 static bool first_run = true;
142 if (run_pre_init) {
143 // We will run pre_init from here (default).
144 assert(!g_ceph_context && first_run);
145 global_pre_init(alt_def_args, args, module_type, code_env, flags);
146 } else {
147 // Caller should have invoked pre_init manually.
148 assert(g_ceph_context && first_run);
149 }
150 first_run = false;
151
152 // Verify flags have not changed if global_pre_init() has been called
153 // manually. If they have, update them.
154 if (g_ceph_context->get_init_flags() != flags) {
155 g_ceph_context->set_init_flags(flags);
156 }
157
158 // signal stuff
159 int siglist[] = { SIGPIPE, 0 };
160 block_signals(siglist, NULL);
161
162 if (g_conf->fatal_signal_handlers)
163 install_standard_sighandlers();
164
165 if (g_conf->log_flush_on_exit)
166 g_ceph_context->_log->set_flush_on_exit();
167
168 // drop privileges?
169 ostringstream priv_ss;
170
171 // consider --setuser root a no-op, even if we're not root
172 if (getuid() != 0) {
173 if (g_conf->setuser.length()) {
174 cerr << "ignoring --setuser " << g_conf->setuser << " since I am not root"
175 << std::endl;
176 }
177 if (g_conf->setgroup.length()) {
178 cerr << "ignoring --setgroup " << g_conf->setgroup
179 << " since I am not root" << std::endl;
180 }
181 } else if (g_conf->setgroup.length() ||
182 g_conf->setuser.length()) {
183 uid_t uid = 0; // zero means no change; we can only drop privs here.
184 gid_t gid = 0;
185 std::string uid_string;
186 std::string gid_string;
187 if (g_conf->setuser.length()) {
188 uid = atoi(g_conf->setuser.c_str());
189 if (!uid) {
190 char buf[4096];
191 struct passwd pa;
192 struct passwd *p = 0;
193 getpwnam_r(g_conf->setuser.c_str(), &pa, buf, sizeof(buf), &p);
194 if (!p) {
195 cerr << "unable to look up user '" << g_conf->setuser << "'"
196 << std::endl;
197 exit(1);
198 }
199 uid = p->pw_uid;
200 gid = p->pw_gid;
201 uid_string = g_conf->setuser;
202 }
203 }
204 if (g_conf->setgroup.length() > 0) {
205 gid = atoi(g_conf->setgroup.c_str());
206 if (!gid) {
207 char buf[4096];
208 struct group gr;
209 struct group *g = 0;
210 getgrnam_r(g_conf->setgroup.c_str(), &gr, buf, sizeof(buf), &g);
211 if (!g) {
212 cerr << "unable to look up group '" << g_conf->setgroup << "'"
213 << ": " << cpp_strerror(errno) << std::endl;
214 exit(1);
215 }
216 gid = g->gr_gid;
217 gid_string = g_conf->setgroup;
218 }
219 }
220 if ((uid || gid) &&
221 g_conf->setuser_match_path.length()) {
222 // induce early expansion of setuser_match_path config option
223 string match_path = g_conf->setuser_match_path;
224 g_conf->early_expand_meta(match_path, &cerr);
225 struct stat st;
226 int r = ::stat(match_path.c_str(), &st);
227 if (r < 0) {
228 cerr << "unable to stat setuser_match_path "
229 << g_conf->setuser_match_path
230 << ": " << cpp_strerror(errno) << std::endl;
231 exit(1);
232 }
233 if ((uid && uid != st.st_uid) ||
234 (gid && gid != st.st_gid)) {
235 cerr << "WARNING: will not setuid/gid: " << match_path
236 << " owned by " << st.st_uid << ":" << st.st_gid
237 << " and not requested " << uid << ":" << gid
238 << std::endl;
239 uid = 0;
240 gid = 0;
241 uid_string.erase();
242 gid_string.erase();
243 } else {
244 priv_ss << "setuser_match_path "
245 << match_path << " owned by "
246 << st.st_uid << ":" << st.st_gid << ". ";
247 }
248 }
249 g_ceph_context->set_uid_gid(uid, gid);
250 g_ceph_context->set_uid_gid_strings(uid_string, gid_string);
251 if ((flags & CINIT_FLAG_DEFER_DROP_PRIVILEGES) == 0) {
252 if (setgid(gid) != 0) {
253 cerr << "unable to setgid " << gid << ": " << cpp_strerror(errno)
254 << std::endl;
255 exit(1);
256 }
257 if (setuid(uid) != 0) {
258 cerr << "unable to setuid " << uid << ": " << cpp_strerror(errno)
259 << std::endl;
260 exit(1);
261 }
262 priv_ss << "set uid:gid to " << uid << ":" << gid << " (" << uid_string << ":" << gid_string << ")";
263 } else {
264 priv_ss << "deferred set uid:gid to " << uid << ":" << gid << " (" << uid_string << ":" << gid_string << ")";
265 }
266 }
267
268 #if defined(HAVE_SYS_PRCTL_H)
269 if (prctl(PR_SET_DUMPABLE, 1) == -1) {
270 cerr << "warning: unable to set dumpable flag: " << cpp_strerror(errno) << std::endl;
271 }
272 #endif
273
274 // Expand metavariables. Invoke configuration observers. Open log file.
275 g_conf->apply_changes(NULL);
276
277 if (g_conf->run_dir.length() &&
278 code_env == CODE_ENVIRONMENT_DAEMON &&
279 !(flags & CINIT_FLAG_NO_DAEMON_ACTIONS)) {
280 int r = ::mkdir(g_conf->run_dir.c_str(), 0755);
281 if (r < 0 && errno != EEXIST) {
282 cerr << "warning: unable to create " << g_conf->run_dir << ": " << cpp_strerror(errno) << std::endl;
283 }
284 }
285
286 register_assert_context(g_ceph_context);
287
288 // call all observers now. this has the side-effect of configuring
289 // and opening the log file immediately.
290 g_conf->call_all_observers();
291
292 if (priv_ss.str().length()) {
293 dout(0) << priv_ss.str() << dendl;
294 }
295
296 if ((flags & CINIT_FLAG_DEFER_DROP_PRIVILEGES) &&
297 (g_ceph_context->get_set_uid() || g_ceph_context->get_set_gid())) {
298 // Fix ownership on log files and run directories if needed.
299 // Admin socket files are chown()'d during the common init path _after_
300 // the service thread has been started. This is sadly a bit of a hack :(
301 chown_path(g_conf->run_dir,
302 g_ceph_context->get_set_uid(),
303 g_ceph_context->get_set_gid(),
304 g_ceph_context->get_set_uid_string(),
305 g_ceph_context->get_set_gid_string());
306 g_ceph_context->_log->chown_log_file(
307 g_ceph_context->get_set_uid(),
308 g_ceph_context->get_set_gid());
309 }
310
311 // Now we're ready to complain about config file parse errors
312 g_conf->complain_about_parse_errors(g_ceph_context);
313
314 // test leak checking
315 if (g_conf->debug_deliberately_leak_memory) {
316 derr << "deliberately leaking some memory" << dendl;
317 char *s = new char[1234567];
318 (void)s;
319 // cppcheck-suppress memleak
320 }
321
322 if (code_env == CODE_ENVIRONMENT_DAEMON && !(flags & CINIT_FLAG_NO_DAEMON_ACTIONS))
323 output_ceph_version();
324
325 if (g_ceph_context->crush_location.init_on_startup()) {
326 cerr << " failed to init_on_startup : " << cpp_strerror(errno) << std::endl;
327 exit(1);
328 }
329
330 return boost::intrusive_ptr<CephContext>{g_ceph_context, false};
331 }
332
333 void intrusive_ptr_add_ref(CephContext* cct)
334 {
335 cct->get();
336 }
337
338 void intrusive_ptr_release(CephContext* cct)
339 {
340 cct->put();
341 }
342
343 void global_print_banner(void)
344 {
345 output_ceph_version();
346 }
347
348 int global_init_prefork(CephContext *cct)
349 {
350 if (g_code_env != CODE_ENVIRONMENT_DAEMON)
351 return -1;
352
353 const md_config_t *conf = cct->_conf;
354 if (!conf->daemonize) {
355
356 if (pidfile_write(conf) < 0)
357 exit(1);
358
359 if ((cct->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES) &&
360 (cct->get_set_uid() || cct->get_set_gid())) {
361 chown_path(conf->pid_file, cct->get_set_uid(), cct->get_set_gid(),
362 cct->get_set_uid_string(), cct->get_set_gid_string());
363 }
364
365 return -1;
366 }
367
368 cct->notify_pre_fork();
369 // stop log thread
370 cct->_log->flush();
371 cct->_log->stop();
372 return 0;
373 }
374
375 void global_init_daemonize(CephContext *cct)
376 {
377 if (global_init_prefork(cct) < 0)
378 return;
379
380 #if !defined(_AIX)
381 int ret = daemon(1, 1);
382 if (ret) {
383 ret = errno;
384 derr << "global_init_daemonize: BUG: daemon error: "
385 << cpp_strerror(ret) << dendl;
386 exit(1);
387 }
388
389 global_init_postfork_start(cct);
390 global_init_postfork_finish(cct);
391 #else
392 # warning daemon not supported on aix
393 #endif
394 }
395
396 void global_init_postfork_start(CephContext *cct)
397 {
398 // restart log thread
399 cct->_log->start();
400 cct->notify_post_fork();
401
402 /* This is the old trick where we make file descriptors 0, 1, and possibly 2
403 * point to /dev/null.
404 *
405 * We have to do this because otherwise some arbitrary call to open() later
406 * in the program might get back one of these file descriptors. It's hard to
407 * guarantee that nobody ever writes to stdout, even though they're not
408 * supposed to.
409 */
410 VOID_TEMP_FAILURE_RETRY(close(STDIN_FILENO));
411 if (open("/dev/null", O_RDONLY) < 0) {
412 int err = errno;
413 derr << "global_init_daemonize: open(/dev/null) failed: error "
414 << err << dendl;
415 exit(1);
416 }
417
418 const md_config_t *conf = cct->_conf;
419 if (pidfile_write(conf) < 0)
420 exit(1);
421
422 if ((cct->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES) &&
423 (cct->get_set_uid() || cct->get_set_gid())) {
424 chown_path(conf->pid_file, cct->get_set_uid(), cct->get_set_gid(),
425 cct->get_set_uid_string(), cct->get_set_gid_string());
426 }
427 }
428
429 void global_init_postfork_finish(CephContext *cct)
430 {
431 /* We only close stdout+stderr once the caller decides the daemonization
432 * process is finished. This way we can allow error or other messages to be
433 * propagated in a manner that the user is able to see.
434 */
435 if (!(cct->get_init_flags() & CINIT_FLAG_NO_CLOSE_STDERR)) {
436 int ret = global_init_shutdown_stderr(cct);
437 if (ret) {
438 derr << "global_init_daemonize: global_init_shutdown_stderr failed with "
439 << "error code " << ret << dendl;
440 exit(1);
441 }
442 }
443
444 VOID_TEMP_FAILURE_RETRY(close(STDOUT_FILENO));
445 if (open("/dev/null", O_RDONLY) < 0) {
446 int err = errno;
447 derr << "global_init_daemonize: open(/dev/null) failed: error "
448 << err << dendl;
449 exit(1);
450 }
451
452 ldout(cct, 1) << "finished global_init_daemonize" << dendl;
453 }
454
455
456 void global_init_chdir(const CephContext *cct)
457 {
458 const md_config_t *conf = cct->_conf;
459 if (conf->chdir.empty())
460 return;
461 if (::chdir(conf->chdir.c_str())) {
462 int err = errno;
463 derr << "global_init_chdir: failed to chdir to directory: '"
464 << conf->chdir << "': " << cpp_strerror(err) << dendl;
465 }
466 }
467
468 /* Map stderr to /dev/null. This isn't really re-entrant; we rely on the old unix
469 * behavior that the file descriptor that gets assigned is the lowest
470 * available one.
471 */
472 int global_init_shutdown_stderr(CephContext *cct)
473 {
474 VOID_TEMP_FAILURE_RETRY(close(STDERR_FILENO));
475 if (open("/dev/null", O_RDONLY) < 0) {
476 int err = errno;
477 derr << "global_init_shutdown_stderr: open(/dev/null) failed: error "
478 << err << dendl;
479 return 1;
480 }
481 cct->_log->set_stderr_level(-1, -1);
482 return 0;
483 }
484
485 int global_init_preload_erasure_code(const CephContext *cct)
486 {
487 const md_config_t *conf = cct->_conf;
488 string plugins = conf->osd_erasure_code_plugins;
489
490 // validate that this is a not a legacy plugin
491 list<string> plugins_list;
492 get_str_list(plugins, plugins_list);
493 for (list<string>::iterator i = plugins_list.begin();
494 i != plugins_list.end();
495 ++i) {
496 string plugin_name = *i;
497 string replacement = "";
498
499 if (plugin_name == "jerasure_generic" ||
500 plugin_name == "jerasure_sse3" ||
501 plugin_name == "jerasure_sse4" ||
502 plugin_name == "jerasure_neon") {
503 replacement = "jerasure";
504 }
505 else if (plugin_name == "shec_generic" ||
506 plugin_name == "shec_sse3" ||
507 plugin_name == "shec_sse4" ||
508 plugin_name == "shec_neon") {
509 replacement = "shec";
510 }
511
512 if (replacement != "") {
513 dout(0) << "WARNING: osd_erasure_code_plugins contains plugin "
514 << plugin_name << " that is now deprecated. Please modify the value "
515 << "for osd_erasure_code_plugins to use " << replacement << " instead." << dendl;
516 }
517 }
518
519 stringstream ss;
520 int r = ErasureCodePluginRegistry::instance().preload(
521 plugins,
522 conf->get_val<std::string>("erasure_code_dir"),
523 &ss);
524 if (r)
525 derr << ss.str() << dendl;
526 else
527 dout(0) << ss.str() << dendl;
528 return r;
529 }