1
0
Fork 0
mirror of https://github.com/pygos/init.git synced 2024-05-19 04:06:13 +02:00

Compare commits

...

42 commits
v0.9 ... master

Author SHA1 Message Date
David Oberhollenzer 5307b95b93 Cleanup: remove flag mechanism from config parser entirely
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2020-04-24 12:28:06 +02:00
David Oberhollenzer 70ea16b0b4 Cleanup: remove rdsvc flags
With the previous changes, there were only used by the status
command.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2020-04-24 12:26:26 +02:00
David Oberhollenzer 5f28289731 cleanup: merge runsvc back into initd
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2020-04-24 12:09:18 +02:00
David Oberhollenzer 0975ed0fb7 runsvc: make sure we close all fds before running a service
Just in case initd leaks anything. Also, the service has no
buisness writing all over /dev/console. It's a system service, it
better use syslog or its own internal logging service.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2020-04-06 15:55:47 +02:00
David Oberhollenzer 9b43890591 cleanup: simplify runsvc environment config parsing
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2020-04-06 15:44:15 +02:00
David Oberhollenzer 87a524d931 cleanup: delete remains of libutil
- exec_t belongs to service.h, the main place where it is used/needed
 - code for executing exec_t is moved to runsvc for the same reason
 - what is left are NORETURN and ARRAY_SIZE
   - the former can be replaced with direct attribute usage since
     the only relevant compilers all support the attribute.
   - the later is only used in 3 places and can be trivially replaced
     with direct usage of sizeof().

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2020-03-31 18:19:27 +02:00
David Oberhollenzer 9f9807d4d3 cleanup: initd: simplify and merge linux specific code into main.c
Targetting anything else than Linux isn't really relevant. All
other systems ($BSD and other Unices) are a closed ecosystem
where kernel & userspace are developed together. They don't need
something like a third party init system, so compatibillity can
be largely ignored.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2020-03-31 13:09:04 +02:00
David Oberhollenzer 0d985a7430 Add RDSVC_NO_DESC flag
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-06-16 20:51:23 +02:00
David Oberhollenzer 60efd9dc33 Remove unused SOCK_FLAG_* enum
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-06-16 18:20:26 +02:00
David Oberhollenzer 1c72dd2c2f Fix remove by id
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-06-16 16:09:49 +02:00
David Oberhollenzer 2e89d32a75 Release init-0.10
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer c1cb8491f9 fix: actually remove started service from list
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer c8c0f10ce1 Keep original respawn limit
We also want this meachanism to still work for manually started
service (especially after reloading services).

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer be06641904 cleanup: init socket wire format
Replace array adhockery with structs and make use of the handy
endianness conversion macros.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer c3d14cbfa8 cleanup: init status response
- rename init_status_response_t to init_status_t
 - merge code for handling it
 - fix memory leak in status command

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer 7cfe6e8458 cleanup: move init specific stuff of init socket to initd
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer f844c4e2c2 Update documentation
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer 028394b8a5 Add service configuration reloading
This commit add the ability to initd to reload the service configuration
while running. The new configuration is merged with the existing one as
follows:

For each target:
 - If the existing service list is not NULL, we have not started that
   target yet. Simply replace it with the new list.
 - If it is NULL, the services have already been started.
    - First, remove all entries for services in that target that no
      loner exist (except from the 'running' list).
    - Second, add new services that we don't have yet. Treat them as
      recently diseased and let the user start them manualy.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-29 21:00:53 +01:00
David Oberhollenzer 6fa0393be4 Add start/stop commands to service tool
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-28 15:32:10 +01:00
David Oberhollenzer ba12700080 Add start/stop commands to init socket
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-28 15:32:10 +01:00
David Oberhollenzer 9e7478397a Include service ID in initsock status response
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-28 15:19:06 +01:00
David Oberhollenzer c16735414b initd: Hand out unique IDs to services
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-28 13:45:33 +01:00
David Oberhollenzer d16d260181 Add filter argument to status request
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-27 17:48:32 +01:00
David Oberhollenzer affe9e4b88 Use fixed size integer for init socket request
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-27 17:48:32 +01:00
David Oberhollenzer 72c02308cd Fix: actually process SIGUSR1 in initd
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-25 16:32:45 +01:00
David Oberhollenzer 9fece2eb88 Add hacky utiliy that waits for the presence of some files
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-24 18:02:17 +01:00
David Oberhollenzer ec04681c4d Make service status matching fuzzier (also allow service name match)
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-21 17:11:52 +01:00
David Oberhollenzer a6c059203b Fix warnings from gcc 8.3 + musl build
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-20 20:43:41 +01:00
David Oberhollenzer e21840cfce initd: don't start runsvc for services without exec block
First in rdsvc, tag the services that *do* have exec lines, even if we don't
read them.

Second, if a service does not have that flag set, don't try to execute it.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-20 15:44:14 +01:00
David Oberhollenzer 390175c406 cleanup runsvc: merge codepaths for execution, remove cleanup code
Simply execute the last entry in the list directly instead of forking and
remove the cleanup code.

If the list is empty, we return success.

If the list only has one entry, we directly execute that. No need to make a
distinction between single entry vs list anymore.

If the list is an actual list, we run it as before but execute the last one
directly. Typically, the last one is something like a daemon preceeded by
setup code. The daemon ends up directly underneath init, without a dummy
waiting runsvc stuck in the process list.

If we always do an exec, there is no point in doing cleanup. All our mapped
memory is evicted anyway. Same if we exit appruptly because of an error.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-20 15:09:35 +01:00
David Oberhollenzer 4f1b393cee Dump more information in service status command
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-20 14:47:45 +01:00
David Oberhollenzer 1850f31d6d Seperate service loading/error loging from dumpscript command
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-19 23:53:49 +01:00
David Oberhollenzer 065d3b678d cleanup: remove broken stat ... open pattern
confused deputy is confused.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 19:49:42 +01:00
David Oberhollenzer 11053ebe6a Add filtering parameters to status command
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 19:16:34 +01:00
David Oberhollenzer a9602ad6e0 Cleanup status reporting
- mimic format of initd
 - skip formatting if not a tty
 - distinguish exited because failed vs exited because done

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 18:47:20 +01:00
David Oberhollenzer 3f40c4d3ed Add status report command to service command line utility
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 18:42:38 +01:00
David Oberhollenzer 40ad83dc6a initd: implement handling of socket requests
Actually process requests and send an answer to status inquiries.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 18:29:02 +01:00
David Oberhollenzer 23b713c3b5 Add functions to transmit service status accross initd socket
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 18:29:02 +01:00
David Oberhollenzer 08f72865b2 Add init socket to initd
Create a socket if boot target is done. Close and reopen socket
if SIGUSR1 is received.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 14:15:58 +01:00
David Oberhollenzer c78bbd2f73 Add helpers for initd socket
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-18 14:15:58 +01:00
David Oberhollenzer c544fcc7a3 initd: store completed services in list instead of deleting them
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-15 10:46:11 +01:00
David Oberhollenzer 5b106abaed Update e-mail address
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-03-14 17:18:34 +01:00
43 changed files with 1277 additions and 500 deletions

1
.gitignore vendored
View file

@ -21,5 +21,6 @@ shutdown
killall5
runsvc
gcrond
waitfile
etc/initd.env

View file

@ -1,4 +1,4 @@
Copyright (c) 2018 David Oberhollenzer <david.oberhollenzer@tele2.at>
Copyright (c) 2018 David Oberhollenzer <goliath@infraroot.at>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View file

@ -3,31 +3,32 @@ shutdown_CPPFLAGS = $(AM_CPPFLAGS)
shutdown_CFLAGS = $(AM_CFLAGS)
shutdown_LDFLAGS = $(AM_LDFLAGS)
runsvc_SOURCES = cmd/runsvc/runsvc.c cmd/runsvc/env.c cmd/runsvc/runsvc.h
runsvc_CPPFLAGS = $(AM_CPPFLAGS)
runsvc_CFLAGS = $(AM_CFLAGS)
runsvc_LDFLAGS = $(AM_LDFLAGS)
runsvc_LDADD = libinit.a libcfg.a libutil.a
killall5_SOURCES = cmd/killall5.c
killall5_CPPFLAGS = $(AM_CPPFLAGS)
killall5_CFLAGS = $(AM_CFLAGS)
killall5_LDFLAGS = $(AM_LDFLAGS)
waitfile_SOURCES = cmd/waitfile.c
waitfile_CPPFLAGS = $(AM_CPPFLAGS)
waitfile_CFLAGS = $(AM_CFLAGS)
waitfile_LDFLAGS = $(AM_LDFLAGS)
SRVHEADERS = cmd/service/servicecmd.h
service_SOURCES = cmd/service/servicecmd.c cmd/service/help.c
service_SOURCES += cmd/service/enable.c cmd/service/disable.c
service_SOURCES += cmd/service/dumpscript.c cmd/service/list.c
service_SOURCES += cmd/service/status.c cmd/service/loadsvc.c
service_SOURCES += cmd/service/startstop.c
service_SOURCES += $(SRVHEADERS)
service_CPPFLAGS = $(AM_CPPFLAGS)
service_CFLAGS = $(AM_CFLAGS)
service_LDFLAGS = $(AM_LDFLAGS)
service_LDADD = libinit.a libcfg.a libutil.a
service_LDADD = libinit.a libcfg.a
dist_man8_MANS += cmd/shutdown.8 cmd/service/service.8
EXTRA_DIST += $(SRVHEADERS)
sbin_PROGRAMS += service shutdown
helper_PROGRAMS += killall5 runsvc
helper_PROGRAMS += killall5 waitfile

View file

@ -9,9 +9,7 @@
#include <ctype.h>
#include <errno.h>
#include "util.h"
static NORETURN void usage_and_exit(void)
static __attribute__((noreturn)) void usage_and_exit(void)
{
fputs("Usage: killall5 SIGNAL\n", stderr);
exit(EXIT_FAILURE);

View file

@ -1,109 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "runsvc.h"
struct entry {
struct entry *next;
char data[];
};
extern char **environ;
static void free_list(struct entry *list)
{
struct entry *e;
while (list != NULL) {
e = list;
list = list->next;
free(e);
}
}
static struct entry *parse_list(rdline_t *rd)
{
struct entry *e, *list = NULL;
char *ptr;
while (rdline(rd) == 0) {
ptr = rd->line;
while (*ptr != '\0' && *ptr != ' ' && *ptr != '=')
++ptr;
if (*ptr == ' ')
memmove(ptr, ptr + 1, strlen(ptr + 1) + 1);
if (*(ptr++) != '=') {
fprintf(stderr, "%s: %zu: line is not of the shape "
"'key = value', skipping\n",
rd->filename, rd->lineno);
continue;
}
if (*ptr == ' ')
memmove(ptr, ptr + 1, strlen(ptr + 1) + 1);
if (unescape(ptr)) {
fprintf(stderr, "%s: %zu: malformed string constant, "
"skipping\n",
rd->filename, rd->lineno);
continue;
}
e = calloc(1, sizeof(*e) + strlen(rd->line) + 1);
if (e == NULL)
goto fail_oom;
strcpy(e->data, rd->line);
e->next = list;
list = e;
}
return list;
fail_oom:
fputs("out of memory\n", stderr);
free_list(list);
return NULL;
}
static struct entry *list_from_file(void)
{
struct entry *list;
rdline_t rd;
if (rdline_init(&rd, AT_FDCWD, ENVFILE, 0, NULL))
return NULL;
list = parse_list(&rd);
rdline_cleanup(&rd);
return list;
}
int initenv(void)
{
struct entry *list, *e;
int i, count;
char **envp;
list = list_from_file();
if (list == NULL)
return -1;
for (count = 0, e = list; e != NULL; e = e->next)
++count;
envp = malloc((count + 1) * sizeof(char *));
if (envp == NULL) {
fputs("out of memory\n", stderr);
free_list(list);
return -1;
}
for (i = 0, e = list; e != NULL; e = e->next)
envp[i] = e->data;
envp[i] = NULL;
environ = envp;
return 0;
}

View file

@ -1,80 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "runsvc.h"
static int runlst_wait(exec_t *list)
{
pid_t ret, pid;
int status;
for (; list != NULL; list = list->next) {
pid = fork();
if (pid == 0)
argv_exec(list);
if (pid == -1) {
perror("fork");
return EXIT_FAILURE;
}
do {
ret = waitpid(pid, &status, 0);
} while (ret != pid);
if (!WIFEXITED(status))
return EXIT_FAILURE;
if (WEXITSTATUS(status) != EXIT_SUCCESS)
return WEXITSTATUS(status);
}
return EXIT_SUCCESS;
}
/*****************************************************************************/
int main(int argc, char **argv)
{
int dirfd, ret = EXIT_FAILURE;
service_t *svc = NULL;
if (argc != 3) {
fputs("usage: runsvc <directory> <filename>\n", stderr);
goto out;
}
if (getppid() != 1) {
fputs("must be run by init!\n", stderr);
goto out;
}
dirfd = open(argv[1], O_RDONLY | O_DIRECTORY);
if (dirfd < 0) {
perror(argv[1]);
goto out;
}
svc = rdsvc(dirfd, argv[2], RDSVC_NO_FNAME | RDSVC_NO_DEPS);
close(dirfd);
if (svc == NULL)
goto out;
if (svc->exec == NULL) {
ret = EXIT_SUCCESS;
goto out;
}
if (initenv())
goto out;
if (setup_tty(svc->ctty, (svc->flags & SVC_FLAG_TRUNCATE_OUT) != 0))
goto out;
if (svc->exec->next == NULL)
argv_exec(svc->exec);
ret = runlst_wait(svc->exec);
out:
delsvc(svc);
return ret;
}

View file

@ -1,21 +0,0 @@
/* SPDX-License-Identifier: ISC */
#ifndef RUNSVC_H
#define RUNSVC_H
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include "service.h"
#include "libcfg.h"
#include "util.h"
#define ENVFILE ETCPATH "/initd.env"
int initenv(void);
#endif /* RUNSVC_H */

View file

@ -4,6 +4,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
@ -55,6 +56,8 @@ static int cmd_disable(int argc, char **argv)
goto out;
}
kill(1, SIGHUP);
ret = EXIT_SUCCESS;
out:
free(linkname);

View file

@ -6,37 +6,6 @@
#include <errno.h>
#include "servicecmd.h"
#include "service.h"
static service_t *try_load(const char *directory, const char *filename)
{
int dirfd, type;
struct stat sb;
service_t *svc;
dirfd = open(directory, O_RDONLY | O_DIRECTORY);
if (dirfd < 0) {
perror(directory);
return NULL;
}
if (fstatat(dirfd, filename, &sb, AT_SYMLINK_NOFOLLOW)) {
fprintf(stderr, "stat %s/%s: %s\n",
directory, filename, strerror(errno));
close(dirfd);
return NULL;
}
type = (sb.st_mode & S_IFMT);
if (type != S_IFREG && type != S_IFLNK)
return NULL;
svc = rdsvc(dirfd, filename, 0);
close(dirfd);
return svc;
}
enum {
NEED_QUOTES = 0x01,
@ -124,7 +93,7 @@ static int cmd_dumpscript(int argc, char **argv)
strcat(filename, argv[i]);
}
svc = try_load(SVCDIR, filename);
svc = loadsvc(SVCDIR, filename);
if (svc == NULL) {
fprintf(stderr, "Could not load service '%s'\n", filename);

View file

@ -4,6 +4,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
@ -60,6 +61,8 @@ static int cmd_enable(int argc, char **argv)
goto out;
}
kill(1, SIGHUP);
ret = EXIT_SUCCESS;
out:
free(linkname);

View file

@ -24,7 +24,7 @@ static int cmd_list(int argc, char **argv)
if (check_arguments(argv[0], argc, 1, 2))
return EXIT_FAILURE;
if (svcscan(SVCDIR, &list, 0)) {
if (svcscan(SVCDIR, &list)) {
fprintf(stderr, "Error while reading services from %s\n",
SVCDIR);
ret = EXIT_FAILURE;

23
cmd/service/loadsvc.c Normal file
View file

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: ISC */
#include "servicecmd.h"
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
service_t *loadsvc(const char *directory, const char *filename)
{
service_t *svc;
int dirfd;
dirfd = open(directory, O_RDONLY | O_DIRECTORY);
if (dirfd < 0) {
perror(directory);
return NULL;
}
svc = rdsvc(dirfd, filename);
close(dirfd);
return svc;
}

View file

@ -38,6 +38,17 @@ the desired service instance.
.BR dumpscript " " \fI<service>\fP " " \fI[arguments]\fP
Parse a service file from and produce a pseudo shell script containing the
exact commands executed when starting the service.
.TP
.BR status " " \fI[--detail|-d]\fP " " \fI[services...]\fP
Print a status report of all supervised services, i.e. if they are currently
running, have exited or waiting to be scheduled. A specific list of services
can be specified. Shell globbing patterns can be used.
.TP
.BR start " " \fIservices...\fP
Start one or more currently not running services. Shell globbing patterns can be used.
.TP
.BR stop " " \fIservices...\fP
Stop one or more currently running services. Shell globbing patterns can be used.
.SH AVAILABILITY
This program is part of the Pygos init system.
.SH COPYRIGHT

View file

@ -5,7 +5,6 @@
#include "servicecmd.h"
#include "service.h"
#include "config.h"
#include "util.h"
command_t *commands;

View file

@ -2,12 +2,14 @@
#ifndef SERVICECMD_H
#define SERVICECMD_H
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "util.h"
#include "service.h"
#include "config.h"
/*
Describes a command that can be launched by passing its name as
@ -38,11 +40,13 @@ typedef struct command_t {
/* Global list of available commands */
extern command_t *commands;
service_t *loadsvc(const char *directory, const char *filename);
/*
Implemented in servicecmd.c. Prints program usage message and
terminates with the given exit status.
*/
void usage(int status) NORETURN;
void usage(int status) __attribute__((noreturn));
/*
Write a message to stderr that advises the user how to consult the

104
cmd/service/startstop.c Normal file
View file

@ -0,0 +1,104 @@
/* SPDX-License-Identifier: ISC */
#include "servicecmd.h"
#include "initsock.h"
#include "service.h"
#include "config.h"
#include <fnmatch.h>
#include <getopt.h>
#include <unistd.h>
static int cmd_startstop(int argc, char **argv,
E_SERVICE_STATE filter, E_INIT_REQUEST action)
{
int i, fd, ret = EXIT_FAILURE;
init_status_t resp;
char tmppath[256];
bool found;
if (check_arguments(argv[0], argc, 2, 2))
return EXIT_FAILURE;
sprintf(tmppath, "/tmp/svcstatus.%d.sock", (int)getpid());
fd = init_socket_open(tmppath);
if (fd < 0) {
unlink(tmppath);
return EXIT_FAILURE;
}
if (init_socket_send_request(fd, EIR_STATUS, filter))
goto out;
for (;;) {
memset(&resp, 0, sizeof(resp));
if (init_socket_recv_status(fd, &resp)) {
perror("reading from initd socket");
free_init_status(&resp);
goto out;
}
if (resp.state == ESS_NONE) {
free_init_status(&resp);
break;
}
found = false;
for (i = optind; i < argc; ++i) {
if (fnmatch(argv[i], resp.service_name, 0) == 0) {
found = true;
break;
}
if (fnmatch(argv[i], resp.filename, 0) == 0) {
found = true;
break;
}
}
if (found) {
if (init_socket_send_request(fd, action, resp.id))
goto out;
}
free_init_status(&resp);
}
ret = EXIT_SUCCESS;
out:
close(fd);
unlink(tmppath);
return ret;
}
static int cmd_start(int argc, char **argv)
{
return cmd_startstop(argc, argv, ESS_NONE, EIR_START);
}
static int cmd_stop(int argc, char **argv)
{
return cmd_startstop(argc, argv, ESS_NONE, EIR_STOP);
}
static command_t start = {
.cmd = "start",
.usage = "services...",
.s_desc = "start a currently not running service",
.l_desc = "Start one or more service that are currently not running. "
"Shell style globbing patterns can used for service names.",
.run_cmd = cmd_start,
};
static command_t stop = {
.cmd = "stop",
.usage = "services...",
.s_desc = "stop a currently running service",
.l_desc = "Stop one or more service that are currently running. "
"Shell style globbing patterns can used for service names.",
.run_cmd = cmd_stop,
};
REGISTER_COMMAND(start)
REGISTER_COMMAND(stop)

170
cmd/service/status.c Normal file
View file

@ -0,0 +1,170 @@
/* SPDX-License-Identifier: ISC */
#include "servicecmd.h"
#include "initsock.h"
#include "service.h"
#include "config.h"
#include <fnmatch.h>
#include <getopt.h>
#include <unistd.h>
static const struct option long_opts[] = {
{ "detail", no_argument, NULL, 'd' },
{ NULL, 0, NULL, 0 },
};
static const char *short_opts = "d";
static int cmd_status(int argc, char **argv)
{
bool is_tty, found, show_details = false;
int i, fd, ret = EXIT_FAILURE;
init_status_t resp;
char tmppath[256];
const char *state;
service_t *svc;
for (;;) {
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
if (i == -1)
break;
switch (i) {
case 'd':
show_details = true;
break;
default:
tell_read_help(argv[0]);
return EXIT_FAILURE;
}
}
sprintf(tmppath, "/tmp/svcstatus.%d.sock", (int)getpid());
fd = init_socket_open(tmppath);
if (fd < 0) {
unlink(tmppath);
return EXIT_FAILURE;
}
if (init_socket_send_request(fd, EIR_STATUS, ESS_NONE))
goto out;
is_tty = (isatty(STDOUT_FILENO) == 1);
for (;;) {
memset(&resp, 0, sizeof(resp));
if (init_socket_recv_status(fd, &resp)) {
perror("reading from initd socket");
free_init_status(&resp);
goto out;
}
if (resp.state == ESS_NONE) {
free_init_status(&resp);
break;
}
if (optind < argc) {
found = false;
for (i = optind; i < argc; ++i) {
if (fnmatch(argv[i],
resp.service_name, 0) == 0) {
found = true;
break;
}
if (fnmatch(argv[i], resp.filename, 0) == 0) {
found = true;
break;
}
}
if (!found) {
free_init_status(&resp);
continue;
}
}
switch (resp.state) {
case ESS_RUNNING:
if (!is_tty) {
state = "UP";
break;
}
state = "\033[22;32m UP \033[0m";
break;
case ESS_ENQUEUED:
state = "SCHED";
break;
case ESS_FAILED:
if (!is_tty) {
state = "FAIL";
break;
}
state = "\033[22;31mFAIL\033[0m";
break;
case ESS_DONE:
if (!is_tty) {
state = "DONE";
break;
}
state = "\033[22;33mDONE\033[0m";
break;
default:
if (!is_tty) {
state = "UNKNOWN";
break;
}
state = "\033[22;31mUNKNOWN\033[0m";
break;
}
if (show_details) {
printf("Service: %s\n", resp.filename);
printf("\tStatus: %s\n", state);
printf("\tTemplate name: %s\n", resp.service_name);
printf("\tExit status: %d\n", resp.exit_status);
svc = loadsvc(SVCDIR, resp.filename);
if (svc == NULL) {
fputs("\tError loading service file\n", stdout);
} else {
printf("\tDescription: %s\n", svc->desc);
printf("\tType: %s\n",
svc_type_to_string(svc->type));
printf("\tTarget: %s\n",
svc_target_to_string(svc->target));
delsvc(svc);
}
} else {
printf("[%s] %s\n", state, resp.filename);
}
free_init_status(&resp);
}
ret = EXIT_SUCCESS;
out:
close(fd);
unlink(tmppath);
return ret;
}
static command_t status = {
.cmd = "status",
.usage = "[-d|--detail] [services...]",
.s_desc = "report the status of the currently enabled services",
.l_desc = "Gathers a list of all currently running services and the "
"state that they are in (currently running, done, failed, "
"wating to get scheduled). A list of services with wildcard "
"globbing patterns can be specified. If ommitted, produces "
"a general overview of all services. If the --detail "
"is given, more details are shown about a service.",
.run_cmd = cmd_status,
};
REGISTER_COMMAND(status)

View file

@ -10,8 +10,6 @@
#include <sys/reboot.h>
#include <linux/reboot.h>
#include "util.h"
#define FL_FORCE 0x01
#define FL_NOSYNC 0x02
@ -29,7 +27,7 @@ static const char *shortopt = "hprfn";
static const char *defact_str = "power-off";
static int defact = RB_POWER_OFF;
static NORETURN void usage(const char *progname, int status)
static __attribute__((noreturn)) void usage(const char *progname, int status)
{
fprintf(status == EXIT_SUCCESS ? stdout : stderr,
"%s [OPTIONS...]\n\n"

82
cmd/waitfile.c Normal file
View file

@ -0,0 +1,82 @@
/* SPDX-License-Identifier: ISC */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <poll.h>
static int strtoui(const char *str)
{
int i = 0;
if (!isdigit(*str))
return -1;
while (isdigit(*str)) {
if (i > (INT_MAX / 10))
return -1;
i = i * 10 + (*(str++)) - '0';
}
if (*str != '\0')
return -1;
return i;
}
static void sigproc(int signo)
{
if (signo == SIGALRM) {
fputs("waitfile timeout\n", stderr);
exit(EXIT_FAILURE);
}
}
int main(int argc, char **argv)
{
int i, found, timeout, probetime;
struct stat sb;
if (argc < 4)
goto fail_usage;
timeout = strtoui(argv[1]);
probetime = strtoui(argv[2]);
if (timeout < 0 || probetime < 0)
goto fail_timeout;
signal(SIGALRM, sigproc);
alarm(timeout);
for (;;) {
found = 1;
for (i = 3; i < argc; ++i) {
if (stat(argv[i], &sb) != 0) {
found = 0;
break;
}
}
if (found) {
alarm(0);
break;
}
poll(NULL, 0, probetime);
}
return EXIT_SUCCESS;
fail_timeout:
fputs("Timeout values must be integers!\n", stderr);
goto fail_usage;
fail_usage:
fputs("Usage: waitfile <timeout secs> <probe time ms> FILES...\n",
stderr);
return EXIT_FAILURE;
}

View file

@ -1,8 +1,8 @@
AC_PREREQ([2.60])
m4_define([RELEASE], 0.9)
m4_define([RELEASE], 0.10)
AC_INIT([init], [RELEASE], [david.oberhollenzer@tele2.at], init)
AC_INIT([init], [RELEASE], [goliath@infraroot.at], init)
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign subdir-objects dist-xz])
AM_SILENT_RULES([yes])

View file

@ -18,6 +18,10 @@ Currently available service commands are:
* list - list all enabled service. A target can be specified to only list
services for the specified target.
* help - display a short help text and a list of available commands.
* start - start one or more services listed on the command line.
* stop - stop one or more services listed on the command line.
* status - display status of all services or the ones specified
on the command line.
## shutdown and reboot

View file

@ -1,8 +1,8 @@
init_SOURCES = initd/main.c initd/init.h initd/signal_linux.c initd/runsvc.c
init_SOURCES += initd/status.c initd/supervisor.c
init_SOURCES = initd/main.c initd/init.h initd/runsvc.c
init_SOURCES += initd/status.c initd/supervisor.c initd/initsock.c
init_CPPFLAGS = $(AM_CPPFLAGS)
init_CFLAGS = $(AM_CFLAGS)
init_LDFLAGS = $(AM_LDFLAGS)
init_LDADD = libinit.a libcfg.a libutil.a
init_LDADD = libinit.a libcfg.a
sbin_PROGRAMS += init

View file

@ -5,9 +5,11 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <endian.h>
#include <stdio.h>
#include <errno.h>
#include <poll.h>
@ -15,12 +17,15 @@
#include <linux/reboot.h>
#include <sys/signalfd.h>
#include <sys/reboot.h>
#include <stdbool.h>
#include <signal.h>
#include "initsock.h"
#include "service.h"
#include "util.h"
#include "config.h"
#define RUNSVCBIN SCRIPTDIR "/runsvc"
#define ENVFILE ETCPATH "/initd.env"
#define PROCFDDIR "/proc/self/fd"
enum {
STATUS_OK = 0,
@ -65,27 +70,20 @@ void supervisor_init(void);
bool supervisor_process_queues(void);
/********** signal_<platform>.c **********/
void supervisor_reload_config(void);
/*
Setup signal handling. Returns -1 on error, a file descriptor on
success.
void supervisor_answer_status_request(int fd, const void *dest_addr,
size_t addrlen, E_SERVICE_STATE filter);
The returned file descriptor can be polled and becomes readable
when a signal arrives. Reading from it returns a signalfd_siginfo
structure.
void supervisor_start(int id);
The returned file descriptor has the close on exec flag set.
void supervisor_stop(int id);
The kernel is also told to send us SIGINT signals if a user presses
the local equivalent of CTRL+ALT+DEL.
*/
int sigsetup(void);
/********** initsock.c **********/
/*
Undo everything that sigsetup() changed about signal handling and
restore the default.
*/
void sigreset(void);
int init_socket_create(void);
int init_socket_send_status(int fd, const void *dest_addr, size_t addrlen,
E_SERVICE_STATE state, service_t *svc);
#endif /* INIT_H */

88
initd/initsock.c Normal file
View file

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: ISC */
#include "init.h"
static int send_retry(int fd, const void *dst, size_t addrlen,
const void *buffer, size_t size)
{
ssize_t ret;
retry:
ret = sendto(fd, buffer, size, MSG_NOSIGNAL, dst, addrlen);
if (ret < 0 && errno == EINTR)
goto retry;
if (ret < 0 || (size_t)ret < size)
return -1;
return 0;
}
static int send_string(int fd, const void *dst, size_t addrlen,
const char *str)
{
size_t len = strlen(str);
uint16_t raw;
if (len > 0xFFFF)
return -1;
raw = htobe16(len);
if (send_retry(fd, dst, addrlen, &raw, 2))
return -1;
return len > 0 ? send_retry(fd, dst, addrlen, str, len) : 0;
}
int init_socket_create(void)
{
struct sockaddr_un un;
int fd;
fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (fd < 0) {
perror("socket");
return -1;
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, INIT_SOCK_PATH);
unlink(INIT_SOCK_PATH);
if (bind(fd, (struct sockaddr *)&un, sizeof(un))) {
perror("bind: " INIT_SOCK_PATH);
close(fd);
unlink(INIT_SOCK_PATH);
return -1;
}
return fd;
}
int init_socket_send_status(int fd, const void *dest_addr, size_t addrlen,
E_SERVICE_STATE state, service_t *svc)
{
init_response_status_t info;
memset(&info, 0, sizeof(info));
if (svc == NULL || state == ESS_NONE) {
info.state = ESS_NONE;
info.id = -1;
} else {
info.state = state;
info.exit_status = svc->status & 0xFF;
info.id = htobe32(svc->id);
}
if (send_retry(fd, dest_addr, addrlen, &info, sizeof(info)))
return -1;
if (svc != NULL && state != ESS_NONE) {
if (send_string(fd, dest_addr, addrlen, svc->fname))
return -1;
if (send_string(fd, dest_addr, addrlen, svc->name))
return -1;
}
return 0;
}

View file

@ -2,6 +2,7 @@
#include "init.h"
static int sigfd = -1;
static int sockfd = -1;
static void handle_signal(void)
{
@ -29,6 +30,51 @@ static void handle_signal(void)
case SIGINT:
supervisor_set_target(TGT_REBOOT);
break;
case SIGHUP:
supervisor_reload_config();
break;
case SIGUSR1:
if (sockfd >= 0) {
close(sockfd);
unlink(INIT_SOCK_PATH);
sockfd = -1;
}
sockfd = init_socket_create();
break;
}
}
static void handle_request(void)
{
struct sockaddr_un addr;
init_request_t rq;
socklen_t addrlen;
ssize_t ret;
retry:
memset(&rq, 0, sizeof(rq));
addrlen = sizeof(addr);
ret = recvfrom(sockfd, &rq, sizeof(rq), MSG_DONTWAIT | MSG_TRUNC,
(struct sockaddr *)&addr, &addrlen);
if (ret < 0 && errno == EINTR)
goto retry;
if ((size_t)ret < sizeof(rq))
return;
switch (rq.rq) {
case EIR_STATUS:
supervisor_answer_status_request(sockfd, &addr, addrlen,
rq.arg.status.filter);
break;
case EIR_START:
rq.arg.startstop.id = be32toh(rq.arg.startstop.id);
supervisor_start(rq.arg.startstop.id);
break;
case EIR_STOP:
rq.arg.startstop.id = be32toh(rq.arg.startstop.id);
supervisor_stop(rq.arg.startstop.id);
break;
}
}
@ -36,6 +82,8 @@ void target_completed(int target)
{
switch (target) {
case TGT_BOOT:
if (sockfd < 0)
sockfd = init_socket_create();
break;
case TGT_SHUTDOWN:
for (;;)
@ -48,9 +96,32 @@ void target_completed(int target)
}
}
static int sigsetup(void)
{
sigset_t mask;
int sfd;
sigfillset(&mask);
if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
perror("sigprocmask");
return -1;
}
sfd = signalfd(-1, &mask, SFD_CLOEXEC);
if (sfd == -1) {
perror("signalfd");
return -1;
}
if (reboot(LINUX_REBOOT_CMD_CAD_OFF))
perror("cannot disable CTRL+ALT+DEL");
return sfd;
}
int main(void)
{
int ret, count;
int i, ret, count;
struct pollfd pfd[2];
if (getpid() != 1) {
@ -69,15 +140,29 @@ int main(void)
;
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = sigfd;
pfd[0].events = POLLIN;
count = 1;
count = 0;
pfd[count].fd = sigfd;
pfd[count].events = POLLIN;
++count;
if (sockfd >= 0) {
pfd[count].fd = sockfd;
pfd[count].events = POLLIN;
++count;
}
ret = poll(pfd, count, -1);
if (ret <= 0)
continue;
if (ret > 0) {
if (pfd[0].revents & POLLIN)
handle_signal();
for (i = 0; i < count; ++i) {
if (pfd[i].revents & POLLIN) {
if (pfd[i].fd == sigfd)
handle_signal();
if (pfd[i].fd == sockfd)
handle_request();
}
}
}

View file

@ -3,32 +3,175 @@
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include "init.h"
static int setup_env(void)
{
int status = -1;
ssize_t ret;
FILE *fp;
clearenv();
fp = fopen(ENVFILE, "r");
if (fp == NULL) {
perror(ENVFILE);
return -1;
}
do {
char *line = NULL;
size_t n = 0;
errno = 0;
ret = getline(&line, &n, fp);
if (ret < 0) {
if (errno == 0) {
status = 0;
} else {
perror(ENVFILE);
}
} else if (ret > 0 && putenv(line) != 0) {
perror("putenv");
ret = -1;
}
free(line);
} while (ret >= 0);
fclose(fp);
return status;
}
static int close_all_files(void)
{
struct dirent *ent;
DIR *dir;
int fd;
dir = opendir(PROCFDDIR);
if (dir == NULL) {
perror(PROCFDDIR);
return -1;
}
while ((ent = readdir(dir)) != NULL) {
if (!isdigit(ent->d_name[0]))
continue;
fd = atoi(ent->d_name);
close(fd);
}
closedir(dir);
return 0;
}
static int setup_tty(const char *tty, bool truncate)
{
int fd;
if (tty == NULL)
return 0;
fd = open(tty, O_RDWR);
if (fd < 0) {
perror(tty);
return -1;
}
if (truncate)
ftruncate(fd, 0);
setsid();
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
return 0;
}
static __attribute__((noreturn)) void argv_exec(exec_t *e)
{
char **argv = alloca(sizeof(char *) * (e->argc + 1)), *ptr;
int i;
for (ptr = e->args, i = 0; i < e->argc; ++i, ptr += strlen(ptr) + 1)
argv[i] = ptr;
argv[i] = NULL;
execvp(argv[0], argv);
perror(argv[0]);
exit(EXIT_FAILURE);
}
static int run_sequentially(exec_t *list)
{
pid_t ret, pid;
int status;
for (; list != NULL; list = list->next) {
if (list->next == NULL)
argv_exec(list);
pid = fork();
if (pid == 0)
argv_exec(list);
if (pid == -1) {
perror("fork");
return EXIT_FAILURE;
}
do {
ret = waitpid(pid, &status, 0);
} while (ret != pid);
if (!WIFEXITED(status))
return EXIT_FAILURE;
if (WEXITSTATUS(status) != EXIT_SUCCESS)
return WEXITSTATUS(status);
}
return EXIT_SUCCESS;
}
pid_t runsvc(service_t *svc)
{
char *argv[4], *envp[1];
sigset_t mask;
pid_t pid;
argv[0] = (char *)RUNSVCBIN;
argv[1] = (char *)SVCDIR;
argv[2] = svc->fname;
argv[3] = NULL;
envp[0] = NULL;
pid = fork();
if (pid == -1)
perror("fork");
if (pid == 0) {
sigreset();
execve(argv[0], argv, envp);
perror(argv[0]);
exit(EXIT_FAILURE);
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
if (setup_env())
exit(EXIT_FAILURE);
if (close_all_files())
exit(EXIT_FAILURE);
if (setup_tty(svc->ctty,
(svc->flags & SVC_FLAG_TRUNCATE_OUT) != 0)) {
exit(EXIT_FAILURE);
}
exit(run_sequentially(svc->exec));
}
return pid;

View file

@ -1,40 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include <stdio.h>
#include "init.h"
int sigsetup(void)
{
sigset_t mask;
int sfd;
sigfillset(&mask);
if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
perror("sigprocmask");
return -1;
}
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sfd = signalfd(-1, &mask, SFD_CLOEXEC);
if (sfd == -1) {
perror("signalfd");
return -1;
}
if (reboot(LINUX_REBOOT_CMD_CAD_OFF))
perror("cannot disable CTRL+ALT+DEL");
return sfd;
}
void sigreset(void)
{
sigset_t mask;
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
}

View file

@ -3,19 +3,66 @@
static service_list_t cfg;
static int service_id = 1;
static int target = -1;
static service_t *running = NULL;
static service_t *terminated = NULL;
static service_t *queue = NULL;
static service_t *completed = NULL;
static service_t *failed = NULL;
static int singleshot = 0;
static bool waiting = false;
static bool find_service(service_t *list, service_t *svc)
{
while (list != NULL) {
if (strcmp(list->fname, svc->fname) == 0)
return true;
list = list->next;
}
return false;
}
static void remove_not_in_list(service_t **current, service_t *list, int tgt)
{
service_t *it = *current, *prev = NULL;
while (it != NULL) {
if (it->target == tgt && !find_service(list, it)) {
if (prev == NULL) {
delsvc(it);
*current = (*current)->next;
it = *current;
} else {
prev->next = it->next;
delsvc(it);
it = prev->next;
}
} else {
prev = it;
it = it->next;
}
}
}
static bool have_service(service_t *svc)
{
return find_service(running, svc) || find_service(terminated, svc) ||
find_service(queue, svc) || find_service(completed, svc) ||
find_service(failed, svc);
}
static int start_service(service_t *svc)
{
if (svc->id < 1)
svc->id = service_id++;
svc->pid = runsvc(svc);
if (svc->pid == -1) {
print_status(svc->desc, STATUS_FAIL, false);
delsvc(svc);
svc->next = completed;
completed = svc;
return -1;
}
@ -31,12 +78,15 @@ static void handle_terminated_service(service_t *svc)
if (target == TGT_REBOOT || target == TGT_SHUTDOWN)
break;
if (svc->rspwn_limit > 0) {
svc->rspwn_limit -= 1;
if (svc->flags & SVC_FLAG_ADMIN_STOPPED)
break;
if (svc->rspwn_limit == 0) {
if (svc->rspwn_limit > 0) {
svc->rspwn_count += 1;
if (svc->rspwn_count >= svc->rspwn_limit) {
print_status(svc->desc, STATUS_FAIL, false);
break;
goto out_failure;
}
}
@ -49,6 +99,8 @@ static void handle_terminated_service(service_t *svc)
STATUS_OK : STATUS_FAIL, true);
if (singleshot == 0 && queue == NULL)
target_completed(target);
if (svc->status != EXIT_SUCCESS)
goto out_failure;
break;
case SVC_ONCE:
singleshot -= 1;
@ -57,9 +109,16 @@ static void handle_terminated_service(service_t *svc)
STATUS_OK : STATUS_FAIL, false);
if (singleshot == 0 && queue == NULL && !waiting)
target_completed(target);
if (svc->status != EXIT_SUCCESS)
goto out_failure;
break;
}
delsvc(svc);
svc->next = completed;
completed = svc;
return;
out_failure:
svc->next = failed;
failed = svc;
}
void supervisor_handle_exited(pid_t pid, int status)
@ -116,7 +175,7 @@ void supervisor_init(void)
{
int status = STATUS_OK;
if (svcscan(SVCDIR, &cfg, RDSVC_NO_EXEC | RDSVC_NO_CTTY))
if (svcscan(SVCDIR, &cfg))
status = STATUS_FAIL;
target = TGT_BOOT;
@ -126,6 +185,45 @@ void supervisor_init(void)
print_status("reading configuration from " SVCDIR, status, false);
}
void supervisor_reload_config(void)
{
service_list_t newcfg;
service_t *svc;
int i;
if (svcscan(SVCDIR, &newcfg))
return;
for (i = 0; i < TGT_MAX; ++i) {
if (cfg.targets[i] == NULL) {
remove_not_in_list(&queue, newcfg.targets[i], i);
remove_not_in_list(&terminated, newcfg.targets[i], i);
remove_not_in_list(&completed, newcfg.targets[i], i);
remove_not_in_list(&failed, newcfg.targets[i], i);
while (newcfg.targets[i] != NULL) {
svc = newcfg.targets[i];
newcfg.targets[i] = svc->next;
if (have_service(svc)) {
delsvc(svc);
} else {
svc->id = service_id++;
svc->status = EXIT_SUCCESS;
svc->next = completed;
completed = svc;
}
}
} else {
svc = cfg.targets[i];
cfg.targets[i] = newcfg.targets[i];
newcfg.targets[i] = svc;
}
}
del_svc_list(&newcfg);
}
bool supervisor_process_queues(void)
{
service_t *svc;
@ -144,6 +242,14 @@ bool supervisor_process_queues(void)
svc = queue;
queue = queue->next;
if (!(svc->flags & SVC_FLAG_HAS_EXEC)) {
print_status(svc->desc, STATUS_OK, false);
svc->status = EXIT_SUCCESS;
svc->next = completed;
completed = svc;
goto out;
}
if (start_service(svc) != 0)
return true;
@ -159,8 +265,97 @@ bool supervisor_process_queues(void)
singleshot += 1;
break;
}
out:
if (singleshot == 0 && queue == NULL && !waiting)
target_completed(target);
return true;
}
static int send_svc_list(int fd, const void *dst, size_t addrlen,
E_SERVICE_STATE filter, E_SERVICE_STATE state,
service_t *list)
{
if (filter != ESS_NONE && filter != state)
return 0;
while (list != NULL) {
if (init_socket_send_status(fd, dst, addrlen, state, list))
return -1;
list = list->next;
}
return 0;
}
void supervisor_answer_status_request(int fd, const void *dst, size_t addrlen,
E_SERVICE_STATE filter)
{
if (send_svc_list(fd, dst, addrlen, filter, ESS_RUNNING, running))
return;
if (send_svc_list(fd, dst, addrlen, filter, ESS_DONE, completed))
return;
if (send_svc_list(fd, dst, addrlen, filter, ESS_FAILED, failed))
return;
if (send_svc_list(fd, dst, addrlen, filter, ESS_ENQUEUED, queue))
return;
if (send_svc_list(fd, dst, addrlen, filter, ESS_ENQUEUED, terminated))
return;
init_socket_send_status(fd, dst, addrlen, ESS_NONE, NULL);
}
static service_t *remove_by_id(service_t **list, int id)
{
service_t *svc = *list, *prev = NULL;
while (svc != NULL && svc->id != id) {
prev = svc;
svc = svc->next;
}
if (svc != NULL) {
if (prev == NULL) {
*list = svc->next;
} else {
prev->next = svc->next;
}
}
return svc;
}
void supervisor_start(int id)
{
service_t *svc;
svc = remove_by_id(&completed, id);
if (svc != NULL)
goto found;
svc = remove_by_id(&failed, id);
if (svc != NULL)
goto found;
return;
found:
svc->rspwn_count = 0;
svc->flags &= ~SVC_FLAG_ADMIN_STOPPED;
svc->next = queue;
queue = svc;
}
void supervisor_stop(int id)
{
service_t *svc;
for (svc = running; svc != NULL; svc = svc->next) {
if (svc->id == id)
break;
}
if (svc != NULL) {
/* TODO: something more sophisticated? */
svc->flags |= SVC_FLAG_ADMIN_STOPPED;
kill(svc->pid, SIGTERM);
}
}

View file

@ -1,16 +1,15 @@
libinit_a_SOURCES = lib/init/delsvc.c lib/init/svcmap.c lib/init/rdsvc.c
libinit_a_SOURCES += lib/init/svcscan.c lib/init/del_svc_list.c
libinit_a_SOURCES += lib/init/svc_tsort.c lib/include/service.h
libinit_a_SOURCES += lib/init/init_socket_open.c lib/init/free_init_status.c
libinit_a_SOURCES += lib/include/initsock.h lib/init/init_socket_send_request.c
libinit_a_SOURCES += lib/init/init_socket_recv_status.c
libinit_a_CPPFLAGS = $(AM_CPPFLAGS)
libinit_a_CFLAGS = $(AM_CFLAGS)
libutil_a_SOURCES = lib/util/argv_exec.c lib/include/util.h
libutil_a_CPPFLAGS = $(AM_CPPFLAGS)
libutil_a_CFLAGS = $(AM_CFLAGS)
libcfg_a_SOURCES = lib/libcfg/rdline.c lib/libcfg/unescape.c lib/libcfg/rdcfg.c
libcfg_a_SOURCES += lib/libcfg/pack_argv.c lib/include/libcfg.h
libcfg_a_CPPFLAGS = $(AM_CPPFLAGS)
libcfg_a_CFLAGS = $(AM_CFLAGS)
noinst_LIBRARIES += libinit.a libcfg.a libutil.a
noinst_LIBRARIES += libinit.a libcfg.a

65
lib/include/initsock.h Normal file
View file

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: ISC */
#ifndef INITSOCK_H
#define INITSOCK_H
#include <stdint.h>
#include "config.h"
#include "service.h"
#define INIT_SOCK_PATH SOCKDIR "/init.sock"
typedef enum {
EIR_STATUS = 0x00,
EIR_START = 0x01,
EIR_STOP = 0x02,
} E_INIT_REQUEST;
typedef enum {
ESS_NONE = 0x00,
ESS_RUNNING = 0x01,
ESS_ENQUEUED = 0x02,
ESS_DONE = 0x03,
ESS_FAILED = 0x04
} E_SERVICE_STATE;
typedef struct {
uint8_t rq;
uint8_t padd[3];
union {
struct {
uint8_t filter;
uint8_t padd[3];
} status;
struct {
uint32_t id;
} startstop;
} arg;
} init_request_t;
typedef struct {
uint8_t state;
uint8_t exit_status;
uint8_t padd[2];
int32_t id;
} init_response_status_t;
typedef struct {
E_SERVICE_STATE state;
int exit_status;
int id;
char *filename;
char *service_name;
} init_status_t;
int init_socket_open(const char *tmppath);
int init_socket_send_request(int fd, E_INIT_REQUEST rq, ...);
int init_socket_recv_status(int fd, init_status_t *resp);
void free_init_status(init_status_t *resp);
#endif /* INITSOCK_H */

View file

@ -27,7 +27,7 @@ typedef struct {
*/
unsigned int allow_block : 1;
int (*handle)(void *obj, char *arg, rdline_t *rd, int flags);
int (*handle)(void *obj, char *arg, rdline_t *rd);
} cfg_param_t;
/*
@ -92,11 +92,10 @@ int pack_argv(char *str);
/*
Parse a configuration file containing '<keyword> [arguments...]' lines.
The cfgobj and flags are passed to the callback in the params array.
The cfgobj is passed to the callback in the params array.
Returns zero on success.
*/
int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count,
int flags);
int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count);
#endif /* LIBCONFIG_H */

View file

@ -4,7 +4,11 @@
#include <sys/types.h>
#include "util.h"
typedef struct exec_t {
struct exec_t *next;
int argc; /* number of elements in argument vector */
char args[]; /* argument vectot string blob */
} exec_t;
enum {
/*
@ -31,16 +35,12 @@ enum {
TGT_MAX
};
enum {
RDSVC_NO_FNAME = 0x01, /* do not store a copy of the filename */
RDSVC_NO_EXEC = 0x02, /* do not store executable script */
RDSVC_NO_CTTY = 0x04, /* do not store the controlling tty */
RDSVC_NO_DEPS = 0x08, /* do not store dependencies */
};
enum {
/* truncate stdout */
SVC_FLAG_TRUNCATE_OUT = 0x01,
SVC_FLAG_HAS_EXEC = 0x10,
SVC_FLAG_ADMIN_STOPPED = 0x20,
};
typedef struct service_t {
@ -53,6 +53,7 @@ typedef struct service_t {
char *desc; /* description string */
char *ctty; /* controlling tty or log file */
int rspwn_limit; /* maximum respawn count */
int rspwn_count; /* services respawn counter */
unsigned int flags; /* SVC_FLAG_* bit field */
/* linked list of command lines to execute */
@ -66,6 +67,7 @@ typedef struct service_t {
pid_t pid;
int status; /* process exit status */
int id; /* service ID used by initd */
char name[]; /* canonical service name */
} service_t;
@ -77,7 +79,7 @@ typedef struct {
/*
Read a service from a file.
*/
service_t *rdsvc(int dirfd, const char *filename, int flags);
service_t *rdsvc(int dirfd, const char *filename);
void delsvc(service_t *svc);
@ -88,7 +90,7 @@ void delsvc(service_t *svc);
Returns 0 on success, -1 on failure. The function takes care of
printing error messages on failure.
*/
int svcscan(const char *directory, service_list_t *list, int flags);
int svcscan(const char *directory, service_list_t *list);
void del_svc_list(service_list_t *list);

View file

@ -1,39 +0,0 @@
/* SPDX-License-Identifier: ISC */
#ifndef UTIL_H
#define UTIL_H
#include <sys/types.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include "config.h"
#ifdef __GNUC__
#define NORETURN __attribute__((noreturn))
#endif
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
typedef struct exec_t {
struct exec_t *next;
int argc; /* number of elements in argument vector */
char args[]; /* argument vectot string blob */
} exec_t;
enum {
/* only allow root to connect */
SOCK_FLAG_ROOT_ONLY = 0x01,
/* allow everyone to connect */
SOCK_FLAG_EVERYONE = 0x02,
/* create a datagram socket, otherwise use a stream socket */
SOCK_FLAG_DGRAM = 0x04,
};
int setup_tty(const char *tty, bool truncate);
NORETURN void argv_exec(exec_t *e);
#endif /* UTIL_H */

View file

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: ISC */
#include <stdlib.h>
#include "initsock.h"
void free_init_status(init_status_t *resp)
{
free(resp->filename);
free(resp->service_name);
}

View file

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: ISC */
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include "initsock.h"
int init_socket_open(const char *tmppath)
{
struct sockaddr_un un;
int fd;
fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (fd < 0) {
perror("socket");
return -1;
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, tmppath);
if (bind(fd, (struct sockaddr *)&un, sizeof(un))) {
fprintf(stderr, "bind: %s: %s", tmppath, strerror(errno));
close(fd);
unlink(tmppath);
return -1;
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, INIT_SOCK_PATH);
if (connect(fd, (struct sockaddr *)&un, sizeof(un))) {
perror("connect: " INIT_SOCK_PATH);
close(fd);
return -1;
}
return fd;
}

View file

@ -0,0 +1,87 @@
/* SPDX-License-Identifier: ISC */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "initsock.h"
static int read_retry(int fd, void *buffer, size_t size)
{
ssize_t ret;
retry:
ret = read(fd, buffer, size);
if (ret < 0) {
if (errno == EINTR)
goto retry;
return -1;
}
if ((size_t)ret < size) {
errno = EPROTO;
return 0;
}
return 1;
}
static char *read_string(int fd)
{
uint16_t len;
char *buffer;
int ret;
ret = read_retry(fd, &len, sizeof(len));
if (ret <= 0)
return NULL;
len = be16toh(len);
buffer = calloc(1, len + 1);
if (buffer == NULL)
return NULL;
if (len > 0) {
ret = read_retry(fd, buffer, len);
if (ret <= 0) {
ret = errno;
free(buffer);
errno = ret;
return NULL;
}
}
return buffer;
}
int init_socket_recv_status(int fd, init_status_t *resp)
{
init_response_status_t info;
memset(resp, 0, sizeof(*resp));
if (read_retry(fd, &info, sizeof(info)) <= 0)
return -1;
resp->state = info.state;
resp->exit_status = info.exit_status;
resp->id = be32toh(info.id);
if (resp->state == ESS_NONE)
return 0;
resp->filename = read_string(fd);
if (resp->filename == NULL)
return -1;
resp->service_name = read_string(fd);
if (resp->service_name == NULL)
return -1;
return 0;
}

View file

@ -0,0 +1,45 @@
/* SPDX-License-Identifier: ISC */
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <endian.h>
#include <stdio.h>
#include <errno.h>
#include "initsock.h"
int init_socket_send_request(int fd, E_INIT_REQUEST rq, ...)
{
init_request_t request;
ssize_t ret;
va_list ap;
memset(&request, 0, sizeof(request));
request.rq = rq;
va_start(ap, rq);
switch (rq) {
case EIR_STATUS:
request.arg.status.filter = va_arg(ap, E_SERVICE_STATE);
break;
case EIR_START:
case EIR_STOP:
request.arg.startstop.id = htobe32(va_arg(ap, int));
break;
default:
break;
}
va_end(ap);
retry:
ret = write(fd, &request, sizeof(request));
if (ret < 0) {
if (errno == EINTR)
goto retry;
perror(INIT_SOCK_PATH);
return -1;
}
return 0;
}

View file

@ -11,7 +11,6 @@
#include "service.h"
#include "libcfg.h"
#include "util.h"
static int try_unescape(char *arg, rdline_t *rd)
{
@ -44,10 +43,9 @@ static int try_pack_argv(char *str, rdline_t *rd)
return count;
}
static int svc_desc(void *user, char *arg, rdline_t *rd, int flags)
static int svc_desc(void *user, char *arg, rdline_t *rd)
{
service_t *svc = user;
(void)flags;
if (try_unescape(arg, rd))
return -1;
@ -55,13 +53,10 @@ static int svc_desc(void *user, char *arg, rdline_t *rd, int flags)
return svc->desc == NULL ? -1 : 0;
}
static int svc_tty(void *user, char *arg, rdline_t *rd, int flags)
static int svc_tty(void *user, char *arg, rdline_t *rd)
{
service_t *svc = user;
if (flags & RDSVC_NO_CTTY)
return 0;
if (strncmp(arg, "truncate", 8) == 0 && isspace(arg[8])) {
svc->flags |= SVC_FLAG_TRUNCATE_OUT;
arg += 8;
@ -76,13 +71,12 @@ static int svc_tty(void *user, char *arg, rdline_t *rd, int flags)
return svc->ctty == NULL ? -1 : 0;
}
static int svc_exec(void *user, char *arg, rdline_t *rd, int flags)
static int svc_exec(void *user, char *arg, rdline_t *rd)
{
service_t *svc = user;
exec_t *e, *end;
if (flags & RDSVC_NO_EXEC)
return 0;
svc->flags |= SVC_FLAG_HAS_EXEC;
e = calloc(1, sizeof(*e) + strlen(arg) + 1);
if (e == NULL) {
@ -107,13 +101,10 @@ static int svc_exec(void *user, char *arg, rdline_t *rd, int flags)
return 0;
}
static int svc_before(void *user, char *arg, rdline_t *rd, int flags)
static int svc_before(void *user, char *arg, rdline_t *rd)
{
service_t *svc = user;
if (flags & RDSVC_NO_DEPS)
return 0;
if (svc->before != NULL) {
fprintf(stderr, "%s: %zu: 'before' dependencies respecified\n",
rd->filename, rd->lineno);
@ -128,13 +119,10 @@ static int svc_before(void *user, char *arg, rdline_t *rd, int flags)
return (svc->num_before < 0) ? -1 : 0;
}
static int svc_after(void *user, char *arg, rdline_t *rd, int flags)
static int svc_after(void *user, char *arg, rdline_t *rd)
{
service_t *svc = user;
if (flags & RDSVC_NO_DEPS)
return 0;
if (svc->after != NULL) {
fprintf(stderr, "%s: %zu: 'after' dependencies respecified\n",
rd->filename, rd->lineno);
@ -149,11 +137,10 @@ static int svc_after(void *user, char *arg, rdline_t *rd, int flags)
return (svc->num_after < 0) ? -1 : 0;
}
static int svc_type(void *user, char *arg, rdline_t *rd, int flags)
static int svc_type(void *user, char *arg, rdline_t *rd)
{
service_t *svc = user;
int count = try_pack_argv(arg, rd);
(void)flags;
if (count < 1)
return -1;
@ -197,11 +184,10 @@ fail_limit:
return -1;
}
static int svc_target(void *user, char *arg, rdline_t *rd, int flags)
static int svc_target(void *user, char *arg, rdline_t *rd)
{
service_t *svc = user;
int target;
(void)flags;
if (try_unescape(arg, rd))
return -1;
@ -228,7 +214,7 @@ static const cfg_param_t svc_params[] = {
{ "after", 0, svc_after },
};
service_t *rdsvc(int dirfd, const char *filename, int flags)
service_t *rdsvc(int dirfd, const char *filename)
{
const char *arg, *args[1];
service_t *svc = NULL;
@ -252,16 +238,17 @@ service_t *rdsvc(int dirfd, const char *filename, int flags)
if (svc == NULL)
goto fail_oom;
if (!(flags & RDSVC_NO_FNAME)) {
svc->fname = strdup(filename);
if (svc->fname == NULL)
goto fail_oom;
}
svc->fname = strdup(filename);
if (svc->fname == NULL)
goto fail_oom;
memcpy(svc->name, filename, nlen);
svc->id = -1;
if (rdcfg(svc, &rd, svc_params, ARRAY_SIZE(svc_params), flags))
if (rdcfg(svc, &rd, svc_params,
sizeof(svc_params) / sizeof(svc_params[0]))) {
goto fail;
}
out:
rdline_cleanup(&rd);

View file

@ -23,7 +23,7 @@ int svc_type_from_string(const char *type)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(type_map); ++i) {
for (i = 0; i < sizeof(type_map) / sizeof(type_map[0]); ++i) {
if (strcmp(type_map[i], type) == 0)
return i;
}
@ -40,7 +40,7 @@ int svc_target_from_string(const char *target)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(target_map); ++i) {
for (i = 0; i < sizeof(target_map) / sizeof(target_map[0]); ++i) {
if (strcmp(target_map[i], target) == 0)
return i;
}

View file

@ -11,7 +11,7 @@
#include "service.h"
int svcscan(const char *directory, service_list_t *list, int flags)
int svcscan(const char *directory, service_list_t *list)
{
int i, dfd, type, ret = 0;
struct dirent *ent;
@ -66,7 +66,7 @@ int svcscan(const char *directory, service_list_t *list, int flags)
if (type != S_IFREG && type != S_IFLNK)
continue;
svc = rdsvc(dfd, ent->d_name, flags);
svc = rdsvc(dfd, ent->d_name);
if (svc == NULL) {
ret = -1;
continue;

View file

@ -47,8 +47,7 @@ static int splitkv(rdline_t *rd, char **k, char **v)
return 0;
}
int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count,
int flags)
int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count)
{
const cfg_param_t *p;
char *key, *value;
@ -67,7 +66,7 @@ int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count,
;
if (*value != '\0') {
ret = p->handle(cfgobj, value, rd, flags);
ret = p->handle(cfgobj, value, rd);
if (ret)
return -1;
}
@ -75,7 +74,7 @@ int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count,
while ((ret = rdline(rd)) == 0) {
if (strcmp(rd->line, "}") == 0)
break;
if (p->handle(cfgobj, rd->line, rd, flags))
if (p->handle(cfgobj, rd->line, rd))
return -1;
}
@ -83,7 +82,7 @@ int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count,
return -1;
if (ret > 0)
goto fail_bra;
} else if (p->handle(cfgobj, value, rd, flags)) {
} else if (p->handle(cfgobj, value, rd)) {
return -1;
}
}

View file

@ -8,7 +8,6 @@
#include <fcntl.h>
#include "libcfg.h"
#include "util.h"
int rdline_init(rdline_t *t, int dirfd, const char *filename,
int argc, const char *const *argv)

View file

@ -1,51 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "service.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
int setup_tty(const char *tty, bool truncate)
{
int fd;
if (tty == NULL)
return 0;
fd = open(tty, O_RDWR);
if (fd < 0) {
perror(tty);
return -1;
}
if (truncate)
ftruncate(fd, 0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
setsid();
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
return 0;
}
void argv_exec(exec_t *e)
{
char **argv = alloca(sizeof(char *) * (e->argc + 1)), *ptr;
int i;
for (ptr = e->args, i = 0; i < e->argc; ++i, ptr += strlen(ptr) + 1)
argv[i] = ptr;
argv[i] = NULL;
execvp(argv[0], argv);
perror(argv[0]);
exit(EXIT_FAILURE);
}