supervise.c (5521B) download
1#include "config.h"
2#include "service.h"
3#include "util.h"
4
5#include <errno.h>
6#include <fcntl.h>
7#include <limits.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/socket.h>
12#include <sys/stat.h>
13#include <sys/un.h>
14#include <sys/wait.h>
15#include <unistd.h>
16
17
18bool daemon_running = true;
19
20static void signal_child(int unused) {
21 (void) unused;
22
23 int status;
24 pid_t died_pid;
25 service_t* s = NULL;
26
27 if ((died_pid = wait(&status)) == -1) {
28 print_error("error: cannot wait for process: %s\n");
29 return;
30 }
31
32 if (!WIFEXITED(status) && !WIFSIGNALED(status))
33 return;
34
35 for (int i = 0; i < services_size; i++) {
36 if (services[i].pid == died_pid) {
37 s = &services[i];
38 break;
39 }
40 }
41 if (s == NULL)
42 return;
43
44 service_handle_exit(s, WIFSIGNALED(status), WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status));
45}
46
47static void check_deaths(void) {
48 service_t* s;
49 for (int i = 0; i < services_size; i++) {
50 s = &services[i];
51 if (s->state == STATE_ACTIVE_PID) {
52 if (kill(s->pid, 0) == -1 && errno == ESRCH)
53 service_handle_exit(s, false, 0);
54 }
55 }
56}
57
58static void check_services(void) {
59 service_t* s;
60 for (int i = 0; i < services_size; i++) {
61 s = &services[i];
62 if (s->state == STATE_DEAD)
63 continue;
64 if (service_need_restart(s)) {
65 if (s->state == STATE_INACTIVE) {
66 service_start(s, NULL);
67 s->status_change = time(NULL);
68 service_update_status(s);
69 }
70 } else {
71 if (s->state != STATE_INACTIVE) {
72 service_stop(s, NULL);
73 s->status_change = time(NULL);
74 service_update_status(s);
75 }
76 }
77 }
78}
79
80static void accept_socket(void) {
81 int client_fd;
82 if ((client_fd = accept(control_socket, NULL, NULL)) == -1) {
83 if (errno == EWOULDBLOCK) {
84 sleep(SV_ACCEPT_INTERVAL);
85 } else {
86 print_error("error: cannot accept client from control-socket: %s\n");
87 }
88 } else {
89 service_handle_client(client_fd);
90 }
91}
92
93static void control_sockets(void) {
94#if SV_RUNIT_COMPAT != 0
95 service_t* s;
96 char cmd;
97 for (int i = 0; i < services_size; i++) {
98 s = &services[i];
99
100 while (recv(s->control, &cmd, 1, MSG_DONTWAIT) == 1) {
101 service_handle_command_runit(s, cmd);
102 }
103 }
104#endif
105}
106
107int service_supervise(const char* service_dir_, const char* runlevel_, bool force_socket) {
108 struct sigaction sigact = { 0 };
109 sigact.sa_handler = signal_child;
110 sigaction(SIGCHLD, &sigact, NULL);
111 sigact.sa_handler = SIG_IGN;
112 sigaction(SIGPIPE, &sigact, NULL);
113
114 strncpy(runlevel, runlevel_, SV_NAME_MAX);
115 service_dir_path = service_dir_;
116 if ((service_dir = open(service_dir_, O_DIRECTORY)) == -1) {
117 print_error("error: cannot open directory %s: %s\n", service_dir_);
118 return 1;
119 }
120
121 // setenv("SERVICE_RUNLEVEL", runlevel, true);
122
123 umask(0002);
124
125 char socket_path[PATH_MAX];
126 snprintf(socket_path, PATH_MAX, SV_CONTROL_SOCKET, runlevel);
127
128 if ((null_fd = open("/dev/null", O_RDWR)) == -1) {
129 print_error("error: cannot open /dev/null: %s\n");
130 null_fd = 1;
131 }
132
133 struct stat socket_stat;
134 if (force_socket) {
135 if (unlink(socket_path) == -1 && errno != ENOENT) {
136 print_error("error: cannot unlink socket: %s\n");
137 }
138 } else if (stat(socket_path, &socket_stat) != -1 && S_ISREG(socket_stat.st_mode)) {
139 printf("error: %s exist and is locking supervision,\nrun this program with '-f' flag if you are sure no other superviser is running.", socket_path);
140 return 1;
141 }
142 // create socket
143 if ((control_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
144 print_error("error: cannot create socket: %s\n");
145 return 1;
146 }
147
148 // bind socket to address
149 struct sockaddr_un addr = { 0 };
150 addr.sun_family = AF_UNIX;
151 strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
152 if (bind(control_socket, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
153 print_error("error: cannot bind %s to socket: %s\n", socket_path);
154 return 1;
155 }
156
157 // listen for connections
158 if (listen(control_socket, 5) == -1) {
159 print_error("error: cannot listen to control socket: %s\n");
160 return 1;
161 }
162
163 int sockflags = fcntl(control_socket, F_GETFL, 0);
164 if (sockflags == -1) {
165 print_error("warn: fcntl-getflags on control-socket failed: %s\n");
166 } else if (fcntl(control_socket, F_SETFL, sockflags | O_NONBLOCK) == -1) {
167 print_error("warn: fcntl-setflags on control-socket failed: %s\n");
168 }
169
170 printf(":: starting services on '%s'\n", runlevel);
171
172 if (service_refresh_directory() < 0)
173 return 1;
174
175 printf(":: started services\n");
176
177 // accept connections and handle requests
178 while (daemon_running) {
179 check_deaths();
180 service_refresh_directory();
181 check_services();
182 control_sockets();
183 accept_socket();
184 }
185
186 close(control_socket);
187
188 if (unlink(socket_path) == -1 && errno != ENOENT) {
189 print_error("error: cannot unlink socket: %s\n");
190 }
191
192 printf(":: terminating\n");
193
194 service_t* s;
195 for (int i = 0; i < services_size; i++) {
196 s = &services[i];
197 service_stop(s, NULL);
198 }
199
200 time_t start = time(NULL);
201 int running;
202 do {
203 sleep(1); // sleep for one second
204 running = 0;
205 for (int i = 0; i < services_size; i++) {
206 if (services[i].state != STATE_INACTIVE)
207 running++;
208 }
209 printf(":: %d running...\r", running);
210 } while (running > 0 && (time(NULL) - start) < SV_STOP_TIMEOUT);
211
212 printf("\n");
213
214 for (int i = 0; i < services_size; i++) {
215 if (services[i].pid) {
216 printf(":: killing %s\n", services[i].name);
217 service_kill(&services[i], SIGKILL);
218 }
219 }
220
221 printf(":: all services stopped\n");
222
223 signal(SIGPIPE, SIG_DFL);
224 signal(SIGCHLD, SIG_DFL);
225 signal(SIGINT, SIG_DFL);
226 signal(SIGCONT, SIG_DFL);
227 return 0;
228}