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