/************************************************************************* * * Copyright 2020 highstreet technologies GmbH and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ #define _GNU_SOURCE #include "supervisor.h" #include "utils/log_utils.h" #include "utils/sys_utils.h" #include #include #include "core/framework.h" #include #include #include #include #include #include typedef struct { char *name; char *path; char **args; bool nomanual; bool autorestart; char *stdout_path; char *stderr_path; pid_t pid; int running; } supervisor_control_block_t; static void supervisor_spawn(supervisor_control_block_t *scb); static void supervisor_free_scb(int count, supervisor_control_block_t *scb); static void supervisor_on_signal(int signo); static volatile sig_atomic_t supervisor_got_signal_stop = 0; static bool nts_manual; int supervisor_run(void) { int scb_count = framework_config.supervisor.rules_count; supervisor_control_block_t *scb = (supervisor_control_block_t*)malloc(sizeof(supervisor_control_block_t) * framework_config.supervisor.rules_count); if(scb == 0) { log_error("malloc failed\n"); return NTS_ERR_FAILED; } for(int i = 0; i < scb_count; i++) { scb[i].name = strdup(framework_config.supervisor.rules[i].name); scb[i].path = strdup(framework_config.supervisor.rules[i].path); scb[i].args = malloc(sizeof(char *) * (framework_config.supervisor.rules[i].args_count + 2)); scb[i].args[0] = strdup(framework_config.supervisor.rules[i].path); for(int j = 0; j < framework_config.supervisor.rules[i].args_count; j++) { scb[i].args[j + 1] = strdup(framework_config.supervisor.rules[i].args[j]); } scb[i].args[framework_config.supervisor.rules[i].args_count + 1] = 0; scb[i].autorestart = framework_config.supervisor.rules[i].autorestart; scb[i].nomanual = framework_config.supervisor.rules[i].nomanual; scb[i].stdout_path = framework_config.supervisor.rules[i].stdout_path ? strdup(framework_config.supervisor.rules[i].stdout_path) : 0; scb[i].stderr_path = framework_config.supervisor.rules[i].stderr_path ? strdup(framework_config.supervisor.rules[i].stderr_path) : 0; scb[i].pid = 0; scb[i].running = 0; } nts_manual = framework_environment.nts.manual; signal(SIGINT, supervisor_on_signal); signal(SIGTERM, supervisor_on_signal); signal(SIGQUIT, supervisor_on_signal); for(int i = 0; i < scb_count; i++) { supervisor_spawn(&scb[i]); log_add_verbose(1, "[supervisor] spawning %s... with pid %lu\n", scb[i].name, scb[i].pid); } int running = 1; while(running) { int defunct_status; pid_t defunct_pid = waitpid(-1, &defunct_status, WNOHANG); if(defunct_pid > 0) { for(int i = 0; i < scb_count; i++) { if(scb[i].pid == defunct_pid) { log_add_verbose(1, "[supervisor] process %s (pid=%lu) exited with status %d\n", scb[i].name, defunct_pid, defunct_status); scb[i].running = 0; if(scb[i].autorestart) { supervisor_spawn(&scb[i]); log_add_verbose(1, "[supervisor] respawned %s (pid=%lu)\n", scb[i].name, scb[i].pid); } } } } if(supervisor_got_signal_stop) { for(int i = 0; i < scb_count; i++) { if(scb[i].running) { log_add_verbose(1, "[supervisor] sending %d to %s (pid=%lu)...\n", supervisor_got_signal_stop, scb[i].name, scb[i].pid); kill(scb[i].pid, supervisor_got_signal_stop); } } supervisor_got_signal_stop = 0; running = 0; } sleep(1); } //after SIGTERM was forwarded running = 1; while(running) { int defunct_status; pid_t defunct_pid = waitpid(-1, &defunct_status, WNOHANG); if(defunct_pid > 0) { char *name = 0; for(int i = 0; i < scb_count; i++) { if(scb[i].pid == defunct_pid) { scb[i].running = 0; name = scb[i].name; } } log_add_verbose(1, "[supervisor] process %s (pid=%d) exited with status %d\n", name, defunct_pid, defunct_status); } running = 0; for(int i = 0; i < scb_count; i++) { if(scb[i].running == 1) { running = 1; } } } supervisor_free_scb(scb_count, scb); framework_free(); return NTS_ERR_OK; } static void supervisor_spawn(supervisor_control_block_t *scb) { if(nts_manual && scb->nomanual) { return; } scb->running = 1; scb->pid = fork(); if(scb->pid == -1) { log_error("fork() failed\n"); return; } if(scb->pid == 0) { //child process int stdout_fd = 0; int stderr_fd = 0; signal(SIGINT, 0); signal(SIGTERM, 0); signal(SIGQUIT, 0); framework_free(); setsid(); if(scb->stdout_path) { if(scb->stdout_path[0] == 0) { free(scb->stdout_path); scb->stdout_path = strdup("/dev/null"); } stdout_fd = open(scb->stdout_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); dup2(stdout_fd, STDOUT_FILENO); } if(scb->stderr_path) { if(scb->stderr_path[0] == 0) { free(scb->stderr_path); scb->stderr_path = strdup("/dev/null"); } stderr_fd = open(scb->stderr_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); dup2(stderr_fd, STDERR_FILENO); } execv(scb->path, scb->args); _exit(0); } } static void supervisor_free_scb(int count, supervisor_control_block_t *scb) { for(int i = 0; i < count; i++) { free(scb[i].name); free(scb[i].path); int j = 0; while(scb[i].args[j]) { free(scb[i].args[j]); j++; } free(scb[i].args); free(scb[i].stdout_path); free(scb[i].stderr_path); } free(scb); } static void supervisor_on_signal(int signo) { supervisor_got_signal_stop = signo; }