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  */
main(int argc,char ** argv)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         TRY
174         {
175                 handle_options(argc, argv, arguments);
176         }
177         ELSE
178         {
179                 Log_error("%s\n", Exception_frame.message);
180                 exit(1);
181         }
182         END_TRY;
183         do_init();
184         do_action(arguments);
185         List_free(&arguments);
186         do_exit(false);
187         return 0;
188 }
189 
190 
191 /**
192  * Wakeup a sleeping monit daemon.
193  * Returns true on success otherwise false
194  */
do_wakeupcall()195 bool do_wakeupcall() {
196         pid_t pid;
197 
198         if ((pid = exist_daemon()) > 0) {
199                 kill(pid, SIGUSR1);
200                 Log_info("Monit daemon with PID %d awakened\n", pid);
201 
202                 return true;
203         }
204 
205         return false;
206 }
207 
208 
interrupt()209 bool interrupt() {
210         return Run.flags & Run_Stopped || Run.flags & Run_DoReload;
211 }
212 
213 
214 /* ----------------------------------------------------------------- Private */
215 
216 
_validateOnce(void)217 static void _validateOnce(void) {
218         if (State_open()) {
219                 State_restore();
220                 validate();
221                 State_save();
222                 State_close();
223         }
224 }
225 
226 
227 /**
228  * Initialize this application - Register signal handlers,
229  * Parse the control file and initialize the program's
230  * datastructures and the log system.
231  */
do_init()232 static void do_init() {
233         /*
234          * Register interest for the SIGTERM signal,
235          * in case we run in daemon mode this signal
236          * will terminate a running daemon.
237          */
238         signal(SIGTERM, do_destroy);
239 
240         /*
241          * Register interest for the SIGUSER1 signal,
242          * in case we run in daemon mode this signal
243          * will wakeup a sleeping daemon.
244          */
245         signal(SIGUSR1, do_wakeup);
246 
247         /*
248          * Register interest for the SIGINT signal,
249          * in case we run as a server but not as a daemon
250          * we need to catch this signal if the user pressed
251          * CTRL^C in the terminal
252          */
253         signal(SIGINT, do_destroy);
254 
255         /*
256          * Register interest for the SIGHUP signal,
257          * in case we run in daemon mode this signal
258          * will reload the configuration.
259          */
260         signal(SIGHUP, do_reload);
261 
262         /*
263          * Register no interest for the SIGPIPE signal,
264          */
265         signal(SIGPIPE, SIG_IGN);
266 
267         /*
268          * Initialize the random number generator
269          */
270         srandom((unsigned)(Time_now() + getpid()));
271 
272         /*
273          * Initialize the Runtime mutex. This mutex
274          * is used to synchronize handling of global
275          * service data
276          */
277         Mutex_init(Run.mutex);
278 
279         /*
280          * Initialize heartbeat mutex and condition
281          */
282         Mutex_init(heartbeatMutex);
283         Sem_init(heartbeatCond);
284 
285         /*
286          * Get the position of the control file
287          */
288         if (! Run.files.control)
289                 Run.files.control = file_findControlFile();
290 
291         /*
292          * Initialize the system information data collecting interface
293          */
294         if (init_system_info())
295                 Run.flags |= Run_ProcessEngineEnabled;
296 
297         /*
298          * Start the Parser and create the service list. This will also set
299          * any Runtime constants defined in the controlfile.
300          */
301         if (! parse(Run.files.control))
302                 exit(1);
303 
304         /*
305          * Initialize the log system
306          */
307         if (! Log_init())
308                 exit(1);
309 
310         /*
311          * Did we find any service ?
312          */
313         if (! servicelist) {
314                 Log_error("No service has been specified\n");
315                 exit(0);
316         }
317 
318         /*
319          * Initialize Runtime file variables
320          */
321         file_init();
322 
323         /*
324          * Should we print debug information ?
325          */
326         if (Run.debug) {
327                 Util_printRunList();
328                 Util_printServiceList();
329         }
330 
331         /*
332          * Reap any stray child processes we may have created
333          */
334         atexit(waitforchildren);
335 }
336 
337 
338 /**
339  * Re-Initialize the application - called if a
340  * monit daemon receives the SIGHUP signal.
341  */
do_reinit()342 static void do_reinit() {
343         Log_info("Reinitializing Monit -- control file '%s'\n", Run.files.control);
344 
345         /* Wait non-blocking for any children that has exited. Since we
346          reinitialize any information about children we have setup to wait
347          for will be lost. This may create zombie processes until Monit
348          itself exit. However, Monit will wait on all children that has exited
349          before it itself exit. TODO: Later refactored versions will use a
350          globale process table which a sigchld handler can check */
351         waitforchildren();
352 
353         if (Run.mmonits && heartbeatRunning) {
354                 Sem_signal(heartbeatCond);
355                 Thread_join(heartbeatThread);
356                 heartbeatRunning = false;
357         }
358 
359         Run.flags &= ~Run_DoReload;
360 
361         /* Stop http interface */
362         if (Run.httpd.flags & Httpd_Net || Run.httpd.flags & Httpd_Unix)
363                 monit_http(Httpd_Stop);
364 
365         /* Save the current state (no changes are possible now since the http thread is stopped) */
366         State_save();
367         State_close();
368 
369         /* Run the garbage collector */
370         gc();
371 
372         if (! parse(Run.files.control)) {
373                 Log_error("%s stopped -- error parsing configuration file\n", prog);
374                 exit(1);
375         }
376 
377         /* Close the current log */
378         Log_close();
379 
380         /* Reinstall the log system */
381         if (! Log_init())
382                 exit(1);
383 
384         /* Did we find any services ?  */
385         if (! servicelist) {
386                 Log_error("No service has been specified\n");
387                 exit(0);
388         }
389 
390         /* Reinitialize Runtime file variables */
391         file_init();
392 
393         if (! file_createPidFile(Run.files.pid)) {
394                 Log_error("%s stopped -- cannot create a pid file\n", prog);
395                 exit(1);
396         }
397 
398         /* Update service data from the state repository */
399         if (! State_open())
400                 exit(1);
401         State_restore();
402 
403         /* Start http interface */
404         if (can_http())
405                 monit_http(Httpd_Start);
406 
407         /* send the monit startup notification */
408         Event_post(Run.system, Event_Instance, State_Changed, Run.system->action_MONIT_START, "Monit reloaded");
409 
410         if (Run.mmonits) {
411                 Thread_create(heartbeatThread, heartbeat, NULL);
412                 heartbeatRunning = true;
413         }
414 }
415 
416 
_isMemberOfGroup(Service_T s,ServiceGroup_T g)417 static bool _isMemberOfGroup(Service_T s, ServiceGroup_T g) {
418         for (list_t m = g->members->head; m; m = m->next) {
419                 Service_T member = m->e;
420                 if (s == member)
421                         return true;
422         }
423         return false;
424 }
425 
426 
_hasParentInTheSameGroup(Service_T s,ServiceGroup_T g)427 static bool _hasParentInTheSameGroup(Service_T s, ServiceGroup_T g) {
428         for (Dependant_T d = s->dependantlist; d; d = d->next ) {
429                 Service_T parent = Util_getService(d->dependant);
430                 if (parent && _isMemberOfGroup(parent, g))
431                         return true;
432         }
433         return false;
434 }
435 
436 
437 /**
438  * Dispatch to the submitted action - actions are program arguments
439  */
do_action(List_T arguments)440 static void do_action(List_T arguments) {
441         char *action = List_pop(arguments);
442 
443         Run.flags |= Run_Once;
444 
445         if (! action) {
446                 do_default();
447         } else if (IS(action, "start")     ||
448                    IS(action, "stop")      ||
449                    IS(action, "monitor")   ||
450                    IS(action, "unmonitor") ||
451                    IS(action, "restart")) {
452                 char *service = List_pop(arguments);
453                 if (Run.mygroup || service) {
454                         int errors = 0;
455                         List_T services = List_new();
456                         if (Run.mygroup) {
457                                 for (ServiceGroup_T sg = servicegrouplist; sg; sg = sg->next) {
458                                         if (IS(Run.mygroup, sg->name)) {
459                                                 for (list_t m = sg->members->head; m; m = m->next) {
460                                                         Service_T s = m->e;
461                                                         if (IS(action, "restart") && _hasParentInTheSameGroup(s, sg)) {
462                                                                 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);
463                                                                 continue;
464                                                         }
465                                                         List_append(services, s->name);
466                                                 }
467                                                 break;
468                                         }
469                                 }
470                                 if (List_length(services) == 0) {
471                                         List_free(&services);
472                                         Log_error("Group '%s' not found\n", Run.mygroup);
473                                         exit(1);
474                                 }
475                         } else if (IS(service, "all")) {
476                                 for (Service_T s = servicelist; s; s = s->next)
477                                         List_append(services, s->name);
478                         } else {
479                                 List_append(services, service);
480                         }
481                         errors = exist_daemon() ? (HttpClient_action(action, services) ? 0 : 1) : control_service_string(services, action);
482                         List_free(&services);
483                         if (errors)
484                                 exit(1);
485                 } else {
486                         Log_error("Please specify a service name or 'all' after %s\n", action);
487                         exit(1);
488                 }
489         } else if (IS(action, "reload")) {
490                 Log_info("Reinitializing %s daemon\n", prog);
491                 kill_daemon(SIGHUP);
492         } else if (IS(action, "status")) {
493                 char *service = List_pop(arguments);
494                 if (! HttpClient_status(Run.mygroup, service))
495                         exit(1);
496         } else if (IS(action, "summary")) {
497                 char *service = List_pop(arguments);
498                 if (! HttpClient_summary(Run.mygroup, service))
499                         exit(1);
500         } else if (IS(action, "report")) {
501                 char *type = List_pop(arguments);
502                 if (! HttpClient_report(Run.mygroup, type))
503                         exit(1);
504         } else if (IS(action, "procmatch")) {
505                 char *pattern = List_pop(arguments);
506                 if (! pattern) {
507                         printf("Invalid syntax - usage: procmatch \"<pattern>\"\n");
508                         exit(1);
509                 }
510                 ProcessTree_testMatch(pattern);
511         } else if (IS(action, "quit")) {
512                 kill_daemon(SIGTERM);
513         } else if (IS(action, "validate")) {
514                 if (do_wakeupcall()) {
515                         char *service = List_pop(arguments);
516                         HttpClient_status(Run.mygroup, service);
517                 } else {
518                         _validateOnce();
519                 }
520                 exit(1);
521         } else {
522                 Log_error("Invalid argument -- %s  (-h will show valid arguments)\n", action);
523                 exit(1);
524         }
525 }
526 
527 
528 /**
529  * Finalize monit
530  */
do_exit(bool saveState)531 static void do_exit(bool saveState) {
532         set_signal_block();
533         Run.flags |= Run_Stopped;
534         if ((Run.flags & Run_Daemon) && ! (Run.flags & Run_Once)) {
535                 if (can_http())
536                         monit_http(Httpd_Stop);
537 
538                 if (Run.mmonits && heartbeatRunning) {
539                         Sem_signal(heartbeatCond);
540                         Thread_join(heartbeatThread);
541                         heartbeatRunning = false;
542                 }
543 
544                 Log_info("Monit daemon with pid [%d] stopped\n", (int)getpid());
545 
546                 /* send the monit stop notification */
547                 Event_post(Run.system, Event_Instance, State_Changed, Run.system->action_MONIT_STOP, "Monit %s stopped", VERSION);
548         }
549         if (saveState) {
550                 State_save();
551         }
552         gc();
553 #ifdef HAVE_OPENSSL
554         Ssl_stop();
555 #endif
556         exit(0);
557 }
558 
559 
560 /**
561  * Default action - become a daemon if defined in the Run object and
562  * run validate() between sleeps. If not, just run validate() once.
563  * Also, if specified, start the monit http server if in daemon mode.
564  */
do_default()565 static void do_default() {
566         if (Run.flags & Run_Daemon) {
567                 if (do_wakeupcall())
568                         exit(0);
569 
570                 Run.flags &= ~Run_Once;
571                 if (can_http()) {
572                         if (Run.httpd.flags & Httpd_Net)
573                                 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);
574                         else if (Run.httpd.flags & Httpd_Unix)
575                                 Log_info("Starting Monit %s daemon with http interface at %s\n", VERSION, Run.httpd.socket.unix.path);
576                 } else {
577                         Log_info("Starting Monit %s daemon\n", VERSION);
578                 }
579 
580                 if (! (Run.flags & Run_Foreground))
581                         daemonize();
582 
583                 if (! file_createPidFile(Run.files.pid)) {
584                         Log_error("Monit daemon died\n");
585                         exit(1);
586                 }
587 
588                 if (! State_open())
589                         exit(1);
590                 State_restore();
591 
592                 atexit(file_finalize);
593 
594                 if (Run.startdelay) {
595                         if (State_reboot()) {
596                                 time_t now = Time_monotonic();
597                                 time_t delay = now + Run.startdelay;
598 
599                                 Log_info("Monit will delay for %ds on first start after reboot ...\n", Run.startdelay);
600 
601                                 /* sleep can be interrupted by signal => make sure we paused long enough */
602                                 while (now < delay) {
603                                         sleep((unsigned int)(delay - now));
604                                         if (Run.flags & Run_Stopped)
605                                                 do_exit(false);
606                                         now = Time_monotonic();
607                                 }
608                         } else {
609                                 DEBUG("Monit delay %ds skipped -- the system boot time has not changed since last Monit start\n", Run.startdelay);
610                         }
611                 }
612 
613                 if (can_http())
614                         monit_http(Httpd_Start);
615 
616                 /* send the monit startup notification */
617                 Event_post(Run.system, Event_Instance, State_Changed, Run.system->action_MONIT_START, "Monit %s started", VERSION);
618 
619                 if (Run.mmonits) {
620                         Thread_create(heartbeatThread, heartbeat, NULL);
621                         heartbeatRunning = true;
622                 }
623 
624                 while (true) {
625                         validate();
626 
627                         /* In the case that there is no pending action then sleep */
628                         if (! (Run.flags & Run_ActionPending) && ! interrupt())
629                                 sleep(Run.polltime);
630 
631                         if (Run.flags & Run_DoWakeup) {
632                                 Run.flags &= ~Run_DoWakeup;
633                                 Log_info("Awakened by User defined signal 1\n");
634                         }
635 
636                         if (Run.flags & Run_Stopped) {
637                                 do_exit(true);
638                         } else if (Run.flags & Run_DoReload) {
639                                 do_reinit();
640                         } else {
641                                 State_saveIfDirty();
642                         }
643                 }
644         } else {
645                 _validateOnce();
646         }
647 }
648 
649 
650 /**
651  * Handle program options - Options set from the commandline
652  * takes precedence over those found in the control file
653  */
handle_options(int argc,char ** argv,List_T arguments)654 static void handle_options(int argc, char **argv, List_T arguments) {
655         int opt;
656         int deferred_opt = 0;
657         opterr = 0;
658         Run.mygroup = NULL;
659         const char *shortopts = "+c:d:g:l:p:s:HIirtvVhB";
660         while (optind < argc) {
661 #ifdef HAVE_GETOPT_LONG
662                 struct option longopts[] = {
663                         {"conf",        required_argument,      NULL,   'c'},
664                         {"daemon",      required_argument,      NULL,   'd'},
665                         {"group",       required_argument,      NULL,   'g'},
666                         {"logfile",     required_argument,      NULL,   'l'},
667                         {"pidfile",     required_argument,      NULL,   'p'},
668                         {"statefile",   required_argument,      NULL,   's'},
669                         {"hash",        optional_argument,      NULL,   'H'},
670                         {"id",          no_argument,            NULL,   'i'},
671                         {"help",        no_argument,            NULL,   'h'},
672                         {"resetid",     no_argument,            NULL,   'r'},
673                         {"test",        no_argument,            NULL,   't'},
674                         {"verbose",     no_argument,            NULL,   'v'},
675                         {"batch",       no_argument,            NULL,   'B'},
676                         {"interactive", no_argument,            NULL,   'I'},
677                         {"version",     no_argument,            NULL,   'V'},
678                         {0}
679                 };
680                 if ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
681 #else
682                 if ((opt = getopt(argc, argv, shortopts)) != -1)
683 #endif
684                 {
685                         switch (opt) {
686                                 case 'c':
687                                 {
688                                         char *f = optarg;
689                                         char realpath[PATH_MAX] = {};
690                                         if (Run.files.control) {
691                                                 Log_warning("WARNING: The -c option was specified multiple times, only the last value will be used\n");
692                                                 FREE(Run.files.control);
693                                         }
694                                         if (f[0] != SEPARATOR_CHAR)
695                                                 f = File_getRealPath(optarg, realpath);
696                                         if (! f)
697                                                 THROW(AssertException, "The control file '%s' does not exist at %s", Str_trunc(optarg, 80), Dir_cwd((char[STRLEN]){}, STRLEN));
698                                         if (! File_isFile(f))
699                                                 THROW(AssertException, "The control file '%s' is not a file", Str_trunc(f, 80));
700                                         if (! File_isReadable(f))
701                                                 THROW(AssertException, "The control file '%s' is not readable", Str_trunc(f, 80));
702                                         Run.files.control = Str_dup(f);
703                                         break;
704                                 }
705                                 case 'd':
706                                 {
707                                         Run.flags |= Run_Daemon;
708                                         if (sscanf(optarg, "%d", &Run.polltime) != 1 || Run.polltime < 1) {
709                                                 Log_error("Option -%c requires a natural number\n", opt);
710                                                 exit(1);
711                                         }
712                                         break;
713                                 }
714                                 case 'g':
715                                 {
716                                         if (Run.mygroup) {
717                                                 Log_warning("WARNING: The -g option was specified multiple times, only the last value will be used\n");
718                                                 FREE(Run.mygroup);
719                                         }
720                                         Run.mygroup = Str_dup(optarg);
721                                         break;
722                                 }
723                                 case 'l':
724                                 {
725                                         if (Run.files.log) {
726                                                 Log_warning("WARNING: The -l option was specified multiple times, only the last value will be used\n");
727                                                 FREE(Run.files.log);
728                                         }
729                                         Run.files.log = Str_dup(optarg);
730                                         if (IS(Run.files.log, "syslog"))
731                                                 Run.flags |= Run_UseSyslog;
732                                         Run.flags |= Run_Log;
733                                         break;
734                                 }
735                                 case 'p':
736                                 {
737                                         if (Run.files.pid) {
738                                                 Log_warning("WARNING: The -p option was specified multiple times, only the last value will be used\n");
739                                                 FREE(Run.files.pid);
740                                         }
741                                         Run.files.pid = Str_dup(optarg);
742                                         break;
743                                 }
744                                 case 's':
745                                 {
746                                         if (Run.files.state) {
747                                                 Log_warning("WARNING: The -s option was specified multiple times, only the last value will be used\n");
748                                                 FREE(Run.files.state);
749                                         }
750                                         Run.files.state = Str_dup(optarg);
751                                         break;
752                                 }
753                                 case 'I':
754                                 {
755                                         Run.flags |= Run_Foreground;
756                                         break;
757                                 }
758                                 case 'i':
759                                 {
760                                         deferred_opt = 'i';
761                                         break;
762                                 }
763                                 case 'r':
764                                 {
765                                         deferred_opt = 'r';
766                                         break;
767                                 }
768                                 case 't':
769                                 {
770                                         deferred_opt = 't';
771                                         break;
772                                 }
773                                 case 'v':
774                                 {
775                                         Run.debug++;
776                                         break;
777                                 }
778                                 case 'H':
779                                 {
780                                         if (argc > optind)
781                                                 Checksum_printHash(argv[optind]);
782                                         else
783                                                 Checksum_printHash(NULL);
784                                         exit(0);
785                                         break;
786                                 }
787                                 case 'V':
788                                 {
789                                         version();
790                                         exit(0);
791                                         break;
792                                 }
793                                 case 'h':
794                                 {
795                                         help();
796                                         exit(0);
797                                         break;
798                                 }
799                                 case 'B':
800                                 {
801                                         Run.flags |= Run_Batch;
802                                         break;
803                                 }
804                                 case '?':
805                                 {
806                                         switch (optopt) {
807                                                 case 'c':
808                                                 case 'd':
809                                                 case 'g':
810                                                 case 'l':
811                                                 case 'p':
812                                                 case 's':
813                                                 {
814                                                         Log_error("Option -- %c requires an argument\n", optopt);
815                                                         break;
816                                                 }
817                                                 default:
818                                                 {
819                                                         Log_error("Invalid option -- %c  (-h will show valid options)\n", optopt);
820                                                 }
821                                         }
822                                         exit(1);
823                                 }
824                         }
825                 } else {
826                         List_append(arguments, argv[optind++]);
827                 }
828         }
829         /* Handle deferred options to make arguments to the program positional
830          independent. These options are handled last, here as they represent exit
831          points in the application and the control-file might be set with -c and
832          these options need to respect the new control-file location as they call
833          do_init */
834         switch (deferred_opt) {
835                 case 't':
836                 {
837                         do_init(); // Parses control file and initialize program, exit on error
838                         printf("Control file syntax OK\n");
839                         exit(0);
840                         break;
841                 }
842                 case 'r':
843                 {
844                         do_init();
845                         assert(Run.id);
846                         printf("Reset Monit Id? [y/N]> ");
847                         if (tolower(getchar()) == 'y') {
848                                 File_delete(Run.files.id);
849                                 Util_monitId(Run.files.id);
850                                 kill_daemon(SIGHUP); // make any running Monit Daemon reload the new ID-File
851                         }
852                         exit(0);
853                         break;
854                 }
855                 case 'i':
856                 {
857                         do_init();
858                         assert(Run.id);
859                         printf("Monit ID: %s\n", Run.id);
860                         exit(0);
861                         break;
862                 }
863         }
864 }
865 
866 
867 /**
868  * Print the program's help message
869  */
help()870 static void help() {
871         printf(
872                "Usage: %s [options]+ [command]\n"
873                "Options are as follows:\n"
874                " -c file       Use this control file\n"
875                " -d n          Run as a daemon once per n seconds\n"
876                " -g name       Set group name for monit commands\n"
877                " -l logfile    Print log information to this file\n"
878                " -p pidfile    Use this lock file in daemon mode\n"
879                " -s statefile  Set the file monit should write state information to\n"
880                " -I            Do not run in background (needed when run from init)\n"
881                " --id          Print Monit's unique ID\n"
882                " --resetid     Reset Monit's unique ID. Use with caution\n"
883                " -B            Batch command line mode (do not output tables or colors)\n"
884                " -t            Run syntax check for the control file\n"
885                " -v            Verbose mode, work noisy (diagnostic output)\n"
886                " -vv           Very verbose mode, same as -v plus log stacktrace on error\n"
887                " -H [filename] Print SHA1 and MD5 hashes of the file or of stdin if the\n"
888                "               filename is omitted; monit will exit afterwards\n"
889                " -V            Print version number and patchlevel\n"
890                " -h            Print this text\n"
891                "Optional commands are as follows:\n"
892                " start all             - Start all services\n"
893                " start <name>          - Only start the named service\n"
894                " stop all              - Stop all services\n"
895                " stop <name>           - Stop the named service\n"
896                " restart all           - Stop and start all services\n"
897                " restart <name>        - Only restart the named service\n"
898                " monitor all           - Enable monitoring of all services\n"
899                " monitor <name>        - Only enable monitoring of the named service\n"
900                " unmonitor all         - Disable monitoring of all services\n"
901                " unmonitor <name>      - Only disable monitoring of the named service\n"
902                " reload                - Reinitialize monit\n"
903                " status [name]         - Print full status information for service(s)\n"
904                " summary [name]        - Print short status information for service(s)\n"
905                " report [up|down|..]   - Report state of services. See manual for options\n"
906                " quit                  - Kill the monit daemon process\n"
907                " validate              - Check all services and start if not running\n"
908                " procmatch <pattern>   - Test process matching pattern\n",
909                prog);
910 }
911 
912 /**
913  * Print version information
914  */
version()915 static void version() {
916         printf("This is Monit version %s\n", VERSION);
917         printf("Built with");
918 #ifndef HAVE_OPENSSL
919         printf("out");
920 #endif
921         printf(" ssl, with");
922 #ifndef HAVE_IPV6
923         printf("out");
924 #endif
925         printf(" ipv6, with");
926 #ifndef HAVE_LIBZ
927         printf("out");
928 #endif
929         printf(" compression, with");
930 #ifndef HAVE_LIBPAM
931         printf("out");
932 #endif
933         printf(" pam and with");
934 #ifndef HAVE_LARGEFILES
935         printf("out");
936 #endif
937         printf(" large files\n");
938         printf("Copyright (C) 2001-2021 Tildeslash Ltd. All Rights Reserved.\n");
939 }
940 
941 
942 /**
943  * M/Monit heartbeat thread
944  */
heartbeat(void * args)945 static void *heartbeat(__attribute__ ((unused)) void *args) {
946         set_signal_block();
947         Log_info("M/Monit heartbeat started\n");
948         LOCK(heartbeatMutex)
949         {
950                 while (! interrupt()) {
951                         MMonit_send(NULL);
952                         struct timespec wait = {.tv_sec = Time_now() + Run.polltime, .tv_nsec = 0};
953                         Sem_timeWait(heartbeatCond, heartbeatMutex, wait);
954                 }
955         }
956         END_LOCK;
957 #ifdef HAVE_OPENSSL
958         Ssl_threadCleanup();
959 #endif
960         Log_info("M/Monit heartbeat stopped\n");
961         return NULL;
962 }
963 
964 
965 /**
966  * Signalhandler for a daemon reload call
967  */
do_reload(int sig)968 static void do_reload(__attribute__ ((unused)) int sig) {
969         Run.flags |= Run_DoReload;
970 }
971 
972 
973 /**
974  * Signalhandler for monit finalization
975  */
do_destroy(int sig)976 static void do_destroy(__attribute__ ((unused)) int sig) {
977         Run.flags |= Run_Stopped;
978 }
979 
980 
981 /**
982  * Signalhandler for a daemon wakeup call
983  */
do_wakeup(int sig)984 static void do_wakeup(__attribute__ ((unused)) int sig) {
985         Run.flags |= Run_DoWakeup;
986 }
987 
988 
989 /* A simple non-blocking reaper to ensure that we wait-for and reap all/any stray child processes
990  we may have created and not waited on, so we do not create any zombie processes at exit */
waitforchildren(void)991 static void waitforchildren(void) {
992         while (waitpid(-1, NULL, WNOHANG) > 0) ;
993 }
994