#include #include #include #include #include #include #include #include #include #include #include "log.h" #define MODULE_NAME "runner" #define MAX_STR_LEN 255u /** up to 20 arguments*/ #define MAX_ARGC 64u typedef struct { char *name; char strings[MAX_ARGC * 2u][MAX_STR_LEN]; char *envs[MAX_ARGC + 1]; char *params[MAX_ARGC + 1]; size_t n_params; size_t n_envs; size_t s_p; } runner_cmd_t; static volatile int running = 1; static runner_cmd_t cmd; static void int_handler(int dummy) { (void)dummy; running = 0; } static void execute_command(list_t *plist) { pid_t pid; ASSERT(cmd.name != NULL, "Invalid service handler (NULL)"); for (int i=0; environ[i]!=NULL && cmd.n_envs < MAX_ARGC; i++) { cmd.envs[cmd.n_envs] = environ[i]; cmd.n_envs++; } pid = fork(); ASSERT(pid != -1, "Unable to fork: %s", strerror(errno)); if (pid == 0) { M_LOG(MODULE_NAME, "Running %s", cmd.params[0]); execve(cmd.params[0], &cmd.params[0], &cmd.envs[0]); // Nothing below this line should be executed by child process. If so, // it means that the execl function wasn't successfull, so lets exit: _exit(1); } else { M_LOG(MODULE_NAME, "Running service %s (%d)...", cmd.name, pid); (void)memset(&cmd, 0, sizeof(cmd)); list_put_i(plist, pid); } } static int ini_handle(void *user_data, const char *section, const char *name, const char *value) { list_t *plist = (list_t *)user_data; ASSERT(cmd.s_p < MAX_ARGC * 2u, "String buffer overflow: %ld", cmd.s_p); ASSERT(cmd.n_params <= MAX_ARGC, "Max arguments reached %ld", cmd.n_params); ASSERT(cmd.n_envs <= MAX_ARGC, "Max environment variables reached %ld", cmd.n_envs); if ((cmd.name == NULL) || ! EQU(section, cmd.name)) { if (cmd.params[0]) { execute_command(plist); } cmd.n_params = 1u; (void)strncpy(cmd.strings[cmd.s_p], section, MAX_STR_LEN); cmd.name = cmd.strings[cmd.s_p]; cmd.s_p++; } if (EQU(name, "exec")) { (void)strncpy(cmd.strings[cmd.s_p], value, MAX_STR_LEN); cmd.params[0] = cmd.strings[cmd.s_p]; cmd.s_p++; } if (EQU(name, "param")) { (void)strncpy(cmd.strings[cmd.s_p], value, MAX_STR_LEN); cmd.params[cmd.n_params] = cmd.strings[cmd.s_p]; cmd.s_p++; cmd.n_params++; } else { (void)snprintf(cmd.strings[cmd.s_p], MAX_STR_LEN, "%s=%s", name, value); cmd.envs[cmd.n_envs] = cmd.strings[cmd.s_p]; cmd.s_p++; cmd.n_envs++; } return 1; } extern char **environ; int main(int argc, char const *argv[]) { const char *conf_file; item_t item; pid_t w_pid; int pid_count; list_t pids; signal(SIGPIPE, SIG_IGN); signal(SIGABRT, SIG_IGN); signal(SIGINT, int_handler); if (argc != 2) { printf("Usage: %s /path/to/conf.ini\n", argv[0]); return -1; } conf_file = argv[1]; LOG_INIT(MODULE_NAME); M_LOG(MODULE_NAME,"config file is %s", conf_file); pids = list_init(); (void)memset(&cmd, 0, sizeof(cmd)); ASSERT(ini_parse(conf_file, ini_handle, &pids) == 0, "Can't load service from '%s'", conf_file); if (cmd.params[0] != NULL) { execute_command(&pids); } pid_count = list_size(pids); // monitoring the process while (running != 0 && pid_count != 0) { pid_count = 0; list_for_each(item, pids) { w_pid = waitpid((pid_t)item->value.i, NULL, WNOHANG); if (w_pid == -1 || w_pid > 0) { // child exits M_LOG(MODULE_NAME, "Process %d exits", item->value.i); item->value.i = -1; } else { pid_count++; } } // 500 ms usleep(500000); } // kill all the remaining processes list_for_each(item, pids) { if (item->value.i != -1) { if (kill(item->value.i, SIGKILL) == -1) { M_ERROR(MODULE_NAME, "Unable to kill process %d: %s", item->value.i, strerror(errno)); } else { (void)waitpid(item->value.i, NULL, 0); } } } list_free(&pids); return 0; }