Add blank type of NTS.
[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 static void supervisor_on_sigusr(int signo);
51
52 static volatile sig_atomic_t supervisor_got_signal_stop = 0;
53 static volatile sig_atomic_t supervisor_got_signal_reload = 0;
54 static bool nts_manual;
55
56 int supervisor_run(int argc, char **argv) {
57 supervisor_start:
58     if(file_exists("/opt/dev/deploy/.env")) {
59         FILE * fp;
60         char * line = 0;
61         size_t len = 0;
62         ssize_t read;
63
64         fp = fopen("/opt/dev/deploy/.env", "r");
65         if(fp) {
66             log_add_verbose(1, "[supervisor] found /opt/dev/deploy/.env\n");
67
68             while ((read = getline(&line, &len, fp)) != -1) {
69                 if(line[strlen(line) - 1] == '\n') {
70                     line[strlen(line) - 1] = 0;
71                 }
72
73                 log_add_verbose(1, "[supervisor] adding .env var: %s\n", line);
74                 putenv(strdup(line));
75             }
76
77             fclose(fp);
78             free(line);
79         }
80     }
81
82     supervisor_got_signal_reload = 0;
83     supervisor_got_signal_stop = 0;
84
85     int scb_count = framework_config.supervisor.rules_count;
86     supervisor_control_block_t *scb = (supervisor_control_block_t*)malloc(sizeof(supervisor_control_block_t) * framework_config.supervisor.rules_count);
87     if(scb == 0) {
88         log_error("malloc failed\n");
89         return NTS_ERR_FAILED;
90     }
91
92     for(int i = 0; i < scb_count; i++) {
93         scb[i].name = strdup(framework_config.supervisor.rules[i].name);
94         scb[i].path = strdup(framework_config.supervisor.rules[i].path);
95         scb[i].args = malloc(sizeof(char *) * (framework_config.supervisor.rules[i].args_count + 2));
96         scb[i].args[0] = strdup(framework_config.supervisor.rules[i].path);
97         for(int j = 0; j < framework_config.supervisor.rules[i].args_count; j++) {
98             scb[i].args[j + 1] = strdup(framework_config.supervisor.rules[i].args[j]);
99         }
100         scb[i].args[framework_config.supervisor.rules[i].args_count + 1] = 0;
101         scb[i].autorestart = framework_config.supervisor.rules[i].autorestart;
102         scb[i].nomanual = framework_config.supervisor.rules[i].nomanual;
103         scb[i].stdout_path = framework_config.supervisor.rules[i].stdout_path ? strdup(framework_config.supervisor.rules[i].stdout_path) : 0;
104         scb[i].stderr_path = framework_config.supervisor.rules[i].stderr_path ? strdup(framework_config.supervisor.rules[i].stderr_path) : 0;
105         scb[i].pid = 0;
106         scb[i].running = 0;
107     }
108
109     nts_manual = framework_environment.nts.manual;
110     
111     signal(SIGINT, supervisor_on_signal);
112     signal(SIGTERM, supervisor_on_signal);
113     signal(SIGQUIT, supervisor_on_signal);
114     signal(SIGUSR1, supervisor_on_sigusr);
115
116     for(int i = 0; i < scb_count; i++) {
117         supervisor_spawn(&scb[i]);
118         log_add_verbose(1, "[supervisor] spawning %s... with pid %lu\n", scb[i].name, scb[i].pid);
119     }
120
121     int running = 1;
122     while(running) {
123         int defunct_status;
124         pid_t defunct_pid = waitpid(-1, &defunct_status, WNOHANG);
125         if(defunct_pid > 0) {
126             for(int i = 0; i < scb_count; i++) {
127                 if(scb[i].pid == defunct_pid) {
128                     log_add_verbose(1, "[supervisor] process %s (pid=%lu) exited with status %d\n", scb[i].name, defunct_pid, defunct_status);
129                     scb[i].running = 0;
130                     if(scb[i].autorestart) {
131                         supervisor_spawn(&scb[i]);
132                         log_add_verbose(1, "[supervisor] respawned %s (pid=%lu)\n", scb[i].name, scb[i].pid);
133                     }
134                 }
135             }
136         }
137
138         if(supervisor_got_signal_stop) {
139             for(int i = 0; i < scb_count; i++) {
140                 if(scb[i].running) {
141                     log_add_verbose(1, "[supervisor] sending %d to %s (pid=%lu)...\n", supervisor_got_signal_stop, scb[i].name, scb[i].pid);
142                     kill(scb[i].pid, supervisor_got_signal_stop);
143                 }
144             }
145             supervisor_got_signal_stop = 0;
146             running = 0;
147         }
148
149         sleep(1);
150     }
151     
152     //after SIGTERM was forwarded
153     running = 1;
154     while(running) {
155         int defunct_status;
156         pid_t defunct_pid = waitpid(-1, &defunct_status, WNOHANG);
157         if(defunct_pid > 0) {
158             char *name = 0;
159             for(int i = 0; i < scb_count; i++) {
160                 if(scb[i].pid == defunct_pid) {
161                     scb[i].running = 0;
162                     name = scb[i].name;
163                 }
164             }
165             log_add_verbose(1, "[supervisor] process %s (pid=%d) exited with status %d\n", name, defunct_pid, defunct_status);
166         }
167
168         running = 0;
169         for(int i = 0; i < scb_count; i++) {
170             if(scb[i].running == 1) {
171                 running = 1;
172             }
173         }
174     }
175
176     supervisor_free_scb(scb_count, scb);
177     framework_free();
178
179     if(supervisor_got_signal_reload) {
180         if(framework_init(argc, argv) != NTS_ERR_OK) {
181             log_error(LOG_COLOR_BOLD_RED"framework_init() error\n");
182             framework_free();
183             return EXIT_FAILURE;
184         }
185
186         log_add_verbose(1, "[supervisor] SIGUSR1 received, restarting everything... (this is a *new* logfile)\n");
187
188         goto supervisor_start;
189     }
190
191     return NTS_ERR_OK;
192 }
193
194 static void supervisor_spawn(supervisor_control_block_t *scb) {
195     if(nts_manual && scb->nomanual) {
196         return;
197     }
198
199     scb->running = 1;
200     scb->pid = fork();
201     if(scb->pid == -1) {
202         log_error("fork() failed\n");
203         return;
204     }
205
206     if(scb->pid == 0) {
207         //child process
208         int stdout_fd = 0;
209         int stderr_fd = 0;
210
211         signal(SIGINT, 0);
212         signal(SIGTERM, 0);
213         signal(SIGQUIT, 0);
214         framework_free();
215         setsid();
216
217         if(scb->stdout_path) {
218             if(scb->stdout_path[0] == 0) {
219                 free(scb->stdout_path);
220                 scb->stdout_path = strdup("/dev/null");
221             }
222             stdout_fd = open(scb->stdout_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
223             dup2(stdout_fd, STDOUT_FILENO);
224         }
225
226         if(scb->stderr_path) {
227             if(scb->stderr_path[0] == 0) {
228                 free(scb->stderr_path);
229                 scb->stderr_path = strdup("/dev/null");
230             }
231             stderr_fd = open(scb->stderr_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
232             dup2(stderr_fd, STDERR_FILENO);
233         }
234
235         execv(scb->path, scb->args);
236         _exit(0);
237     }
238 }
239
240 static void supervisor_free_scb(int count, supervisor_control_block_t *scb) {
241     for(int i = 0; i < count; i++) {
242         free(scb[i].name);
243         free(scb[i].path);
244         int j = 0;
245         while(scb[i].args[j]) {
246             free(scb[i].args[j]);
247             j++;
248         }
249         free(scb[i].args);
250         free(scb[i].stdout_path);
251         free(scb[i].stderr_path);
252     }
253
254     free(scb);
255 }
256
257 static void supervisor_on_signal(int signo) {
258     supervisor_got_signal_stop = signo;
259 }
260
261 static void supervisor_on_sigusr(int signo) {
262     supervisor_got_signal_stop = SIGTERM;
263     supervisor_got_signal_reload = 1;
264 }