/* +-------------------------------------------------------------------------+ | 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; iid, 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; }