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.
16 #include "common/async/context_pool.h"
17 #include "common/ceph_argparse.h"
18 #include "common/code_environment.h"
19 #include "common/config.h"
20 #include "common/debug.h"
21 #include "common/errno.h"
22 #include "common/signal.h"
23 #include "common/version.h"
24 #include "erasure-code/ErasureCodePlugin.h"
25 #include "extblkdev/ExtBlkDevPlugin.h"
26 #include "global/global_context.h"
27 #include "global/global_init.h"
28 #include "global/pidfile.h"
29 #include "global/signal_handler.h"
30 #include "include/compat.h"
31 #include "include/str_list.h"
32 #include "mon/MonClient.h"
40 #ifdef HAVE_SYS_PRCTL_H
41 #include <sys/prctl.h>
44 #define dout_context g_ceph_context
45 #define dout_subsys ceph_subsys_
47 namespace fs
= std::filesystem
;
52 static void global_init_set_globals(CephContext
*cct
)
55 get_process_name(g_process_name
, sizeof(g_process_name
));
58 static void output_ceph_version()
61 snprintf(buf
, sizeof(buf
), "%s, process %s, pid %d",
62 pretty_version_to_str().c_str(),
63 get_process_name_cpp().c_str(), getpid());
64 generic_dout(0) << buf
<< dendl
;
67 static const char* c_str_or_null(const std::string
&str
)
74 static int chown_path(const std::string
&pathname
, const uid_t owner
, const gid_t group
,
75 const std::string
&uid_str
, const std::string
&gid_str
)
81 const char *pathname_cstr
= c_str_or_null(pathname
);
87 int r
= ::chown(pathname_cstr
, owner
, group
);
91 cerr
<< "warning: unable to chown() " << pathname
<< " as "
92 << uid_str
<< ":" << gid_str
<< ": " << cpp_strerror(r
) << std::endl
;
100 const std::map
<std::string
,std::string
> *defaults
,
101 std::vector
< const char* >& args
,
102 uint32_t module_type
, code_environment_t code_env
,
105 std::string conf_file_list
;
106 std::string cluster
= "";
108 // ensure environment arguments are included in early processing
111 CephInitParameters iparams
= ceph_argparse_early_args(
113 &cluster
, &conf_file_list
);
115 CephContext
*cct
= common_preinit(iparams
, code_env
, flags
);
116 cct
->_conf
->cluster
= cluster
;
117 global_init_set_globals(cct
);
118 auto& conf
= cct
->_conf
;
120 if (flags
& (CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
|
121 CINIT_FLAG_NO_MON_CONFIG
)) {
122 conf
->no_mon_config
= true;
125 // alternate defaults
127 for (auto& i
: *defaults
) {
128 conf
.set_val_default(i
.first
, i
.second
);
132 if (conf
.get_val
<bool>("no_config_file")) {
133 flags
|= CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
;
136 int ret
= conf
.parse_config_files(c_str_or_null(conf_file_list
),
140 cerr
<< "global_init: error parsing config file." << std::endl
;
143 else if (ret
== -ENOENT
) {
144 if (!(flags
& CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
)) {
145 if (conf_file_list
.length()) {
147 cerr
<< "global_init: unable to open config file from search list "
148 << conf_file_list
<< std::endl
;
151 cerr
<< "did not load config file, using default settings."
158 cerr
<< "global_init: error reading config file. "
159 << conf
.get_parse_error() << std::endl
;
163 // environment variables override (CEPH_ARGS, CEPH_KEYRING)
164 conf
.parse_env(cct
->get_module_type());
166 // command line (as passed by caller)
167 conf
.parse_argv(args
);
169 if (!cct
->_log
->is_started()) {
173 // do the --show-config[-val], if present in argv
174 conf
.do_argv_commands();
176 // Now we're ready to complain about config file parse errors
177 g_conf().complain_about_parse_error(g_ceph_context
);
180 boost::intrusive_ptr
<CephContext
>
181 global_init(const std::map
<std::string
,std::string
> *defaults
,
182 std::vector
< const char* >& args
,
183 uint32_t module_type
, code_environment_t code_env
,
184 int flags
, bool run_pre_init
)
186 // Ensure we're not calling the global init functions multiple times.
187 static bool first_run
= true;
189 // We will run pre_init from here (default).
190 ceph_assert(!g_ceph_context
&& first_run
);
191 global_pre_init(defaults
, args
, module_type
, code_env
, flags
);
193 // Caller should have invoked pre_init manually.
194 ceph_assert(g_ceph_context
&& first_run
);
198 // Verify flags have not changed if global_pre_init() has been called
199 // manually. If they have, update them.
200 if (g_ceph_context
->get_init_flags() != flags
) {
201 g_ceph_context
->set_init_flags(flags
);
202 if (flags
& (CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
|
203 CINIT_FLAG_NO_MON_CONFIG
)) {
204 g_conf()->no_mon_config
= true;
210 int siglist
[] = { SIGPIPE
, 0 };
211 block_signals(siglist
, NULL
);
214 if (g_conf()->fatal_signal_handlers
) {
215 install_standard_sighandlers();
217 ceph::register_assert_context(g_ceph_context
);
219 if (g_conf()->log_flush_on_exit
)
220 g_ceph_context
->_log
->set_flush_on_exit();
223 std::ostringstream priv_ss
;
226 // consider --setuser root a no-op, even if we're not root
228 if (g_conf()->setuser
.length()) {
229 cerr
<< "ignoring --setuser " << g_conf()->setuser
<< " since I am not root"
232 if (g_conf()->setgroup
.length()) {
233 cerr
<< "ignoring --setgroup " << g_conf()->setgroup
234 << " since I am not root" << std::endl
;
236 } else if (g_conf()->setgroup
.length() ||
237 g_conf()->setuser
.length()) {
238 uid_t uid
= 0; // zero means no change; we can only drop privs here.
240 std::string uid_string
;
241 std::string gid_string
;
242 std::string home_directory
;
243 if (g_conf()->setuser
.length()) {
246 struct passwd
*p
= 0;
248 uid
= atoi(g_conf()->setuser
.c_str());
250 getpwuid_r(uid
, &pa
, buf
, sizeof(buf
), &p
);
252 getpwnam_r(g_conf()->setuser
.c_str(), &pa
, buf
, sizeof(buf
), &p
);
254 cerr
<< "unable to look up user '" << g_conf()->setuser
<< "'"
261 uid_string
= g_conf()->setuser
;
264 if (p
&& p
->pw_dir
!= nullptr) {
265 home_directory
= std::string(p
->pw_dir
);
268 if (g_conf()->setgroup
.length() > 0) {
269 gid
= atoi(g_conf()->setgroup
.c_str());
274 getgrnam_r(g_conf()->setgroup
.c_str(), &gr
, buf
, sizeof(buf
), &g
);
276 cerr
<< "unable to look up group '" << g_conf()->setgroup
<< "'"
277 << ": " << cpp_strerror(errno
) << std::endl
;
281 gid_string
= g_conf()->setgroup
;
285 g_conf()->setuser_match_path
.length()) {
286 // induce early expansion of setuser_match_path config option
287 string match_path
= g_conf()->setuser_match_path
;
288 g_conf().early_expand_meta(match_path
, &cerr
);
290 int r
= ::stat(match_path
.c_str(), &st
);
292 cerr
<< "unable to stat setuser_match_path "
293 << g_conf()->setuser_match_path
294 << ": " << cpp_strerror(errno
) << std::endl
;
297 if ((uid
&& uid
!= st
.st_uid
) ||
298 (gid
&& gid
!= st
.st_gid
)) {
299 cerr
<< "WARNING: will not setuid/gid: " << match_path
300 << " owned by " << st
.st_uid
<< ":" << st
.st_gid
301 << " and not requested " << uid
<< ":" << gid
308 priv_ss
<< "setuser_match_path "
309 << match_path
<< " owned by "
310 << st
.st_uid
<< ":" << st
.st_gid
<< ". ";
313 g_ceph_context
->set_uid_gid(uid
, gid
);
314 g_ceph_context
->set_uid_gid_strings(uid_string
, gid_string
);
315 if ((flags
& CINIT_FLAG_DEFER_DROP_PRIVILEGES
) == 0) {
316 if (setgid(gid
) != 0) {
317 cerr
<< "unable to setgid " << gid
<< ": " << cpp_strerror(errno
)
321 #if defined(HAVE_SYS_PRCTL_H)
322 if (g_conf().get_val
<bool>("set_keepcaps")) {
323 if (prctl(PR_SET_KEEPCAPS
, 1) == -1) {
324 cerr
<< "warning: unable to set keepcaps flag: " << cpp_strerror(errno
) << std::endl
;
328 if (setuid(uid
) != 0) {
329 cerr
<< "unable to setuid " << uid
<< ": " << cpp_strerror(errno
)
333 if (setenv("HOME", home_directory
.c_str(), 1) != 0) {
334 cerr
<< "warning: unable to set HOME to " << home_directory
<< ": "
335 << cpp_strerror(errno
) << std::endl
;
337 priv_ss
<< "set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
339 priv_ss
<< "deferred set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
344 #if defined(HAVE_SYS_PRCTL_H)
345 if (prctl(PR_SET_DUMPABLE
, 1) == -1) {
346 cerr
<< "warning: unable to set dumpable flag: " << cpp_strerror(errno
) << std::endl
;
348 # if defined(PR_SET_THP_DISABLE)
349 if (!g_conf().get_val
<bool>("thp") && prctl(PR_SET_THP_DISABLE
, 1, 0, 0, 0) == -1) {
350 cerr
<< "warning: unable to disable THP: " << cpp_strerror(errno
) << std::endl
;
356 // Utterly important to run first network connection after setuid().
357 // In case of rdma transport uverbs kernel module starts returning
358 // -EACCESS on each operation if credentials has been changed, see
359 // callers of ib_safe_file_access() for details.
361 // fork() syscall also matters, so daemonization won't work in case
364 if (!g_conf()->no_mon_config
) {
365 // make sure our mini-session gets legacy values
366 g_conf().apply_changes(nullptr);
368 ceph::async::io_context_pool
cp(1);
369 MonClient
mc_bootstrap(g_ceph_context
, cp
);
370 if (mc_bootstrap
.get_monmap_and_config() < 0) {
372 g_ceph_context
->_log
->flush();
373 cerr
<< "failed to fetch mon config (--no-mon-config to skip)"
380 // Expand metavariables. Invoke configuration observers. Open log file.
381 g_conf().apply_changes(nullptr);
383 if (g_conf()->run_dir
.length() &&
384 code_env
== CODE_ENVIRONMENT_DAEMON
&&
385 !(flags
& CINIT_FLAG_NO_DAEMON_ACTIONS
)) {
387 if (!fs::exists(g_conf()->run_dir
.c_str())) {
389 if (!fs::create_directory(g_conf()->run_dir
, ec
)) {
390 cerr
<< "warning: unable to create " << g_conf()->run_dir
391 << ec
.message() << std::endl
;
394 g_conf()->run_dir
.c_str(),
395 fs::perms::owner_all
|
396 fs::perms::group_read
| fs::perms::group_exec
|
397 fs::perms::others_read
| fs::perms::others_exec
);
401 // call all observers now. this has the side-effect of configuring
402 // and opening the log file immediately.
403 g_conf().call_all_observers();
405 if (priv_ss
.str().length()) {
406 dout(0) << priv_ss
.str() << dendl
;
409 if ((flags
& CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
410 (g_ceph_context
->get_set_uid() || g_ceph_context
->get_set_gid())) {
411 // Fix ownership on log files and run directories if needed.
412 // Admin socket files are chown()'d during the common init path _after_
413 // the service thread has been started. This is sadly a bit of a hack :(
414 chown_path(g_conf()->run_dir
,
415 g_ceph_context
->get_set_uid(),
416 g_ceph_context
->get_set_gid(),
417 g_ceph_context
->get_set_uid_string(),
418 g_ceph_context
->get_set_gid_string());
419 g_ceph_context
->_log
->chown_log_file(
420 g_ceph_context
->get_set_uid(),
421 g_ceph_context
->get_set_gid());
424 // Now we're ready to complain about config file parse errors
425 g_conf().complain_about_parse_error(g_ceph_context
);
427 // test leak checking
428 if (g_conf()->debug_deliberately_leak_memory
) {
429 derr
<< "deliberately leaking some memory" << dendl
;
430 char *s
= new char[1234567];
432 // cppcheck-suppress memleak
435 if (code_env
== CODE_ENVIRONMENT_DAEMON
&& !(flags
& CINIT_FLAG_NO_DAEMON_ACTIONS
))
436 output_ceph_version();
438 if (g_ceph_context
->crush_location
.init_on_startup()) {
439 cerr
<< " failed to init_on_startup : " << cpp_strerror(errno
) << std::endl
;
443 return boost::intrusive_ptr
<CephContext
>{g_ceph_context
, false};
446 void global_print_banner(void)
448 output_ceph_version();
451 int global_init_prefork(CephContext
*cct
)
453 if (g_code_env
!= CODE_ENVIRONMENT_DAEMON
)
456 const auto& conf
= cct
->_conf
;
457 if (!conf
->daemonize
) {
459 if (pidfile_write(conf
->pid_file
) < 0)
462 if ((cct
->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
463 (cct
->get_set_uid() || cct
->get_set_gid())) {
464 chown_path(conf
->pid_file
, cct
->get_set_uid(), cct
->get_set_gid(),
465 cct
->get_set_uid_string(), cct
->get_set_gid_string());
471 cct
->notify_pre_fork();
478 void global_init_daemonize(CephContext
*cct
)
480 if (global_init_prefork(cct
) < 0)
483 #if !defined(_AIX) && !defined(_WIN32)
484 int ret
= daemon(1, 1);
487 derr
<< "global_init_daemonize: BUG: daemon error: "
488 << cpp_strerror(ret
) << dendl
;
492 global_init_postfork_start(cct
);
493 global_init_postfork_finish(cct
);
495 # warning daemon not supported on aix
499 /* Make file descriptors 0, 1, and possibly 2 point to /dev/null.
501 * Instead of just closing fd, we redirect it to /dev/null with dup2().
502 * We have to do this because otherwise some arbitrary call to open() later
503 * in the program might get back one of these file descriptors. It's hard to
504 * guarantee that nobody ever writes to stdout, even though they're not
507 int reopen_as_null(CephContext
*cct
, int fd
)
509 int newfd
= open(DEV_NULL
, O_RDWR
| O_CLOEXEC
);
512 lderr(cct
) << __func__
<< " failed to open /dev/null: " << cpp_strerror(err
)
516 // atomically dup newfd to target fd. target fd is implicitly closed if
517 // open and atomically replaced; see man dup2
518 int r
= dup2(newfd
, fd
);
521 lderr(cct
) << __func__
<< " failed to dup2 " << fd
<< ": "
522 << cpp_strerror(err
) << dendl
;
525 // close newfd (we cloned it to target fd)
526 VOID_TEMP_FAILURE_RETRY(close(newfd
));
527 // N.B. FD_CLOEXEC is cleared on fd (see dup2(2))
531 void global_init_postfork_start(CephContext
*cct
)
533 // reexpand the meta in child process
534 cct
->_conf
.finalize_reexpand_meta();
536 // restart log thread
538 cct
->notify_post_fork();
540 reopen_as_null(cct
, STDIN_FILENO
);
542 const auto& conf
= cct
->_conf
;
543 if (pidfile_write(conf
->pid_file
) < 0)
546 if ((cct
->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
547 (cct
->get_set_uid() || cct
->get_set_gid())) {
548 chown_path(conf
->pid_file
, cct
->get_set_uid(), cct
->get_set_gid(),
549 cct
->get_set_uid_string(), cct
->get_set_gid_string());
553 void global_init_postfork_finish(CephContext
*cct
)
555 /* We only close stdout+stderr once the caller decides the daemonization
556 * process is finished. This way we can allow error or other messages to be
557 * propagated in a manner that the user is able to see.
559 if (!(cct
->get_init_flags() & CINIT_FLAG_NO_CLOSE_STDERR
)) {
560 int ret
= global_init_shutdown_stderr(cct
);
562 derr
<< "global_init_daemonize: global_init_shutdown_stderr failed with "
563 << "error code " << ret
<< dendl
;
568 reopen_as_null(cct
, STDOUT_FILENO
);
570 ldout(cct
, 1) << "finished global_init_daemonize" << dendl
;
574 void global_init_chdir(const CephContext
*cct
)
576 const auto& conf
= cct
->_conf
;
577 if (conf
->chdir
.empty())
579 if (::chdir(conf
->chdir
.c_str())) {
581 derr
<< "global_init_chdir: failed to chdir to directory: '"
582 << conf
->chdir
<< "': " << cpp_strerror(err
) << dendl
;
586 int global_init_shutdown_stderr(CephContext
*cct
)
588 reopen_as_null(cct
, STDERR_FILENO
);
589 cct
->_log
->set_stderr_level(-2, -2);
593 int global_init_preload_erasure_code(const CephContext
*cct
)
595 const auto& conf
= cct
->_conf
;
596 string plugins
= conf
->osd_erasure_code_plugins
;
598 // validate that this is a not a legacy plugin
599 std::list
<string
> plugins_list
;
600 get_str_list(plugins
, plugins_list
);
601 for (auto i
= plugins_list
.begin(); i
!= plugins_list
.end(); ++i
) {
602 string plugin_name
= *i
;
603 string replacement
= "";
605 if (plugin_name
== "jerasure_generic" ||
606 plugin_name
== "jerasure_sse3" ||
607 plugin_name
== "jerasure_sse4" ||
608 plugin_name
== "jerasure_neon") {
609 replacement
= "jerasure";
611 else if (plugin_name
== "shec_generic" ||
612 plugin_name
== "shec_sse3" ||
613 plugin_name
== "shec_sse4" ||
614 plugin_name
== "shec_neon") {
615 replacement
= "shec";
618 if (replacement
!= "") {
619 dout(0) << "WARNING: osd_erasure_code_plugins contains plugin "
620 << plugin_name
<< " that is now deprecated. Please modify the value "
621 << "for osd_erasure_code_plugins to use " << replacement
<< " instead." << dendl
;
625 std::stringstream ss
;
626 int r
= ceph::ErasureCodePluginRegistry::instance().preload(
628 conf
.get_val
<std::string
>("erasure_code_dir"),
631 derr
<< ss
.str() << dendl
;
633 dout(0) << ss
.str() << dendl
;