diff options
Diffstat (limited to 'initd')
| -rw-r--r-- | initd/Makemodule.am | 2 | ||||
| -rw-r--r-- | initd/init.h | 40 | ||||
| -rw-r--r-- | initd/main.c | 155 | ||||
| -rw-r--r-- | initd/runsvc.c | 15 | ||||
| -rw-r--r-- | initd/supervisor.c | 182 | ||||
| -rw-r--r-- | initd/svclist.c | 60 | 
6 files changed, 239 insertions, 215 deletions
| diff --git a/initd/Makemodule.am b/initd/Makemodule.am index f740885..54ae43d 100644 --- a/initd/Makemodule.am +++ b/initd/Makemodule.am @@ -1,5 +1,5 @@  init_SOURCES = initd/main.c initd/init.h initd/signal_linux.c initd/runsvc.c -init_SOURCES += initd/status.c initd/svclist.c +init_SOURCES += initd/status.c initd/supervisor.c  init_CPPFLAGS = $(AM_CPPFLAGS)  init_CFLAGS = $(AM_CFLAGS)  init_LDFLAGS = $(AM_LDFLAGS) diff --git a/initd/init.h b/initd/init.h index 2c39489..7d525a2 100644 --- a/initd/init.h +++ b/initd/init.h @@ -18,6 +18,16 @@  #ifndef INIT_H  #define INIT_H +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <poll.h> +  #include <linux/reboot.h>  #include <sys/signalfd.h>  #include <sys/reboot.h> @@ -36,6 +46,10 @@ enum {  	STATUS_STARTED,  }; +/********** main.c **********/ + +void target_completed(int target); +  /********** runsvc.c **********/  /* @@ -45,12 +59,6 @@ enum {  */  pid_t runsvc(service_t *svc); -/* -	Start a service using runsvc, but wait until the child process -	terminats and return its exit status. -*/ -int runsvc_wait(service_t *svc); -  /********** status.c **********/  /* @@ -64,25 +72,15 @@ int runsvc_wait(service_t *svc);  */  void print_status(const char *msg, int type, bool update); -/********** svclist.c **********/ +/********** supervisor.c **********/ -/* -	Returns true if the list of running services contains -	single shot processes. -*/ -bool svclist_have_singleshot(void); +void supervisor_handle_exited(pid_t pid, int status); -/* Add a service to the list of running services */ -void svclist_add(service_t *svc); +void supervisor_set_target(int next); -/* -	Remove a service, identifierd by PID, from the list of -	running services. +void supervisor_init(void); -	Returns the service identified by the PID or NULL if there -	is no such service. -*/ -service_t *svclist_remove(pid_t pid); +bool supervisor_process_queues(void);  /********** signal_<platform>.c **********/ diff --git a/initd/main.c b/initd/main.c index 2cacf12..78e8635 100644 --- a/initd/main.c +++ b/initd/main.c @@ -15,60 +15,14 @@   * You should have received a copy of the GNU General Public License   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <poll.h> -  #include "init.h" -static service_list_t cfg; - -static int target = TGT_BOOT;		/* runlevel we are targetting */ -static int runlevel = -1;		/* runlevel we are currently on */ +static int ti_sock = -1; +static int sigfd = -1; -static void handle_exited(service_t *svc) -{ -	switch (svc->type) { -	case SVC_RESPAWN: -		if (target == TGT_REBOOT || target == TGT_SHUTDOWN) -			break; - -		if (svc->rspwn_limit > 0) { -			svc->rspwn_limit -= 1; - -			if (svc->rspwn_limit == 0) { -				print_status(svc->desc, STATUS_FAIL, false); -				break; -			} -		} - -		svc->pid = runsvc(svc); -		if (svc->pid == -1) { -			print_status(svc->desc, STATUS_FAIL, false); -			break; -		} - -		svclist_add(svc); -		return; -	case SVC_ONCE: -		print_status(svc->desc, -			     svc->status == EXIT_SUCCESS ? -			     STATUS_OK : STATUS_FAIL, false); -		break; -	} -	delsvc(svc); -} - -static void handle_signal(int sigfd) +static void handle_signal(void)  {  	struct signalfd_siginfo info; -	service_t *svc;  	int status;  	pid_t pid; @@ -83,10 +37,7 @@ static void handle_signal(int sigfd)  			status = WIFEXITED(status) ? WEXITSTATUS(status) :  						     EXIT_FAILURE; -			svc = svclist_remove(pid); - -			if (svc != NULL) -				handle_exited(svc); +			supervisor_handle_exited(pid, status);  		}  		break;  	case SIGINT: @@ -95,41 +46,6 @@ static void handle_signal(int sigfd)  	}  } -static void start_runlevel(int level) -{ -	service_t *svc; -	int status; - -	while (cfg.targets[level] != NULL) { -		svc = cfg.targets[level]; -		cfg.targets[level] = svc->next; - -		if (svc->type == SVC_WAIT) { -			print_status(svc->desc, STATUS_WAIT, false); - -			status = runsvc_wait(svc); - -			print_status(svc->desc, -				     status == EXIT_SUCCESS ? -				     STATUS_OK : STATUS_FAIL, -				     true); -			delsvc(svc); -		} else { -			svc->pid = runsvc(svc); -			if (svc->pid == -1) { -				print_status(svc->desc, STATUS_FAIL, false); -				delsvc(svc); -				continue; -			} - -			if (svc->type == SVC_RESPAWN) -				print_status(svc->desc, STATUS_STARTED, false); - -			svclist_add(svc); -		} -	} -} -  static int read_msg(int fd, ti_msg_t *msg)  {  	ssize_t ret; @@ -151,7 +67,7 @@ retry:  	return 0;  } -static void handle_tellinit(int ti_sock) +static void handle_tellinit(void)  {  	ti_msg_t msg;  	int fd; @@ -167,19 +83,31 @@ static void handle_tellinit(int ti_sock)  	switch (msg.type) {  	case TI_SHUTDOWN: -		target = TGT_SHUTDOWN; +		supervisor_set_target(TGT_SHUTDOWN);  		break;  	case TI_REBOOT: -		target = TGT_REBOOT; +		supervisor_set_target(TGT_REBOOT);  		break;  	}  	close(fd);  } +void target_completed(int target) +{ +	switch (target) { +	case TGT_BOOT: +		if (ti_sock == -1) +			ti_sock = mksock(INITSOCK, SOCK_FLAG_ROOT_ONLY); +		break; +	default: +		break; +	} +} +  int main(void)  { -	int ti_sock = -1, sfd, ret, count; +	int ret, count;  	struct pollfd pfd[2];  	if (getpid() != 1) { @@ -187,44 +115,35 @@ int main(void)  		return EXIT_FAILURE;  	} -	if (svcscan(SVCDIR, &cfg, RDSVC_NO_EXEC | RDSVC_NO_CTTY)) { -		fputs("Error reading service list from " SVCDIR "\n" -			"Trying to continue anyway\n", stderr); -	} +	supervisor_init(); -	sfd = sigsetup(); -	if (sfd < 0) +	sigfd = sigsetup(); +	if (sigfd < 0)  		return -1; -	memset(pfd, 0, sizeof(pfd)); -	pfd[0].fd = sfd; -	pfd[0].events = POLLIN; -	count = 1; -  	for (;;) { -		if (!svclist_have_singleshot() && target != runlevel) { -			start_runlevel(target); -			runlevel = target; - -			if (target == TGT_BOOT && ti_sock == -1) { -				ti_sock = mksock(INITSOCK, SOCK_FLAG_ROOT_ONLY); -				if (ti_sock != -1) { -					pfd[1].fd = ti_sock; -					pfd[1].events = POLLIN; -					count = 2; -				} -			} -			continue; +		while (supervisor_process_queues()) +			; + +		memset(pfd, 0, sizeof(pfd)); +		pfd[0].fd = sigfd; +		pfd[0].events = POLLIN; +		count = 1; + +		if (ti_sock != -1) { +			pfd[1].fd = ti_sock; +			pfd[1].events = POLLIN; +			count = 2;  		}  		ret = poll(pfd, count, -1);  		if (ret > 0) {  			if (pfd[0].revents & POLLIN) -				handle_signal(sfd); +				handle_signal();  			if (ti_sock != -1 && pfd[1].revents & POLLIN) -				handle_tellinit(ti_sock); +				handle_tellinit();  		}  	} diff --git a/initd/runsvc.c b/initd/runsvc.c index aba83b1..1b23cb6 100644 --- a/initd/runsvc.c +++ b/initd/runsvc.c @@ -49,18 +49,3 @@ pid_t runsvc(service_t *svc)  	return pid;  } - -int runsvc_wait(service_t *svc) -{ -	pid_t ret, pid = runsvc(svc); -	int status; - -	if (pid == -1) -		return EXIT_FAILURE; - -	do { -		ret = waitpid(pid, &status, 0); -	} while (ret != pid); - -	return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; -} diff --git a/initd/supervisor.c b/initd/supervisor.c new file mode 100644 index 0000000..f439f6d --- /dev/null +++ b/initd/supervisor.c @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ +#include "init.h" + +static service_list_t cfg; + +static int target = -1; +static service_t *running = NULL; +static service_t *terminated = NULL; +static service_t *queue = NULL; +static int singleshot = 0; +static bool waiting = false; + +static int start_service(service_t *svc) +{ +	svc->pid = runsvc(svc); +	if (svc->pid == -1) { +		print_status(svc->desc, STATUS_FAIL, false); +		delsvc(svc); +		return -1; +	} + +	svc->next = running; +	running = svc; +	return 0; +} + +static void handle_terminated_service(service_t *svc) +{ +	switch (svc->type) { +	case SVC_RESPAWN: +		if (target == TGT_REBOOT || target == TGT_SHUTDOWN) +			break; + +		if (svc->rspwn_limit > 0) { +			svc->rspwn_limit -= 1; + +			if (svc->rspwn_limit == 0) { +				print_status(svc->desc, STATUS_FAIL, false); +				break; +			} +		} + +		start_service(svc); +		return; +	case SVC_WAIT: +		waiting = false; +		print_status(svc->desc, +			     svc->status == EXIT_SUCCESS ? +			     STATUS_OK : STATUS_FAIL, true); +		if (singleshot == 0 && queue == NULL) +			target_completed(target); +		break; +	case SVC_ONCE: +		singleshot -= 1; +		print_status(svc->desc, +			     svc->status == EXIT_SUCCESS ? +			     STATUS_OK : STATUS_FAIL, false); +		if (singleshot == 0 && queue == NULL && !waiting) +			target_completed(target); +		break; +	} +	delsvc(svc); +} + +void supervisor_handle_exited(pid_t pid, int status) +{ +	service_t *prev = NULL, *svc = running; + +	while (svc != NULL && svc->pid != pid) { +		prev = svc; +		svc = svc->next; +	} + +	if (svc == NULL) +		return; + +	if (prev != NULL) { +		prev->next = svc->next; +	} else { +		running = svc->next; +	} + +	svc->status = status; +	svc->next = terminated; +	terminated = svc; +} + +void supervisor_set_target(int next) +{ +	service_t *svc; + +	if (target == TGT_REBOOT || target == TGT_SHUTDOWN || next == target) +		return; + +	if (next == TGT_REBOOT || next == TGT_SHUTDOWN) { +		while (queue != NULL) { +			svc = queue; +			queue = queue->next; +			delsvc(svc); +		} +	} + +	if (queue != NULL) { +		for (svc = queue; svc->next != NULL; svc = svc->next) +			; +		svc->next = cfg.targets[next]; +	} else { +		queue = cfg.targets[next]; +	} + +	cfg.targets[next] = NULL; +	target = next; +} + +void supervisor_init(void) +{ +	int status = STATUS_OK; + +	if (svcscan(SVCDIR, &cfg, RDSVC_NO_EXEC | RDSVC_NO_CTTY)) +		status = STATUS_FAIL; + +	target = TGT_BOOT; +	queue = cfg.targets[TGT_BOOT]; +	cfg.targets[TGT_BOOT] = NULL; + +	print_status("reading configuration from " SVCDIR, status, false); +} + +bool supervisor_process_queues(void) +{ +	service_t *svc; + +	if (terminated != NULL) { +		svc = terminated; +		terminated = terminated->next; + +		handle_terminated_service(svc); +		return true; +	} + +	if (waiting || queue == NULL) +		return false; + +	svc = queue; +	queue = queue->next; + +	if (start_service(svc) != 0) +		return true; + +	switch (svc->type) { +	case SVC_WAIT: +		print_status(svc->desc, STATUS_WAIT, false); +		waiting = true; +		break; +	case SVC_RESPAWN: +		print_status(svc->desc, STATUS_STARTED, false); +		break; +	case SVC_ONCE: +		singleshot += 1; +		break; +	} + +	if (singleshot == 0 && queue == NULL && !waiting) +		target_completed(target); +	return true; +} diff --git a/initd/svclist.c b/initd/svclist.c deleted file mode 100644 index 4ed897f..0000000 --- a/initd/svclist.c +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program.  If not, see <https://www.gnu.org/licenses/>. - */ -#include "init.h" - -static service_t *running = NULL;	/* currently supervised services */ -static int singleshot = 0;		/* active singleshot services */ - -bool svclist_have_singleshot(void) -{ -	return singleshot > 0; -} - -void svclist_add(service_t *svc) -{ -	svc->next = running; -	running = svc; - -	if (svc->type == SVC_ONCE) -		singleshot += 1; -} - -service_t *svclist_remove(pid_t pid) -{ -	service_t *prev = NULL, *svc = running; - -	while (svc != NULL) { -		if (svc->pid == pid) { -			if (prev != NULL) { -				prev->next = svc->next; -			} else { -				running = svc->next; -			} -			svc->next = NULL; - -			if (svc->type == SVC_ONCE) -				singleshot -= 1; -			break; -		} - -		prev = svc; -		svc = svc->next; -	} - -	return svc; -} | 
