monit-dev
[Top][All Lists]
Advanced

[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;

}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]