|  | /* | 
|  | * Copyright (C) Tildeslash Ltd. All rights reserved. | 
|  | * | 
|  | * This program is free software: you can redistribute it and/or modify | 
|  | * it under the terms of the GNU Affero General Public License version 3. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Affero General Public License | 
|  | * along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
|  | * | 
|  | * In addition, as a special exception, the copyright holders give | 
|  | * permission to link the code of portions of this program with the | 
|  | * OpenSSL library under certain conditions as described in each | 
|  | * individual source file, and distribute linked combinations | 
|  | * including the two. | 
|  | * | 
|  | * You must obey the GNU Affero General Public License in all respects | 
|  | * for all of the code used other than OpenSSL. | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #ifdef HAVE_STRING_H | 
|  | #include <string.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_STRINGS_H | 
|  | #include <strings.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_SYS_TYPES_H | 
|  | #include <sys/types.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_SYS_TIME_H | 
|  | #include <sys/time.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_TIME_H | 
|  | #include <time.h> | 
|  | #endif | 
|  |  | 
|  |  | 
|  | #ifdef HAVE_SYS_STAT_H | 
|  | #include <sys/stat.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_UNISTD_H | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_DIRENT_H | 
|  | #include <dirent.h> | 
|  | #endif | 
|  |  | 
|  | #include "monit.h" | 
|  | #include "alert.h" | 
|  | #include "event.h" | 
|  | #include "process.h" | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Implementation of the event interface. | 
|  | * | 
|  | * @file | 
|  | */ | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------- Definitions */ | 
|  |  | 
|  | EventTable_T Event_Table[]= { | 
|  | {Event_Action,     "Action done",             "Action done",                "Action done",              "Action done"}, | 
|  | {Event_Checksum,   "Checksum failed",         "Checksum succeeded",         "Checksum changed",         "Checksum not changed"}, | 
|  | {Event_Connection, "Connection failed",       "Connection succeeded",       "Connection changed",       "Connection not changed"}, | 
|  | {Event_Content,    "Content failed",          "Content succeeded",          "Content match",            "Content doesn't match"}, | 
|  | {Event_Data,       "Data access error",       "Data access succeeded",      "Data access changed",      "Data access not changed"}, | 
|  | {Event_Exec,       "Execution failed",        "Execution succeeded",        "Execution changed",        "Execution not changed"}, | 
|  | {Event_Fsflag,     "Filesystem flags failed", "Filesystem flags succeeded", "Filesystem flags changed", "Filesystem flags not changed"}, | 
|  | {Event_Gid,        "GID failed",              "GID succeeded",              "GID changed",              "GID not changed"}, | 
|  | {Event_Heartbeat,  "Heartbeat failed",        "Heartbeat succeeded",        "Heartbeat changed",        "Heartbeat not changed"}, | 
|  | {Event_Icmp,       "ICMP failed",             "ICMP succeeded",             "ICMP changed",             "ICMP not changed"}, | 
|  | {Event_Instance,   "Monit instance failed",   "Monit instance succeeded",   "Monit instance changed",   "Monit instance not changed"}, | 
|  | {Event_Invalid,    "Invalid type",            "Type succeeded",             "Type changed",             "Type not changed"}, | 
|  | {Event_Nonexist,   "Does not exist",          "Exists",                     "Existence changed",        "Existence not changed"}, | 
|  | {Event_Permission, "Permission failed",       "Permission succeeded",       "Permission changed",       "Permission not changed"}, | 
|  | {Event_Pid,        "PID failed",              "PID succeeded",              "PID changed",              "PID not changed"}, | 
|  | {Event_PPid,       "PPID failed",             "PPID succeeded",             "PPID changed",             "PPID not changed"}, | 
|  | {Event_Resource,   "Resource limit matched",  "Resource limit succeeded",   "Resource limit changed",   "Resource limit not changed"}, | 
|  | {Event_Size,       "Size failed",             "Size succeeded",             "Size changed",             "Size not changed"}, | 
|  | {Event_Status,     "Status failed",           "Status succeeded",           "Status changed",           "Status not changed"}, | 
|  | {Event_Timeout,    "Timeout",                 "Timeout recovery",           "Timeout changed",          "Timeout not changed"}, | 
|  | {Event_Timestamp,  "Timestamp failed",        "Timestamp succeeded",        "Timestamp changed",        "Timestamp not changed"}, | 
|  | {Event_Uid,        "UID failed",              "UID succeeded",              "UID changed",              "UID not changed"}, | 
|  | {Event_Uptime,     "Uptime failed",           "Uptime succeeded",           "Uptime changed",           "Uptime not changed"}, | 
|  | /* Virtual events */ | 
|  | {Event_Null,       "No Event",                "No Event",                   "No Event",                 "No Event"} | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* -------------------------------------------------------------- Prototypes */ | 
|  |  | 
|  |  | 
|  | static void handle_event(Event_T); | 
|  | static void handle_action(Event_T, Action_T); | 
|  | static void Event_queue_add(Event_T); | 
|  | static void Event_queue_update(Event_T, const char *); | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------ Public */ | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Post a new Event | 
|  | * @param service The Service the event belongs to | 
|  | * @param id The event identification | 
|  | * @param state The event state | 
|  | * @param action Description of the event action | 
|  | * @param s Optional message describing the event | 
|  | */ | 
|  | void Event_post(Service_T service, long id, short state, EventAction_T action, char *s, ...) { | 
|  | Event_T e; | 
|  |  | 
|  | ASSERT(service); | 
|  | ASSERT(action); | 
|  | ASSERT(state == STATE_FAILED || state == STATE_SUCCEEDED || state == STATE_CHANGED || state == STATE_CHANGEDNOT); | 
|  |  | 
|  | if ((e = service->eventlist) == NULL) { | 
|  | /* Only first failed/changed event can initialize the queue for given event type, | 
|  | * thus succeeded events are ignored until first error. */ | 
|  | if (state == STATE_SUCCEEDED || state == STATE_CHANGEDNOT) | 
|  | return; | 
|  |  | 
|  | /* Initialize event list and add first event. The manadatory informations | 
|  | * are cloned so the event is as standalone as possible and may be saved | 
|  | * to the queue without the dependency on the original service, thus | 
|  | * persistent and managable across monit restarts */ | 
|  | NEW(e); | 
|  | e->id = id; | 
|  | gettimeofday(&e->collected, NULL); | 
|  | e->source = Str_dup(service->name); | 
|  | e->mode = service->mode; | 
|  | e->type = service->type; | 
|  | e->state = STATE_INIT; | 
|  | e->state_map = 1; | 
|  | e->action = action; | 
|  | if (s) { | 
|  | va_list ap; | 
|  | va_start(ap, s); | 
|  | e->message = Str_vcat(s, ap); | 
|  | va_end(ap); | 
|  | } | 
|  | service->eventlist = 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) { | 
|  | gettimeofday(&e->collected, NULL); | 
|  |  | 
|  | /* Shift the existing event flags to the left | 
|  | * and set the first bit based on actual state */ | 
|  | e->state_map <<= 1; | 
|  | e->state_map |= ((state == STATE_SUCCEEDED || state == STATE_CHANGEDNOT) ? 0 : 1); | 
|  |  | 
|  | /* Update the message */ | 
|  | if (s) { | 
|  | va_list ap; | 
|  | FREE(e->message); | 
|  | va_start(ap, s); | 
|  | e->message = Str_vcat(s, ap); | 
|  | va_end(ap); | 
|  | } | 
|  | break; | 
|  | } | 
|  | e = e->next; | 
|  | } while (e); | 
|  |  | 
|  | if (!e) { | 
|  | /* Only first failed/changed event can initialize the queue for given event type, | 
|  | * thus succeeded events are ignored until first error. */ | 
|  | if (state == STATE_SUCCEEDED || state == STATE_CHANGEDNOT) | 
|  | return; | 
|  |  | 
|  | /* Event was not found in the pending events list, we will add it. | 
|  | * The manadatory informations are cloned so the event is as standalone | 
|  | * as possible and may be saved to the queue without the dependency on | 
|  | * the original service, thus persistent and managable across monit | 
|  | * restarts */ | 
|  | NEW(e); | 
|  | e->id = id; | 
|  | gettimeofday(&e->collected, NULL); | 
|  | e->source = Str_dup(service->name); | 
|  | e->mode = service->mode; | 
|  | e->type = service->type; | 
|  | e->state = STATE_INIT; | 
|  | e->state_map = 1; | 
|  | e->action = action; | 
|  | if (s) { | 
|  | va_list ap; | 
|  | va_start(ap, s); | 
|  | e->message = Str_vcat(s, ap); | 
|  | va_end(ap); | 
|  | } | 
|  | e->next = service->eventlist; | 
|  | service->eventlist = e; | 
|  | } | 
|  | } | 
|  |  | 
|  | e->state_changed = Event_check_state(e, state); | 
|  |  | 
|  | /* In the case that the state changed, update it and reset the counter */ | 
|  | if (e->state_changed) { | 
|  | e->state = state; | 
|  | e->count = 1; | 
|  | } else | 
|  | e->count++; | 
|  |  | 
|  | handle_event(e); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* -------------------------------------------------------------- Properties */ | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get the Service where the event orginated | 
|  | * @param E An event object | 
|  | * @return The Service where the event orginated | 
|  | */ | 
|  | Service_T Event_get_source(Event_T E) { | 
|  | Service_T s = NULL; | 
|  |  | 
|  | ASSERT(E); | 
|  |  | 
|  | if (!(s = Util_getService(E->source))) | 
|  | LogError("Service %s not found in monit configuration\n", E->source); | 
|  |  | 
|  | return s; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get the Service name where the event orginated | 
|  | * @param E An event object | 
|  | * @return The Service name where the event orginated | 
|  | */ | 
|  | char *Event_get_source_name(Event_T E) { | 
|  | ASSERT(E); | 
|  | return (E->source); | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get the service type of the service where the event orginated | 
|  | * @param E An event object | 
|  | * @return The service type of the service where the event orginated | 
|  | */ | 
|  | int Event_get_source_type(Event_T E) { | 
|  | ASSERT(E); | 
|  | return (E->type); | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get the Event timestamp | 
|  | * @param E An event object | 
|  | * @return The Event timestamp | 
|  | */ | 
|  | struct timeval *Event_get_collected(Event_T E) { | 
|  | ASSERT(E); | 
|  | return &E->collected; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get the Event raw state | 
|  | * @param E An event object | 
|  | * @return The Event raw state | 
|  | */ | 
|  | short Event_get_state(Event_T E) { | 
|  | ASSERT(E); | 
|  | return E->state; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Return the actual event state based on event state bitmap | 
|  | * and event ratio needed to trigger the state change | 
|  | * @param E An event object | 
|  | * @param S Actual posted state | 
|  | * @return The Event raw state | 
|  | */ | 
|  | short Event_check_state(Event_T E, short S) { | 
|  | int       i; | 
|  | int       count = 0; | 
|  | short     state = (S == STATE_SUCCEEDED || S == STATE_CHANGEDNOT) ? 0 : 1; /* translate to 0/1 class */ | 
|  | Action_T  action; | 
|  | Service_T service; | 
|  | long long flag; | 
|  |  | 
|  | ASSERT(E); | 
|  |  | 
|  | if (!(service = Event_get_source(E))) | 
|  | return TRUE; | 
|  |  | 
|  | /* Only true failed/changed state condition can change the initial state */ | 
|  | if (!state && E->state == STATE_INIT && !(service->error & E->id)) | 
|  | return FALSE; | 
|  |  | 
|  | action = !state ? E->action->succeeded : E->action->failed; | 
|  |  | 
|  | /* Compare as many bits as cycles able to trigger the action */ | 
|  | for (i = 0; i < action->cycles; i++) { | 
|  | /* Check the state of the particular cycle given by the bit position */ | 
|  | flag = (E->state_map >> i) & 0x1; | 
|  |  | 
|  | /* Count occurences of the posted state */ | 
|  | if (flag == state) | 
|  | count++; | 
|  | } | 
|  |  | 
|  | /* the internal instance and action events are handled as changed any time since we need to deliver alert whenever it occurs */ | 
|  | if (E->id == Event_Instance || E->id == Event_Action || (count >= action->count && (S != E->state || S == STATE_CHANGED))) | 
|  | return TRUE; | 
|  |  | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get the Event type | 
|  | * @param E An event object | 
|  | * @return The Event type | 
|  | */ | 
|  | long Event_get_id(Event_T E) { | 
|  | ASSERT(E); | 
|  | return E->id; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get the optionally Event message describing why the event was | 
|  | * fired. | 
|  | * @param E An event object | 
|  | * @return The Event message. May be NULL | 
|  | */ | 
|  | const char *Event_get_message(Event_T E) { | 
|  | ASSERT(E); | 
|  | return E->message; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get a textual description of actual event type. | 
|  | * @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) { | 
|  | EventTable_T *et= Event_Table; | 
|  |  | 
|  | ASSERT(E); | 
|  |  | 
|  | while ((*et).id) { | 
|  | if (E->id == (*et).id) { | 
|  | switch (E->state) { | 
|  | case STATE_SUCCEEDED: | 
|  | return (*et).description_succeeded; | 
|  | case STATE_FAILED: | 
|  | return (*et).description_failed; | 
|  | case STATE_INIT: | 
|  | return (*et).description_failed; | 
|  | case STATE_CHANGED: | 
|  | return (*et).description_changed; | 
|  | case STATE_CHANGEDNOT: | 
|  | return (*et).description_changednot; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | et++; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Get an event action id. | 
|  | * @param E An event object | 
|  | * @return An action id | 
|  | */ | 
|  | short Event_get_action(Event_T E) { | 
|  | Action_T A = NULL; | 
|  |  | 
|  | ASSERT(E); | 
|  |  | 
|  | switch (E->state) { | 
|  | case STATE_SUCCEEDED: | 
|  | case STATE_CHANGEDNOT: | 
|  | A = E->action->succeeded; | 
|  | break; | 
|  | case STATE_FAILED: | 
|  | case STATE_CHANGED: | 
|  | case STATE_INIT: | 
|  | A = E->action->failed; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (! A) | 
|  | return ACTION_IGNORE; | 
|  |  | 
|  | /* 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 */ | 
|  | return (E->mode == MODE_PASSIVE && ((A->id == ACTION_START) || (A->id == ACTION_STOP) || (A->id == ACTION_RESTART))) ? ACTION_ALERT : A->id; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * 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_Checksumthe 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) { | 
|  | ASSERT(E); | 
|  | return actionnames[Event_get_action(E)]; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Reprocess the partially handled event queue | 
|  | */ | 
|  | void Event_queue_process() { | 
|  | DIR           *dir = NULL; | 
|  | FILE          *file = NULL; | 
|  | struct dirent *de = NULL; | 
|  | EventAction_T  ea = NULL; | 
|  | Action_T       a = NULL; | 
|  |  | 
|  | /* return in the case that the eventqueue is not enabled or empty */ | 
|  | if (! Run.eventlist_dir || (! Run.handler_init && ! Run.handler_queue[HANDLER_ALERT] && ! Run.handler_queue[HANDLER_MMONIT])) | 
|  | return; | 
|  |  | 
|  | if (! (dir = opendir(Run.eventlist_dir)) ) { | 
|  | if (errno != ENOENT) | 
|  | LogError("%s: cannot open the directory %s -- %s\n", prog, Run.eventlist_dir, STRERROR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if ((de = readdir(dir))) | 
|  | DEBUG("Processing postponed events queue\n"); | 
|  |  | 
|  | NEW(ea); | 
|  | NEW(a); | 
|  |  | 
|  | while (de) { | 
|  | size_t         size; | 
|  | int            handlers_passed = 0; | 
|  | int           *version = NULL; | 
|  | short         *action = NULL; | 
|  | Event_T        e = NULL; | 
|  | struct stat    st; | 
|  | char           file_name[STRLEN]; | 
|  |  | 
|  | /* In the case that all handlers failed, skip the further processing in | 
|  | * this cycle. Alert handler is currently defined anytime (either | 
|  | * explicitly or localhost by default) */ | 
|  | if ( (Run.mmonits && FLAG(Run.handler_flag, HANDLER_MMONIT) && FLAG(Run.handler_flag, HANDLER_ALERT)) || FLAG(Run.handler_flag, HANDLER_ALERT)) | 
|  | break; | 
|  |  | 
|  | snprintf(file_name, STRLEN, "%s/%s", Run.eventlist_dir, de->d_name); | 
|  |  | 
|  | if (!stat(file_name, &st) && S_ISREG(st.st_mode)) { | 
|  | DEBUG("%s: processing queued event %s\n", prog, file_name); | 
|  |  | 
|  | if (! (file = fopen(file_name, "r")) ) { | 
|  | LogError("%s: queued event processing failed - cannot open the file %s -- %s\n", prog, file_name, STRERROR); | 
|  | goto error1; | 
|  | } | 
|  |  | 
|  | /* read event structure version */ | 
|  | if (!(version = file_readQueue(file, &size))) { | 
|  | LogError("skipping queued event %s - unknown data format\n", file_name); | 
|  | goto error2; | 
|  | } | 
|  | if (size != sizeof(int)) { | 
|  | LogError("Aborting queued event %s - invalid size %d\n", file_name, size); | 
|  | goto error3; | 
|  | } | 
|  | if (*version != EVENT_VERSION) { | 
|  | LogError("Aborting queued event %s - incompatible data format version %d\n", file_name, *version); | 
|  | goto error3; | 
|  | } | 
|  |  | 
|  | /* read event structure */ | 
|  | if (!(e = file_readQueue(file, &size))) | 
|  | goto error3; | 
|  | if (size != sizeof(*e)) | 
|  | goto error4; | 
|  |  | 
|  | /* read source */ | 
|  | if (!(e->source = file_readQueue(file, &size))) | 
|  | goto error4; | 
|  |  | 
|  | /* read message */ | 
|  | if (!(e->message = file_readQueue(file, &size))) | 
|  | goto error5; | 
|  |  | 
|  | /* read event action */ | 
|  | if (!(action = file_readQueue(file, &size))) | 
|  | goto error6; | 
|  | if (size != sizeof(short)) | 
|  | goto error7; | 
|  | a->id = *action; | 
|  | if (e->state == STATE_FAILED) | 
|  | ea->failed = a; | 
|  | else | 
|  | ea->succeeded = a; | 
|  | e->action = ea; | 
|  |  | 
|  | /* Retry all remaining handlers */ | 
|  |  | 
|  | /* alert */ | 
|  | if (e->flag & HANDLER_ALERT) { | 
|  | if (Run.handler_init) | 
|  | Run.handler_queue[HANDLER_ALERT]++; | 
|  | if ((Run.handler_flag & HANDLER_ALERT) != HANDLER_ALERT) { | 
|  | if ( handle_alert(e) != HANDLER_ALERT ) { | 
|  | e->flag &= ~HANDLER_ALERT; | 
|  | Run.handler_queue[HANDLER_ALERT]--; | 
|  | handlers_passed++; | 
|  | } else { | 
|  | LogError("Alert handler failed, retry scheduled for next cycle\n"); | 
|  | Run.handler_flag |= HANDLER_ALERT; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* mmonit */ | 
|  | if (e->flag & HANDLER_MMONIT) { | 
|  | if (Run.handler_init) | 
|  | Run.handler_queue[HANDLER_MMONIT]++; | 
|  | if ((Run.handler_flag & HANDLER_MMONIT) != HANDLER_MMONIT) { | 
|  | if ( handle_mmonit(e) != HANDLER_MMONIT ) { | 
|  | e->flag &= ~HANDLER_MMONIT; | 
|  | Run.handler_queue[HANDLER_MMONIT]--; | 
|  | handlers_passed++; | 
|  | } else { | 
|  | LogError("M/Monit handler failed, retry scheduled for next cycle\n"); | 
|  | Run.handler_flag |= HANDLER_MMONIT; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If no error persists, remove it from the queue */ | 
|  | if (e->flag == HANDLER_SUCCEEDED) { | 
|  | DEBUG("Removing queued event %s\n", file_name); | 
|  | if (unlink(file_name) < 0) | 
|  | LogError("Failed to remove queued event file '%s' -- %s\n", file_name, STRERROR); | 
|  | } else if (handlers_passed > 0) { | 
|  | DEBUG("Updating queued event %s (some handlers passed)\n", file_name); | 
|  | Event_queue_update(e, file_name); | 
|  | } | 
|  |  | 
|  | error7: | 
|  | FREE(action); | 
|  | error6: | 
|  | FREE(e->message); | 
|  | error5: | 
|  | FREE(e->source); | 
|  | error4: | 
|  | FREE(e); | 
|  | error3: | 
|  | FREE(version); | 
|  | error2: | 
|  | fclose(file); | 
|  | } | 
|  | error1: | 
|  | de = readdir(dir); | 
|  | } | 
|  | Run.handler_init = FALSE; | 
|  | closedir(dir); | 
|  | FREE(a); | 
|  | FREE(ea); | 
|  | return; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ----------------------------------------------------------------- Private */ | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Handle the event | 
|  | * @param E An event | 
|  | */ | 
|  | static void handle_event(Event_T E) { | 
|  | Service_T S; | 
|  |  | 
|  | ASSERT(E); | 
|  | ASSERT(E->action); | 
|  | ASSERT(E->action->failed); | 
|  | ASSERT(E->action->succeeded); | 
|  |  | 
|  | /* We will handle only first succeeded event, recurrent succeeded events | 
|  | * or insufficient succeeded events during failed service state are | 
|  | * ignored. Failed events are handled each time. */ | 
|  | if (!E->state_changed && (E->state == STATE_SUCCEEDED || E->state == STATE_CHANGEDNOT || ((E->state_map & 0x1) ^ 0x1))) | 
|  | return; | 
|  |  | 
|  | S = Event_get_source(E); | 
|  | if (!S) { | 
|  | LogError("Event handling aborted\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (E->message) { | 
|  | /* In the case that the service state is initializing yet and error | 
|  | * occured, log it and exit. Succeeded events in init state are not | 
|  | * logged. Instance and action events are logged always with priority | 
|  | * info. */ | 
|  | if (E->state != STATE_INIT || E->state_map & 0x1) { | 
|  | if (E->state == STATE_SUCCEEDED || E->state == STATE_CHANGEDNOT || E->id == Event_Instance || E->id == Event_Action) | 
|  | LogInfo("'%s' %s\n", S->name, E->message); | 
|  | else | 
|  | LogError("'%s' %s\n", S->name, E->message); | 
|  | } | 
|  | if (E->state == STATE_INIT) | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (E->state == STATE_FAILED || E->state == STATE_CHANGED) { | 
|  | if (E->id != Event_Instance && E->id != Event_Action) { // We are not interested in setting error flag for instance and action events | 
|  | S->error |= E->id; | 
|  | /* The error hint provides second dimension for error bitmap and differentiates between failed/changed event states (failed=0, chaged=1) */ | 
|  | if (E->state == STATE_CHANGED) | 
|  | S->error_hint |= E->id; | 
|  | else | 
|  | S->error_hint &= ~E->id; | 
|  | } | 
|  | handle_action(E, E->action->failed); | 
|  | } else { | 
|  | S->error &= ~E->id; | 
|  | handle_action(E, E->action->succeeded); | 
|  | } | 
|  |  | 
|  | /* Possible event state change was handled so we will reset the flag. */ | 
|  | E->state_changed = FALSE; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void handle_action(Event_T E, Action_T A) { | 
|  | Service_T s; | 
|  |  | 
|  | ASSERT(E); | 
|  | ASSERT(A); | 
|  |  | 
|  | E->flag = HANDLER_SUCCEEDED; | 
|  |  | 
|  | if (A->id == ACTION_IGNORE) | 
|  | return; | 
|  |  | 
|  | /* Alert and mmonit event notification are common actions */ | 
|  | E->flag |= handle_mmonit(E); | 
|  | E->flag |= handle_alert(E); | 
|  |  | 
|  | /* In the case that some subhandler failed, enqueue the event for | 
|  | * partial reprocessing */ | 
|  | if (E->flag != HANDLER_SUCCEEDED) { | 
|  | if (Run.eventlist_dir) | 
|  | Event_queue_add(E); | 
|  | else | 
|  | LogError("Aborting event\n"); | 
|  | } | 
|  |  | 
|  | if (!(s = Event_get_source(E))) { | 
|  | LogError("Event action handling aborted\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Action event is handled already. For Instance events | 
|  | * we don't want actions like stop to be executed | 
|  | * to prevent the disabling of system service monitoring */ | 
|  | if (A->id == ACTION_ALERT || E->id == Event_Instance) { | 
|  | return; | 
|  | } else if (A->id == ACTION_EXEC) { | 
|  | LogInfo("'%s' exec: %s\n", s->name, A->exec->arg[0]); | 
|  | spawn(s, A->exec, E); | 
|  | return; | 
|  | } else { | 
|  | if (s->actionratelist && (A->id == ACTION_START || A->id == ACTION_RESTART)) | 
|  | s->nstart++; | 
|  |  | 
|  | if (s->mode == MODE_PASSIVE && (A->id == ACTION_START || A->id == ACTION_STOP  || A->id == ACTION_RESTART)) | 
|  | return; | 
|  |  | 
|  | control_service(s->name, A->id); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Add the partialy handled event to the global queue | 
|  | * @param E An event object | 
|  | */ | 
|  | static void Event_queue_add(Event_T E) { | 
|  | FILE        *file = NULL; | 
|  | char         file_name[STRLEN]; | 
|  | int          version = EVENT_VERSION; | 
|  | short        action = Event_get_action(E); | 
|  | int          rv = FALSE; | 
|  | mode_t       mask; | 
|  |  | 
|  | ASSERT(E); | 
|  | ASSERT(E->flag != HANDLER_SUCCEEDED); | 
|  |  | 
|  | if (!file_checkQueueDirectory(Run.eventlist_dir, 0700)) { | 
|  | LogError("%s: Aborting event - cannot access the directory %s\n", prog, Run.eventlist_dir); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!file_checkQueueLimit(Run.eventlist_dir, Run.eventlist_slots)) { | 
|  | LogError("%s: Aborting event - queue over quota\n", prog); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* compose the file name of actual timestamp and service name */ | 
|  | snprintf(file_name, STRLEN, "%s/%ld_%lx", Run.eventlist_dir, (long int)time(NULL), (long unsigned)E->source); | 
|  |  | 
|  | DEBUG("%s: Adding event to the queue file %s for later delivery\n", prog, file_name); | 
|  |  | 
|  | mask = umask(QUEUEMASK); | 
|  | file = fopen(file_name, "w"); | 
|  | umask(mask); | 
|  | if (! file) { | 
|  | LogError("%s: Aborting event - cannot open the event file %s -- %s\n", prog, file_name, STRERROR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* write event structure version */ | 
|  | if (!(rv = file_writeQueue(file, &version, sizeof(int)))) | 
|  | goto error; | 
|  |  | 
|  | /* write event structure */ | 
|  | if (!(rv = file_writeQueue(file, E, sizeof(*E)))) | 
|  | goto error; | 
|  |  | 
|  | /* write source */ | 
|  | if (!(rv = file_writeQueue(file, E->source, E->source ? strlen(E->source)+1 : 0))) | 
|  | goto error; | 
|  |  | 
|  | /* write message */ | 
|  | if (!(rv = file_writeQueue(file, E->message, E->message ? strlen(E->message)+1 : 0))) | 
|  | goto error; | 
|  |  | 
|  | /* write event action */ | 
|  | if (!(rv = file_writeQueue(file, &action, sizeof(short)))) | 
|  | goto error; | 
|  |  | 
|  | error: | 
|  | fclose(file); | 
|  | if (!rv) { | 
|  | LogError("%s: Aborting event - unable to save event information to %s\n",  prog, file_name); | 
|  | if (unlink(file_name) < 0) | 
|  | LogError("Failed to remove event file '%s' -- %s\n", file_name, STRERROR); | 
|  | } else { | 
|  | if (!Run.handler_init && E->flag & HANDLER_ALERT) | 
|  | Run.handler_queue[HANDLER_ALERT]++; | 
|  | if (!Run.handler_init && E->flag & HANDLER_MMONIT) | 
|  | Run.handler_queue[HANDLER_MMONIT]++; | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Update the partialy handled event in the global queue | 
|  | * @param E An event object | 
|  | * @param file_name File name | 
|  | */ | 
|  | static void Event_queue_update(Event_T E, const char *file_name) { | 
|  | FILE        *file = NULL; | 
|  | int          version = EVENT_VERSION; | 
|  | short        action = Event_get_action(E); | 
|  | int          rv = FALSE; | 
|  | mode_t       mask; | 
|  |  | 
|  | ASSERT(E); | 
|  | ASSERT(E->flag != HANDLER_SUCCEEDED); | 
|  |  | 
|  | if (!file_checkQueueDirectory(Run.eventlist_dir, 0700)) { | 
|  | LogError("%s: Aborting event - cannot access the directory %s\n", prog, Run.eventlist_dir); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DEBUG("%s: Updating event in the queue file %s for later delivery\n", prog, file_name); | 
|  |  | 
|  | mask = umask(QUEUEMASK); | 
|  | file = fopen(file_name, "w"); | 
|  | umask(mask); | 
|  | if (! file) | 
|  | { | 
|  | LogError("%s: Aborting event - cannot open the event file %s -- %s\n", prog, file_name, STRERROR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* write event structure version */ | 
|  | if (!(rv = file_writeQueue(file, &version, sizeof(int)))) | 
|  | goto error; | 
|  |  | 
|  | /* write event structure */ | 
|  | if (!(rv = file_writeQueue(file, E, sizeof(*E)))) | 
|  | goto error; | 
|  |  | 
|  | /* write source */ | 
|  | if (!(rv = file_writeQueue(file, E->source, E->source ? strlen(E->source)+1 : 0))) | 
|  | goto error; | 
|  |  | 
|  | /* write message */ | 
|  | if (!(rv = file_writeQueue(file, E->message, E->message ? strlen(E->message)+1 : 0))) | 
|  | goto error; | 
|  |  | 
|  | /* write event action */ | 
|  | if (!(rv = file_writeQueue(file, &action, sizeof(short)))) | 
|  | goto error; | 
|  |  | 
|  | error: | 
|  | fclose(file); | 
|  | if (!rv) { | 
|  | LogError("%s: Aborting event - unable to update event information to %s\n",  prog, file_name); | 
|  | if (unlink(file_name) < 0) | 
|  | LogError("Failed to remove event file '%s' -- %s\n", file_name, STRERROR); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  |