/*
+-------------------------------------------------------------------------+
| Copyright (C) 2002-2006 The Cacti Group |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301, USA |
| |
+-------------------------------------------------------------------------+
| cactid: a backend data gatherer for cacti |
+-------------------------------------------------------------------------+
| This poller would not have been possible without: |
| - Larry Adams (current development and enhancements) |
| - Rivo Nurges (rrd support, mysql poller cache, misc functions) |
| - RTG (core poller code, pthreads, snmp, autoconf examples) |
| - Brady Alleman/Doug Warner (threading ideas, implimentation details) |
+-------------------------------------------------------------------------+
| - Cacti - http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/
#include "common.h"
#include "cactid.h"
/*! \fn void *child(void *arg)
* \brief function is called via the fork command and initiates a poll of a host
* \param arg a pointer to an integer point to the host_id to be polled
*
* This function will call the primary Cactid polling function to poll a host
* and then reduce the number of active threads by one so that the next host
* can be polled.
*
*/
void *child(void *arg) {
int host_id = *(int *) arg;
CACTID_LOG_DEBUG(("DEBUG: In Poller, About to Start Polling of Host\n"));
poll_host(host_id);
thread_mutex_lock(LOCK_THREAD);
active_threads--;
thread_mutex_unlock(LOCK_THREAD);
CACTID_LOG_DEBUG(("DEBUG: The Value of Active Threads is %i\n" ,active_threads));
pthread_exit(0);
exit(0);
}
/*! \fn void poll_host(int host_id)
* \brief core Cactid function that polls a host
* \param host_id integer value for the host_id from the hosts table in Cacti
*
* This function is core to Cactid. It will take a host_id and then poll it.
*
* Prior to the poll, the system will ping the host to verifiy that it is up.
* In addition, the system will check to see if any reindexing of data query's
* is required.
*
* If reindexing is required, the Cacti poller.php function will spawn that
* reindexing process.
*
* In the case of hosts that require reindexing because of a sysUptime
* rollback, Cactid will store an unknown (NaN) value for all objects to prevent
* spikes in the graphs.
*
* With regard to snmp calls, if the host has multiple snmp agents running
* Cactid will re-initialize the snmp session and poll under those new ports
* as the host poller_items table dictates.
*
*/
void poll_host(int host_id) {
char query1[BUFSIZE];
char query2[BUFSIZE];
char *query3;
char query4[BUFSIZE];
char query5[BUFSIZE];
char query6[BUFSIZE];
char query7[BUFSIZE];
char query8[BUFSIZE];
char errstr[BUFSIZE];
char *sysUptime;
char result_string[BUFSIZE];
int num_rows;
int assert_fail = 0;
int spike_kill = 0;
int rows_processed = 0;
int i;
int j;
int num_oids = 0;
int snmp_poller_items = 0;
int out_buffer;
int php_process;
char *poll_result = NULL;
char *host_time = NULL;
char update_sql[BUFSIZE];
char temp_result[BUFSIZE];
int last_snmp_version = 0;
int last_snmp_port = 0;
char last_snmp_username[50];
char last_snmp_password[50];
/* reindex shortcuts to speed polling */
int previous_assert_failure = FALSE;
int last_data_query_id = 0;
int perform_assert = TRUE;
int new_buffer = TRUE;
reindex_t *reindex;
host_t *host;
ping_t *ping;
target_t *poller_items;
snmp_oids_t *snmp_oids;
MYSQL mysql;
MYSQL_RES *result;
MYSQL_ROW row;
#ifndef OLD_MYSQL
mysql_thread_init();
#endif
db_connect(set.dbdb, &mysql);
/* allocate host and ping structures with appropriate values */
if (!(host = (host_t *) malloc(sizeof(host_t)))) {
die("ERROR: Fatal malloc error: poller.c host struct!\n");
}
memset(host, 0, sizeof(host));
if (!(ping = (ping_t *) malloc(sizeof(ping_t)))) {
die("ERROR: Fatal malloc error: poller.c ping struct!\n");
}
memset(ping, 0, sizeof(ping_t));
if (!(reindex = (reindex_t *) malloc(sizeof(reindex_t)))) {
die("ERROR: Fatal malloc error: poller.c reindex poll!\n");
}
memset(reindex, 0, sizeof(reindex_t));
if (!(sysUptime = (char *) malloc(BUFSIZE))) {
die("ERROR: Fatal malloc error: poller.c sysUptime\n");
}
memset(sysUptime, 0, BUFSIZE);
/* initialize query strings */
snprintf(query1, sizeof(query1),
"SELECT action, hostname, snmp_community, "
"snmp_version, snmp_username, snmp_password, "
"rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, "
"rrd_num, snmp_port, snmp_timeout"
" FROM poller_item"
" WHERE host_id=%i"
" ORDER BY arg1", host_id);
snprintf(query2, sizeof(query2),
"SELECT id, hostname, snmp_community, "
"snmp_username, snmp_password, snmp_version, "
"snmp_port, snmp_timeout, status, "
"status_event_count, status_fail_date, "
"status_rec_date, status_last_error, "
"min_time, max_time, cur_time, avg_time, "
"total_polls, failed_polls, availability "
" FROM host"
" WHERE id=%i", host_id);
snprintf(query4, sizeof(query4),
"SELECT data_query_id, action, op, assert_value, arg1"
" FROM poller_reindex"
" WHERE host_id=%i", host_id);
snprintf(query5, sizeof(query5),
"SELECT action, hostname, snmp_community, snmp_version, "
"snmp_username, snmp_password, rrd_name, "
"rrd_path, arg1, arg2, arg3, local_data_id, "
"rrd_num, snmp_port, snmp_timeout"
" FROM poller_item"
" WHERE host_id=%i and rrd_next_step <=0"
" ORDER by rrd_path,rrd_name", host_id);
snprintf(query6, sizeof(query6),
"UPDATE poller_item"
" SET rrd_next_step=rrd_next_step-%i"
" WHERE host_id=%i", set.poller_interval, host_id);
snprintf(query7, sizeof(query7),
"UPDATE poller_item"
" SET rrd_next_step=rrd_step-%i"
" WHERE rrd_next_step < 0 and host_id=%i",
set.poller_interval, host_id);
snprintf(query8, sizeof(query8),
"INSERT INTO poller_output"
" (local_data_id, rrd_name, time, output) VALUES");
/* get the host polling time */
host_time = get_host_poll_time();
/* initialize the ping structure variables */
snprintf(ping->ping_status, sizeof(ping->ping_status)-1, "down");
snprintf(ping->ping_response, sizeof(ping->ping_response)-1, "Ping not performed due to setting.");
snprintf(ping->snmp_status, sizeof(ping->snmp_status)-1, "down");
snprintf(ping->snmp_response, sizeof(ping->snmp_response)-1, "SNMP not performed due to setting or ping result");
if (host_id) {
/* get data about this host */
result = db_query(&mysql, query2);
num_rows = (int)mysql_num_rows(result);
if (num_rows != 1) {
CACTID_LOG(("Host[%i] ERROR: Unknown Host ID", host_id));
mysql_free_result(result);
#ifndef OLD_MYSQL
mysql_thread_end();
#endif
mysql_close(&mysql);
return;
}
row = mysql_fetch_row(result);
/* populate host structure */
host->ignore_host = 0;
host->id = atoi(row[0]);
host->hostname[0] = '\0';
host->snmp_community[0] = '\0';
host->snmp_username[0] = '\0';
host->snmp_password[0] = '\0';
if (row[1] != NULL) STRNCOPY(host->hostname, row[1]);
if (row[2] != NULL) STRNCOPY(host->snmp_community, row[2]);
if (row[3] != NULL) STRNCOPY(host->snmp_username, row[3]);
if (row[4] != NULL) STRNCOPY(host->snmp_password, row[4]);
host->snmp_version = atoi(row[5]);
host->snmp_port = atoi(row[6]);
host->snmp_timeout = atoi(row[7]);
host->status = atoi(row[8]);
host->status_event_count = atoi(row[9]);
STRNCOPY(host->status_fail_date, row[10]);
STRNCOPY(host->status_rec_date, row[11]);
host->status_last_error[0] = '\0';
if (row[12] != NULL) STRNCOPY(host->status_last_error, row[12]);
host->min_time = atof(row[13]);
host->max_time = atof(row[14]);
host->cur_time = atof(row[15]);
host->avg_time = atof(row[16]);
host->total_polls = atoi(row[17]);
host->failed_polls = atoi(row[18]);
host->availability = atof(row[19]);
if (((host->snmp_version <= 2) && (strlen(host->snmp_community) > 0)) || (host->snmp_version == 3)) {
host->snmp_session = snmp_host_init(host->id, host->hostname, host->snmp_version, host->snmp_community,
host->snmp_username, host->snmp_password, host->snmp_port, host->snmp_timeout);
}else{
host->snmp_session = NULL;
}
/* save snmp status data for future use */
last_snmp_port = host->snmp_port;
last_snmp_version = host->snmp_version;
snprintf(last_snmp_username, sizeof(last_snmp_username)-1, "%s", host->snmp_username);
snprintf(last_snmp_password, sizeof(last_snmp_password)-1, "%s", host->snmp_password);
/* perform a check to see if the host is alive by polling it's SysDesc
* if the host down from an snmp perspective, don't poll it.
* function sets the ignore_host bit */
if ((set.availability_method == AVAIL_SNMP) && (strlen(host->snmp_community) == 0)) {
host->ignore_host = 0;
update_host_status(HOST_UP, host, ping, set.availability_method);
CACTID_LOG_MEDIUM(("Host[%i] No host availability check possible for '%s'\n", host->id, host->hostname));
}else{
if (ping_host(host, ping) == HOST_UP) {
host->ignore_host = 0;
update_host_status(HOST_UP, host, ping, set.availability_method);
}else{
host->ignore_host = 1;
update_host_status(HOST_DOWN, host, ping, set.availability_method);
}
}
/* update host table */
snprintf(update_sql, sizeof(update_sql)-1, "update host set status='%i', status_event_count='%i', status_fail_date='%s', status_rec_date='%s', status_last_error='%s', min_time='%f', max_time='%f', cur_time='%f', avg_time='%f', total_polls='%i', failed_polls='%i', availability='%.4f' where id='%i'",
host->status,
host->status_event_count,
host->status_fail_date,
host->status_rec_date,
host->status_last_error,
host->min_time,
host->max_time,
host->cur_time,
host->avg_time,
host->total_polls,
host->failed_polls,
host->availability,
host->id);
db_insert(&mysql, update_sql);
}else{
host->id = 0;
host->ignore_host = 0;
}
/* do the reindex check for this host if not script based */
if ((!host->ignore_host) && (host_id)) {
result = db_query(&mysql, query4);
num_rows = (int)mysql_num_rows(result);
if (num_rows > 0) {
CACTID_LOG_DEBUG(("Host[%i] RECACHE: Processing %i items in the auto reindex cache for '%s'\n", host->id, num_rows, host->hostname));
while ((row = mysql_fetch_row(result))) {
assert_fail = FALSE;
reindex->data_query_id = atoi(row[0]);
reindex->action = atoi(row[1]);
if (row[2] != NULL) snprintf(reindex->op, sizeof(reindex->op)-1, "%s", row[2]);
if (row[3] != NULL) snprintf(reindex->assert_value, sizeof(reindex->assert_value)-1, "%s", row[3]);
if (row[4] != NULL) snprintf(reindex->arg1, sizeof(reindex->arg1)-1, "%s", row[4]);
/* shortcut assertion checks if a data query reindex has already been queued */
if ((last_data_query_id == reindex->data_query_id) &&
(!previous_assert_failure)) {
perform_assert = TRUE;
}else if (last_data_query_id != reindex->data_query_id) {
last_data_query_id = reindex->data_query_id;
perform_assert = TRUE;
previous_assert_failure = FALSE;
}else{
perform_assert = FALSE;
}
if (perform_assert) {
switch(reindex->action) {
case POLLER_ACTION_SNMP: /* snmp */
/* check to see if you are checking uptime */
if (!strcmp(reindex->arg1,".1.3.6.1.2.1.1.3.0")) {
if (strlen(sysUptime) > 0) {
if (!(poll_result = (char *) malloc(BUFSIZE))) {
die("ERROR: Fatal malloc error: poller.c poll_result\n");
}
memset(poll_result, 0, BUFSIZE);
snprintf(poll_result, BUFSIZE-1, "%s", sysUptime);
}else{
poll_result = snmp_get(host, reindex->arg1);
snprintf(sysUptime, BUFSIZE-1, "%s", poll_result);
}
}else{
poll_result = snmp_get(host, reindex->arg1);
}
break;
case POLLER_ACTION_SCRIPT: /* script (popen) */
poll_result = exec_poll(host, reindex->arg1);
break;
}
if (!(query3 = (char *)malloc(BUFSIZE))) {
die("ERROR: Fatal malloc error: poller.c reindex insert!\n");
}
memset(query3, 0, BUFSIZE);
/* assume ok if host is up and result wasn't obtained */
if ((IS_UNDEFINED(poll_result)) || (STRIMATCH(poll_result, "No Such Instance"))) {
assert_fail = FALSE;
}else if ((!strcmp(reindex->op, "=")) && (strcmp(reindex->assert_value,poll_result))) {
CACTID_LOG_HIGH(("Host[%i] ASSERT: '%s' .eq. '%s' failed. Recaching host '%s', data query #%i\n", host->id, reindex->assert_value, poll_result, host->hostname, reindex->data_query_id));
snprintf(query3, BUFSIZE, "replace into poller_command (poller_id, time, action,command) values (0, NOW(), %i, '%i:%i')", POLLER_COMMAND_REINDEX, host->id, reindex->data_query_id);
db_insert(&mysql, query3);
assert_fail = TRUE;
previous_assert_failure = TRUE;
}else if ((!strcmp(reindex->op, ">")) && (strtoll(reindex->assert_value, (char **)NULL, 10) < strtoll(poll_result, (char **)NULL, 10))) {
CACTID_LOG_HIGH(("Host[%i] ASSERT: '%s' .gt. '%s' failed. Recaching host '%s', data query #%i\n", host->id, reindex->assert_value, poll_result, host->hostname, reindex->data_query_id));
snprintf(query3, BUFSIZE, "replace into poller_command (poller_id, time, action, command) values (0, NOW(), %i, '%i:%i')", POLLER_COMMAND_REINDEX, host->id, reindex->data_query_id);
db_insert(&mysql, query3);
assert_fail = TRUE;
previous_assert_failure = TRUE;
}else if ((!strcmp(reindex->op, "<")) && (strtoll(reindex->assert_value, (char **)NULL, 10) > strtoll(poll_result, (char **)NULL, 10))) {
CACTID_LOG_HIGH(("Host[%i] ASSERT: '%s' .lt. '%s' failed. Recaching host '%s', data query #%i\n", host->id, reindex->assert_value, poll_result, host->hostname, reindex->data_query_id));
snprintf(query3, BUFSIZE, "replace into poller_command (poller_id, time, action, command) values (0, NOW(), %i, '%i:%i')", POLLER_COMMAND_REINDEX, host->id, reindex->data_query_id);
db_insert(&mysql, query3);
assert_fail = TRUE;
previous_assert_failure = TRUE;
}
/* update 'poller_reindex' with the correct information if:
* 1) the assert fails
* 2) the OP code is > or < meaning the current value could have changed without causing
* the assert to fail */
if ((assert_fail) || (!strcmp(reindex->op, ">")) || (!strcmp(reindex->op, "<"))) {
snprintf(query3, 254, "update poller_reindex set assert_value='%s' where host_id='%i' and data_query_id='%i' and arg1='%s'", poll_result, host_id, reindex->data_query_id, reindex->arg1);
db_insert(&mysql, query3);
if ((assert_fail) && (!strcmp(reindex->arg1,".1.3.6.1.2.1.1.3.0"))) {
spike_kill = TRUE;
CACTID_LOG_MEDIUM(("Host[%i] NOTICE: Spike Kill in Effect for '%s'", host_id, host->hostname));
}
}
free(query3);
free(poll_result);
}
}
}
}
/* calculate the number of poller items to poll this cycle */
if (set.poller_interval == 0) {
result = db_query(&mysql, query1);
num_rows = (int)mysql_num_rows(result);
}else{
result = db_query(&mysql, query5);
num_rows = (int)mysql_num_rows(result);
/* update poller_items table for next polling interval */
db_query(&mysql, query6);
db_query(&mysql, query7);
}
if (num_rows > 0) {
/* retreive each hosts polling items from poller cache and load into array */
poller_items = (target_t *) calloc(num_rows, sizeof(target_t));
memset(poller_items, 0, sizeof(target_t)*num_rows);
i = 0;
while ((row = mysql_fetch_row(result))) {
/* initialize monitored object */
poller_items[i].target_id = 0;
poller_items[i].action = atoi(row[0]);
if (row[1] != NULL) snprintf(poller_items[i].hostname, sizeof(poller_items[i].hostname)-1, "%s", row[1]);
if (row[2] != NULL) {
snprintf(poller_items[i].snmp_community, sizeof(poller_items[i].snmp_community)-1, "%s", row[2]);
}else{
poller_items[i].snmp_community[0] = '\0';
}
poller_items[i].snmp_version = atoi(row[3]);
if (row[4] != NULL) {
snprintf(poller_items[i].snmp_username, sizeof(poller_items[i].snmp_username)-1, "%s", row[4]);
}else{
poller_items[i].snmp_username[0] = '\0';
}
if (row[5] != NULL) {
snprintf(poller_items[i].snmp_password, sizeof(poller_items[i].snmp_password)-1, "%s", row[5]);
}else{
poller_items[i].snmp_password[0] = '\0';
}
if (row[6] != NULL) snprintf(poller_items[i].rrd_name, sizeof(poller_items[i].rrd_name)-1, "%s", row[6]);
if (row[7] != NULL) snprintf(poller_items[i].rrd_path, sizeof(poller_items[i].rrd_path)-1, "%s", row[7]);
if (row[8] != NULL) snprintf(poller_items[i].arg1, sizeof(poller_items[i].arg1)-1, "%s", row[8]);
if (row[9] != NULL) snprintf(poller_items[i].arg2, sizeof(poller_items[i].arg2)-1, "%s", row[9]);
if (row[10] != NULL) snprintf(poller_items[i].arg3, sizeof(poller_items[i].arg3)-1, "%s", row[10]);
poller_items[i].local_data_id = atoi(row[11]);
poller_items[i].rrd_num = atoi(row[12]);
poller_items[i].snmp_port = atoi(row[13]);
poller_items[i].snmp_timeout = atoi(row[14]);
SET_UNDEFINED(poller_items[i].result);
if (poller_items[i].action == POLLER_ACTION_SNMP) {
snmp_poller_items++;
}
i++;
}
/* create an array for snmp oids */
snmp_oids = (snmp_oids_t *) calloc(set.snmp_max_get_size, sizeof(snmp_oids_t));
memset(snmp_oids, 0, sizeof(snmp_oids_t)*set.snmp_max_get_size);
i = 0;
while ((i < num_rows) && (!host->ignore_host)) {
if (!host->ignore_host) {
switch(poller_items[i].action) {
case POLLER_ACTION_SNMP: /* raw SNMP poll */
/* initialize or reinitialize snmp as required */
if (host->snmp_session == NULL) {
last_snmp_port = poller_items[i].snmp_port;
last_snmp_version = poller_items[i].snmp_version;
STRNCOPY(last_snmp_username, poller_items[i].snmp_username);
STRNCOPY(last_snmp_password, poller_items[i].snmp_password);
host->snmp_session = snmp_host_init(host->id, poller_items[i].hostname, poller_items[i].snmp_version,
poller_items[i].snmp_community,poller_items[i].snmp_username,
poller_items[i].snmp_password, poller_items[i].snmp_port, poller_items[i].snmp_timeout);
}
/* catch snmp initialization issues */
if (host->snmp_session == NULL) {
host->ignore_host = 1;
break;
}
/* some snmp data changed from poller item to poller item. therefore, poll host and store data */
if ((last_snmp_port != poller_items[i].snmp_port) ||
(last_snmp_version != poller_items[i].snmp_version) ||
(strcmp(last_snmp_username, poller_items[i].snmp_username) != 0) ||
(strcmp(last_snmp_password, poller_items[i].snmp_password) != 0)) {
if (num_oids > 0) {
snmp_get_multi(host, snmp_oids, num_oids);
for (j = 0; j < num_oids; j++) {
if (host->ignore_host) {
CACTID_LOG(("Host[%i] DS[%i] WARNING: SNMP timeout detected [%i ms], ignoring host '%s'\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_timeout, host->hostname));
SET_UNDEFINED(snmp_oids[j].result);
}else {
/* remove double or single quotes from string */
snprintf(temp_result, BUFSIZE-1, "%s", strip_quotes(snmp_oids[j].result));
snprintf(snmp_oids[j].result, sizeof(snmp_oids[j].result)-1, "%s", strip_alpha(temp_result));
/* detect erroneous non-numeric result */
if (!validate_result(snmp_oids[j].result)) {
snprintf(errstr, sizeof(errstr)-1, "%s", snmp_oids[j].result);
CACTID_LOG(("Host[%i] DS[%i] WARNING: Result from SNMP not valid. Partial Result: %.100s...\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, errstr));
SET_UNDEFINED(snmp_oids[j].result);
}
}
snprintf(poller_items[snmp_oids[j].array_position].result, 254, "%s", snmp_oids[j].result);
CACTID_LOG_MEDIUM(("Host[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result));
}
/* clear snmp_oid's memory and reset num_snmps */
memset(snmp_oids, 0, sizeof(snmp_oids_t)*set.snmp_max_get_size);
num_oids = 0;
}
snmp_host_cleanup(host->snmp_session);
host->snmp_session = snmp_host_init(host->id, poller_items[i].hostname, poller_items[i].snmp_version,
poller_items[i].snmp_community,poller_items[i].snmp_username,
poller_items[i].snmp_password, poller_items[i].snmp_port, poller_items[i].snmp_timeout);
last_snmp_port = poller_items[i].snmp_port;
last_snmp_version = poller_items[i].snmp_version;
STRNCOPY(last_snmp_username, poller_items[i].snmp_username);
STRNCOPY(last_snmp_password, poller_items[i].snmp_password);
}
if (num_oids >= set.snmp_max_get_size) {
snmp_get_multi(host, snmp_oids, num_oids);
for (j = 0; j < num_oids; j++) {
if (host->ignore_host) {
CACTID_LOG(("Host[%i] DS[%i] WARNING: SNMP timeout detected [%i ms], ignoring host '%s'\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_timeout, host->hostname));
SET_UNDEFINED(snmp_oids[j].result);
}else {
/* remove double or single quotes from string */
snprintf(temp_result, BUFSIZE-1, "%s", strip_quotes(snmp_oids[j].result));
snprintf(snmp_oids[j].result, sizeof(snmp_oids[j].result)-1, "%s", strip_alpha(temp_result));
/* detect erroneous non-numeric result */
if (!validate_result(snmp_oids[j].result)) {
snprintf(errstr, sizeof(errstr)-1, "%s", snmp_oids[j].result);
CACTID_LOG(("Host[%i] DS[%i] WARNING: Result from SNMP not valid. Partial Result: %.20s...\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, errstr));
SET_UNDEFINED(snmp_oids[j].result);
}
}
snprintf(poller_items[snmp_oids[j].array_position].result, 254, "%s", snmp_oids[j].result);
CACTID_LOG_MEDIUM(("Host[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result));
if (poller_items[snmp_oids[j].array_position].result != NULL) {
/* insert a NaN in place of the actual value if the snmp agent restarts */
if ((spike_kill) && (!strstr(poller_items[snmp_oids[j].array_position].result,":"))) {
SET_UNDEFINED(poller_items[snmp_oids[j].array_position].result);
}
}
}
/* clear snmp_oid's memory and reset num_snmps */
memset(snmp_oids, 0, sizeof(snmp_oids_t)*set.snmp_max_get_size);
num_oids = 0;
}
snprintf(snmp_oids[num_oids].oid, sizeof(snmp_oids[num_oids].oid)-1, "%s", poller_items[i].arg1);
snmp_oids[num_oids].array_position = i;
num_oids++;
break;
case POLLER_ACTION_SCRIPT: /* execute script file */
poll_result = exec_poll(host, poller_items[i].arg1);
/* remove double or single quotes from string */
snprintf(temp_result, BUFSIZE-1, "%s", strip_quotes(poll_result));
snprintf(poller_items[i].result, sizeof(poller_items[i].result)-1, "%s", strip_alpha(temp_result));
free(poll_result);
/* detect erroneous result. can be non-numeric */
if (!validate_result(poller_items[i].result)) {
snprintf(errstr, sizeof(errstr)-1, "%s", poller_items[i].result);
CACTID_LOG(("Host[%i] DS[%i] WARNING: Result from SCRIPT not valid. Partial Result: %.20s...\n", host_id, poller_items[i].local_data_id, errstr));
SET_UNDEFINED(poller_items[i].result);
}
CACTID_LOG_MEDIUM(("Host[%i] DS[%i] SCRIPT: %s, output: %s\n", host_id, poller_items[i].local_data_id, poller_items[i].arg1, poller_items[i].result));
if (poller_items[i].result != NULL) {
/* insert a NaN in place of the actual value if the snmp agent restarts */
if ((spike_kill) && (!strstr(poller_items[i].result,":"))) {
SET_UNDEFINED(poller_items[i].result);
}
}
break;
case POLLER_ACTION_PHP_SCRIPT_SERVER: /* execute script server */
php_process = php_get_process();
poll_result = php_cmd(poller_items[i].arg1, php_process);
/* remove double or single quotes from string */
snprintf(temp_result, BUFSIZE-1, "%s", strip_quotes(poll_result));
snprintf(poller_items[i].result, sizeof(poller_items[i].result)-1, "%s", strip_alpha(temp_result));
free(poll_result);
/* detect erroneous result. can be non-numeric */
if (!validate_result(poller_items[i].result)) {
snprintf(errstr, sizeof(errstr)-1, "%s", poller_items[i].result);
CACTID_LOG(("Host[%i] DS[%i] SS[%i] WARNING: Result from SERVER not valid. Partial Result: %.20s...\n", host_id, poller_items[i].local_data_id, php_process, errstr));
SET_UNDEFINED(poller_items[i].result);
}
CACTID_LOG_MEDIUM(("Host[%i] DS[%i] SS[%i] SERVER: %s, output: %s\n", host_id, poller_items[i].local_data_id, php_process, poller_items[i].arg1, poller_items[i].result));
if (poller_items[i].result != NULL) {
/* insert a NaN in place of the actual value if the snmp agent restarts */
if ((spike_kill) && (!strstr(poller_items[i].result,":"))) {
SET_UNDEFINED(poller_items[i].result);
}
}
break;
default: /* unknown action, generate error */
CACTID_LOG(("Host[%i] DS[%i] ERROR: Unknown Poller Action: %s\n", host_id, poller_items[i].local_data_id, poller_items[i].arg1));
break;
}
}
i++;
rows_processed++;
}
/* process last multi-get request if applicable */
if (num_oids > 0) {
snmp_get_multi(host, snmp_oids, num_oids);
for (j = 0; j < num_oids; j++) {
if (host->ignore_host) {
CACTID_LOG(("Host[%i] DS[%i] WARNING: SNMP timeout detected [%i ms], ignoring host '%s'\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_timeout, host->hostname));
SET_UNDEFINED(snmp_oids[j].result);
}else{
/* remove double or single quotes from string */
snprintf(temp_result, BUFSIZE-1, "%s", strip_quotes(snmp_oids[j].result));
snprintf(snmp_oids[j].result, sizeof(snmp_oids[j].result)-1, "%s", strip_alpha(temp_result));
/* detect erroneous non-numeric result */
if (!validate_result(snmp_oids[j].result)) {
snprintf(errstr, sizeof(errstr)-1, "%s", snmp_oids[j].result);
CACTID_LOG(("Host[%i] DS[%i] WARNING: Result from SNMP not valid. Partial Result: %.20s...\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, errstr));
SET_UNDEFINED(snmp_oids[j].result);
}
}
snprintf(poller_items[snmp_oids[j].array_position].result, 254, "%s", snmp_oids[j].result);
CACTID_LOG_MEDIUM(("Host[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s\n", host_id, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result));
if (poller_items[snmp_oids[j].array_position].result != NULL) {
/* insert a NaN in place of the actual value if the snmp agent restarts */
if ((spike_kill) && (!strstr(poller_items[snmp_oids[j].array_position].result,":"))) {
SET_UNDEFINED(poller_items[snmp_oids[j].array_position].result);
}
}
}
}
/* insert the query results into the database */
if (!(query3 = (char *)malloc(MAX_MYSQL_BUF_SIZE+BUFSIZE))) {
die("ERROR: Fatal malloc error: poller.c query3 oids!\n");
}
query3[0] = '\0';
strncat(query3, query8, sizeof(query8));
out_buffer = strlen(query3);
i = 0;
while (i < rows_processed) {
snprintf(result_string, sizeof(result_string), " (%i,'%s','%s','%s')", poller_items[i].local_data_id, poller_items[i].rrd_name, host_time, poller_items[i].result);
/* if the next element to the buffer will overflow it, write to the database */
if ((out_buffer + strlen(result_string)) >= MAX_MYSQL_BUF_SIZE) {
/* insert the record */
db_insert(&mysql, query3);
/* re-initialize the query buffer */
query3[0] = '\0';
strncat(query3, query8, sizeof(query8));
/* reset the output buffer length */
out_buffer = strlen(query3);
/* set binary, let the system know we are a new buffer */
new_buffer = TRUE;
}
/* if this is our first pass, or we just outputted to the database, need to change the delimeter */
if (new_buffer) {
result_string[0] = ' ';
}else{
result_string[0] = ',';
}
out_buffer = out_buffer + strlen(result_string);
strncat(query3, result_string, strlen(result_string));
new_buffer = FALSE;
i++;
}
/* perform the last insert if there is data to process */
if (out_buffer > strlen(query8)) {
/* insert records into database */
db_insert(&mysql, query3);
}
/* cleanup memory and prepare for function exit */
if (host_id) {
snmp_host_cleanup(host->snmp_session);
}
free(query3);
free(poller_items);
free(snmp_oids);
}
free(host);
free(host_time);
free(reindex);
free(sysUptime);
free(ping);
mysql_free_result(result);
#ifndef OLD_MYSQL
mysql_thread_end();
#endif
mysql_close(&mysql);
CACTID_LOG_DEBUG(("Host[%i] DEBUG: HOST COMPLETE: About to Exit Host Polling Thread Function\n", host_id));
}
/*! \fn int validate_result(char *result)
* \brief validates the output from the polling action is valid
* \param result the value to be checked for legality
*
* This function will poll a specific host using the script pointed to by
* the command variable.
*
* \return TRUE if the result is valid, otherwise FALSE.
*
*/
int validate_result(char *result) {
int space_cnt = 0;
int delim_cnt = 0;
int i;
/* check the easy case first */
if (is_numeric(result)) {
return TRUE;
}else{
/* it must have delimiters */
if (((strstr(result, ":") != 0) || (strstr(result, "!") != 0))) {
if (strstr(result, " ") == 0) {
return TRUE;
}
if (strstr(result, " ") != 0) {
const int len = strlen(result);
for(i=0; i<len; i++) {
if ((result[i] == ':') || (result[i] == '!')) {
delim_cnt = delim_cnt + 1;
}else if (result[i] == ' ') {
space_cnt = space_cnt + 1;
}
}
if (space_cnt+1 == delim_cnt) {
return TRUE;
}else{
return FALSE;
}
}
}
}
return FALSE;
}
/*! \fn char *exec_poll(host_t *current_host, char *command)
* \brief polls a host using a script
* \param current_host a pointer to the current host structure
* \param command the command to be executed
*
* This function will poll a specific host using the script pointed to by
* the command variable.
*
* \return a pointer to a character buffer containing the result.
*
*/
char *exec_poll(host_t *current_host, char *command) {
int cmd_fd;
int bytes_read;
fd_set fds;
int numfds;
double begin_time = 0;
double end_time = 0;
struct timeval timeout;
char *proc_command;
char *result_string;
if (!(result_string = (char *) malloc(BUFSIZE))) {
die("ERROR: Fatal malloc error: poller.c exec_poll!\n");
}
memset(result_string, 0, BUFSIZE);
/* establish timeout of 25 seconds for pipe response */
timeout.tv_sec = set.script_timeout;
timeout.tv_usec = 0;
/* compensate for back slashes in arguments */
proc_command = add_slashes(command, 2);
/* record start time */
begin_time = get_time_as_double();
cmd_fd = nft_popen((char *)proc_command, "r");
free(proc_command);
CACTID_LOG_DEBUG(("Host[%i] DEBUG: The POPEN returned the following File Descriptor %i\n", current_host->id, cmd_fd));
if (cmd_fd >= 0) {
/* Initialize File Descriptors to Review for Input/Output */
FD_ZERO(&fds);
FD_SET(cmd_fd,&fds);
numfds = cmd_fd + 1;
/* wait x seonds for pipe response */
retry:
switch (select(numfds, &fds, NULL, NULL, &timeout)) {
case -1:
switch (errno) {
case EBADF:
CACTID_LOG(("Host[%i] ERROR: One or more of the file descriptor sets specified a file descriptor that is not a valid open file descriptor.\n", current_host->id));
SET_UNDEFINED(result_string);
break;
case EAGAIN:
case EINTR:
#ifndef SOLAR_THREAD
/* take a moment */
usleep(2000);
#endif
/* record end time */
end_time = get_time_as_double();
/* re-establish new timeout value */
timeout.tv_sec = rint(floor(set.script_timeout-(end_time-begin_time)));
timeout.tv_usec = rint((set.script_timeout-(end_time-begin_time)-timeout.tv_sec)*1000000);
if ((end_time - begin_time) < set.script_timeout) {
goto retry;
}else{
CACTID_LOG(("WARNING: A script timed out while processing EINTR's.\n"));
SET_UNDEFINED(result_string);
}
break;
case EINVAL:
CACTID_LOG(("Host[%i] ERROR: Possible invalid timeout specified in select() statement.\n", current_host->id));
SET_UNDEFINED(result_string);
break;
default:
CACTID_LOG(("Host[%i] ERROR: The script/command select() failed\n", current_host->id));
SET_UNDEFINED(result_string);
break;
}
case 0:
CACTID_LOG(("Host[%i] ERROR: The POPEN timed out\n", current_host->id));
SET_UNDEFINED(result_string);
break;
default:
/* get only one line of output, we will ignore the rest */
bytes_read = read(cmd_fd, result_string, BUFSIZE-1);
if (bytes_read > 0) {
result_string[bytes_read] = '\0';
}else{
CACTID_LOG(("Host[%i] ERROR: Empty result [%s]: '%s'\n", current_host->id, current_host->hostname, command));
SET_UNDEFINED(result_string);
}
}
/* close pipe */
nft_pclose(cmd_fd);
}else{
CACTID_LOG(("Host[%i] ERROR: Problem executing POPEN [%s]: '%s'\n", current_host->id, current_host->hostname, command));
SET_UNDEFINED(result_string);
}
return result_string;
}
syntax highlighted by Code2HTML, v. 0.9.1