[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[monit-dev] syncrone monit : prototype for monitor.c
From: |
Eric Pailleau |
Subject: |
[monit-dev] syncrone monit : prototype for monitor.c |
Date: |
Wed, 22 Apr 2009 09:22:29 +0200 |
User-agent: |
Thunderbird 2.0.0.21 (X11/20090330) |
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, <address@hidden>
* @author Martin Pala <address@hidden>
* @author Christian Hopp, <address@hidden>
*
* @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] syncrone monit : prototype for monitor.c,
Eric Pailleau <=