]> git.proxmox.com Git - ceph.git/blame - ceph/src/global/global_init.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / global / global_init.cc
CommitLineData
7c673cae
FG
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"
11fdf7f2 29#include "mon/MonClient.h"
7c673cae
FG
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
42static void global_init_set_globals(CephContext *cct)
43{
44 g_ceph_context = cct;
11fdf7f2 45 get_process_name(g_process_name, sizeof(g_process_name));
7c673cae
FG
46}
47
48static 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
57static 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
64static 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
11fdf7f2
TL
84void global_pre_init(
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,
88 int flags)
7c673cae
FG
89{
90 std::string conf_file_list;
91 std::string cluster = "";
11fdf7f2
TL
92
93 CephInitParameters iparams = ceph_argparse_early_args(
94 args, module_type,
95 &cluster, &conf_file_list);
96
b32b8144 97 CephContext *cct = common_preinit(iparams, code_env, flags);
7c673cae
FG
98 cct->_conf->cluster = cluster;
99 global_init_set_globals(cct);
11fdf7f2 100 auto& conf = cct->_conf;
7c673cae 101
11fdf7f2
TL
102 if (flags & (CINIT_FLAG_NO_DEFAULT_CONFIG_FILE|
103 CINIT_FLAG_NO_MON_CONFIG)) {
104 conf->no_mon_config = true;
105 }
106
107 // alternate defaults
108 if (defaults) {
109 for (auto& i : *defaults) {
110 conf.set_val_default(i.first, i.second);
111 }
112 }
7c673cae 113
11fdf7f2
TL
114 int ret = conf.parse_config_files(c_str_or_null(conf_file_list),
115 &cerr, flags);
7c673cae 116 if (ret == -EDOM) {
11fdf7f2
TL
117 cct->_log->flush();
118 cerr << "global_init: error parsing config file." << std::endl;
7c673cae
FG
119 _exit(1);
120 }
121 else if (ret == -ENOENT) {
122 if (!(flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)) {
123 if (conf_file_list.length()) {
11fdf7f2
TL
124 cct->_log->flush();
125 cerr << "global_init: unable to open config file from search list "
126 << conf_file_list << std::endl;
7c673cae
FG
127 _exit(1);
128 } else {
11fdf7f2 129 cerr << "did not load config file, using default settings." << std::endl;
7c673cae
FG
130 }
131 }
132 }
133 else if (ret) {
11fdf7f2
TL
134 cct->_log->flush();
135 cerr << "global_init: error reading config file." << std::endl;
7c673cae
FG
136 _exit(1);
137 }
138
11fdf7f2
TL
139 // environment variables override (CEPH_ARGS, CEPH_KEYRING)
140 conf.parse_env(cct->get_module_type());
141
142 // command line (as passed by caller)
143 conf.parse_argv(args);
7c673cae 144
11fdf7f2
TL
145 if (conf->log_early &&
146 !cct->_log->is_started()) {
147 cct->_log->start();
148 }
149
150 if (!conf->no_mon_config) {
151 // make sure our mini-session gets legacy values
152 conf.apply_changes(nullptr);
153
154 MonClient mc_bootstrap(g_ceph_context);
155 if (mc_bootstrap.get_monmap_and_config() < 0) {
156 cct->_log->flush();
157 cerr << "failed to fetch mon config (--no-mon-config to skip)"
158 << std::endl;
159 _exit(1);
160 }
161 }
162 if (!cct->_log->is_started()) {
163 cct->_log->start();
164 }
165
166 // do the --show-config[-val], if present in argv
167 conf.do_argv_commands();
7c673cae
FG
168
169 // Now we're ready to complain about config file parse errors
11fdf7f2 170 g_conf().complain_about_parse_errors(g_ceph_context);
7c673cae
FG
171}
172
173boost::intrusive_ptr<CephContext>
11fdf7f2 174global_init(const std::map<std::string,std::string> *defaults,
7c673cae
FG
175 std::vector < const char* >& args,
176 uint32_t module_type, code_environment_t code_env,
177 int flags,
178 const char *data_dir_option, bool run_pre_init)
179{
180 // Ensure we're not calling the global init functions multiple times.
181 static bool first_run = true;
182 if (run_pre_init) {
183 // We will run pre_init from here (default).
11fdf7f2
TL
184 ceph_assert(!g_ceph_context && first_run);
185 global_pre_init(defaults, args, module_type, code_env, flags);
7c673cae
FG
186 } else {
187 // Caller should have invoked pre_init manually.
11fdf7f2 188 ceph_assert(g_ceph_context && first_run);
7c673cae
FG
189 }
190 first_run = false;
191
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);
196 }
197
198 // signal stuff
199 int siglist[] = { SIGPIPE, 0 };
200 block_signals(siglist, NULL);
201
11fdf7f2 202 if (g_conf()->fatal_signal_handlers) {
7c673cae 203 install_standard_sighandlers();
11fdf7f2
TL
204 }
205 register_assert_context(g_ceph_context);
7c673cae 206
11fdf7f2 207 if (g_conf()->log_flush_on_exit)
7c673cae
FG
208 g_ceph_context->_log->set_flush_on_exit();
209
210 // drop privileges?
211 ostringstream priv_ss;
212
213 // consider --setuser root a no-op, even if we're not root
214 if (getuid() != 0) {
11fdf7f2
TL
215 if (g_conf()->setuser.length()) {
216 cerr << "ignoring --setuser " << g_conf()->setuser << " since I am not root"
7c673cae
FG
217 << std::endl;
218 }
11fdf7f2
TL
219 if (g_conf()->setgroup.length()) {
220 cerr << "ignoring --setgroup " << g_conf()->setgroup
7c673cae
FG
221 << " since I am not root" << std::endl;
222 }
11fdf7f2
TL
223 } else if (g_conf()->setgroup.length() ||
224 g_conf()->setuser.length()) {
7c673cae
FG
225 uid_t uid = 0; // zero means no change; we can only drop privs here.
226 gid_t gid = 0;
227 std::string uid_string;
228 std::string gid_string;
11fdf7f2
TL
229 if (g_conf()->setuser.length()) {
230 uid = atoi(g_conf()->setuser.c_str());
7c673cae
FG
231 if (!uid) {
232 char buf[4096];
233 struct passwd pa;
234 struct passwd *p = 0;
11fdf7f2 235 getpwnam_r(g_conf()->setuser.c_str(), &pa, buf, sizeof(buf), &p);
7c673cae 236 if (!p) {
11fdf7f2 237 cerr << "unable to look up user '" << g_conf()->setuser << "'"
7c673cae
FG
238 << std::endl;
239 exit(1);
240 }
241 uid = p->pw_uid;
242 gid = p->pw_gid;
11fdf7f2 243 uid_string = g_conf()->setuser;
7c673cae
FG
244 }
245 }
11fdf7f2
TL
246 if (g_conf()->setgroup.length() > 0) {
247 gid = atoi(g_conf()->setgroup.c_str());
7c673cae
FG
248 if (!gid) {
249 char buf[4096];
250 struct group gr;
251 struct group *g = 0;
11fdf7f2 252 getgrnam_r(g_conf()->setgroup.c_str(), &gr, buf, sizeof(buf), &g);
7c673cae 253 if (!g) {
11fdf7f2 254 cerr << "unable to look up group '" << g_conf()->setgroup << "'"
7c673cae
FG
255 << ": " << cpp_strerror(errno) << std::endl;
256 exit(1);
257 }
258 gid = g->gr_gid;
11fdf7f2 259 gid_string = g_conf()->setgroup;
7c673cae
FG
260 }
261 }
262 if ((uid || gid) &&
11fdf7f2 263 g_conf()->setuser_match_path.length()) {
7c673cae 264 // induce early expansion of setuser_match_path config option
11fdf7f2
TL
265 string match_path = g_conf()->setuser_match_path;
266 g_conf().early_expand_meta(match_path, &cerr);
7c673cae
FG
267 struct stat st;
268 int r = ::stat(match_path.c_str(), &st);
269 if (r < 0) {
270 cerr << "unable to stat setuser_match_path "
11fdf7f2 271 << g_conf()->setuser_match_path
7c673cae
FG
272 << ": " << cpp_strerror(errno) << std::endl;
273 exit(1);
274 }
275 if ((uid && uid != st.st_uid) ||
276 (gid && gid != st.st_gid)) {
277 cerr << "WARNING: will not setuid/gid: " << match_path
278 << " owned by " << st.st_uid << ":" << st.st_gid
279 << " and not requested " << uid << ":" << gid
280 << std::endl;
281 uid = 0;
282 gid = 0;
283 uid_string.erase();
284 gid_string.erase();
285 } else {
286 priv_ss << "setuser_match_path "
287 << match_path << " owned by "
288 << st.st_uid << ":" << st.st_gid << ". ";
289 }
290 }
291 g_ceph_context->set_uid_gid(uid, gid);
292 g_ceph_context->set_uid_gid_strings(uid_string, gid_string);
293 if ((flags & CINIT_FLAG_DEFER_DROP_PRIVILEGES) == 0) {
294 if (setgid(gid) != 0) {
295 cerr << "unable to setgid " << gid << ": " << cpp_strerror(errno)
296 << std::endl;
297 exit(1);
298 }
299 if (setuid(uid) != 0) {
300 cerr << "unable to setuid " << uid << ": " << cpp_strerror(errno)
301 << std::endl;
302 exit(1);
303 }
304 priv_ss << "set uid:gid to " << uid << ":" << gid << " (" << uid_string << ":" << gid_string << ")";
305 } else {
306 priv_ss << "deferred set uid:gid to " << uid << ":" << gid << " (" << uid_string << ":" << gid_string << ")";
307 }
308 }
309
310#if defined(HAVE_SYS_PRCTL_H)
311 if (prctl(PR_SET_DUMPABLE, 1) == -1) {
312 cerr << "warning: unable to set dumpable flag: " << cpp_strerror(errno) << std::endl;
313 }
314#endif
315
316 // Expand metavariables. Invoke configuration observers. Open log file.
11fdf7f2 317 g_conf().apply_changes(nullptr);
7c673cae 318
11fdf7f2 319 if (g_conf()->run_dir.length() &&
7c673cae
FG
320 code_env == CODE_ENVIRONMENT_DAEMON &&
321 !(flags & CINIT_FLAG_NO_DAEMON_ACTIONS)) {
11fdf7f2 322 int r = ::mkdir(g_conf()->run_dir.c_str(), 0755);
7c673cae 323 if (r < 0 && errno != EEXIST) {
11fdf7f2 324 cerr << "warning: unable to create " << g_conf()->run_dir << ": " << cpp_strerror(errno) << std::endl;
7c673cae
FG
325 }
326 }
327
7c673cae
FG
328 // call all observers now. this has the side-effect of configuring
329 // and opening the log file immediately.
11fdf7f2 330 g_conf().call_all_observers();
7c673cae
FG
331
332 if (priv_ss.str().length()) {
333 dout(0) << priv_ss.str() << dendl;
334 }
335
336 if ((flags & CINIT_FLAG_DEFER_DROP_PRIVILEGES) &&
337 (g_ceph_context->get_set_uid() || g_ceph_context->get_set_gid())) {
338 // Fix ownership on log files and run directories if needed.
339 // Admin socket files are chown()'d during the common init path _after_
340 // the service thread has been started. This is sadly a bit of a hack :(
11fdf7f2 341 chown_path(g_conf()->run_dir,
7c673cae
FG
342 g_ceph_context->get_set_uid(),
343 g_ceph_context->get_set_gid(),
344 g_ceph_context->get_set_uid_string(),
345 g_ceph_context->get_set_gid_string());
346 g_ceph_context->_log->chown_log_file(
347 g_ceph_context->get_set_uid(),
348 g_ceph_context->get_set_gid());
349 }
350
351 // Now we're ready to complain about config file parse errors
11fdf7f2 352 g_conf().complain_about_parse_errors(g_ceph_context);
7c673cae
FG
353
354 // test leak checking
11fdf7f2 355 if (g_conf()->debug_deliberately_leak_memory) {
7c673cae
FG
356 derr << "deliberately leaking some memory" << dendl;
357 char *s = new char[1234567];
358 (void)s;
359 // cppcheck-suppress memleak
360 }
361
362 if (code_env == CODE_ENVIRONMENT_DAEMON && !(flags & CINIT_FLAG_NO_DAEMON_ACTIONS))
363 output_ceph_version();
364
365 if (g_ceph_context->crush_location.init_on_startup()) {
366 cerr << " failed to init_on_startup : " << cpp_strerror(errno) << std::endl;
367 exit(1);
368 }
369
370 return boost::intrusive_ptr<CephContext>{g_ceph_context, false};
371}
372
373void intrusive_ptr_add_ref(CephContext* cct)
374{
375 cct->get();
376}
377
378void intrusive_ptr_release(CephContext* cct)
379{
380 cct->put();
381}
382
383void global_print_banner(void)
384{
385 output_ceph_version();
386}
387
388int global_init_prefork(CephContext *cct)
389{
390 if (g_code_env != CODE_ENVIRONMENT_DAEMON)
391 return -1;
392
11fdf7f2 393 const auto& conf = cct->_conf;
7c673cae
FG
394 if (!conf->daemonize) {
395
396 if (pidfile_write(conf) < 0)
397 exit(1);
398
399 if ((cct->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES) &&
400 (cct->get_set_uid() || cct->get_set_gid())) {
401 chown_path(conf->pid_file, cct->get_set_uid(), cct->get_set_gid(),
402 cct->get_set_uid_string(), cct->get_set_gid_string());
403 }
404
405 return -1;
406 }
407
408 cct->notify_pre_fork();
409 // stop log thread
410 cct->_log->flush();
411 cct->_log->stop();
412 return 0;
413}
414
415void global_init_daemonize(CephContext *cct)
416{
417 if (global_init_prefork(cct) < 0)
418 return;
419
420#if !defined(_AIX)
421 int ret = daemon(1, 1);
422 if (ret) {
423 ret = errno;
424 derr << "global_init_daemonize: BUG: daemon error: "
425 << cpp_strerror(ret) << dendl;
426 exit(1);
427 }
428
429 global_init_postfork_start(cct);
430 global_init_postfork_finish(cct);
431#else
432# warning daemon not supported on aix
433#endif
434}
435
1adf2230
AA
436int reopen_as_null(CephContext *cct, int fd)
437{
91327a77 438 int newfd = open("/dev/null", O_RDONLY|O_CLOEXEC);
1adf2230
AA
439 if (newfd < 0) {
440 int err = errno;
441 lderr(cct) << __func__ << " failed to open /dev/null: " << cpp_strerror(err)
442 << dendl;
443 return -1;
444 }
445 // atomically dup newfd to target fd. target fd is implicitly closed if
446 // open and atomically replaced; see man dup2
447 int r = dup2(newfd, fd);
448 if (r < 0) {
449 int err = errno;
450 lderr(cct) << __func__ << " failed to dup2 " << fd << ": "
451 << cpp_strerror(err) << dendl;
452 return -1;
453 }
454 // close newfd (we cloned it to target fd)
455 VOID_TEMP_FAILURE_RETRY(close(newfd));
91327a77 456 // N.B. FD_CLOEXEC is cleared on fd (see dup2(2))
1adf2230
AA
457 return 0;
458}
459
7c673cae
FG
460void global_init_postfork_start(CephContext *cct)
461{
462 // restart log thread
463 cct->_log->start();
464 cct->notify_post_fork();
465
466 /* This is the old trick where we make file descriptors 0, 1, and possibly 2
467 * point to /dev/null.
468 *
469 * We have to do this because otherwise some arbitrary call to open() later
470 * in the program might get back one of these file descriptors. It's hard to
471 * guarantee that nobody ever writes to stdout, even though they're not
472 * supposed to.
473 */
1adf2230 474 reopen_as_null(cct, STDIN_FILENO);
7c673cae 475
11fdf7f2 476 const auto& conf = cct->_conf;
7c673cae
FG
477 if (pidfile_write(conf) < 0)
478 exit(1);
479
480 if ((cct->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES) &&
481 (cct->get_set_uid() || cct->get_set_gid())) {
482 chown_path(conf->pid_file, cct->get_set_uid(), cct->get_set_gid(),
483 cct->get_set_uid_string(), cct->get_set_gid_string());
484 }
485}
486
487void global_init_postfork_finish(CephContext *cct)
488{
28e407b8
AA
489 /* We only close stdout+stderr once the caller decides the daemonization
490 * process is finished. This way we can allow error or other messages to be
7c673cae
FG
491 * propagated in a manner that the user is able to see.
492 */
493 if (!(cct->get_init_flags() & CINIT_FLAG_NO_CLOSE_STDERR)) {
494 int ret = global_init_shutdown_stderr(cct);
495 if (ret) {
496 derr << "global_init_daemonize: global_init_shutdown_stderr failed with "
497 << "error code " << ret << dendl;
498 exit(1);
499 }
500 }
28e407b8 501
1adf2230 502 reopen_as_null(cct, STDOUT_FILENO);
28e407b8 503
7c673cae
FG
504 ldout(cct, 1) << "finished global_init_daemonize" << dendl;
505}
506
507
508void global_init_chdir(const CephContext *cct)
509{
11fdf7f2 510 const auto& conf = cct->_conf;
7c673cae
FG
511 if (conf->chdir.empty())
512 return;
513 if (::chdir(conf->chdir.c_str())) {
514 int err = errno;
515 derr << "global_init_chdir: failed to chdir to directory: '"
516 << conf->chdir << "': " << cpp_strerror(err) << dendl;
517 }
518}
519
520/* Map stderr to /dev/null. This isn't really re-entrant; we rely on the old unix
521 * behavior that the file descriptor that gets assigned is the lowest
522 * available one.
523 */
524int global_init_shutdown_stderr(CephContext *cct)
525{
1adf2230 526 reopen_as_null(cct, STDERR_FILENO);
11fdf7f2
TL
527 int l = cct->_conf->err_to_stderr ? -1 : -2;
528 cct->_log->set_stderr_level(l, l);
7c673cae
FG
529 return 0;
530}
531
532int global_init_preload_erasure_code(const CephContext *cct)
533{
11fdf7f2 534 const auto& conf = cct->_conf;
7c673cae
FG
535 string plugins = conf->osd_erasure_code_plugins;
536
537 // validate that this is a not a legacy plugin
538 list<string> plugins_list;
539 get_str_list(plugins, plugins_list);
540 for (list<string>::iterator i = plugins_list.begin();
541 i != plugins_list.end();
542 ++i) {
543 string plugin_name = *i;
544 string replacement = "";
545
546 if (plugin_name == "jerasure_generic" ||
547 plugin_name == "jerasure_sse3" ||
548 plugin_name == "jerasure_sse4" ||
549 plugin_name == "jerasure_neon") {
550 replacement = "jerasure";
551 }
552 else if (plugin_name == "shec_generic" ||
553 plugin_name == "shec_sse3" ||
554 plugin_name == "shec_sse4" ||
555 plugin_name == "shec_neon") {
556 replacement = "shec";
557 }
558
559 if (replacement != "") {
560 dout(0) << "WARNING: osd_erasure_code_plugins contains plugin "
561 << plugin_name << " that is now deprecated. Please modify the value "
562 << "for osd_erasure_code_plugins to use " << replacement << " instead." << dendl;
563 }
564 }
565
566 stringstream ss;
567 int r = ErasureCodePluginRegistry::instance().preload(
568 plugins,
11fdf7f2 569 conf.get_val<std::string>("erasure_code_dir"),
7c673cae
FG
570 &ss);
571 if (r)
572 derr << ss.str() << dendl;
573 else
574 dout(0) << ss.str() << dendl;
575 return r;
576}