Dear all, as I told you, please find joined my prototype for monitor.c .
It must not be used in production. Il it based on 4.10 code, only replace monitor.c and add a #define HAVE_INOTIFY_H 1 in config.h (obviously, check you have really inotify in your kernel :>) ...) Please read my comments to decide the way it can be included in monit for compatibility with the way monit is coded. Hope my work can start a new major feature for monit. Best regards.
/* * 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 <http://www.gnu.org/licenses/>. */ #include <config.h> #if HAVE_INOTIFY_H #include <sys/select.h> #include <sys/inotify.h> #endif #ifdef HAVE_STDIO_H #include <stdio.h> #endif #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif #ifdef HAVE_ERRNO_H #include <errno.h> #endif #ifdef HAVE_SIGNAL_H #include <signal.h> #endif #ifdef HAVE_GETOPT_H #include <getopt.h> #endif #ifdef HAVE_STRING_H #include <string.h> #endif #ifdef HAVE_STRINGS_H #include <strings.h> #endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> #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, <h...@tildeslash.com> * @author Martin Pala <mart...@tildeslash.com> * @author Christian Hopp, <ch...@iei.tu-clausthal.de> * * @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 HAVE_INOTIFY_H int fd, wd, tmp; size_t r; fd_set fds; char buffer[8192]; struct inotify_event *event; struct timeval tv; Service_T s; /* A struct may be better to store link between inotify identifier and service */ char *wdlink[1000]; #endif if(Run.isdaemon) { #if HAVE_INOTIFY_H /* inotify initialisation */ fd = inotify_init(); if (fd < 0) { LogError("%s daemon exit. Cannot init inotify file descriptor\n", prog); exit(1); } #endif 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"); #if HAVE_INOTIFY_H /* Add watcher on daemon pidfile : monit can monit its own pidfile */ wd = inotify_add_watch(fd, Run.pidfile, IN_ALL_EVENTS); if (wd < 0) { LogError("%s daemon exit. Cannot add watcher on daemon pidfile\n", prog); exit(1); } wdlink[wd]=""; /* Add watcher on all files that needs to be watched */ for(s= servicelist; s; s= s->next) { tmp=0; /* Below switch case is only for testing : community/mainteners may choose the better mask for each case. */ /* Mask might be chosen also depending other values than type : uid/gid test for IN_ATTRIB, etc ... */ switch(s->type){ case TYPE_DIRECTORY : case TYPE_FILE : case TYPE_PROCESS : tmp=inotify_add_watch(fd , (const char *)s->path , IN_DELETE_SELF | IN_MOVE_SELF| IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE) ; break; case TYPE_DEVICE : case TYPE_FIFO : /* Well, need to think about it ... */ break; case TYPE_HOST: case TYPE_SYSTEM: /* Cannot be watched with inotify */ break; } if(tmp<0){ LogError("Fail to add watcher on path : %s\n", s->path); }else if(tmp>0){ wdlink[tmp]=s->name ; DEBUG("Add watcher for service '%s' on path : %s\n", wdlink[tmp], s->path); } } #endif while(TRUE) { #if HAVE_INOTIFY_H /* TODO : a 'validate' function only for host/socket. example : validateNetwork */ /* May be in an thread , to be independant from time before select return * or many file/directory events will do many host/socket tests. * well, a thread for both with a mutex might be better ...*/ /* validateNetwork(); */ State_save(); FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = (Run.polltime>1)? (Run.polltime - 1) : 0 ; tv.tv_usec = 0; /* Setting timeout on select with classical Run.polltime value, and gracefull nice time of 1 second (see below) */ if (select(fd + 1, &fds, NULL, NULL, &tv) > 0) { /* Getting informations on coming event */ buffer[0]='\0'; r = read(fd, buffer, sizeof(buffer)); if (r < 0) { LogError("%s daemon exit. Abnormal read error of inotify file descriptor\n", prog); exit(1); }else if(r > 0){ event = (struct inotify_event *) buffer; DEBUG("Event on watched file %d for service %s\n", event->wd, wdlink[event->wd]); /* Creating a validate_inotified( name ) function would be better ! */ /* TODO ! This is only for testing */ check_device(Util_getService(wdlink[event->wd])); check_directory(Util_getService(wdlink[event->wd])); check_file(Util_getService(wdlink[event->wd])); } } /* sleeping at least 1 second to be nice with other processes when flooding events occurs */ sleep(1); #else validate(); State_save(); /* In the case that there is no pending action then sleep */ if(!Run.doaction) sleep(Run.polltime); #endif 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; }
_______________________________________________ monit-dev mailing list monit-dev@nongnu.org http://lists.nongnu.org/mailman/listinfo/monit-dev