nagios4/cgi/archiveutils.c

1651 lines
45 KiB
C
Raw Normal View History

2017-05-19 23:37:19 +02:00
/**************************************************************************
*
* ARCHIVEUTILS.C - Utilities for Nagios CGIs that read archive logs
*
* Copyright (c) 2013 Nagios Enterprises, LLC
* Last Modified: 06-30-2013
*
* License:
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*************************************************************************/
#include "../include/config.h"
#include "../include/common.h"
#include "../include/objects.h"
#include "../include/statusdata.h"
#include "../include/comments.h"
#include "../include/cgiutils.h"
#include "../include/statusdata.h"
#include "../include/archiveutils.h"
#define AU_INITIAL_LIST_SIZE 16
const string_value_mapping svm_au_object_types[] = {
{ "none", AU_OBJTYPE_NONE, "None" },
{ "host", AU_OBJTYPE_HOST, "Host" },
{ "service", AU_OBJTYPE_SERVICE, "Service" },
{ NULL, -1, NULL },
};
const string_value_mapping svm_au_state_types[] = {
{ "hard", AU_STATETYPE_HARD, "Hard" },
{ "soft", AU_STATETYPE_SOFT, "Soft" },
{ "nodata", AU_STATETYPE_NO_DATA, "No Data" },
{ NULL, -1, NULL },
};
const string_value_mapping svm_au_states[] = {
{ "nodata", AU_STATE_NO_DATA, "No Data" },
{ "up", AU_STATE_HOST_UP, "Host Up" },
{ "down", AU_STATE_HOST_DOWN, "How Down" },
{ "unreachable", AU_STATE_HOST_UNREACHABLE, "Host Unreachable" },
{ "ok", AU_STATE_SERVICE_OK, "Service OK" },
{ "warning", AU_STATE_SERVICE_WARNING, "Service Warning" },
{ "critical", AU_STATE_SERVICE_CRITICAL, "Service Critical" },
{ "unknown", AU_STATE_SERVICE_UNKNOWN, "Service Unknown" },
{ "programstart", AU_STATE_PROGRAM_START, "Program Start"},
{ "programend", AU_STATE_PROGRAM_END, "Program End"},
{ "downtimestart", AU_STATE_DOWNTIME_START, "Downtime Start"},
{ "downtimeend", AU_STATE_DOWNTIME_END, "Downtime End"},
{ "currentstate", AU_STATE_CURRENT_STATE, "Current State"},
{ NULL, -1, NULL },
};
const string_value_mapping svm_au_log_types[] = {
{ "alert", AU_LOGTYPE_ALERT, "Alert" },
{ "initialstate", AU_LOGTYPE_STATE_INITIAL, "Initial State" },
{ "currentstate", AU_LOGTYPE_STATE_CURRENT, "Current State" },
{ "notification", AU_LOGTYPE_NOTIFICATION, "Notification" },
{ "downtime", AU_LOGTYPE_DOWNTIME, "Downtime" },
{ "nagios", AU_LOGTYPE_NAGIOS, "Nagios" },
{ NULL, -1, NULL },
};
const string_value_mapping svm_au_notification_types[] = {
{ "nodata", AU_NOTIFICATION_NO_DATA,
"No Data" },
{ "down", AU_NOTIFICATION_HOST_DOWN,
"Host Down" },
{ "unreachable", AU_NOTIFICATION_HOST_UNREACHABLE,
"Host Unreachable" },
{ "recovery", AU_NOTIFICATION_HOST_RECOVERY,
"Host Recovery" },
{ "hostcustom", AU_NOTIFICATION_HOST_CUSTOM,
"Host Custom" },
{ "hostack", AU_NOTIFICATION_HOST_ACK,
"Host Acknowledgement" },
{ "hostflapstart", AU_NOTIFICATION_HOST_FLAPPING_START,
"Host Flapping Start" },
{ "hostflapstop", AU_NOTIFICATION_HOST_FLAPPING_STOP,
"Host Flapping Stop" },
{ "critical", AU_NOTIFICATION_SERVICE_CRITICAL,
"Service Critical" },
{ "warning", AU_NOTIFICATION_SERVICE_WARNING,
"Service Warning" },
{ "recovery", AU_NOTIFICATION_SERVICE_RECOVERY,
"Service Recovery" },
{ "custom", AU_NOTIFICATION_SERVICE_CUSTOM,
"Service Custom" },
{ "serviceack", AU_NOTIFICATION_SERVICE_ACK,
"Service Acknowledgement" },
{ "serviceflapstart", AU_NOTIFICATION_SERVICE_FLAPPING_START,
"Service Flapping Start" },
{ "serviceflapstop", AU_NOTIFICATION_SERVICE_FLAPPING_STOP,
"Service Flapping Stop" },
{ "unknown", AU_NOTIFICATION_SERVICE_UNKNOWN,
"Service Unknown" },
{ NULL, -1, NULL },
};
/* Function prototypes */
int read_log_file(char *, unsigned, unsigned, unsigned, au_log *);
int au_add_nagios_log(au_log *, time_t, int, char *);
void au_free_nagios_log(au_log_nagios *);
int parse_states_and_alerts(char *, time_t, int, unsigned, unsigned, au_log *);
int parse_downtime_alerts(char *, time_t, unsigned, au_log *);
int au_add_downtime_log(au_log *, time_t, int, void *, int);
void au_free_downtime_log(au_log_downtime *);
int parse_notification_log(char *, time_t, int, au_log *);
int au_add_notification_log(au_log *, time_t, int, void *, au_contact *, int,
char *, char *);
void au_free_notification_log(au_log_notification *);
au_log_entry *au_add_log_entry(au_log *, time_t, int, void *);
void au_free_log_entry_void(void *);
void au_free_log_entry(au_log_entry *);
au_host *au_add_host_and_sort(au_array *, char *);
void au_free_host_void(void *);
void au_free_host(au_host *);
int au_cmp_hosts(const void *, const void *);
au_service *au_add_service_and_sort(au_array *, char *, char *);
int au_cmp_services(const void *, const void *);
void au_free_service_void(void *);
void au_free_service(au_service *);
au_contact *au_add_contact_and_sort(au_array *, char *);
au_contact *au_add_contact(au_array *, char *);
void au_free_contact_void(void *);
void au_free_contact(au_contact *);
int au_cmp_contacts(const void *, const void *);
au_contact *au_find_contact(au_array *, char *);
void au_sort_array(au_array *, int(*cmp)(const void *, const void *));
void *au_find_in_array(au_array *, void *,
int(*cmp)(const void *, const void *));
au_linked_list *au_init_list(char *);
void au_empty_list(au_linked_list *, void(*)(void *));
void au_free_list(au_linked_list *, void(*)(void *));
/* External variables */
extern int log_rotation_method;
/* Initialize log structure */
au_log *au_init_log(void) {
au_log *log;
/* Initialize the log structure itself */
if((log = calloc(1, sizeof( au_log))) == NULL) {
return NULL;
}
/* Initialize the host subjects */
if((log->host_subjects = au_init_array("host_subjects")) == NULL) {
au_free_log(log);
return NULL;
}
/* Initialize the service subjects */
if((log->service_subjects = au_init_array("service_subjects")) == NULL) {
au_free_log(log);
return NULL;
}
/* Initialize the log entry list */
if((log->entry_list = au_init_list("global log entries")) == NULL) {
au_free_log(log);
return NULL;
}
/* Initialize the host list */
if((log->hosts = au_init_array("hosts")) == NULL) {
au_free_log(log);
return NULL;
}
/* Initialize the service list */
if((log->services = au_init_array("services")) == NULL) {
au_free_log(log);
return NULL;
}
/* Initialize the contact list */
if((log->contacts = au_init_array("contacts")) == NULL) {
au_free_log(log);
return NULL;
}
return log;
}
void au_free_log(au_log *log) {
if(NULL == log) return;
if(NULL != log->host_subjects) {
au_free_array(log->host_subjects, au_free_host_void);
}
if(NULL != log->service_subjects) {
au_free_array(log->service_subjects, au_free_service_void);
}
if(NULL != log->hosts) {
au_free_array(log->hosts, au_free_host_void);
}
if(NULL != log->services) {
au_free_array(log->services, au_free_service_void);
}
if(NULL != log->contacts) {
au_free_array(log->contacts, au_free_contact_void);
}
if(NULL != log->entry_list) {
au_free_list(log->entry_list, au_free_log_entry_void);
}
free(log);
}
/* reads log files for archived data */
int read_archived_data(time_t start_time, time_t end_time,
int backtrack_archives, unsigned obj_types, unsigned state_types,
unsigned log_types, au_log *log, time_t *last_archive_data_update) {
char filename[MAX_FILENAME_LENGTH];
int oldest_archive = 0;
int newest_archive = 0;
int current_archive = 0;
int x;
int retval = 1;
au_host *global_host;
au_host *temp_host;
au_service *temp_service;
au_node *temp_entry;
struct stat adstat;
/* Determine oldest archive to use when scanning for data
(include backtracked archives as well) */
oldest_archive = determine_archive_to_use_from_time(start_time);
if(log_rotation_method != LOG_ROTATION_NONE) {
oldest_archive += backtrack_archives;
}
/* determine most recent archive to use when scanning for data */
newest_archive = determine_archive_to_use_from_time(end_time);
if(oldest_archive < newest_archive) {
oldest_archive = newest_archive;
}
/* read in all the necessary archived logs (from most recent to earliest) */
for(current_archive = newest_archive; current_archive <= oldest_archive;
current_archive++) {
#ifdef DEBUG
printf("Reading archive #%d... ", current_archive);
#endif
/* get the name of the log file that contains this archive */
get_log_archive_to_use(current_archive, filename, sizeof(filename) - 1);
#ifdef DEBUG
printf("Archive name: '%s'\n", filename);
#endif
/* Record the last modification time of the the archive file */
if(stat(filename, &adstat) < 0) {
/* ENOENT is OK because Nagios may have been down when the
logs were being rotated */
if(ENOENT != errno) return -1;
}
else {
if(*last_archive_data_update < adstat.st_mtime) {
*last_archive_data_update = adstat.st_mtime;
}
/* scan the log file for archived state data */
if(read_log_file(filename, obj_types, state_types, log_types,
log) == 0) {
return 0; /* Memory allocation error */
}
}
}
/* Add Nagios log events to all hosts and services found */
global_host = au_find_host(log->hosts, "*");
if(NULL != global_host) {
for(temp_entry = global_host->log_entries->head; NULL != temp_entry;
temp_entry = temp_entry->next) {
for(x = 0; x < log->hosts->count; x++) {
temp_host = log->hosts->members[x];
if(temp_host == global_host) continue;
if(au_list_add_node(temp_host->log_entries,
temp_entry->data, au_cmp_log_entries) == 0) {
retval = 0;
break;
}
}
if(0 == retval) break;
for(x = 0; x < log->services->count; x++) {
temp_service = log->services->members[x];
if(au_list_add_node(temp_service->log_entries,
temp_entry->data, au_cmp_log_entries) == 0) {
retval = 0;
break;
}
}
if(0 == retval) break;
}
}
return 1;
}
/* grabs archives state data from a log file */
int read_log_file(char *filename, unsigned obj_types, unsigned state_types,
unsigned log_types, au_log *log) {
char *input = NULL;
char *input2 = NULL;
char *temp_buffer = NULL;
time_t time_stamp;
mmapfile *thefile = NULL;
int retval = 1;
if((thefile = mmap_fopen(filename)) == NULL) {
return 1;
}
while(1) {
/* free memory */
free(input);
free(input2);
input = NULL;
input2 = NULL;
/* read the next line */
if((input = mmap_fgets(thefile)) == NULL) break;
strip(input);
if((input2 = strdup(input)) == NULL) continue;
temp_buffer = my_strtok(input2, "]");
time_stamp = (temp_buffer == NULL) ? (time_t)0 :
(time_t)strtoul(temp_buffer + 1, NULL, 10);
/* program starts/restarts */
if(strstr(input, " starting...")) {
if(au_add_nagios_log(log, time_stamp, AU_STATE_PROGRAM_START,
"Program start") == 0) {
retval = 0;
break;
}
}
if(strstr(input, " restarting...")) {
if(au_add_nagios_log(log, time_stamp, AU_STATE_PROGRAM_START,
"Program restart") == 0) {
retval = 0;
break;
}
}
/* program stops */
if(strstr(input, " shutting down...")) {
if(au_add_nagios_log(log, time_stamp, AU_STATE_PROGRAM_END,
"Normal program termination") == 0) {
retval = 0;
break;
}
}
if(strstr(input, "Bailing out")) {
if(au_add_nagios_log(log, time_stamp, AU_STATE_PROGRAM_END,
"Abnormal program termination") == 0) {
retval = 0;
break;
}
}
if(obj_types & AU_OBJTYPE_HOST) {
/* normal host alerts */
if((log_types & AU_LOGTYPE_ALERT) && strstr(input, "HOST ALERT:")) {
if(parse_states_and_alerts(input, time_stamp, AU_LOGTYPE_ALERT,
AU_OBJTYPE_HOST, state_types, log) == 0) {
retval = 0;
break;
}
}
/* host initial states */
else if((log_types & AU_LOGTYPE_STATE) &&
strstr(input, "INITIAL HOST STATE:")) {
if(parse_states_and_alerts(input, time_stamp,
AU_LOGTYPE_STATE_INITIAL, AU_OBJTYPE_HOST, state_types,
log) == 0) {
retval = 0;
break;
}
}
/* host current states */
else if((log_types & AU_LOGTYPE_STATE) &&
strstr(input, "CURRENT HOST STATE:")) {
if(parse_states_and_alerts(input, time_stamp,
AU_LOGTYPE_STATE_CURRENT, AU_OBJTYPE_HOST, state_types,
log) == 0) {
retval = 0;
break;
}
}
/* scheduled downtime notices */
else if((log_types & AU_LOGTYPE_DOWNTIME) &&
strstr(input, "HOST DOWNTIME ALERT:")) {
if(parse_downtime_alerts(input, time_stamp, AU_OBJTYPE_HOST,
log) == 0) {
retval = 0;
break;
}
}
/* host notifications */
else if((log_types & AU_LOGTYPE_NOTIFICATION) &&
strstr(input, "HOST NOTIFICATION:")) {
if(parse_notification_log(input, time_stamp, AU_OBJTYPE_HOST,
log) ==0) {
retval = 0;
break;
}
}
}
if(obj_types & AU_OBJTYPE_SERVICE) {
/* normal service alerts */
if((log_types & AU_LOGTYPE_ALERT) &&
strstr(input, "SERVICE ALERT:")) {
if(parse_states_and_alerts(input, time_stamp, AU_LOGTYPE_ALERT,
AU_OBJTYPE_SERVICE, state_types, log) == 0) {
retval = 0;
break;
}
}
/* service initial states */
else if((log_types & AU_LOGTYPE_STATE) &&
strstr(input, "INITIAL SERVICE STATE:")) {
if(parse_states_and_alerts(input, time_stamp,
AU_LOGTYPE_STATE_INITIAL, AU_OBJTYPE_SERVICE,
state_types, log) == 0) {
retval = 0;
break;
}
}
/* service current states */
else if((log_types & AU_LOGTYPE_STATE) &&
strstr(input, "CURRENT SERVICE STATE:")) {
if(parse_states_and_alerts(input, time_stamp,
AU_LOGTYPE_STATE_CURRENT, AU_OBJTYPE_SERVICE,
state_types, log) == 0) {
retval = 0;
break;
}
}
/* scheduled service downtime notices */
else if((log_types & AU_LOGTYPE_DOWNTIME) &&
strstr(input, "SERVICE DOWNTIME ALERT:")) {
if(parse_downtime_alerts(input, time_stamp, AU_OBJTYPE_SERVICE,
log) == 0) {
retval = 0;
break;
}
}
/* service notifications */
else if((log_types & AU_LOGTYPE_NOTIFICATION) &&
strstr(input, "SERVICE NOTIFICATION:")) {
if(parse_notification_log(input, time_stamp,
AU_OBJTYPE_SERVICE, log) ==0) {
retval = 0;
break;
}
}
}
}
/* free memory and close the file */
free(input);
free(input2);
mmap_fclose(thefile);
return retval;
}
int au_cmp_log_entries(const void *a, const void *b) {
au_log_entry *lea = *(au_log_entry **)a;
au_log_entry *leb = *(au_log_entry **)b;
if(lea->timestamp == leb->timestamp) return 0;
else if(lea->timestamp < leb->timestamp) return -1;
else return 1;
}
int au_add_nagios_log(au_log *log, time_t timestamp, int type,
char *description) {
au_log_nagios *nagios_log;
au_log_entry *new_log_entry;
au_host *global_host;
/* Create the au_log_nagios */
if((nagios_log = calloc(1, sizeof(au_log_nagios))) == NULL) {
return 0;
}
nagios_log->type = type;
if((nagios_log->description = strdup(description)) == NULL) {
au_free_nagios_log(nagios_log);
return 0;
}
/* Create the log entry */
if((new_log_entry = au_add_log_entry(log, timestamp, AU_LOGTYPE_NAGIOS,
(void *)nagios_log)) == NULL) {
au_free_nagios_log(nagios_log);
return 0;
}
/* Find the au_host object associated with the global host */
global_host = au_find_host(log->hosts, "*");
if(NULL == global_host) {
global_host = au_add_host_and_sort(log->hosts, "*");
if(NULL == global_host) { /* Could not allocate memory */
return 0;
}
}
/* Add the entry to the global host */
if(au_list_add_node(global_host->log_entries, new_log_entry,
au_cmp_log_entries) == 0) {
return 0;
}
return 1;
}
void au_free_nagios_log(au_log_nagios *nagios_log) {
if(NULL == nagios_log) return;
if(NULL != nagios_log->description) free(nagios_log->description);
free(nagios_log);
}
/* parse state and alert log entries */
int parse_states_and_alerts(char *input, time_t timestamp, int log_type,
unsigned obj_type, unsigned state_types, au_log *log) {
char *temp_buffer = NULL;
char entry_host_name[MAX_INPUT_BUFFER];
char entry_svc_description[MAX_INPUT_BUFFER];
char *plugin_output;
au_host *temp_host_subject;
au_host *temp_host = NULL;
au_service *temp_service_subject;
au_service *temp_service = NULL;
int state_type;
int state;
/* get host name */
temp_buffer = my_strtok(NULL, ":");
temp_buffer = my_strtok(NULL, ";");
strncpy(entry_host_name, (temp_buffer == NULL) ? "" :
temp_buffer + 1, sizeof(entry_host_name));
entry_host_name[sizeof(entry_host_name) - 1] = '\x0';
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(log->host_subjects->count > 0) {
/* see if there is a corresponding subject for this host */
temp_host_subject = au_find_host(log->host_subjects,
entry_host_name);
if(temp_host_subject == NULL) return 1;
}
/* Find the au_host object associated with the host name */
temp_host = au_find_host(log->hosts, entry_host_name);
if(NULL == temp_host) {
temp_host = au_add_host_and_sort(log->hosts, entry_host_name);
if(NULL == temp_host) { /* Could not allocate memory */
return 0;
}
}
break;
case AU_OBJTYPE_SERVICE:
/* get service description */
temp_buffer = my_strtok(NULL, ";");
strncpy(entry_svc_description, (temp_buffer == NULL) ? "" :
temp_buffer, sizeof(entry_svc_description));
entry_svc_description[sizeof(entry_svc_description) - 1] = '\x0';
if(log->service_subjects->count > 0) {
/* see if there is a corresponding subject for this service */
temp_service_subject = au_find_service(log->service_subjects,
entry_host_name, entry_svc_description);
if(temp_service_subject == NULL) return 1;
}
/* Find the au_service object associated with the service */
temp_service = au_find_service(log->services, entry_host_name,
entry_svc_description);
if(NULL == temp_service) {
temp_service = au_add_service_and_sort(log->services,
entry_host_name, entry_svc_description);
if(NULL == temp_service) { /* Could not allocate memory */
return 0;
}
}
break;
}
/* state types */
if(strstr(input, ";SOFT;")) {
if(!(state_types & AU_STATETYPE_SOFT)) return 1;
state_type = AU_STATETYPE_SOFT;
}
if(strstr(input, ";HARD;")) {
if(!(state_types & AU_STATETYPE_HARD)) return 1;
state_type = AU_STATETYPE_HARD;
}
/* get the plugin output */
temp_buffer = my_strtok(NULL, ";");
temp_buffer = my_strtok(NULL, ";");
temp_buffer = my_strtok(NULL, ";");
plugin_output = my_strtok(NULL, "\n");
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(strstr(input, ";DOWN;")) {
state = AU_STATE_HOST_DOWN;
}
else if(strstr(input, ";UNREACHABLE;")) {
state = AU_STATE_HOST_UNREACHABLE;
}
else if(strstr(input, ";RECOVERY") || strstr(input, ";UP;")) {
state = AU_STATE_HOST_UP;
}
else {
state = AU_STATE_NO_DATA;
state_type = AU_STATETYPE_NO_DATA;
}
if(au_add_alert_or_state_log(log, timestamp, log_type, AU_OBJTYPE_HOST,
(void *)temp_host, state_type, state, plugin_output) == 0) {
return 0;
}
break;
case AU_OBJTYPE_SERVICE:
if(strstr(input, ";CRITICAL;")) {
state = AU_STATE_SERVICE_CRITICAL;
}
else if(strstr(input, ";WARNING;")) {
state = AU_STATE_SERVICE_WARNING;
}
else if(strstr(input, ";UNKNOWN;")) {
state = AU_STATE_SERVICE_UNKNOWN;
}
else if(strstr(input, ";RECOVERY;") || strstr(input, ";OK;")) {
state = AU_STATE_SERVICE_OK;
}
else {
state = AU_STATE_NO_DATA;
state_type = AU_STATETYPE_NO_DATA;
}
if(au_add_alert_or_state_log(log, timestamp, log_type,
AU_OBJTYPE_SERVICE, (void *)temp_service, state_type, state,
plugin_output) == 0) {
return 0;
}
break;
}
return 1;
}
int au_add_alert_or_state_log(au_log *log, time_t timestamp, int log_type,
int obj_type, void *object, int state_type, int state,
char *plugin_output) {
au_log_alert *alert_log;
au_log_entry *new_log_entry;
/* Create the au_log_alert */
if((alert_log = au_create_alert_or_state_log(obj_type, object, state_type,
state, plugin_output)) == NULL) {
return 0;
}
/* Create the log entry */
if((new_log_entry = au_add_log_entry(log, timestamp, log_type,
(void *)alert_log)) == NULL) {
au_free_alert_log(alert_log);
return 0;
}
/* Add the log entry to the logs for the object supplied */
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(au_list_add_node(((au_host *)object)->log_entries,
new_log_entry, au_cmp_log_entries) == 0) {
return 0;
}
break;
case AU_OBJTYPE_SERVICE:
if(au_list_add_node(((au_service *)object)->log_entries,
new_log_entry, au_cmp_log_entries) == 0) {
return 0;
}
break;
}
return 1;
}
au_log_alert *au_create_alert_or_state_log(int obj_type, void *object,
int state_type, int state, char *plugin_output) {
au_log_alert *alert_log;
/* Create the au_log_alert */
if((alert_log = calloc(1, sizeof(au_log_alert))) == NULL) {
return NULL;
}
alert_log->obj_type = obj_type;
alert_log->object = object;
alert_log->state_type = state_type;
alert_log->state = state;
alert_log->plugin_output = NULL;
if(plugin_output != NULL) {
if((alert_log->plugin_output = strdup(plugin_output)) == NULL) {
au_free_alert_log(alert_log);
return NULL;
}
}
return alert_log;
}
void au_free_alert_log(au_log_alert *alert_log) {
if(NULL == alert_log) return;
if(NULL != alert_log->plugin_output) free(alert_log->plugin_output);
free(alert_log);
}
/* parse archive log downtime notifications */
int parse_downtime_alerts(char *input, time_t timestamp, unsigned obj_type,
au_log *log) {
char *temp_buffer = NULL;
char entry_host_name[MAX_INPUT_BUFFER];
char entry_svc_description[MAX_INPUT_BUFFER];
au_host *temp_host_subject;
au_host *temp_host = NULL;
au_service *temp_service_subject;
au_service *temp_service = NULL;
/* get host name */
temp_buffer = my_strtok(NULL, ":");
temp_buffer = my_strtok(NULL, ";");
strncpy(entry_host_name, (temp_buffer == NULL) ? "" :
temp_buffer + 1, sizeof(entry_host_name));
entry_host_name[sizeof(entry_host_name) - 1] = '\x0';
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(log->host_subjects->count > 0) {
/* see if there is a corresponding subject for this host */
temp_host_subject = au_find_host(log->host_subjects,
entry_host_name);
if(temp_host_subject == NULL) return 1;
}
/* Find the au_host object associated with the host name */
temp_host = au_find_host(log->hosts, entry_host_name);
if(NULL == temp_host) {
temp_host = au_add_host_and_sort(log->hosts, entry_host_name);
if(NULL == temp_host) { /* Could not allocate memory */
return 0;
}
}
break;
case AU_OBJTYPE_SERVICE:
/* get service description */
temp_buffer = my_strtok(NULL, ";");
strncpy(entry_svc_description, (temp_buffer == NULL) ? "" :
temp_buffer, sizeof(entry_svc_description));
entry_svc_description[sizeof(entry_svc_description) - 1] = '\x0';
if(log->service_subjects->count > 0) {
/* see if there is a corresponding subject for this service */
temp_service_subject = au_find_service(log->service_subjects,
entry_host_name, entry_svc_description);
if(temp_service_subject == NULL) return 1;
}
/* Find the au_service object associated with the service */
temp_service = au_find_service(log->services, entry_host_name,
entry_svc_description);
if(NULL == temp_service) {
temp_service = au_add_service_and_sort(log->services,
entry_host_name, entry_svc_description);
if(NULL == temp_service) { /* Could not allocate memory */
return 0;
}
}
break;
}
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(strstr(input, ";STARTED;")) {
if(au_add_downtime_log(log, timestamp, AU_OBJTYPE_HOST,
(void *)temp_host, AU_STATE_DOWNTIME_START) == 0) {
return 0;
}
}
else {
if(au_add_downtime_log(log, timestamp, AU_OBJTYPE_HOST,
(void *)temp_host, AU_STATE_DOWNTIME_END) == 0) {
return 0;
}
}
break;
case AU_OBJTYPE_SERVICE:
if(strstr(input, ";STARTED;")) {
if(au_add_downtime_log(log, timestamp, AU_OBJTYPE_SERVICE,
(void *)temp_service, AU_STATE_DOWNTIME_START) == 0) {
return 0;
}
}
else {
if(au_add_downtime_log(log, timestamp, AU_OBJTYPE_SERVICE,
(void *)temp_service, AU_STATE_DOWNTIME_END) == 0) {
return 0;
}
}
break;
}
return 1;
}
int au_add_downtime_log(au_log *log, time_t timestamp, int obj_type,
void *object, int downtime_type) {
au_log_downtime *downtime_log;
au_log_entry *new_log_entry;
/* Create the au_log_downtime */
if((downtime_log = calloc(1, sizeof(au_log_downtime))) == NULL) {
return 0;
}
downtime_log->obj_type = obj_type;
downtime_log->object = object;
downtime_log->downtime_type = downtime_type;
/* Create the log entry */
if((new_log_entry = au_add_log_entry(log, timestamp, AU_LOGTYPE_DOWNTIME,
(void *)downtime_log)) == NULL) {
au_free_downtime_log(downtime_log);
return 0;
}
/* Add the log entry to the logs for the object supplied */
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(au_list_add_node(((au_host *)object)->log_entries,
new_log_entry, au_cmp_log_entries) == 0) {
return 0;
}
break;
case AU_OBJTYPE_SERVICE:
if(au_list_add_node(((au_service *)object)->log_entries,
new_log_entry, au_cmp_log_entries) == 0) {
return 0;
}
break;
}
return 1;
}
void au_free_downtime_log(au_log_downtime *downtime_log) {
if(NULL == downtime_log) return;
free(downtime_log);
}
int parse_notification_log(char *input, time_t timestamp, int obj_type,
au_log *log) {
char entry_contact_name[MAX_INPUT_BUFFER];
char entry_host_name[MAX_INPUT_BUFFER];
char entry_svc_description[MAX_INPUT_BUFFER];
char alert_level[MAX_INPUT_BUFFER];
char method_name[MAX_INPUT_BUFFER];
int notification_detail_type = AU_NOTIFICATION_NO_DATA;
char *temp_buffer = NULL;
au_host *temp_host_subject;
au_service *temp_service_subject;
au_contact *temp_contact;
au_host *temp_host = NULL;
au_service *temp_service = NULL;
/* get the contact name */
temp_buffer = my_strtok(NULL, ":");
temp_buffer = my_strtok(NULL, ";");
strncpy(entry_contact_name, (temp_buffer == NULL) ? "" :
temp_buffer + 1, sizeof(entry_contact_name));
entry_contact_name[sizeof(entry_contact_name) - 1] = '\x0';
/* Find the au_contact object associated with the contact name */
temp_contact = au_find_contact(log->contacts, entry_contact_name);
if(NULL == temp_contact) {
temp_contact = au_add_contact_and_sort(log->contacts,
entry_contact_name);
if(NULL == temp_contact) { /* Could not allocate memory */
return 0;
}
}
/* get the host name */
temp_buffer = (char *)my_strtok(NULL, ";");
snprintf(entry_host_name, sizeof(entry_host_name), "%s",
(temp_buffer == NULL) ? "" : temp_buffer);
entry_host_name[sizeof(entry_host_name) - 1] = '\x0';
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(log->host_subjects->count > 0) {
/* see if there is a corresponding subject for this host */
temp_host_subject = au_find_host(log->host_subjects,
entry_host_name);
if(temp_host_subject == NULL) return 1;
}
/* Find the au_host object associated with the host name */
temp_host = au_find_host(log->hosts, entry_host_name);
if(NULL == temp_host) {
temp_host = au_add_host_and_sort(log->hosts, entry_host_name);
if(NULL == temp_host) { /* Could not allocate memory */
return 0;
}
}
break;
case AU_OBJTYPE_SERVICE:
/* get service description */
temp_buffer = my_strtok(NULL, ";");
strncpy(entry_svc_description, (temp_buffer == NULL) ? "" :
temp_buffer, sizeof(entry_svc_description));
entry_svc_description[sizeof(entry_svc_description) - 1] = '\x0';
if(log->service_subjects->count > 0) {
/* see if there is a corresponding subject for this service */
temp_service_subject = au_find_service(log->service_subjects,
entry_host_name, entry_svc_description);
if(temp_service_subject == NULL) return 1;
}
/* Find the au_service object associated with the service */
temp_service = au_find_service(log->services, entry_host_name,
entry_svc_description);
if(NULL == temp_service) {
temp_service = au_add_service_and_sort(log->services,
entry_host_name, entry_svc_description);
if(NULL == temp_service) { /* Could not allocate memory */
return 0;
}
}
break;
}
/* get the alert level */
temp_buffer = (char *)my_strtok(NULL, ";");
snprintf(alert_level, sizeof(alert_level), "%s",
(temp_buffer == NULL) ? "" : temp_buffer);
alert_level[sizeof(alert_level) - 1] = '\x0';
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(!strcmp(alert_level, "DOWN")) {
notification_detail_type = AU_NOTIFICATION_HOST_DOWN;
}
else if(!strcmp(alert_level, "UNREACHABLE")) {
notification_detail_type = AU_NOTIFICATION_HOST_UNREACHABLE;
}
else if(!strcmp(alert_level, "RECOVERY") ||
!strcmp(alert_level, "UP")) {
notification_detail_type = AU_NOTIFICATION_HOST_RECOVERY;
}
else if(strstr(alert_level, "CUSTOM (")) {
notification_detail_type = AU_NOTIFICATION_HOST_CUSTOM;
}
else if(strstr(alert_level, "ACKNOWLEDGEMENT (")) {
notification_detail_type = AU_NOTIFICATION_HOST_ACK;
}
else if(strstr(alert_level, "FLAPPINGSTART (")) {
notification_detail_type = AU_NOTIFICATION_HOST_FLAPPING_START;
}
else if(strstr(alert_level, "FLAPPINGSTOP (")) {
notification_detail_type = AU_NOTIFICATION_HOST_FLAPPING_STOP;
}
break;
case AU_OBJTYPE_SERVICE:
if(!strcmp(alert_level, "CRITICAL")) {
notification_detail_type = AU_NOTIFICATION_SERVICE_CRITICAL;
}
else if(!strcmp(alert_level, "WARNING")) {
notification_detail_type = AU_NOTIFICATION_SERVICE_WARNING;
}
else if(!strcmp(alert_level, "RECOVERY") ||
!strcmp(alert_level, "OK")) {
notification_detail_type = AU_NOTIFICATION_SERVICE_RECOVERY;
}
else if(strstr(alert_level, "CUSTOM (")) {
notification_detail_type = AU_NOTIFICATION_SERVICE_CUSTOM;
}
else if(strstr(alert_level, "ACKNOWLEDGEMENT (")) {
notification_detail_type = AU_NOTIFICATION_SERVICE_ACK;
}
else if(strstr(alert_level, "FLAPPINGSTART (")) {
notification_detail_type = AU_NOTIFICATION_SERVICE_FLAPPING_START;
}
else if(strstr(alert_level, "FLAPPINGSTOP (")) {
notification_detail_type = AU_NOTIFICATION_SERVICE_FLAPPING_STOP;
}
else {
notification_detail_type = AU_NOTIFICATION_SERVICE_UNKNOWN;
}
break;
}
/* get the method name */
temp_buffer = (char *)my_strtok(NULL, ";");
snprintf(method_name, sizeof(method_name), "%s",
(temp_buffer == NULL) ? "" : temp_buffer);
method_name[sizeof(method_name) - 1] = '\x0';
/* move to the informational message */
temp_buffer = my_strtok(NULL, ";");
/* Create the log entry */
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(au_add_notification_log(log, timestamp, AU_OBJTYPE_HOST,
(void *)temp_host, temp_contact, notification_detail_type,
method_name, temp_buffer) == 0) {
return 0;
}
break;
case AU_OBJTYPE_SERVICE:
if(au_add_notification_log(log, timestamp, AU_OBJTYPE_SERVICE,
(void *)temp_service, temp_contact, notification_detail_type,
method_name, temp_buffer) == 0) {
return 0;
}
break;
}
return 1;
}
int au_add_notification_log(au_log *log, time_t timestamp, int obj_type,
void *object, au_contact *contact, int notification_type,
char *method, char *message) {
au_log_notification *notification_log;
au_log_entry *new_log_entry;
/* Create the au_log_downtime */
if((notification_log = calloc(1, sizeof(au_log_notification))) == NULL) {
return 0;
}
notification_log->obj_type = obj_type;
notification_log->object = object;
notification_log->contact = contact;
notification_log->notification_type = notification_type;
if((notification_log->method = strdup(method))
== NULL) {
au_free_notification_log(notification_log);
return 0;
}
if((notification_log->message = strdup(message)) == NULL) {
au_free_notification_log(notification_log);
return 0;
}
/* Create the log entry */
if((new_log_entry = au_add_log_entry(log, timestamp,
AU_LOGTYPE_NOTIFICATION, (void *)notification_log)) == NULL) {
au_free_notification_log(notification_log);
return 0;
}
/* Add the log entry to the logs for the object supplied */
switch(obj_type) {
case AU_OBJTYPE_HOST:
if(au_list_add_node((void *)((au_host *)object)->log_entries,
new_log_entry, au_cmp_log_entries) == 0) {
return 0;
}
break;
case AU_OBJTYPE_SERVICE:
if(au_list_add_node((void *)((au_service *)object)->log_entries,
new_log_entry, au_cmp_log_entries) == 0) {
return 0;
}
break;
}
return 1;
}
void au_free_notification_log(au_log_notification *notification_log) {
if(NULL == notification_log) return;
if(NULL != notification_log->method) free(notification_log->method);
if(NULL != notification_log->message) free(notification_log->message);
free(notification_log);
}
au_log_entry *au_add_log_entry(au_log *log, time_t timestamp, int entry_type,
void *entry) {
au_log_entry *new_log_entry;
/* Create the au_log_entry */
if((new_log_entry = calloc(1, sizeof(au_log_entry))) == NULL) {
return NULL;
}
new_log_entry->timestamp = timestamp;
new_log_entry->entry_type = entry_type;
new_log_entry->entry = entry;
if(au_list_add_node(log->entry_list, (void *)new_log_entry,
au_cmp_log_entries) == NULL) {
au_free_log_entry(new_log_entry);
return NULL;
}
return new_log_entry;
}
void au_free_log_entry_void(void *log_entry) {
au_free_log_entry((au_log_entry *)log_entry);
}
void au_free_log_entry(au_log_entry *log_entry) {
if(NULL == log_entry) return;
switch(log_entry->entry_type) {
case AU_LOGTYPE_ALERT:
case AU_LOGTYPE_STATE:
au_free_alert_log((au_log_alert *)log_entry->entry);
break;
case AU_LOGTYPE_NOTIFICATION:
au_free_notification_log((au_log_notification *)log_entry->entry);
break;
case AU_LOGTYPE_DOWNTIME:
au_free_downtime_log((au_log_downtime *)log_entry->entry);
break;
case AU_LOGTYPE_NAGIOS:
au_free_nagios_log((au_log_nagios *)log_entry->entry);
break;
}
free(log_entry);
}
/* Add a host to a host list and sort the list */
au_host *au_add_host_and_sort(au_array *host_list, char *name) {
au_host *temp_host;
temp_host = au_add_host(host_list, name);
if(NULL != temp_host) {
au_sort_array(host_list, au_cmp_hosts);
}
return temp_host;
}
/* Add a host to a host list */
au_host *au_add_host(au_array *host_list, char *name) {
au_host *new_host;
char buf[8192];
/* Should have been allocated during au_log_init() */
if(NULL == host_list) {
return NULL;
}
/* Create the host */
if((new_host = calloc(1, sizeof(au_host))) == NULL) {
return NULL;
}
if((new_host->name = strdup(name)) == NULL) {
au_free_host(new_host);
return NULL;
}
new_host->hostp = find_host(name);
new_host->availability = NULL;
snprintf(buf, sizeof(buf) - 1, "Host %s log entries", name);
if((new_host->log_entries = au_init_list(buf)) == NULL) {
au_free_host(new_host);
return NULL;
}
/* Add it to the list of hosts */
if(0 == au_array_append_member(host_list, (void *)new_host)) {
au_free_host(new_host);
return NULL;
}
return new_host;
}
void au_free_host_void(void *host) {
au_free_host((au_host *)host);
}
void au_free_host(au_host *host) {
if(NULL == host) return;
if(NULL != host->name) free(host->name);
/* Do not free the log entry data here because they are freed when the
master list's log entries are freed */
if(NULL != host->log_entries) au_free_list(host->log_entries, NULL);
if(NULL != host->availability) free(host->availability);
free(host);
return;
}
int au_cmp_hosts(const void *a, const void *b) {
au_host *hosta = *(au_host **)a;
au_host *hostb = *(au_host **)b;
return strcmp(hosta->name, hostb->name);
}
au_host *au_find_host(au_array *host_list, char *name) {
au_host key;
void *found;
if(NULL == host_list) return NULL;
key.name = name;
found = au_find_in_array(host_list, (void *)&key, au_cmp_hosts);
if(NULL == found) return NULL;
return *(au_host **)found;
}
au_service *au_add_service_and_sort(au_array *service_list, char *host_name,
char *description) {
au_service *temp_service;
temp_service = au_add_service(service_list, host_name, description);
if(NULL != temp_service) {
au_sort_array(service_list, au_cmp_services);
}
return temp_service;
}
au_service *au_add_service(au_array *service_list, char *host_name,
char *description) {
au_service *new_service;
char buf[8192];
/* Should have been allocated during au_log_init() */
if(NULL == service_list) {
return NULL;
}
/* Create the service */
if((new_service = calloc(1, sizeof(au_service))) == NULL) {
return NULL;
}
if((new_service->host_name = strdup(host_name)) == NULL) {
au_free_service(new_service);
return NULL;
}
if((new_service->description = strdup(description)) == NULL) {
au_free_service(new_service);
return NULL;
}
new_service->servicep = find_service(host_name, description);
new_service->availability = NULL;
snprintf(buf, sizeof(buf) - 1, "Service %s:%s log entries", host_name,
description);
if((new_service->log_entries = au_init_list(buf)) == NULL) {
au_free_service(new_service);
return NULL;
}
/* Add it to the list of services */
if(0 == au_array_append_member(service_list, (void *)new_service)) {
au_free_service(new_service);
return NULL;
}
return new_service;
}
void au_free_service_void(void *service) {
au_free_service((au_service *)service);
}
void au_free_service(au_service *service) {
if(NULL == service) return;
if(NULL != service->host_name) free(service->host_name);
if(NULL != service->description) free(service->description);
/* Do not free the log entry data here because they are freed when the
master list's log entries are freed */
if(NULL != service->log_entries) au_free_list(service->log_entries, NULL);
if(NULL != service->availability) free(service->availability);
free(service);
return;
}
au_service *au_find_service(au_array *service_list, char *host_name,
char *description) {
au_service key;
void *found;
if(NULL == service_list) return NULL;
key.host_name = host_name;
key.description = description;
found = (au_service *)au_find_in_array(service_list, (void *)&key,
au_cmp_services);
if(NULL == found) return NULL;
return *(au_service **)found;
}
int au_cmp_services(const void *a, const void *b) {
au_service *servicea = *(au_service **)a;
au_service *serviceb = *(au_service **)b;
int host_result;
host_result = strcmp(servicea->host_name, serviceb->host_name);
if(0 == host_result) {
return strcmp(servicea->description, serviceb->description);
}
else {
return host_result;
}
}
/* Add a contact to a contact list and sort the list */
au_contact *au_add_contact_and_sort(au_array *contact_list, char *name) {
au_contact *temp_contact;
temp_contact = au_add_contact(contact_list, name);
if(NULL != temp_contact) {
au_sort_array(contact_list, au_cmp_contacts);
}
return temp_contact;
}
/* Add a contact to a contact list */
au_contact *au_add_contact(au_array *contact_list, char *name) {
au_contact *new_contact;
/* Should have been allocated during au_log_init() */
if(NULL == contact_list) {
return NULL;
}
/* Create the contact */
if((new_contact = calloc(1, sizeof(au_contact))) == NULL) {
return NULL;
}
if((new_contact->name = strdup(name)) == NULL) {
au_free_contact(new_contact);
return NULL;
}
/* Add it to the list of contacts */
if(0 == au_array_append_member(contact_list, (void *)new_contact)) {
au_free_contact(new_contact);
return NULL;
}
return new_contact;
}
void au_free_contact_void(void *contact) {
au_free_contact((au_contact *)contact);
}
void au_free_contact(au_contact *contact) {
if(NULL == contact) return;
if(NULL != contact->name) free(contact->name);
free(contact);
return;
}
int au_cmp_contacts(const void *a, const void *b) {
au_contact *contacta = (au_contact *)a;
au_contact *contactb = (au_contact *)b;
return strcmp(contacta->name, contactb->name);
}
au_contact *au_find_contact(au_array *contact_list, char *name) {
au_contact key;
void *found;
if(NULL == contact_list) return NULL;
key.name = name;
found = (au_contact *)au_find_in_array(contact_list, (void *)&key,
au_cmp_contacts);
if(NULL == found) return NULL;
return *(au_contact **)found;
}
au_array *au_init_array(char *label) {
au_array *array;
if((array = calloc(1, sizeof(au_array))) == NULL) {
return NULL;
}
array->label = NULL;
if(NULL != label) {
if((array->label = strdup(label)) == NULL) {
free(array);
return NULL;
}
}
array->size = 0;
array->count = 0;
array->members = (void **)NULL;
array->new = 0;
return array;
}
void au_free_array(au_array *array, void(*datafree)(void *)) {
int x;
if(NULL == array) return;
if(NULL != array->label) free(array->label);
for(x = 0; x < array->count; x++) {
if((NULL != datafree) && (NULL != array->members[x])) {
datafree(array->members[x]);
}
}
if(NULL != array->members) free(array->members);
free(array);
return;
}
int au_array_append_member(au_array *array, void *member) {
/* Check whether the list needs to be grown before adding the member */
if(array->count == array->size) {
/* Need to grow the list */
if(0 == array->count) {
/* Never had any members */
if((array->members = (void **)calloc(AU_INITIAL_LIST_SIZE,
sizeof(void *))) == NULL) {
return 0;
}
array->size = AU_INITIAL_LIST_SIZE;
}
else {
/* Double the size of the list */
if((array->members = (void **)realloc(array->members,
sizeof(void *) * array->size * 2)) == NULL) {
return 0;
}
array->size *= 2;
}
}
array->members[array->count] = member;
array->new++; /* Number of appends since last sort */
array->count++;
return 1;
}
void au_sort_array(au_array *array, int(*cmp)(const void *, const void *)) {
/* TODO: Use array->new to determine whether to do a quick sort or a
bubble sort */
qsort(array->members, array->count, sizeof(void *), cmp);
array->new = 0;
}
void *au_find_in_array(au_array *array, void *key,
int(*cmp)(const void *, const void *)) {
return bsearch(&key, array->members, array->count, sizeof(void *), cmp);
}
au_linked_list *au_init_list(char *label) {
au_linked_list *list;
if((list = calloc(1, sizeof(au_linked_list))) == NULL) {
return NULL;
}
list->label = NULL;
if(NULL != label) {
if((list->label = strdup(label)) == NULL) {
free(list);
return NULL;
}
}
list->head = (au_node *)0;
list->last_new = (au_node *)0;
return list;
}
au_node *au_list_add_node(au_linked_list *list, void *data,
int(*cmp)(const void *, const void *)) {
au_node *new_node;
au_node *temp_node;
/* Create the new node */
if((new_node = calloc(1, sizeof(au_node))) == NULL) {
return NULL;
}
new_node->data = data;
new_node->next = NULL;
/* Add it to the list */
if(NULL == list->head) {
/* If the list is empty, add this node as the only item in the list */
list->head = new_node;
}
else if(cmp(&(list->last_new->data), &(new_node->data)) <= 0) {
/* The new node goes somewhere on the list downstream of the most
recently added node */
if(NULL == list->last_new->next) {
/* If the most recently added node is the last one on the list,
append the list with this node */
list->last_new->next = new_node;
}
else if(cmp(&(list->last_new->next->data), &(new_node->data)) > 0) {
/* If the next node is "greater than" the new node, the new
node goes here */
new_node->next = list->last_new->next;
list->last_new->next = new_node;
}
else {
/* Visit each node downstream in the list until we reach the
end of the list or until we find a node whose next
node is "greater than" the new node */
temp_node = list->last_new;
while((NULL != temp_node->next) && (cmp(&(temp_node->next->data),
&(new_node->data)) <= 0)) {
temp_node = temp_node->next;
}
if(NULL == temp_node->next) {
/* If we reach the end of the list, the new node gets
appended */
temp_node->next = new_node;
}
else {
/* Otherwise, the new node gets inserted here */
new_node->next = temp_node->next;
temp_node->next = new_node;
}
}
}
else {
/* The new node is "less than" the last new node. Visit each node
starting at the beginning of the list until we reach the end of
the list (which shouldn't happen because that case was covered
earlier) or we find a node whose next node is is "greater than"
the new node. */
temp_node = list->head;
while((NULL != temp_node->next) && (cmp(&(temp_node->next->data),
&(new_node->data)) <= 0)) {
temp_node = temp_node->next;
}
if(temp_node == list->head) {
/* We insert at the beginning of the list */
new_node->next = list->head;
list->head = new_node;
}
else if(NULL == temp_node->next) {
/* If we reach the end of the list, the new node gets appended */
temp_node->next = new_node;
}
else {
/* Otherwise, the new node gets inserted here */
new_node->next = temp_node->next;
temp_node->next = new_node;
}
}
list->last_new = new_node;
return new_node;
}
void au_empty_list(au_linked_list *list, void(*datafree)(void *)) {
au_node *temp_node1;
au_node *temp_node2;
temp_node1 = list->head;
while(NULL != temp_node1) {
temp_node2 = temp_node1->next;
if((NULL != datafree) && (NULL != temp_node1->data)) {
datafree(temp_node1->data);
}
free(temp_node1);
temp_node1 = temp_node2;
}
list->head = NULL;
list->last_new = NULL;
}
void au_free_list(au_linked_list *list, void(*datafree)(void *)) {
if(NULL == list) return;
if(NULL != list->label) free(list->label);
au_empty_list(list, datafree);
free(list);
}