monit-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

new event engine patch++ (various fixes)


From: Martin Pala
Subject: new event engine patch++ (various fixes)
Date: Tue, 23 Mar 2004 00:22:17 +0100
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040312 Debian/1.6-3

Hi,

here is another (current) version of event engine patch.

Fixes:
- properly handle alert
- properly handle recurring passed and failed events
- send mail only once (on the state change)
- simplifications


Martin

Martin Pala wrote:
Sorry, attached patch contained bison debug output, here is new (smaller) one.

Martin

Martin Pala wrote:

Hi,

here is first preview of event handler refactoring. It compiles, roughly works and is tested softly. Because the event engine refactoring affected the code base a lot, lot of bugs are expected to appear.

Syntax is backward compatible (for example):

  IF rule THEN action [ELSE IF RECOVERED THEN action]

  if failed checksum then exec "/grey/hound start"
  else if recovered then exec "/grey/hound stop"

The part [else if recovered ...] is optional. In the case that it is ommited, alert action is supposed as default on recovery, thus following rules are equivalent:

  if failed checksum then alert

  if failed checksum then alert else if recovered then alert


The behaviour changed externaly and internaly - event engine is general, evets and actions are strictly separated, some were added, some removed and some changed.

Syntax, alert output, etc. are tentative - it is possible to change it.


In the case of problem, monit will do appropriate action, for example if monitored process doesn't exist any more, it will restart it (default action for EVENT_NONEXIST is ACTION_RESTART) and send alert:

--
Subject: monit alert --  Does not exist slapd

Does not exist Service slapd

    Date:   Mon, 22 Mar 2004 22:18:30 +0100
    Action: restart
    Host:   unicorn

Your faithful employee,
monit

Process 'slapd' is not running
--

and as soon as the service is up again (EVENT_NONEXIST is not true any more), it will send alert (default action for EVENT_NONEXIST recovery is ACTION_ALERT)

--
Subject: monit alert --  Exists slapd
Exists Service slapd

    Date:   Mon, 22 Mar 2004 22:18:35 +0100
    Action: alert
    Host:   unicorn

Your faithful employee,
monit

Process 'slapd' is running with pid 11056
--


If you agree with the architecture, i can commit it to cvs after 4.2 will be released.


Patch testing, fixes, etc. are highly appreciated :)


TODO:
- extensive testing
- documentation (behaviour and syntax)



Martin
diff -Naur monit-cvs20040315/alert.c monit-cvs20040315-mp/alert.c
--- monit-cvs20040315/alert.c   2004-02-19 16:43:10.000000000 +0100
+++ monit-cvs20040315-mp/alert.c        2004-03-19 18:43:10.000000000 +0100
@@ -66,7 +66,7 @@
 
 static void copy_mail(Mail_T, Mail_T);
 static void replace_bare_linefeed(Mail_T *);
-static void substitute(Mail_T*, char *name, const char *event);
+static void substitute(Mail_T*, char *, const char *, const char *);
 
 
 /* ------------------------------------------------------------------ Public */
@@ -79,7 +79,7 @@
 void handle_alert(Event_T E) {
 
   Service_T s;
-  
+
   ASSERT(E);
 
   s= Event_get_source(E);
@@ -113,7 +113,7 @@
          tmp->opt_message= xstrdup(Event_get_message(E));
        }
 
-       substitute(&tmp, s->name, EVENT_DESCRIPTION(E));
+       substitute(&tmp, s->name, EVENT_DESCRIPTION(E), 
EVENTACTION_DESCRIPTION(E));
        
        replace_bare_linefeed(&tmp);
        
@@ -156,7 +156,7 @@
        if(Event_get_message(E))
          tmp->opt_message= xstrdup(Event_get_message(E));
 
-       substitute(&tmp, s->name, EVENT_DESCRIPTION(E));
+       substitute(&tmp, s->name, EVENT_DESCRIPTION(E), 
EVENTACTION_DESCRIPTION(E));
        
        replace_bare_linefeed(&tmp);
        
@@ -183,12 +183,12 @@
 }
 
 
-static void substitute(Mail_T *m, char *name, const char *event) {
+static void substitute(Mail_T *m, char *name, const char *event, const char 
*action) {
 
   char host[STRLEN];
   char *now= get_RFC822date(NULL);
 
-  ASSERT(m && name && event);
+  ASSERT(m && name && event && action);
 
   if(gethostname(host, sizeof(host)) < 0) {
     snprintf(host, STRLEN, "%s", LOCALHOST);
@@ -205,6 +205,8 @@
   replace_string(&(*m)->message, "$SERVICE", name);
   replace_string(&(*m)->subject, "$EVENT", event);
   replace_string(&(*m)->message, "$EVENT", event);
+  replace_string(&(*m)->subject, "$ACTION", action);
+  replace_string(&(*m)->message, "$ACTION", action);
  
   FREE(now);
 
diff -Naur monit-cvs20040315/alert.h monit-cvs20040315-mp/alert.h
--- monit-cvs20040315/alert.h   2004-02-06 21:02:20.000000000 +0100
+++ monit-cvs20040315-mp/alert.h        2004-03-19 13:56:37.000000000 +0100
@@ -31,8 +31,14 @@
 #define ALERT_SUBJECT "monit alert --  $EVENT $SERVICE"
 
 /** Default mail message */
-#define ALERT_MESSAGE "$EVENT Service $SERVICE \r\n\r\n\tDate: $DATE\r\n"\
-             "\tHost: $HOST\r\n\r\nYour faithful employee,\r\nmonit\r\n"
+#define ALERT_MESSAGE "$EVENT Service $SERVICE \r\n"\
+                      "\r\n"\
+                      "\tDate:   $DATE\r\n"\
+                      "\tAction: $ACTION\r\n"\
+                     "\tHost:   $HOST\r\n"\
+                     "\r\n"\
+                     "Your faithful employee,\r\n"\
+                     "monit\r\n"
 
 
 /**
diff -Naur monit-cvs20040315/control.c monit-cvs20040315-mp/control.c
--- monit-cvs20040315/control.c 2004-02-06 21:02:20.000000000 +0100
+++ monit-cvs20040315-mp/control.c      2004-03-22 17:45:39.000000000 +0100
@@ -548,7 +548,9 @@
   }
   
   if(!is_process_running(s)) {
-    Event_post(s, EVENT_FAILED, "Failed to start '%s'\n", s->name);
+    Event_post(s, EVENT_EXEC, TRUE, s->action_EXEC, "failed to start '%s'", 
s->name);
+  } else {
+    Event_post(s, EVENT_EXEC, FALSE, s->action_EXEC, "started '%s'", s->name);
   }
 
   return NULL;
@@ -578,8 +580,10 @@
   }
 
   if(is_process_running(s)) {
-    Event_post(s, EVENT_FAILED, "Failed to stop '%s'\n", s->name);
+    Event_post(s, EVENT_EXEC, TRUE, s->action_EXEC, "failed to stop '%s'", 
s->name);
     return FALSE;
+  } else {
+    Event_post(s, EVENT_EXEC, FALSE, s->action_EXEC, "stopped '%s'", s->name);
   }
 
   return TRUE;
diff -Naur monit-cvs20040315/env.c monit-cvs20040315-mp/env.c
--- monit-cvs20040315/env.c     2004-03-12 17:27:03.000000000 +0100
+++ monit-cvs20040315-mp/env.c  2004-03-16 13:35:33.000000000 +0100
@@ -88,23 +88,6 @@
   /* Setup program environment */
   set_environment();
 
-  /* Free program environment on exit */
-  atexit(destroy_env);
-  
-}
-
-
-/**
- * Destroy the program environment
- */
-void destroy_env() {
-  
-  FREE(Run.Env.cwd);
-  FREE(Run.Env.home);
-  FREE(Run.Env.user);
-  FREE(Run.localhostname);
-  FREE(Run.controlfile);
-  
 }
 
 
diff -Naur monit-cvs20040315/event.c monit-cvs20040315-mp/event.c
--- monit-cvs20040315/event.c   2004-02-14 23:16:18.000000000 +0100
+++ monit-cvs20040315-mp/event.c        2004-03-23 00:11:39.000000000 +0100
@@ -28,8 +28,10 @@
 #endif
 
 
+#include "monitor.h"
 #include "alert.h"
 #include "event.h"
+#include "process.h"
 
 
 /**
@@ -45,86 +47,143 @@
 /* ------------------------------------------------------------- Definitions */
 
 
-struct Event_T {
-  int id;
-  char *message;
-  Service_T source;
-};
-
-
 struct Event_Table {
   int id;
-  char description[STRLEN];
+  char description_failed[STRLEN];
+  char description_passed[STRLEN];
 } Event_Table[]= {
-    {EVENT_NULL,       "No Event"},
-    {EVENT_FAILED,     "Failed"},
-    {EVENT_START,      "Started"},
-    {EVENT_STOP,       "Stopped"},
-    {EVENT_RESTART,    "Restarted"},
-    {EVENT_CHECKSUM,   "Checksum changed"},
-    {EVENT_RESOURCE,   "Resource limit matched"},
-    {EVENT_TIMEOUT,    "Timeout"},
-    {EVENT_TIMESTAMP,  "Timestamp rule matched"},
-    {EVENT_SIZE,       "Size rule matched"},
-    {EVENT_CONNECTION, "Connection test failed"},
-    {EVENT_PERMISSION, "Permission error"},
-    {EVENT_UID,        "UID error"},
-    {EVENT_GID,        "GID error"},
-    {EVENT_UNMONITOR,  "Monitoring disabled"}
+  {EVENT_CHECKSUM,   "Checksum changed",        "Checksum passed"},
+  {EVENT_CONNECTION, "Connection failed",       "Connection passed"},
+  {EVENT_DATA,       "Data access error",       "Data access succeeded"},
+  {EVENT_EXEC,       "Execution error",         "Execution succeeded"},
+  {EVENT_GID,        "GID error",               "GID passed"},
+  {EVENT_INVALID,    "Invalid type",            "Type passed"},
+  {EVENT_NONEXIST,   "Does not exist",          "Exists"},
+  {EVENT_PERMISSION, "Permission error",        "Permission passed"},
+  {EVENT_RESOURCE,   "Resource limit matched",  "Resource limit passed"},
+  {EVENT_SIZE,       "Size limit matched",      "Size limit passed"},
+  {EVENT_TIMEOUT,    "Timeout",                 "Timeout recovery"},
+  {EVENT_TIMESTAMP,  "Timestamp limit matched", "Timestamp limit passed"},
+  {EVENT_UID,        "UID error",               "UID passed"},
+  /* Virtual events */
+  {EVENT_NULL,       "No Event",                "No Event"},
+  {EVENT_FAILED,     "Failed",                  "Failure recovery"}
 };
 
-static pthread_mutex_t handle_mutex= PTHREAD_MUTEX_INITIALIZER;
-
 
 /* -------------------------------------------------------------- Prototypes */
 
 
-static void handle_event(Event_T E);
-
-static void handle_uid(Event_T E);
-static void handle_gid(Event_T E);
-static void handle_size(Event_T E);
-static void handle_stop(Event_T E);
-static void handle_start(Event_T E);
-static void handle_restart(Event_T E);
-static void handle_timeout(Event_T E);
-static void handle_checksum(Event_T E);
-static void handle_resource(Event_T E);
-static void handle_timestamp(Event_T E);
-static void handle_connection(Event_T E);
-static void handle_permission(Event_T E);
-static void handle_unmonitor(Event_T E);
+static void handle_event(Event_T);
+static int  handle_action(Event_T, Action_T);
 
 
 /* ------------------------------------------------------------------ Public */
 
 
 /**
- * Construct and post a new Event
- * @param source The Service the event occured on
- * @param id The event type
+ * Post a new Event
+ * @param service The Service the event belongs to
+ * @param id The event identification
+ * @param state TRUE for failed, FALSE for passed event state
+ * @param action Description of the event action
  * @param s Optional message describing the event
  */
-void Event_post(Service_T source, long id, char *s, ...) {
+void Event_post(Service_T service, long id, short state, EventAction_T action, 
char *s, ...) {
 
-  long l;
-  Event_T e;
-  
-  ASSERT(source);
+  Event_T e = service->eventslist;
+
+  ASSERT(service);
+  ASSERT(action);
 
-  NEW(e);
-  e->id= id;
-  e->message= NULL;
-  e->source= source;
-
-  if(s) {
-    va_list ap;
-    va_start(ap, s);
-    e->message= format(s, ap, &l);
-    va_end(ap);
+  if(e == NULL)
+  {
+    /* Only first failed event can initialize the queue for given event type,
+     * thus passed events are ignored until first error */
+    if(!state)
+      return;
+
+    /* Initialize event list and add first event. */
+    NEW(e);
+    e->id = id;
+    e->source = service;
+    e->state = state;
+    e->state_changed = TRUE;
+    e->action = action;
+    if(s)
+    {
+      long l;
+      va_list ap;
+
+      va_start(ap, s);
+      e->message = format(s, ap, &l);
+      va_end(ap);
+    }
+    service->eventslist = e;
+  }
+  else
+  {
+    /* Try to find the event with the same origin and type identification.
+     * Each service and each test have its own custom actions object, so
+     * we share actions object address to identify event source. */
+    do
+    {
+      if(e->action == action && e->id == id)
+      {
+        LOCK(e->mutex)
+          /* update the message */
+          if(s)
+          {
+            long l;
+            va_list ap;
+
+            va_start(ap, s);
+            e->message = format(s, ap, &l);
+            va_end(ap);
+          }
+
+          if(e->state != state)
+          {
+            /* event state changed */
+            e->state = state;
+            e->state_changed = TRUE;
+          }
+        END_LOCK;
+       break;
+      }
+      e = e->next;
+    }
+    while(e);
+
+    if(!e)
+    {
+      /* Only first failed event can initialize the queue for given event type,
+       * thus passed events are ignored until first error */
+      if(!state)
+        return;
+
+      /* Event was not found in the pending events list, we will add it. */
+      NEW(e);
+      e->id = id;
+      e->source = service;
+      e->state = state;
+      e->state_changed = TRUE;
+      e->action = action;
+      if(s)
+      {
+        long l;
+        va_list ap;
+
+        va_start(ap, s);
+        e->message = format(s, ap, &l);
+        va_end(ap);
+      }
+      e->next = service->eventslist;
+      service->eventslist = e;
+    }
   }
 
-  LOCK(handle_mutex)
+  LOCK(e->mutex)
     handle_event(e);
   END_LOCK;
 
@@ -192,270 +251,158 @@
 
 
 /**
- * Get a textual description of the event type. For instance if the
- * event type is EVENT_RESTART, the textual description is
- * "Restart". Likewise if the event type is EVENT_CHECKSUM the textual
- * description is "Checksum error" and so on.
+ * Get a textual description of actual event type. For instance if the
+ * event type is possitive EVENT_TIMESTAMP, the textual description is
+ * "Timestamp error". Likewise if the event type is negative EVENT_CHECKSUM
+ * the textual description is "Checksum recovery" and so on.
  * @param E An event object
  * @return A string describing the event type in clear text. If the
  * event type is not found NULL is returned.
  */
 const char *Event_get_description(Event_T E) {
 
-  int i, event;
+  int i;
   int size= sizeof(Event_Table)/sizeof(Event_Table[0]);
 
   ASSERT(E);
 
-  /* In the case of passive mode we replace the description of start, stop
-   * or restart event for failed, because these actions are passive in
-   * this mode */
-  event= (E->source->mode == MODE_PASSIVE &&
-          ((E->id == EVENT_START)||
-           (E->id == EVENT_STOP) ||
-           (E->id == EVENT_RESTART))
-         )?EVENT_FAILED:E->id;
-
   for(i= 0; i < size; i++)
-    if(event == Event_Table[i].id)
-      return Event_Table[i].description;
+    if(E->id == Event_Table[i].id)
+      return 
E->state?Event_Table[i].description_failed:Event_Table[i].description_passed;
   
   return NULL;
 
 }
 
 
-/* ----------------------------------------------------------------- Private */
-
-
-/*
- * Handle the event
- * @param E An event
+/**
+ * Get a textual description of actual event action. For instance if the
+ * event type is possitive EVENT_NONEXIST, the textual description of
+ * failed state related action is "restart". Likewise if the event type is
+ * negative EVENT_CHECKSUM the textual description of recovery related action
+ * is "alert" and so on.
+ * @param E An event object
+ * @return A string describing the event type in clear text. If the
+ * event type is not found NULL is returned.
  */
-static void handle_event(Event_T E) {
-
-  ASSERT(E);
-
-  if(E->message) {
-    log("%s", E->message);
-  }
+const char *Event_get_action_description(Event_T E) {
 
-  switch(E->id) {
-  case EVENT_UID:        handle_uid(E); break;
-  case EVENT_GID:        handle_gid(E); break;
-  case EVENT_SIZE:       handle_size(E); break;
-  case EVENT_STOP:       handle_stop(E); break;
-  case EVENT_START:      handle_start(E); break;
-  case EVENT_RESTART:    handle_restart(E); break;
-  case EVENT_TIMEOUT:    handle_timeout(E); break;
-  case EVENT_RESOURCE:   handle_resource(E); break;
-  case EVENT_CHECKSUM:   handle_checksum(E); break;
-  case EVENT_TIMESTAMP:  handle_timestamp(E); break;
-  case EVENT_CONNECTION: handle_connection(E); break;
-  case EVENT_PERMISSION: handle_permission(E); break;
-  case EVENT_UNMONITOR:  handle_unmonitor(E); break;
-  }
-  
-  if(E->id != EVENT_NULL) {
-    handle_alert(E);
-  }
-  
-  Event_free(&E);
-
-}
-
-
-/* ---------------------------------------------------------- Event handlers */
-
-
-static void handle_stop(Event_T E) {
-
-  if((E->source->mode != MODE_PASSIVE)) {
-    check_service(E->source->name, "stop");
-  } 
-  
-}
-
-
-static void handle_start(Event_T E) {
-  
-  if(E->source->def_timeout)
-      E->source->nstart++;
-  
-  if((E->source->mode != MODE_PASSIVE)) {
-    check_service(E->source->name, "start");
-  } 
-
-}
-
-
-static void handle_restart(Event_T E) {
+  int id;
+  Action_T A;
 
-  if(E->source->def_timeout)
-      E->source->nstart++;
-  
-  if((E->source->mode != MODE_PASSIVE)) {
-    check_service(E->source->name, "restart");
-  }
+  ASSERT(E);
 
-}
+  A = E->state?E->action->failed:E->action->passed;
 
+  /* In the case of passive mode we replace the description of start, stop
+   * or restart action for alert action, because these actions are passive in
+   * this mode */
+  id= (E->source->mode == MODE_PASSIVE &&
+       ((A->id == ACTION_START)||
+        (A->id == ACTION_STOP) ||
+        (A->id == ACTION_RESTART))
+      )?ACTION_ALERT:A->id;
 
-static void handle_timeout(Event_T E) {
+  return actionnames[id];
 
-  E->source->do_monitor= FALSE;
-  
 }
 
 
-static void handle_resource(Event_T E) {
-
-  Device_T d;
-  Resource_T r;
-  Service_T s= E->source;
-
-  for(d= s->devicelist; d; d= d->next) {
-    if(d->event_flag && d->exec != NULL) {
-      d->event_flag= FALSE; 
-      spawn(s, d->exec, EVENT_DESCRIPTION(E));
-    }
-  }
-
-  for(r= s->resourcelist; r; r= r->next) {
-    if(r->event_flag && r->exec != NULL) {
-      /* Reset the event_flag so the command is not executed again
-       * unless the flag is explicit set */
-      r->event_flag= FALSE; 
-      spawn(s, r->exec, EVENT_DESCRIPTION(E));
-    }
-  }
+/* ----------------------------------------------------------------- Private */
 
-}
 
+/*
+ * Handle the event
+ * @param E An event
+ */
+static void handle_event(Event_T E) {
 
-static void handle_timestamp(Event_T E) {
+  ASSERT(E);
+  ASSERT(E->action);
+  ASSERT(E->action->failed);
+  ASSERT(E->action->passed);
 
-  Timestamp_T t;
-  Service_T s= E->source;
-  
-  /* Check for executables to run */
-  for(t= s->timestamplist; t; t= t->next) {
-    if(t->event_flag && t->exec != NULL) {
-      /* Reset the event_flag so the command is not executed again
-       * unless the flag is explicit set */
-      t->event_flag= FALSE; 
-      spawn(s, t->exec, EVENT_DESCRIPTION(E));
-    }
+  /* We will handle only first passed event. All failed events are handled. */
+  if(!E->state_changed && !E->state)
+  {
+    return;
   }
 
-}
-
-
-static void handle_size(Event_T E) {
-
-  Size_T sl;
-  Service_T s= E->source;
-  
-  /* Check for executables to run */
-  for(sl= s->sizelist; sl; sl= sl->next) {
-    if(sl->event_flag && sl->exec != NULL) {
-      /* Reset the event_flag so the command is not executed again
-       * unless the flag is explicit set */
-      sl->event_flag= FALSE; 
-      spawn(s, sl->exec, EVENT_DESCRIPTION(E));
-    }
+  if(E->message)
+  {
+    log("%s\n", E->message);
   }
 
-}
-
-
-static void handle_connection(Event_T E) {
-  
-  Port_T p;
-  Icmp_T i;
-  Service_T s= E->source;
-  
-  /* Check for executables to run in the port list*/
-  for(p= s->portlist; p; p= p->next) {
-    if(p->event_flag && p->exec != NULL) {
-      /* Reset the event_flag so the command is not executed again
-       * unless the flag is explicit set */
-      p->event_flag= FALSE; 
-      spawn(s, p->exec, EVENT_DESCRIPTION(E));
-    }
-  }
-  
-  /* Check for executables to run in the icmp list */
-  for(i= s->icmplist; i; i= i->next) {
-    if(i->event_flag && i->exec != NULL) {
-      i->event_flag= FALSE; 
-      spawn(s, i->exec, EVENT_DESCRIPTION(E));
-    }
+  if(E->state)
+  {
+    handle_action(E, E->action->failed);
   }
-
-}
-
-
-static void handle_checksum(Event_T E) {
-
-  Service_T s= E->source;
-
-  if(s->checksum->event_flag && s->checksum->exec != NULL) {
-    s->checksum->event_flag= FALSE; 
-    spawn(s, s->checksum->exec, EVENT_DESCRIPTION(E));
+  else
+  {
+    handle_action(E, E->action->passed);
   }
 
-}
-
-
-static void handle_permission(Event_T E) {
-
-  Service_T s= E->source;
-
-  s->perm->has_error= TRUE;
-
-  if(s->perm->event_flag && s->perm->exec != NULL) {
-    s->perm->event_flag= FALSE; 
-    spawn(s, s->perm->exec, EVENT_DESCRIPTION(E));
-  }
+  /* Possible event state change was handled so we will reset the flag. */
+  E->state_changed = FALSE;
 
 }
 
 
-static void handle_uid(Event_T E) {
-
-  Service_T s= E->source;
+static int handle_action(Event_T E, Action_T A) {
 
-  s->uid->has_error= TRUE;
+  ASSERT(E);
+  ASSERT(A);
 
-  if(s->uid->event_flag && s->uid->exec != NULL) {
-    s->uid->event_flag= FALSE; 
-    spawn(s, s->uid->exec, EVENT_DESCRIPTION(E));
+  /* Alert is send regardless of action on state change only. */
+  if(E->state_changed)
+  {
+    handle_alert(E);
   }
 
-}
-
+  switch(A->id)
+  {
+  case ACTION_ALERT:
+      /* Already handled. */
+      break;
+
+  case ACTION_EXEC:
+      spawn(E->source, A->exec, EVENT_DESCRIPTION(E));
+      break;
+
+  case ACTION_RESTART:
+      if(E->source->def_timeout)
+        E->source->nstart++;
+      if((E->source->mode != MODE_PASSIVE))
+      {
+        check_service(E->source->name, "restart");
+      }
+      return FALSE;
+
+  case ACTION_START:
+      if(E->source->def_timeout)
+        E->source->nstart++;
+      if((E->source->mode != MODE_PASSIVE))
+      {
+        check_service(E->source->name, "start");
+      } 
+      return FALSE;
+
+  case ACTION_STOP:
+      if((E->source->mode != MODE_PASSIVE))
+      {
+        check_service(E->source->name, "stop");
+      } 
+      return FALSE;
+
+  case ACTION_UNMONITOR:
+      check_service(E->source->name, "unmonitor");
+      return FALSE;
+
+  default:
+      log("'%s' error -- unknown failure action: [%d]\n", E->source->name, 
A->id);
+      break;
 
-static void handle_gid(Event_T E) {
-
-  Service_T s= E->source;
-
-  s->gid->has_error= TRUE;
-  if(s->gid->event_flag && s->gid->exec != NULL) {
-    s->gid->event_flag= FALSE; 
-    spawn(s, s->gid->exec, EVENT_DESCRIPTION(E));
   }
-
+  return TRUE;
 }
 
-
-static void handle_unmonitor(Event_T E) {
-
-  check_service(E->source->name, "unmonitor");
-
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-
diff -Naur monit-cvs20040315/event.h monit-cvs20040315-mp/event.h
--- monit-cvs20040315/event.h   2004-01-29 18:52:11.000000000 +0100
+++ monit-cvs20040315-mp/event.h        2004-03-21 20:49:53.000000000 +0100
@@ -24,23 +24,24 @@
 
 
 #define EVENT_NULL       0x0
-#define EVENT_FAILED     0x01
-#define EVENT_START      0x02
-#define EVENT_STOP       0x04
-#define EVENT_RESTART    0x08
-#define EVENT_CHECKSUM   0x10
-#define EVENT_RESOURCE   0x20
-#define EVENT_TIMEOUT    0x40
-#define EVENT_TIMESTAMP  0x80
-#define EVENT_SIZE       0x100
-#define EVENT_CONNECTION 0x200
-#define EVENT_PERMISSION 0x400
-#define EVENT_UID        0x800
-#define EVENT_GID        0x1000
-#define EVENT_UNMONITOR  0x2000
+#define EVENT_FAILED     0x1
+#define EVENT_CHECKSUM   0x2
+#define EVENT_RESOURCE   0x4
+#define EVENT_TIMEOUT    0x8
+#define EVENT_TIMESTAMP  0x10
+#define EVENT_SIZE       0x20
+#define EVENT_CONNECTION 0x40
+#define EVENT_PERMISSION 0x80
+#define EVENT_UID        0x100
+#define EVENT_GID        0x200
+#define EVENT_NONEXIST   0x400
+#define EVENT_INVALID    0x800
+#define EVENT_DATA       0x1000
+#define EVENT_EXEC       0x2000
 
 
 #define EVENT_DESCRIPTION(E) Event_get_description(E)
+#define EVENTACTION_DESCRIPTION(E) Event_get_action_description(E)
 #define IS_EVENT_SET(value, mask) ((value & mask) != 0)
 
 
@@ -48,28 +49,27 @@
  * This class implements the <b>event</b> processing machinery used by
  * monit. In monit an event is an object containing a Service_T
  * reference indicating the object where the event orginated, an id
- * specifying the event type and an optional message describing why
- * the event was fired.
+ * specifying the event type, a value representing up or down state
+ * and an optional message describing why the event was fired.
  *
  * Clients may use the function Event_post() to post events to the
  * event handler for processing.
  * 
  * @author Jan-Henrik Haukeland, <address@hidden>
+ * @author Martin Pala, <address@hidden>
  * @version \$Id: event.h,v 1.13 2004/01/29 17:52:11 martinp Exp $
  * @file
  */
 
-
-typedef struct Event_T *Event_T;
-
-
 /**
- * Construct and post a new Event
- * @param source The Service the event occured on
- * @param id The event type
+ * Post a new Event
+ * @param service The Service the event belongs to
+ * @param id The event identification
+ * @param state TRUE for failed, FALSE for passed event state
+ * @param action Description of the event action
  * @param s Optional message describing the event
  */
-void Event_post(Service_T source, long id, char *s, ...);
+void Event_post(Service_T service, long id, short state, EventAction_T action, 
char *s, ...);
 
 
 /**
@@ -105,10 +105,10 @@
 
 
 /**
- * Get a textual description of the event type. For instance if the
- * event type is EVENT_RESTART, the textual description is
- * "Restart". Likewise if the event type is EVENT_CHECKSUM the textual
- * description is "Checksum error" and so on.
+ * Get a textual description of actual event type. For instance if the
+ * event type is possitive EVENT_TIMESTAMP, the textual description is
+ * "Timestamp error". Likewise if the event type is negative EVENT_CHECKSUM
+ * the textual description is "Checksum recovery" and so on.
  * @param E An event object
  * @return A string describing the event type in clear text. If the
  * event type is not found NULL is returned.
@@ -116,4 +116,17 @@
 const char *Event_get_description(Event_T E);
 
 
+/**
+ * Get a textual description of actual event action. For instance if the
+ * event type is possitive EVENT_NONEXIST, the textual description of
+ * failed state related action is "restart". Likewise if the event type is
+ * negative EVENT_CHECKSUM the textual description of recovery related action
+ * is "alert" and so on.
+ * @param E An event object
+ * @return A string describing the event type in clear text. If the
+ * event type is not found NULL is returned.
+ */
+const char *Event_get_action_description(Event_T E);
+
+
 #endif
diff -Naur monit-cvs20040315/gc.c monit-cvs20040315-mp/gc.c
--- monit-cvs20040315/gc.c      2004-03-13 01:05:04.000000000 +0100
+++ monit-cvs20040315-mp/gc.c   2004-03-22 13:31:03.000000000 +0100
@@ -44,6 +44,9 @@
 static void _gcppil(ProcInfo_T *);
 static void _gcptl(Timestamp_T *);
 static void _gccmd(Command_T *);
+static void _gc_action(Action_T *);
+static void _gc_eventaction(EventAction_T *);
+static void _gc_event(Event_T *);
 static void _gcpdil(DeviceInfo_T *);
 static void _gcpdl(Dependant_T *);
 static void _gcso(Size_T *);
@@ -123,9 +126,8 @@
   
   ASSERT(s&&*s);
 
-  if((*s)->next) {
+  if((*s)->next)
     _gc_service_list(&(*s)->next);
-  }
   
   _gc_service(&(*s));
     
@@ -136,69 +138,71 @@
 
   ASSERT(s&&*s);
   
-  if((*s)->portlist) {
+  if((*s)->portlist)
     _gcppl(&(*s)->portlist);
-  }
 
-  if((*s)->devicelist) {
+  if((*s)->devicelist)
     _gcdevice(&(*s)->devicelist);
-  }
 
-  if((*s)->icmplist) {
+  if((*s)->icmplist)
     _gcicmp(&(*s)->icmplist);
-  }
 
-  if((*s)->maillist) {
+  if((*s)->maillist)
     gc_mail_list(&(*s)->maillist);
-  }
 
-  if((*s)->resourcelist) {
+  if((*s)->resourcelist)
     _gcpql(&(*s)->resourcelist);
-  }
 
-  if((*s)->procinfo) {
+  if((*s)->procinfo)
     _gcppil(&(*s)->procinfo);
-  }
   
-  if((*s)->devinfo) {
+  if((*s)->devinfo)
     _gcpdil(&(*s)->devinfo);
-  }
   
-  if((*s)->timestamplist) {
+  if((*s)->timestamplist)
     _gcptl(&(*s)->timestamplist);
-  }
 
-  if((*s)->sizelist) {
+  if((*s)->sizelist)
     _gcso(&(*s)->sizelist);
-  }
 
-  if((*s)->checksum) {
+  if((*s)->checksum)
     _gcchecksum(&(*s)->checksum);
-  }
 
-  if((*s)->perm) {
+  if((*s)->perm)
     _gcperm(&(*s)->perm);
-  }
 
-  if((*s)->uid) {
+  if((*s)->uid)
     _gcuid(&(*s)->uid);
-  }
 
-  if((*s)->gid) {
+  if((*s)->gid)
     _gcgid(&(*s)->gid);
-  }
 
-  if((*s)->dependantlist) {
+  if((*s)->dependantlist)
     _gcpdl(&(*s)->dependantlist);
-  }
 
-  if((*s)->start) {
+  if((*s)->start)
     _gccmd(&(*s)->start);
-  }
-  
-  if((*s)->stop) {
+
+  if((*s)->stop)
     _gccmd(&(*s)->stop);
-  }
+
+  if((*s)->action_DATA)
+    _gc_eventaction(&(*s)->action_DATA);
+  
+  if((*s)->action_EXEC)
+    _gc_eventaction(&(*s)->action_EXEC);
+  
+  if((*s)->action_INVALID)
+    _gc_eventaction(&(*s)->action_INVALID);
+  
+  if((*s)->action_NONEXIST)
+    _gc_eventaction(&(*s)->action_NONEXIST);
+  
+  if((*s)->action_TIMEOUT)
+    _gc_eventaction(&(*s)->action_TIMEOUT);
+  
+  if((*s)->eventslist)
+    _gc_event(&(*s)->eventslist);
   
   FREE((*s)->name);
   FREE((*s)->path);
@@ -217,9 +221,8 @@
 
   if(!s&&!*s) return;
   
-  if((*s)->next) {
+  if((*s)->next)
     _gc_mail_server(&(*s)->next);
-  }
   
   FREE((*s)->host);
   FREE(*s);
@@ -240,21 +243,54 @@
 }
 
 
+static void _gc_action(Action_T *a) {
+
+  ASSERT(a&&*a);
+  
+  if((*a)->exec)
+    _gccmd(&(*a)->exec);
+  FREE(*a);
+
+}
+
+
+static void _gc_eventaction(EventAction_T *e) {
+
+  ASSERT(e&&*e);
+  
+  _gc_action(&(*e)->failed);
+  _gc_action(&(*e)->passed);
+  FREE(*e);
+
+}
+
+
+static void _gc_event(Event_T *e) {
+
+  ASSERT(e&&*e);
+  
+  if((*e)->next)
+    _gc_event(&(*e)->next);
+
+  (*e)->action= NULL;
+  FREE((*e)->message);
+  FREE(*e);
+
+}
+
+
 static void _gcppl(Port_T *p) {
   
   ASSERT(p&&*p);
 
-  if((*p)->next) {
+  if((*p)->next)
     _gcppl(&(*p)->next);
-  }
 
-  if((*p)->exec) {
-    _gccmd(&(*p)->exec);
-  }
+  if((*p)->action)
+    _gc_eventaction(&(*p)->action);
 
-  if((*p)->generic) {
+  if((*p)->generic)
     _gcgrc(&(*p)->generic);
-  }
 
   FREE((*p)->address);
   FREE((*p)->request);
@@ -271,13 +307,11 @@
   
   ASSERT(d&&*d);
 
-  if((*d)->next) {
+  if((*d)->next)
     _gcdevice(&(*d)->next);
-  }
 
-  if((*d)->exec) {
-    _gccmd(&(*d)->exec);
-  }
+  if((*d)->action)
+    _gc_eventaction(&(*d)->action);
 
   FREE(*d);
 
@@ -288,13 +322,11 @@
   
   ASSERT(i&&*i);
 
-  if((*i)->next) {
+  if((*i)->next)
     _gcicmp(&(*i)->next);
-  }
 
-  if((*i)->exec) {
-    _gccmd(&(*i)->exec);
-  }
+  if((*i)->action)
+    _gc_eventaction(&(*i)->action);
 
   FREE(*i);
 
@@ -305,13 +337,11 @@
 
   ASSERT(q);
 
-  if((*q)->next) {
+  if((*q)->next)
     _gcpql(&(*q)->next);
-  }
   
-  if((*q)->exec) {
-    _gccmd(&(*q)->exec);
-  }
+  if((*q)->action)
+    _gc_eventaction(&(*q)->action);
 
   FREE(*q);
   
@@ -340,13 +370,12 @@
   
   ASSERT(p);
 
-  if((*p)->next) {
+  if((*p)->next)
     _gcptl(&(*p)->next);
-  }
 
-  if((*p)->exec) {
-    _gccmd(&(*p)->exec);
-  }
+  if((*p)->action)
+    _gc_eventaction(&(*p)->action);
+
   FREE(*p);
 
 }
@@ -359,8 +388,8 @@
   if((*s)->next)
     _gcso(&(*s)->next);
 
-  if((*s)->exec)
-    _gccmd(&(*s)->exec);
+  if((*s)->action)
+    _gc_eventaction(&(*s)->action);
 
   FREE(*s);
 
@@ -371,8 +400,8 @@
   
   ASSERT(s);
 
-  if((*s)->exec)
-    _gccmd(&(*s)->exec);
+  if((*s)->action)
+    _gc_eventaction(&(*s)->action);
 
   FREE((*s)->hash);
   FREE(*s);
@@ -384,8 +413,8 @@
   
   ASSERT(s);
 
-  if((*s)->exec)
-    _gccmd(&(*s)->exec);
+  if((*s)->action)
+    _gc_eventaction(&(*s)->action);
 
   FREE(*s);
 
@@ -396,8 +425,8 @@
   
   ASSERT(s);
 
-  if((*s)->exec)
-    _gccmd(&(*s)->exec);
+  if((*s)->action)
+    _gc_eventaction(&(*s)->action);
 
   FREE(*s);
 
@@ -408,8 +437,8 @@
   
   ASSERT(s);
 
-  if((*s)->exec)
-    _gccmd(&(*s)->exec);
+  if((*s)->action)
+    _gc_eventaction(&(*s)->action);
 
   FREE(*s);
 
@@ -420,9 +449,8 @@
 
   ASSERT(d);
 
-  if((*d)->next) {
+  if((*d)->next)
     _gcpdl(&(*d)->next);
-  }
 
   FREE((*d)->dependant);
   FREE(*d);
@@ -434,15 +462,13 @@
 
   ASSERT(g);
 
-  if((*g)->next) {
+  if((*g)->next)
     _gcgrc(&(*g)->next);
-  }
 
   FREE((*g)->send);
 #ifdef HAVE_REGEX_H
-  if ((*g)->expect!=NULL) {
+  if ((*g)->expect!=NULL)
     regfree((*g)->expect);
-  }
 #endif
   FREE((*g)->expect);
   FREE(*g);
@@ -454,9 +480,8 @@
 
   ASSERT(c);
 
-  if((*c)->next) {
+  if((*c)->next)
     _gcath(&(*c)->next);
-  }
 
   FREE((*c)->uname);
   FREE((*c)->passwd);
diff -Naur monit-cvs20040315/http/cervlet.c monit-cvs20040315-mp/http/cervlet.c
--- monit-cvs20040315/http/cervlet.c    2004-02-29 23:24:45.000000000 +0100
+++ monit-cvs20040315-mp/http/cervlet.c 2004-03-22 19:52:46.000000000 +0100
@@ -1172,16 +1172,22 @@
   
   print_service_common_params(res, s);
   
-  if(s->perm)
-      out_print(res, "<tr><td>Associated permission</td>"
-               "<td>if failed %o then %s</td></tr>",
-               s->perm->perm, actionnames[s->perm->action]);
-  if(s->uid)
-      out_print(res, "<tr><td>Associated UID</td><td>if failed %d then %s</td>"
-               "</tr>", (int)s->uid->uid, actionnames[s->uid->action]);
-  if(s->gid)
-      out_print(res, "<tr><td>Associated GID</td><td>if failed %d then %s</td>"
-               "</tr>", (int)s->gid->gid, actionnames[s->gid->action]);
+  if(s->perm) {
+    EventAction_T a= s->perm->action;
+    out_print(res, "<tr><td>Associated permission</td>"
+      "<td>if failed %o then %s else if recovered then %s</td></tr>",
+      s->perm->perm, actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+  if(s->uid) {
+    EventAction_T a= s->uid->action;
+    out_print(res, "<tr><td>Associated UID</td><td>if failed %d then %s else 
if recovered then %s</td>"
+      "</tr>", (int)s->uid->uid, actionnames[a->failed->id], 
actionnames[a->passed->id]);
+  }
+  if(s->gid) {
+    EventAction_T a= s->gid->action;
+    out_print(res, "<tr><td>Associated GID</td><td>if failed %d then %s else 
if recovered then %s</td>"
+      "</tr>", (int)s->gid->gid, actionnames[a->failed->id], 
actionnames[a->passed->id]);
+  }
   out_print(res,
            "<tr><td>Permission</td><td>%s%o</font></td></tr>",
            (s->perm&&s->perm->has_error)?
@@ -1208,26 +1214,28 @@
     if(s->devicelist) {
       
       for(dl= s->devicelist; dl; dl= dl->next) {
+        EventAction_T a= dl->action;
        
         if(dl->resource == RESOURCE_ID_INODE) {
          
           out_print(res,
-                   "<tr><td>Inodes usage limit</td><td>if %s %ld %s "
-                   "then %s</td></tr>", operatornames[dl->operator],
-                   (dl->limit_absolute > -1)?
-                   dl->limit_absolute:dl->limit_percent,
-                   (dl->limit_absolute > -1)?"":"%",
-                   actionnames[dl->action]);
+           "<tr><td>Inodes usage limit</td><td>If %s %ld %s "
+           "then %s else if recovered then %s</td></tr>",
+           operatornames[dl->operator],
+           (dl->limit_absolute > -1)?
+           dl->limit_absolute:dl->limit_percent,
+           (dl->limit_absolute > -1)?"":"%",
+           actionnames[a->failed->id], actionnames[a->passed->id]);
          
         } else if(dl->resource == RESOURCE_ID_SPACE) {
          
           out_print(res,
-                   "<tr><td>Space usage limit</td><td>if %s %ld %s "
-                   "then %s</td></tr>", operatornames[dl->operator],
-                   (dl->limit_absolute > -1)?
-                   dl->limit_absolute:dl->limit_percent,
-                   (dl->limit_absolute > -1)?"blocks":"%",
-                   actionnames[dl->action]);
+           "<tr><td>Space usage limit</td><td>If %s %ld %s "
+           "then %s else if recovered then %s</td></tr>", 
operatornames[dl->operator],
+           (dl->limit_absolute > -1)?
+           dl->limit_absolute:dl->limit_percent,
+           (dl->limit_absolute > -1)?"blocks":"%",
+           actionnames[a->failed->id], actionnames[a->passed->id]);
         }
 
       }
@@ -1323,35 +1331,38 @@
   print_service_common_params(res, s);
   
   {
-    struct mytimestamp *t;
+    Timestamp_T t;
     for(t= s->timestamplist; t; t= t->next) {
-      if(t->test_changes) {
-       out_print(res,
-                 "<tr><td>Associated timestamp</td>"
-                 "<td>If changed then %s</td></tr>",
-                 actionnames[t->action]);
-      } else {
-       out_print(res,
-                 "<tr><td>Associated timestamp</td>"
-                 "<td>If %s %d second(s) then %s</td></tr>",
-                 operatornames[t->operator],
-                 t->time, actionnames[t->action]);
-      }
-    }
+      EventAction_T a= t->action;
+
+      if(t->test_changes)
+        out_print(res,
+          "<tr><td>Associated timestamp</td><td>If changed then %s else if 
recovered then %s</td></tr>",
+          actionnames[a->failed->id], actionnames[a->passed->id]);
+      else
+        out_print(res,
+          "<tr><td>Associated timestamp</td>"
+          "<td>If %s %d second(s) then %s else if recovered then %s</td></tr>",
+          operatornames[t->operator],
+          t->time, actionnames[a->failed->id], actionnames[a->passed->id]);
+    }
+  }
+  if(s->perm) {
+    EventAction_T a= s->perm->action;
+    out_print(res, "<tr><td>Associated permission</td>"
+      "<td>If failed %o then %s else if recovered then %s</td></tr>",
+      s->perm->perm, actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+  if(s->uid) {
+    EventAction_T a= s->uid->action;
+    out_print(res, "<tr><td>Associated UID</td><td>If failed %d then %s else 
if recovered then %s</td>"
+      "</tr>", (int)s->uid->uid, actionnames[a->failed->id], 
actionnames[a->passed->id]);
+  }
+  if(s->gid) {
+    EventAction_T a= s->gid->action;
+    out_print(res, "<tr><td>Associated GID</td><td>If failed %d then %s else 
if recovered then %s</td>"
+      "</tr>", (int)s->gid->gid, actionnames[a->failed->id], 
actionnames[a->passed->id]);
   }
-  if(s->perm)
-      out_print(res,
-               "<tr><td>Associated permission</td><td>"
-               "if failed %o then %s</td></tr>",
-               s->perm->perm, actionnames[s->perm->action]);
-  if(s->uid)
-      out_print(res, "<tr><td>Associated UID</td><td>"
-               "if failed %d then %s</td></tr>",
-               (int)s->uid->uid, actionnames[s->uid->action]);
-  if(s->gid)
-      out_print(res, "<tr><td>Associated GID</td><td>"
-               "if failed %d then %s</td></tr>",
-               (int)s->gid->gid, actionnames[s->gid->action]);
   out_print(res,
            "<tr><td>Permission</td><td>%s%o</font></td></tr>",
            (s->perm&&s->perm->has_error)?
@@ -1410,72 +1421,64 @@
   print_service_common_params(res,s);
   
   {
-    struct mytimestamp *t;
+    Timestamp_T t;
     for(t= s->timestamplist; t; t= t->next) {
-      if(t->test_changes) {
-       out_print(res,
-                 "<tr><td>Associated timestamp</td>"
-                 "<td>If changed then %s</td></tr>",
-                 actionnames[t->action]);
-      } else {
-       out_print(res,
-                 "<tr><td>Associated timestamp</td>"
-                 "<td>If %s %d second(s) then %s</td></tr>",
-                 operatornames[t->operator],
-                 t->time, actionnames[t->action]);
-      }
+      EventAction_T a= t->action;
+      if(t->test_changes)
+        out_print(res,
+          "<tr><td>Associated timestamp</td>"
+          "<td>If changed then %s else if recovered then %s</td></tr>",
+          actionnames[a->failed->id], actionnames[a->passed->id]);
+      else
+        out_print(res,
+          "<tr><td>Associated timestamp</td>"
+          "<td>If %s %d second(s) then %s else if recovered then %s</td></tr>",
+          operatornames[t->operator],
+          t->time, actionnames[a->failed->id], actionnames[a->passed->id]);
     }
   }
   {
     Size_T sl;
     for(sl= s->sizelist; sl; sl= sl->next) {
-      if(sl->test_changes) {
-       out_print(res,
-                 "<tr><td>Associated size</td>"
-                 "<td>If changed then %s</td></tr>",
-                 actionnames[sl->action]);
-      } else {
-       out_print(res,
-                 "<tr><td>Associated size</td>"
-                 "<td>If %s %lu byte(s) then %s</td></tr>",
-                 operatornames[sl->operator],
-                 sl->size, actionnames[sl->action]);
-      }
+      EventAction_T a= sl->action;
+      if(sl->test_changes)
+        out_print(res,
+          "<tr><td>Associated size</td>"
+          "<td>If changed then %s else if recovered then %s</td></tr>",
+          actionnames[a->failed->id], actionnames[a->passed->id]);
+      else
+        out_print(res,
+          "<tr><td>Associated size</td>"
+          "<td>If %s %lu byte(s) then %s else if recovered then %s</td></tr>",
+          operatornames[sl->operator],
+          sl->size, actionnames[a->failed->id], actionnames[a->passed->id]);
     }
   }
 
   if(s->checksum) {
-    switch (s->checksum->type) {
-    case HASH_MD5:
-        out_print(res, "<tr><td>Associated checksum</td><td>"
-                  "if failed MD5(%s) then %s</td></tr>",
-                  s->checksum->hash, actionnames[s->checksum->action]);
-        break;
-    case HASH_SHA1:
-        out_print(res, "<tr><td>Associated checksum</td><td>"
-                  "if failed SHA1(%s) then %s</td></tr>",
-                  s->checksum->hash, actionnames[s->checksum->action]);
-        break;
-    default:
-        out_print(res, "<tr><td>Associated checksum</td><td>"
-                  "if failed UNKNOWN(%s) then %s</td></tr>",
-                  s->checksum->hash, actionnames[s->checksum->action]);
-        break;
-    }
+    EventAction_T a= s->checksum->action;
+    out_print(res, "<tr><td>Associated checksum</td><td>"
+      "if failed %s(%s) then %s else if recovered then %s</td></tr>",
+      s->checksum->hash, checksumnames[s->checksum->type],
+      actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+  
+  if(s->perm) {
+    EventAction_T a= s->perm->action;
+    out_print(res, "<tr><td>Associated permission</td>"
+      "<td>if failed %o then %s else if recovered then %s</td></tr>",
+      s->perm->perm, actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+  if(s->uid) {
+    EventAction_T a= s->uid->action;
+    out_print(res, "<tr><td>Associated UID</td><td>if failed %d then %s else 
if recovered then %s</td>"
+      "</tr>", (int)s->uid->uid, actionnames[a->failed->id], 
actionnames[a->passed->id]);
+  }
+  if(s->gid) {
+    EventAction_T a= s->gid->action;
+    out_print(res, "<tr><td>Associated GID</td><td>if failed %d then %s else 
if recovered then %s</td>"
+      "</tr>", (int)s->gid->gid, actionnames[a->failed->id], 
actionnames[a->passed->id]);
   }
-  
-  if(s->perm)
-      out_print(res, "<tr><td>Associated permission</td><td>"
-               "if failed %o then %s</td></tr>",
-               s->perm->perm, actionnames[s->perm->action]);
-  if(s->uid)
-      out_print(res, "<tr><td>Associated UID</td><td>"
-               "if failed %d then %s</td></tr>",
-               (int)s->uid->uid, actionnames[s->uid->action]);
-  if(s->gid)
-      out_print(res, "<tr><td>Associated GID</td><td>"
-               "if failed %d then %s</td></tr>",
-               (int)s->gid->gid, actionnames[s->gid->action]);
   out_print(res,
            "<tr><td>Size</td><td>%lu B</td></tr>",
            (unsigned long)stat_buf.st_size);
@@ -1528,8 +1531,6 @@
            "<tr><td>Pid file</td><td>%s</td></tr>",
            s->path);
 
-  print_service_common_params(res, s);
-  
   if(Run.doprocess) {
     ProcInfo_T pi= s->procinfo;
     
@@ -1547,34 +1548,29 @@
              "<td>%.1f%% [%ldkB]</td></tr>",
               pi->total_mem_percent/10.0,pi->total_mem_kbyte);
   }
+
+  print_service_common_params(res, s);
+  
   {
     Port_T n;
     for(n= s->portlist; n; n= n->next) {
+      EventAction_T a= n->action;
+
       if(n->family == AF_INET) {
-       if(n->SSL.use_ssl) {
-         out_print(res,
-                   "<tr><td>Host:Port</td>"
-                   "<td>if failed %s:%d%s [%s via SSL] then %s</td>"
-                   "</tr>", n->hostname, n->port, n->request?n->request:"",
-                   n->protocol->name, actionnames[n->action]);
-         
-         if(n->SSL.certmd5 != NULL) {
-           out_print(res,
-             "<tr><td>Server certificate md5 sum</td><td>%s</td></tr>",
-                     n->SSL.certmd5);
-         }
-       } else {
+       out_print(res,
+         "<tr><td>Host:Port</td>"
+         "<td>If failed %s:%d%s [%s%s] with timeout %d seconds then %s else if 
recovered then %s</td></tr>",
+         n->hostname, n->port, n->request?n->request:"",
+          n->SSL.use_ssl?" via SSL":"", n->protocol->name, n->timeout,
+          actionnames[a->failed->id], actionnames[a->passed->id]);
+       if(n->SSL.certmd5 != NULL)
          out_print(res,
-                   "<tr><td>Host:Port</td>"
-                   "<td>if failed %s:%d%s [%s] then %s</td></tr>",
-                   n->hostname, n->port, n->request?n->request:"",
-                   n->protocol->name, actionnames[n->action]);
-       }
+           "<tr><td>Server certificate md5 sum</td><td>%s</td></tr>",
+           n->SSL.certmd5);
       } else if(n->family == AF_UNIX) {
        out_print(res,
-                 "<tr><td>Unix Socket</td>"
-                 "<td>if failed %s [%s] then %s</td></tr>",
-                 n->pathname, n->protocol->name, actionnames[n->action]);
+         "<tr><td>Unix Socket</td><td>If failed %s [%s] with timeout %d 
seconds then %s else if recovered then %s</td></tr>",
+         n->pathname, n->protocol->name, n->timeout, 
actionnames[a->failed->id], actionnames[a->passed->id]);
       }
     }
   }
@@ -1582,84 +1578,90 @@
     Resource_T q;
     
     for (q= s->resourcelist; q; q= q->next) {
+      EventAction_T a= q->action;
+
       switch (q->resource_id) {
        
       case RESOURCE_ID_CPU_PERCENT: 
          
-         out_print(res,"<tr><td>CPU usage limit</td>"
-                   "<td>If %s %.1f%% for %d cycle(s) then %s</td></tr>", 
-                   operatornames[q->operator],
-                   q->limit/10.0, q->max_cycle, 
-                   actionnames[q->action]);
-         break;
+       out_print(res,"<tr><td>CPU usage limit</td>"
+         "<td>If %s %.1f%% for %d cycle(s) then %s else if recovered then 
%s</td></tr>", 
+         operatornames[q->operator],
+         q->limit/10.0, q->max_cycle, 
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_MEM_PERCENT: 
          
-         out_print(res,"<tr><td>Memory usage limit</td>"
-                   "<td>If %s %.1f%% for %d cycle(s) then %s</td></tr>", 
-                   operatornames[q->operator],
-                   q->limit/10.0, q->max_cycle, 
-                   actionnames[q->action]);
-         break;
+       out_print(res,"<tr><td>Memory usage limit</td>"
+         "<td>If %s %.1f%% for %d cycle(s) then %s else if recovered then 
%s</td></tr>", 
+         operatornames[q->operator],
+         q->limit/10.0, q->max_cycle, 
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_MEM_KBYTE: 
          
-         out_print(res,"<tr><td>Memory amount limit</td>"
-                   "<td>If %s %ld for %d cycle(s) then %s</td></tr>", 
-                   operatornames[q->operator],
-                   q->limit, q->max_cycle, 
-                   actionnames[q->action]);
-         break;
+       out_print(res,"<tr><td>Memory amount limit</td>"
+         "<td>If %s %ld for %d cycle(s) then %s else if recovered then 
%s</td></tr>", 
+         operatornames[q->operator],
+         q->limit, q->max_cycle, 
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_LOAD1: 
          
-         out_print(res,"<tr><td>Load average (1min)</td>"
-                   "<td>If %s %.1f for %d cycle(s) then %s</td></tr>", 
-                   operatornames[q->operator],
-                   q->limit/10.0, q->max_cycle, 
-                   actionnames[q->action]);
-         break;
+       out_print(res,"<tr><td>Load average (1min)</td>"
+         "<td>If %s %.1f for %d cycle(s) then %s else if recovered then 
%s</td></tr>", 
+         operatornames[q->operator],
+         q->limit/10.0, q->max_cycle, 
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_LOAD5: 
          
-         out_print(res,"<tr><td>Load average (5min)</td>"
-                   "<td>If %s %.1f for %d cycle(s) then %s</td></tr>", 
-                   operatornames[q->operator],
-                   q->limit/10.0, q->max_cycle, 
-                   actionnames[q->action]);
-         break;
+       out_print(res,"<tr><td>Load average (5min)</td>"
+         "<td>If %s %.1f for %d cycle(s) then %s else if recovered then 
%s</td></tr>", 
+         operatornames[q->operator],
+         q->limit/10.0, q->max_cycle, 
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_LOAD15: 
-         out_print(res,"<tr><td>Load average (15min)</td>"
-                   "<td>If %s %.1f for %d cycle(s) then %s</td></tr>", 
-                   operatornames[q->operator],
-                   q->limit/10.0, q->max_cycle, 
-                   actionnames[q->action]);
-         break;
+
+       out_print(res,"<tr><td>Load average (15min)</td>"
+         "<td>If %s %.1f for %d cycle(s) then %s else if recovered then 
%s</td></tr>", 
+         operatornames[q->operator],
+         q->limit/10.0, q->max_cycle, 
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_CHILDREN:
-         out_print(res,"<tr><td>Children</td>"
-                   "<td>If %s %d for %d cycle(s) then %s</td></tr>",
-                   operatornames[q->operator],
-                   q->limit, q->max_cycle,
-                   actionnames[q->action]);
-         break;
+
+       out_print(res,"<tr><td>Children</td>"
+         "<td>If %s %d for %d cycle(s) then %s else if recovered then 
%s</td></tr>",
+         operatornames[q->operator],
+         q->limit, q->max_cycle,
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_TOTAL_MEM_KBYTE:
-         out_print(res,"<tr><td>Memory amount limit (incl. children)</td>"
-                   "<td>If %s %d for %d cycle(s) then %s</td></tr>",
-                   operatornames[q->operator],
-                   q->limit, q->max_cycle,
-                   actionnames[q->action]);
-         break;
+
+       out_print(res,"<tr><td>Memory amount limit (incl. children)</td>"
+         "<td>If %s %d for %d cycle(s) then %s else if recovered then 
%s</td></tr>",
+         operatornames[q->operator],
+         q->limit, q->max_cycle,
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       case RESOURCE_ID_TOTAL_MEM_PERCENT:
-         out_print(res,"<tr><td>Memory usage limit (incl. children)</td>"
-                   "<td>If %s %.1f%% for %d cycle(s) then %s</td></tr>",
-                   operatornames[q->operator],
-                   q->limit/10.0, q->max_cycle,
-                   actionnames[q->action]);
-         break;
+
+       out_print(res,"<tr><td>Memory usage limit (incl. children)</td>"
+         "<td>If %s %.1f%% for %d cycle(s) then %s else if recovered then 
%s</td></tr>",
+         operatornames[q->operator],
+         q->limit/10.0, q->max_cycle,
+         actionnames[a->failed->id], actionnames[a->passed->id]);
+       break;
          
       }    
     }
@@ -1711,41 +1713,34 @@
   {
     Port_T n;
     for(n= s->portlist; n; n= n->next) {
+      EventAction_T a= n->action;
       if(n->family == AF_INET) {
-       if(n->SSL.use_ssl) {
-         out_print(res,
-                   "<tr><td>Host:Port</td>"
-                   "<td>if failed %s:%d%s [%s via SSL] then %s</td></tr>",
-                   n->hostname, n->port, n->request?n->request:"",
-                   n->protocol->name, actionnames[n->action]);
-         
-         if(n->SSL.certmd5 != NULL) {
-           out_print(res,
-             "<tr><td>Server certificate md5 sum</td><td>%s</td></tr>",
-                     n->SSL.certmd5);
-         }
-       } else {
+       out_print(res,
+         "<tr><td>Host:Port</td>"
+         "<td>If failed %s:%d%s [%s%s] then %s else if recovered then 
%s</td></tr>",
+         n->hostname, n->port, n->request?n->request:"",
+          n->SSL.use_ssl?" via SSL":"", n->protocol->name,
+          actionnames[a->failed->id], actionnames[a->passed->id]);
+       if(n->SSL.certmd5 != NULL)
          out_print(res,
-                   "<tr><td>Host:Port</td>"
-                   "<td>if failed %s:%d%s [%s] then %s</td></tr>",
-                   n->hostname, n->port, n->request?n->request:"",
-                   n->protocol->name, actionnames[n->action]);
-       }
+           "<tr><td>Server certificate md5 sum</td><td>%s</td></tr>",
+           n->SSL.certmd5);
       } else if(n->family == AF_UNIX) {
        out_print(res,
-                 "<tr><td>Unix Socket</td>"
-                 "<td>if failed %s [%s] then %s</td></tr>",
-                 n->pathname, n->protocol->name, actionnames[n->action]);
+         "<tr><td>Unix Socket</td><td>If failed %s [%s] then %s else if 
recovered then %s</td></tr>",
+         n->pathname, n->protocol->name, actionnames[a->failed->id], 
actionnames[a->passed->id]);
       }
     }
   }
   {
     Icmp_T n;
-    for(n= s->icmplist; n; n= n->next)
+    for(n= s->icmplist; n; n= n->next) {
+      EventAction_T a= n->action;
       out_print(res,
-               "<tr><td>ICMP</td>"
-               "<td>if failed %s with timeout %d seconds then %s</td></tr>",
-               icmpnames[n->type], n->timeout, actionnames[n->action]);
+       "<tr><td>ICMP</td>"
+       "<td>If failed %s with timeout %d seconds then %s else if recovered 
then %s</td></tr>",
+       icmpnames[n->type], n->timeout, actionnames[a->failed->id], 
actionnames[a->passed->id]);
+    }
   }
   
   print_alerts(res, s->maillist);
@@ -1775,32 +1770,32 @@
       out_print(res, "All events");
 
     } else {
-      if(IS_EVENT_SET(r->events, EVENT_START))
-         out_print(res, "Start ");
-      if(IS_EVENT_SET(r->events, EVENT_STOP))
-         out_print(res, "Stop ");
-      if(IS_EVENT_SET(r->events, EVENT_RESTART))
-         out_print(res, "Restart ");
       if(IS_EVENT_SET(r->events, EVENT_CHECKSUM))
          out_print(res, "Checksum ");
+      if(IS_EVENT_SET(r->events, EVENT_CONNECTION))
+         out_print(res, "Connection ");
+      if(IS_EVENT_SET(r->events, EVENT_DATA))
+         out_print(res, "Data ");
+      if(IS_EVENT_SET(r->events, EVENT_EXEC))
+         out_print(res, "Exec ");
+      if(IS_EVENT_SET(r->events, EVENT_GID))
+         out_print(res, "Gid ");
+      if(IS_EVENT_SET(r->events, EVENT_INVALID))
+         out_print(res, "Invalid ");
+      if(IS_EVENT_SET(r->events, EVENT_NONEXIST))
+         out_print(res, "Nonexist ");
+      if(IS_EVENT_SET(r->events, EVENT_PERMISSION))
+         out_print(res, "Permission ");
       if(IS_EVENT_SET(r->events, EVENT_RESOURCE))
          out_print(res, "Resource ");
+      if(IS_EVENT_SET(r->events, EVENT_SIZE))
+         out_print(res, "Size ");
       if(IS_EVENT_SET(r->events, EVENT_TIMEOUT))
          out_print(res, "Timeout ");
       if(IS_EVENT_SET(r->events, EVENT_TIMESTAMP))
          out_print(res, "Timestamp ");
-      if(IS_EVENT_SET(r->events, EVENT_SIZE))
-         out_print(res, "Size ");
-      if(IS_EVENT_SET(r->events, EVENT_CONNECTION))
-         out_print(res, "Connection ");
-      if(IS_EVENT_SET(r->events, EVENT_PERMISSION))
-         out_print(res, "Permission ");
       if(IS_EVENT_SET(r->events, EVENT_UID))
          out_print(res, "Uid ");
-      if(IS_EVENT_SET(r->events, EVENT_GID))
-         out_print(res, "Gid ");
-      if(IS_EVENT_SET(r->events, EVENT_UNMONITOR))
-         out_print(res, "Unmonitor ");
     }
       
     out_print(res, "</td></tr>");
@@ -1898,14 +1893,47 @@
   }
   
   out_print(res,
-           "<tr><td>Check service</td><td>Every %d cycle</td></tr>",
+           "<tr><td>Check service</td><td>every %d cycle</td></tr>",
            s->every?s->every:1);
   
-  if(s->def_timeout) {
+  if(s->action_DATA) {
+    EventAction_T a= s->action_DATA;
+    out_print(res,
+      "<tr><td>Data access</td>"
+      "<td>If data access failed then %s else if recovered then %s</td></tr>",
+       actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+
+  if(s->action_EXEC) {
+    EventAction_T a= s->action_EXEC;
+    out_print(res,
+      "<tr><td>Execution failure</td>"
+      "<td>If program execution failed then %s else if recovered then 
%s</td></tr>",
+       actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+
+  if(s->action_INVALID) {
+    EventAction_T a= s->action_INVALID;
+    out_print(res,
+      "<tr><td>Invalid object</td>"
+      "<td>If invalid object type then %s else if recovered then %s</td></tr>",
+       actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+
+  if(s->action_NONEXIST) {
+    EventAction_T a= s->action_NONEXIST;
+    out_print(res,
+      "<tr><td>Nonexistence</td>"
+      "<td>If does not exist then %s else if recovered then %s</td></tr>",
+       actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+
+  if(s->def_timeout && s->action_TIMEOUT) {
+    EventAction_T a= s->action_TIMEOUT;
     out_print(res,
-             "<tr><td>Timeout</td><td>"
-             "Timeout if %d restart within %d cycles</td></tr>",
-             s->to_start, s->to_cycle);
+      "<tr><td>Timeout</td>"
+      "<td>If %d restart within %d cycles then %s else if recovered then 
%s</td></tr>",
+       s->to_start, s->to_cycle, actionnames[a->failed->id], 
actionnames[a->passed->id]);
   }
 
 }
diff -Naur monit-cvs20040315/l.l monit-cvs20040315-mp/l.l
--- monit-cvs20040315/l.l       2004-03-02 16:39:54.000000000 +0100
+++ monit-cvs20040315-mp/l.l    2004-03-22 13:46:02.000000000 +0100
@@ -226,6 +226,11 @@
 sha1              { return SHA1HASH; }
 crypt             { return CRYPT; }
 signature         { return SIGNATURE; }
+nonexist          { return NONEXIST; }
+invalid           { return INVALID; }
+data              { return DATA; }
+recovered         { return RECOVERED; }
+else              { return ELSE; }
 include           { BEGIN(INCLUDE); }
 
 {byte}            { return BYTE; }
diff -Naur monit-cvs20040315/monitor.h monit-cvs20040315-mp/monitor.h
--- monit-cvs20040315/monitor.h 2004-03-12 16:37:54.000000000 +0100
+++ monit-cvs20040315-mp/monitor.h      2004-03-22 21:20:53.000000000 +0100
@@ -99,6 +99,7 @@
 #define ACTION_STOP        3
 #define ACTION_EXEC        4
 #define ACTION_UNMONITOR   5
+#define ACTION_START       6
 
 #define TYPE_DEVICE        0
 #define TYPE_DIRECTORY     1
@@ -127,6 +128,7 @@
 #define UNIT_MEGABYTE      1048580
 #define UNIT_GIGABYTE      1073740000
 
+#define HASH_UNKNOWN       0
 #define HASH_MD5           1
 #define HASH_SHA1          2
 #define DEFAULT_HASH       HASH_MD5
@@ -182,6 +184,38 @@
 
 /* --------------------------------------------------------- Data structures */
 
+
+/**
+ * Defines a Command with ARGMAX optional arguments. The arguments
+ * array must be NULL terminated and the first entry is the program
+ * itself. In addition, a user and group may be set for the Command
+ * which means that the Command should run as a certain user and with
+ * certain group.
+ */
+typedef struct mycommand {
+  char *arg[ARGMAX];                             /**< Program with arguments */
+  int   length;                       /**< The length of the arguments array */
+  int   has_uid;          /**< TRUE if a new uid is defined for this Command */
+  uid_t uid;         /**< The user id to switch to when running this Command */
+  int   has_gid;          /**< TRUE if a new gid is defined for this Command */
+  gid_t gid;        /**< The group id to switch to when running this Command */
+} *Command_T;
+
+
+/** Defines an event action object */
+typedef struct myaction {
+    int       id;                                     /**< Action to be done */
+    Command_T exec;                    /**< Optional command to be executed  */
+} *Action_T;
+
+
+/** Defines event's up and down actions */
+typedef struct myeventaction {
+  Action_T  failed;                  /**< Action in the case of failure down */
+  Action_T  passed;                    /**< Action in the case of failure up */
+} *EventAction_T;
+
+
 /** Defines a mailinglist object */
 typedef struct mymail {
   char *to;                         /**< Mail address for alert notification */
@@ -286,23 +320,6 @@
 };
 
 
-/**
- * Defines a Command with ARGMAX optional arguments. The arguments
- * array must be NULL terminated and the first entry is the program
- * itself. In addition, a user and group may be set for the Command
- * which means that the Command should run as a certain user and with
- * certain group.
- */
-typedef struct mycommand {
-  char *arg[ARGMAX];                             /**< Program with arguments */
-  int length;                         /**< The length of the arguments array */
-  int has_uid;            /**< TRUE if a new uid is defined for this Command */
-  uid_t uid;         /**< The user id to switch to when running this Command */
-  int has_gid;            /**< TRUE if a new gid is defined for this Command */
-  gid_t gid;        /**< The group id to switch to when running this Command */
-} *Command_T;
-
-
 /** Defines a protocol object with protocol functions */
 typedef struct myprotocol {
   const char *name;                                       /**< Protocol name */
@@ -336,12 +353,9 @@
   char *pathname;                   /**< Pathname, in case of an UNIX socket */
   char *address;               /**< Human readable destination of the socket */
   Generic_T generic;                                /**< Generic test handle */
-  int  action;                                /**< Action in case of failure */
-  int  event_flag;              /**< TRUE if an event occured on this object */
-  int  event_handled;        /**< TRUE if this event has been handled before */
-  Command_T exec;      /**< Optional command to be executed upon a port event*/
   int timeout;   /**< The timeout in seconds to wait for connect or read i/o */
   int is_available;                /**< TRUE if the server/port is available */
+  EventAction_T action;  /**< Description of the action upon event occurence */
   
   struct {
     int use_ssl;          /**< TRUE if this port requires SSL for connection */
@@ -361,11 +375,8 @@
 typedef struct myicmp {
   int type;                                              /**< ICMP type used */
   int timeout;              /**< The timeout in seconds to wait for response */
-  int action;                                 /**< Action in case of failure */
-  int event_flag;               /**< TRUE if an event occured on this object */
-  int  event_handled;        /**< TRUE if this event has been handled before */
-  Command_T exec;     /**< Optional command to be executed upon a icmp event */
   int is_available;                     /**< TRUE if the server is available */
+  EventAction_T action;  /**< Description of the action upon event occurence */
   
   /** For internal use */
   struct myicmp *next;                               /**< next icmp in chain */
@@ -387,10 +398,7 @@
   int  operator;                                    /**< Comparison operator */
   int  cycle;                                     /**< Cycle overrun counter */
   int  max_cycle;                                   /**< Cycle overrun limit */
-  int  action;                                /**< Action in case of failure */
-  int  event_flag;              /**< TRUE if an event occured on this object */
-  int  event_handled;        /**< TRUE if this event has been handled before */
-  Command_T exec;   /**< Optional command to be executed upon a resurce event*/
+  EventAction_T action;  /**< Description of the action upon event occurence */
   
   /** For internal use */
   struct myresource *next;                       /**< next resource in chain */
@@ -401,12 +409,9 @@
 typedef struct mytimestamp {
   int  operator;                                    /**< Comparison operator */
   int  time;                                        /**< Timestamp watermark */
-  int  action;                                /**< Action in case of failure */
   int  test_changes;            /**< TRUE if we only should test for changes */
   time_t timestamp; /**< The original last modified timestamp for this object*/
-  int  event_flag;              /**< TRUE if an event occured on this object */
-  int  event_handled;        /**< TRUE if this event has been handled before */
-  Command_T exec; /**< Optional command to be executed upon a timestamp event*/
+  EventAction_T action;  /**< Description of the action upon event occurence */
   
   /** For internal use */
   struct mytimestamp *next;                     /**< next timestamp in chain */
@@ -417,12 +422,9 @@
 typedef struct mysize {
   int  operator;                                    /**< Comparison operator */
   unsigned long size;                                    /**< Size watermark */
-  int  action;                                /**< Action in case of failure */
   int  test_changes;            /**< TRUE if we only should test for changes */
   unsigned long runsize;              /**< The original size for this object */
-  int  event_flag;              /**< TRUE if an event occured on this object */
-  int  event_handled;        /**< TRUE if this event has been handled before */
-  Command_T exec;     /**< Optional command to be executed upon a size event */
+  EventAction_T action;  /**< Description of the action upon event occurence */
   
   /** For internal use */
   struct mysize *next;                          /**< next timestamp in chain */
@@ -434,39 +436,31 @@
   char     *hash;                 /**< A checksum hash computed for the path */
   int       type;                   /**< The type of hash (e.g. md5 or sha1) */
   int       length;                                  /**< Length of the hash */
-  int       action;                           /**< Action in case of failure */
-  int       event_flag;         /**< TRUE if an event occured on this object */
-  Command_T exec; /**< Optional command to be executed upon a checksum event */
+  EventAction_T action;  /**< Description of the action upon event occurence */
 } *Checksum_T;
 
 
 /** Defines permission object */
 typedef struct myperm {
   int       perm;                                     /**< Access permission */
-  int       action;                           /**< Action in case of failure */
-  int       event_flag;         /**< TRUE if an event occured on this object */
-  Command_T exec;     /**< Optional command to be executed upon a perm event */
   int       has_error;       /**< TRUE if the service has a permission error */
+  EventAction_T action;  /**< Description of the action upon event occurence */
 } *Perm_T;
 
 
 /** Defines uid object */
 typedef struct myuid {
   uid_t     uid;                                            /**< Owner's uid */
-  int       action;                           /**< Action in case of failure */
-  int       event_flag;         /**< TRUE if an event occured on this object */
-  Command_T exec;      /**< Optional command to be executed upon a uid event */
   int       has_error;              /**< TRUE if the service has a UID error */
+  EventAction_T action;  /**< Description of the action upon event occurence */
 } *Uid_T;
 
 
 /** Defines gid object */
 typedef struct mygid {
   gid_t     gid;                                            /**< Owner's gid */
-  int       action;                           /**< Action in case of failure */
-  int       event_flag;         /**< TRUE if an event occured on this object */
-  Command_T exec;       /**< Optional command to be executed upon a gid event*/
   int       has_error;              /**< TRUE if the service has a GID error */
+  EventAction_T action;  /**< Description of the action upon event occurence */
 } *Gid_T;
 
 
@@ -510,10 +504,7 @@
   int  operator;                                    /**< Comparison operator */
   long limit_absolute;                               /**< Watermark - blocks */
   int  limit_percent;                               /**< Watermark - percent */
-  int  action;                                /**< Action in case of failure */
-  int  event_flag;              /**< TRUE if an event occured on this object */
-  int  event_handled;        /**< TRUE if this event has been handled before */
-  Command_T exec;   /**< Optional command to be executed upon a device event */
+  EventAction_T action;  /**< Description of the action upon event occurence */
 
   /** For internal use */
   struct mydevice *next;                           /**< next device in chain */
@@ -527,8 +518,6 @@
   char *name;                                  /**< Service descriptive name */
   char *group;                                       /**< Service group name */
   int (*check)(struct myservice *);       /**< Service verification function */
-  Command_T start;                    /**< The start command for the service */
-  Command_T stop;                      /**< The stop command for the service */
   int  type;                                     /**< Monitored service type */
   int  do_monitor; /**< Monitor flag, if FALSE, the service is not monitored */
   int  mode;                            /**< Monitoring mode for the service */
@@ -538,17 +527,18 @@
   int  to_cycle;                                  /**< Timeout cycle ceiling */
   int  every;                        /**< Check this program at given cycles */
   int  nevery;          /**< Counter for every.  When nevery == every, check */
-  int  def_every;              /**< TRUE if every is defined for the service */
   int  def_timeout;          /**< TRUE if timeout is defined for the service */
+  int  def_every;              /**< TRUE if every is defined for the service */
   int  def_procinfo;        /**< TRUE if procinfo is defined for the service */
   int  visited;      /**< Service visited flag, set if dependencies are used */
   int  depend_visited;/**< Depend visited flag, set if dependencies are used */
-  int  event_handled; /**< TRUE if an event has been handled on this service */
+  Command_T     start;                /**< The start command for the service */
+  Command_T     stop;                  /**< The stop command for the service */
 
+  Dependant_T dependantlist;                     /**< Dependant service list */
   Mail_T      maillist;                  /**< Alert notification mailinglist */
-  Dependant_T dependantlist;                      /**<Dependant service list */
 
-  /** Tests */
+  /** Test rules and event handlers */
   Checksum_T  checksum;                                  /**< Checksum check */
   Device_T    devicelist;                             /**< Device check list */
   Gid_T       gid;                                            /**< Gid check */
@@ -560,9 +550,27 @@
   Timestamp_T timestamplist;                       /**< Timestamp check list */
   Uid_T       uid;                                            /**< Uid check */
   
+  /** General event handlers */
+  EventAction_T action_DATA;       /**< Description of the action upon event */
+  EventAction_T action_EXEC;       /**< Description of the action upon event */
+  EventAction_T action_INVALID;    /**< Description of the action upon event */
+  EventAction_T action_NONEXIST;   /**< Description of the action upon event */
+  EventAction_T action_TIMEOUT;    /**< Description of the action upon event */
+
   /** Runtime parameters */
   DeviceInfo_T devinfo;                       /**< Data for the device check */
   ProcInfo_T   procinfo;                      /**< Data for the procfs check */
+  struct myevent {
+    int               id;                      /**< The event identification */
+    struct myservice *source;                              /**< Event source */
+    short             state;            /**< TRUE if failed, FALSE if passed */
+    short             state_changed;              /**< TRUE if state changed */
+    char             *message;    /**< Optional message describing the event */
+    EventAction_T     action;           /**< Description of the event action */
+    /** For internal use */
+    pthread_mutex_t   mutex;      /**< Mutex used for action synchronization */
+    struct myevent   *next;                         /**< next event in chain */
+  } *eventslist;                                    /**< Pending events list */
 
   /** Context specific parameters */
   char *path;   /**< Path to the device, file, directory or process pid file */
@@ -574,6 +582,7 @@
   struct myservice *next_depend;           /**< next depend service in chain */
 } *Service_T;
 
+typedef struct myevent *Event_T;
 
 /* -------------------------------------------------------- Global variables */
 
@@ -649,7 +658,6 @@
 void  sendmail(Mail_T);
 int   sock_msg(int sock, char *, ...);
 void  init_env();
-void  destroy_env();
 void *xmalloc (int);
 void *xcalloc(long, long);
 char *xstrdup(const char *);
diff -Naur monit-cvs20040315/monitrc monit-cvs20040315-mp/monitrc
--- monit-cvs20040315/monitrc   2004-01-30 08:10:23.000000000 +0100
+++ monit-cvs20040315-mp/monitrc        2004-03-22 17:19:24.000000000 +0100
@@ -332,8 +332,9 @@
 #    if failed permission 700 then alert
 #    if failed uid data then alert
 #    if failed gid data then alert
-#    if size > 100 MB then alert
 #    if timestamp > 15 minutes then alert
+#    if size > 100 MB then alert
+#       else if recovered then exec "/check/my/db"
 #
 #
 #  check directory bin with path /bin
diff -Naur monit-cvs20040315/p.y monit-cvs20040315-mp/p.y
--- monit-cvs20040315/p.y       2004-03-13 10:48:08.000000000 +0100
+++ monit-cvs20040315-mp/p.y    2004-03-22 23:58:39.000000000 +0100
@@ -116,23 +116,22 @@
     int pidfile;
   }; 
 
-
   struct PortSet {
     int  socket;
-    char *hostname;
     int  port;
     int  type;
     int  family;
     int  ssl;
     int  sslversion;
+    int  timeout;
+    char *certmd5;
+    char *hostname;
+    char *pathname;
     char *request;
     char *request_checksum;
     Generic_T generic;
-    char *pathname;
-    char *certmd5;
     Protocol_T protocol;
-    int  action;
-    int timeout;
+    EventAction_T action;
   };
 
   struct ResourceSet {
@@ -140,42 +139,42 @@
     int  limit;
     int  operator;
     int  max_cycle;
-    int  action;
+    EventAction_T action;
   };
 
   struct TimestampSet {
     int  operator;
     int  time;
     int  test_changes;
-    int  action;
+    EventAction_T action;
   };
 
   struct SizeSet {
     int  operator;
     unsigned int size;
     int  test_changes;
-    int  action;
+    EventAction_T action;
   };
 
   struct ChecksumSet {
-    char *hash;
-    int   action;
     int   type;
+    char *hash;
+    EventAction_T action;
   };
 
   struct PermSet {
     int perm;
-    int action;
+    EventAction_T action;
   };
 
   struct UidSet {
     uid_t uid;
-    int   action;
+    EventAction_T action;
   };
 
   struct GidSet {
     gid_t gid;
-    int   action;
+    EventAction_T action;
   };
 
   struct DeviceSet {
@@ -183,13 +182,13 @@
     int  operator;
     long limit_absolute;
     int  limit_percent;
-    int  action;
+    EventAction_T action;
   };
 
   struct IcmpSet {
     int  type;
     int  timeout;
-    int  action;
+    EventAction_T action;
   };
 
   /* yacc interface */
@@ -217,21 +216,22 @@
   static struct mymail mailset;
   static unsigned int eventset;
   static Command_T command= NULL;
+  static Command_T command1= NULL;
+  static Command_T command2= NULL;
   static Service_T depend_list= NULL;
   static struct IHavePrecedence ihp= {FALSE, FALSE, FALSE};
-  static struct PortSet portset= {-1, NULL, 0, SOCK_STREAM, AF_INET, FALSE,
-                                 SSL_VERSION_AUTO, NULL,NULL,  NULL, NULL,
-                                 NULL, NULL, ACTION_ALERT, NET_TIMEOUT};
-  static struct ResourceSet resourceset= {0, 0, OPERATOR_EQUAL, 1,
-    ACTION_ALERT};
-  static struct TimestampSet timestampset= {OPERATOR_EQUAL, 0, 0, 
ACTION_ALERT};
-  static struct SizeSet sizeset= {OPERATOR_EQUAL, 0, 0, ACTION_ALERT};
-  static struct ChecksumSet checksumset= {NULL, ACTION_ALERT};
-  static struct PermSet permset= {0, ACTION_ALERT};
-  static struct UidSet uidset= {0, ACTION_ALERT};
-  static struct GidSet gidset= {0, ACTION_ALERT};
-  static struct DeviceSet deviceset= {0, OPERATOR_EQUAL, -1, -1, ACTION_ALERT};
-  static struct IcmpSet icmpset= {ICMP_ECHO, NET_TIMEOUT, ACTION_ALERT};
+  static struct PortSet portset= {-1, 0, SOCK_STREAM, AF_INET, FALSE,
+    SSL_VERSION_AUTO, NET_TIMEOUT, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL};
+  static struct ResourceSet resourceset= {0, 0, OPERATOR_EQUAL, 1, NULL};
+  static struct TimestampSet timestampset= {OPERATOR_EQUAL, 0, 0, NULL};
+  static struct SizeSet sizeset= {OPERATOR_EQUAL, 0, 0, NULL};
+  static struct ChecksumSet checksumset= {HASH_UNKNOWN, NULL, NULL};
+  static struct PermSet permset= {0, NULL};
+  static struct UidSet uidset= {0, NULL};
+  static struct GidSet gidset= {0, NULL};
+  static struct DeviceSet deviceset= {0, OPERATOR_EQUAL, -1, -1, NULL};
+  static struct IcmpSet icmpset= {ICMP_ECHO, NET_TIMEOUT, NULL};
   static char * htpasswd_file= NULL;
   static int    digesttype= DIGEST_CLEARTEXT;
   
@@ -263,6 +263,7 @@
   static void  addgid(struct GidSet *);
   static void  addeuid(uid_t);
   static void  addegid(gid_t);
+  static void  addeventaction(EventAction_T *, int, int);
   static void  setlogfile(char *);
   static void  setpidfile(char *);
   static void  reset_mailset();
@@ -297,7 +298,7 @@
   char *string;
 }
 
-%token IF THEN FAILED CYCLE
+%token IF ELSE THEN FAILED CYCLE
 %token SET LOGFILE FACILITY DAEMON SYSLOG MAILSERVER HTTPD ALLOW ADDRESS INIT
 %token READONLY CLEARTEXT MD5HASH SHA1HASH CRYPT
 %token PEMFILE ENABLE DISABLE HTTPDSSL CLIENTPEMFILE ALLOWSELFCERTIFICATION
@@ -321,7 +322,7 @@
 %token SSLAUTO SSLV2 SSLV3 TLSV1 CERTMD5
 %token BYTE KILOBYTE MEGABYTE GIGABYTE
 %token INODE SPACE PERMISSION SIZE
-%token EXEC UNMONITOR ICMP ICMPECHO
+%token EXEC UNMONITOR ICMP ICMPECHO NONEXIST INVALID DATA RECOVERED
 
 %left GREATER LESS EQUAL NOTEQUAL
 
@@ -709,24 +710,44 @@
                 | GID NUMBER { addegid( get_gid(NULL, $2) ); }
                 ;
 
-connection      : IF FAILED host port type protocol nettimeout THEN action {
+connection      : IF FAILED host port type protocol nettimeout THEN action1 {
                    portset.timeout= $<number>7;
-                   portset.action= $<number>9;
+                   addeventaction(&(portset).action, $<number>9, ACTION_ALERT);
+                  addport(&portset);
+                  }
+                | IF FAILED host port type protocol nettimeout THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                   portset.timeout= $<number>7;
+                   addeventaction(&(portset).action, $<number>9, $<number>14);
                   addport(&portset);
                   }
                 ;
 
-connectionunix  : IF FAILED unixsocket type protocol nettimeout THEN action {
+connectionunix  : IF FAILED unixsocket type protocol nettimeout THEN action1 {
                    portset.timeout= $<number>6;
-                   portset.action= $<number>8;
+                   addeventaction(&(portset).action, $<number>8, ACTION_ALERT);
+                   addport(&portset);
+                  }
+                | IF FAILED unixsocket type protocol nettimeout THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                   portset.timeout= $<number>6;
+                   addeventaction(&(portset).action, $<number>8, $<number>13);
                    addport(&portset);
                   }
                 ;
 
-icmp            : IF FAILED ICMP icmptype nettimeout THEN action {
+
+icmp            : IF FAILED ICMP icmptype nettimeout THEN action1 {
                    icmpset.type= $<number>4;
                    icmpset.timeout= $<number>5;
-                   icmpset.action= $<number>7;
+                   addeventaction(&(icmpset).action, $<number>7, ACTION_ALERT);
+                   addicmp(&icmpset);
+                  }
+                | IF FAILED ICMP icmptype nettimeout THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                   icmpset.type= $<number>4;
+                   icmpset.timeout= $<number>5;
+                   addeventaction(&(icmpset).action, $<number>7, $<number>12);
                    addicmp(&icmpset);
                   }
                 ;
@@ -842,19 +863,19 @@
                 | eventoptionlist eventoption
                 ;
 
-eventoption     : START      { eventset |= EVENT_START; }
-                | STOP       { eventset |= EVENT_STOP; }
-                | RESTART    { eventset |= EVENT_RESTART; }
-                | CHECKSUM   { eventset |= EVENT_CHECKSUM; }
+eventoption     : CHECKSUM   { eventset |= EVENT_CHECKSUM; }
+                | CONNECTION { eventset |= EVENT_CONNECTION; }
+                | DATA       { eventset |= EVENT_DATA; }
+                | EXEC       { eventset |= EVENT_EXEC; }
+                | GID        { eventset |= EVENT_GID; }
+                | INVALID    { eventset |= EVENT_INVALID; }
+                | NONEXIST   { eventset |= EVENT_NONEXIST; }
+                | PERMISSION { eventset |= EVENT_PERMISSION; }
                 | RESOURCE   { eventset |= EVENT_RESOURCE; }
+                | SIZE       { eventset |= EVENT_SIZE; }
                 | TIMEOUT    { eventset |= EVENT_TIMEOUT; }
                 | TIMESTAMP  { eventset |= EVENT_TIMESTAMP; }
-                | SIZE       { eventset |= EVENT_SIZE; }
-                | CONNECTION { eventset |= EVENT_CONNECTION; }
-                | PERMISSION { eventset |= EVENT_PERMISSION; }
                 | UID        { eventset |= EVENT_UID; }
-                | GID        { eventset |= EVENT_GID; }
-                | UNMONITOR  { eventset |= EVENT_UNMONITOR; }
                 ;
 
 formatlist      : /* EMPTY */
@@ -897,8 +918,13 @@
 dependant       : SERVICENAME { adddependant($<string>1); }
                 ;
 
-resourcesystem  : IF resource resourcecycle THEN action {
-                   resourceset.action= $<number>5;
+resourcesystem  : IF resource resourcecycle THEN action1 {
+                   addeventaction(&(resourceset).action, $<number>5, 
ACTION_ALERT);
+                  addresource(&resourceset);
+                 }
+                | IF resource resourcecycle THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                   addeventaction(&(resourceset).action, $<number>5, 
$<number>10);
                   addresource(&resourceset);
                  }
                 ;
@@ -980,16 +1006,30 @@
                 | NUMBER CYCLE { resourceset.max_cycle= $1; }
                 ;
 
-timestamp       : IF TIMESTAMP operator NUMBER time THEN action {
+timestamp       : IF TIMESTAMP operator NUMBER time THEN action1 {
                    timestampset.operator= $<number>3;
                    timestampset.time= ($4 * $<number>5);
-                   timestampset.action= $<number>7;
                    timestampset.test_changes= FALSE;
+                    addeventaction(&(timestampset).action, $<number>7, 
ACTION_ALERT);
+                   addtimestamp(&timestampset);
+                  }
+                | IF TIMESTAMP operator NUMBER time THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                   timestampset.operator= $<number>3;
+                   timestampset.time= ($4 * $<number>5);
+                   timestampset.test_changes= FALSE;
+                    addeventaction(&(timestampset).action, $<number>7, 
$<number>12);
+                   addtimestamp(&timestampset);
+                  }
+                | IF TIMESTAMP CHANGED THEN action1 {
+                   timestampset.test_changes= TRUE;
+                    addeventaction(&(timestampset).action, $<number>5, 
ACTION_ALERT);
                    addtimestamp(&timestampset);
                   }
-                | IF TIMESTAMP CHANGED THEN action {
+                | IF TIMESTAMP CHANGED THEN action1
+                 ELSE IF RECOVERED THEN action2 {
                    timestampset.test_changes= TRUE;
-                   timestampset.action= $<number>5;
+                    addeventaction(&(timestampset).action, $<number>5, 
$<number>10);
                    addtimestamp(&timestampset);
                   }
                 ;
@@ -1010,45 +1050,91 @@
                 ;
 
 action          : ALERT       { $<number>$= ACTION_ALERT; }
-                | RESTART     { $<number>$= ACTION_RESTART; }
-                | STOP        { $<number>$= ACTION_STOP; }
                 | EXEC argumentlist { $<number>$= ACTION_EXEC; }
                 | EXEC argumentlist useroptionlist { $<number>$= ACTION_EXEC; }
+                | RESTART     { $<number>$= ACTION_RESTART; }
+                | START       { $<number>$= ACTION_START; }
+                | STOP        { $<number>$= ACTION_STOP; }
                | UNMONITOR   { $<number>$= ACTION_UNMONITOR; }
                 ;
 
-checksum        : IF FAILED hashtype CHECKSUM THEN action {
-                    checksumset.action= $<number>6;
+action1         : action {
+                    $<number>$= $<number>1;
+                   if($<number>1 == ACTION_EXEC && command) {
+                     command1= command;
+                     command= NULL;
+                    }
+                 }
+                ;
+
+action2         : action {
+                    $<number>$= $<number>1;
+                   if($<number>1 == ACTION_EXEC && command) {
+                     command2= command;
+                     command= NULL;
+                    }
+                 }
+                ;
+
+checksum        : IF FAILED hashtype CHECKSUM THEN action1 {
+                    addeventaction(&(checksumset).action, $<number>6, 
ACTION_ALERT);
                     addchecksum(&checksumset);
                   }
-                | IF FAILED hashtype CHECKSUM EXPECT STRING THEN action {
+                | IF FAILED hashtype CHECKSUM THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    addeventaction(&(checksumset).action, $<number>6, 
$<number>11);
+                    addchecksum(&checksumset);
+                  }
+                | IF FAILED hashtype CHECKSUM EXPECT STRING THEN action1 {
                     checksumset.hash= $6;
-                    checksumset.action= $<number>8;
+                    addeventaction(&(checksumset).action, $<number>8, 
ACTION_ALERT);
+                    addchecksum(&checksumset);
+                  }
+                | IF FAILED hashtype CHECKSUM EXPECT STRING THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    checksumset.hash= $6;
+                    addeventaction(&(checksumset).action, $<number>8, 
$<number>13);
                     addchecksum(&checksumset);
                   }
                 ;
-hashtype        : /* EMPTY */ { checksumset.type= 0; }
-                | MD5HASH { checksumset.type= HASH_MD5; }
-                | SHA1HASH { checksumset.type= HASH_SHA1; }
+hashtype        : /* EMPTY */ { checksumset.type= HASH_UNKNOWN; }
+                | MD5HASH     { checksumset.type= HASH_MD5; }
+                | SHA1HASH    { checksumset.type= HASH_SHA1; }
                 ;
 
-inode           : IF INODE operator NUMBER THEN action {
+inode           : IF INODE operator NUMBER THEN action1 {
                     deviceset.resource= RESOURCE_ID_INODE;
                     deviceset.operator= $<number>3;
                     deviceset.limit_absolute= $4;
-                    deviceset.action= $<number>6;
+                    addeventaction(&(deviceset).action, $<number>6, 
ACTION_ALERT);
                     adddevice(&deviceset);
                   }
-                | IF INODE operator PERCENT THEN action {
+                | IF INODE operator NUMBER THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    deviceset.resource= RESOURCE_ID_INODE;
+                    deviceset.operator= $<number>3;
+                    deviceset.limit_absolute= $4;
+                    addeventaction(&(deviceset).action, $<number>6, 
$<number>11);
+                    adddevice(&deviceset);
+                  }
+                | IF INODE operator PERCENT THEN action1 {
                     deviceset.resource= RESOURCE_ID_INODE;
                     deviceset.operator= $<number>3;
                     deviceset.limit_percent= (int) $<real>4;
-                    deviceset.action= $<number>6;
+                    addeventaction(&(deviceset).action, $<number>6, 
ACTION_ALERT);
+                    adddevice(&deviceset);
+                  }
+                | IF INODE operator PERCENT THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    deviceset.resource= RESOURCE_ID_INODE;
+                    deviceset.operator= $<number>3;
+                    deviceset.limit_percent= (int) $<real>4;
+                    addeventaction(&(deviceset).action, $<number>6, 
$<number>11);
                     adddevice(&deviceset);
                   }
                 ;
 
-space           : IF SPACE operator value unit THEN action {
+space           : IF SPACE operator value unit THEN action1 {
                     if(!DeviceInfo_Usage(current->devinfo, current->path)) {
                       yyerror2("cannot read usage of device %s",
                                current->path);
@@ -1059,14 +1145,37 @@
                        (int)((float)$<real>4 /
                              (float)current->devinfo->f_bsize * 
                              (float)$<number>5 );
-                    deviceset.action= $<number>7;
+                    addeventaction(&(deviceset).action, $<number>7, 
ACTION_ALERT);
                     adddevice(&deviceset);
                   }
-                | IF SPACE operator PERCENT THEN action {
+                | IF SPACE operator value unit THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    if(!DeviceInfo_Usage(current->devinfo, current->path)) {
+                      yyerror2("cannot read usage of device %s",
+                               current->path);
+                   }
+                    deviceset.resource= RESOURCE_ID_SPACE;
+                    deviceset.operator= $<number>3;
+                    deviceset.limit_absolute=
+                       (int)((float)$<real>4 /
+                             (float)current->devinfo->f_bsize * 
+                             (float)$<number>5 );
+                    addeventaction(&(deviceset).action, $<number>7, 
$<number>12);
+                    adddevice(&deviceset);
+                  }
+                | IF SPACE operator PERCENT THEN action1 {
+                    deviceset.resource= RESOURCE_ID_SPACE;
+                    deviceset.operator= $<number>3;
+                    deviceset.limit_percent= (int) $<real>4;
+                    addeventaction(&(deviceset).action, $<number>6, 
ACTION_ALERT);
+                    adddevice(&deviceset);
+                  }
+                | IF SPACE operator PERCENT THEN action1
+                 ELSE IF RECOVERED THEN action2 {
                     deviceset.resource= RESOURCE_ID_SPACE;
                     deviceset.operator= $<number>3;
                     deviceset.limit_percent= (int) $<real>4;
-                    deviceset.action= $<number>6;
+                    addeventaction(&(deviceset).action, $<number>6, 
$<number>11);
                     adddevice(&deviceset);
                   }
                 ;
@@ -1077,49 +1186,95 @@
                 | GIGABYTE { $<number>$= UNIT_GIGABYTE; }
                 ;
 
-permission      : IF FAILED PERMISSION NUMBER THEN action {
+permission      : IF FAILED PERMISSION NUMBER THEN action1 {
+                    permset.perm= check_perm($4);
+                    addeventaction(&(permset).action, $<number>6, 
ACTION_ALERT);
+                   addperm(&permset);
+                 }
+                | IF FAILED PERMISSION NUMBER THEN action1
+                 ELSE IF RECOVERED THEN action2 {
                     permset.perm= check_perm($4);
-                   permset.action= $<number>6;
+                    addeventaction(&(permset).action, $<number>6, $<number>11);
                    addperm(&permset);
                  }
                 ;
 
-size            : IF SIZE operator NUMBER unit THEN action {
+size            : IF SIZE operator NUMBER unit THEN action1 {
                    sizeset.operator= $<number>3;
                    sizeset.size= ((unsigned long)$4 * $<number>5);
-                   sizeset.action= $<number>7;
                    sizeset.test_changes= FALSE;
+                    addeventaction(&(sizeset).action, $<number>7, 
ACTION_ALERT);
                    addsize(&sizeset);
                   }
-                | IF SIZE CHANGED THEN action {
+                | IF SIZE operator NUMBER unit THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                   sizeset.operator= $<number>3;
+                   sizeset.size= ((unsigned long)$4 * $<number>5);
+                   sizeset.test_changes= FALSE;
+                    addeventaction(&(sizeset).action, $<number>7, $<number>12);
+                   addsize(&sizeset);
+                  }
+                | IF SIZE CHANGED THEN action1 {
                    sizeset.test_changes= TRUE;
-                   sizeset.action= $<number>5;
+                    addeventaction(&(sizeset).action, $<number>5, 
ACTION_ALERT);
+                   addsize(&sizeset);
+                  }
+                | IF SIZE CHANGED THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                   sizeset.test_changes= TRUE;
+                    addeventaction(&(sizeset).action, $<number>5, $<number>10);
                    addsize(&sizeset);
                   }
                 ;
 
-uid             : IF FAILED UID STRING THEN action {
+uid             : IF FAILED UID STRING THEN action1 {
                     uidset.uid= get_uid($4, 0);
-                   uidset.action= $<number>6;
+                    addeventaction(&(uidset).action, $<number>6, ACTION_ALERT);
                    adduid(&uidset);
                     FREE($4);
                  }
-                | IF FAILED UID NUMBER THEN action {
+                | IF FAILED UID STRING THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    uidset.uid= get_uid($4, 0);
+                    addeventaction(&(uidset).action, $<number>6, $<number>11);
+                   adduid(&uidset);
+                    FREE($4);
+                 }
+                | IF FAILED UID NUMBER THEN action1 {
                     uidset.uid= get_uid(NULL, $4);
-                   uidset.action= $<number>6;
+                    addeventaction(&(uidset).action, $<number>6, ACTION_ALERT);
+                   adduid(&uidset);
+                 }
+                | IF FAILED UID NUMBER THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    uidset.uid= get_uid(NULL, $4);
+                    addeventaction(&(uidset).action, $<number>6, $<number>11);
                    adduid(&uidset);
                  }
                 ;
 
-gid             : IF FAILED GID STRING THEN action {
+gid             : IF FAILED GID STRING THEN action1 {
+                    gidset.gid= get_gid($4, 0);
+                    addeventaction(&(gidset).action, $<number>6, ACTION_ALERT);
+                   addgid(&gidset);
+                    FREE($4);
+                 }
+                | IF FAILED GID STRING THEN action1
+                 ELSE IF RECOVERED THEN action2 {
                     gidset.gid= get_gid($4, 0);
-                   gidset.action= $<number>6;
+                    addeventaction(&(gidset).action, $<number>6, $<number>11);
                    addgid(&gidset);
                     FREE($4);
                  }
-                | IF FAILED GID NUMBER THEN action {
+                | IF FAILED GID NUMBER THEN action1 {
                     gidset.gid= get_gid(NULL, $4);
-                   gidset.action= $<number>6;
+                    addeventaction(&(gidset).action, $<number>6, ACTION_ALERT);
+                   addgid(&gidset);
+                 }
+                | IF FAILED GID NUMBER THEN action1
+                 ELSE IF RECOVERED THEN action2 {
+                    gidset.gid= get_gid(NULL, $4);
+                    addeventaction(&(gidset).action, $<number>6, $<number>11);
                    addgid(&gidset);
                  }
                 ;
@@ -1306,7 +1461,7 @@
   Run.MailFormat.subject= NULL;
   Run.MailFormat.message= NULL;
   depend_list= NULL;
-  
+
 }
 
 
@@ -1381,6 +1536,13 @@
     createprocinfo();
   }
 
+  /* Initialize general event handlers */
+  addeventaction(&(current)->action_DATA,     ACTION_ALERT,     ACTION_ALERT);
+  addeventaction(&(current)->action_EXEC,     ACTION_ALERT,     ACTION_ALERT);
+  addeventaction(&(current)->action_INVALID,  ACTION_RESTART,   ACTION_ALERT);
+  addeventaction(&(current)->action_NONEXIST, ACTION_RESTART,   ACTION_ALERT);
+  addeventaction(&(current)->action_TIMEOUT,  ACTION_UNMONITOR, ACTION_ALERT);
+  
   pthread_mutex_init(&current->mutex, NULL);
 
 }
@@ -1527,11 +1689,6 @@
     }
   }
   
-  if((p->action == ACTION_EXEC) && command) {
-    p->exec= command;
-    command= NULL;
-  }
-  
   p->next= current->portlist;
   current->portlist= p;
   
@@ -1568,16 +1725,9 @@
   r->next= current->resourcelist;
 
   if(r->max_cycle < 1) {
-
     yyerror2("the number of cycles must be greater then 0");
-    
   }
 
-  if((r->action == ACTION_EXEC) && command) {
-    r->exec= command;
-    command= NULL;
-  }
-  
   current->resourcelist= r;
   reset_resourceset();
 
@@ -1608,11 +1758,6 @@
     }
   }
   
-  if((t->action == ACTION_EXEC) && command) {
-    t->exec= command;
-    command= NULL;
-  }
-
   t->next= current->timestamplist;
   current->timestamplist= t;
 
@@ -1645,11 +1790,6 @@
   if(ss->test_changes)
     s->runsize= (unsigned long)buf.st_size;
  
-  if((s->action == ACTION_EXEC) && command) {
-    s->exec= command;
-    command= NULL;
-  }
- 
   s->next= current->sizelist;
   current->sizelist= s;
 
@@ -1662,12 +1802,13 @@
  */
 static void addchecksum(struct ChecksumSet *cs) {
 
-  Checksum_T c;
   int len;
+  Checksum_T c;
+
   ASSERT(cs);
 
   if(!cs->hash) {
-    if (!cs->type) {
+    if(cs->type == HASH_UNKNOWN) {
       cs->type=DEFAULT_HASH;
     }
     if( !(cs->hash= get_checksum(current->path, cs->type))) {
@@ -1677,9 +1818,9 @@
     }
   }
 
-  len=cleanup_hash_string(cs->hash);
+  len= cleanup_hash_string(cs->hash);
 
-  if (!cs->type) {
+  if(cs->type == HASH_UNKNOWN) {
     if (len==32) {
       cs->type=HASH_MD5;
     } else if (len==40) {
@@ -1702,11 +1843,6 @@
   c->hash= cs->hash;
   c->action= cs->action;
  
-  if((c->action == ACTION_EXEC) && command) {
-    c->exec= command;
-    command= NULL;
-  }
- 
   current->checksum= c;
 
   reset_checksumset();
@@ -1727,11 +1863,6 @@
   p->perm= ps->perm;
   p->action= ps->action;
  
-  if((p->action == ACTION_EXEC) && command) {
-    p->exec= command;
-    command= NULL;
-  }
- 
   current->perm= p;
 
   reset_permset();
@@ -1752,11 +1883,6 @@
   u->uid= us->uid;
   u->action= us->action;
  
-  if((u->action == ACTION_EXEC) && command) {
-    u->exec= command;
-    command= NULL;
-  }
- 
   current->uid= u;
 
   reset_uidset();
@@ -1777,11 +1903,6 @@
   g->gid= gs->gid;
   g->action= gs->action;
  
-  if((g->action == ACTION_EXEC) && command) {
-    g->exec= command;
-    command= NULL;
-  }
- 
   current->gid= g;
 
   reset_gidset();
@@ -1805,11 +1926,6 @@
   dev->limit_percent= ds->limit_percent;
   dev->action= ds->action;
 
-  if((dev->action == ACTION_EXEC) && command) {
-    dev->exec= command;
-    command= NULL;
-  }
-
   dev->next= current->devicelist;
   current->devicelist= dev;
 
@@ -1834,11 +1950,6 @@
     icmp->timeout= is->timeout;
     icmp->action= is->action;
     
-    if((icmp->action == ACTION_EXEC) && command) {
-      icmp->exec= command;
-      command= NULL;
-    }
-    
     icmp->next= current->icmplist;
     current->icmplist= icmp;
     
@@ -1854,6 +1965,37 @@
 
 
 /*
+ * Set EventAction object
+ */
+static void addeventaction(EventAction_T *_ea, int failed, int passed) {
+
+  EventAction_T ea;
+
+  ASSERT(_ea);
+
+  NEW(ea);
+  NEW(ea->failed);
+  NEW(ea->passed);
+  ea->failed->id= failed;
+  ea->passed->id= passed;
+
+  if(failed == ACTION_EXEC) {
+    ASSERT(command1);
+    ea->failed->exec = command1;
+    command1 = NULL;
+  }
+  if(passed == ACTION_EXEC) {
+    ASSERT(command2);
+    ea->passed->exec = command2;
+    command2 = NULL;
+  }
+ 
+  *_ea= ea;
+
+}
+
+
+/*
  * Adds runtime device info to current service
  */
 static void createdevinfo() {
@@ -2421,7 +2563,7 @@
   portset.generic= NULL;
   portset.protocol= NULL;
   portset.pathname= NULL;
-  portset.action= ACTION_ALERT;
+  portset.action= NULL;
   portset.timeout= NET_TIMEOUT;
 
 }
@@ -2435,7 +2577,7 @@
   resourceset.resource_id= 0;
   resourceset.limit= 0;
   resourceset.max_cycle= 1;
-  resourceset.action= ACTION_ALERT;
+  resourceset.action= NULL;
   resourceset.operator= OPERATOR_EQUAL;
 
 }
@@ -2449,7 +2591,7 @@
   timestampset.operator= OPERATOR_EQUAL;
   timestampset.time= 0;
   timestampset.test_changes= 0;
-  timestampset.action= ACTION_ALERT;
+  timestampset.action= NULL;
 
 }
 
@@ -2462,7 +2604,7 @@
   sizeset.operator= OPERATOR_EQUAL;
   sizeset.size= 0;
   sizeset.test_changes= 0;
-  sizeset.action= ACTION_ALERT;
+  sizeset.action= NULL;
 
 }
 
@@ -2472,9 +2614,9 @@
  */
 static void reset_checksumset() {
 
+  checksumset.type= HASH_UNKNOWN;
   checksumset.hash= NULL;
-  checksumset.type= 0;
-  checksumset.action= ACTION_ALERT;
+  checksumset.action= NULL;
 
 }
 
@@ -2485,7 +2627,7 @@
 static void reset_permset() {
 
   permset.perm= 0;
-  permset.action= ACTION_ALERT;
+  permset.action= NULL;
 
 }
 
@@ -2496,7 +2638,7 @@
 static void reset_uidset() {
 
   uidset.uid= 0;
-  uidset.action= ACTION_ALERT;
+  uidset.action= NULL;
 
 }
 
@@ -2507,7 +2649,7 @@
 static void reset_gidset() {
 
   gidset.gid= 0;
-  gidset.action= ACTION_ALERT;
+  gidset.action= NULL;
 
 }
 
@@ -2521,7 +2663,7 @@
   deviceset.operator= OPERATOR_EQUAL;
   deviceset.limit_absolute= -1;
   deviceset.limit_percent= -1;
-  deviceset.action= ACTION_ALERT;
+  deviceset.action= NULL;
 
 }
 
@@ -2533,7 +2675,7 @@
 
   icmpset.type= ICMP_ECHO;
   icmpset.timeout= NET_TIMEOUT;
-  icmpset.action= ACTION_ALERT;
+  icmpset.action= NULL;
 
 }
 
diff -Naur monit-cvs20040315/process.c monit-cvs20040315-mp/process.c
--- monit-cvs20040315/process.c 2004-02-29 23:24:44.000000000 +0100
+++ monit-cvs20040315-mp/process.c      2004-03-22 21:19:54.000000000 +0100
@@ -70,8 +70,9 @@
 /* ------------------------------------------------------------------ Public */
 
 char actionnames[][STRLEN]=   {"ignore", "alert", "restart", "stop", "exec",
-                              "unmonitor"};
+                              "unmonitor", "start", "timeout"};
 char modenames[][STRLEN]=     {"active", "passive", "manual"};
+char checksumnames[][STRLEN]= {"UNKNOWN", "MD5", "SHA1"};
 char operatornames[][STRLEN]= {"greater than", "less than", "equal to",
                                "not equal to"};
 char operatorshortnames[][3]= {">", "<", "=", "!="};
diff -Naur monit-cvs20040315/process.h monit-cvs20040315-mp/process.h
--- monit-cvs20040315/process.h 2004-02-29 21:16:39.000000000 +0100
+++ monit-cvs20040315-mp/process.h      2004-03-17 10:21:37.000000000 +0100
@@ -31,6 +31,7 @@
 extern int  include_children;
 extern char actionnames[][STRLEN];
 extern char modenames[][STRLEN];
+extern char checksumnames[][STRLEN];
 extern char operatornames[][STRLEN];
 extern char operatorshortnames[][3];
 extern char statusnames[][STRLEN];
diff -Naur monit-cvs20040315/state.c monit-cvs20040315-mp/state.c
--- monit-cvs20040315/state.c   2004-02-14 23:36:08.000000000 +0100
+++ monit-cvs20040315-mp/state.c        2004-03-19 10:15:16.000000000 +0100
@@ -78,8 +78,8 @@
 typedef struct mystate {
   char name[STRLEN];   
   int  mode;       
-  int  nstart;     
-  int  ncycle;     
+  int  nstart;
+  int  ncycle;
   int  do_monitor;
   int  has_permission_error;
   int  has_uid_error;
diff -Naur monit-cvs20040315/util.c monit-cvs20040315-mp/util.c
--- monit-cvs20040315/util.c    2004-03-13 10:48:09.000000000 +0100
+++ monit-cvs20040315-mp/util.c 2004-03-22 21:26:57.000000000 +0100
@@ -569,32 +569,32 @@
       if(list->events == (~((unsigned int)0))) {
         printf("All events");
       } else {
-        if(IS_EVENT_SET(list->events, EVENT_START))
-         printf("Start ");
-        if(IS_EVENT_SET(list->events, EVENT_STOP))
-         printf("Stop ");
-        if(IS_EVENT_SET(list->events, EVENT_RESTART))
-         printf("Restart ");
         if(IS_EVENT_SET(list->events, EVENT_CHECKSUM))
          printf("Checksum ");
+        if(IS_EVENT_SET(list->events, EVENT_CONNECTION))
+         printf("Connection ");
+        if(IS_EVENT_SET(list->events, EVENT_DATA))
+         printf("Data ");
+        if(IS_EVENT_SET(list->events, EVENT_EXEC))
+         printf("Exec ");
+        if(IS_EVENT_SET(list->events, EVENT_GID))
+         printf("Gid ");
+        if(IS_EVENT_SET(list->events, EVENT_INVALID))
+         printf("Invalid ");
+        if(IS_EVENT_SET(list->events, EVENT_NONEXIST))
+         printf("Nonexist ");
+        if(IS_EVENT_SET(list->events, EVENT_PERMISSION))
+         printf("Permission ");
         if(IS_EVENT_SET(list->events, EVENT_RESOURCE))
          printf("Resource ");
+        if(IS_EVENT_SET(list->events, EVENT_SIZE))
+         printf("Size ");
         if(IS_EVENT_SET(list->events, EVENT_TIMEOUT))
          printf("Timeout ");
         if(IS_EVENT_SET(list->events, EVENT_TIMESTAMP))
          printf("Timestamp ");
-        if(IS_EVENT_SET(list->events, EVENT_SIZE))
-         printf("Size ");
-        if(IS_EVENT_SET(list->events, EVENT_CONNECTION))
-         printf("Connection ");
-        if(IS_EVENT_SET(list->events, EVENT_PERMISSION))
-         printf("Permission ");
         if(IS_EVENT_SET(list->events, EVENT_UID))
          printf("Uid ");
-        if(IS_EVENT_SET(list->events, EVENT_GID))
-         printf("Gid ");
-        if(IS_EVENT_SET(list->events, EVENT_UNMONITOR))
-         printf("Unmonitor ");
       }
       printf("\n");
     }
@@ -650,65 +650,67 @@
     if(d->dependant != NULL)
       printf(" %-20s = %s\n", "Depends on Service", d->dependant);
 
-  if(s->checksum) {
-    switch (s->checksum->type) {
-    case HASH_MD5:
-        printf(" %-20s = if failed MD5(%s) then %s\n",
-               "Checksum", s->checksum->hash,
-               actionnames[s->checksum->action]);
-        break;
-    case HASH_SHA1:
-        printf(" %-20s = if failed SHA1(%s) then %s\n",
-               "Checksum", s->checksum->hash,
-               actionnames[s->checksum->action]);
-        break;
-    default:
-        printf(" %-20s = if failed UNKNOWN(%s) then %s\n",
-               "Checksum", s->checksum->hash,
-               actionnames[s->checksum->action]);
-        break;
-    }
+  if(s->checksum && s->checksum->action) {
+    EventAction_T a= s->checksum->action;
+
+    printf(" %-20s = if failed %s(%s) then %s else if recovered %s\n",
+      "Checksum", s->checksum->hash, checksumnames[s->checksum->type],
+      actionnames[a->failed->id], actionnames[a->passed->id]);
   }
   
-  if(s->perm)
-    printf(" %-20s = if failed %o then %s\n",
-           "Permission", s->perm->perm, actionnames[s->perm->action]);
-
-  if(s->uid)
-    printf(" %-20s = if failed %d then %s\n",
-           "UID", (int)s->uid->uid, actionnames[s->uid->action]);
-
-  if(s->gid)
-    printf(" %-20s = if failed %d then %s\n",
-           "GID", (int)s->gid->gid, actionnames[s->gid->action]);
+  if(s->perm && s->perm->action) {
+    EventAction_T a= s->perm->action;
+
+    printf(" %-20s = if failed %o then %s else if recovered then %s\n",
+      "Permission", s->perm->perm, actionnames[a->failed->id],
+      actionnames[a->passed->id]);
+  }
+
+  if(s->uid && s->uid->action) {
+    EventAction_T a= s->uid->action;
+
+    printf(" %-20s = if failed %d then %s else if recovered then %s\n",
+      "UID", (int)s->uid->uid, actionnames[a->failed->id],
+      actionnames[a->passed->id]);
+  }
+
+  if(s->gid && s->gid->action) {
+    EventAction_T a= s->gid->action;
+
+    printf(" %-20s = if failed %d then %s else if recovered then %s\n",
+      "GID", (int)s->gid->gid, actionnames[a->failed->id],
+      actionnames[a->passed->id]);
+  }
 
   if(s->portlist) {
     
     for(n= s->portlist; n; n= n->next) {
+      EventAction_T a= n->action;
       
       if(n->family == AF_INET) {
        
        if(n->SSL.use_ssl) {
          
-         printf(" %-20s = %s:%d%s [protocol %s via SSL] with timeout %d 
seconds\n",
+         printf(" %-20s = if failed %s:%d%s [protocol %s via SSL] with timeout 
%d seconds then %s else if recovered then %s\n",
                "Host:Port", n->hostname, n->port, n->request?n->request:"",
-                n->protocol->name, n->timeout);
+                n->protocol->name, n->timeout, actionnames[a->failed->id], 
actionnames[a->passed->id]);
          
          if(n->SSL.certmd5 != NULL)
              printf(" %-20s = %s\n", "Server cert md5 sum", n->SSL.certmd5);
          
         } else {
 
-         printf(" %-20s = %s:%d%s [protocol %s] with timeout %d seconds\n",
+         printf(" %-20s = if failed %s:%d%s [protocol %s] with timeout %d 
seconds then %s else if recovered then %s\n",
                 "Host:Port", n->hostname, n->port, n->request?n->request:"",
-                n->protocol->name, n->timeout);
+                n->protocol->name, n->timeout, actionnames[a->failed->id], 
actionnames[a->passed->id]);
          
        }
        
       } else if(n->family == AF_UNIX) {
        
-        printf(" %-20s = %s [protocol %s] with timeout %d seconds\n",
-              "Unix Socket", n->pathname, n->protocol->name, n->timeout);
+        printf(" %-20s = if failed %s [protocol %s] with timeout %d seconds 
then %s else if recovered then %s\n",
+              "Unix Socket", n->pathname, n->protocol->name, n->timeout,
+               actionnames[a->failed->id], actionnames[a->passed->id]);
        
       }
       
@@ -717,116 +719,139 @@
   }
   
   if(s->icmplist)
-    for(i= s->icmplist; i; i= i->next)
-      printf(" %-20s = if failed %s with timeout %d seconds then %s\n",
-             "ICMP", icmpnames[i->type], i->timeout, actionnames[i->action]);
+    for(i= s->icmplist; i; i= i->next) {
+      EventAction_T a= i->action;
 
-  for(t= s->timestamplist; t; t= t->next) {
-    
-    if(t->test_changes) {
-      printf(" %-20s = if changed then %s\n",
-            "Timestamp",
-            actionnames[t->action]);
-    } else {
-      printf(" %-20s = if %s %d second(s) then %s\n",
-            "Timestamp",
-            operatornames[t->operator],
-            t->time,
-            actionnames[t->action]);
+      printf(" %-20s = if failed %s with timeout %d seconds then %s else if 
recovered then %s\n",
+        "ICMP", icmpnames[i->type], i->timeout, actionnames[a->failed->id],
+        actionnames[a->passed->id]);
     }
+
+  for(t= s->timestamplist; t; t= t->next) {
+    EventAction_T a= t->action;
+
+    if(t->test_changes)
+      printf(" %-20s = if changed then %s else if recovered then %s\n",
+        "Timestamp", actionnames[a->failed->id], actionnames[a->passed->id]);
+    else
+      printf(" %-20s = if %s %d second(s) then %s else if recovered then %s\n",
+        "Timestamp", operatornames[t->operator], t->time,
+        actionnames[a->failed->id], actionnames[a->passed->id]);
     
   }
 
   for(sl= s->sizelist; sl; sl= sl->next) {
+    EventAction_T a= sl->action;
     
-    if(sl->test_changes) {
-      printf(" %-20s = if changed then %s\n",
-            "Size",
-            actionnames[sl->action]);
-    } else {
-      printf(" %-20s = if %s %lu byte(s) then %s\n",
-            "Size",
-            operatornames[sl->operator],
-            sl->size,
-            actionnames[sl->action]);
-    }
+    if(sl->test_changes)
+      printf(" %-20s = if changed then %s else if recovered then %s\n",
+        "Size", actionnames[a->failed->id], actionnames[a->passed->id]);
+    else
+      printf(" %-20s = if %s %lu byte(s) then %s else if recovered then %s\n",
+        "Size", operatornames[sl->operator], sl->size,
+        actionnames[a->failed->id], actionnames[a->passed->id]);
     
   }
 
   for(dl= s->devicelist; dl; dl= dl->next) {
+    EventAction_T a= dl->action;
     
     if(dl->resource == RESOURCE_ID_INODE) {
-
-      printf(" %-20s = if %s %ld %s then %s\n",
-           "Inodes usage limit",
-           operatornames[dl->operator],
-           (dl->limit_absolute > -1)?dl->limit_absolute:dl->limit_percent,
-           (dl->limit_absolute > -1)?"":"%",
-           actionnames[dl->action]);
-
+           
+      printf(" %-20s = if %s %ld %s then %s else if recovered then %s\n",
+        "Inodes usage limit",
+        operatornames[dl->operator],
+        (dl->limit_absolute > -1)?dl->limit_absolute:dl->limit_percent,
+        (dl->limit_absolute > -1)?"":"%",
+        actionnames[a->failed->id], actionnames[a->passed->id]);
+      
     } else if(dl->resource == RESOURCE_ID_SPACE) {
-
-      printf(" %-20s = if %s %ld %s then %s\n",
-           "Space usage limit",
-           operatornames[dl->operator],
-           (dl->limit_absolute > -1)?dl->limit_absolute:dl->limit_percent,
-           (dl->limit_absolute > -1)?"blocks":"%",
-           actionnames[dl->action]);
+           
+      printf(" %-20s = if %s %ld %s then %s else if recovered then %s\n",
+        "Space usage limit",
+        operatornames[dl->operator],
+        (dl->limit_absolute > -1)?dl->limit_absolute:dl->limit_percent,
+        (dl->limit_absolute > -1)?"blocks":"%",
+        actionnames[a->failed->id], actionnames[a->passed->id]);
 
     }
     
   }
 
   for(q= s->resourcelist; q; q= q->next) {
+    EventAction_T a= q->action;
 
     switch(q->resource_id) {
 
     case RESOURCE_ID_CPU_PERCENT: 
 
-      printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s\n", 
-            "CPU usage limit", 
-            operatornames[q->operator], 
-            q->limit/10.0, q->max_cycle, actionnames[q->action]);
+      printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s else if recovered 
then %s\n", 
+        "CPU usage limit", 
+        operatornames[q->operator], q->limit/10.0, q->max_cycle,
+        actionnames[a->failed->id], actionnames[a->passed->id]);
       break;
 
     case RESOURCE_ID_MEM_PERCENT: 
 
-      printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s\n", 
-            "Memory usage limit", 
-            operatornames[q->operator], q->limit/10.0, q->max_cycle, 
-            actionnames[q->action]);
+      printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s else if recovered 
then %s\n", 
+        "Memory usage limit", 
+        operatornames[q->operator], q->limit/10.0, q->max_cycle, 
+        actionnames[a->failed->id], actionnames[a->passed->id]);
       break;
 
     case RESOURCE_ID_MEM_KBYTE: 
 
-      printf(" %-20s = if %s %ldkB for %d cycle(s) then %s\n", 
-            "Memory amount limit", 
-            operatornames[q->operator], q->limit, q->max_cycle, 
-            actionnames[q->action]);
+      printf(" %-20s = if %s %ldkB for %d cycle(s) then %s else if recovered 
then %s\n", 
+        "Memory amount limit", 
+        operatornames[q->operator], q->limit, q->max_cycle, 
+        actionnames[a->failed->id], actionnames[a->passed->id]);
       break;
 
     case RESOURCE_ID_LOAD1: 
 
-      printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n", 
-            "Load avg. (1min)", 
-            operatornames[q->operator], q->limit/10.0, q->max_cycle, 
-            actionnames[q->action]);
+      printf(" %-20s = if %s %.1f for %d cycle(s) then %s else if recovered 
then %s\n", 
+        "Load avg. (1min)", 
+        operatornames[q->operator], q->limit/10.0, q->max_cycle, 
+        actionnames[a->failed->id], actionnames[a->passed->id]);
       break;
 
     case RESOURCE_ID_LOAD5: 
 
-      printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n", 
-            "Load avg. (5min)", 
-            operatornames[q->operator], q->limit/10.0, q->max_cycle, 
-            actionnames[q->action]);
+      printf(" %-20s = if %s %.1f for %d cycle(s) then %s else if recovered 
then %s\n", 
+        "Load avg. (5min)", 
+        operatornames[q->operator], q->limit/10.0, q->max_cycle, 
+        actionnames[a->failed->id], actionnames[a->passed->id]);
       break;
 
     case RESOURCE_ID_LOAD15: 
 
-      printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n", 
-            "Load avg. (15min)", 
-            operatornames[q->operator], q->limit/10.0, q->max_cycle, 
-            actionnames[q->action]);
+      printf(" %-20s = if %s %.1f for %d cycle(s) then %s else if recovered 
then %s\n", 
+        "Load avg. (15min)", 
+        operatornames[q->operator], q->limit/10.0, q->max_cycle, 
+        actionnames[a->failed->id], actionnames[a->passed->id]);
+      break;
+
+    case RESOURCE_ID_CHILDREN:
+
+      printf(" %-20s = If %s %ld for %d cycle(s) then %s else if recovered 
then %s",
+        "Children", operatornames[q->operator], q->limit, q->max_cycle,
+        actionnames[a->failed->id], actionnames[a->passed->id]);
+      break;
+
+    case RESOURCE_ID_TOTAL_MEM_KBYTE:
+
+      printf(" %-20s = If %s %ld for %d cycle(s) then %s else if recovered 
then %s",
+        "Memory amount limit (incl. children)",
+        operatornames[q->operator], q->limit, q->max_cycle,
+        actionnames[a->failed->id], actionnames[a->passed->id]);
+      break;
+
+    case RESOURCE_ID_TOTAL_MEM_PERCENT:
+
+      printf(" %-20s = If %s %.1f%% for %d cycle(s) then %s else if recovered 
then %s",
+        "Memory usage limit (incl. children)",
+        operatornames[q->operator], q->limit/10.0, q->max_cycle,
+        actionnames[a->failed->id], actionnames[a->passed->id]);
       break;
 
     }    
@@ -835,10 +860,35 @@
   if(s->def_every)
     printf(" %-20s = Check service every %d cycles\n", "Every", s->every);
   
+  if(s->action_DATA) {
+    EventAction_T a= s->action_DATA;
+    printf(" %-20s = If data access failed then %s else if recovered then 
%s\n",
+      "Data access", actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
 
-  if(s->def_timeout)
-    printf(" %-20s = Do timeout if %d restart within %d cycles\n",
-          "Timeout", s->to_start, s->to_cycle);
+  if(s->action_EXEC) {
+    EventAction_T a= s->action_EXEC;
+    printf(" %-20s = If program execution failed then %s else if recovered 
then %s\n",
+      "Execution failure", actionnames[a->failed->id], 
actionnames[a->passed->id]);
+  }
+
+  if(s->action_INVALID) {
+    EventAction_T a= s->action_INVALID;
+    printf(" %-20s = If invalid object type then %s else if recovered then 
%s\n",
+      "Invalid object", actionnames[a->failed->id], 
actionnames[a->passed->id]);
+  }
+
+  if(s->action_NONEXIST) {
+    EventAction_T a= s->action_NONEXIST;
+    printf(" %-20s = If does not exist then %s else if recovered then %s\n",
+      "Nonexistence", actionnames[a->failed->id], actionnames[a->passed->id]);
+  }
+
+  if(s->def_timeout && s->action_TIMEOUT) {
+    EventAction_T a= s->action_TIMEOUT;
+    printf(" %-20s = If %d restart within %d cycles then %s else if recovered 
then %s\n",
+      "Timeout", s->to_start, s->to_cycle, actionnames[a->failed->id], 
actionnames[a->passed->id]);
+  }
 
   for(r= s->maillist; r; r= r->next) {
     
@@ -850,32 +900,32 @@
       printf("All events");
 
     } else {
-      if(IS_EVENT_SET(r->events, EVENT_START))
-         printf("Start ");
-      if(IS_EVENT_SET(r->events, EVENT_STOP))
-         printf("Stop ");
-      if(IS_EVENT_SET(r->events, EVENT_RESTART))
-         printf("Restart ");
       if(IS_EVENT_SET(r->events, EVENT_CHECKSUM))
          printf("Checksum ");
+      if(IS_EVENT_SET(r->events, EVENT_CONNECTION))
+         printf("Connection ");
+      if(IS_EVENT_SET(r->events, EVENT_DATA))
+         printf("Data ");
+      if(IS_EVENT_SET(r->events, EVENT_EXEC))
+         printf("Exec ");
+      if(IS_EVENT_SET(r->events, EVENT_GID))
+         printf("Gid ");
+      if(IS_EVENT_SET(r->events, EVENT_INVALID))
+         printf("Invalid ");
+      if(IS_EVENT_SET(r->events, EVENT_NONEXIST))
+         printf("Nonexist ");
+      if(IS_EVENT_SET(r->events, EVENT_PERMISSION))
+         printf("Permission ");
       if(IS_EVENT_SET(r->events, EVENT_RESOURCE))
          printf("Resource ");
+      if(IS_EVENT_SET(r->events, EVENT_SIZE))
+         printf("Size ");
       if(IS_EVENT_SET(r->events, EVENT_TIMEOUT))
          printf("Timeout ");
       if(IS_EVENT_SET(r->events, EVENT_TIMESTAMP))
          printf("Timestamp ");
-      if(IS_EVENT_SET(r->events, EVENT_SIZE))
-         printf("Size ");
-      if(IS_EVENT_SET(r->events, EVENT_CONNECTION))
-         printf("Connection ");
-      if(IS_EVENT_SET(r->events, EVENT_PERMISSION))
-         printf("Permission ");
       if(IS_EVENT_SET(r->events, EVENT_UID))
          printf("Uid ");
-      if(IS_EVENT_SET(r->events, EVENT_GID))
-         printf("Gid ");
-      if(IS_EVENT_SET(r->events, EVENT_UNMONITOR))
-         printf("Unmonitor ");
     }
     
     printf("\n");
diff -Naur monit-cvs20040315/validate.c monit-cvs20040315-mp/validate.c
--- monit-cvs20040315/validate.c        2004-03-12 19:55:19.000000000 +0100
+++ monit-cvs20040315-mp/validate.c     2004-03-21 20:28:39.000000000 +0100
@@ -91,7 +91,6 @@
 
 
 static int  compare_value(int, int, int);
-static void reset_resource_counter(Service_T);
 
 static int  check_process_state(Service_T, char *);
 static int  check_process_resources(Service_T, Resource_T, char *);
@@ -101,21 +100,19 @@
 
 static int  check_device_resources(Service_T, Device_T, char *);
 
-static int  check_timestamps(Service_T);
+static void check_timestamps(Service_T);
 static int  check_timestamp_item(Service_T, Timestamp_T, char *);
 
 static int  check_size_item(Service_T, Size_T, unsigned long, char *);
 
-static int  check_timeout(Service_T);
 static int  check_skip(Service_T);
+static int  check_timeout(Service_T);
 
 static int  check_checksum(Service_T, char *);
 static int  check_perm(Service_T, mode_t, char *);
 static int  check_uid(Service_T, uid_t, char *);
 static int  check_gid(Service_T, gid_t, char *);
 
-static int  eval_actions(int, Service_T, char *, char *, int);
-
 
 /* ---------------------------------------------------------------- Public */
 
@@ -165,41 +162,32 @@
   Port_T pp= NULL;
   Resource_T pr= NULL;
   char report[STRLEN]={0};
-  
+
   /* Test for running process */
   if(!(pid= is_process_running(s))) {
     /* Reset the proc info object to prevent false data in the first run */
     memset(s->procinfo, 0, sizeof *(s->procinfo));
-    Event_post(s, EVENT_START, "Event: Process '%s' is not running.\n",
-              s->name);
+    Event_post(s, EVENT_NONEXIST, TRUE, s->action_NONEXIST, "Process '%s' is 
not running", s->name);
     return FALSE;
   } else {
-    DEBUG("'%s' is running with pid %d\n", s->name, (int)pid);
+    Event_post(s, EVENT_NONEXIST, FALSE, s->action_NONEXIST, "Process '%s' is 
running with pid %d", s->name, (int)pid);
   }
 
   if(Run.doprocess) {
     if(update_process_data(s, ptree, ptreesize, pid)) {
       if(! check_process_state(s, report)) {
-       Event_post(s, EVENT_RESOURCE, "Event: %s\n", report);
+        Event_post(s, EVENT_DATA, TRUE, s->action_DATA, report);
       } else {
-       DEBUG("'%s' check_process_state() passed.\n", s->name);
+        Event_post(s, EVENT_DATA, FALSE, s->action_DATA, "'%s' check process 
state passed", s->name);
       }
-      
+
       for(pr= s->resourcelist; pr; pr= pr->next) {
-       if(!check_process_resources(s, pr, report)) {
-         pr->cycle=0;
-         if(! pr->event_handled) {
-           pr->event_flag= TRUE;
-           pr->event_handled= TRUE;
-           if(! eval_actions(pr->action, s, report, "resource",
-                             EVENT_RESOURCE)) {
-             reset_resource_counter(s);
-             return FALSE;
-           }
-         }
-       } else {
-         pr->event_handled= FALSE;
-       }
+        if(!check_process_resources(s, pr, report)) {
+          pr->cycle=0;
+          Event_post(s, EVENT_RESOURCE, TRUE, pr->action, report);
+        } else {
+          Event_post(s, EVENT_RESOURCE, FALSE, pr->action, "'%s' resource 
passed", s->name);
+        }
       }
     } else {
       log("'%s' failed to get service data\n", s->name);
@@ -209,19 +197,12 @@
   /* Test each host:port and protocol in the service's portlist */
   for(pp= s->portlist; pp; pp= pp->next) {
     if(!check_process_connection(s, pp, report)) {
-      if(! pp->event_handled) {
-       pp->event_flag= TRUE;
-       pp->event_handled= TRUE;
-       if(! eval_actions(pp->action, s, report, "connection",
-                         EVENT_CONNECTION)) {
-         return FALSE;
-       }
-      }
+      Event_post(s, EVENT_CONNECTION, TRUE, pp->action, report);
     } else {
-      pp->event_handled= FALSE;
+      Event_post(s, EVENT_CONNECTION, FALSE, pp->action, "'%s' connection 
passed", s->name);
     }
   }
-  
+
   return TRUE;
   
 }
@@ -238,74 +219,53 @@
   char report[STRLEN]= {0};
 
   if(stat(s->path, &stat_buf) != 0) {
-    if(! s->event_handled) {
-      Event_post(s, EVENT_START, "Event: device '%s' doesn't exist\n", 
s->name);
-      s->event_handled= TRUE;
-    }
+    Event_post(s, EVENT_NONEXIST, TRUE, s->action_NONEXIST, "device '%s' 
doesn't exist", s->name);
     return FALSE;
   } else {
-    s->event_handled= FALSE;
+    Event_post(s, EVENT_NONEXIST, FALSE, s->action_NONEXIST, "device '%s' 
exist", s->name);
   }
-  
-  if(check_perm(s, stat_buf.st_mode, report)) {
-    s->perm->event_flag= TRUE;
-    if(! eval_actions(s->perm->action, s, report, "permission",
-                     EVENT_PERMISSION))
-      return FALSE;
+
+  if(s->perm) {
+    if(check_perm(s, stat_buf.st_mode, report)) {
+      Event_post(s, EVENT_PERMISSION, TRUE, s->perm->action, report);
+    } else {
+      Event_post(s, EVENT_PERMISSION, FALSE, s->perm->action, "'%s' permission 
passed", s->name);
+    }
   }
 
-  if(check_uid(s, stat_buf.st_uid, report)) {
-    s->uid->event_flag= TRUE;
-    if(! eval_actions(s->uid->action, s, report, "uid", EVENT_UID))
-      return FALSE;
+  if(s->uid) {
+    if(check_uid(s, stat_buf.st_uid, report)) {
+      Event_post(s, EVENT_UID, TRUE, s->uid->action, report);
+    } else {
+      Event_post(s, EVENT_UID, FALSE, s->uid->action, "'%s' uid passed", 
s->name);
+    }
   }
 
-  if(check_gid(s, stat_buf.st_gid, report)) {
-    s->gid->event_flag= TRUE;
-    if(! eval_actions(s->gid->action, s, report, "gid", EVENT_GID))
-      return FALSE;
+  if(s->gid) {
+    if(check_gid(s, stat_buf.st_gid, report)) {
+      Event_post(s, EVENT_GID, TRUE, s->gid->action, report);
+    } else {
+      Event_post(s, EVENT_GID, FALSE, s->gid->action, "'%s' gid passed", 
s->name);
+    }
   }
 
   if(!DeviceInfo_Usage(s->devinfo, s->path)) {
-    if(! s->devinfo->event_handled) {
-      Event_post(s, EVENT_START,
-                "Event: unable to read device '%s' state\n",
-                s->path);
-      s->devinfo->event_handled= TRUE;
-    }
+    Event_post(s, EVENT_DATA, TRUE, s->action_DATA, report);
     return FALSE;
   } else {
-    s->devinfo->event_handled= FALSE;
-    DEBUG("'%s' succeeded getting device statistic for %s\n", s->name, 
s->path);
+    Event_post(s, EVENT_DATA, FALSE, s->action_DATA, "'%s' succeeded getting 
device statistic for %s", s->name, s->path);
   }
 
-  /* Test devices */
   if(s->devicelist) {
     for(td= s->devicelist; td; td= td->next) {
       if(!check_device_resources(s, td, report)) {
-       if(! td->event_handled) {
-         td->event_flag= TRUE; /* Turn on the object's event_flag
-                                * to indicate that the resource
-                                * event occured on this particular
-                                * object */
-         td->event_handled= TRUE; /* Turn on the object's
-                                   * event_handled flag so this
-                                   * test is not handled in
-                                   * subsequent poll cycles until
-                                   * the error condition was reset.
-                                   */
-
-         if(! eval_actions(td->action, s, report, "device", EVENT_RESOURCE)) {
-           return FALSE;
-         }
-       }
+        Event_post(s, EVENT_RESOURCE, TRUE, td->action, report);
       } else {
-       td->event_handled= FALSE; 
+        Event_post(s, EVENT_RESOURCE, FALSE, td->action, "'%s' device 
resources passed", s->name);
       }
     }
-    
   }
-  
+
   return TRUE;
 
 }
@@ -322,75 +282,60 @@
   char report[STRLEN]= {0};
 
   if(stat(s->path, &stat_buf) != 0) {
-    if(! s->event_handled) {
-      Event_post(s, EVENT_START, "Event: file '%s' doesn't exist\n", s->name);
-      s->event_handled= TRUE;
-    }
+    Event_post(s, EVENT_NONEXIST, TRUE, s->action_NONEXIST, "file '%s' doesn't 
exist", s->name);
     return FALSE;
   } else {
-    s->event_handled= TRUE;
+    Event_post(s, EVENT_NONEXIST, FALSE, s->action_NONEXIST, "file '%s' 
exist", s->name);
   }
-  
+
   if(!S_ISREG(stat_buf.st_mode)) {
-    if(! s->event_handled) {
-      Event_post(s, EVENT_UNMONITOR,
-                "Event: '%s' is not regular file\n", s->name);
-      s->event_handled= TRUE;
-    }
+    Event_post(s, EVENT_INVALID, TRUE, s->action_INVALID, "'%s' is not regular 
file", s->name);
     return FALSE;
   } else {
-    s->event_handled= FALSE;
+    Event_post(s, EVENT_INVALID, FALSE, s->action_INVALID, "'%s' is regular 
file", s->name);
   }
-  
-  if(check_checksum(s, report)) {
-      s->checksum->event_flag= TRUE;
-      if(! eval_actions(s->checksum->action, s, report, "checksum",
-                       EVENT_CHECKSUM))
-         return FALSE;
+
+  if(s->checksum) {
+    if(check_checksum(s, report)) {
+      Event_post(s, EVENT_CHECKSUM, TRUE, s->checksum->action, report);
+    } else {
+      Event_post(s, EVENT_CHECKSUM, FALSE, s->checksum->action, "'%s' checksum 
passed", s->name);
+    }
   }
-  
-  if(check_perm(s, stat_buf.st_mode, report)) {
-    s->perm->event_flag= TRUE;
-    if(! eval_actions(s->perm->action, s, report, "permission",
-                     EVENT_PERMISSION))
-       return FALSE;
+
+  if(s->perm) {
+    if(check_perm(s, stat_buf.st_mode, report)) {
+      Event_post(s, EVENT_PERMISSION, TRUE, s->perm->action, report);
+    } else {
+      Event_post(s, EVENT_PERMISSION, FALSE, s->perm->action, "'%s' permission 
passed", s->name);
+    }
   }
 
-  if(check_uid(s, stat_buf.st_uid, report)) {
-    s->uid->event_flag= TRUE;
-    if(! eval_actions(s->uid->action, s, report, "uid", EVENT_UID))
-       return FALSE;
+  if(s->uid) {
+    if(check_uid(s, stat_buf.st_uid, report)) {
+      Event_post(s, EVENT_UID, TRUE, s->uid->action, report);
+    } else {
+      Event_post(s, EVENT_UID, FALSE, s->uid->action, "'%s' uid passed", 
s->name);
+    }
   }
-  
-  if(check_gid(s, stat_buf.st_gid, report)) {
-    s->gid->event_flag= TRUE;
-    if(! eval_actions(s->gid->action, s, report, "gid", EVENT_GID))
-       return FALSE;
+
+  if(s->gid) {
+    if(check_gid(s, stat_buf.st_gid, report)) {
+      Event_post(s, EVENT_GID, TRUE, s->gid->action, report);
+    } else {
+      Event_post(s, EVENT_GID, FALSE, s->gid->action, "'%s' gid passed", 
s->name);
+    }
   }
 
   for(sl= s->sizelist; sl; sl= sl->next) {
     if(!check_size_item(s, sl, (unsigned long)stat_buf.st_size, report)) {
-      if(! sl->event_handled) {
-       /* Turn on the object's event_flag to indicate that the size event
-        * occured on this particular object */
-       sl->event_flag= TRUE;
-       sl->event_handled= TRUE; /* Turn on the object's
-                                 * event_handled flag so this
-                                 * test is not handled in
-                                 * subsequent poll cycles until
-                                 * the error condition was reset.
-                                 */
-       if(! eval_actions(sl->action, s, report, "size", EVENT_SIZE)) {
-         return FALSE;
-       }
-      }
+      Event_post(s, EVENT_SIZE, TRUE, sl->action, report);
     } else {
-      sl->event_handled= FALSE;
+      Event_post(s, EVENT_SIZE, FALSE, sl->action, "'%s' size passed", 
s->name);
     }
   }
 
-  if(!check_timestamps(s))
-    return FALSE;
+  check_timestamps(s);
 
   return TRUE;
 
@@ -398,8 +343,8 @@
 
 
 /**
- * Validate a given directory service s.  Events are posted according to 
- * its configuration.  In case of a fatal event FALSE is returned.
+ * Validate a given directory service s. Events are posted according to
+ * its configuration. In case of a fatal event FALSE is returned.
  */
 int check_directory(Service_T s) {
 
@@ -407,48 +352,44 @@
   char report[STRLEN]= {0};
 
   if(stat(s->path, &stat_buf) != 0) {
-    if(! s->event_handled) {
-      Event_post(s, EVENT_START,
-                "Event: directory '%s' doesn't exist\n", s->name);
-      s->event_handled= TRUE;
-    }
+    Event_post(s, EVENT_NONEXIST, TRUE, s->action_NONEXIST, "directory '%s' 
doesn't exist", s->name);
     return FALSE;
   } else {
-    s->event_handled= FALSE;
+    Event_post(s, EVENT_NONEXIST, FALSE, s->action_NONEXIST, "directory '%s' 
exist", s->name);
   }
 
   if(!S_ISDIR(stat_buf.st_mode)) {
-    if(!s->event_handled) {
-      Event_post(s, EVENT_UNMONITOR,
-                "Event: '%s' is not directory\n", s->name);
-      s->event_handled= TRUE;
-    }
+    Event_post(s, EVENT_INVALID, TRUE, s->action_INVALID, "'%s' is not 
directory", s->name);
     return FALSE;
   } else {
-    s->event_handled= FALSE;
+    Event_post(s, EVENT_INVALID, FALSE, s->action_INVALID, "'%s' is 
directory", s->name);
   }
-  
-  if(check_perm(s, stat_buf.st_mode, report)) {
-    s->perm->event_flag= TRUE;
-    if(! eval_actions(s->perm->action, s, report, "permission",
-                     EVENT_PERMISSION))
-       return FALSE;
+
+  if(s->perm) {
+    if(check_perm(s, stat_buf.st_mode, report)) {
+      Event_post(s, EVENT_PERMISSION, TRUE, s->perm->action, report);
+    } else {
+      Event_post(s, EVENT_PERMISSION, FALSE, s->perm->action, "'%s' permission 
passed", s->name);
+    }
   }
-  
-  if(check_uid(s, stat_buf.st_uid, report)) {
-    s->uid->event_flag= TRUE;
-    if(! eval_actions(s->uid->action, s, report, "uid", EVENT_UID))
-       return FALSE;
-  } 
-  
-  if(check_gid(s, stat_buf.st_gid, report)) {
-    s->gid->event_flag= TRUE;
-    if(! eval_actions(s->gid->action, s, report, "gid", EVENT_GID))
-       return FALSE;
-  } 
 
-  if(!check_timestamps(s))
-    return FALSE;
+  if(s->uid) {
+    if(check_uid(s, stat_buf.st_uid, report)) {
+      Event_post(s, EVENT_UID, TRUE, s->uid->action, report);
+    } else {
+      Event_post(s, EVENT_UID, FALSE, s->uid->action, "'%s' uid passed", 
s->name);
+    }
+  }
+
+  if(s->gid) {
+    if(check_gid(s, stat_buf.st_gid, report)) {
+      Event_post(s, EVENT_GID, TRUE, s->gid->action, report);
+    } else {
+      Event_post(s, EVENT_GID, FALSE, s->gid->action, "'%s' gid passed", 
s->name);
+    }
+  }
+
+  check_timestamps(s);
 
   return TRUE;
 
@@ -471,17 +412,10 @@
     if(!check_icmp_connection(s, icmp, report)) {
       icmp->is_available= FALSE;
       last_ping= icmp;
-      if(! icmp->event_handled) {
-       icmp->event_handled= TRUE;
-       icmp->event_flag= TRUE;
-       if(! eval_actions(icmp->action, s, report, "icmp",
-                         EVENT_CONNECTION)) {
-         return FALSE;
-       }
-      }
+      Event_post(s, EVENT_CONNECTION, TRUE, icmp->action, report);
     } else {
       icmp->is_available= TRUE;
-      icmp->event_handled= FALSE;
+      Event_post(s, EVENT_CONNECTION, FALSE, icmp->action, "'%s' icmp passed", 
s->name);
     }
   }
 
@@ -489,26 +423,19 @@
    * continue to check any port connections  */
   if(last_ping && !last_ping->is_available) {
     DEBUG("'%s' icmp ping failed, skipping any port connection tests\n",
-         s->name);
+          s->name);
     return FALSE;
   }
-    
+
 
   /* Test each host:port and protocol in the service's portlist */
   for(p= s->portlist; p; p= p->next) {
     if(!check_process_connection(s, p, report)) {
       p->is_available= FALSE;
-      if(! p->event_handled) {
-       p->event_flag= TRUE; 
-       p->event_handled= TRUE;
-       if(! eval_actions(p->action, s, report, "connection",
-                         EVENT_CONNECTION)) {
-         return FALSE;
-       }
-      }
+      Event_post(s, EVENT_CONNECTION, TRUE, p->action, report);
     } else {
       p->is_available= TRUE;
-      p->event_handled= FALSE; 
+      Event_post(s, EVENT_CONNECTION, FALSE, p->action, "'%s' connection 
passed", s->name);
     }
   }
 
@@ -521,49 +448,6 @@
 
 
 /**
- * Evaluate actions of a service.  Post appropriate actions if necessary.
- * Return FALSE in case of a fatal event.
- */
-static int eval_actions(int action, Service_T s, char *report, char *check,
-                       int event) {
-
-  ASSERT(s);
-  ASSERT(check);
-  ASSERT(report);
-
-  switch(action) {
-  case ACTION_ALERT:
-      Event_post(s, event, "Event: %s\n", report);
-      break; /* continue */
-    
-  case ACTION_STOP:
-      Event_post(s, EVENT_STOP, "Event: %s\n", report);
-      return FALSE;
-      
-  case ACTION_RESTART:
-      Event_post(s, EVENT_RESTART, "Event: %s\n", report);
-      return FALSE;
-      
-  case ACTION_EXEC:
-      Event_post(s, event, "Event: %s\n", report);
-      break; /* continue */
-
-  case ACTION_UNMONITOR:
-      Event_post(s, event, "Event: %s\n", report);
-      Event_post(s, EVENT_UNMONITOR, "Event: %s\n", report);
-      return FALSE;
-
-  default:
-      log("'%s' error -- unknown failure action: [%s]\n", s->name, check);
-      break;
-    
-  }
-
-  return TRUE;
-}
-
-
-/**
  * Returns TRUE if the connection and protocol test succeeded
  * otherwise FALSE.
  */
@@ -577,9 +461,9 @@
   /* Open a socket to the destination INET[hostname:port] or UNIX[pathname] */
   socket= socket_create(p);
   if(!socket) {
-    snprintf(report, STRLEN, 
-            "'%s' failed, cannot open a connection to %s",
-            s->name, p->address);
+    snprintf(report, STRLEN,
+             "'%s' failed, cannot open a connection to %s",
+             s->name, p->address);
     rv= FALSE;
     goto error;
   } else {
@@ -588,28 +472,28 @@
 
   /* Verify that the socket is ready for i|o */
   if(! socket_is_ready(socket)) {
-    snprintf(report, STRLEN, 
-            "'%s' failed, the socket at %s is not ready for i|o -- %s",
-            s->name, p->address, STRERROR);
+    snprintf(report, STRLEN,
+             "'%s' failed, the socket at %s is not ready for i|o -- %s",
+             s->name, p->address, STRERROR);
     rv= FALSE;
     goto error;
   }
 
   /* Run the protocol verification routine through the socket */
   if(! p->protocol->check(socket)) {
-    snprintf(report, STRLEN, 
-            "'%s' failed protocol test [%s] at %s.",
-            s->name, p->protocol->name, p->address);
+    snprintf(report, STRLEN,
+             "'%s' failed protocol test [%s] at %s.",
+             s->name, p->protocol->name, p->address);
     rv= FALSE;
     goto error;
   } else {
     DEBUG("'%s' succeeded testing protocol [%s] at %s\n",
-         s->name, p->protocol->name, p->address);
+          s->name, p->protocol->name, p->address);
   }
 
   error:
   if(socket) socket_free(&socket);
-  
+
   return rv;
       
 }
@@ -645,40 +529,25 @@
   ProcInfo_T pi;
 
   ASSERT(s);
-  
+
   pi= s->procinfo;
-  
+
   if(pi->status_flag & PROCESS_ZOMBIE) {
-    snprintf(report, STRLEN, "process with pid %d is a zombie", pi->pid);
+    snprintf(report, STRLEN,
+             "process with pid %d is a zombie", pi->pid);
     /* We do not check the process anymore if it's a zombie
        since such a process is (usually) unmanageable */
     LOCK(Run.mutex)
-      s->do_monitor= FALSE;
+        s->do_monitor= FALSE;
     END_LOCK;
     return FALSE;
   } else {
     DEBUG("'%s' zombie check passed [status_flag=%04x]\n",
           s->name,  pi->status_flag);
   }
-  
-  return TRUE;
-
-}
 
+  return TRUE;
 
-/**
- * Resets the resource counter
- */
-static void reset_resource_counter(Service_T s) {
-  
-  Resource_T pr;
-
-  ASSERT(s);
-  
-  for(pr= s->resourcelist; pr; pr= pr->next) {
-    pr->cycle=0;
-  }
-  
 }
 
 
@@ -695,115 +564,115 @@
   ASSERT(pr);
 
   pi= s->procinfo;
-  
+
   switch(pr->resource_id) {
-  case RESOURCE_ID_CPU_PERCENT: 
+  case RESOURCE_ID_CPU_PERCENT:
       if(compare_value(pr->operator, pi->cpu_percent, pr->limit)) {
-       snprintf(report, STRLEN,
-        "cpu usage of %.1f%% matches resource limit [cpu usage%s%.1f%%]",
-                pi->cpu_percent/10.0, operatorshortnames[pr->operator],
-                pr->limit/10.0);
-       okay= FALSE;
+        snprintf(report, STRLEN,
+         "cpu usage of %.1f%% matches resource limit [cpu usage%s%.1f%%]",
+         pi->cpu_percent/10.0, operatorshortnames[pr->operator],
+         pr->limit/10.0);
+        okay= FALSE;
       } else {
-       DEBUG("'%s' cpu usage check passed [current cpu usage=%.1f%%]\n", 
-             s->name, pi->cpu_percent/10.0);
+        DEBUG("'%s' cpu usage check passed [current cpu usage=%.1f%%]\n",
+              s->name, pi->cpu_percent/10.0);
       }
       break;
 
   case RESOURCE_ID_MEM_PERCENT:
       if(compare_value(pr->operator, pi->mem_percent, pr->limit)) {
-       snprintf(report, STRLEN,
-          "mem usage of %.1f%% matches resource limit [mem usage%s%.1f%%]",
-                pi->mem_percent/10.0, operatorshortnames[pr->operator],
-                pr->limit/10.0);
-       okay= FALSE;
+        snprintf(report, STRLEN,
+           "mem usage of %.1f%% matches resource limit [mem usage%s%.1f%%]",
+                 pi->mem_percent/10.0, operatorshortnames[pr->operator],
+                 pr->limit/10.0);
+        okay= FALSE;
       } else {
-       DEBUG("'%s' mem usage check passed [current mem usage=%.1f%%]\n", 
-             s->name, pi->mem_percent/10.0);
+        DEBUG("'%s' mem usage check passed [current mem usage=%.1f%%]\n",
+              s->name, pi->mem_percent/10.0);
       }
       break;
-      
-  case RESOURCE_ID_MEM_KBYTE: 
+
+  case RESOURCE_ID_MEM_KBYTE:
     if(compare_value(pr->operator, pi->mem_kbyte, pr->limit)) {
       snprintf(report, STRLEN,
-          "mem amount of %ldkB matches resource limit [mem amount%s%ldkB]", 
-              pi->mem_kbyte, operatorshortnames[pr->operator],
-              pr->limit);      
+           "mem amount of %ldkB matches resource limit [mem amount%s%ldkB]",
+               pi->mem_kbyte, operatorshortnames[pr->operator],
+               pr->limit);
       okay= FALSE;
     } else {
-      DEBUG("'%s' mem amount check passed [current mem amount=%ldkB]\n", 
-           s->name, pi->mem_kbyte);
+      DEBUG("'%s' mem amount check passed [current mem amount=%ldkB]\n",
+            s->name, pi->mem_kbyte);
     }
     break;
 
-  case RESOURCE_ID_LOAD1: 
+  case RESOURCE_ID_LOAD1:
     if(compare_value(pr->operator, (int)(Run.loadavg[0]*10.0), pr->limit)) {
       snprintf(report, STRLEN,
-              "loadavg(1min) of %.1f matches resource limit "
-              "[loadavg(1min)%s%.1f]", 
-              Run.loadavg[0], operatorshortnames[pr->operator],
-              pr->limit/10.0);      
+               "loadavg(1min) of %.1f matches resource limit "
+               "[loadavg(1min)%s%.1f]",
+               Run.loadavg[0], operatorshortnames[pr->operator],
+               pr->limit/10.0);
       okay= FALSE;
     } else {
-      DEBUG("'%s' loadavg(1min) check passed [current loadavg(1min)=%.1f]\n", 
-           s->name, Run.loadavg[0]);
+      DEBUG("'%s' loadavg(1min) check passed [current loadavg(1min)=%.1f]\n",
+            s->name, Run.loadavg[0]);
     }
     break;
 
-  case RESOURCE_ID_LOAD5: 
+  case RESOURCE_ID_LOAD5:
     if(compare_value(pr->operator, (int)(Run.loadavg[1]*10.0), pr->limit)) {
       snprintf(report, STRLEN,
-              "loadavg(5min) of %.1f matches resource limit "
-              "[loadavg(5min)%s%.1f]", 
-              Run.loadavg[1], operatorshortnames[pr->operator],
-              pr->limit/10.0);      
+               "loadavg(5min) of %.1f matches resource limit "
+               "[loadavg(5min)%s%.1f]",
+               Run.loadavg[1], operatorshortnames[pr->operator],
+               pr->limit/10.0);
       okay= FALSE;
     } else {
-      DEBUG("'%s' loadavg(5min) check passed [current loadavg(5min)=%.1f]\n", 
-           s->name, Run.loadavg[1]);
+      DEBUG("'%s' loadavg(5min) check passed [current loadavg(5min)=%.1f]\n",
+            s->name, Run.loadavg[1]);
     }
     break;
-    
-  case RESOURCE_ID_LOAD15: 
+
+  case RESOURCE_ID_LOAD15:
       if(compare_value(pr->operator, (int)(Run.loadavg[2]*10.0), pr->limit)) {
-       snprintf(report, STRLEN,
-                "loadavg(15min) of %.1f matches resource limit "
-                "[loadavg(15min)%s%.1f]", 
-                Run.loadavg[2], operatorshortnames[pr->operator],
-                pr->limit/10.0);      
-       okay= FALSE;
+        snprintf(report, STRLEN,
+                 "loadavg(15min) of %.1f matches resource limit "
+                 "[loadavg(15min)%s%.1f]",
+                 Run.loadavg[2], operatorshortnames[pr->operator],
+                 pr->limit/10.0);
+        okay= FALSE;
       } else {
-       DEBUG("'%s' loadavg(15min) check passed "
-             "[current loadavg(15min)=%.1f]\n", 
-             s->name, Run.loadavg[2]);
+        DEBUG("'%s' loadavg(15min) check passed "
+              "[current loadavg(15min)=%.1f]\n",
+              s->name, Run.loadavg[2]);
       }
       break;
-      
+
   case RESOURCE_ID_CHILDREN:
       if(compare_value(pr->operator, pi->children, pr->limit)) {
-       snprintf(report, STRLEN,
-                "children of %i matches resource limit [children%s%ld]",
-                pi->children, operatorshortnames[pr->operator],
-                pr->limit);
-       okay= FALSE;
+        snprintf(report, STRLEN,
+                 "children of %i matches resource limit [children%s%ld]",
+                 pi->children, operatorshortnames[pr->operator],
+                 pr->limit);
+        okay= FALSE;
       } else {
-       DEBUG("'%s' children check passed [current children=%i]\n",
-             s->name, pi->children);
+        DEBUG("'%s' children check passed [current children=%i]\n",
+              s->name, pi->children);
       }
     break;
 
   case RESOURCE_ID_TOTAL_MEM_KBYTE:
     if(compare_value(pr->operator, pi->total_mem_kbyte, pr->limit)) {
       snprintf(report, STRLEN,
-              "total mem amount of %ldkB matches resource limit"
-              " [total mem amount%s%ldkB]",
-              pi->total_mem_kbyte, operatorshortnames[pr->operator],
-              pr->limit);
+               "total mem amount of %ldkB matches resource limit"
+               " [total mem amount%s%ldkB]",
+               pi->total_mem_kbyte, operatorshortnames[pr->operator],
+               pr->limit);
       okay= FALSE;
     } else {
       DEBUG("'%s' total mem amount check passed "
-           "[current total mem amount=%ldkB]\n",
-           s->name, pi->total_mem_kbyte);
+            "[current total mem amount=%ldkB]\n",
+            s->name, pi->total_mem_kbyte);
     }
     break;
 
@@ -820,40 +689,38 @@
   if(pr->cycle >= pr->max_cycle) {
     return FALSE;
   }
-    
+
   return TRUE;
   
 }
 
 
 /**
- * Returns TRUE if the service timed out, otherwise FALSE. 
+ * Returns TRUE if the service timed out, otherwise FALSE.
  */
 static int check_timeout(Service_T s) {
 
   ASSERT(s);
-  
-  if(!s->def_timeout) {
+
+  if(!s->def_timeout)
     return FALSE;
-  }
-  
+
   /*
    * Start counting cycles
    */
-  if(s->nstart > 0) {
+  if(s->nstart > 0)
     s->ncycle++;
-  }
-  
+
   /*
    * Check timeout
    */
   if(s->nstart >= s->to_start && s->ncycle <= s->to_cycle) {
-    Event_post(s, EVENT_TIMEOUT,
-              "Service '%s' timed out and will not be checked anymore.\n",
-              s->name);
+    Event_post(s, EVENT_TIMEOUT, TRUE, s->action_TIMEOUT,
+              "service '%s' timed out and will not be checked anymore\n",
+              s->name);
     return TRUE;
   }
-  
+
   /*
    * Stop counting and reset if the
    * cycle interval is passed
@@ -864,7 +731,7 @@
   }
 
   return FALSE;
-  
+
 }
 
 
@@ -892,23 +759,15 @@
 
 /**
  * Returns TRUE if the checksum was changed for associated
- * path and send an alert warning.
- * Returns FALSE if the checksum is the same or not defined
- * for this service.
+ * path
+ * Returns FALSE if the checksum is the same
  */
 static int check_checksum(Service_T s, char *report) {
 
-  ASSERT(s);
-
-  if(!s->checksum)
-    return FALSE;
+  ASSERT(s && s->checksum);
 
   if( !check_hash(s->path, s->checksum->hash, s->checksum->type) ) {
     snprintf(report, STRLEN, "checksum test failed for %s", s->path);
-    /* Set the old checksum to the new value so we do not report this
-     * more than once per change */
-    FREE(s->checksum->hash);
-    s->checksum->hash= get_checksum(s->path, s->checksum->type);
     return TRUE;
   }
   
@@ -921,16 +780,12 @@
 
 /**
  * Returns TRUE if the permission was changed for associated
- * path and send an alert warning.
- * Returns FALSE if the permission is the same or not defined
- * for this service.
+ * path
+ * Returns FALSE if the permission is the same
  */
 static int check_perm(Service_T s, mode_t mode, char *report) {
 
-  ASSERT(s);
-
-  if(!s->perm)
-    return FALSE;
+  ASSERT(s && s->perm);
 
   if((mode & 07777) != s->perm->perm) {
     snprintf(report, STRLEN,
@@ -950,16 +805,12 @@
 
 /**
  * Returns TRUE if the uid was changed for associated
- * path and send an alert warning.
- * Returns FALSE if the uid is the same or not defined
- * for this service.
+ * path
+ * Returns FALSE if the uid is the same
  */
 static int check_uid(Service_T s, uid_t uid, char *report) {
 
-  ASSERT(s);
-
-  if(!s->uid)
-    return FALSE;
+  ASSERT(s && s->uid);
 
   if( uid != s->uid->uid ) {
     snprintf(report, STRLEN,
@@ -979,16 +830,12 @@
 
 /**
  * Returns TRUE if the gid was changed for associated
- * path and send an alert warning.
- * Returns FALSE if the gid is the same or not defined
- * for this service.
+ * path
+ * Returns FALSE if the gid is the same
  */
 static int check_gid(Service_T s, gid_t gid, char *report) {
 
-  ASSERT(s);
-
-  if(!s->gid)
-    return FALSE;
+  ASSERT(s && s->gid);
 
   if( gid != s->gid->gid ) {
     snprintf(report, STRLEN,
@@ -1010,25 +857,19 @@
  * Validate all given timestamps of a service s.  Events are posted according 
  * to its configuration.  In case of a fatal event FALSE is returned.
  */
-static int check_timestamps(Service_T s) {
+static void check_timestamps(Service_T s) {
 
   Timestamp_T tl;
   char report[STRLEN];
   
   for(tl= s->timestamplist; tl; tl= tl->next) {
     if(!check_timestamp_item(s, tl, report)) {
-      tl->event_flag= TRUE; /* Turn on the object's event_flag to
-                            * indicate that the timestamp event
-                            * occured on this particular object */
-      if(! eval_actions(tl->action, s, report, "timestamp",
-                       EVENT_TIMESTAMP)) {
-       return FALSE;
-      }
+      Event_post(s, EVENT_TIMESTAMP, TRUE, tl->action, report);
+    } else {
+      Event_post(s, EVENT_TIMESTAMP, FALSE, tl->action, "'%s' timestamp 
passed", s->name);
     }
   }
   
-  return TRUE;
-
 }
 
 
@@ -1056,20 +897,12 @@
   if(t->test_changes) {
     if(t->timestamp != timestamp) {
       snprintf(report, STRLEN, "timestamp was changed for %s", s->path);
-      /* Set the old timestamp to the new value so we do not report
-       * this more than once per change */
-      t->timestamp= timestamp;
       return FALSE;
     }
   } else {
     if(compare_value(t->operator, (int)(now - timestamp), t->time)) {
-      if(! t->event_handled) {
-       snprintf(report, STRLEN, "timestamp test failed for %s", s->path);
-       t->event_handled= TRUE;
-       return FALSE;
-      }
-    } else {
-      t->event_handled= FALSE;
+      snprintf(report, STRLEN, "timestamp test failed for %s", s->path);
+      return FALSE;
     }
   }
 
@@ -1092,9 +925,6 @@
   if(sl->test_changes) {
     if(sl->runsize != size) {
       snprintf(report, STRLEN, "size was changed for %s", s->path);
-      /* Set the old size to the new value so we do not report
-       * this more than once per change */
-      sl->runsize= size;
       return FALSE;
     }
   } else {
@@ -1142,11 +972,10 @@
               100 * (t->devinfo->f_files - t->devinfo->f_filesfree) /
               t->devinfo->f_files, td->limit_percent)) {
          snprintf(report, STRLEN,
-          "inode usage %ld%% matches resource limit [inode usage%s%d%%]",
-                  100 * (t->devinfo->f_files - t->devinfo->f_filesfree) /
-                  t->devinfo->f_files,
-                  operatorshortnames[td->operator],
-                  td->limit_percent);
+            "inode usage %ld%% matches resource limit [inode usage%s%d%%]",
+           100 * (t->devinfo->f_files - t->devinfo->f_filesfree) /
+           t->devinfo->f_files, operatorshortnames[td->operator],
+           td->limit_percent);
          return FALSE;
        }
       } else {
@@ -1185,10 +1014,10 @@
         if(compare_value(td->operator, t->devinfo->f_blocks -
                         t->devinfo->f_blocksfreetotal, td->limit_absolute)) {
          snprintf(report, STRLEN,
-      "space usage %ld blocks matches resource limit [space usage%s%ld 
blocks]",
-                  t->devinfo->f_blocks - t->devinfo->f_blocksfreetotal,
-                  operatorshortnames[td->operator],
-                  td->limit_absolute);
+            "space usage %ld blocks matches resource limit [space usage%s%ld 
blocks]",
+           t->devinfo->f_blocks - t->devinfo->f_blocksfreetotal,
+           operatorshortnames[td->operator],
+           td->limit_absolute);
          return FALSE;
         }
       }

reply via email to

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