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 "common/admin_socket.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
)
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
;
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
,
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
;
99 conf
->parse_argv(*alt_def_args
); // alternative default args
101 int ret
= conf
->parse_config_files(c_str_or_null(conf_file_list
),
104 dout_emergency("global_init: error parsing config file.\n");
107 else if (ret
== -ENOENT
) {
108 if (!(flags
& CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
)) {
109 if (conf_file_list
.length()) {
111 oss
<< "global_init: unable to open config file from search list "
112 << conf_file_list
<< "\n";
113 dout_emergency(oss
.str());
116 derr
<< "did not load config file, using default settings." << dendl
;
121 dout_emergency("global_init: error reading config file.\n");
125 conf
->parse_env(); // environment variables override
127 conf
->parse_argv(args
); // argv override
129 // Now we're ready to complain about config file parse errors
130 g_conf
->complain_about_parse_errors(g_ceph_context
);
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
,
138 const char *data_dir_option
, bool run_pre_init
)
140 // Ensure we're not calling the global init functions multiple times.
141 static bool first_run
= true;
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
);
147 // Caller should have invoked pre_init manually.
148 assert(g_ceph_context
&& first_run
);
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
);
159 int siglist
[] = { SIGPIPE
, 0 };
160 block_signals(siglist
, NULL
);
162 if (g_conf
->fatal_signal_handlers
)
163 install_standard_sighandlers();
165 if (g_conf
->log_flush_on_exit
)
166 g_ceph_context
->_log
->set_flush_on_exit();
169 ostringstream priv_ss
;
171 // consider --setuser root a no-op, even if we're not root
173 if (g_conf
->setuser
.length()) {
174 cerr
<< "ignoring --setuser " << g_conf
->setuser
<< " since I am not root"
177 if (g_conf
->setgroup
.length()) {
178 cerr
<< "ignoring --setgroup " << g_conf
->setgroup
179 << " since I am not root" << std::endl
;
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.
185 std::string uid_string
;
186 std::string gid_string
;
187 if (g_conf
->setuser
.length()) {
188 uid
= atoi(g_conf
->setuser
.c_str());
192 struct passwd
*p
= 0;
193 getpwnam_r(g_conf
->setuser
.c_str(), &pa
, buf
, sizeof(buf
), &p
);
195 cerr
<< "unable to look up user '" << g_conf
->setuser
<< "'"
201 uid_string
= g_conf
->setuser
;
204 if (g_conf
->setgroup
.length() > 0) {
205 gid
= atoi(g_conf
->setgroup
.c_str());
210 getgrnam_r(g_conf
->setgroup
.c_str(), &gr
, buf
, sizeof(buf
), &g
);
212 cerr
<< "unable to look up group '" << g_conf
->setgroup
<< "'"
213 << ": " << cpp_strerror(errno
) << std::endl
;
217 gid_string
= g_conf
->setgroup
;
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
);
226 int r
= ::stat(match_path
.c_str(), &st
);
228 cerr
<< "unable to stat setuser_match_path "
229 << g_conf
->setuser_match_path
230 << ": " << cpp_strerror(errno
) << std::endl
;
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
244 priv_ss
<< "setuser_match_path "
245 << match_path
<< " owned by "
246 << st
.st_uid
<< ":" << st
.st_gid
<< ". ";
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
)
257 if (setuid(uid
) != 0) {
258 cerr
<< "unable to setuid " << uid
<< ": " << cpp_strerror(errno
)
262 priv_ss
<< "set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
264 priv_ss
<< "deferred set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
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
;
274 // Expand metavariables. Invoke configuration observers. Open log file.
275 g_conf
->apply_changes(NULL
);
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
;
286 register_assert_context(g_ceph_context
);
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();
292 if (priv_ss
.str().length()) {
293 dout(0) << priv_ss
.str() << dendl
;
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());
311 // Now we're ready to complain about config file parse errors
312 g_conf
->complain_about_parse_errors(g_ceph_context
);
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];
319 // cppcheck-suppress memleak
322 if (code_env
== CODE_ENVIRONMENT_DAEMON
&& !(flags
& CINIT_FLAG_NO_DAEMON_ACTIONS
))
323 output_ceph_version();
325 if (g_ceph_context
->crush_location
.init_on_startup()) {
326 cerr
<< " failed to init_on_startup : " << cpp_strerror(errno
) << std::endl
;
330 return boost::intrusive_ptr
<CephContext
>{g_ceph_context
, false};
333 void intrusive_ptr_add_ref(CephContext
* cct
)
338 void intrusive_ptr_release(CephContext
* cct
)
343 void global_print_banner(void)
345 output_ceph_version();
348 int global_init_prefork(CephContext
*cct
)
350 if (g_code_env
!= CODE_ENVIRONMENT_DAEMON
)
353 const md_config_t
*conf
= cct
->_conf
;
354 if (!conf
->daemonize
) {
356 if (pidfile_write(conf
) < 0)
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());
368 cct
->notify_pre_fork();
375 void global_init_daemonize(CephContext
*cct
)
377 if (global_init_prefork(cct
) < 0)
381 int ret
= daemon(1, 1);
384 derr
<< "global_init_daemonize: BUG: daemon error: "
385 << cpp_strerror(ret
) << dendl
;
389 global_init_postfork_start(cct
);
390 global_init_postfork_finish(cct
);
392 # warning daemon not supported on aix
396 void global_init_postfork_start(CephContext
*cct
)
398 // restart log thread
400 cct
->notify_post_fork();
402 /* This is the old trick where we make file descriptors 0, 1, and possibly 2
403 * point to /dev/null.
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
410 VOID_TEMP_FAILURE_RETRY(close(STDIN_FILENO
));
411 if (open("/dev/null", O_RDONLY
) < 0) {
413 derr
<< "global_init_daemonize: open(/dev/null) failed: error "
418 const md_config_t
*conf
= cct
->_conf
;
419 if (pidfile_write(conf
) < 0)
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());
429 void global_init_postfork_finish(CephContext
*cct
)
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.
435 if (!(cct
->get_init_flags() & CINIT_FLAG_NO_CLOSE_STDERR
)) {
436 int ret
= global_init_shutdown_stderr(cct
);
438 derr
<< "global_init_daemonize: global_init_shutdown_stderr failed with "
439 << "error code " << ret
<< dendl
;
444 VOID_TEMP_FAILURE_RETRY(close(STDOUT_FILENO
));
445 if (open("/dev/null", O_RDONLY
) < 0) {
447 derr
<< "global_init_daemonize: open(/dev/null) failed: error "
452 ldout(cct
, 1) << "finished global_init_daemonize" << dendl
;
456 void global_init_chdir(const CephContext
*cct
)
458 const md_config_t
*conf
= cct
->_conf
;
459 if (conf
->chdir
.empty())
461 if (::chdir(conf
->chdir
.c_str())) {
463 derr
<< "global_init_chdir: failed to chdir to directory: '"
464 << conf
->chdir
<< "': " << cpp_strerror(err
) << dendl
;
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
472 int global_init_shutdown_stderr(CephContext
*cct
)
474 VOID_TEMP_FAILURE_RETRY(close(STDERR_FILENO
));
475 if (open("/dev/null", O_RDONLY
) < 0) {
477 derr
<< "global_init_shutdown_stderr: open(/dev/null) failed: error "
481 cct
->_log
->set_stderr_level(-1, -1);
485 int global_init_preload_erasure_code(const CephContext
*cct
)
487 const md_config_t
*conf
= cct
->_conf
;
488 string plugins
= conf
->osd_erasure_code_plugins
;
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();
496 string plugin_name
= *i
;
497 string replacement
= "";
499 if (plugin_name
== "jerasure_generic" ||
500 plugin_name
== "jerasure_sse3" ||
501 plugin_name
== "jerasure_sse4" ||
502 plugin_name
== "jerasure_neon") {
503 replacement
= "jerasure";
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";
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
;
520 int r
= ErasureCodePluginRegistry::instance().preload(
522 conf
->get_val
<std::string
>("erasure_code_dir"),
525 derr
<< ss
.str() << dendl
;
527 dout(0) << ss
.str() << dendl
;