Browse Source

Initial commit

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
master
David Oberhollenzer 4 years ago
commit
9a88f7da45
  1. 26
      .gitignore
  2. 14
      Makefile.am
  3. 3
      autogen.sh
  4. 13
      cmd/Makemodule.am
  5. 139
      cmd/shutdown.c
  6. 26
      configure.ac
  7. 8
      initd/Makemodule.am
  8. 36
      initd/init.h
  9. 238
      initd/main.c
  10. 65
      initd/mksock.c
  11. 128
      initd/runlst.c
  12. 23
      initd/setup_tty.c
  13. 37
      initd/shutdown.c
  14. 27
      initd/status.c
  15. 43
      initd/svclist.c
  16. 13
      lib/Makemodule.am
  17. 62
      lib/include/service.h
  18. 20
      lib/include/telinit.h
  19. 58
      lib/include/util.h
  20. 18
      lib/src/del_srv_list.c
  21. 25
      lib/src/delsrv.c
  22. 15
      lib/src/enum_by_name.c
  23. 33
      lib/src/opensock.c
  24. 54
      lib/src/rdline.c
  25. 249
      lib/src/rdsrv.c
  26. 124
      lib/src/splitkv.c
  27. 75
      lib/src/srv_tsort.c
  28. 93
      lib/src/srvscan.c
  29. 55
      lib/src/strexpand.c
  30. 35
      m4/ac_define_dir.m4
  31. 12
      servicecmd/Makemodule.am
  32. 75
      servicecmd/disable.c
  33. 84
      servicecmd/enable.c
  34. 112
      servicecmd/help.c
  35. 71
      servicecmd/list.c
  36. 56
      servicecmd/servicecmd.c
  37. 36
      servicecmd/servicecmd.h
  38. 5
      services/Makemodule.am
  39. 7
      services/agetty.in
  40. 7
      services/hostname.in
  41. 6
      services/hwclock.in
  42. 10
      services/loopback.in
  43. 8
      services/sysctl.in
  44. 4
      services/sysinit

26
.gitignore

@ -0,0 +1,26 @@
*.in
*.cache
.*
*.o
*.a
*~
Makefile
aclocal.m4
compile
config.*
configure
depcomp
install-sh
missing
stamp-h1
init
service
reboot
shutdown
services/agetty
services/hostname
services/loopback
services/sysctl
services/hwclock

14
Makefile.am

@ -0,0 +1,14 @@
ACLOCAL_AMFLAGS = -I m4
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/lib/include
AM_CFLAGS = -std=c99 -pedantic -Wall -Wextra
sbin_PROGRAMS =
noinst_LIBRARIES =
EXTRA_DIST =
include lib/Makemodule.am
include cmd/Makemodule.am
include initd/Makemodule.am
include services/Makemodule.am
include servicecmd/Makemodule.am

3
autogen.sh

@ -0,0 +1,3 @@
#!/bin/sh
autoreconf --force --install --symlink

13
cmd/Makemodule.am

@ -0,0 +1,13 @@
shutdown_SOURCES = cmd/shutdown.c
shutdown_CPPFLAGS = $(AM_CPPFLAGS) -DPROGNAME=shutdown
shutdown_CFLAGS = $(AM_CFLAGS)
shutdown_LDFLAGS = $(AM_LDFLAGS)
shutdown_LDADD = libinit.a
reboot_SOURCES = cmd/shutdown.c
reboot_CPPFLAGS = $(AM_CPPFLAGS) -DPROGNAME=reboot
reboot_CFLAGS = $(AM_CFLAGS)
reboot_LDFLAGS = $(AM_LDFLAGS)
reboot_LDADD = libinit.a
sbin_PROGRAMS += reboot shutdown

139
cmd/shutdown.c

@ -0,0 +1,139 @@
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/reboot.h>
#include <linux/reboot.h>
#include "telinit.h"
#include "util.h"
#define STRINIFY(x) #x
#define STRINIFY_VALUE(x) STRINIFY(x)
#define PROGRAM_NAME STRINIFY_VALUE(PROGNAME)
#define FL_FORCE 0x01
#define FL_NOSYNC 0x02
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "poweroff", no_argument, NULL, 'p' },
{ "reboot", no_argument, NULL, 'r' },
{ "force", no_argument, NULL, 'f' },
{ "no-sync", no_argument, NULL, 'n' },
{ NULL, 0, NULL, 0 },
};
static const char *shortopt = "hVprfn";
static const char *defact_str = "power-off";
static int defact = TI_SHUTDOWN;
static NORETURN void usage(int status)
{
fprintf(status == EXIT_SUCCESS ? stdout : stderr,
"%s [OPTIONS...]\n\n"
"Perform a system shutdown or reboot.\n\n"
" -h, --help Display this help text and exit.\n"
" -V, --version Display version information and exit.\n"
" -p, --poweroff Power-off the machine.\n"
" -r, --reboot Reboot the machine.\n"
" -f, --force Force immediate power-off or reboot. Do not contact the\n"
" init system.\n"
" -n, --no-sync Don't sync storage media before power-off or reboot.\n\n"
"If no option is specified, the default action is %s.\n",
PROGRAM_NAME, defact_str);
exit(status);
}
static NORETURN void version(void)
{
fputs(
PROGRAM_NAME " (Pygos init) " PACKAGE_VERSION "\n"
"Copyright (C) 2018 David Oberhollenzer\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n",
stdout);
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
int c, fd, flags = 0;
ti_msg_t msg;
ssize_t ret;
if (!strcmp(PROGRAM_NAME, "reboot")) {
defact_str = "reboot";
defact = TI_REBOOT;
}
while (1) {
c = getopt_long(argc, argv, shortopt, options, NULL);
if (c == -1)
break;
switch (c) {
case 'f':
flags |= FL_FORCE;
break;
case 'n':
flags |= FL_NOSYNC;
break;
case 'p':
defact = TI_SHUTDOWN;
break;
case 'r':
defact = TI_REBOOT;
break;
case 'V':
version();
case 'h':
usage(EXIT_SUCCESS);
default:
exit(EXIT_FAILURE);
}
}
if (flags & FL_FORCE) {
if (!(flags & FL_NOSYNC))
sync();
switch (defact) {
case TI_REBOOT:
reboot(RB_AUTOBOOT);
break;
case TI_SHUTDOWN:
reboot(RB_POWER_OFF);
break;
}
perror("reboot system call");
return EXIT_FAILURE;
}
fd = opensock();
if (fd < 0)
return EXIT_FAILURE;
msg.type = defact;
retry:
ret = write(fd, &msg, sizeof(msg));
if (ret < 0) {
if (errno == EINTR)
goto retry;
perror("write on init socket");
close(fd);
return EXIT_FAILURE;
}
close(fd);
return EXIT_SUCCESS;
}

26
configure.ac

@ -0,0 +1,26 @@
AC_PREREQ([2.60])
AC_INIT([init], 0.1, [david.oberhollenzer@tele2.at], init)
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign subdir-objects dist-bzip2])
AM_SILENT_RULES([yes])
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_RANLIB
AC_CONFIG_HEADERS([lib/include/config.h])
AC_DEFINE_DIR(SVCDIR, sysconfdir/init.d, [Startup service directory])
AC_DEFINE_DIR(TEMPLATEDIR, datadir/init, [Service template directory])
AC_DEFINE_DIR(SCRIPTDIR, libexecdir/init, [Helper script directory])
AC_DEFINE_DIR(SOCKDIR, localstatedir/run, [Directory for initd socket])
AC_DEFINE_DIR(BINPATH, bindir, [Fully evaluated bin directory])
AC_DEFINE_DIR(SBINPATH, sbindir, [Fully evaluated sbin directory])
AC_CONFIG_FILES([services/agetty])
AC_CONFIG_FILES([services/hostname])
AC_CONFIG_FILES([services/loopback])
AC_CONFIG_FILES([services/sysctl])
AC_CONFIG_FILES([services/hwclock])
AC_OUTPUT([Makefile])

8
initd/Makemodule.am

@ -0,0 +1,8 @@
init_SOURCES = initd/main.c initd/runlst.c initd/init.h initd/setup_tty.c
init_SOURCES += initd/status.c initd/mksock.c initd/shutdown.c initd/svclist.c
init_CPPFLAGS = $(AM_CPPFLAGS)
init_CFLAGS = $(AM_CFLAGS)
init_LDFLAGS = $(AM_LDFLAGS)
init_LDADD = libinit.a
sbin_PROGRAMS += init

36
initd/init.h

@ -0,0 +1,36 @@
#ifndef INIT_H
#define INIT_H
#include <linux/reboot.h>
#include <sys/reboot.h>
#include "service.h"
#include "telinit.h"
#include "util.h"
enum {
STATUS_OK = 0,
STATUS_FAIL,
STATUS_WAIT,
};
int runlst_wait(char **exec, size_t num, const char *ctty);
pid_t runlst(char **exec, size_t num, const char *ctty);
int setup_tty(void);
void print_status(const char *msg, int type, bool update);
int mksock(void);
NORETURN void do_shutdown(int type);
bool svclist_have_singleshot(void);
void svclist_add(service_t *svc);
service_t *svclist_remove(pid_t pid);
#endif /* INIT_H */

238
initd/main.c

@ -0,0 +1,238 @@
#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 <sys/signalfd.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 void handle_exited(service_t *svc)
{
switch (svc->type) {
case SVC_RESPAWN:
if (target == TGT_REBOOT || target == TGT_SHUTDOWN) {
delsrv(svc);
break;
}
svc->pid = runlst(svc->exec, svc->num_exec, svc->ctty);
if (svc->pid == -1) {
print_status(svc->desc, STATUS_FAIL, false);
delsrv(svc);
}
svclist_add(svc);
break;
case SVC_ONCE:
print_status(svc->desc,
svc->status == EXIT_SUCCESS ?
STATUS_OK : STATUS_FAIL, false);
/* fall-through */
default:
delsrv(svc);
break;
}
}
static void handle_signal(int sigfd)
{
struct signalfd_siginfo info;
service_t *svc;
int status;
pid_t pid;
if (read(sigfd, &info, sizeof(info)) != sizeof(info)) {
perror("read on signal fd");
return;
}
switch (info.ssi_signo) {
case SIGCHLD:
for (;;) {
pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0)
break;
status = WIFEXITED(status) ? WEXITSTATUS(status) :
EXIT_FAILURE;
svc = svclist_remove(pid);
if (svc != NULL)
handle_exited(svc);
}
break;
case SIGINT:
/* TODO: ctrl-alt-del */
break;
}
}
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->num_exec) {
print_status(svc->desc, STATUS_OK, false);
delsrv(svc);
continue;
}
if (svc->type == SVC_WAIT) {
print_status(svc->desc, STATUS_WAIT, false);
status = runlst_wait(svc->exec, svc->num_exec,
svc->ctty);
print_status(svc->desc,
status == EXIT_SUCCESS ?
STATUS_OK : STATUS_FAIL,
true);
delsrv(svc);
} else {
svc->pid = runlst(svc->exec, svc->num_exec, svc->ctty);
if (svc->pid == -1) {
print_status(svc->desc, STATUS_FAIL, false);
delsrv(svc);
continue;
}
svclist_add(svc);
}
}
}
static int read_msg(int fd, ti_msg_t *msg)
{
ssize_t ret;
retry:
ret = read(fd, msg, sizeof(*msg));
if (ret < 0) {
if (errno == EINTR)
goto retry;
perror("read on telinit socket");
return -1;
}
if ((size_t)ret < sizeof(*msg)) {
fputs("short read on telinit socket", stderr);
return -1;
}
return 0;
}
static void handle_tellinit(int ti_sock)
{
ti_msg_t msg;
int fd;
fd = accept(ti_sock, NULL, NULL);
if (fd == -1)
return;
if (read_msg(fd, &msg)) {
close(fd);
return;
}
switch (msg.type) {
case TI_SHUTDOWN:
target = TGT_SHUTDOWN;
break;
case TI_REBOOT:
target = TGT_REBOOT;
break;
}
close(fd);
}
int main(void)
{
int ti_sock, sfd, ret;
struct pollfd pfd[2];
sigset_t mask;
if (getpid() != 1) {
fputs("init does not have pid 1, terminating!\n", stderr);
return EXIT_FAILURE;
}
if (reboot(LINUX_REBOOT_CMD_CAD_OFF))
perror("cannot disable CTRL+ALT+DEL");
if (srvscan(SVCDIR, &cfg)) {
fputs("Error reading service list from " SVCDIR "\n"
"Trying to continue anyway\n", stderr);
}
sigfillset(&mask);
if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
perror("sigprocmask");
return EXIT_FAILURE;
}
sfd = signalfd(-1, &mask, SFD_CLOEXEC);
if (sfd == -1) {
perror("signalfd");
return EXIT_FAILURE;
}
ti_sock = mksock();
if (ti_sock == -1)
return EXIT_FAILURE;
if (setup_tty())
return EXIT_FAILURE;
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = sfd;
pfd[1].fd = ti_sock;
pfd[0].events = pfd[1].events = POLLIN;
for (;;) {
if (!svclist_have_singleshot()) {
if (target != runlevel) {
start_runlevel(target);
runlevel = target;
continue;
}
if (runlevel == TGT_SHUTDOWN)
do_shutdown(RB_POWER_OFF);
if (runlevel == TGT_REBOOT)
do_shutdown(RB_AUTOBOOT);
}
ret = poll(pfd, sizeof(pfd) / sizeof(pfd[0]), -1);
if (ret > 0) {
if (pfd[0].revents & POLLIN)
handle_signal(sfd);
if (pfd[1].revents & POLLIN)
handle_tellinit(ti_sock);
}
}
return EXIT_SUCCESS;
}

65
initd/mksock.c

@ -0,0 +1,65 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include "telinit.h"
#include "init.h"
int mksock(void)
{
struct sockaddr_un un;
int fd, flags;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
}
flags = fcntl(fd, F_GETFD);
if (flags == -1) {
perror("socket F_GETFD");
goto fail;
}
if (fcntl(fd, F_SETFD, flags | O_CLOEXEC)) {
perror("socket F_SETFD");
goto fail;
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, INITSOCK);
if (bind(fd, (struct sockaddr *)&un, sizeof(un))) {
perror("bind: " INITSOCK);
goto fail;
}
if (chown(INITSOCK, 0, 0)) {
perror("chown: " INITSOCK);
goto fail;
}
if (chmod(INITSOCK, 0770)) {
perror("chmod: " INITSOCK);
goto fail;
}
if (listen(fd, 10)) {
perror("listen");
goto fail;
}
return fd;
fail:
close(fd);
unlink(INITSOCK);
return -1;
}

128
initd/runlst.c

@ -0,0 +1,128 @@
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include "init.h"
extern char **environ;
static NORETURN void split_and_exec(char *cmd)
{
char *argv[128];
size_t i = 0;
while (*cmd != '\0') {
argv[i++] = cmd; /* FIXME: buffer overflow!! */
while (*cmd != '\0' && !isspace(*cmd))
++cmd;
if (isspace(*cmd)) {
*(cmd++) = '\0';
while (isspace(*cmd))
++cmd;
}
}
argv[i] = NULL;
execve(argv[0], argv, environ);
perror(argv[0]);
exit(EXIT_FAILURE);
}
static int child_setup(const char *ctty)
{
sigset_t mask;
int fd;
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
if (ctty != NULL) {
fd = open(ctty, O_RDWR);
if (fd < 0) {
perror(ctty);
return -1;
}
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;
}
int runlst_wait(char **exec, size_t num, const char *ctty)
{
pid_t ret, pid;
int status;
size_t i;
for (i = 0; i < num; ++i) {
pid = fork();
if (pid == 0) {
if (child_setup(ctty))
exit(EXIT_FAILURE);
split_and_exec(exec[i]);
}
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 runlst(char **exec, size_t num, const char *ctty)
{
int status;
pid_t pid;
pid = fork();
if (pid == 0) {
if (child_setup(ctty))
exit(EXIT_FAILURE);
if (num > 1) {
status = runlst_wait(exec, num, NULL);
exit(status);
} else {
split_and_exec(exec[0]);
}
}
if (pid == -1)
perror("fork");
return pid;
}

23
initd/setup_tty.c

@ -0,0 +1,23 @@
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include "init.h"
int setup_tty(void)
{
int fd;
fd = open("/dev/console", O_WRONLY | O_NOCTTY);
if (fd < 0) {
perror("/dev/console");
return -1;
}
close(STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
return 0;
}

37
initd/shutdown.c

@ -0,0 +1,37 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include "init.h"
void do_shutdown(int type)
{
struct timespec req, rem;
print_status("sending SIGTERM to all processes", STATUS_WAIT, false);
kill(-1, SIGTERM);
memset(&req, 0, sizeof(req));
memset(&rem, 0, sizeof(rem));
req.tv_sec = 5; /* TODO: make configurable? */
while (nanosleep(&req, &rem) != 0 && errno == EINTR)
req = rem;
print_status("sending SIGTERM to all processes", STATUS_OK, true);
kill(-1, SIGKILL);
print_status("sending SIGKILL to remaining processes",
STATUS_OK, false);
print_status("sync", STATUS_WAIT, false);
sync();
print_status("sync", STATUS_OK, true);
reboot(type);
perror("reboot system call");
exit(EXIT_FAILURE);
}

27
initd/status.c

@ -0,0 +1,27 @@
#include <stdio.h>
#include "init.h"
void print_status(const char *msg, int type, bool update)
{
const char *str;
switch (type) {
case STATUS_FAIL:
str = "\033[22;31mFAIL\033[0m";
break;
case STATUS_WAIT:
str = "\033[22;33m .. \033[0m";
break;
default:
str = "\033[22;32m OK \033[0m";
break;
}
if (update)
fputc('\r', stdout);
printf("[%s] %s", str, msg);
if (type != STATUS_WAIT)
fputc('\n', stdout);
fflush(stdout);
}

43
initd/svclist.c

@ -0,0 +1,43 @@
#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;
}

13
lib/Makemodule.am

@ -0,0 +1,13 @@
HEADRS = lib/include/util.h lib/include/service.h lib/include/telinit.h
libinit_a_SOURCES = lib/src/delsrv.c lib/src/rdline.c
libinit_a_SOURCES += lib/src/splitkv.c lib/src/enum_by_name.c
libinit_a_SOURCES += lib/src/strexpand.c lib/src/rdsrv.c lib/src/srvscan.c
libinit_a_SOURCES += lib/src/del_srv_list.c lib/src/srv_tsort.c
libinit_a_SOURCES += lib/src/opensock.c $(HEADRS)
libinit_a_CPPFLAGS = $(AM_CPPFLAGS)
libinit_a_CFLAGS = $(AM_CFLAGS)
EXTRA_DIST += $(HEADRS)
noinst_LIBRARIES += libinit.a

62
lib/include/service.h

@ -0,0 +1,62 @@
#ifndef SERVICE_H
#define SERVICE_H
#include <sys/types.h>
enum {
SVC_ONCE = 0,
SVC_WAIT,
SVC_RESPAWN,
};
enum {
TGT_BOOT = 0,
TGT_SHUTDOWN,
TGT_REBOOT,
TGT_CAD,
TGT_MAX
};
typedef struct service_t {
int type; /* SVC_* service type */
int target; /* TGT_* service target */
char *name; /* canonical service name */
char *desc; /* description string */
char **exec; /* command lines to execute */
size_t num_exec; /* number of command lines */
char *ctty; /* controlling tty or log file */
char **before;
size_t num_before;
char **after;
size_t num_after;
pid_t pid;
int status; /* process exit status */
struct service_t *next;
} service_t;
typedef struct {
service_t *targets[TGT_MAX];
} service_list_t;
/*
Read a service from a file.
*/
service_t *rdsrv(int dirfd, const char *filename);
void delsrv(service_t *srv);
int srvscan(const char *directory, service_list_t *list);
void del_srv_list(service_list_t *list);
/*
Sort a list of services by dependencies.
*/
service_t *srv_tsort(service_t *list);
#endif /* SERVICE_H */

20
lib/include/telinit.h

@ -0,0 +1,20 @@
#ifndef TELINIT_H
#define TELINIT_H
#include "config.h"
#define INITSOCK SOCKDIR "/" "initd.socket"
enum {
TI_SHUTDOWN = 1,
TI_REBOOT = 2,
};
typedef struct {
int type;
} ti_msg_t;
int opensock(void);
#endif /* TELINIT_H */

58
lib/include/util.h

@ -0,0 +1,58 @@
#ifndef UTIL_H
#define UTIL_H
#include <sys/types.h>
#include <stdbool.h>
#include <stddef.h>
#include "config.h"
#ifdef __GNUC__
#define NORETURN __attribute__((noreturn))
#endif
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
typedef struct {
const char *name;
int value;
} enum_map_t;
/*
Read from fd until end-of-file or a line feed is encountered.
Returns NULL with errno set on failure. Returns NULL with errno
cleared if end-of-file is reached.
The line must be deallocated with free().
*/
char *rdline(int fd);
/*
Split a line of the shape "key = value" into key and value part.
The key can contain alphanumeric characters and can be padded with
spaces or tabs.
The value can be either a sequence of alphanumeric characters, period
or underscore OR a string in quotation marks. For strings, the
quotation marks are removed and escape sequences are processed.
The value may also be padded with spaces or tabs but the line may not
contain anything else after the value, except for spaces, tabs or
the '#' symbol which is interpreted as start of a comment.
*/
int splitkv(char *line, char **key, char **value);
/*
Search through an array of enum_map_t entries to resolve a string to
a numeric value. The end of the map is indicated by a sentinel entry
with the name set to NULL.
*/
const enum_map_t *enum_by_name(const enum_map_t *map, const char *name);
char *strexpand(const char *inp, size_t argc, const char *const *argv);
#endif /* UTIL_H */

18
lib/src/del_srv_list.c

@ -0,0 +1,18 @@
#include <stdlib.h>
#include "service.h"
void del_srv_list(service_list_t *list)
{
service_t *srv;
int i;
for (i = 0; i < TGT_MAX; ++i) {
while (list->targets[i] != NULL) {
srv = list->targets[i];
list->targets[i] = srv->next;
delsrv(srv);
}
}
}

25
lib/src/delsrv.c

@ -0,0 +1,25 @@
#include <stdlib.h>
#include "service.h"
void delsrv(service_t *srv)
{
size_t i;
for (i = 0; i < srv->num_exec; ++i)
free(srv->exec[i]);
for (i = 0; i < srv->num_before; ++i)
free(srv->before[i]);
for (i = 0; i < srv->num_after; ++i)
free(srv->after[i]);
free(srv->before);
free(srv->after);
free(srv->name);
free(srv->desc);
free(srv->exec);
free(srv->ctty);
free(srv);
}

15
lib/src/enum_by_name.c

@ -0,0 +1,15 @@
#include <string.h>
#include "util.h"
const enum_map_t *enum_by_name(const enum_map_t *map, const char *name)
{
size_t i;
for (i = 0; map[i].name != NULL; ++i) {
if (!strcmp(map[i].name, name))
return map + i;
}
return NULL;
}

33
lib/src/opensock.c

@ -0,0 +1,33 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "telinit.h"
int opensock(void)
{
struct sockaddr_un un;
int fd;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, INITSOCK);
if (connect(fd, (struct sockaddr *)&un, sizeof(un))) {
perror("connect: " INITSOCK);
close(fd);
return -1;
}
return fd;
}

54
lib/src/rdline.c

@ -0,0 +1,54 @@
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include "util.h"
char *rdline(int fd)
{
size_t i = 0, bufsiz = 0, newsz;
char c, *new, *buffer = NULL;
int ret;
for (;;) {
switch (read(fd, &c, 1)) {
case 0:
if (i == 0) {
errno = 0;
return NULL;
}
c = '\0';
break;
case 1:
if (c == '\n')
c = '\0';
break;
default:
if (errno == EINTR)
continue;
goto fail;
}
if (i == bufsiz) {
newsz = bufsiz ? bufsiz * 2 : 16;
new = realloc(buffer, newsz);
if (new == NULL)
goto fail;
buffer = new;
bufsiz = newsz;
}
buffer[i++] = c;
if (c == '\0')
break;
}
return buffer;
fail:
ret = errno;
free(buffer);
errno = ret;
return NULL;
}

249
lib/src/rdsrv.c

@ -0,0 +1,249 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include "service.h"
#include "util.h"
static const enum_map_t type_map[] = {
{ "once", SVC_ONCE },
{ "wait", SVC_WAIT },
{ "respawn", SVC_RESPAWN },
{ NULL, 0 },
};
static const enum_map_t target_map[] = {
{ "boot", TGT_BOOT },
{ "shutdown", TGT_SHUTDOWN },
{ "reboot", TGT_REBOOT },
{ "ctrlaltdel", TGT_CAD },
{ NULL, 0 },
};
static int srv_name(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
(void)filename; (void)lineno;
srv->name = arg;
return 0;
}
static int srv_desc(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
(void)filename; (void)lineno;
srv->desc = arg;
return 0;
}
static int srv_tty(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
(void)filename; (void)lineno;
srv->ctty = arg;
return 0;
}
static int srv_exec(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
char **new = realloc(srv->exec, sizeof(char*) * (srv->num_exec + 1));
if (new == NULL) {
fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
free(arg);
return -1;
}
srv->exec = new;
srv->exec[srv->num_exec++] = arg;
return 0;
}
static int srv_before(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
char **new = realloc(srv->before,
sizeof(char*) * (srv->num_before + 1));
if (new == NULL) {
fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
free(arg);
return -1;
}
srv->before = new;
srv->before[srv->num_before++] = arg;
return 0;
}
static int srv_after(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
char **new = realloc(srv->after, sizeof(char*) * (srv->num_after + 1));
if (new == NULL) {
fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
free(arg);
return -1;
}
srv->after = new;
srv->after[srv->num_after++] = arg;
return 0;
}
static int srv_type(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
const enum_map_t *ent = enum_by_name(type_map, arg);
if (ent == NULL) {
fprintf(stderr, "%s: %zu: unknown service type '%s'\n",
filename, lineno, arg);
return -1;
}
srv->type = ent->value;
free(arg);
return 0;
}
static int srv_target(service_t *srv, char *arg,
const char *filename, size_t lineno)
{
const enum_map_t *ent = enum_by_name(target_map, arg);
if (ent == NULL) {
fprintf(stderr, "%s: %zu: unknown service target '%s'\n",
filename, lineno, arg);
return -1;
}
srv->target = ent->value;
free(arg);
return 0;
}
static const struct {
const char *key;
int (*handle)(service_t *srv, char *arg,
const char *filename, size_t lineno);
} srv_params[] = {
{ "name", srv_name },
{ "description", srv_desc },
{ "exec", srv_exec },
{ "type", srv_type },
{ "target", srv_target },
{ "tty", srv_tty },
{ "before", srv_before },
{ "after", srv_after },
};
service_t *rdsrv(int dirfd, const char *filename)
{
const char *arg, *args[1];
char *line, *key, *value;
size_t i, argc, lineno;
service_t *srv;
int fd;
fd = openat(dirfd, filename, O_RDONLY);
if (fd < 0) {
perror(filename);
return NULL;
}
arg = strchr(filename, '@');
if (arg != NULL) {
args[0] = arg + 1;
argc = 1;
} else {
argc = 0;
}
srv = calloc(1, sizeof(*srv));
if (srv == NULL) {
fputs("out of memory\n", stderr);
close(fd);
return NULL;
}
for (lineno = 1; ; ++lineno) {
errno = 0;
line = rdline(fd);
if (line == NULL) {
if (errno != 0) {
fprintf(stderr, "read: %s: %zu: %s\n",
filename, lineno, strerror(errno));
goto fail;
}
break;
}
if (splitkv(line, &key, &value)) {
if (key == NULL) {
fprintf(stderr,
"%s: %zu: expected <key> = <value>\n",
filename, lineno);
} else if (value == NULL) {
fprintf(stderr,
"%s: %zu: expected value after %s\n",
filename, lineno, key);
} else {
fprintf(stderr,
"%s: %zu: unexpected arguments "
"after key-value pair\n",
filename, lineno);
}
goto fail;
}
if (key == NULL) {
free(line);
continue;
}
for (i = 0; i < ARRAY_SIZE(srv_params); ++i) {
if (!strcmp(srv_params[i].key, key))
break;
}
if (i >= ARRAY_SIZE(srv_params)) {
fprintf(stderr, "%s: %zu: unknown parameter '%s'\n",
filename, lineno, key);
goto fail_line;
}
value = strexpand(value, argc, args);
if (value == NULL) {
fputs("out of memory", stderr);
goto fail_line;
}
if (srv_params[i].handle(srv, value, filename, lineno)) {
free(value);
goto fail_line;
}
free(line);
}
close(fd);
return srv;
fail_line:
free(line);
fail:
close(fd);
delsrv(srv);
return NULL;
}

124
lib/src/splitkv.c

@ -0,0 +1,124 @@
#include <ctype.h>
#include "util.h"
static char *skpspc(char *ptr)
{
while (*ptr == ' ' || *ptr == '\t')
++ptr;
return ptr;
}
static int xdigit(int x)
{
if (isupper(x))
return x - 'A' + 0x0A;
if (islower(x))
return x - 'a' + 0x0A;
return x - '0';
}
static char *parse_str(char *src)
{
char *dst = src;
int c;
for (;;) {
c = *(src++);
switch (c) {
case '\\':
c = *(src++);
switch (c) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 't': c = '\t'; break;
case '\\': break;
case '"': break;
case 'x':
c = 0;
if (isxdigit(*src))
c = (c << 4) | xdigit(*(src++));
if (isxdigit(*src))
c = (c << 4) | xdigit(*(src++));
break;
case '0':
c = 0;
if (isdigit(*src) && *src < '8')
c = (c << 3) | (*(src++) - '0');
if (isdigit(*src) && *src < '8')
c = (c << 3) | (*(src++) - '0');
if (isdigit(*src) && *src < '8')
c = (c << 3) | (*(src++) - '0');
break;
default:
return NULL;
}
break;
case '"':
*(dst++) = '\0';
goto out;
}
*(dst++) = c;
}
out:
return src;
}
int splitkv(char *line, char **key, char **value)
{
*key = NULL;
*value = NULL;
line = skpspc(line);
if (*line == '#' || *line == '\0')
return 0;
if (!isalpha(*line))
return -1;
*key = line;
while (isalnum(*line))
++line;
if (*line == ' ' || *line == '\t') {
*(line++) = '\0';
line = skpspc(line);
}
if (*line != '=')
return -1;
*(line++) = '\0';
line = skpspc(line);
if (*line == '"') {
++line;
*value = line;
line = parse_str(line);
} else if (isalnum(*line)) {
*value = line;
while (isalnum(*line) || *line == '.' || *line == '_')
++line;
if (*line != '\0')
*(line++) = '\0';
} else {
return -1;
}
line = skpspc(line);
if (*line != '\0' && *line != '#')
return -1;
return 0;
}

75
lib/src/srv_tsort.c

@ -0,0 +1,75 @@
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include "service.h"
static bool has_dependencies(service_t *list, service_t *svc)
{
size_t i;
while (list != NULL) {
for (i = 0; i < svc->num_after; ++i) {
if (!strcmp(svc->after[i], list->name))
return true;
}
for (i = 0; i < list->num_before; ++i) {
if (!strcmp(list->before[i], svc->name))
return true;
}
list = list->next;
}
return false;
}
service_t *srv_tsort(service_t *list)
{
service_t *nl = NULL, *end = NULL;
service_t *svc, *prev;
while (list != NULL) {
/* remove first service without dependencies */
prev = NULL;
svc = list;
while (svc != NULL) {
if (has_dependencies(list, svc)) {
prev = svc;
svc = svc->next;
} else {
if (prev != NULL) {
prev->next = svc->next;
} else {
list = svc->next;
}
svc->next = NULL;
break;
}
}
/* cycle! */
if (svc == NULL) {
if (end == NULL) {
nl = list;
} else {
end->next = list;
}
errno = ELOOP;
break;
}
/* append to new list */
if (end == NULL) {
nl = end = svc;
} else {
end->next = svc;
end = svc;
}
}
return nl;
}

93
lib/src/srvscan.c

@ -0,0 +1,93 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include "service.h"
int srvscan(const char *directory, service_list_t *list)
{
int i, dfd, type, ret = 0;
struct dirent *ent;