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