/* * Copyright (C), 2000-2007 by the monit project group. * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #ifdef HAVE_STDIO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_GETOPT_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #include "monitor.h" #include "net.h" #include "ssl.h" #include "process.h" #include "md5.h" #include "sha.h" #include "state.h" #include "event.h" /** * DESCRIPTION * monit - system for monitoring services on a Unix system * * SYNOPSIS * monit [options] {arguments} * * @author Jan-Henrik Haukeland, * @author Martin Pala * @author Christian Hopp, * * @version \$Id: monitor.c,v 1.136 2007/07/25 12:54:29 hauk Exp $ * * @file */ /* -------------------------------------------------------------- Prototypes */ static void do_init(); /* Initialize this application */ static void do_reinit(); /* Re-initialize the runtime application */ static void do_action(char **); /* Dispatch to the submitted action */ static void do_exit(); /* Finalize monit */ static void do_default(); /* Do default action */ static void handle_options(int, char **); /* Handle program options */ static void help(); /* Print program help message to stdout */ static void version(); /* Print version information */ static RETSIGTYPE do_reload(int); /* Signalhandler for a daemon reload */ static RETSIGTYPE do_destroy(int); /* Signalhandler for monit finalization */ static RETSIGTYPE do_wakeup(int); /* Signalhandler for a daemon wakeup call */ /* ------------------------------------------------------------------ Public */ /** * The Prime mover */ int main(int argc, char **argv) { prog= Util_basename(argv[0]); init_env(); handle_options(argc, argv); do_init(); do_action(argv); do_exit(); return 0; } /** * Wakeup a sleeping monit daemon. * Returns TRUE on success otherwise FALSE */ int do_wakeupcall() { pid_t pid; if((pid= exist_daemon()) > 0) { kill(pid, SIGUSR1); LogInfo("%s daemon at %d awakened\n", prog, pid); return TRUE; } return FALSE; } /* ----------------------------------------------------------------- Private */ /** * Initialize this application - Register signal handlers, * Parse the control file and initialize the program's * datastructures and the log system. */ static void do_init() { int status; /* * Register interest for the SIGTERM signal, * in case we run in daemon mode this signal * will terminate a running daemon. */ signal(SIGTERM, do_destroy); /* * Register interest for the SIGUSER1 signal, * in case we run in daemon mode this signal * will wakeup a sleeping daemon. */ signal(SIGUSR1, do_wakeup); /* * Register interest for the SIGINT signal, * in case we run as a server but not as a daemon * we need to catch this signal if the user pressed * CTRL^C in the terminal */ signal(SIGINT, do_destroy); /* * Register interest for the SIGHUP signal, * in case we run in daemon mode this signal * will reload the configuration. */ signal(SIGHUP, do_reload); /* * Register no interest for the SIGPIPE signal, */ signal(SIGPIPE, SIG_IGN); /* * Initialize the Runtime mutex. This mutex * is used to synchronize handling of global * service data */ status= pthread_mutex_init(&Run.mutex, NULL); if(status != 0) { LogError("%s: Cannot initialize mutex -- %s\n", prog, strerror(status)); exit(1); } /* * Get the position of the control file */ if(! Run.controlfile) { Run.controlfile= File_findControlFile(); } /* * Initialize the process information gathering interface */ Run.doprocess= init_process_info(); /* * Start the Parser and create the service list. This will also set * any Runtime constants defined in the controlfile. */ if(! parse(Run.controlfile)) { exit(1); } /* * Stop and report success if we are just validating the Control * file syntax. The previous parse statement exits the program with * an error message if a syntax error is present in the control * file. */ if(Run.testing) { LogInfo("Control file syntax OK\n"); exit(0); } /* * Initialize the log system */ if(! log_init()) { exit(1); } /* * Did we find any service ? */ if(! servicelist) { LogError("%s: No services has been specified\n", prog); exit(0); } /* * Initialize Runtime file variables */ File_init(); /* * Should we print debug information ? */ if(Run.debug) { Util_printRunList(); Util_printServiceList(); } } /** * Re-Initialize the application - called if a * monit daemon receives the SIGHUP signal. */ static void do_reinit() { Run.doreload= FALSE; LogInfo("Awakened by the SIGHUP signal\n"); LogInfo("Reinitializing %s - Control file '%s'\n", prog, Run.controlfile); /* Stop http interface */ if(Run.dohttpd) monit_http(STOP_HTTP); /* Save the current state (no changes are possible now since the http thread is stopped) */ State_save(); /* wait for all wait_start threads to finish */ while(Run.wait_start) sleep(1); /* Run the garbage collector */ gc(); if(! parse(Run.controlfile)) { LogError("%s daemon died\n", prog); exit(1); } /* Close the current log */ log_close(); /* Reinstall the log system */ if(! log_init()) exit(1); /* Did we find any services ? */ if(! servicelist) { LogError("%s: No services has been specified\n", prog); exit(0); } /* Reinitialize Runtime file variables */ File_init(); if(! File_createPidFile(Run.pidfile)) { LogError("%s daemon died\n", prog); exit(1); } /* Update service data from the state repository */ State_update(); /* Start http interface */ if(can_http()) monit_http(START_HTTP); /* send the monit startup notification */ Event_post(Run.system, EVENT_INSTANCE, STATE_FAILED, Run.system->action_MONIT_RELOAD, "Monit reloaded"); } /** * Dispatch to the submitted action - actions are program arguments */ static void do_action(char **args) { char *action= args[optind]; char *service= args[++optind]; Run.once= TRUE; if(! action) { do_default(); } else if(IS(action, "start") || IS(action, "stop") || IS(action, "monitor") || IS(action, "unmonitor") || IS(action, "restart") ) { if(service) { void (*_control_service)(const char *, const char *)= exist_daemon()?control_service_daemon:control_service_string; if(IS(service, "all")) { Service_T s= NULL; for(s= servicelist; s; s= s->next) { if(s->visited) continue; if( !Run.mygroup || IS(s->group, Run.mygroup) ) { _control_service(s->name, action); } } } else { _control_service(service, action); } } else { LogError("%s: please specify the configured service " "name or 'all' after %s\n", prog, action); exit(1); } } else if(IS(action, "reload")) { LogInfo("Reinitializing monit daemon\n", prog); kill_daemon(SIGHUP); } else if(IS(action, "status")) { status(LEVEL_NAME_FULL); } else if(IS(action, "summary")) { status(LEVEL_NAME_SUMMARY); } else if(IS(action, "quit")) { kill_daemon(SIGTERM); } else if(IS(action, "validate")) { validate(); } else { LogError("%s: invalid argument -- %s (-h will show " "valid arguments)\n", prog, action); exit(1); } } /** * Finalize monit */ static void do_exit() { sigset_t ns; set_signal_block(&ns, NULL); Run.stopped= TRUE; if(Run.isdaemon && !Run.once) { if(can_http()) monit_http(STOP_HTTP); LogInfo("%s daemon with pid [%d] killed\n", prog, (int)getpid()); /* send the monit stop notification */ Event_post(Run.system, EVENT_INSTANCE, STATE_FAILED, Run.system->action_MONIT_STOP, "Monit stopped"); } /* wait for all wait_start threads to finish */ while(Run.wait_start) sleep(1); gc(); exit(0); } /** * Default action - become a daemon if defined in the Run object and * run validate() between sleeps. If not, just run validate() once. * Also, if specified, start the monit http server if in deamon mode. */ static void do_default() { if(Run.isdaemon) { if(do_wakeupcall()) exit(0); Run.once= FALSE; if(can_http()) LogInfo("Starting %s daemon with http interface at [%s:%d]\n", prog, Run.bind_addr?Run.bind_addr:"*", Run.httpdport); else LogInfo("Starting %s daemon\n", prog); if(Run.init != TRUE) daemonize(); if(! File_createPidFile(Run.pidfile)) { LogError("%s daemon died\n", prog); exit(1); } if(State_shouldUpdate()) State_update(); atexit(File_finalize); if(can_http()) monit_http(START_HTTP); /* send the monit startup notification */ Event_post(Run.system, EVENT_INSTANCE, STATE_FAILED, Run.system->action_MONIT_START, "Monit started"); while(TRUE) { validate(); State_save(); /* In the case that there is no pending action then sleep */ if(!Run.doaction) sleep(Run.polltime); if(Run.dowakeup) { Run.dowakeup = FALSE; LogInfo("Awakened by User defined signal 1\n"); } if(Run.stopped) { do_exit(); } else if(Run.doreload) { do_reinit(); } else { Event_post(Run.system, EVENT_INSTANCE, STATE_PASSED, Run.system->action_MONIT_START, "Monit has not changed"); Event_post(Run.system, EVENT_INSTANCE, STATE_PASSED, Run.system->action_MONIT_RELOAD, "Monit has not changed"); } } } else { validate(); } } /** * Handle program options - Options set from the commandline * takes precedence over those found in the control file */ static void handle_options(int argc, char **argv) { int opt; opterr= 0; Run.mygroup= NULL; while((opt= getopt(argc,argv,"c:d:g:l:p:s:iItvVhH")) != -1) { switch(opt) { case 'c': Run.controlfile= xstrdup(optarg); break; case 'd': Run.isdaemon= TRUE; sscanf(optarg, "%d", &Run.polltime); if(Run.polltime<1) { LogError("%s: option -%c requires a natural number\n", prog, opt); exit(1); } break; case 'g': Run.mygroup= xstrdup(optarg); break; case 'l': Run.logfile= xstrdup(optarg); if(IS(Run.logfile, "syslog")) Run.use_syslog= TRUE; Run.dolog= TRUE; break; case 'p': Run.pidfile= xstrdup(optarg); break; case 's': Run.statefile= xstrdup(optarg); break; case 'I': Run.init= TRUE; break; case 't': Run.testing= TRUE; break; case 'v': Run.debug= TRUE; break; case 'H': if (argc > optind) { Util_printHash(argv[optind]); } else { Util_printHash(NULL); } exit(0); break; case 'V': version(); exit(0); break; case 'h': help(); exit(0); break; case '?': switch(optopt) { case 'c': case 'd': case 'g': case 'l': case 'p': case 's': LogError("%s: option -- %c requires an argument\n", prog, optopt); break; default: LogError("%s: invalid option -- %c (-h will show valid options)\n", prog, optopt); } exit(1); } } } /** * Print the program's help message */ static void help() { printf("Usage: %s [options] {arguments}\n", prog); printf("Options are as follows:\n"); printf(" -c file Use this control file\n"); printf(" -d n Run as a daemon once per n seconds\n"); printf(" -g name Set group name for start, stop, restart and status\n"); printf(" -l logfile Print log information to this file\n"); printf(" -p pidfile Use this lock file in daemon mode\n"); printf(" -s statefile Set the file monit should write state information to\n"); printf(" -I Do not run in background (needed for run from init)\n"); printf(" -t Run syntax check for the control file\n"); printf(" -v Verbose mode, work noisy (diagnostic output)\n"); printf(" -H [filename] Print SHA1 and MD5 hashes of the file or of stdin if the\n"); printf(" filename is omited; monit will exit afterwards\n"); printf(" -V Print version number and patchlevel\n"); printf(" -h Print this text\n"); printf("Optional action arguments for non-daemon mode are as follows:\n"); printf(" start all - Start all services\n"); printf(" start name - Only start the named service\n"); printf(" stop all - Stop all services\n"); printf(" stop name - Only stop the named service\n"); printf(" restart all - Stop and start all services\n"); printf(" restart name - Only restart the named service\n"); printf(" monitor all - Enable monitoring of all services\n"); printf(" monitor name - Only enable monitoring of the named service\n"); printf(" unmonitor all - Disable monitoring of all services\n"); printf(" unmonitor name - Only disable monitoring of the named service\n"); printf(" reload - Reinitialize monit\n"); printf(" status - Print full status information for each service\n"); printf(" summary - Print short status information for each service\n"); printf(" quit - Kill monit daemon process\n"); printf(" validate - Check all services and start if not running\n"); printf("\n"); printf("(Action arguments operate on services defined in the control file)\n"); } /** * Print version information */ static void version() { printf("This is monit version %s\n", VERSION); printf("Copyright (C) 2000-2007 by the monit project group."); printf(" All Rights Reserved.\n"); } /** * Signalhandler for a daemon reload call */ static RETSIGTYPE do_reload(int sig) { Run.doreload= TRUE; } /** * Signalhandler for monit finalization */ static RETSIGTYPE do_destroy(int sig) { Run.stopped= TRUE; } /** * Signalhandler for a daemon wakeup call */ static RETSIGTYPE do_wakeup(int sig) { Run.dowakeup= TRUE; }