54#include "ServerExitConditions.h"
55#include "SocketListener.h"
57#include "UnixSocket.h"
59#include "BESModuleApp.h"
60#include "DaemonCommandHandler.h"
61#include "BESServerUtils.h"
65#include "TheBESKeys.h"
67#include "BESDaemonConstants.h"
70#define BES_SERVER "/beslistener"
71#define BES_SERVER_PID "/bes.pid"
72#define DAEMON_PORT_STR "BES.DaemonPort"
73#define DAEMON_UNIX_SOCK_STR "BES.DaemonUnixSocket"
78extern "C" int set_sups(
int target_sups_size, gid_t* target_sups_list);
82void unblock_signals();
83int start_master_beslistener();
84bool stop_all_beslisteners(
int sig);
86static string daemon_name;
89static string beslistener_path;
90static string file_for_daemon_pid;
93volatile int master_beslistener_status = BESLISTENER_STOPPED;
95volatile int num_children = 0;
97static volatile int master_beslistener_pid = -1;
99typedef map<string, string> arg_map;
100static arg_map global_args;
101static string debug_sink;
105static PPTServer *command_server =
nullptr;
109static volatile sig_atomic_t sigchild = 0;
110static volatile sig_atomic_t sigterm = 0;
111static volatile sig_atomic_t sighup = 0;
113static string errno_str(
const string &msg)
116 oss << daemon_name << msg;
117 const char *perror_string = strerror(errno);
118 if (perror_string) oss << perror_string;
131static int pr_exit(
int status)
133 if (WIFEXITED(status)) {
134 switch (WEXITSTATUS(status)) {
135 case SERVER_EXIT_NORMAL_SHUTDOWN:
138 case SERVER_EXIT_FATAL_CANNOT_START:
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;
143 case SERVER_EXIT_ABNORMAL_TERMINATION:
144 cerr << daemon_name <<
": abnormal server termination, exited with status " << WEXITSTATUS(status) << endl;
147 case SERVER_EXIT_RESTART:
148 cerr << daemon_name <<
": server has been requested to re-start." << endl;
149 return SERVER_EXIT_RESTART;
155 else if (WIFSIGNALED(status)) {
156 cerr << daemon_name <<
": abnormal server termination, signaled with signal number " << WTERMSIG(status)
159 if (WCOREDUMP(status)) {
160 cerr << daemon_name <<
": server dumped core." << endl;
166 else if (WIFSTOPPED(status)) {
167 cerr << daemon_name <<
": abnormal server termination, stopped with signal number " << WSTOPSIG(status) << endl;
182 sigaddset(&set, SIGCHLD);
183 sigaddset(&set, SIGHUP);
184 sigaddset(&set, SIGTERM);
186 if (sigprocmask(SIG_BLOCK, &set, 0) < 0) {
187 cerr << errno_str(
": sigprocmask error, blocking signals in stop_all_beslisteners ");
192void unblock_signals()
196 sigaddset(&set, SIGCHLD);
197 sigaddset(&set, SIGHUP);
198 sigaddset(&set, SIGTERM);
200 if (sigprocmask(SIG_UNBLOCK, &set, 0) < 0) {
201 cerr << errno_str(
": sigprocmask error unblocking signals in stop_all_beslisteners ");
218bool stop_all_beslisteners(
int sig)
220 BESDEBUG(
"besdaemon",
"besdaemon: stopping listeners" << endl);
224 BESDEBUG(
"besdaemon",
"besdaemon: master_beslistener_pid " << master_beslistener_pid << endl);
227 int status = killpg(master_beslistener_pid, sig);
230 cerr <<
"The sig argument is not a valid signal number." << endl;
235 <<
"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."
240 cerr <<
"No process can be found in the process group specified by the process group ("
241 << master_beslistener_pid <<
")." << endl;
248 bool mbes_status_caught =
false;
250 while ((pid = wait(&status)) > 0) {
251 BESDEBUG(
"besdaemon",
"besdaemon: caught listener: " << pid <<
" raw status: " << status << endl);
252 if (pid == master_beslistener_pid) {
253 master_beslistener_status = pr_exit(status);
254 mbes_status_caught =
true;
255 BESDEBUG(
"besdaemon",
256 "besdaemon: caught master beslistener: " << pid <<
" status: " << master_beslistener_status << endl);
260 BESDEBUG(
"besdaemon",
"besdaemon: done catching listeners (last pid:" << pid <<
")" << endl);
264 BESDEBUG(
"besdaemon",
"besdaemon: unblocking signals " << endl);
266 return mbes_status_caught;
276char **update_beslistener_args()
278 char **arguments =
new char*[global_args.size() * 2 + 1];
282 arguments[0] = strdup(global_args[
"beslistener"].c_str());
285 arg_map::iterator it;
286 for (it = global_args.begin(); it != global_args.end(); ++it) {
287 BESDEBUG(
"besdaemon",
"besdaemon; global_args " << (*it).first <<
" => " << (*it).second << endl);
291 if ((*it).first ==
"-d") {
292 arguments[i++] = strdup(
"-d");
297 arguments[i++] = strdup(debug_opts.c_str());
299 else if ((*it).first !=
"beslistener") {
300 arguments[i++] = strdup((*it).first.c_str());
301 arguments[i++] = strdup((*it).second.c_str());
321int start_master_beslistener()
326 if (pipe(pipefd) < 0) {
327 cerr << errno_str(
": pipe error ");
332 if ((pid = fork()) < 0) {
333 cerr << errno_str(
": fork error ");
347 if (dup2(pipefd[1], MASTER_TO_DAEMON_PIPE_FD) != MASTER_TO_DAEMON_PIPE_FD) {
348 cerr << errno_str(
": dup2 error ");
354 char **arguments = update_beslistener_args();
356 BESDEBUG(
"besdaemon",
"Starting: " << arguments[0] << endl);
361 if (command_server) command_server->closeConnection();
364 execvp(arguments[0], arguments);
367 cerr << errno_str(
": mounting listener, subprocess failed: ");
378 BESDEBUG(
"besdaemon",
"besdaemon: master beslistener pid: " << pid << endl);
381 int beslistener_start_status;
382 long status = read(pipefd[0], &beslistener_start_status,
sizeof(beslistener_start_status));
385 cerr <<
"Could not read master beslistener status; the master pid was not changed." << endl;
389 else if (beslistener_start_status != BESLISTENER_RUNNING) {
390 cerr <<
"The beslistener status is not 'BESLISTENER_RUNNING' (it is '" << beslistener_start_status
391 <<
"') the master pid was not changed." << endl;
396 BESDEBUG(
"besdaemon",
"besdaemon: master beslistener start status: " << beslistener_start_status << endl);
399 master_beslistener_pid = pid;
400 master_beslistener_status = BESLISTENER_RUNNING;
410static void cleanup_resources()
416 if (!access(file_for_daemon_pid.c_str(), F_OK)) {
417 (void) remove(file_for_daemon_pid.c_str());
421 (void) remove(file_for_daemon_pid.c_str());
427static void catch_sig_child(
int signal)
429 if (signal == SIGCHLD) {
434static void catch_sig_hup(
int signal)
436 if (signal == SIGHUP) {
441static void catch_sig_term(
int signal)
443 if (signal == SIGTERM) {
448static void process_signals()
458 int pid = wait(&status);
465 if (pid == master_beslistener_pid) master_beslistener_status = pr_exit(status);
482 stop_all_beslisteners(SIGHUP);
485 if (start_master_beslistener() == 0) {
486 cerr <<
"Could not restart the master beslistener." << endl;
487 stop_all_beslisteners(SIGTERM);
500 stop_all_beslisteners(SIGTERM);
524 BESDEBUG(
"besdaemon",
"besdaemon: Starting command processor." << endl);
535 port = strtol(port_str.c_str(), &ptr, 10);
537 cerr <<
"Invalid port number for daemon command interface: " << port_str << endl;
543 BESDEBUG(
"besdaemon",
"besdaemon: listening on port: " << port << endl);
545 listener.listen(my_socket);
552 if (!usock_str.empty()) {
553 BESDEBUG(
"besdaemon",
"besdaemon: listening on unix socket: " << usock_str << endl);
555 listener.listen(unix_socket);
558 if (!port_found && !usock_found) {
559 BESDEBUG(
"besdaemon",
"Neither a port nor a unix socket was set for the daemon command interface." << endl);
563 BESDEBUG(
"besdaemon",
"besdaemon: starting command interface on port: " << port << endl);
564 command_server =
new PPTServer(&handler, &listener,
false);
577 cerr <<
"daemon: " <<
"caught unknown exception" << endl;
581 command_server->closeConnection();
583 delete command_server;
584 command_server =
nullptr;
590 unix_socket =
nullptr;
593 stop_all_beslisteners(SIGTERM);
606static void register_signal_handlers()
608 struct sigaction act;
611 sigemptyset(&act.sa_mask);
612 sigaddset(&act.sa_mask, SIGCHLD);
613 sigaddset(&act.sa_mask, SIGTERM);
614 sigaddset(&act.sa_mask, SIGHUP);
617 BESDEBUG(
"besdaemon",
"besdaemon: setting restart for sigchld." << endl);
618 act.sa_flags |= SA_RESTART;
621 act.sa_handler = catch_sig_child;
622 if (sigaction(SIGCHLD, &act, 0)) {
623 cerr <<
"Could not register a handler to catch beslistener status." << endl;
627 act.sa_handler = catch_sig_term;
628 if (sigaction(SIGTERM, &act, 0) < 0) {
629 cerr <<
"Could not register a handler to catch the terminate signal." << endl;
633 act.sa_handler = catch_sig_hup;
634 if (sigaction(SIGHUP, &act, 0) < 0) {
635 cerr <<
"Could not register a handler to catch the hang-up signal." << endl;
646static int daemon_init()
649 if ((pid = fork()) < 0)
663static void store_daemon_id(
int pid)
665 ofstream f(file_for_daemon_pid.c_str());
667 cerr << errno_str(
": unable to create pid file " + file_for_daemon_pid +
": ");
672 mode_t new_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
673 (void) chmod(file_for_daemon_pid.c_str(), new_mode);
685static bool load_names(
const string &install_dir,
const string &pid_dir)
687 string bindir =
"/bin";
688 if (!pid_dir.empty()) {
689 file_for_daemon_pid = pid_dir;
692 if (!install_dir.empty()) {
693 beslistener_path = install_dir;
694 beslistener_path += bindir;
695 if (file_for_daemon_pid.empty()) {
696 file_for_daemon_pid = install_dir +
"/var/run";
705 string prog = daemon_name;
706 string::size_type slash = prog.find_last_of(
'/');
707 if (slash != string::npos) {
708 beslistener_path = prog.substr(0, slash);
709 slash = prog.find_last_of(
'/');
710 if (slash != string::npos) {
711 string root = prog.substr(0, slash);
712 if (file_for_daemon_pid.empty()) {
713 file_for_daemon_pid = root +
"/var/run";
719 if (file_for_daemon_pid.empty()) {
720 file_for_daemon_pid = beslistener_path;
726 if (beslistener_path.empty()) {
727 beslistener_path =
".";
728 if (file_for_daemon_pid.empty()) {
729 file_for_daemon_pid =
"./run";
733 beslistener_path += BES_SERVER;
734 file_for_daemon_pid += BES_SERVER_PID;
736 if (access(beslistener_path.c_str(), F_OK) != 0) {
737 cerr << daemon_name <<
": cannot find " << beslistener_path << endl
738 <<
"Please either pass -i <install_dir> on the command line." << endl;
743 global_args[
"beslistener"] = beslistener_path;
748static void set_group_id()
750#if !defined(OS2) && !defined(TPF)
756 BESDEBUG(
"server",
"beslistener: Setting group id ... " << endl);
758 string key =
"BES.Group";
764 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
767 ERROR_LOG(err << endl);
768 exit(SERVER_EXIT_FATAL_CANNOT_START);
771 if (!found || group_str.empty()) {
772 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
773 string err =
"FAILED: Group not specified in BES configuration file";
775 ERROR_LOG(err << endl);
776 exit(SERVER_EXIT_FATAL_CANNOT_START);
778 BESDEBUG(
"server",
"to " << group_str <<
" ... " << endl);
781 if (group_str[0] ==
'#') {
783 const char *group_c = group_str.c_str();
785 new_gid = atoi(group_c);
792 ent = getgrnam(group_str.c_str());
795 struct group *result =
nullptr;
796 vector<char> buffer(1024);
797 int rc = getgrnam_r(group_str.c_str(), &in, buffer.data(), buffer.size(), &result);
798 if (rc != 0 || result ==
nullptr) {
799 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
800 string err = string(
"FAILED: Group ") + group_str +
" does not exist (" + strerror(errno) +
").";
802 ERROR_LOG(err << endl);
803 exit(SERVER_EXIT_FATAL_CANNOT_START);
805 new_gid = result->gr_gid;
809 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
811 err <<
"FAILED: Group id " << new_gid <<
" not a valid group id for BES";
812 cerr << err.str() << endl;
813 ERROR_LOG(err.str() << endl);
814 exit(SERVER_EXIT_FATAL_CANNOT_START);
817 BESDEBUG(
"server",
"to id " << new_gid <<
" ... " << endl);
818 if (setgid(new_gid) == -1) {
819 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
821 err <<
"FAILED: unable to set the group id to " << new_gid;
822 cerr << err.str() << endl;
823 ERROR_LOG(err.str() << endl);
824 exit(SERVER_EXIT_FATAL_CANNOT_START);
827 BESDEBUG(
"server",
"OK" << endl);
829 BESDEBUG(
"server",
"beslistener: Groups not supported in this OS" << endl );
833static void set_user_id()
835 BESDEBUG(
"server",
"beslistener: Setting user id ... " << endl);
842 string key =
"BES.User";
848 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
849 string err = (string)
"FAILED: " + e.
get_message();
851 ERROR_LOG(err << endl);
852 exit(SERVER_EXIT_FATAL_CANNOT_START);
855 if (!found || user_str.empty()) {
856 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
857 auto err = (string)
"FAILED: User not specified in BES config file";
859 ERROR_LOG(err << endl);
860 exit(SERVER_EXIT_FATAL_CANNOT_START);
862 BESDEBUG(
"server",
"to " << user_str <<
" ... " << endl);
865 if (user_str[0] ==
'#') {
866 const char *user_str_c = user_str.c_str();
868 new_id = atoi(user_str_c);
873 ent = getpwnam(user_str.c_str());
877 struct passwd *result =
nullptr;
878 vector<char> buffer(1024);
879 int rc = getpwnam_r(user_str.c_str(), &in, buffer.data(), buffer.size(), &result);
880 if (rc != 0 || result ==
nullptr) {
881 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
882 string err = (string)
"FAILED: Bad user name specified: " + user_str +
"(" + strerror(errno) +
").";
884 ERROR_LOG(err << endl);
885 exit(SERVER_EXIT_FATAL_CANNOT_START);
887 new_id = result->pw_uid;
892 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
893 auto err = (string)
"FAILED: BES cannot run as root";
895 ERROR_LOG(err << endl);
896 exit(SERVER_EXIT_FATAL_CANNOT_START);
901 vector<gid_t> groups(1);
902 groups.at(0) = getegid();
903 if (set_sups(groups.size(), groups.data()) == -1) {
904 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
906 err <<
"FAILED: Unable to relinquish supplementary groups (" << new_id <<
")";
907 cerr << err.str() << endl;
908 ERROR_LOG(err.str() << endl);
909 exit(SERVER_EXIT_FATAL_CANNOT_START);
912 BESDEBUG(
"server",
"to " << new_id <<
" ... " << endl);
913 if (setuid(new_id) == -1) {
914 BESDEBUG(
"server",
"beslistener: FAILED" << endl);
916 err <<
"FAILED: Unable to set user id to " << new_id;
917 cerr << err.str() << endl;
918 ERROR_LOG(err.str() << endl);
919 exit(SERVER_EXIT_FATAL_CANNOT_START);
922 BESDEBUG(
"server",
"OK" << endl);
928int main(
int argc,
char *argv[])
930 uid_t curr_euid = geteuid();
935 cerr <<
"FAILED: Must be root to run BES" << endl;
936 exit(SERVER_EXIT_FATAL_CANNOT_START);
939 cerr <<
"Developer Mode: Not testing if BES is run by root" << endl;
942 daemon_name =
"besdaemon";
947 bool become_daemon =
true;
953 BESServerUtils::show_usage(daemon_name);
961 unsigned short num_args = 1;
966 while ((c = getopt(argc, argv,
"hvsd:c:p:u:i:r:n")) != -1) {
969 BESServerUtils::show_version(daemon_name);
973 BESServerUtils::show_usage(daemon_name);
977 cerr <<
"Running in foreground!" << endl;
981 install_dir = optarg;
983 cout <<
"The specified install directory (-i option) "
984 <<
"is incorrectly formatted. Must be less than "
985 <<
"255 characters and include the characters " <<
"[0-9A-z_./-]" << endl;
988 global_args[
"-i"] = install_dir;
992 global_args[
"-s"] =
"";
998 cout <<
"The specified state directory (-r option) "
999 <<
"is incorrectly formatted. Must be less than "
1000 <<
"255 characters and include the characters " <<
"[0-9A-z_./-]" << endl;
1003 global_args[
"-r"] = pid_dir;
1007 config_file = optarg;
1009 cout <<
"The specified configuration file (-c option) "
1010 <<
"is incorrectly formatted. Must be less than "
1011 <<
"255 characters and include the characters " <<
"[0-9A-z_./-]" << endl;
1014 global_args[
"-c"] = config_file;
1019 string check_path = optarg;
1021 cout <<
"The specified unix socket (-u option) " <<
"is incorrectly formatted. Must be less than "
1022 <<
"255 characters and include the characters " <<
"[0-9A-z_./-]" << endl;
1025 global_args[
"-u"] = check_path;
1031 string port_num = optarg;
1032 for (
unsigned int i = 0; i < port_num.length(); i++) {
1033 if (!isdigit(port_num[i])) {
1034 cout <<
"The specified port contains non-digit " <<
"characters: " << port_num << endl;
1038 global_args[
"-p"] = port_num;
1044 string check_arg = optarg;
1046 cout <<
"The specified debug options \"" << check_arg <<
"\" contains invalid characters" << endl;
1050 global_args[
"-d"] = check_arg;
1051 debug_sink = check_arg.substr(0, check_arg.find(
','));
1056 BESServerUtils::show_usage(daemon_name);
1064 if (argc > num_args) {
1065 cout << daemon_name <<
": too many arguments passed to the BES";
1066 BESServerUtils::show_usage(daemon_name);
1069 if (pid_dir.empty()) {
1070 pid_dir = install_dir;
1074 if (!config_file.empty()) {
1081 if (config_file.empty() && !install_dir.empty()) {
1083 string conf_file = install_dir +
"etc/bes/bes.conf";
1090 cerr <<
"Caught BES Error while processing the daemon's options: " << e.
get_message() << endl;
1093 catch (
const std::exception &e) {
1094 cerr <<
"Caught C++ error while processing the daemon's options: " << e.what() << endl;
1098 cerr <<
"Caught unknown error while processing the daemon's options." << endl;
1104 if (!load_names(install_dir, pid_dir))
return 1;
1106 if (!access(file_for_daemon_pid.c_str(), F_OK)) {
1107 ifstream temp(file_for_daemon_pid.c_str());
1108 cout << daemon_name <<
": there seems to be a BES daemon already running at ";
1110 temp.getline(buf, 500);
1111 cout << buf << endl;
1120 store_daemon_id(getpid());
1122 if (curr_euid == 0) {
1124 cerr <<
"Developer Mode: Running as root - setting group and user ids" << endl;
1130 cerr <<
"Developer Mode: Not setting group or user ids" << endl;
1133 register_signal_handlers();
1140 cerr <<
"Could not initialize the modules to get the log contexts." << endl;
1158 if (global_args.count(
"-d") == 0) {
1185 master_beslistener_pid = start_master_beslistener();
1186 if (master_beslistener_pid == 0) {
1187 cerr << daemon_name <<
": server cannot mount at first try (core dump). "
1188 <<
"Please correct problems on the process manager " << beslistener_path << endl;
1189 return master_beslistener_pid;
1192 BESDEBUG(
"besdaemon",
"besdaemon: master_beslistener_pid: " << master_beslistener_pid << endl);
1195 cerr <<
"Caught BES Error during initialization: " << e.
get_message() << endl;
1198 catch (
const std::exception &e) {
1199 cerr <<
"Caught C++ error during initialization: " << e.what() << endl;
1203 cerr <<
"Caught unknown error during initialization." << endl;
1213 status = start_command_processor(handler);
1224 BESDEBUG(
"besdaemon",
"besdaemon: master_beslistener_status: " << master_beslistener_status << endl);
1225 if (master_beslistener_status == BESLISTENER_RESTART) {
1226 master_beslistener_status = BESLISTENER_STOPPED;
1228 start_master_beslistener();
1231 else if (master_beslistener_status != BESLISTENER_RUNNING) {
1241 cerr <<
"Caught BES Error while starting the command handler: " << e.
get_message() << endl;
1243 catch (
const std::exception &e) {
1245 cerr <<
"Caught C++ error while starting the command handler: " << e.what() << endl;
1249 cerr <<
"Caught unknown error while starting the command handler." << endl;
1252 BESDEBUG(
"besdaemon",
"besdaemon: past the command processor start" << endl);
1254 cleanup_resources();
static void SetStrm(std::ostream *strm, bool created)
set the debug output stream to the specified stream
static void SetUp(const std::string &values)
Sets up debugging for the bes.
static void Register(const std::string &flagName)
register the specified debug flag
static std::string GetOptionsString()
Base exception class for the BES with basic string message.
std::string get_message() const
get the error message for this exception
Base application object for all BES applications.
int initialize(int argC, char **argV) override
Load and initialize any BES modules.
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...
static bool command_line_arg_ok(const std::string &arg)
sanitize command line arguments
static void trim_if_trailing_slash(std::string &value)
If the string ends in a slash, remove it This function works for empty strings (doing nothing)....
void initConnection() override
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
static TheBESKeys * TheKeys()
static std::string ConfigFile