/*! \fn void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable) * * \brief replacement for the buggy net-snmp.org snprint_value function * * This function format an output buffer with the correct string representation * of an snmp OID result fetched with snmp_get_multi. The buffer pointed to by * the function is modified. * */ void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable) { u_char *buf = NULL; size_t out_len = 0; if ((buf = (u_char *) calloc(buf_len, 1)) != 0) { if (sprint_realloc_value(&buf, &buf_len, &out_len, 1, objid, objidlen, variable)) { snprintf(obuf, buf_len, "%s", buf); }else{ snprintf(obuf, buf_len, "%s [TRUNCATED]", buf); } }else{ SET_UNDEFINED(obuf); } free(buf); }
/*! \fn char *snmp_get_multi(host_t *current_host, snmp_oids_t *snmp_oids, int num_oids) * \brief performs multiple OID snmp_get's in a single network call * * This function will a group of snmp OID's for a host. The host snmp * session must already be established. The function will modify elements of * the snmp_oids array with the results from the snmp api call. * */ void snmp_get_multi(host_t *current_host, snmp_oids_t *snmp_oids, int num_oids) { struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct variable_list *vars = NULL; int status; int i; int max_repetitions = 1; int non_repeaters = 0; int array_count; int index_count; /* get rid of some compiler warnings */ errstat = 0; errindex = 0; struct nameStruct { oid name[MAX_OID_LEN]; size_t name_len; } *name, *namep; /* load up oids */ namep = name = (struct nameStruct *) calloc(num_oids, sizeof(*name)); pdu = snmp_pdu_create(SNMP_MSG_GET); for (i = 0; i < num_oids; i++) { namep->name_len = MAX_OID_LEN; if (!snmp_parse_oid(snmp_oids[i].oid, namep->name, &namep->name_len)) { SPINE_LOG(("Host[%i] ERROR: Problems parsing Multi SNMP OID! (oid: %s)\n", current_host->id, snmp_oids[i].oid)); /* Mark this OID as "bad" */ SET_UNDEFINED(snmp_oids[i].result); }else{ snmp_add_null_var(pdu, namep->name, namep->name_len); } namep++; } status = STAT_DESCRIP_ERROR; /* execute the multi-get request */ retry: status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response); /* liftoff, successful poll, process it!! */ if (status == STAT_SUCCESS) { if (response == NULL) { SPINE_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_get_multi\n")); status = STAT_ERROR; }else{ if (response->errstat == SNMP_ERR_NOERROR) { vars = response->variables; for(i = 0; i < num_oids && vars; i++) { if (!IS_UNDEFINED(snmp_oids[i].result)) { #ifdef USE_NET_SNMP snmp_snprint_value(snmp_oids[i].result, RESULTS_BUFFER, vars->name, vars->name_length, vars); #else sprint_value(snmp_oids[i].result, vars->name, vars->name_length, vars); #endif vars = vars->next_variable; } } }else{ if (response->errindex != 0) { index_count = 1; array_count = 0; /* Find our index against errindex */ while (array_count < num_oids) { if (IS_UNDEFINED(snmp_oids[array_count].result) ) { array_count++; }else{ /* if we have found our error, exit */ if (index_count == response->errindex) { SET_UNDEFINED(snmp_oids[array_count].result); break; } array_count++; index_count++; } } /* remote the invalid OID from the PDU */ pdu = snmp_fix_pdu(response, SNMP_MSG_GET); /* free the previous response */ snmp_free_pdu(response); response = NULL; if (pdu != NULL) { /* retry the request */ goto retry; }else{ /* all OID's errored out so exit cleanly */ status = STAT_SUCCESS; } }else{ status = STAT_DESCRIP_ERROR; } } } } if (status != STAT_SUCCESS) { current_host->ignore_host = 1; for (i = 0; i < num_oids; i++) { SET_UNDEFINED(snmp_oids[i].result); } } if (response != NULL) { snmp_free_pdu(response); } }
/*! \fn char *snmp_getnext(host_t *current_host, char *snmp_oid) * \brief performs a single snmp_getnext for a specific snmp OID * * This function will poll a specific snmp OID for a host. The host snmp * session must already be established. * * \return returns the character representaton of the snmp OID, or "U" if * unsuccessful. * */ char *snmp_getnext(host_t *current_host, char *snmp_oid) { struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct variable_list *vars = NULL; size_t anOID_len = MAX_OID_LEN; oid anOID[MAX_OID_LEN]; int status; char *result_string; if (!(result_string = (char *) malloc(RESULTS_BUFFER))) { die("ERROR: Fatal malloc error: snmp.c snmp_get!"); } result_string[0] = '\0'; status = STAT_DESCRIP_ERROR; if (current_host->snmp_session != NULL) { anOID_len = MAX_OID_LEN; pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); if (!snmp_parse_oid(snmp_oid, anOID, &anOID_len)) { SPINE_LOG(("ERROR: Problems parsing SNMP OID\n")); SET_UNDEFINED(result_string); return result_string; }else{ snmp_add_null_var(pdu, anOID, anOID_len); } /* poll host */ status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response); /* liftoff, successful poll, process it!! */ if (status == STAT_SUCCESS) { if (response == NULL) { SPINE_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_get\n")); SET_UNDEFINED(result_string); status = STAT_ERROR; }else{ if (response->errstat == SNMP_ERR_NOERROR) { vars = response->variables; #ifdef USE_NET_SNMP snprint_value(result_string, RESULTS_BUFFER, anOID, anOID_len, vars); #else sprint_value(result_string, anOID, anOID_len, vars); #endif } } } if (response) { snmp_free_pdu(response); response = NULL; } }else{ status = STAT_DESCRIP_ERROR; } if (status != STAT_SUCCESS) { current_host->ignore_host = TRUE; SET_UNDEFINED(result_string); } return result_string; }
/*! \fn char *php_readpipe(int php_process) * \brief read a line from a PHP Script Server process * \param php_process the PHP Script Server process to obtain output from * * This function will read the output pipe from the PHP Script Server process * and return that string to the Spine thread requesting the output. If for * some reason the PHP Script Server process does not respond in time, it will * be closed using the php_close function, then restarted. * * \return a string pointer to the PHP Script Server response */ char *php_readpipe(int php_process) { fd_set fds; struct timeval timeout; double begin_time = 0; double end_time = 0; char *result_string; int i; char *cp; char *bptr; if (!(result_string = (char *)malloc(BUFSIZE))) { die("ERROR: Fatal malloc error: php.c php_readpipe!"); } result_string[0] = '\0'; /* record start time */ begin_time = get_time_as_double(); /* initialize file descriptors to review for input/output */ FD_ZERO(&fds); FD_SET(php_processes[php_process].php_read_fd,&fds); /* establish timeout value for the PHP script server to respond */ timeout.tv_sec = set.script_timeout; timeout.tv_usec = 0; /* check to see which pipe talked and take action * should only be the READ pipe */ retry: switch (select(FD_SETSIZE, &fds, NULL, NULL, &timeout)) { case -1: switch (errno) { case EBADF: SPINE_LOG(("ERROR: SS[%i] An invalid file descriptor was given in one of the sets.\n", php_process)); 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{ SPINE_LOG(("WARNING: SS[%i] The Script Server script timed out while processing EINTR's.\n", php_process)); } break; case EINVAL: SPINE_LOG(("ERROR: SS[%i] N is negative or the value contained within timeout is invalid.\n", php_process)); break; case ENOMEM: SPINE_LOG(("ERROR: SS[%i] Select was unable to allocate memory for internal tables.\n", php_process)); break; default: SPINE_LOG(("ERROR: SS[%i] Unknown fatal select() error\n", php_process)); break; } SET_UNDEFINED(result_string); /* kill script server because it is misbehaving */ php_close(php_process); php_init(php_process); break; case 0: SPINE_LOG(("WARNING: SS[%i] The PHP Script Server did not respond in time and will therefore be restarted\n", php_process)); SET_UNDEFINED(result_string); /* kill script server because it is misbehaving */ php_close(php_process); php_init(php_process); break; default: bptr = result_string; while (1) { i = read(php_processes[php_process].php_read_fd,bptr,BUFSIZE-(bptr-result_string)); if (i <= 0) { SET_UNDEFINED(result_string); break; } bptr += i; *bptr = '\0'; /* make what we've got into a string */ if ((cp = strstr(result_string,"\n")) > 0) { *cp = '\0'; if ((cp = strstr(result_string,"\r")) > 0) { *cp = '\0'; } break; } if (bptr >= result_string+BUFSIZE) { SPINE_LOG(("ERROR: SS[%i] The Script Server result was longer than the acceptable range\n", php_process)); SET_UNDEFINED(result_string); } } php_processes[php_process].php_state = PHP_READY; } return result_string; }
/*! \fn char *php_cmd(const char *php_command, int php_process) * \brief calls the script server and executes a script command * \param php_command the formatted php script server command * \param php_process the php script server process to call * * This function is called directly by the spine poller when a script server * request has been initiated for a host. It will place the PHP Script Server * command on it's output pipe and then wait the pre-defined timeout period for * a response on the PHP Script Servers output pipe. * * \return pointer to the string results. Must be freed by the parent. * */ char *php_cmd(const char *php_command, int php_process) { char *result_string; char command[BUFSIZE]; ssize_t bytes; int retries = 0; assert(php_command != 0); /* pad command with CR-LF */ snprintf(command, BUFSIZE, "%s\r\n", php_command); /* place lock around mutex */ switch (php_process) { case 0: thread_mutex_lock(LOCK_PHP_PROC_0); break; case 1: thread_mutex_lock(LOCK_PHP_PROC_1); break; case 2: thread_mutex_lock(LOCK_PHP_PROC_2); break; case 3: thread_mutex_lock(LOCK_PHP_PROC_3); break; case 4: thread_mutex_lock(LOCK_PHP_PROC_4); break; case 5: thread_mutex_lock(LOCK_PHP_PROC_5); break; case 6: thread_mutex_lock(LOCK_PHP_PROC_6); break; case 7: thread_mutex_lock(LOCK_PHP_PROC_7); break; case 8: thread_mutex_lock(LOCK_PHP_PROC_8); break; case 9: thread_mutex_lock(LOCK_PHP_PROC_9); break; } /* send command to the script server */ retry: bytes = write(php_processes[php_process].php_write_fd, command, strlen(command)); /* if write status is <= 0 then the script server may be hung */ if (bytes <= 0) { result_string = strdup("U"); SPINE_LOG(("ERROR: SS[%i] PHP Script Server communications lost. Restarting PHP Script Server", php_process)); php_close(php_process); php_init(php_process); /* increment and retry a few times on the next item */ retries++; if (retries < 3) { goto retry; } } else { /* read the result from the php_command */ result_string = php_readpipe(php_process); /* check for a null */ if (!strlen(result_string)) { SET_UNDEFINED(result_string); } } /* unlock around php process */ switch (php_process) { case 0: thread_mutex_unlock(LOCK_PHP_PROC_0); break; case 1: thread_mutex_unlock(LOCK_PHP_PROC_1); break; case 2: thread_mutex_unlock(LOCK_PHP_PROC_2); break; case 3: thread_mutex_unlock(LOCK_PHP_PROC_3); break; case 4: thread_mutex_unlock(LOCK_PHP_PROC_4); break; case 5: thread_mutex_unlock(LOCK_PHP_PROC_5); break; case 6: thread_mutex_unlock(LOCK_PHP_PROC_6); break; case 7: thread_mutex_unlock(LOCK_PHP_PROC_7); break; case 8: thread_mutex_unlock(LOCK_PHP_PROC_8); break; case 9: thread_mutex_unlock(LOCK_PHP_PROC_9); break; } return result_string; }
/*! \fn char *snmp_get_multi(host_t *current_host, snmp_oids_t *snmp_oids, int num_oids) * \brief performs multiple OID snmp_get's in a single network call * * This function will a group of snmp OID's for a host. The host snmp * session must already be established. The function will modify elements of * the snmp_oids array with the results from the snmp api call. * */ void snmp_get_multi(host_t *current_host, snmp_oids_t *snmp_oids, int num_oids) { struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct variable_list *vars = NULL; int status; int i; int max_repetitions = 1; int non_repeaters = 0; struct nameStruct { oid name[MAX_OID_LEN]; size_t name_len; } *name, *namep; /* load up oids */ namep = name = (struct nameStruct *) calloc(num_oids, sizeof(*name)); pdu = snmp_pdu_create(SNMP_MSG_GET); for (i = 0; i < num_oids; i++) { namep->name_len = MAX_OID_LEN; if (!snmp_parse_oid(snmp_oids[i].oid, namep->name, &namep->name_len)) { CACTID_LOG(("Host[%i] ERROR: Problems parsing Multi SNMP OID! (oid: %s)\n", current_host->id, snmp_oids[i].oid)); /* Mark this OID as "bad" */ SET_UNDEFINED(snmp_oids[i].result); }else{ snmp_add_null_var(pdu, namep->name, namep->name_len); } namep++; } status = STAT_DESCRIP_ERROR; /* execute the multi-get request */ retry: status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response); /* liftoff, successful poll, process it!! */ if (status == STAT_SUCCESS) { if (response == NULL) { CACTID_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_get_multi\n")); status = STAT_ERROR; }else{ if (response->errstat == SNMP_ERR_NOERROR) { vars = response->variables; for(i = 0; i < num_oids && vars; i++) { if (!IS_UNDEFINED(snmp_oids[i].result)) { #ifdef USE_NET_SNMP snmp_snprint_value(snmp_oids[i].result, sizeof(snmp_oids[i].result), vars->name, vars->name_length, vars); #else sprint_value(snmp_oids[i].result, vars->name, vars->name_length, vars); #endif vars = vars->next_variable; } } }else{ if (response->errindex != 0) { /* removed errored OID and then retry */ int count; /* Find our index against errindex */ count = 0; for(i = 0; i < num_oids && count < response->errindex; i++) { if ( ! IS_UNDEFINED(snmp_oids[i].result) ) { count++; } } i--; SET_UNDEFINED(snmp_oids[i].result); for (count = 1, vars = response->variables; vars && count != response->errindex; vars = vars->next_variable, count++) { } pdu = snmp_fix_pdu(response, SNMP_MSG_GET); snmp_free_pdu(response); response = NULL; if (pdu != NULL) { goto retry; }else{ status = STAT_DESCRIP_ERROR; } }else{ status = STAT_DESCRIP_ERROR; } } } } if (status != STAT_SUCCESS) { current_host->ignore_host = 1; for (i = 0; i < num_oids; i++) { SET_UNDEFINED(snmp_oids[i].result); } } if (response != NULL) { snmp_free_pdu(response); } }