bes  Updated for version 3.20.5
daemon.cc
1 // daemon.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <unistd.h> // for getopt fork setsid execvp access geteuid
34 
35 #include <grp.h> // for getgrnam
36 #include <pwd.h> // for getpwnam
37 
38 #include <sys/wait.h> // for waitpid
39 #include <sys/types.h>
40 #include <sys/stat.h> // for chmod
41 #include <ctype.h> // for isdigit
42 #include <signal.h>
43 
44 #include <fstream>
45 #include <iostream>
46 #include <string>
47 #include <sstream>
48 #include <cstring>
49 #include <cstdlib>
50 #include <cerrno>
51 
52 using std::ifstream;
53 using std::ofstream;
54 using std::cout;
55 using std::endl;
56 using std::cerr;
57 using std::flush;
58 using std::string;
59 
60 #include "config.h"
61 #include "ServerExitConditions.h"
62 #include "SocketListener.h"
63 #include "TcpSocket.h"
64 #include "UnixSocket.h"
65 #include "PPTServer.h"
66 #include "BESModuleApp.h"
67 #include "DaemonCommandHandler.h"
68 #include "BESServerUtils.h"
69 #include "BESScrub.h"
70 #include "BESError.h"
71 #include "BESDebug.h"
72 #include "TheBESKeys.h"
73 #include "BESLog.h"
74 #include "BESDaemonConstants.h"
75 
76 #define BES_SERVER "/beslistener"
77 #define BES_SERVER_PID "/bes.pid"
78 #define DAEMON_PORT_STR "BES.DaemonPort"
79 #define DAEMON_UNIX_SOCK_STR "BES.DaemonUnixSocket"
80 
81 // Defined in setgroups.c
82 extern "C" int set_sups(const int target_sups_size, const gid_t* const target_sups_list);
83 
84 // These are called from DaemonCommandHandler
85 void block_signals();
86 void unblock_signals();
87 int start_master_beslistener();
88 bool stop_all_beslisteners(int sig);
89 
90 static string daemon_name;
91 
92 // This two variables are set by load_names
93 static string beslistener_path;
94 static string file_for_daemon_pid;
95 
96 // This can be used to see if HUP or TERM has been sent to the master bes
97 volatile int master_beslistener_status = BESLISTENER_STOPPED;
98 volatile int num_children = 0;
99 static volatile int master_beslistener_pid = -1; // This is also the process group id
100 
101 typedef map<string, string> arg_map;
102 static arg_map global_args;
103 static string debug_sink = "";
104 
105 static TcpSocket *my_socket = 0;
106 static UnixSocket *unix_socket = 0;
107 static PPTServer *command_server = 0;
108 
109 // These are set to 1 by their respective handlers and then processed in the
110 // signal processing loop. jhrg 3/5/14
111 static volatile sig_atomic_t sigchild = 0;
112 static volatile sig_atomic_t sigterm = 0;
113 static volatile sig_atomic_t sighup = 0;
114 
115 static string errno_str(const string &msg)
116 {
117  ostringstream oss;
118  oss << daemon_name << msg;
119  const char *perror_string = strerror(errno);
120  if (perror_string) oss << perror_string;
121  oss << endl;
122  return oss.str();
123 }
124 
133 static int pr_exit(int status)
134 {
135  if (WIFEXITED(status)) {
136  switch (WEXITSTATUS(status)) {
137  case SERVER_EXIT_NORMAL_SHUTDOWN:
138  return 0;
139 
140  case SERVER_EXIT_FATAL_CANNOT_START:
141  cerr << daemon_name << ": server cannot start, exited with status " << WEXITSTATUS(status) << endl;
142  cerr << "Please check all error messages " << "and adjust server installation" << endl;
143  return 1;
144 
145  case SERVER_EXIT_ABNORMAL_TERMINATION:
146  cerr << daemon_name << ": abnormal server termination, exited with status " << WEXITSTATUS(status) << endl;
147  return 1;
148 
149  case SERVER_EXIT_RESTART:
150  cerr << daemon_name << ": server has been requested to re-start." << endl;
151  return SERVER_EXIT_RESTART;
152 
153  default:
154  return 1;
155  }
156  }
157  else if (WIFSIGNALED(status)) {
158  cerr << daemon_name << ": abnormal server termination, signaled with signal number " << WTERMSIG(status)
159  << endl;
160 #ifdef WCOREDUMP
161  if (WCOREDUMP(status)) {
162  cerr << daemon_name << ": server dumped core." << endl;
163  return 1;
164  }
165 #endif
166  return 1;
167  }
168  else if (WIFSTOPPED(status)) {
169  cerr << daemon_name << ": abnormal server termination, stopped with signal number " << WSTOPSIG(status) << endl;
170  return 1;
171  }
172 
173  return 0;
174 }
175 
180 void block_signals()
181 {
182  sigset_t set;
183  sigemptyset(&set);
184  sigaddset(&set, SIGCHLD);
185  sigaddset(&set, SIGHUP);
186  sigaddset(&set, SIGTERM);
187 
188  if (sigprocmask(SIG_BLOCK, &set, 0) < 0) {
189  cerr << errno_str(": sigprocmask error, blocking signals in stop_all_beslisteners ");
190  }
191 }
192 
194 void unblock_signals()
195 {
196  sigset_t set;
197  sigemptyset(&set);
198  sigaddset(&set, SIGCHLD);
199  sigaddset(&set, SIGHUP);
200  sigaddset(&set, SIGTERM);
201 
202  if (sigprocmask(SIG_UNBLOCK, &set, 0) < 0) {
203  cerr << errno_str(": sigprocmask error unblocking signals in stop_all_beslisteners ");
204  }
205 }
206 
220 bool stop_all_beslisteners(int sig)
221 {
222  BESDEBUG("besdaemon", "besdaemon: stopping listeners" << endl);
223 
224  block_signals();
225 
226  BESDEBUG("besdaemon", "besdaemon: master_beslistener_pid " << master_beslistener_pid << endl);
227  // Send 'sig' to all members of the process group with/of the master bes.
228  // The master beslistener pid is the group id of all of the beslisteners.
229  int status = killpg(master_beslistener_pid, sig);
230  switch (status) {
231  case EINVAL:
232  cerr << "The sig argument is not a valid signal number." << endl;
233  break;
234 
235  case EPERM:
236  cerr
237  << "The sending process is not the super-user and one or more of the target processes has an effective user ID different from that of the sending process."
238  << endl;
239  break;
240 
241  case ESRCH:
242  cerr << "No process can be found in the process group specified by the process group ("
243  << master_beslistener_pid << ")." << endl;
244  break;
245 
246  default: // No error
247  break;
248  }
249 
250  bool mbes_status_caught = false;
251  int pid;
252  while ((pid = wait(&status)) > 0) {
253  BESDEBUG("besdaemon", "besdaemon: caught listener: " << pid << " raw status: " << status << endl);
254  if (pid == master_beslistener_pid) {
255  master_beslistener_status = pr_exit(status);
256  mbes_status_caught = true;
257  BESDEBUG("besdaemon",
258  "besdaemon: caught master beslistener: " << pid << " status: " << master_beslistener_status << endl);
259  }
260  }
261 
262  BESDEBUG("besdaemon", "besdaemon: done catching listeners (last pid:" << pid << ")" << endl);
263 
264  unblock_signals();
265  BESDEBUG("besdaemon", "besdaemon: unblocking signals " << endl);
266  return mbes_status_caught;
267 }
268 
276 char **update_beslistener_args()
277 {
278  char **arguments = new char*[global_args.size() * 2 + 1];
279 
280  // Marshal the arguments to the listener from the command line
281  // arguments to the daemon
282  arguments[0] = strdup(global_args["beslistener"].c_str());
283 
284  int i = 1;
285  arg_map::iterator it;
286  for (it = global_args.begin(); it != global_args.end(); ++it) {
287  BESDEBUG("besdaemon", "besdaemon; global_args " << (*it).first << " => " << (*it).second << endl);
288  // Build the complete command line args for the beslistener, with
289  // special case code for -d and to omit the 'beslistener' line
290  // since it's already set in arguments[0].
291  if ((*it).first == "-d") {
292  arguments[i++] = strdup("-d");
293  // This is where the current debug/log settings are grabbed and
294  // used to build the correct '-d' option value for the new
295  // beslistener.
296  string debug_opts = debug_sink + "," + BESDebug::GetOptionsString();
297  arguments[i++] = strdup(debug_opts.c_str());
298  }
299  else if ((*it).first != "beslistener") {
300  arguments[i++] = strdup((*it).first.c_str());
301  arguments[i++] = strdup((*it).second.c_str());
302  }
303  }
304  arguments[i] = 0; // terminal null
305 
306  return arguments;
307 }
308 
321 int start_master_beslistener()
322 {
323  // The only certain way to know that the beslistener master has started is
324  // to pass back its status once it is initialized. Use a pipe for that.
325  int pipefd[2];
326  if (pipe(pipefd) < 0) {
327  cerr << errno_str(": pipe error ");
328  return 0;
329  }
330 
331  int pid;
332  if ((pid = fork()) < 0) {
333  cerr << errno_str(": fork error ");
334  return 0;
335  }
336  else if (pid == 0) { // child process (the master beslistener)
337  // See 'int ServerApp::run()' for the place where the program exec'd
338  // below writes the pid value to the pipe.
339 
340  close(pipefd[0]); // Close the read end of the pipe in the child
341 
342  // dup2 so we know the FD to write to in the child (the beslistener).
343  // BESLISTENER_PIPE_FD is '1' which is stdout; since beslistener is a
344  // daemon process both stdin and out have been closed so these descriptors
345  // are available. Using higher numbers can cause problems (see ticket
346  // 1783). jhrg 7/15/11
347  if (dup2(pipefd[1], BESLISTENER_PIPE_FD) != BESLISTENER_PIPE_FD) {
348  cerr << errno_str(": dup2 error ");
349  return 0;
350  }
351 
352  // We don't have to free this because this is a different process
353  // than the parent.
354  char **arguments = update_beslistener_args();
355 
356  BESDEBUG("besdaemon", "Starting: " << arguments[0] << endl);
357 
358  // Close the socket for the besdaemon here. This keeps it from being
359  // passed into the master beslistener and then entering the state
360  // CLOSE_WAIT once the besdaemon's client closes it's end.
361  if (command_server) command_server->closeConnection();
362 
363  // This is where beslistener - the master listener - is started
364  execvp(arguments[0], arguments);
365 
366  // if we are still here, it's an error...
367  cerr << errno_str(": mounting listener, subprocess failed: ");
368  exit(1); //NB: This exits from the child process.
369  }
370 
371  // parent process (the besdaemon)
372 
373  // The daemon records the pid of the master beslistener, but only does so
374  // when that process writes its status to the pipe 'fd'.
375 
376  close(pipefd[1]); // close the write end of the pipe in the parent.
377 
378  BESDEBUG("besdaemon", "besdaemon: master beslistener pid: " << pid << endl);
379 
380  // Read the status from the child (beslistener).
381  int beslistener_start_status;
382  int status = read(pipefd[0], &beslistener_start_status, sizeof(beslistener_start_status));
383 
384  if (status < 0) {
385  cerr << "Could not read master beslistener status; the master pid was not changed." << endl;
386  close(pipefd[0]);
387  return 0;
388  }
389  else if (beslistener_start_status != BESLISTENER_RUNNING) {
390  cerr << "The beslistener status is not 'BESLISTENER_RUNNING' (it is '" << beslistener_start_status
391  << "') the master pid was not changed." << endl;
392  close(pipefd[0]);
393  return 0;
394  }
395  else {
396  BESDEBUG("besdaemon", "besdaemon: master beslistener start status: " << beslistener_start_status << endl);
397  // Setting master_beslistener_pid here and not forcing callers to use the
398  // return value means that this global can be local to this file.
399  master_beslistener_pid = pid;
400  master_beslistener_status = BESLISTENER_RUNNING;
401  }
402 
403  close(pipefd[0]);
404  return pid;
405 }
406 
410 static void cleanup_resources()
411 {
412  // TOCTOU error. Since the code ignores the error code from
413  // remove(), we might as well drop the test. We could test for an
414  // error and print a warning to the log... jhrg 10/23/15
415 #if 0
416  if (!access(file_for_daemon_pid.c_str(), F_OK)) {
417  (void) remove(file_for_daemon_pid.c_str());
418  }
419 #endif
420 
421  (void) remove(file_for_daemon_pid.c_str());
422 }
423 
424 // Note that SIGCHLD, SIGTERM and SIGHUP are blocked while in these three
425 // signal handlers below.
426 
427 static void CatchSigChild(int signal)
428 {
429  if (signal == SIGCHLD) {
430  sigchild = 1;
431  }
432 }
433 
434 static void CatchSigHup(int signal)
435 {
436  if (signal == SIGHUP) {
437  sighup = 1;
438  }
439 }
440 
441 static void CatchSigTerm(int signal)
442 {
443  if (signal == SIGTERM) {
444  sigterm = 1;
445  }
446 }
447 
448 static void process_signals()
449 {
450  block_signals();
451 
452  // Process SIGCHLD. This is used to detect if the HUP signal was sent to the
453  // master listener and it has returned SERVER_EXIT_RESTART by recording
454  // that value in the global 'master_beslistener_status'. Other code needs
455  // to test that (static) global to see if the beslistener should be restarted.
456  if (sigchild) {
457  int status;
458  int pid = wait(&status);
459 
460  // Decode and record the exit status, but only if it really is the
461  // master beslistener this daemon is using. If two or more Start commands
462  // are sent in a row, a master beslistener will start, fail to bind to
463  // the port (because another master beslstener is already bound to it)
464  // and exit. We don't want to record that second process's exit status here.
465  if (pid == master_beslistener_pid) master_beslistener_status = pr_exit(status);
466 
467  sigchild = 0;
468  }
469 
470  // The two following signals implement a simple stop/restart behavior
471  // for the daemon. The TERM signal (which is the default for the 'kill'
472  // command) is used to stop the entire server, including the besdaemon. The HUP
473  // signal is used to stop all beslisteners and then restart the master
474  // beslistener, forcing a re-read of the config file. Note that the daemon
475  // does not re-read the config file.
476 
477  // When the daemon gets the HUP signal, it forwards that onto each beslistener.
478  // They then all exit, returning the 'restart' code so that the daemon knows
479  // to restart the master beslistener.
480  if (sighup) {
481  // restart the beslistener(s); read their exit status
482  stop_all_beslisteners(SIGHUP);
483 
484  // FIXME jhrg 3/5/14
485  if (start_master_beslistener() == 0) {
486  cerr << "Could not restart the master beslistener." << endl;
487  stop_all_beslisteners(SIGTERM);
488  cleanup_resources();
489  exit(1);
490  }
491 
492  sighup = 0;
493  }
494 
495  // When TERM (the default for 'kill') is sent to this process, send it also
496  // to each beslistener. This will cause the beslisteners to all exit with a zero
497  // value (the code for 'do not restart').
498  if (sigterm) {
499  // Stop all of the beslistener(s); read their exit status
500  stop_all_beslisteners(SIGTERM);
501 
502  // FIXME jhrg 3/5/14
503  cleanup_resources();
504  // Once all the child exit status values are read, exit the daemon
505  exit(0);
506  }
507 
508  unblock_signals();
509 }
510 
522 static int start_command_processor(DaemonCommandHandler &handler)
523 {
524  BESDEBUG("besdaemon", "besdaemon: Starting command processor." << endl);
525 
526  try {
527  SocketListener listener;
528 
529  string port_str;
530  bool port_found;
531  int port = 0;
532  TheBESKeys::TheKeys()->get_value(DAEMON_PORT_STR, port_str, port_found);
533  if (port_found) {
534  char *ptr;
535  port = strtol(port_str.c_str(), &ptr, 10);
536  if (port == 0) {
537  cerr << "Invalid port number for daemon command interface: " << port_str << endl;
538  exit(1);
539  }
540  }
541 
542  if (port) {
543  BESDEBUG("besdaemon", "besdaemon: listening on port: " << port << endl);
544  my_socket = new TcpSocket(port);
545  listener.listen(my_socket);
546  }
547 
548  string usock_str;
549  bool usock_found;
550  TheBESKeys::TheKeys()->get_value(DAEMON_UNIX_SOCK_STR, usock_str, usock_found);
551 
552  if (!usock_str.empty()) {
553  BESDEBUG("besdaemon", "besdaemon: listening on unix socket: " << usock_str << endl);
554  unix_socket = new UnixSocket(usock_str);
555  listener.listen(unix_socket);
556  }
557 
558  if (!port_found && !usock_found) {
559  BESDEBUG("besdaemon", "Neither a port nor a unix socket was set for the daemon command interface." << endl);
560  return 0;
561  }
562 
563  BESDEBUG("besdaemon", "besdaemon: starting command interface on port: " << port << endl);
564  command_server = new PPTServer(&handler, &listener, /*is_secure*/false);
565 
566  // Once initialized, 'handler' loops until it's told to exit.
567  while (true) {
568  process_signals();
569 
570  command_server->initConnection();
571  }
572 
573  // Once the handler exits, close sockets and free memory
574  command_server->closeConnection();
575  }
576  catch (BESError &se) {
577  cerr << "daemon: " << se.get_message() << endl;
578  }
579  catch (...) {
580  cerr << "daemon: " << "caught unknown exception" << endl;
581  }
582 
583  delete command_server;
584  command_server = 0;
585 
586  // delete closes the sockets
587  delete my_socket;
588  my_socket = 0;
589  delete unix_socket;
590  unix_socket = 0;
591 
592  // When/if the command interpreter exits, stop the all listeners.
593  stop_all_beslisteners(SIGTERM);
594 
595  return 1;
596 }
597 
606 static void register_signal_handlers()
607 {
608  struct sigaction act;
609 
610  // block chld, term and hup in the handlers
611  sigemptyset(&act.sa_mask);
612  sigaddset(&act.sa_mask, SIGCHLD);
613  sigaddset(&act.sa_mask, SIGTERM);
614  sigaddset(&act.sa_mask, SIGHUP);
615  act.sa_flags = 0;
616 #ifdef SA_RESTART
617  BESDEBUG("besdaemon", "besdaemon: setting restart for sigchld." << endl);
618  act.sa_flags |= SA_RESTART;
619 #endif
620 
621  act.sa_handler = CatchSigChild;
622  if (sigaction(SIGCHLD, &act, 0)) {
623  cerr << "Could not register a handler to catch beslistener status." << endl;
624  exit(1);
625  }
626 
627  act.sa_handler = CatchSigTerm;
628  if (sigaction(SIGTERM, &act, 0) < 0) {
629  cerr << "Could not register a handler to catch the terminate signal." << endl;
630  exit(1);
631  }
632 
633  act.sa_handler = CatchSigHup;
634  if (sigaction(SIGHUP, &act, 0) < 0) {
635  cerr << "Could not register a handler to catch the hang-up signal." << endl;
636  exit(1);
637  }
638 }
639 
646 static int daemon_init()
647 {
648  pid_t pid;
649  if ((pid = fork()) < 0) // error
650  return -1;
651  else if (pid != 0) // parent exits
652  exit(0);
653  setsid(); // child establishes its own process group
654  return 0;
655 }
656 
663 static void store_daemon_id(int pid)
664 {
665  ofstream f(file_for_daemon_pid.c_str());
666  if (!f) {
667  cerr << errno_str(": unable to create pid file " + file_for_daemon_pid + ": ");
668  }
669  else {
670  // systemd/systemctl (CentOS 7 and elsewhere) expects just a PID number as text.
671  // jhrg 1/31/19
672  // f << "PID: " << pid << " UID: " << getuid() << endl;
673  f << pid << endl;
674  f.close();
675  mode_t new_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
676  (void) chmod(file_for_daemon_pid.c_str(), new_mode);
677  }
678 }
679 
688 static bool load_names(const string &install_dir, const string &pid_dir)
689 {
690  string bindir = "/bin";
691  if (!pid_dir.empty()) {
692  file_for_daemon_pid = pid_dir;
693  }
694 
695  if (!install_dir.empty()) {
696  beslistener_path = install_dir;
697  beslistener_path += bindir;
698  if (file_for_daemon_pid.empty()) {
699  file_for_daemon_pid = install_dir + "/var/run";
700  // Added jhrg 2/9/12 ... and removed 1/31/19. The special dir breaks
701  // systemctl/systemd on CentOS 7. We might be able to tweak things so
702  // it would work, but I'm switching back to what other daemons do. jhrg
703  // file_for_daemon_pid = install_dir + "/var/run/bes";
704 
705  }
706  }
707  else {
708  string prog = daemon_name;
709  string::size_type slash = prog.find_last_of('/');
710  if (slash != string::npos) {
711  beslistener_path = prog.substr(0, slash);
712  slash = prog.find_last_of('/');
713  if (slash != string::npos) {
714  string root = prog.substr(0, slash);
715  if (file_for_daemon_pid.empty()) {
716  file_for_daemon_pid = root + "/var/run";
717  // Added jhrg 2/9/12. See about 1/31/19 jhrg
718  // file_for_daemon_pid = root + "/var/run/bes";
719  }
720  }
721  else {
722  if (file_for_daemon_pid.empty()) {
723  file_for_daemon_pid = beslistener_path;
724  }
725  }
726  }
727  }
728 
729  if (beslistener_path == "") {
730  beslistener_path = ".";
731  if (file_for_daemon_pid.empty()) {
732  file_for_daemon_pid = "./run";
733  }
734  }
735 
736  beslistener_path += BES_SERVER;
737  file_for_daemon_pid += BES_SERVER_PID;
738 
739  if (access(beslistener_path.c_str(), F_OK) != 0) {
740  cerr << daemon_name << ": cannot find " << beslistener_path << endl
741  << "Please either pass -i <install_dir> on the command line." << endl;
742  return false;
743  }
744 
745  // Record the name for use when building the arg list for the beslistener
746  global_args["beslistener"] = beslistener_path;
747 
748  return true;
749 }
750 
751 static void set_group_id()
752 {
753 #if !defined(OS2) && !defined(TPF)
754  // OS/2 and TPF don't support groups.
755 
756  // get group id or name from BES configuration file
757  // If BES.Group begins with # then it is a group id,
758  // else it is a group name and look up the id.
759  BESDEBUG("server", "beslistener: Setting group id ... " << endl);
760  bool found = false;
761  string key = "BES.Group";
762  string group_str;
763  try {
764  TheBESKeys::TheKeys()->get_value(key, group_str, found);
765  }
766  catch (BESError &e) {
767  BESDEBUG("server", "beslistener: FAILED" << endl);
768  string err = string("FAILED: ") + e.get_message();
769  cerr << err << endl;
770  LOG(err << endl);
771  exit(SERVER_EXIT_FATAL_CANNOT_START);
772  }
773 
774  if (!found || group_str.empty()) {
775  BESDEBUG("server", "beslistener: FAILED" << endl);
776  string err = "FAILED: Group not specified in BES configuration file";
777  cerr << err << endl;
778  LOG(err << endl);
779  exit(SERVER_EXIT_FATAL_CANNOT_START);
780  }
781  BESDEBUG("server", "to " << group_str << " ... " << endl);
782 
783  gid_t new_gid = 0;
784  if (group_str[0] == '#') {
785  // group id starts with a #, so is a group id
786  const char *group_c = group_str.c_str();
787  group_c++;
788  new_gid = atoi(group_c);
789  }
790  else {
791  // specified group is a group name
792  struct group *ent;
793  ent = getgrnam(group_str.c_str());
794  if (!ent) {
795  BESDEBUG("server", "beslistener: FAILED" << endl);
796  string err = (string) "FAILED: Group " + group_str + " does not exist";
797  cerr << err << endl;
798  LOG(err << endl);
799  exit(SERVER_EXIT_FATAL_CANNOT_START);
800  }
801  new_gid = ent->gr_gid;
802  }
803 
804  if (new_gid < 1) {
805  BESDEBUG("server", "beslistener: FAILED" << endl);
806  ostringstream err;
807  err << "FAILED: Group id " << new_gid << " not a valid group id for BES";
808  cerr << err.str() << endl;
809  LOG(err.str() << endl);
810  exit(SERVER_EXIT_FATAL_CANNOT_START);
811  }
812 
813  BESDEBUG("server", "to id " << new_gid << " ... " << endl);
814  if (setgid(new_gid) == -1) {
815  BESDEBUG("server", "beslistener: FAILED" << endl);
816  ostringstream err;
817  err << "FAILED: unable to set the group id to " << new_gid;
818  cerr << err.str() << endl;
819  LOG(err.str() << endl);
820  exit(SERVER_EXIT_FATAL_CANNOT_START);
821  }
822 
823  BESDEBUG("server", "OK" << endl);
824 #else
825  BESDEBUG( "server", "beslistener: Groups not supported in this OS" << endl );
826 #endif
827 }
828 
829 static void set_user_id()
830 {
831  BESDEBUG("server", "beslistener: Setting user id ... " << endl);
832 
833  // Get user name or id from the BES configuration file.
834  // If the BES.User value begins with # then it is a user
835  // id, else it is a user name and need to look up the
836  // user id.
837  bool found = false;
838  string key = "BES.User";
839  string user_str;
840  try {
841  TheBESKeys::TheKeys()->get_value(key, user_str, found);
842  }
843  catch (BESError &e) {
844  BESDEBUG("server", "beslistener: FAILED" << endl);
845  string err = (string) "FAILED: " + e.get_message();
846  cerr << err << endl;
847  LOG(err << endl);
848  exit(SERVER_EXIT_FATAL_CANNOT_START);
849  }
850 
851  if (!found || user_str.empty()) {
852  BESDEBUG("server", "beslistener: FAILED" << endl);
853  string err = (string) "FAILED: User not specified in BES config file";
854  cerr << err << endl;
855  LOG(err << endl);
856  exit(SERVER_EXIT_FATAL_CANNOT_START);
857  }
858  BESDEBUG("server", "to " << user_str << " ... " << endl);
859 
860  uid_t new_id = 0;
861  if (user_str[0] == '#') {
862  const char *user_str_c = user_str.c_str();
863  user_str_c++;
864  new_id = atoi(user_str_c);
865  }
866  else {
867  struct passwd *ent;
868  ent = getpwnam(user_str.c_str());
869  if (!ent) {
870  BESDEBUG("server", "beslistener: FAILED" << endl);
871  string err = (string) "FAILED: Bad user name specified: " + user_str;
872  cerr << err << endl;
873  LOG(err << endl);
874  exit(SERVER_EXIT_FATAL_CANNOT_START);
875  }
876  new_id = ent->pw_uid;
877  }
878 
879  // new user id cannot be root (0)
880  if (!new_id) {
881  BESDEBUG("server", "beslistener: FAILED" << endl);
882  string err = (string) "FAILED: BES cannot run as root";
883  cerr << err << endl;
884  LOG(err << endl);
885  exit(SERVER_EXIT_FATAL_CANNOT_START);
886  }
887 
888  // Right before we relinquish root, remove any 'supplementary groups'
889  //int set_sups(const int target_sups_size, const gid_t* const target_sups_list)
890  vector<gid_t> groups(1);
891  groups.at(0) = getegid();
892  if (set_sups(groups.size(), &groups[0]) == -1) {
893  BESDEBUG("server", "beslistener: FAILED" << endl);
894  ostringstream err;
895  err << "FAILED: Unable to relinquish supplementary groups (" << new_id << ")";
896  cerr << err.str() << endl;
897  LOG(err.str() << endl);
898  exit(SERVER_EXIT_FATAL_CANNOT_START);
899  }
900 
901  BESDEBUG("server", "to " << new_id << " ... " << endl);
902  if (setuid(new_id) == -1) {
903  BESDEBUG("server", "beslistener: FAILED" << endl);
904  ostringstream err;
905  err << "FAILED: Unable to set user id to " << new_id;
906  cerr << err.str() << endl;
907  LOG(err.str() << endl);
908  exit(SERVER_EXIT_FATAL_CANNOT_START);
909  }
910 
911  BESDEBUG("server", "OK" << endl);
912 }
913 
917 int main(int argc, char *argv[])
918 {
919  uid_t curr_euid = geteuid();
920 
921 #ifndef BES_DEVELOPER
922  // must be root to run this app and to set user id and group id later
923  if (curr_euid) {
924  cerr << "FAILED: Must be root to run BES" << endl;
925  exit(SERVER_EXIT_FATAL_CANNOT_START);
926  }
927 #else
928  cerr << "Developer Mode: Not testing if BES is run by root" << endl;
929 #endif
930 
931  daemon_name = "besdaemon";
932 
933  string install_dir;
934  string pid_dir;
935 
936  bool become_daemon = true;
937 
938  // there are 16 arguments allowed to the daemon, including the program
939  // name. 3 options do not have arguments and 6 have arguments
940  if (argc > 16) {
941  // the show_usage method exits
942  BESServerUtils::show_usage(daemon_name);
943  }
944 
945  try {
946  // Most of the argument processing is just for vetting the arguments
947  // that will be passed onto the beslistener(s), but we do grab some info
948  string config_file = "";
949  // argv[0] is the name of the program, so start num_args at 1
950  unsigned short num_args = 1;
951 
952  // If you change the getopt statement below, be sure to make the
953  // corresponding change in ServerApp.cc and besctl.in
954  int c = 0;
955  while ((c = getopt(argc, argv, "hvsd:c:p:u:i:r:n")) != -1) {
956  switch (c) {
957  case 'v': // version
958  BESServerUtils::show_version(daemon_name);
959  break;
960  case '?': // unknown option
961  case 'h': // help
962  BESServerUtils::show_usage(daemon_name);
963  break;
964  case 'n': // no-daemon (Do Not Become A daemon process)
965  become_daemon=false;
966  cerr << "Running in foreground!" << endl;
967  num_args++;
968  break;
969  case 'i': // BES install directory
970  install_dir = optarg;
971  if (BESScrub::pathname_ok(install_dir, true) == false) {
972  cout << "The specified install directory (-i option) "
973  << "is incorrectly formatted. Must be less than "
974  << "255 characters and include the characters " << "[0-9A-z_./-]" << endl;
975  return 1;
976  }
977  global_args["-i"] = install_dir;
978  num_args += 2;
979  break;
980  case 's': // secure server
981  global_args["-s"] = "";
982  num_args++;
983  break;
984  case 'r': // where to write the pid file
985  pid_dir = optarg;
986  if (BESScrub::pathname_ok(pid_dir, true) == false) {
987  cout << "The specified state directory (-r option) "
988  << "is incorrectly formatted. Must be less than "
989  << "255 characters and include the characters " << "[0-9A-z_./-]" << endl;
990  return 1;
991  }
992  global_args["-r"] = pid_dir;
993  num_args += 2;
994  break;
995  case 'c': // configuration file
996  config_file = optarg;
997  if (BESScrub::pathname_ok(config_file, true) == false) {
998  cout << "The specified configuration file (-c option) "
999  << "is incorrectly formatted. Must be less than "
1000  << "255 characters and include the characters " << "[0-9A-z_./-]" << endl;
1001  return 1;
1002  }
1003  global_args["-c"] = config_file;
1004  num_args += 2;
1005  break;
1006  case 'u': // unix socket
1007  {
1008  string check_path = optarg;
1009  if (BESScrub::pathname_ok(check_path, true) == false) {
1010  cout << "The specified unix socket (-u option) " << "is incorrectly formatted. Must be less than "
1011  << "255 characters and include the characters " << "[0-9A-z_./-]" << endl;
1012  return 1;
1013  }
1014  global_args["-u"] = check_path;
1015  num_args += 2;
1016  break;
1017  }
1018  case 'p': // TCP port
1019  {
1020  string port_num = optarg;
1021  for (unsigned int i = 0; i < port_num.length(); i++) {
1022  if (!isdigit(port_num[i])) {
1023  cout << "The specified port contains non-digit " << "characters: " << port_num << endl;
1024  return 1;
1025  }
1026  }
1027  global_args["-p"] = port_num;
1028  num_args += 2;
1029  }
1030  break;
1031  case 'd': // debug
1032  {
1033  string check_arg = optarg;
1034  if (BESScrub::command_line_arg_ok(check_arg) == false) {
1035  cout << "The specified debug options \"" << check_arg << "\" contains invalid characters" << endl;
1036  return 1;
1037  }
1038  BESDebug::SetUp(check_arg);
1039  global_args["-d"] = check_arg;
1040  debug_sink = check_arg.substr(0, check_arg.find(','));
1041  num_args += 2;
1042  break;
1043  }
1044  default:
1045  BESServerUtils::show_usage(daemon_name);
1046  break;
1047  }
1048  }
1049 
1050  // if the number of arguments is greater than the number of allowed arguments
1051  // then extra arguments were passed that aren't options. Show usage and
1052  // exit.
1053  if (argc > num_args) {
1054  cout << daemon_name << ": too many arguments passed to the BES";
1055  BESServerUtils::show_usage(daemon_name);
1056  }
1057 
1058  if (pid_dir.empty()) {
1059  pid_dir = install_dir;
1060  }
1061 
1062  // If the -c option was passed, set the config file name in TheBESKeys
1063  if (!config_file.empty()) {
1064  TheBESKeys::ConfigFile = config_file;
1065  }
1066 
1067  // If the -c option was not passed, but the -i option
1068  // was passed, then use the -i option to construct
1069  // the path to the config file
1070  if (config_file.empty() && !install_dir.empty()) {
1071  if (install_dir[install_dir.length() - 1] != '/') {
1072  install_dir += '/';
1073  }
1074  string conf_file = install_dir + "etc/bes/bes.conf";
1075  TheBESKeys::ConfigFile = conf_file;
1076  }
1077  }
1078  catch (BESError &e) {
1079  // (*BESLog::TheLog())
1080  // BESLog::TheLog throws exceptions...
1081  cerr << "Caught BES Error while processing the daemon's options: " << e.get_message() << endl;
1082  return 1;
1083  }
1084  catch (std::exception &e) {
1085  cerr << "Caught C++ error while processing the daemon's options: " << e.what() << endl;
1086  return 2;
1087  }
1088  catch (...) {
1089  cerr << "Caught unknown error while processing the daemon's options." << endl;
1090  return 3;
1091  }
1092 
1093  try {
1094  // Set the name of the listener and the file for the listener pid
1095  if (!load_names(install_dir, pid_dir)) return 1;
1096 
1097  if (!access(file_for_daemon_pid.c_str(), F_OK)) {
1098  ifstream temp(file_for_daemon_pid.c_str());
1099  cout << daemon_name << ": there seems to be a BES daemon already running at ";
1100  char buf[500];
1101  temp.getline(buf, 500);
1102  cout << buf << endl;
1103  temp.close();
1104  return 1;
1105  }
1106 
1107  if(become_daemon){
1108  daemon_init();
1109  }
1110 
1111  store_daemon_id(getpid());
1112 
1113  register_signal_handlers();
1114 
1115  // Load the modules in the conf file(s) so that the debug (log) contexts
1116  // will be available to the BESDebug singleton so we can tell the OLFS/HAI
1117  // about them. Then Register the 'besdaemon' context.
1118  BESModuleApp app;
1119  if (app.initialize(argc, argv) != 0) {
1120  cerr << "Could not initialize the modules to get the log contexts." << endl;
1121  }
1122  BESDebug::Register("besdaemon");
1123 
1124  // These are from the beslistener - they are valid contexts but are not
1125  // registered by a module. See ServerApp.cc
1126  BESDebug::Register("server");
1127  BESDebug::Register("ppt");
1128 
1129  if (curr_euid == 0) {
1130 #ifdef BES_DEVELOPER
1131  cerr << "Developer Mode: Running as root - setting group and user ids" << endl;
1132 #endif
1133  set_group_id();
1134  set_user_id();
1135  }
1136  else {
1137  cerr << "Developer Mode: Not setting group or user ids" << endl;
1138  }
1139 
1140  // The stuff in global_args is used whenever a call to start_master_beslistener()
1141  // is made, so any time the BESDebug contexts are changed, a change to the
1142  // global_args will change the way the the beslistener is started. In fact,
1143  // it's not limited to the debug stuff, but that's we're using it for now.
1144  // jhrg 6/16/11
1145 
1146  // The -d option was not given; add one setting up a default log sink using
1147  // the log file from the bes.conf file or the name "LOG".
1148  if (global_args.count("-d") == 0) {
1149  bool found = false;
1150  // string log_file_name;
1151  TheBESKeys::TheKeys()->get_value("BES.LogName", debug_sink, found);
1152  if (!found) {
1153  // This is a crude fallback that avoids a value without any name
1154  // for a log file (which would be a syntax error).
1155  global_args["-d"] = "cerr," + BESDebug::GetOptionsString();
1156  }
1157  else {
1158  // I use false for the 'created' flag so that subsequent changes to the
1159  // debug stream won't do odd things like delete the ostream pointer.
1160  // Note that the beslistener has to recognize that "LOG" means to use
1161  // the bes.log file for a debug/log sink
1162  BESDebug::SetStrm(BESLog::TheLog()->get_log_ostream(), false);
1163 
1164  global_args["-d"] = debug_sink + "," + BESDebug::GetOptionsString();
1165  }
1166  }
1167  // The option was given; use the token read from the options for the sink
1168  // so that the beslistener will open the correct thing.
1169  else {
1170  global_args["-d"] = debug_sink + "," + BESDebug::GetOptionsString();
1171  }
1172 
1173  // master_beslistener_pid is global so that the signal handlers can use it;
1174  // it is actually assigned a value in start_master_beslistener but it's
1175  // assigned here to make it clearer what's going on.
1176  master_beslistener_pid = start_master_beslistener();
1177  if (master_beslistener_pid == 0) {
1178  cerr << daemon_name << ": server cannot mount at first try (core dump). "
1179  << "Please correct problems on the process manager " << beslistener_path << endl;
1180  return master_beslistener_pid;
1181  }
1182 
1183  BESDEBUG("besdaemon", "besdaemon: master_beslistener_pid: " << master_beslistener_pid << endl);
1184  }
1185  catch (BESError &e) {
1186  // (*BESLog::TheLog())
1187  // BESLog::TheLog throws exceptions...
1188  cerr << "Caught BES Error during initialization: " << e.get_message() << endl;
1189  return 1;
1190  }
1191  catch (std::exception &e) {
1192  cerr << "Caught C++ error during initialization: " << e.what() << endl;
1193  return 2;
1194  }
1195  catch (...) {
1196  cerr << "Caught unknown error during initialization." << endl;
1197  return 3;
1198  }
1199 
1200  int status = 0;
1201  try {
1202  // start_command_processor() does not return unless all commands have been
1203  // processed and the daemon has been told to exit (status == 1) or the
1204  // bes.conf file was set so that the processor never starts (status == 0).
1206  status = start_command_processor(handler);
1207 
1208  // if the command processor does not start, drop into this loop which
1209  // implements the simple restart-on-HUP behavior of the daemon.
1210  if (status == 0) {
1211  bool done = false;
1212  while (!done) {
1213  pause();
1214 
1215  process_signals();
1216 
1217  BESDEBUG("besdaemon", "besdaemon: master_beslistener_status: " << master_beslistener_status << endl);
1218  if (master_beslistener_status == BESLISTENER_RESTART) {
1219  master_beslistener_status = BESLISTENER_STOPPED;
1220  // master_beslistener_pid = start_master_beslistener();
1221  start_master_beslistener();
1222  }
1223  // If the status is not 'restart' and not running, then exit loop
1224  else if (master_beslistener_status != BESLISTENER_RUNNING) {
1225  done = true;
1226  }
1227  }
1228  }
1229  }
1230  catch (BESError &e) {
1231  status = 1;
1232  // (*BESLog::TheLog())
1233  // BESLog::TheLog throws exceptions...
1234  cerr << "Caught BES Error while starting the command handler: " << e.get_message() << endl;
1235  }
1236  catch (std::exception &e) {
1237  status = 2;
1238  cerr << "Caught C++ error while starting the command handler: " << e.what() << endl;
1239  }
1240  catch (...) {
1241  status = 3;
1242  cerr << "Caught unknown error while starting the command handler." << endl;
1243  }
1244 
1245  BESDEBUG("besdaemon", "besdaemon: past the command processor start" << endl);
1246 
1247  cleanup_resources();
1248 
1249  return status;
1250 }
1251 
BESModuleApp::initialize
virtual int initialize(int argC, char **argV)
Load and initialize any BES modules.
Definition: BESModuleApp.cc:69
TheBESKeys::ConfigFile
static std::string ConfigFile
Definition: TheBESKeys.h:147
PPTServer
Definition: PPTServer.h:42
BESModuleApp
Base application object for all BES applications.
Definition: BESModuleApp.h:59
BESError::get_message
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:99
BESScrub::pathname_ok
static bool pathname_ok(const string &path, bool strict)
Does the string name a potentailly valid pathname? Test the given pathname to verfiy that it is a val...
Definition: BESScrub.cc:88
TcpSocket
Definition: TcpSocket.h:40
BESDebug::SetUp
static void SetUp(const std::string &values)
Sets up debugging for the bes.
Definition: BESDebug.cc:64
TheBESKeys::TheKeys
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:61
UnixSocket
Definition: UnixSocket.h:44
BESDebug::GetOptionsString
static std::string GetOptionsString()
Definition: BESDebug.cc:182
BESDebug::Register
static void Register(const std::string &flagName)
register the specified debug flag
Definition: BESDebug.h:138
BESScrub::command_line_arg_ok
static bool command_line_arg_ok(const string &arg)
sanitize command line arguments
Definition: BESScrub.cc:52
TheBESKeys::get_value
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:420
DaemonCommandHandler
Definition: DaemonCommandHandler.h:43
BESDebug::SetStrm
static void SetStrm(std::ostream *strm, bool created)
set the debug output stream to the specified stream
Definition: BESDebug.h:198
SocketListener
Definition: SocketListener.h:42
BESError
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
PPTServer::initConnection
virtual void initConnection()
Definition: PPTServer.cc:134