util/sockroot

supervise.c in master
Repositories | Summary | Log | Files

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}