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