#include #include #include #include #include #include #include #include "yasnd.h" cfg_t *cfg; // pointer to configuration structure host_decl* hosts=NULL; // structure with hosts' definitions int hosts_count=0; // count of hosts int debug_flag=0; bool sms_send_enable=false; // send or not send alerts via SMS char* phone_device=NULL; // phone device for gammu, for example: "/dev/ttyACM0" char* phone_model=NULL; // gammu phone model, for example: "at" char* phone_connection=NULL; // gammu phone connection type, for example: "at" char* recipient_number=NULL; // recipient of sms alerts bool lpt_enable=false; // control usage of LPT port to reset target devices int lpt_port=0x378; // LPT port in hex(0x378 is usually LPT1) bool loop_locked=false; // flag for locking main loop while config re-reading long failures_first=3; // count of failures while hosts checking, that triggers SMS sending long failures_second=6; // count of failures while hosts checking, that triggers host's reset long failures_third=9; // count of failures while hosts checking, that clean failure statistics pid_t mainloop_clone_pid; // pid of clone process, that containt main loop void log_debug(const char *message,int verbosity) { if (debug_flag>=verbosity) syslog(LOG_INFO,"%s",message); } void log_event(const char *message) { syslog(LOG_INFO,"%s",message); } void* allocate_memory(int bytes) { void* result=malloc(bytes); if (result==NULL) { log_event("Error: malloc failed"); exit(EXIT_FAILURE); } return result; } /* Function, that turn off all LPT port pins. This is needed to unlock LPT control circuit, when management computer is just booted */ void lpt_init() { if (ioperm (lpt_port, 3, 1)) { log_event("Error: Unlocking the circuit fails due to LPT port access error"); // disable LPT port usage lpt_enable=false; return; } // Unlock circuit outb (0, lpt_port); } /* Function, that turn on first LPT port pin and turn off others. This is needed to lock LPT control circuit, when management computer is rebooted */ void lpt_lock() { if (ioperm (lpt_port, 3, 1)) { log_event("Error: Locking the circuit fails due to LPT port access error"); // disable LPT port usage lpt_enable=false; return; } // Lock circuit outb (1, lpt_port); } // Function, that checks that defined host entry is valid int cb_validate_host(cfg_t *cfg, cfg_opt_t *opt) { return 0; // success } void init() { static cfg_opt_t host_opts[] = { CFG_STR("hostname", 0, CFGF_NONE), CFG_INT("lpt_pin", 0, CFGF_NONE), CFG_END() }; // Initialize config parsing library cfg_opt_t opts[] = { CFG_SEC("host", host_opts, CFGF_MULTI | CFGF_TITLE), CFG_SIMPLE_INT("debug", &debug_flag), CFG_SIMPLE_BOOL("lpt_enable", &lpt_enable), CFG_SIMPLE_INT("lpt_port", &lpt_port), CFG_SIMPLE_BOOL("sms_send_enable", &sms_send_enable), CFG_SIMPLE_STR("phone_device", &phone_device), CFG_SIMPLE_STR("phone_model", &phone_connection), CFG_SIMPLE_STR("phone_connection", &phone_model), CFG_SIMPLE_STR("sms_recipient", &recipient_number), CFG_END() }; cfg = cfg_init(opts, CFGF_NONE); // set a validating callback function for host sections cfg_set_validate_func(cfg, "host", &cb_validate_host); // check if config file is valid and parse it if (cfg_parse(cfg, "/etc/yasnd.conf") == CFG_PARSE_ERROR) { log_event("Error parsing config file"); exit(EXIT_FAILURE); } // if SMS sending is enabled, all of phone parameters must be explicitly defined if (sms_send_enable) { if (phone_model==NULL) { log_event("Error: phone model is not defined in config file"); exit(EXIT_FAILURE); } if (phone_connection==NULL) { log_event("Error: phone connection type is not defined in config file"); exit(EXIT_FAILURE); } if (phone_device==NULL) { log_event("Error: phone device is not defined in config file"); exit(EXIT_FAILURE); } if (recipient_number==NULL) { log_event("Error: no recipient for sms alerts defined in config file"); exit(EXIT_FAILURE); } } // hosts' array must not be empty hosts_count=cfg_size(cfg,"host"); if (hosts_count==0) { log_event("Error: no hosts to check defined in config file"); exit(EXIT_FAILURE); } // allocate memory for hosts array... hosts=allocate_memory(hosts_count*sizeof(host_decl)); // ...and fill it with config file content for (int i=0;ihostname); log_debug(tmp,DEBUG_BASE); execl("/bin/ping","/bin/ping","-c 1","-n",host->hostname,(char*) 0); // STUB: check result of exec call exit(EXIT_SUCCESS); } // Save child's pid host->helper_pid=pid; } /* Function, that turn on only one LPT port pin, defined by it argument, wait a second, and turn off all pins. That is enough to reset specified host. */ void reset_pin(int pin_num) { if (pin_num==0) return; // there is no LPT control for target host if (pin_num<0 || pin_num>8) { log_event("Error: incorrent LPT pin number"); return; } int pins = 2<<(pin_num-1); if (ioperm (lpt_port, 3, 1)) { log_event("Error: LPT port access error"); return; } // Reset host outb (pins, lpt_port); sleep(1); outb (0, lpt_port); } int loop_function() { // set default signal handlers signal(SIGTERM,SIG_DFL); signal(SIGHUP,SIG_DFL); // for (int i=0;i 0) { if (WIFEXITED(pid_status)) { char tmp[50]; sprintf(tmp, "Finished child %d with result %d", result, WEXITSTATUS(pid_status)); log_debug(tmp,DEBUG_ALL); // if host is alive - reset failure counter // otherwise - increment it if (WEXITSTATUS(pid_status) == 0) { hosts[i].fail_count=0; hosts[i].alert_sent=false; hosts[i].reaction_obtained=false; } else hosts[i].fail_count++; } } } for (int i=0;ifailures_second && hosts[i].alert_sent && !hosts[i].reaction_obtained) { if (lpt_enable) { char message[150]; sprintf(message,"Host %s does not answer and no reaction on this. Trying to reset it(LPT pin %d)",hosts[i].hostname,hosts[i].lpt_pin); log_debug(message,DEBUG_BASE); if (sms_send_enable) gammu_send_sms(message); reset_pin(hosts[i].lpt_pin); hosts[i].reaction_obtained=true; } continue; } if (hosts[i].fail_count>failures_first && !hosts[i].alert_sent) { char message[100]; sprintf(message,"Host %s does not answer(LPT pin %d)",hosts[i].hostname,hosts[i].lpt_pin); log_debug(message,DEBUG_BASE); if (sms_send_enable) gammu_send_sms(message); // set alert flag to prevent sending more than 1 message // for unreachable host hosts[i].alert_sent=true; continue; } } } void signal_handler(int signum) { void free_resources() { // Stop IPC loop and remove IPC queue ipc_free(); // free config structure cfg_free(cfg); // free gammu structure gammu_free(); // free hosts structures memory if (hosts!=NULL) free(hosts); } if (signum==SIGHUP) { log_event("SIGHUP captured, daemon re-read config"); // lock Main Loop loop_locked=true; // kill Main Loop clone process kill(mainloop_clone_pid,SIGTERM); waitpid(mainloop_clone_pid,NULL,__WCLONE); // free all resources free_resources(); // Init IPC loop again ipc_init(); // run init again init(); // sleep 5 seconds and unlock Main Loop sleep(5); loop_locked=false; return; } if (signum==SIGTERM) { log_event("SIGTERM captured, daemon stopping"); // block LPT control circuit if (lpt_enable) lpt_lock(); // close log decriptor closelog(); // free resources free_resources(); // exit(EXIT_SUCCESS); } } int main(void) { /* Our process ID and Session ID */ pid_t pid, sid; /* Fork off the parent process */ pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } /* If we got a good PID, then we can exit the parent process. */ if (pid > 0) { exit(EXIT_SUCCESS); } /* Change the file mode mask */ umask(0); /* Open any logs here */ openlog("yasnd", LOG_PID|LOG_CONS, LOG_USER); /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { /* Log the failure */ exit(EXIT_FAILURE); } /* Change the current working directory */ if ((chdir("/")) < 0) { /* Log the failure */ exit(EXIT_FAILURE); } /* Close out the standard file descriptors */ close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // initialize IPC queue and create process // that will watch for new messages in it ipc_init(); // Init apropriate structures init(); // Set signal handlers signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); /* The Main Loop */ while (1) { if (!loop_locked) { unsigned char child_stack[16384]; mainloop_clone_pid=clone(loop_function,child_stack+8192,CLONE_VM,NULL); sleep(60); /* wait 60 seconds */ // wait for clone process to prevent zombie waitpid(mainloop_clone_pid,NULL,__WCLONE); // log_event("Exiting"); // exit(EXIT_SUCCESS); } else { // if loop is locked, just wait 1 second and start it again sleep(1); } } exit(EXIT_SUCCESS); }