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