1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2011 New Dream Network
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.
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 "mon/MonClient.h"
35 #ifdef HAVE_SYS_PRCTL_H
36 #include <sys/prctl.h>
39 #define dout_context g_ceph_context
40 #define dout_subsys ceph_subsys_
42 static void global_init_set_globals(CephContext
*cct
)
45 get_process_name(g_process_name
, sizeof(g_process_name
));
48 static void output_ceph_version()
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
;
57 static const char* c_str_or_null(const std::string
&str
)
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
)
67 const char *pathname_cstr
= c_str_or_null(pathname
);
73 int r
= ::chown(pathname_cstr
, owner
, group
);
77 cerr
<< "warning: unable to chown() " << pathname
<< " as "
78 << uid_str
<< ":" << gid_str
<< ": " << cpp_strerror(r
) << std::endl
;
85 const std::map
<std::string
,std::string
> *defaults
,
86 std::vector
< const char* >& args
,
87 uint32_t module_type
, code_environment_t code_env
,
90 std::string conf_file_list
;
91 std::string cluster
= "";
93 CephInitParameters iparams
= ceph_argparse_early_args(
95 &cluster
, &conf_file_list
);
97 CephContext
*cct
= common_preinit(iparams
, code_env
, flags
);
98 cct
->_conf
->cluster
= cluster
;
99 global_init_set_globals(cct
);
100 auto& conf
= cct
->_conf
;
102 if (flags
& (CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
|
103 CINIT_FLAG_NO_MON_CONFIG
)) {
104 conf
->no_mon_config
= true;
107 // alternate defaults
109 for (auto& i
: *defaults
) {
110 conf
.set_val_default(i
.first
, i
.second
);
114 int ret
= conf
.parse_config_files(c_str_or_null(conf_file_list
),
118 cerr
<< "global_init: error parsing config file." << std::endl
;
121 else if (ret
== -ENOENT
) {
122 if (!(flags
& CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
)) {
123 if (conf_file_list
.length()) {
125 cerr
<< "global_init: unable to open config file from search list "
126 << conf_file_list
<< std::endl
;
129 cerr
<< "did not load config file, using default settings." << std::endl
;
135 cerr
<< "global_init: error reading config file." << std::endl
;
139 // environment variables override (CEPH_ARGS, CEPH_KEYRING)
140 conf
.parse_env(cct
->get_module_type());
142 // command line (as passed by caller)
143 conf
.parse_argv(args
);
145 if (conf
->log_early
&&
146 !cct
->_log
->is_started()) {
150 if (!conf
->no_mon_config
) {
151 // make sure our mini-session gets legacy values
152 conf
.apply_changes(nullptr);
154 MonClient
mc_bootstrap(g_ceph_context
);
155 if (mc_bootstrap
.get_monmap_and_config() < 0) {
157 cerr
<< "failed to fetch mon config (--no-mon-config to skip)"
162 if (!cct
->_log
->is_started()) {
166 // do the --show-config[-val], if present in argv
167 conf
.do_argv_commands();
169 // Now we're ready to complain about config file parse errors
170 g_conf().complain_about_parse_errors(g_ceph_context
);
173 boost::intrusive_ptr
<CephContext
>
174 global_init(const std::map
<std::string
,std::string
> *defaults
,
175 std::vector
< const char* >& args
,
176 uint32_t module_type
, code_environment_t code_env
,
178 const char *data_dir_option
, bool run_pre_init
)
180 // Ensure we're not calling the global init functions multiple times.
181 static bool first_run
= true;
183 // We will run pre_init from here (default).
184 ceph_assert(!g_ceph_context
&& first_run
);
185 global_pre_init(defaults
, args
, module_type
, code_env
, flags
);
187 // Caller should have invoked pre_init manually.
188 ceph_assert(g_ceph_context
&& first_run
);
192 // Verify flags have not changed if global_pre_init() has been called
193 // manually. If they have, update them.
194 if (g_ceph_context
->get_init_flags() != flags
) {
195 g_ceph_context
->set_init_flags(flags
);
199 int siglist
[] = { SIGPIPE
, 0 };
200 block_signals(siglist
, NULL
);
202 if (g_conf()->fatal_signal_handlers
) {
203 install_standard_sighandlers();
205 register_assert_context(g_ceph_context
);
207 if (g_conf()->log_flush_on_exit
)
208 g_ceph_context
->_log
->set_flush_on_exit();
211 ostringstream priv_ss
;
213 // consider --setuser root a no-op, even if we're not root
215 if (g_conf()->setuser
.length()) {
216 cerr
<< "ignoring --setuser " << g_conf()->setuser
<< " since I am not root"
219 if (g_conf()->setgroup
.length()) {
220 cerr
<< "ignoring --setgroup " << g_conf()->setgroup
221 << " since I am not root" << std::endl
;
223 } else if (g_conf()->setgroup
.length() ||
224 g_conf()->setuser
.length()) {
225 uid_t uid
= 0; // zero means no change; we can only drop privs here.
227 std::string uid_string
;
228 std::string gid_string
;
229 std::string home_directory
;
230 if (g_conf()->setuser
.length()) {
233 struct passwd
*p
= 0;
235 uid
= atoi(g_conf()->setuser
.c_str());
237 getpwuid_r(uid
, &pa
, buf
, sizeof(buf
), &p
);
239 getpwnam_r(g_conf()->setuser
.c_str(), &pa
, buf
, sizeof(buf
), &p
);
241 cerr
<< "unable to look up user '" << g_conf()->setuser
<< "'"
248 uid_string
= g_conf()->setuser
;
251 if (p
&& p
->pw_dir
!= nullptr) {
252 home_directory
= std::string(p
->pw_dir
);
255 if (g_conf()->setgroup
.length() > 0) {
256 gid
= atoi(g_conf()->setgroup
.c_str());
261 getgrnam_r(g_conf()->setgroup
.c_str(), &gr
, buf
, sizeof(buf
), &g
);
263 cerr
<< "unable to look up group '" << g_conf()->setgroup
<< "'"
264 << ": " << cpp_strerror(errno
) << std::endl
;
268 gid_string
= g_conf()->setgroup
;
272 g_conf()->setuser_match_path
.length()) {
273 // induce early expansion of setuser_match_path config option
274 string match_path
= g_conf()->setuser_match_path
;
275 g_conf().early_expand_meta(match_path
, &cerr
);
277 int r
= ::stat(match_path
.c_str(), &st
);
279 cerr
<< "unable to stat setuser_match_path "
280 << g_conf()->setuser_match_path
281 << ": " << cpp_strerror(errno
) << std::endl
;
284 if ((uid
&& uid
!= st
.st_uid
) ||
285 (gid
&& gid
!= st
.st_gid
)) {
286 cerr
<< "WARNING: will not setuid/gid: " << match_path
287 << " owned by " << st
.st_uid
<< ":" << st
.st_gid
288 << " and not requested " << uid
<< ":" << gid
295 priv_ss
<< "setuser_match_path "
296 << match_path
<< " owned by "
297 << st
.st_uid
<< ":" << st
.st_gid
<< ". ";
300 g_ceph_context
->set_uid_gid(uid
, gid
);
301 g_ceph_context
->set_uid_gid_strings(uid_string
, gid_string
);
302 if ((flags
& CINIT_FLAG_DEFER_DROP_PRIVILEGES
) == 0) {
303 if (setgid(gid
) != 0) {
304 cerr
<< "unable to setgid " << gid
<< ": " << cpp_strerror(errno
)
308 if (setuid(uid
) != 0) {
309 cerr
<< "unable to setuid " << uid
<< ": " << cpp_strerror(errno
)
313 if (setenv("HOME", home_directory
.c_str(), 1) != 0) {
314 cerr
<< "warning: unable to set HOME to " << home_directory
<< ": "
315 << cpp_strerror(errno
) << std::endl
;
317 priv_ss
<< "set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
319 priv_ss
<< "deferred set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
323 #if defined(HAVE_SYS_PRCTL_H)
324 if (prctl(PR_SET_DUMPABLE
, 1) == -1) {
325 cerr
<< "warning: unable to set dumpable flag: " << cpp_strerror(errno
) << std::endl
;
329 // Expand metavariables. Invoke configuration observers. Open log file.
330 g_conf().apply_changes(nullptr);
332 if (g_conf()->run_dir
.length() &&
333 code_env
== CODE_ENVIRONMENT_DAEMON
&&
334 !(flags
& CINIT_FLAG_NO_DAEMON_ACTIONS
)) {
335 int r
= ::mkdir(g_conf()->run_dir
.c_str(), 0755);
336 if (r
< 0 && errno
!= EEXIST
) {
337 cerr
<< "warning: unable to create " << g_conf()->run_dir
<< ": " << cpp_strerror(errno
) << std::endl
;
341 // call all observers now. this has the side-effect of configuring
342 // and opening the log file immediately.
343 g_conf().call_all_observers();
345 if (priv_ss
.str().length()) {
346 dout(0) << priv_ss
.str() << dendl
;
349 if ((flags
& CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
350 (g_ceph_context
->get_set_uid() || g_ceph_context
->get_set_gid())) {
351 // Fix ownership on log files and run directories if needed.
352 // Admin socket files are chown()'d during the common init path _after_
353 // the service thread has been started. This is sadly a bit of a hack :(
354 chown_path(g_conf()->run_dir
,
355 g_ceph_context
->get_set_uid(),
356 g_ceph_context
->get_set_gid(),
357 g_ceph_context
->get_set_uid_string(),
358 g_ceph_context
->get_set_gid_string());
359 g_ceph_context
->_log
->chown_log_file(
360 g_ceph_context
->get_set_uid(),
361 g_ceph_context
->get_set_gid());
364 // Now we're ready to complain about config file parse errors
365 g_conf().complain_about_parse_errors(g_ceph_context
);
367 // test leak checking
368 if (g_conf()->debug_deliberately_leak_memory
) {
369 derr
<< "deliberately leaking some memory" << dendl
;
370 char *s
= new char[1234567];
372 // cppcheck-suppress memleak
375 if (code_env
== CODE_ENVIRONMENT_DAEMON
&& !(flags
& CINIT_FLAG_NO_DAEMON_ACTIONS
))
376 output_ceph_version();
378 if (g_ceph_context
->crush_location
.init_on_startup()) {
379 cerr
<< " failed to init_on_startup : " << cpp_strerror(errno
) << std::endl
;
383 return boost::intrusive_ptr
<CephContext
>{g_ceph_context
, false};
386 void intrusive_ptr_add_ref(CephContext
* cct
)
391 void intrusive_ptr_release(CephContext
* cct
)
396 void global_print_banner(void)
398 output_ceph_version();
401 int global_init_prefork(CephContext
*cct
)
403 if (g_code_env
!= CODE_ENVIRONMENT_DAEMON
)
406 const auto& conf
= cct
->_conf
;
407 if (!conf
->daemonize
) {
409 if (pidfile_write(conf
) < 0)
412 if ((cct
->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
413 (cct
->get_set_uid() || cct
->get_set_gid())) {
414 chown_path(conf
->pid_file
, cct
->get_set_uid(), cct
->get_set_gid(),
415 cct
->get_set_uid_string(), cct
->get_set_gid_string());
421 cct
->notify_pre_fork();
428 void global_init_daemonize(CephContext
*cct
)
430 if (global_init_prefork(cct
) < 0)
434 int ret
= daemon(1, 1);
437 derr
<< "global_init_daemonize: BUG: daemon error: "
438 << cpp_strerror(ret
) << dendl
;
442 global_init_postfork_start(cct
);
443 global_init_postfork_finish(cct
);
445 # warning daemon not supported on aix
449 int reopen_as_null(CephContext
*cct
, int fd
)
451 int newfd
= open("/dev/null", O_RDONLY
|O_CLOEXEC
);
454 lderr(cct
) << __func__
<< " failed to open /dev/null: " << cpp_strerror(err
)
458 // atomically dup newfd to target fd. target fd is implicitly closed if
459 // open and atomically replaced; see man dup2
460 int r
= dup2(newfd
, fd
);
463 lderr(cct
) << __func__
<< " failed to dup2 " << fd
<< ": "
464 << cpp_strerror(err
) << dendl
;
467 // close newfd (we cloned it to target fd)
468 VOID_TEMP_FAILURE_RETRY(close(newfd
));
469 // N.B. FD_CLOEXEC is cleared on fd (see dup2(2))
473 void global_init_postfork_start(CephContext
*cct
)
475 // restart log thread
477 cct
->notify_post_fork();
479 /* This is the old trick where we make file descriptors 0, 1, and possibly 2
480 * point to /dev/null.
482 * We have to do this because otherwise some arbitrary call to open() later
483 * in the program might get back one of these file descriptors. It's hard to
484 * guarantee that nobody ever writes to stdout, even though they're not
487 reopen_as_null(cct
, STDIN_FILENO
);
489 const auto& conf
= cct
->_conf
;
490 if (pidfile_write(conf
) < 0)
493 if ((cct
->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
494 (cct
->get_set_uid() || cct
->get_set_gid())) {
495 chown_path(conf
->pid_file
, cct
->get_set_uid(), cct
->get_set_gid(),
496 cct
->get_set_uid_string(), cct
->get_set_gid_string());
500 void global_init_postfork_finish(CephContext
*cct
)
502 /* We only close stdout+stderr once the caller decides the daemonization
503 * process is finished. This way we can allow error or other messages to be
504 * propagated in a manner that the user is able to see.
506 if (!(cct
->get_init_flags() & CINIT_FLAG_NO_CLOSE_STDERR
)) {
507 int ret
= global_init_shutdown_stderr(cct
);
509 derr
<< "global_init_daemonize: global_init_shutdown_stderr failed with "
510 << "error code " << ret
<< dendl
;
515 reopen_as_null(cct
, STDOUT_FILENO
);
517 ldout(cct
, 1) << "finished global_init_daemonize" << dendl
;
521 void global_init_chdir(const CephContext
*cct
)
523 const auto& conf
= cct
->_conf
;
524 if (conf
->chdir
.empty())
526 if (::chdir(conf
->chdir
.c_str())) {
528 derr
<< "global_init_chdir: failed to chdir to directory: '"
529 << conf
->chdir
<< "': " << cpp_strerror(err
) << dendl
;
533 /* Map stderr to /dev/null. This isn't really re-entrant; we rely on the old unix
534 * behavior that the file descriptor that gets assigned is the lowest
537 int global_init_shutdown_stderr(CephContext
*cct
)
539 reopen_as_null(cct
, STDERR_FILENO
);
540 int l
= cct
->_conf
->err_to_stderr
? -1 : -2;
541 cct
->_log
->set_stderr_level(l
, l
);
545 int global_init_preload_erasure_code(const CephContext
*cct
)
547 const auto& conf
= cct
->_conf
;
548 string plugins
= conf
->osd_erasure_code_plugins
;
550 // validate that this is a not a legacy plugin
551 list
<string
> plugins_list
;
552 get_str_list(plugins
, plugins_list
);
553 for (list
<string
>::iterator i
= plugins_list
.begin();
554 i
!= plugins_list
.end();
556 string plugin_name
= *i
;
557 string replacement
= "";
559 if (plugin_name
== "jerasure_generic" ||
560 plugin_name
== "jerasure_sse3" ||
561 plugin_name
== "jerasure_sse4" ||
562 plugin_name
== "jerasure_neon") {
563 replacement
= "jerasure";
565 else if (plugin_name
== "shec_generic" ||
566 plugin_name
== "shec_sse3" ||
567 plugin_name
== "shec_sse4" ||
568 plugin_name
== "shec_neon") {
569 replacement
= "shec";
572 if (replacement
!= "") {
573 dout(0) << "WARNING: osd_erasure_code_plugins contains plugin "
574 << plugin_name
<< " that is now deprecated. Please modify the value "
575 << "for osd_erasure_code_plugins to use " << replacement
<< " instead." << dendl
;
580 int r
= ErasureCodePluginRegistry::instance().preload(
582 conf
.get_val
<std::string
>("erasure_code_dir"),
585 derr
<< ss
.str() << dendl
;
587 dout(0) << ss
.str() << dendl
;