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