1
0
Fork 0
mirror of https://github.com/pygos/init.git synced 2024-12-22 23:30:50 +01:00

initd: restructure and merge service supervision code

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
David Oberhollenzer 2018-08-24 16:51:15 +02:00
parent 532f5e6819
commit ec74e5bbee
6 changed files with 238 additions and 214 deletions

View file

@ -1,5 +1,5 @@
init_SOURCES = initd/main.c initd/init.h initd/signal_linux.c initd/runsvc.c 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_CPPFLAGS = $(AM_CPPFLAGS)
init_CFLAGS = $(AM_CFLAGS) init_CFLAGS = $(AM_CFLAGS)
init_LDFLAGS = $(AM_LDFLAGS) init_LDFLAGS = $(AM_LDFLAGS)

View file

@ -18,6 +18,16 @@
#ifndef INIT_H #ifndef INIT_H
#define 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 <linux/reboot.h>
#include <sys/signalfd.h> #include <sys/signalfd.h>
#include <sys/reboot.h> #include <sys/reboot.h>
@ -36,6 +46,10 @@ enum {
STATUS_STARTED, STATUS_STARTED,
}; };
/********** main.c **********/
void target_completed(int target);
/********** runsvc.c **********/ /********** runsvc.c **********/
/* /*
@ -45,12 +59,6 @@ enum {
*/ */
pid_t runsvc(service_t *svc); 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 **********/ /********** status.c **********/
/* /*
@ -64,25 +72,15 @@ int runsvc_wait(service_t *svc);
*/ */
void print_status(const char *msg, int type, bool update); void print_status(const char *msg, int type, bool update);
/********** svclist.c **********/ /********** supervisor.c **********/
/* void supervisor_handle_exited(pid_t pid, int status);
Returns true if the list of running services contains
single shot processes.
*/
bool svclist_have_singleshot(void);
/* Add a service to the list of running services */ void supervisor_set_target(int next);
void svclist_add(service_t *svc);
/* void supervisor_init(void);
Remove a service, identifierd by PID, from the list of
running services.
Returns the service identified by the PID or NULL if there bool supervisor_process_queues(void);
is no such service.
*/
service_t *svclist_remove(pid_t pid);
/********** signal_<platform>.c **********/ /********** signal_<platform>.c **********/

View file

@ -15,60 +15,14 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * 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" #include "init.h"
static service_list_t cfg; static int ti_sock = -1;
static int sigfd = -1;
static int target = TGT_BOOT; /* runlevel we are targetting */ static void handle_signal(void)
static int runlevel = -1; /* runlevel we are currently on */
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)
{ {
struct signalfd_siginfo info; struct signalfd_siginfo info;
service_t *svc;
int status; int status;
pid_t pid; pid_t pid;
@ -83,10 +37,7 @@ static void handle_signal(int sigfd)
status = WIFEXITED(status) ? WEXITSTATUS(status) : status = WIFEXITED(status) ? WEXITSTATUS(status) :
EXIT_FAILURE; EXIT_FAILURE;
svc = svclist_remove(pid); supervisor_handle_exited(pid, status);
if (svc != NULL)
handle_exited(svc);
} }
break; break;
case SIGINT: 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) static int read_msg(int fd, ti_msg_t *msg)
{ {
ssize_t ret; ssize_t ret;
@ -151,7 +67,7 @@ retry:
return 0; return 0;
} }
static void handle_tellinit(int ti_sock) static void handle_tellinit(void)
{ {
ti_msg_t msg; ti_msg_t msg;
int fd; int fd;
@ -167,19 +83,31 @@ static void handle_tellinit(int ti_sock)
switch (msg.type) { switch (msg.type) {
case TI_SHUTDOWN: case TI_SHUTDOWN:
target = TGT_SHUTDOWN; supervisor_set_target(TGT_SHUTDOWN);
break; break;
case TI_REBOOT: case TI_REBOOT:
target = TGT_REBOOT; supervisor_set_target(TGT_REBOOT);
break; break;
} }
close(fd); 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 main(void)
{ {
int ti_sock = -1, sfd, ret, count; int ret, count;
struct pollfd pfd[2]; struct pollfd pfd[2];
if (getpid() != 1) { if (getpid() != 1) {
@ -187,44 +115,35 @@ int main(void)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (svcscan(SVCDIR, &cfg, RDSVC_NO_EXEC | RDSVC_NO_CTTY)) { supervisor_init();
fputs("Error reading service list from " SVCDIR "\n"
"Trying to continue anyway\n", stderr);
}
sfd = sigsetup(); sigfd = sigsetup();
if (sfd < 0) if (sigfd < 0)
return -1; return -1;
for (;;) {
while (supervisor_process_queues())
;
memset(pfd, 0, sizeof(pfd)); memset(pfd, 0, sizeof(pfd));
pfd[0].fd = sfd; pfd[0].fd = sigfd;
pfd[0].events = POLLIN; pfd[0].events = POLLIN;
count = 1; 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) { if (ti_sock != -1) {
pfd[1].fd = ti_sock; pfd[1].fd = ti_sock;
pfd[1].events = POLLIN; pfd[1].events = POLLIN;
count = 2; count = 2;
} }
}
continue;
}
ret = poll(pfd, count, -1); ret = poll(pfd, count, -1);
if (ret > 0) { if (ret > 0) {
if (pfd[0].revents & POLLIN) if (pfd[0].revents & POLLIN)
handle_signal(sfd); handle_signal();
if (ti_sock != -1 && pfd[1].revents & POLLIN) if (ti_sock != -1 && pfd[1].revents & POLLIN)
handle_tellinit(ti_sock); handle_tellinit();
} }
} }

View file

@ -49,18 +49,3 @@ pid_t runsvc(service_t *svc)
return pid; 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;
}

182
initd/supervisor.c Normal file
View file

@ -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;
}

View file

@ -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;
}