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
,
88 const char *data_dir_option
)
90 std::string conf_file_list
;
91 std::string cluster
= "";
92 CephInitParameters iparams
= ceph_argparse_early_args(args
, module_type
,
93 &cluster
, &conf_file_list
);
94 CephContext
*cct
= common_preinit(iparams
, code_env
, flags
, data_dir_option
);
95 cct
->_conf
->cluster
= cluster
;
96 global_init_set_globals(cct
);
97 md_config_t
*conf
= cct
->_conf
;
100 conf
->parse_argv(*alt_def_args
); // alternative default args
102 int ret
= conf
->parse_config_files(c_str_or_null(conf_file_list
),
105 dout_emergency("global_init: error parsing config file.\n");
108 else if (ret
== -ENOENT
) {
109 if (!(flags
& CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
)) {
110 if (conf_file_list
.length()) {
112 oss
<< "global_init: unable to open config file from search list "
113 << conf_file_list
<< "\n";
114 dout_emergency(oss
.str());
117 derr
<< "did not load config file, using default settings." << dendl
;
122 dout_emergency("global_init: error reading config file.\n");
126 conf
->parse_env(); // environment variables override
128 conf
->parse_argv(args
); // argv override
130 // Now we're ready to complain about config file parse errors
131 g_conf
->complain_about_parse_errors(g_ceph_context
);
134 boost::intrusive_ptr
<CephContext
>
135 global_init(std::vector
< const char * > *alt_def_args
,
136 std::vector
< const char* >& args
,
137 uint32_t module_type
, code_environment_t code_env
,
139 const char *data_dir_option
, bool run_pre_init
)
141 // Ensure we're not calling the global init functions multiple times.
142 static bool first_run
= true;
144 // We will run pre_init from here (default).
145 assert(!g_ceph_context
&& first_run
);
146 global_pre_init(alt_def_args
, args
, module_type
, code_env
, flags
);
148 // Caller should have invoked pre_init manually.
149 assert(g_ceph_context
&& first_run
);
153 // Verify flags have not changed if global_pre_init() has been called
154 // manually. If they have, update them.
155 if (g_ceph_context
->get_init_flags() != flags
) {
156 g_ceph_context
->set_init_flags(flags
);
160 int siglist
[] = { SIGPIPE
, 0 };
161 block_signals(siglist
, NULL
);
163 if (g_conf
->fatal_signal_handlers
)
164 install_standard_sighandlers();
166 if (g_conf
->log_flush_on_exit
)
167 g_ceph_context
->_log
->set_flush_on_exit();
170 ostringstream priv_ss
;
172 // consider --setuser root a no-op, even if we're not root
174 if (g_conf
->setuser
.length()) {
175 cerr
<< "ignoring --setuser " << g_conf
->setuser
<< " since I am not root"
178 if (g_conf
->setgroup
.length()) {
179 cerr
<< "ignoring --setgroup " << g_conf
->setgroup
180 << " since I am not root" << std::endl
;
182 } else if (g_conf
->setgroup
.length() ||
183 g_conf
->setuser
.length()) {
184 uid_t uid
= 0; // zero means no change; we can only drop privs here.
186 std::string uid_string
;
187 std::string gid_string
;
188 if (g_conf
->setuser
.length()) {
189 uid
= atoi(g_conf
->setuser
.c_str());
193 struct passwd
*p
= 0;
194 getpwnam_r(g_conf
->setuser
.c_str(), &pa
, buf
, sizeof(buf
), &p
);
196 cerr
<< "unable to look up user '" << g_conf
->setuser
<< "'"
202 uid_string
= g_conf
->setuser
;
205 if (g_conf
->setgroup
.length() > 0) {
206 gid
= atoi(g_conf
->setgroup
.c_str());
211 getgrnam_r(g_conf
->setgroup
.c_str(), &gr
, buf
, sizeof(buf
), &g
);
213 cerr
<< "unable to look up group '" << g_conf
->setgroup
<< "'"
214 << ": " << cpp_strerror(errno
) << std::endl
;
218 gid_string
= g_conf
->setgroup
;
222 g_conf
->setuser_match_path
.length()) {
223 // induce early expansion of setuser_match_path config option
224 string match_path
= g_conf
->setuser_match_path
;
225 g_conf
->early_expand_meta(match_path
, &cerr
);
227 int r
= ::stat(match_path
.c_str(), &st
);
229 cerr
<< "unable to stat setuser_match_path "
230 << g_conf
->setuser_match_path
231 << ": " << cpp_strerror(errno
) << std::endl
;
234 if ((uid
&& uid
!= st
.st_uid
) ||
235 (gid
&& gid
!= st
.st_gid
)) {
236 cerr
<< "WARNING: will not setuid/gid: " << match_path
237 << " owned by " << st
.st_uid
<< ":" << st
.st_gid
238 << " and not requested " << uid
<< ":" << gid
245 priv_ss
<< "setuser_match_path "
246 << match_path
<< " owned by "
247 << st
.st_uid
<< ":" << st
.st_gid
<< ". ";
250 g_ceph_context
->set_uid_gid(uid
, gid
);
251 g_ceph_context
->set_uid_gid_strings(uid_string
, gid_string
);
252 if ((flags
& CINIT_FLAG_DEFER_DROP_PRIVILEGES
) == 0) {
253 if (setgid(gid
) != 0) {
254 cerr
<< "unable to setgid " << gid
<< ": " << cpp_strerror(errno
)
258 if (setuid(uid
) != 0) {
259 cerr
<< "unable to setuid " << uid
<< ": " << cpp_strerror(errno
)
263 priv_ss
<< "set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
265 priv_ss
<< "deferred set uid:gid to " << uid
<< ":" << gid
<< " (" << uid_string
<< ":" << gid_string
<< ")";
269 #if defined(HAVE_SYS_PRCTL_H)
270 if (prctl(PR_SET_DUMPABLE
, 1) == -1) {
271 cerr
<< "warning: unable to set dumpable flag: " << cpp_strerror(errno
) << std::endl
;
275 // Expand metavariables. Invoke configuration observers. Open log file.
276 g_conf
->apply_changes(NULL
);
278 if (g_conf
->run_dir
.length() &&
279 code_env
== CODE_ENVIRONMENT_DAEMON
&&
280 !(flags
& CINIT_FLAG_NO_DAEMON_ACTIONS
)) {
281 int r
= ::mkdir(g_conf
->run_dir
.c_str(), 0755);
282 if (r
< 0 && errno
!= EEXIST
) {
283 cerr
<< "warning: unable to create " << g_conf
->run_dir
<< ": " << cpp_strerror(errno
) << std::endl
;
287 register_assert_context(g_ceph_context
);
289 // call all observers now. this has the side-effect of configuring
290 // and opening the log file immediately.
291 g_conf
->call_all_observers();
293 if (priv_ss
.str().length()) {
294 dout(0) << priv_ss
.str() << dendl
;
297 if ((flags
& CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
298 (g_ceph_context
->get_set_uid() || g_ceph_context
->get_set_gid())) {
299 // Fix ownership on log files and run directories if needed.
300 // Admin socket files are chown()'d during the common init path _after_
301 // the service thread has been started. This is sadly a bit of a hack :(
302 chown_path(g_conf
->run_dir
,
303 g_ceph_context
->get_set_uid(),
304 g_ceph_context
->get_set_gid(),
305 g_ceph_context
->get_set_uid_string(),
306 g_ceph_context
->get_set_gid_string());
307 g_ceph_context
->_log
->chown_log_file(
308 g_ceph_context
->get_set_uid(),
309 g_ceph_context
->get_set_gid());
312 // Now we're ready to complain about config file parse errors
313 g_conf
->complain_about_parse_errors(g_ceph_context
);
315 // test leak checking
316 if (g_conf
->debug_deliberately_leak_memory
) {
317 derr
<< "deliberately leaking some memory" << dendl
;
318 char *s
= new char[1234567];
320 // cppcheck-suppress memleak
323 if (code_env
== CODE_ENVIRONMENT_DAEMON
&& !(flags
& CINIT_FLAG_NO_DAEMON_ACTIONS
))
324 output_ceph_version();
326 if (g_ceph_context
->crush_location
.init_on_startup()) {
327 cerr
<< " failed to init_on_startup : " << cpp_strerror(errno
) << std::endl
;
331 return boost::intrusive_ptr
<CephContext
>{g_ceph_context
, false};
334 void intrusive_ptr_add_ref(CephContext
* cct
)
339 void intrusive_ptr_release(CephContext
* cct
)
344 void global_print_banner(void)
346 output_ceph_version();
349 int global_init_prefork(CephContext
*cct
)
351 if (g_code_env
!= CODE_ENVIRONMENT_DAEMON
)
354 const md_config_t
*conf
= cct
->_conf
;
355 if (!conf
->daemonize
) {
357 if (pidfile_write(conf
) < 0)
360 if ((cct
->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
361 (cct
->get_set_uid() || cct
->get_set_gid())) {
362 chown_path(conf
->pid_file
, cct
->get_set_uid(), cct
->get_set_gid(),
363 cct
->get_set_uid_string(), cct
->get_set_gid_string());
369 cct
->notify_pre_fork();
376 void global_init_daemonize(CephContext
*cct
)
378 if (global_init_prefork(cct
) < 0)
382 int ret
= daemon(1, 1);
385 derr
<< "global_init_daemonize: BUG: daemon error: "
386 << cpp_strerror(ret
) << dendl
;
390 global_init_postfork_start(cct
);
391 global_init_postfork_finish(cct
);
393 # warning daemon not supported on aix
397 void global_init_postfork_start(CephContext
*cct
)
399 // restart log thread
401 cct
->notify_post_fork();
403 /* This is the old trick where we make file descriptors 0, 1, and possibly 2
404 * point to /dev/null.
406 * We have to do this because otherwise some arbitrary call to open() later
407 * in the program might get back one of these file descriptors. It's hard to
408 * guarantee that nobody ever writes to stdout, even though they're not
411 VOID_TEMP_FAILURE_RETRY(close(STDIN_FILENO
));
412 if (open("/dev/null", O_RDONLY
) < 0) {
414 derr
<< "global_init_daemonize: open(/dev/null) failed: error "
418 VOID_TEMP_FAILURE_RETRY(close(STDOUT_FILENO
));
419 if (open("/dev/null", O_RDONLY
) < 0) {
421 derr
<< "global_init_daemonize: open(/dev/null) failed: error "
426 const md_config_t
*conf
= cct
->_conf
;
427 if (pidfile_write(conf
) < 0)
430 if ((cct
->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES
) &&
431 (cct
->get_set_uid() || cct
->get_set_gid())) {
432 chown_path(conf
->pid_file
, cct
->get_set_uid(), cct
->get_set_gid(),
433 cct
->get_set_uid_string(), cct
->get_set_gid_string());
437 void global_init_postfork_finish(CephContext
*cct
)
439 /* We only close stderr once the caller decides the daemonization
440 * process is finished. This way we can allow error messages to be
441 * propagated in a manner that the user is able to see.
443 if (!(cct
->get_init_flags() & CINIT_FLAG_NO_CLOSE_STDERR
)) {
444 int ret
= global_init_shutdown_stderr(cct
);
446 derr
<< "global_init_daemonize: global_init_shutdown_stderr failed with "
447 << "error code " << ret
<< dendl
;
451 ldout(cct
, 1) << "finished global_init_daemonize" << dendl
;
455 void global_init_chdir(const CephContext
*cct
)
457 const md_config_t
*conf
= cct
->_conf
;
458 if (conf
->chdir
.empty())
460 if (::chdir(conf
->chdir
.c_str())) {
462 derr
<< "global_init_chdir: failed to chdir to directory: '"
463 << conf
->chdir
<< "': " << cpp_strerror(err
) << dendl
;
467 /* Map stderr to /dev/null. This isn't really re-entrant; we rely on the old unix
468 * behavior that the file descriptor that gets assigned is the lowest
471 int global_init_shutdown_stderr(CephContext
*cct
)
473 VOID_TEMP_FAILURE_RETRY(close(STDERR_FILENO
));
474 if (open("/dev/null", O_RDONLY
) < 0) {
476 derr
<< "global_init_shutdown_stderr: open(/dev/null) failed: error "
480 cct
->_log
->set_stderr_level(-1, -1);
484 int global_init_preload_erasure_code(const CephContext
*cct
)
486 const md_config_t
*conf
= cct
->_conf
;
487 string plugins
= conf
->osd_erasure_code_plugins
;
489 // validate that this is a not a legacy plugin
490 list
<string
> plugins_list
;
491 get_str_list(plugins
, plugins_list
);
492 for (list
<string
>::iterator i
= plugins_list
.begin();
493 i
!= plugins_list
.end();
495 string plugin_name
= *i
;
496 string replacement
= "";
498 if (plugin_name
== "jerasure_generic" ||
499 plugin_name
== "jerasure_sse3" ||
500 plugin_name
== "jerasure_sse4" ||
501 plugin_name
== "jerasure_neon") {
502 replacement
= "jerasure";
504 else if (plugin_name
== "shec_generic" ||
505 plugin_name
== "shec_sse3" ||
506 plugin_name
== "shec_sse4" ||
507 plugin_name
== "shec_neon") {
508 replacement
= "shec";
511 if (replacement
!= "") {
512 dout(0) << "WARNING: osd_erasure_code_plugins contains plugin "
513 << plugin_name
<< " that is now deprecated. Please modify the value "
514 << "for osd_erasure_code_plugins to use " << replacement
<< " instead." << dendl
;
519 int r
= ErasureCodePluginRegistry::instance().preload(
521 conf
->get_val
<std::string
>("erasure_code_dir"),
524 derr
<< ss
.str() << dendl
;
526 dout(0) << ss
.str() << dendl
;