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  	
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  	{
(1) Event open_fn: Returning handle opened by "open".
(2) Event var_assign: Assigning: "newfd" = handle returned from "open("/dev/null", 524288)".
Also see events: [noescape][leaked_handle]
461  	  int newfd = open("/dev/null", O_RDONLY|O_CLOEXEC);
(3) Event cond_false: Condition "newfd < 0", taking false branch.
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;
(4) Event if_end: End of if statement.
467  	  }
468  	  // atomically dup newfd to target fd.  target fd is implicitly closed if
469  	  // open and atomically replaced; see man dup2
(5) Event noescape: Resource "newfd" is not freed or pointed-to in "dup2".
Also see events: [open_fn][var_assign][leaked_handle]
470  	  int r = dup2(newfd, fd);
(6) Event cond_true: Condition "r < 0", taking true branch.
471  	  if (r < 0) {
472  	    int err = errno;
(7) Event cond_true: Condition "should_gather", taking true branch.
473  	    lderr(cct) << __func__ << " failed to dup2 " << fd << ": "
474  		       << cpp_strerror(err) << dendl;
(8) Event leaked_handle: Handle variable "newfd" going out of scope leaks the handle.
Also see events: [open_fn][var_assign][noescape]
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