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