Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / global / global_init.cc
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 "common/admin_socket.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   g_conf = cct->_conf;
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(std::vector < const char * > *alt_def_args,
85                      std::vector < const char* >& args,
86                      uint32_t module_type, code_environment_t code_env,
87                      int flags,
88                      const char *data_dir_option)
89 {
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;
98
99   if (alt_def_args)
100     conf->parse_argv(*alt_def_args);  // alternative default args
101
102   int ret = conf->parse_config_files(c_str_or_null(conf_file_list),
103                                      &cerr, flags);
104   if (ret == -EDOM) {
105     dout_emergency("global_init: error parsing config file.\n");
106     _exit(1);
107   }
108   else if (ret == -ENOENT) {
109     if (!(flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)) {
110       if (conf_file_list.length()) {
111         ostringstream oss;
112         oss << "global_init: unable to open config file from search list "
113             << conf_file_list << "\n";
114         dout_emergency(oss.str());
115         _exit(1);
116       } else {
117         derr << "did not load config file, using default settings." << dendl;
118       }
119     }
120   }
121   else if (ret) {
122     dout_emergency("global_init: error reading config file.\n");
123     _exit(1);
124   }
125
126   conf->parse_env(); // environment variables override
127
128   conf->parse_argv(args); // argv override
129
130   // Now we're ready to complain about config file parse errors
131   g_conf->complain_about_parse_errors(g_ceph_context);
132 }
133
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,
138             int flags,
139             const char *data_dir_option, bool run_pre_init)
140 {
141   // Ensure we're not calling the global init functions multiple times.
142   static bool first_run = true;
143   if (run_pre_init) {
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);
147   } else {
148     // Caller should have invoked pre_init manually.
149     assert(g_ceph_context && first_run);
150   }
151   first_run = false;
152
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);
157   }
158
159   // signal stuff
160   int siglist[] = { SIGPIPE, 0 };
161   block_signals(siglist, NULL);
162
163   if (g_conf->fatal_signal_handlers)
164     install_standard_sighandlers();
165
166   if (g_conf->log_flush_on_exit)
167     g_ceph_context->_log->set_flush_on_exit();
168
169   // drop privileges?
170   ostringstream priv_ss;
171  
172   // consider --setuser root a no-op, even if we're not root
173   if (getuid() != 0) {
174     if (g_conf->setuser.length()) {
175       cerr << "ignoring --setuser " << g_conf->setuser << " since I am not root"
176            << std::endl;
177     }
178     if (g_conf->setgroup.length()) {
179       cerr << "ignoring --setgroup " << g_conf->setgroup
180            << " since I am not root" << std::endl;
181     }
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.
185     gid_t gid = 0;
186     std::string uid_string;
187     std::string gid_string;
188     if (g_conf->setuser.length()) {
189       uid = atoi(g_conf->setuser.c_str());
190       if (!uid) {
191         char buf[4096];
192         struct passwd pa;
193         struct passwd *p = 0;
194         getpwnam_r(g_conf->setuser.c_str(), &pa, buf, sizeof(buf), &p);
195         if (!p) {
196           cerr << "unable to look up user '" << g_conf->setuser << "'"
197                << std::endl;
198           exit(1);
199         }
200         uid = p->pw_uid;
201         gid = p->pw_gid;
202         uid_string = g_conf->setuser;
203       }
204     }
205     if (g_conf->setgroup.length() > 0) {
206       gid = atoi(g_conf->setgroup.c_str());
207       if (!gid) {
208         char buf[4096];
209         struct group gr;
210         struct group *g = 0;
211         getgrnam_r(g_conf->setgroup.c_str(), &gr, buf, sizeof(buf), &g);
212         if (!g) {
213           cerr << "unable to look up group '" << g_conf->setgroup << "'"
214                << ": " << cpp_strerror(errno) << std::endl;
215           exit(1);
216         }
217         gid = g->gr_gid;
218         gid_string = g_conf->setgroup;
219       }
220     }
221     if ((uid || gid) &&
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);
226       struct stat st;
227       int r = ::stat(match_path.c_str(), &st);
228       if (r < 0) {
229         cerr << "unable to stat setuser_match_path "
230              << g_conf->setuser_match_path
231              << ": " << cpp_strerror(errno) << std::endl;
232         exit(1);
233       }
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
239              << std::endl;
240         uid = 0;
241         gid = 0;
242         uid_string.erase();
243         gid_string.erase();
244       } else {
245         priv_ss << "setuser_match_path "
246                 << match_path << " owned by "
247                 << st.st_uid << ":" << st.st_gid << ". ";
248       }
249     }
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)
255              << std::endl;
256         exit(1);
257       }
258       if (setuid(uid) != 0) {
259         cerr << "unable to setuid " << uid << ": " << cpp_strerror(errno)
260              << std::endl;
261         exit(1);
262       }
263       priv_ss << "set uid:gid to " << uid << ":" << gid << " (" << uid_string << ":" << gid_string << ")";
264     } else {
265       priv_ss << "deferred set uid:gid to " << uid << ":" << gid << " (" << uid_string << ":" << gid_string << ")";
266     }
267   }
268
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;
272   }
273 #endif
274
275   // Expand metavariables. Invoke configuration observers. Open log file.
276   g_conf->apply_changes(NULL);
277
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;
284     }
285   }
286
287   register_assert_context(g_ceph_context);
288
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();
292
293   if (priv_ss.str().length()) {
294     dout(0) << priv_ss.str() << dendl;
295   }
296
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());
310   }
311
312   // Now we're ready to complain about config file parse errors
313   g_conf->complain_about_parse_errors(g_ceph_context);
314
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];
319     (void)s;
320     // cppcheck-suppress memleak
321   }
322
323   if (code_env == CODE_ENVIRONMENT_DAEMON && !(flags & CINIT_FLAG_NO_DAEMON_ACTIONS))
324     output_ceph_version();
325
326   if (g_ceph_context->crush_location.init_on_startup()) {
327     cerr << " failed to init_on_startup : " << cpp_strerror(errno) << std::endl;
328     exit(1);
329   }
330
331   return boost::intrusive_ptr<CephContext>{g_ceph_context, false};
332 }
333
334 void intrusive_ptr_add_ref(CephContext* cct)
335 {
336   cct->get();
337 }
338
339 void intrusive_ptr_release(CephContext* cct)
340 {
341   cct->put();
342 }
343
344 void global_print_banner(void)
345 {
346   output_ceph_version();
347 }
348
349 int global_init_prefork(CephContext *cct)
350 {
351   if (g_code_env != CODE_ENVIRONMENT_DAEMON)
352     return -1;
353
354   const md_config_t *conf = cct->_conf;
355   if (!conf->daemonize) {
356
357     if (pidfile_write(conf) < 0)
358       exit(1);
359
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());
364     }
365
366     return -1;
367   }
368
369   cct->notify_pre_fork();
370   // stop log thread
371   cct->_log->flush();
372   cct->_log->stop();
373   return 0;
374 }
375
376 void global_init_daemonize(CephContext *cct)
377 {
378   if (global_init_prefork(cct) < 0)
379     return;
380
381 #if !defined(_AIX)
382   int ret = daemon(1, 1);
383   if (ret) {
384     ret = errno;
385     derr << "global_init_daemonize: BUG: daemon error: "
386          << cpp_strerror(ret) << dendl;
387     exit(1);
388   }
389  
390   global_init_postfork_start(cct);
391   global_init_postfork_finish(cct);
392 #else
393 # warning daemon not supported on aix
394 #endif
395 }
396
397 void global_init_postfork_start(CephContext *cct)
398 {
399   // restart log thread
400   cct->_log->start();
401   cct->notify_post_fork();
402
403   /* This is the old trick where we make file descriptors 0, 1, and possibly 2
404    * point to /dev/null.
405    *
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
409    * supposed to.
410    */
411   VOID_TEMP_FAILURE_RETRY(close(STDIN_FILENO));
412   if (open("/dev/null", O_RDONLY) < 0) {
413     int err = errno;
414     derr << "global_init_daemonize: open(/dev/null) failed: error "
415          << err << dendl;
416     exit(1);
417   }
418   VOID_TEMP_FAILURE_RETRY(close(STDOUT_FILENO));
419   if (open("/dev/null", O_RDONLY) < 0) {
420     int err = errno;
421     derr << "global_init_daemonize: open(/dev/null) failed: error "
422          << err << dendl;
423     exit(1);
424   }
425
426   const md_config_t *conf = cct->_conf;
427   if (pidfile_write(conf) < 0)
428     exit(1);
429
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());
434   }
435 }
436
437 void global_init_postfork_finish(CephContext *cct)
438 {
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.
442    */
443   if (!(cct->get_init_flags() & CINIT_FLAG_NO_CLOSE_STDERR)) {
444     int ret = global_init_shutdown_stderr(cct);
445     if (ret) {
446       derr << "global_init_daemonize: global_init_shutdown_stderr failed with "
447            << "error code " << ret << dendl;
448       exit(1);
449     }
450   }
451   ldout(cct, 1) << "finished global_init_daemonize" << dendl;
452 }
453
454
455 void global_init_chdir(const CephContext *cct)
456 {
457   const md_config_t *conf = cct->_conf;
458   if (conf->chdir.empty())
459     return;
460   if (::chdir(conf->chdir.c_str())) {
461     int err = errno;
462     derr << "global_init_chdir: failed to chdir to directory: '"
463          << conf->chdir << "': " << cpp_strerror(err) << dendl;
464   }
465 }
466
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
469  * available one.
470  */
471 int global_init_shutdown_stderr(CephContext *cct)
472 {
473   VOID_TEMP_FAILURE_RETRY(close(STDERR_FILENO));
474   if (open("/dev/null", O_RDONLY) < 0) {
475     int err = errno;
476     derr << "global_init_shutdown_stderr: open(/dev/null) failed: error "
477          << err << dendl;
478     return 1;
479   }
480   cct->_log->set_stderr_level(-1, -1);
481   return 0;
482 }
483
484 int global_init_preload_erasure_code(const CephContext *cct)
485 {
486   const md_config_t *conf = cct->_conf;
487   string plugins = conf->osd_erasure_code_plugins;
488
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();
494        ++i) {
495         string plugin_name = *i;
496         string replacement = "";
497
498         if (plugin_name == "jerasure_generic" || 
499             plugin_name == "jerasure_sse3" ||
500             plugin_name == "jerasure_sse4" ||
501             plugin_name == "jerasure_neon") {
502           replacement = "jerasure";
503         }
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";
509         }
510
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;
515         }
516   }
517
518   stringstream ss;
519   int r = ErasureCodePluginRegistry::instance().preload(
520     plugins,
521     conf->get_val<std::string>("erasure_code_dir"),
522     &ss);
523   if (r)
524     derr << ss.str() << dendl;
525   else
526     dout(0) << ss.str() << dendl;
527   return r;
528 }