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