Add supoprt for D release use-case.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / core / app / supervisor.c
diff --git a/ntsimulator/ntsim-ng/core/app/supervisor.c b/ntsimulator/ntsim-ng/core/app/supervisor.c
new file mode 100644 (file)
index 0000000..8612a08
--- /dev/null
@@ -0,0 +1,216 @@
+/*************************************************************************
+*
+* 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 <stdio.h>
+#include <assert.h>
+
+#include "core/framework.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+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;
+}