Add supoprt for D release use-case.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / core / app / supervisor.c
1 /*************************************************************************
2 *
3 * Copyright 2020 highstreet technologies GmbH and others
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
17
18 #define _GNU_SOURCE
19
20 #include "supervisor.h"
21 #include "utils/log_utils.h"
22 #include "utils/sys_utils.h"
23 #include <stdio.h>
24 #include <assert.h>
25
26 #include "core/framework.h"
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33
34 typedef struct {
35     char *name;
36     char *path;
37     char **args;
38     bool nomanual;
39     bool autorestart;
40     char *stdout_path;
41     char *stderr_path;
42
43     pid_t pid;
44     int running;
45 } supervisor_control_block_t;
46
47 static void supervisor_spawn(supervisor_control_block_t *scb);
48 static void supervisor_free_scb(int count, supervisor_control_block_t *scb);
49 static void supervisor_on_signal(int signo);
50
51 static volatile sig_atomic_t supervisor_got_signal_stop = 0;
52 static bool nts_manual;
53
54 int supervisor_run(void) {
55     int scb_count = framework_config.supervisor.rules_count;
56     supervisor_control_block_t *scb = (supervisor_control_block_t*)malloc(sizeof(supervisor_control_block_t) * framework_config.supervisor.rules_count);
57     if(scb == 0) {
58         log_error("malloc failed\n");
59         return NTS_ERR_FAILED;
60     }
61
62     for(int i = 0; i < scb_count; i++) {
63         scb[i].name = strdup(framework_config.supervisor.rules[i].name);
64         scb[i].path = strdup(framework_config.supervisor.rules[i].path);
65         scb[i].args = malloc(sizeof(char *) * (framework_config.supervisor.rules[i].args_count + 2));
66         scb[i].args[0] = strdup(framework_config.supervisor.rules[i].path);
67         for(int j = 0; j < framework_config.supervisor.rules[i].args_count; j++) {
68             scb[i].args[j + 1] = strdup(framework_config.supervisor.rules[i].args[j]);
69         }
70         scb[i].args[framework_config.supervisor.rules[i].args_count + 1] = 0;
71         scb[i].autorestart = framework_config.supervisor.rules[i].autorestart;
72         scb[i].nomanual = framework_config.supervisor.rules[i].nomanual;
73         scb[i].stdout_path = framework_config.supervisor.rules[i].stdout_path ? strdup(framework_config.supervisor.rules[i].stdout_path) : 0;
74         scb[i].stderr_path = framework_config.supervisor.rules[i].stderr_path ? strdup(framework_config.supervisor.rules[i].stderr_path) : 0;
75         scb[i].pid = 0;
76         scb[i].running = 0;
77     }
78
79     nts_manual = framework_environment.nts.manual;
80     
81     signal(SIGINT, supervisor_on_signal);
82     signal(SIGTERM, supervisor_on_signal);
83     signal(SIGQUIT, supervisor_on_signal);
84
85     for(int i = 0; i < scb_count; i++) {
86         supervisor_spawn(&scb[i]);
87         log_add_verbose(1, "[supervisor] spawning %s... with pid %lu\n", scb[i].name, scb[i].pid);
88     }
89
90     int running = 1;
91     while(running) {
92         int defunct_status;
93         pid_t defunct_pid = waitpid(-1, &defunct_status, WNOHANG);
94         if(defunct_pid > 0) {
95             for(int i = 0; i < scb_count; i++) {
96                 if(scb[i].pid == defunct_pid) {
97                     log_add_verbose(1, "[supervisor] process %s (pid=%lu) exited with status %d\n", scb[i].name, defunct_pid, defunct_status);
98                     scb[i].running = 0;
99                     if(scb[i].autorestart) {
100                         supervisor_spawn(&scb[i]);
101                         log_add_verbose(1, "[supervisor] respawned %s (pid=%lu)\n", scb[i].name, scb[i].pid);
102                     }
103                 }
104             }
105         }
106
107         if(supervisor_got_signal_stop) {
108             for(int i = 0; i < scb_count; i++) {
109                 if(scb[i].running) {
110                     log_add_verbose(1, "[supervisor] sending %d to %s (pid=%lu)...\n", supervisor_got_signal_stop, scb[i].name, scb[i].pid);
111                     kill(scb[i].pid, supervisor_got_signal_stop);
112                 }
113             }
114             supervisor_got_signal_stop = 0;
115             running = 0;
116         }
117
118         sleep(1);
119     }
120     
121     //after SIGTERM was forwarded
122     running = 1;
123     while(running) {
124         int defunct_status;
125         pid_t defunct_pid = waitpid(-1, &defunct_status, WNOHANG);
126         if(defunct_pid > 0) {
127             char *name = 0;
128             for(int i = 0; i < scb_count; i++) {
129                 if(scb[i].pid == defunct_pid) {
130                     scb[i].running = 0;
131                     name = scb[i].name;
132                 }
133             }
134             log_add_verbose(1, "[supervisor] process %s (pid=%d) exited with status %d\n", name, defunct_pid, defunct_status);
135         }
136
137         running = 0;
138         for(int i = 0; i < scb_count; i++) {
139             if(scb[i].running == 1) {
140                 running = 1;
141             }
142         }
143     }
144
145     supervisor_free_scb(scb_count, scb);
146     framework_free();
147
148     return NTS_ERR_OK;
149 }
150
151 static void supervisor_spawn(supervisor_control_block_t *scb) {
152     if(nts_manual && scb->nomanual) {
153         return;
154     }
155
156     scb->running = 1;
157     scb->pid = fork();
158     if(scb->pid == -1) {
159         log_error("fork() failed\n");
160         return;
161     }
162
163     if(scb->pid == 0) {
164         //child process
165         int stdout_fd = 0;
166         int stderr_fd = 0;
167
168         signal(SIGINT, 0);
169         signal(SIGTERM, 0);
170         signal(SIGQUIT, 0);
171         framework_free();
172         setsid();
173
174         if(scb->stdout_path) {
175             if(scb->stdout_path[0] == 0) {
176                 free(scb->stdout_path);
177                 scb->stdout_path = strdup("/dev/null");
178             }
179             stdout_fd = open(scb->stdout_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
180             dup2(stdout_fd, STDOUT_FILENO);
181         }
182
183         if(scb->stderr_path) {
184             if(scb->stderr_path[0] == 0) {
185                 free(scb->stderr_path);
186                 scb->stderr_path = strdup("/dev/null");
187             }
188             stderr_fd = open(scb->stderr_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
189             dup2(stderr_fd, STDERR_FILENO);
190         }
191
192         execv(scb->path, scb->args);
193         _exit(0);
194     }
195 }
196
197 static void supervisor_free_scb(int count, supervisor_control_block_t *scb) {
198     for(int i = 0; i < count; i++) {
199         free(scb[i].name);
200         free(scb[i].path);
201         int j = 0;
202         while(scb[i].args[j]) {
203             free(scb[i].args[j]);
204             j++;
205         }
206         free(scb[i].args);
207         free(scb[i].stdout_path);
208         free(scb[i].stderr_path);
209     }
210
211     free(scb);
212 }
213
214 static void supervisor_on_signal(int signo) {
215     supervisor_got_signal_stop = signo;
216 }