/*! \fn void die(const char *format, ...) * \brief a method to end Spine while returning the fatal error to stderr * * Given a printf-style argument list, format it to the standard * error, append a newline, then exit Spine. * */ void die(const char *format, ...) { va_list args; char logmessage[BUFSIZE]; char flogmessage[BUFSIZE]; va_start(args, format); vsprintf(logmessage, format, args); va_end(args); if (set.logfile_processed) { if (set.parent_fork == SPINE_PARENT) { snprintf(flogmessage, BUFSIZE, "%s (Spine parent)", logmessage); }else{ snprintf(flogmessage, BUFSIZE, "%s (Spine thread)", logmessage); } }else{ snprintf(flogmessage, BUFSIZE, "%s (Spine init)", logmessage); } SPINE_LOG(("%s", flogmessage)); if (set.parent_fork == SPINE_PARENT) { if (set.php_initialized) { php_close(PHP_INIT); } } exit(set.exit_code); }
/*! \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]; int write_status; 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 */ write_status = write(php_processes[php_process].php_write_fd, command, strlen(command)); /* if write status is <= 0 then the script server may be hung */ if (write_status <= 0) { result_string = strdup("U"); SPINE_LOG(("ERROR: SS[%i] PHP Script Server communications lost.\n", php_process)); php_close(php_process); }else{ /* read the result from the php_command */ result_string = php_readpipe(php_process); } /* 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; }
int main(int argc, char *argv[]) { struct timeval now; char *conf_file = NULL; double begin_time, end_time; int num_rows; int device_counter = 0; int last_active_threads = 0; long int THREAD_SLEEP = 100000; time_t nowbin; const struct tm *nowstruct; pthread_t* threads = NULL; pthread_attr_t attr; pthread_mutexattr_t mutexattr; int* ids = NULL; MYSQL mysql; MYSQL_RES *result = NULL; MYSQL_ROW mysql_row; int canexit = 0; int host_id; int i; int mutex_status = 0; int thread_status = 0; char result_string[BUFSIZE] = ""; char logmessage[LOGSIZE]; /* set start time for cacti */ gettimeofday(&now, NULL); begin_time = (double) now.tv_usec / 1000000 + now.tv_sec; /* get time for poller_output table */ if (time(&nowbin) == (time_t) - 1) printf("ERROR: Could not get time of day from time()\n"); nowstruct = localtime(&nowbin); if (strftime(start_datetime, sizeof(start_datetime), "%Y-%m-%d %H:%M:%S", nowstruct) == (size_t) 0) printf("ERROR: Could not get string from strftime()\n"); set.verbose = POLLER_VERBOSITY_HIGH; /* get static defaults for system */ config_defaults(&set); /* scan arguments for errors */ if ((argc != 1) && (argc != 3)) { printf("ERROR: Cactid requires either 0 or 2 input parameters\n"); printf("USAGE: <cactidpath>/cactid [start_id end_id]\n"); exit(-1); } /* return error if the first arg is greater than the second */ if (argc == 3) { if (atol(argv[1]) > atol(argv[2])) { printf("ERROR: Invalid row specifications. First row must be less than the second row\n"); exit(-2); } } /* read configuration file to establish local environment */ if (conf_file) { if ((read_cactid_config(conf_file, &set)) < 0) { printf("ERROR: Could not read config file: %s\n", conf_file); exit(-3); } }else{ conf_file = malloc(BUFSIZE); if (!conf_file) { printf("ERROR: Fatal malloc error!\n"); exit(-1); } for(i=0;i<CONFIG_PATHS;i++) { snprintf(conf_file, BUFSIZE, "%s%s", config_paths[i], DEFAULT_CONF_FILE); if (read_cactid_config(conf_file, &set) >= 0) { break; } if (i == CONFIG_PATHS-1) { snprintf(conf_file, BUFSIZE, "%s%s", config_paths[0], DEFAULT_CONF_FILE); } } } /* read settings table from the database to further establish environment */ read_config_options(&set); /* set the poller ID, stub for next version */ set.poller_id = 0; if (set.verbose == POLLER_VERBOSITY_DEBUG) { snprintf(logmessage, LOGSIZE, "CACTID: Version %s starting\n", VERSION); cacti_log(logmessage); } else { printf("CACTID: Version %s starting\n", VERSION); } /* connect to database */ db_connect(set.dbdb, &mysql); /* initialize SNMP */ init_snmp("cactid"); /* initialize PHP */ php_init(); /* get the id's to poll */ switch (argc) { case 1: result = db_query(&mysql, "SELECT id FROM host WHERE disabled='' ORDER BY id"); break; case 3: snprintf(result_string, sizeof(result_string), "SELECT id FROM host WHERE (disabled='' and (id >= %s and id <= %s)) ORDER BY id\0", argv[1], argv[2]); result = db_query(&mysql, result_string); break; default: break; } num_rows = mysql_num_rows(result); threads = (pthread_t *)malloc(num_rows * sizeof(pthread_t)); ids = (int *)malloc(num_rows * sizeof(int)); /* initialize threads and mutexes */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); init_mutexes(); if (set.verbose == POLLER_VERBOSITY_DEBUG) { snprintf(logmessage, LOGSIZE, "DEBUG: Initial Value of Active Threads is %i\n", active_threads); cacti_log(logmessage); } /* loop through devices until done */ while (device_counter < num_rows) { mutex_status = thread_mutex_trylock(LOCK_THREAD); switch (mutex_status) { case 0: if (last_active_threads != active_threads) { last_active_threads = active_threads; } while ((active_threads < set.threads) && (device_counter < num_rows)) { mysql_row = mysql_fetch_row(result); host_id = atoi(mysql_row[0]); ids[device_counter] = host_id; /* create child process */ thread_status = pthread_create(&threads[device_counter], &attr, child, &ids[device_counter]); switch (thread_status) { case 0: if (set.verbose == POLLER_VERBOSITY_DEBUG) { snprintf(logmessage, LOGSIZE, "DEBUG: Valid Thread to be Created\n"); cacti_log(logmessage); } device_counter++; active_threads++; if (set.verbose == POLLER_VERBOSITY_DEBUG) { snprintf(logmessage, LOGSIZE, "DEBUG: The Value of Active Threads is %i\n", active_threads); cacti_log(logmessage); } break; case EAGAIN: snprintf(logmessage, LOGSIZE, "ERROR: The System Lacked the Resources to Create a Thread\n"); cacti_log(logmessage); break; case EFAULT: snprintf(logmessage, LOGSIZE, "ERROR: The Thread or Attribute Was Invalid\n"); cacti_log(logmessage); break; case EINVAL: snprintf(logmessage, LOGSIZE, "ERROR: The Thread Attribute is Not Initialized\n"); cacti_log(logmessage); break; default: snprintf(logmessage, LOGSIZE, "ERROR: Unknown Thread Creation Error\n"); cacti_log(logmessage); break; } usleep(THREAD_SLEEP); } thread_mutex_unlock(LOCK_THREAD); break; case EBUSY: snprintf(logmessage, LOGSIZE, "ERROR: Deadlock Occured\n"); cacti_log(logmessage); break; case EINVAL: snprintf(logmessage, LOGSIZE, "ERROR: Attempt to Unlock an Uninitialized Mutex\n"); cacti_log(logmessage); break; case EFAULT: snprintf(logmessage, LOGSIZE, "ERROR: Attempt to Unlock an Invalid Mutex\n"); cacti_log(logmessage); break; default: snprintf(logmessage, LOGSIZE, "ERROR: Unknown Mutex Lock Error Code Returned\n"); cacti_log(logmessage); break; } usleep(THREAD_SLEEP); } /* wait for all threads to complete */ while (canexit == 0) { if (thread_mutex_trylock(LOCK_THREAD) != EBUSY) { if (last_active_threads != active_threads) { last_active_threads = active_threads; } if (active_threads == 0) { canexit = 1; } thread_mutex_unlock(LOCK_THREAD); } usleep(THREAD_SLEEP); } /* print out stats */ gettimeofday(&now, NULL); /* update the db for |data_time| on graphs */ db_insert(&mysql, "replace into settings (name,value) values ('date',NOW())"); db_insert(&mysql, "insert into poller_time (poller_id, start_time, end_time) values (0, NOW(), NOW())"); /* cleanup and exit program */ pthread_attr_destroy(&attr); pthread_mutexattr_destroy(&mutexattr); if (set.verbose == POLLER_VERBOSITY_DEBUG) { cacti_log("DEBUG: Thread Cleanup Complete\n"); } /* close the php script server */ php_close(); if (set.verbose == POLLER_VERBOSITY_DEBUG) { cacti_log("DEBUG: PHP Script Server Pipes Closed\n"); } /* free malloc'd variables */ free(threads); free(ids); free(conf_file); if (set.verbose == POLLER_VERBOSITY_DEBUG) { cacti_log("DEBUG: Allocated Variable Memory Freed\n"); } /* close mysql */ mysql_free_result(result); mysql_close(&mysql); if (set.verbose == POLLER_VERBOSITY_DEBUG) { cacti_log("DEBUG: MYSQL Free & Close Completed\n"); } /* finally add some statistics to the log and exit */ end_time = (double) now.tv_usec / 1000000 + now.tv_sec; if ((set.verbose >= POLLER_VERBOSITY_MEDIUM) && (argc != 1)) { snprintf(logmessage, LOGSIZE, "Time: %.4f s, Threads: %i, Hosts: %i\n", (end_time - begin_time), set.threads, num_rows); cacti_log(logmessage); } else { printf("CACTID: Execution Time: %.4f s, Threads: %i, Hosts: %i\n", (end_time - begin_time), set.threads, num_rows); } exit(0); }
/*! \fn main(int argc, char *argv[]) * \brief The Cactid program entry point * \param argc The number of arguments passed to the function plus one (+1) * \param argv An array of the command line arguments * * The Cactid entry point. This function performs the following tasks. * 1) Processes command line input parameters * 2) Processes the Cactid configuration file to obtain database access information * 3) Process runtime parameters from the settings table * 4) Initialize the runtime threads and mutexes for the threaded environment * 5) Initialize Net-SNMP, MySQL, and the PHP Script Server (if required) * 6) Spawns X threads in order to process hosts * 7) Loop until either all hosts have been processed or until the poller runtime * has been exceeded * 8) Close database and free variables * 9) Log poller process statistics if required * 10) Exit * * Note: Command line runtime parameters override any database settings. * * \return 0 if SUCCESS, or -1 if FAILED * */ int main(int argc, char *argv[]) { struct timeval now; char *conf_file = NULL; double begin_time, end_time, current_time; int poller_interval; int num_rows; int device_counter = 0; int poller_counter = 0; int last_active_threads = 0; long int EXTERNAL_THREAD_SLEEP = 100000; long int internal_thread_sleep; time_t nowbin; struct tm now_time; struct tm *now_ptr; pthread_t* threads = NULL; pthread_attr_t attr; int* ids = NULL; MYSQL mysql; MYSQL_RES *result = NULL; MYSQL_ROW mysql_row; int canexit = 0; int host_id; int i; int mutex_status = 0; int thread_status = 0; UNUSED_PARAMETER(argc); /* we operate strictly with argv */ /* establish php processes and initialize space */ php_processes = (php_t*) calloc(MAX_PHP_SERVERS, sizeof(php_t)); for (i = 0; i < MAX_PHP_SERVERS; i++) { php_processes[i].php_state = PHP_BUSY; } /* set start time for cacti */ begin_time = get_time_as_double(); /* get time for poller_output table */ if (time(&nowbin) == (time_t) - 1) { die("ERROR: Could not get time of day from time()\n"); } localtime_r(&nowbin,&now_time); now_ptr = &now_time; if (strftime(start_datetime, sizeof(start_datetime), "%Y-%m-%d %H:%M:%S", now_ptr) == (size_t) 0) { die("ERROR: Could not get string from strftime()\n"); } /* set default verbosity */ set.log_level = POLLER_VERBOSITY_LOW; /* get static defaults for system */ config_defaults(); /*! ---------------------------------------------------------------- * PROCESS COMMAND LINE * * Run through the list of ARGV words looking for parameters we * know about. Most have two flavors (-C + --conf), and many * themselves take a parameter. * * These parameters can be structured in two ways: * * --conf=FILE both parts in one argv[] string * --conf FILE two separate argv[] strings * * We set "arg" to point to "--conf", and "opt" to point to FILE. * The helper routine * * In each loop we set "arg" to next argv[] string, then look * to see if it has an equal sign. If so, we split it in half * and point to the option separately. * * NOTE: most direction to the program is given with dash-type * parameters, but we also allow standalone numeric device IDs * in "first last" format: this is how poller.php calls this * program. */ /* initialize some global variables */ set.start_host_id = -1; set.end_host_id = -1; set.php_initialized = FALSE; set.logfile_processed = FALSE; set.parent_fork = CACTID_PARENT; for (argv++; *argv; argv++) { char *arg = *argv; char *opt = strchr(arg, '='); /* pick off the =VALUE part */ if (opt) *opt++ = '\0'; if (STRIMATCH(arg, "-f") || STRIMATCH(arg, "--first")) { if (HOSTID_DEFINED(set.start_host_id)) { die("ERROR: %s can only be used once", arg); } set.start_host_id = atoi(opt = getarg(opt, &argv)); if (!HOSTID_DEFINED(set.start_host_id)) { die("ERROR: '%s=%s' is invalid first-host ID", arg, opt); } } else if (STRIMATCH(arg, "-l") || STRIMATCH(arg, "--last")) { if (HOSTID_DEFINED(set.end_host_id)) { die("ERROR: %s can only be used once", arg); } set.end_host_id = atoi(opt = getarg(opt, &argv)); if (!HOSTID_DEFINED(set.end_host_id)) { die("ERROR: '%s=%s' is invalid last-host ID", arg, opt); } } else if (STRIMATCH(arg, "-p") || STRIMATCH(arg, "--poller")) { set.poller_id = atoi(getarg(opt, &argv)); } else if (STRIMATCH(arg, "-h") || STRIMATCH(arg, "-v") || STRIMATCH(arg, "--help") || STRIMATCH(arg, "--version")) { display_help(); exit(EXIT_SUCCESS); } else if (STRIMATCH(arg, "-O") || STRIMATCH(arg, "--option")) { char *setting = getarg(opt, &argv); char *value = strchr(setting, ':'); if (*value) { *value++ = '\0'; }else{ die("ERROR: -O requires setting:value"); } set_option(setting, value); } else if (STRIMATCH(arg, "-R") || STRIMATCH(arg, "--readonly") || STRIMATCH(arg, "--read-only")) { set.SQL_readonly = TRUE; } else if (STRIMATCH(arg, "-C") || STRIMATCH(arg, "--conf")) { conf_file = strdup(getarg(opt, &argv)); } else if (STRIMATCH(arg, "-S") || STRIMATCH(arg, "--stdout")) { set_option("log_destination", "STDOUT"); } else if (STRIMATCH(arg, "-L") || STRIMATCH(arg, "--log")) { set_option("log_destination", getarg(opt, &argv)); } else if (STRIMATCH(arg, "-V") || STRIMATCH(arg, "--verbosity")) { set_option("log_verbosity", getarg(opt, &argv)); } else if (STRIMATCH(arg, "--snmponly") || STRIMATCH(arg, "--snmp-only")) { set.snmponly = TRUE; } else if (!HOSTID_DEFINED(set.start_host_id) && all_digits(arg)) { set.start_host_id = atoi(arg); } else if (!HOSTID_DEFINED(set.end_host_id) && all_digits(arg)) { set.end_host_id = atoi(arg); } else { die("ERROR: %s is an unknown command-line parameter", arg); } } /* we require either both the first and last hosts, or niether host */ if (HOSTID_DEFINED(set.start_host_id) != HOSTID_DEFINED(set.end_host_id)) { die("ERROR: must provide both -f/-l, or neither"); } if (set.start_host_id > set.end_host_id) { die("ERROR: Invalid row spec; first host_id must be less than the second"); } /* read configuration file to establish local environment */ if (conf_file) { if ((read_cactid_config(conf_file)) < 0) { die("ERROR: Could not read config file: %s\n", conf_file); } }else{ if (!(conf_file = calloc(1, BUFSIZE))) { die("ERROR: Fatal malloc error: cactid.c conf_file!\n"); } for (i=0; i<CONFIG_PATHS; i++) { snprintf(conf_file, BUFSIZE-1, "%s%s", config_paths[i], DEFAULT_CONF_FILE); if (read_cactid_config(conf_file) >= 0) { break; } if (i == CONFIG_PATHS-1) { snprintf(conf_file, BUFSIZE-1, "%s%s", config_paths[0], DEFAULT_CONF_FILE); } } } /* read settings table from the database to further establish environment */ read_config_options(); /* set the poller interval for those who use less than 5 minute intervals */ if (set.poller_interval == 0) { poller_interval = 300; }else { poller_interval = set.poller_interval; } /* calculate the external_tread_sleep value */ internal_thread_sleep = EXTERNAL_THREAD_SLEEP * set.num_parent_processes / 2; if (set.log_level == POLLER_VERBOSITY_DEBUG) { CACTID_LOG_DEBUG(("CACTID: Version %s starting\n", VERSION)); }else{ printf("CACTID: Version %s starting\n", VERSION); } /* connect to database */ db_connect(set.dbdb, &mysql); /* initialize SNMP */ CACTID_LOG_DEBUG(("CACTID: Initializing Net-SNMP API\n")); snmp_cactid_init(); /* initialize PHP if required */ CACTID_LOG_DEBUG(("CACTID: Initializing PHP Script Server\n")); /* tell cactid that it is parent, and set the poller id */ set.parent_fork = CACTID_PARENT; set.poller_id = 0; /* initialize the script server */ if (set.php_required) { php_init(PHP_INIT); set.php_initialized = TRUE; set.php_current_server = 0; } /* obtain the list of hosts to poll */ { char querybuf[256], *qp = querybuf; qp += sprintf(qp, "SELECT id FROM host"); qp += sprintf(qp, " WHERE disabled=''"); qp += append_hostrange(qp, "id"); /* AND id BETWEEN a AND b */ qp += sprintf(qp, " ORDER BY id"); result = db_query(&mysql, querybuf); } num_rows = mysql_num_rows(result) + 1; /* add 1 for host = 0 */ if (!(threads = (pthread_t *)malloc(num_rows * sizeof(pthread_t)))) { die("ERROR: Fatal malloc error: cactid.c threads!\n"); } if (!(ids = (int *)malloc(num_rows * sizeof(int)))) { die("ERROR: Fatal malloc error: cactid.c host id's!\n"); } /* initialize threads and mutexes */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); init_mutexes(); CACTID_LOG_DEBUG(("DEBUG: Initial Value of Active Threads is %i\n", active_threads)); /* tell fork processes that they are now active */ set.parent_fork = CACTID_FORK; /* loop through devices until done */ while ((device_counter < num_rows) && (canexit == 0)) { mutex_status = thread_mutex_trylock(LOCK_THREAD); switch (mutex_status) { case 0: if (last_active_threads != active_threads) { last_active_threads = active_threads; } while ((active_threads < set.threads) && (device_counter < num_rows)) { if (device_counter > 0) { mysql_row = mysql_fetch_row(result); host_id = atoi(mysql_row[0]); ids[device_counter] = host_id; }else{ ids[device_counter] = 0; } /* create child process */ thread_status = pthread_create(&threads[device_counter], &attr, child, &ids[device_counter]); switch (thread_status) { case 0: CACTID_LOG_DEBUG(("DEBUG: Valid Thread to be Created\n")); device_counter++; active_threads++; CACTID_LOG_DEBUG(("DEBUG: The Value of Active Threads is %i\n", active_threads)); break; case EAGAIN: CACTID_LOG(("ERROR: The System Lacked the Resources to Create a Thread\n")); break; case EFAULT: CACTID_LOG(("ERROR: The Thread or Attribute Was Invalid\n")); break; case EINVAL: CACTID_LOG(("ERROR: The Thread Attribute is Not Initialized\n")); break; default: CACTID_LOG(("ERROR: Unknown Thread Creation Error\n")); break; } usleep(internal_thread_sleep); /* get current time and exit program if time limit exceeded */ if (poller_counter >= 20) { current_time = get_time_as_double(); if ((current_time - begin_time + 6) > poller_interval) { CACTID_LOG(("ERROR: Cactid Timed Out While Processing Hosts Internal\n")); canexit = 1; break; } poller_counter = 0; }else{ poller_counter++; } } thread_mutex_unlock(LOCK_THREAD); break; case EDEADLK: CACTID_LOG(("ERROR: Deadlock Occured\n")); break; case EBUSY: break; case EINVAL: CACTID_LOG(("ERROR: Attempt to Unlock an Uninitialized Mutex\n")); break; case EFAULT: CACTID_LOG(("ERROR: Attempt to Unlock an Invalid Mutex\n")); break; default: CACTID_LOG(("ERROR: Unknown Mutex Lock Error Code Returned\n")); break; } usleep(internal_thread_sleep); /* get current time and exit program if time limit exceeded */ if (poller_counter >= 20) { current_time = get_time_as_double(); if ((current_time - begin_time + 6) > poller_interval) { CACTID_LOG(("ERROR: Cactid Timed Out While Processing Hosts Internal\n")); canexit = 1; break; } poller_counter = 0; }else{ poller_counter++; } } /* wait for all threads to complete */ while (canexit == 0) { if (thread_mutex_trylock(LOCK_THREAD) == 0) { if (last_active_threads != active_threads) { last_active_threads = active_threads; } if (active_threads == 0) { canexit = 1; } thread_mutex_unlock(LOCK_THREAD); } usleep(EXTERNAL_THREAD_SLEEP); /* get current time and exit program if time limit exceeded */ if (poller_counter >= 20) { current_time = get_time_as_double(); if ((current_time - begin_time + 6) > poller_interval) { CACTID_LOG(("ERROR: Cactid Timed Out While Processing Hosts Internal\n")); canexit = 1; break; } poller_counter = 0; }else{ poller_counter++; } } /* tell Cactid that it is now parent */ set.parent_fork = CACTID_PARENT; /* print out stats */ gettimeofday(&now, NULL); /* update the db for |data_time| on graphs */ db_insert(&mysql, "replace into settings (name,value) values ('date',NOW())"); db_insert(&mysql, "insert into poller_time (poller_id, start_time, end_time) values (0, NOW(), NOW())"); /* cleanup and exit program */ pthread_attr_destroy(&attr); CACTID_LOG_DEBUG(("DEBUG: Thread Cleanup Complete\n")); /* close the php script server */ if (set.php_required) { php_close(PHP_INIT); } CACTID_LOG_DEBUG(("DEBUG: PHP Script Server Pipes Closed\n")); /* free malloc'd variables */ free(threads); free(ids); free(conf_file); CACTID_LOG_DEBUG(("DEBUG: Allocated Variable Memory Freed\n")); /* shutdown SNMP */ snmp_cactid_close(); CACTID_LOG_DEBUG(("CACTID: Net-SNMP API Shutdown Completed\n")); /* close mysql */ mysql_free_result(result); mysql_close(&mysql); CACTID_LOG_DEBUG(("DEBUG: MYSQL Free & Close Completed\n")); /* finally add some statistics to the log and exit */ end_time = TIMEVAL_TO_DOUBLE(now); CACTID_LOG_MEDIUM(("Time: %.4f s, Threads: %i, Hosts: %i\n", (end_time - begin_time), set.threads, num_rows)); exit(EXIT_SUCCESS); }
/*! \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; }