"Fossies" - the Fresh Open Source Software Archive

Member "monit-5.28.0/src/monit.c" (28 Mar 2021, 36482 Bytes) of package /linux/privat/monit-5.28.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "monit.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.27.2_vs_5.28.0.

    1 /*
    2  * Copyright (C) Tildeslash Ltd. All rights reserved.
    3  *
    4  * This program is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU Affero General Public License version 3.
    6  *
    7  * This program is distributed in the hope that it will be useful,
    8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10  * GNU General Public License for more details.
   11  *
   12  * You should have received a copy of the GNU Affero General Public License
   13  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   14  *
   15  * In addition, as a special exception, the copyright holders give
   16  * permission to link the code of portions of this program with the
   17  * OpenSSL library under certain conditions as described in each
   18  * individual source file, and distribute linked combinations
   19  * including the two.
   20  *
   21  * You must obey the GNU Affero General Public License in all respects
   22  * for all of the code used other than OpenSSL.
   23  */
   24 
   25 
   26 #include "config.h"
   27 #include <locale.h>
   28 
   29 #ifdef HAVE_STDIO_H
   30 #include <stdio.h>
   31 #endif
   32 
   33 #ifdef HAVE_STDLIB_H
   34 #include <stdlib.h>
   35 #endif
   36 
   37 #ifdef HAVE_ERRNO_H
   38 #include <errno.h>
   39 #endif
   40 
   41 #ifdef HAVE_SIGNAL_H
   42 #include <signal.h>
   43 #endif
   44 
   45 #ifdef HAVE_GETOPT_H
   46 #include <getopt.h>
   47 #endif
   48 
   49 #ifdef HAVE_STRING_H
   50 #include <string.h>
   51 #endif
   52 
   53 #ifdef HAVE_STRINGS_H
   54 #include <strings.h>
   55 #endif
   56 
   57 #ifdef HAVE_CTYPE_H
   58 #include <ctype.h>
   59 #endif
   60 
   61 #ifdef HAVE_UNISTD_H
   62 #include <unistd.h>
   63 #endif
   64 
   65 #ifdef HAVE_SYS_TYPES_H
   66 #include <sys/types.h>
   67 #endif
   68 
   69 #ifdef HAVE_SYS_STAT_H
   70 #include <sys/stat.h>
   71 #endif
   72 
   73 #ifdef HAVE_SYS_WAIT_H
   74 #include <sys/wait.h>
   75 #endif
   76 
   77 #include "monit.h"
   78 #include "ProcessTree.h"
   79 #include "state.h"
   80 #include "event.h"
   81 #include "engine.h"
   82 #include "client.h"
   83 #include "MMonit.h"
   84 #include "md5.h"
   85 #include "sha1.h"
   86 #include "checksum.h"
   87 
   88 // libmonit
   89 #include "Bootstrap.h"
   90 #include "io/Dir.h"
   91 #include "io/File.h"
   92 #include "system/Time.h"
   93 #include "util/List.h"
   94 #include "exceptions/AssertException.h"
   95 
   96 
   97 /**
   98  *  DESCRIPTION
   99  *    monit - system for monitoring services on a Unix system
  100  *
  101  *  SYNOPSIS
  102  *    monit [options] {arguments}
  103  *
  104  *  @file
  105  */
  106 
  107 
  108 /* -------------------------------------------------------------- Prototypes */
  109 
  110 
  111 static void  do_init(void);                   /* Initialize this application */
  112 static void  do_reinit(void);       /* Re-initialize the runtime application */
  113 static void  do_action(List_T);          /* Dispatch to the submitted action */
  114 static void  do_exit(bool);                           /* Finalize monit */
  115 static void  do_default(void);                          /* Do default action */
  116 static void  handle_options(int, char **, List_T); /* Handle program options */
  117 static void  help(void);             /* Print program help message to stdout */
  118 static void  version(void);                     /* Print version information */
  119 static void *heartbeat(void *args);              /* M/Monit heartbeat thread */
  120 static void do_reload(int);             /* Signalhandler for a daemon reload */
  121 static void do_destroy(int);         /* Signalhandler for monit finalization */
  122 static void do_wakeup(int);        /* Signalhandler for a daemon wakeup call */
  123 static void waitforchildren(void); /* Wait for any child process not running */
  124 
  125 
  126 
  127 /* ------------------------------------------------------------------ Global */
  128 
  129 
  130 const char *prog;                              /**< The Name of this Program */
  131 struct Run_T Run;                      /**< Struct holding runtime constants */
  132 Service_T servicelist;                /**< The service list (created in p.y) */
  133 Service_T servicelist_conf;   /**< The service list in conf file (c. in p.y) */
  134 ServiceGroup_T servicegrouplist;/**< The service group list (created in p.y) */
  135 SystemInfo_T systeminfo;                             /**< System information */
  136 
  137 Thread_T heartbeatThread;
  138 Sem_T    heartbeatCond;
  139 Mutex_T  heartbeatMutex;
  140 static volatile bool heartbeatRunning = false;
  141 
  142 const char *actionnames[] = {"ignore", "alert", "restart", "stop", "exec", "unmonitor", "start", "monitor", ""};
  143 const char *modenames[] = {"active", "passive"};
  144 const char *onrebootnames[] = {"start", "nostart", "laststate"};
  145 const char *checksumnames[] = {"UNKNOWN", "MD5", "SHA1"};
  146 const char *operatornames[] = {"less than", "less than or equal to", "greater than", "greater than or equal to", "equal to", "not equal to", "changed"};
  147 const char *operatorshortnames[] = {"<", "<=", ">", ">=", "=", "!=", "<>"};
  148 const char *servicetypes[] = {"Filesystem", "Directory", "File", "Process", "Remote Host", "System", "Fifo", "Program", "Network"};
  149 const char *pathnames[] = {"Path", "Path", "Path", "Pid file", "Path", "", "Path"};
  150 const char *icmpnames[] = {"Reply", "", "", "Destination Unreachable", "Source Quench", "Redirect", "", "", "Ping", "", "", "Time Exceeded", "Parameter Problem", "Timestamp Request", "Timestamp Reply", "Information Request", "Information Reply", "Address Mask Request", "Address Mask Reply"};
  151 const char *socketnames[] = {"unix", "IP", "IPv4", "IPv6"};
  152 const char *timestampnames[] = {"modify/change time", "access time", "change time", "modify time"};
  153 const char *httpmethod[] = {"", "HEAD", "GET"};
  154 
  155 
  156 /* ------------------------------------------------------------------ Public */
  157 
  158 
  159 /**
  160  * The Prime mover
  161  */
  162 int main(int argc, char **argv) {
  163         Bootstrap(); // Bootstrap libmonit
  164         Bootstrap_setAbortHandler(Log_abort_handler);  // Abort Monit on exceptions thrown by libmonit
  165         Bootstrap_setErrorHandler(Log_verror);
  166         setlocale(LC_ALL, "C");
  167         prog = File_basename(argv[0]);
  168 #ifdef HAVE_OPENSSL
  169         Ssl_start();
  170 #endif
  171         init_env();
  172         List_T arguments = List_new();
  173         handle_options(argc, argv, arguments);
  174         do_init();
  175         do_action(arguments);
  176         List_free(&arguments);
  177         do_exit(false);
  178         return 0;
  179 }
  180 
  181 
  182 /**
  183  * Wakeup a sleeping monit daemon.
  184  * Returns true on success otherwise false
  185  */
  186 bool do_wakeupcall() {
  187         pid_t pid;
  188 
  189         if ((pid = exist_daemon()) > 0) {
  190                 kill(pid, SIGUSR1);
  191                 Log_info("Monit daemon with PID %d awakened\n", pid);
  192 
  193                 return true;
  194         }
  195 
  196         return false;
  197 }
  198 
  199 
  200 bool interrupt() {
  201         return Run.flags & Run_Stopped || Run.flags & Run_DoReload;
  202 }
  203 
  204 
  205 /* ----------------------------------------------------------------- Private */
  206 
  207 
  208 static void _validateOnce(void) {
  209         if (State_open()) {
  210                 State_restore();
  211                 validate();
  212                 State_save();
  213                 State_close();
  214         }
  215 }
  216 
  217 
  218 /**
  219  * Initialize this application - Register signal handlers,
  220  * Parse the control file and initialize the program's
  221  * datastructures and the log system.
  222  */
  223 static void do_init() {
  224         /*
  225          * Register interest for the SIGTERM signal,
  226          * in case we run in daemon mode this signal
  227          * will terminate a running daemon.
  228          */
  229         signal(SIGTERM, do_destroy);
  230 
  231         /*
  232          * Register interest for the SIGUSER1 signal,
  233          * in case we run in daemon mode this signal
  234          * will wakeup a sleeping daemon.
  235          */
  236         signal(SIGUSR1, do_wakeup);
  237 
  238         /*
  239          * Register interest for the SIGINT signal,
  240          * in case we run as a server but not as a daemon
  241          * we need to catch this signal if the user pressed
  242          * CTRL^C in the terminal
  243          */
  244         signal(SIGINT, do_destroy);
  245 
  246         /*
  247          * Register interest for the SIGHUP signal,
  248          * in case we run in daemon mode this signal
  249          * will reload the configuration.
  250          */
  251         signal(SIGHUP, do_reload);
  252 
  253         /*
  254          * Register no interest for the SIGPIPE signal,
  255          */
  256         signal(SIGPIPE, SIG_IGN);
  257 
  258         /*
  259          * Initialize the random number generator
  260          */
  261         srandom((unsigned)(Time_now() + getpid()));
  262 
  263         /*
  264          * Initialize the Runtime mutex. This mutex
  265          * is used to synchronize handling of global
  266          * service data
  267          */
  268         Mutex_init(Run.mutex);
  269 
  270         /*
  271          * Initialize heartbeat mutex and condition
  272          */
  273         Mutex_init(heartbeatMutex);
  274         Sem_init(heartbeatCond);
  275 
  276         /*
  277          * Get the position of the control file
  278          */
  279         if (! Run.files.control)
  280                 Run.files.control = file_findControlFile();
  281 
  282         /*
  283          * Initialize the system information data collecting interface
  284          */
  285         if (init_system_info())
  286                 Run.flags |= Run_ProcessEngineEnabled;
  287 
  288         /*
  289          * Start the Parser and create the service list. This will also set
  290          * any Runtime constants defined in the controlfile.
  291          */
  292         if (! parse(Run.files.control))
  293                 exit(1);
  294 
  295         /*
  296          * Initialize the log system
  297          */
  298         if (! Log_init())
  299                 exit(1);
  300 
  301         /*
  302          * Did we find any service ?
  303          */
  304         if (! servicelist) {
  305                 Log_error("No service has been specified\n");
  306                 exit(0);
  307         }
  308 
  309         /*
  310          * Initialize Runtime file variables
  311          */
  312         file_init();
  313 
  314         /*
  315          * Should we print debug information ?
  316          */
  317         if (Run.debug) {
  318                 Util_printRunList();
  319                 Util_printServiceList();
  320         }
  321 
  322         /*
  323          * Reap any stray child processes we may have created
  324          */
  325         atexit(waitforchildren);
  326 }
  327 
  328 
  329 /**
  330  * Re-Initialize the application - called if a
  331  * monit daemon receives the SIGHUP signal.
  332  */
  333 static void do_reinit() {
  334         Log_info("Reinitializing Monit -- control file '%s'\n", Run.files.control);
  335 
  336         /* Wait non-blocking for any children that has exited. Since we
  337          reinitialize any information about children we have setup to wait
  338          for will be lost. This may create zombie processes until Monit
  339          itself exit. However, Monit will wait on all children that has exited
  340          before it itself exit. TODO: Later refactored versions will use a
  341          globale process table which a sigchld handler can check */
  342         waitforchildren();
  343 
  344         if (Run.mmonits && heartbeatRunning) {
  345                 Sem_signal(heartbeatCond);
  346                 Thread_join(heartbeatThread);
  347                 heartbeatRunning = false;
  348         }
  349 
  350         Run.flags &= ~Run_DoReload;
  351 
  352         /* Stop http interface */
  353         if (Run.httpd.flags & Httpd_Net || Run.httpd.flags & Httpd_Unix)
  354                 monit_http(Httpd_Stop);
  355 
  356         /* Save the current state (no changes are possible now since the http thread is stopped) */
  357         State_save();
  358         State_close();
  359 
  360         /* Run the garbage collector */
  361         gc();
  362 
  363         if (! parse(Run.files.control)) {
  364                 Log_error("%s stopped -- error parsing configuration file\n", prog);
  365                 exit(1);
  366         }
  367 
  368         /* Close the current log */
  369         Log_close();
  370 
  371         /* Reinstall the log system */
  372         if (! Log_init())
  373                 exit(1);
  374 
  375         /* Did we find any services ?  */
  376         if (! servicelist) {
  377                 Log_error("No service has been specified\n");
  378                 exit(0);
  379         }
  380 
  381         /* Reinitialize Runtime file variables */
  382         file_init();
  383 
  384         if (! file_createPidFile(Run.files.pid)) {
  385                 Log_error("%s stopped -- cannot create a pid file\n", prog);
  386                 exit(1);
  387         }
  388 
  389         /* Update service data from the state repository */
  390         if (! State_open())
  391                 exit(1);
  392         State_restore();
  393 
  394         /* Start http interface */
  395         if (can_http())
  396                 monit_http(Httpd_Start);
  397 
  398         /* send the monit startup notification */
  399         Event_post(Run.system, Event_Instance, State_Changed, Run.system->action_MONIT_START, "Monit reloaded");
  400 
  401         if (Run.mmonits) {
  402                 Thread_create(heartbeatThread, heartbeat, NULL);
  403                 heartbeatRunning = true;
  404         }
  405 }
  406 
  407 
  408 static bool _isMemberOfGroup(Service_T s, ServiceGroup_T g) {
  409         for (list_t m = g->members->head; m; m = m->next) {
  410                 Service_T member = m->e;
  411                 if (s == member)
  412                         return true;
  413         }
  414         return false;
  415 }
  416 
  417 
  418 static bool _hasParentInTheSameGroup(Service_T s, ServiceGroup_T g) {
  419         for (Dependant_T d = s->dependantlist; d; d = d->next ) {
  420                 Service_T parent = Util_getService(d->dependant);
  421                 if (parent && _isMemberOfGroup(parent, g))
  422                         return true;
  423         }
  424         return false;
  425 }
  426 
  427 
  428 /**
  429  * Dispatch to the submitted action - actions are program arguments
  430  */
  431 static void do_action(List_T arguments) {
  432         char *action = List_pop(arguments);
  433 
  434         Run.flags |= Run_Once;
  435 
  436         if (! action) {
  437                 do_default();
  438         } else if (IS(action, "start")     ||
  439                    IS(action, "stop")      ||
  440                    IS(action, "monitor")   ||
  441                    IS(action, "unmonitor") ||
  442                    IS(action, "restart")) {
  443                 char *service = List_pop(arguments);
  444                 if (Run.mygroup || service) {
  445                         int errors = 0;
  446                         List_T services = List_new();
  447                         if (Run.mygroup) {
  448                                 for (ServiceGroup_T sg = servicegrouplist; sg; sg = sg->next) {
  449                                         if (IS(Run.mygroup, sg->name)) {
  450                                                 for (list_t m = sg->members->head; m; m = m->next) {
  451                                                         Service_T s = m->e;
  452                                                         if (IS(action, "restart") && _hasParentInTheSameGroup(s, sg)) {
  453                                                                 DEBUG("Restart of %s skipped -- it'll be handled as part of the dependency chain, as the parent service is member of the same group\n", s->name);
  454                                                                 continue;
  455                                                         }
  456                                                         List_append(services, s->name);
  457                                                 }
  458                                                 break;
  459                                         }
  460                                 }
  461                                 if (List_length(services) == 0) {
  462                                         List_free(&services);
  463                                         Log_error("Group '%s' not found\n", Run.mygroup);
  464                                         exit(1);
  465                                 }
  466                         } else if (IS(service, "all")) {
  467                                 for (Service_T s = servicelist; s; s = s->next)
  468                                         List_append(services, s->name);
  469                         } else {
  470                                 List_append(services, service);
  471                         }
  472                         errors = exist_daemon() ? (HttpClient_action(action, services) ? 0 : 1) : control_service_string(services, action);
  473                         List_free(&services);
  474                         if (errors)
  475                                 exit(1);
  476                 } else {
  477                         Log_error("Please specify a service name or 'all' after %s\n", action);
  478                         exit(1);
  479                 }
  480         } else if (IS(action, "reload")) {
  481                 Log_info("Reinitializing %s daemon\n", prog);
  482                 kill_daemon(SIGHUP);
  483         } else if (IS(action, "status")) {
  484                 char *service = List_pop(arguments);
  485                 if (! HttpClient_status(Run.mygroup, service))
  486                         exit(1);
  487         } else if (IS(action, "summary")) {
  488                 char *service = List_pop(arguments);
  489                 if (! HttpClient_summary(Run.mygroup, service))
  490                         exit(1);
  491         } else if (IS(action, "report")) {
  492                 char *type = List_pop(arguments);
  493                 if (! HttpClient_report(type))
  494                         exit(1);
  495         } else if (IS(action, "procmatch")) {
  496                 char *pattern = List_pop(arguments);
  497                 if (! pattern) {
  498                         printf("Invalid syntax - usage: procmatch \"<pattern>\"\n");
  499                         exit(1);
  500                 }
  501                 ProcessTree_testMatch(pattern);
  502         } else if (IS(action, "quit")) {
  503                 kill_daemon(SIGTERM);
  504         } else if (IS(action, "validate")) {
  505                 if (do_wakeupcall()) {
  506                         char *service = List_pop(arguments);
  507                         HttpClient_status(Run.mygroup, service);
  508                 } else {
  509                         _validateOnce();
  510                 }
  511                 exit(1);
  512         } else {
  513                 Log_error("Invalid argument -- %s  (-h will show valid arguments)\n", action);
  514                 exit(1);
  515         }
  516 }
  517 
  518 
  519 /**
  520  * Finalize monit
  521  */
  522 static void do_exit(bool saveState) {
  523         set_signal_block();
  524         Run.flags |= Run_Stopped;
  525         if ((Run.flags & Run_Daemon) && ! (Run.flags & Run_Once)) {
  526                 if (can_http())
  527                         monit_http(Httpd_Stop);
  528 
  529                 if (Run.mmonits && heartbeatRunning) {
  530                         Sem_signal(heartbeatCond);
  531                         Thread_join(heartbeatThread);
  532                         heartbeatRunning = false;
  533                 }
  534 
  535                 Log_info("Monit daemon with pid [%d] stopped\n", (int)getpid());
  536 
  537                 /* send the monit stop notification */
  538                 Event_post(Run.system, Event_Instance, State_Changed, Run.system->action_MONIT_STOP, "Monit %s stopped", VERSION);
  539         }
  540         if (saveState) {
  541                 State_save();
  542         }
  543         gc();
  544 #ifdef HAVE_OPENSSL
  545         Ssl_stop();
  546 #endif
  547         exit(0);
  548 }
  549 
  550 
  551 /**
  552  * Default action - become a daemon if defined in the Run object and
  553  * run validate() between sleeps. If not, just run validate() once.
  554  * Also, if specified, start the monit http server if in daemon mode.
  555  */
  556 static void do_default() {
  557         if (Run.flags & Run_Daemon) {
  558                 if (do_wakeupcall())
  559                         exit(0);
  560 
  561                 Run.flags &= ~Run_Once;
  562                 if (can_http()) {
  563                         if (Run.httpd.flags & Httpd_Net)
  564                                 Log_info("Starting Monit %s daemon with http interface at [%s]:%d\n", VERSION, Run.httpd.socket.net.address ? Run.httpd.socket.net.address : "*", Run.httpd.socket.net.port);
  565                         else if (Run.httpd.flags & Httpd_Unix)
  566                                 Log_info("Starting Monit %s daemon with http interface at %s\n", VERSION, Run.httpd.socket.unix.path);
  567                 } else {
  568                         Log_info("Starting Monit %s daemon\n", VERSION);
  569                 }
  570 
  571                 if (! (Run.flags & Run_Foreground))
  572                         daemonize();
  573 
  574                 if (! file_createPidFile(Run.files.pid)) {
  575                         Log_error("Monit daemon died\n");
  576                         exit(1);
  577                 }
  578 
  579                 if (! State_open())
  580                         exit(1);
  581                 State_restore();
  582 
  583                 atexit(file_finalize);
  584 
  585                 if (Run.startdelay) {
  586                         if (State_reboot()) {
  587                                 time_t now = Time_now();
  588                                 time_t delay = now + Run.startdelay;
  589 
  590                                 Log_info("Monit will delay for %ds on first start after reboot ...\n", Run.startdelay);
  591 
  592                                 /* sleep can be interrupted by signal => make sure we paused long enough */
  593                                 while (now < delay) {
  594                                         sleep((unsigned int)(delay - now));
  595                                         if (Run.flags & Run_Stopped)
  596                                                 do_exit(false);
  597                                         now = Time_now();
  598                                 }
  599                         } else {
  600                                 DEBUG("Monit delay %ds skipped -- the system boot time has not changed since last Monit start\n", Run.startdelay);
  601                         }
  602                 }
  603 
  604                 if (can_http())
  605                         monit_http(Httpd_Start);
  606 
  607                 /* send the monit startup notification */
  608                 Event_post(Run.system, Event_Instance, State_Changed, Run.system->action_MONIT_START, "Monit %s started", VERSION);
  609 
  610                 if (Run.mmonits) {
  611                         Thread_create(heartbeatThread, heartbeat, NULL);
  612                         heartbeatRunning = true;
  613                 }
  614 
  615                 while (true) {
  616                         validate();
  617 
  618                         /* In the case that there is no pending action then sleep */
  619                         if (! (Run.flags & Run_ActionPending) && ! interrupt())
  620                                 sleep(Run.polltime);
  621 
  622                         if (Run.flags & Run_DoWakeup) {
  623                                 Run.flags &= ~Run_DoWakeup;
  624                                 Log_info("Awakened by User defined signal 1\n");
  625                         }
  626 
  627                         if (Run.flags & Run_Stopped) {
  628                                 do_exit(true);
  629                         } else if (Run.flags & Run_DoReload) {
  630                                 do_reinit();
  631                         } else {
  632                                 State_saveIfDirty();
  633                         }
  634                 }
  635         } else {
  636                 _validateOnce();
  637         }
  638 }
  639 
  640 
  641 /**
  642  * Handle program options - Options set from the commandline
  643  * takes precedence over those found in the control file
  644  */
  645 static void handle_options(int argc, char **argv, List_T arguments) {
  646         int opt;
  647         int deferred_opt = 0;
  648         opterr = 0;
  649         Run.mygroup = NULL;
  650         const char *shortopts = "+c:d:g:l:p:s:HIirtvVhB";
  651         while (optind < argc) {
  652 #ifdef HAVE_GETOPT_LONG
  653                 struct option longopts[] = {
  654                         {"conf",        required_argument,      NULL,   'c'},
  655                         {"daemon",      required_argument,      NULL,   'd'},
  656                         {"group",       required_argument,      NULL,   'g'},
  657                         {"logfile",     required_argument,      NULL,   'l'},
  658                         {"pidfile",     required_argument,      NULL,   'p'},
  659                         {"statefile",   required_argument,      NULL,   's'},
  660                         {"hash",        optional_argument,      NULL,   'H'},
  661                         {"id",          no_argument,            NULL,   'i'},
  662                         {"help",        no_argument,            NULL,   'h'},
  663                         {"resetid",     no_argument,            NULL,   'r'},
  664                         {"test",        no_argument,            NULL,   't'},
  665                         {"verbose",     no_argument,            NULL,   'v'},
  666                         {"batch",       no_argument,            NULL,   'B'},
  667                         {"interactive", no_argument,            NULL,   'I'},
  668                         {"version",     no_argument,            NULL,   'V'},
  669                         {0}
  670                 };
  671                 if ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
  672 #else
  673                 if ((opt = getopt(argc, argv, shortopts)) != -1)
  674 #endif
  675                 {
  676                         switch (opt) {
  677                                 case 'c':
  678                                 {
  679                                         char *f = optarg;
  680                                         char realpath[PATH_MAX] = {};
  681                                         if (f[0] != SEPARATOR_CHAR)
  682                                                 f = File_getRealPath(optarg, realpath);
  683                                         if (! f)
  684                                                 THROW(AssertException, "The control file '%s' does not exist at %s",
  685                                                       Str_trunc(optarg, 80), Dir_cwd((char[STRLEN]){}, STRLEN));
  686                                         if (! File_isFile(f))
  687                                                 THROW(AssertException, "The control file '%s' is not a file", Str_trunc(f, 80));
  688                                         if (! File_isReadable(f))
  689                                                 THROW(AssertException, "The control file '%s' is not readable", Str_trunc(f, 80));
  690                                         Run.files.control = Str_dup(f);
  691                                         break;
  692                                 }
  693                                 case 'd':
  694                                 {
  695                                         Run.flags |= Run_Daemon;
  696                                         if (sscanf(optarg, "%d", &Run.polltime) != 1 || Run.polltime < 1) {
  697                                                 Log_error("Option -%c requires a natural number\n", opt);
  698                                                 exit(1);
  699                                         }
  700                                         break;
  701                                 }
  702                                 case 'g':
  703                                 {
  704                                         Run.mygroup = Str_dup(optarg);
  705                                         break;
  706                                 }
  707                                 case 'l':
  708                                 {
  709                                         Run.files.log = Str_dup(optarg);
  710                                         if (IS(Run.files.log, "syslog"))
  711                                                 Run.flags |= Run_UseSyslog;
  712                                         Run.flags |= Run_Log;
  713                                         break;
  714                                 }
  715                                 case 'p':
  716                                 {
  717                                         Run.files.pid = Str_dup(optarg);
  718                                         break;
  719                                 }
  720                                 case 's':
  721                                 {
  722                                         Run.files.state = Str_dup(optarg);
  723                                         break;
  724                                 }
  725                                 case 'I':
  726                                 {
  727                                         Run.flags |= Run_Foreground;
  728                                         break;
  729                                 }
  730                                 case 'i':
  731                                 {
  732                                         deferred_opt = 'i';
  733                                         break;
  734                                 }
  735                                 case 'r':
  736                                 {
  737                                         deferred_opt = 'r';
  738                                         break;
  739                                 }
  740                                 case 't':
  741                                 {
  742                                         deferred_opt = 't';
  743                                         break;
  744                                 }
  745                                 case 'v':
  746                                 {
  747                                         Run.debug++;
  748                                         break;
  749                                 }
  750                                 case 'H':
  751                                 {
  752                                         if (argc > optind)
  753                                                 Checksum_printHash(argv[optind]);
  754                                         else
  755                                                 Checksum_printHash(NULL);
  756                                         exit(0);
  757                                         break;
  758                                 }
  759                                 case 'V':
  760                                 {
  761                                         version();
  762                                         exit(0);
  763                                         break;
  764                                 }
  765                                 case 'h':
  766                                 {
  767                                         help();
  768                                         exit(0);
  769                                         break;
  770                                 }
  771                                 case 'B':
  772                                 {
  773                                         Run.flags |= Run_Batch;
  774                                         break;
  775                                 }
  776                                 case '?':
  777                                 {
  778                                         switch (optopt) {
  779                                                 case 'c':
  780                                                 case 'd':
  781                                                 case 'g':
  782                                                 case 'l':
  783                                                 case 'p':
  784                                                 case 's':
  785                                                 {
  786                                                         Log_error("Option -- %c requires an argument\n", optopt);
  787                                                         break;
  788                                                 }
  789                                                 default:
  790                                                 {
  791                                                         Log_error("Invalid option -- %c  (-h will show valid options)\n", optopt);
  792                                                 }
  793                                         }
  794                                         exit(1);
  795                                 }
  796                         }
  797                 } else {
  798                         List_append(arguments, argv[optind++]);
  799                 }
  800         }
  801         /* Handle deferred options to make arguments to the program positional
  802          independent. These options are handled last, here as they represent exit
  803          points in the application and the control-file might be set with -c and
  804          these options need to respect the new control-file location as they call
  805          do_init */
  806         switch (deferred_opt) {
  807                 case 't':
  808                 {
  809                         do_init(); // Parses control file and initialize program, exit on error
  810                         printf("Control file syntax OK\n");
  811                         exit(0);
  812                         break;
  813                 }
  814                 case 'r':
  815                 {
  816                         do_init();
  817                         assert(Run.id);
  818                         printf("Reset Monit Id? [y/N]> ");
  819                         if (tolower(getchar()) == 'y') {
  820                                 File_delete(Run.files.id);
  821                                 Util_monitId(Run.files.id);
  822                                 kill_daemon(SIGHUP); // make any running Monit Daemon reload the new ID-File
  823                         }
  824                         exit(0);
  825                         break;
  826                 }
  827                 case 'i':
  828                 {
  829                         do_init();
  830                         assert(Run.id);
  831                         printf("Monit ID: %s\n", Run.id);
  832                         exit(0);
  833                         break;
  834                 }
  835         }
  836 }
  837 
  838 
  839 /**
  840  * Print the program's help message
  841  */
  842 static void help() {
  843         printf(
  844                "Usage: %s [options]+ [command]\n"
  845                "Options are as follows:\n"
  846                " -c file       Use this control file\n"
  847                " -d n          Run as a daemon once per n seconds\n"
  848                " -g name       Set group name for monit commands\n"
  849                " -l logfile    Print log information to this file\n"
  850                " -p pidfile    Use this lock file in daemon mode\n"
  851                " -s statefile  Set the file monit should write state information to\n"
  852                " -I            Do not run in background (needed when run from init)\n"
  853                " --id          Print Monit's unique ID\n"
  854                " --resetid     Reset Monit's unique ID. Use with caution\n"
  855                " -B            Batch command line mode (do not output tables or colors)\n"
  856                " -t            Run syntax check for the control file\n"
  857                " -v            Verbose mode, work noisy (diagnostic output)\n"
  858                " -vv           Very verbose mode, same as -v plus log stacktrace on error\n"
  859                " -H [filename] Print SHA1 and MD5 hashes of the file or of stdin if the\n"
  860                "               filename is omitted; monit will exit afterwards\n"
  861                " -V            Print version number and patchlevel\n"
  862                " -h            Print this text\n"
  863                "Optional commands are as follows:\n"
  864                " start all             - Start all services\n"
  865                " start <name>          - Only start the named service\n"
  866                " stop all              - Stop all services\n"
  867                " stop <name>           - Stop the named service\n"
  868                " restart all           - Stop and start all services\n"
  869                " restart <name>        - Only restart the named service\n"
  870                " monitor all           - Enable monitoring of all services\n"
  871                " monitor <name>        - Only enable monitoring of the named service\n"
  872                " unmonitor all         - Disable monitoring of all services\n"
  873                " unmonitor <name>      - Only disable monitoring of the named service\n"
  874                " reload                - Reinitialize monit\n"
  875                " status [name]         - Print full status information for service(s)\n"
  876                " summary [name]        - Print short status information for service(s)\n"
  877                " report [up|down|..]   - Report state of services. See manual for options\n"
  878                " quit                  - Kill the monit daemon process\n"
  879                " validate              - Check all services and start if not running\n"
  880                " procmatch <pattern>   - Test process matching pattern\n",
  881                prog);
  882 }
  883 
  884 /**
  885  * Print version information
  886  */
  887 static void version() {
  888         printf("This is Monit version %s\n", VERSION);
  889         printf("Built with");
  890 #ifndef HAVE_OPENSSL
  891         printf("out");
  892 #endif
  893         printf(" ssl, with");
  894 #ifndef HAVE_IPV6
  895         printf("out");
  896 #endif
  897         printf(" ipv6, with");
  898 #ifndef HAVE_LIBZ
  899         printf("out");
  900 #endif
  901         printf(" compression, with");
  902 #ifndef HAVE_LIBPAM
  903         printf("out");
  904 #endif
  905         printf(" pam and with");
  906 #ifndef HAVE_LARGEFILES
  907         printf("out");
  908 #endif
  909         printf(" large files\n");
  910         printf("Copyright (C) 2001-2021 Tildeslash Ltd. All Rights Reserved.\n");
  911 }
  912 
  913 
  914 /**
  915  * M/Monit heartbeat thread
  916  */
  917 static void *heartbeat(__attribute__ ((unused)) void *args) {
  918         set_signal_block();
  919         Log_info("M/Monit heartbeat started\n");
  920         LOCK(heartbeatMutex)
  921         {
  922                 while (! interrupt()) {
  923                         MMonit_send(NULL);
  924                         struct timespec wait = {.tv_sec = Time_now() + Run.polltime, .tv_nsec = 0};
  925                         Sem_timeWait(heartbeatCond, heartbeatMutex, wait);
  926                 }
  927         }
  928         END_LOCK;
  929 #ifdef HAVE_OPENSSL
  930         Ssl_threadCleanup();
  931 #endif
  932         Log_info("M/Monit heartbeat stopped\n");
  933         return NULL;
  934 }
  935 
  936 
  937 /**
  938  * Signalhandler for a daemon reload call
  939  */
  940 static void do_reload(__attribute__ ((unused)) int sig) {
  941         Run.flags |= Run_DoReload;
  942 }
  943 
  944 
  945 /**
  946  * Signalhandler for monit finalization
  947  */
  948 static void do_destroy(__attribute__ ((unused)) int sig) {
  949         Run.flags |= Run_Stopped;
  950 }
  951 
  952 
  953 /**
  954  * Signalhandler for a daemon wakeup call
  955  */
  956 static void do_wakeup(__attribute__ ((unused)) int sig) {
  957         Run.flags |= Run_DoWakeup;
  958 }
  959 
  960 
  961 /* A simple non-blocking reaper to ensure that we wait-for and reap all/any stray child processes
  962  we may have created and not waited on, so we do not create any zombie processes at exit */
  963 static void waitforchildren(void) {
  964         while (waitpid(-1, NULL, WNOHANG) > 0) ;
  965 }