Add support for adding/removing services from /etc/finit.d at runtime.

This patch adds support for adding and removing services from Finit at
runtime.  Configuration file stanzas for service, run and task may now
be written to files in /etc/finit.d/*.conf, using the exact same syntax
as before in /etc/finit.conf.  When a file is added, removed or modified
the user may simply SIGHUP Finit (PID 1) to activate the changes.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2015-05-31 01:55:39 +02:00
1 parent 24051ab2fb
commit 687f52b0b5
10 files changed
+454 -233

No files matched your search

+236 -143
View File
@@ -1,6 +1,6 @@
/* Parser for finit.conf
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2012-2015 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,7 @@
*/
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#include "finit.h"
@@ -33,6 +34,9 @@
#define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
static int parse_conf(char *file);
static char *strip_line(char *line)
{
char *ptr;
@@ -86,13 +90,197 @@ int parse_runlevels(char *runlevels)
return bitmask;
}
static void parse_static(char *line)
{
char *x;
char cmd[CMD_SIZE];
/* Do this before mounting / read-write
* XXX: Move to plugin which checks /etc/fstab instead */
if (MATCH_CMD(line, "check ", x)) {
char *dev = strip_line(x);
strcpy(cmd, "/sbin/fsck -C -a ");
strlcat(cmd, dev, sizeof(cmd));
run_interactive(cmd, "Checking file system %s", dev);
return;
}
if (MATCH_CMD(line, "user ", x)) {
if (username) free(username);
username = strdup(strip_line(x));
return;
}
if (MATCH_CMD(line, "host ", x)) {
if (hostname) free(hostname);
hostname = strdup(strip_line(x));
return;
}
if (MATCH_CMD(line, "module ", x)) {
char *mod = strip_line(x);
strcpy(cmd, "/sbin/modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
return;
}
if (MATCH_CMD(line, "mknod ", x)) {
char *dev = strip_line(x);
strcpy(cmd, "/bin/mknod ");
strlcat(cmd, dev, sizeof(cmd));
run_interactive(cmd, "Creating device node %s", dev);
return;
}
if (MATCH_CMD(line, "network ", x)) {
if (network) free(network);
network = strdup(strip_line(x));
return;
}
/* This is the directory from which executable scripts and
* any finit include files are read, default /etc/finit.d/ */
if (MATCH_CMD(line, "runparts ", x)) {
if (runparts) free(runparts);
runparts = strdup(strip_line(x));
return;
}
/* Parse any include file, use rcsd if absolute path not given */
if (MATCH_CMD(line, "include ", x)) {
char *file = strip_line(x);
strlcpy(cmd, file, sizeof(cmd));
if (!fexist(cmd)) {
/* ... try /etc/finit.d/ as prefix */
snprintf(cmd, sizeof(cmd), "%s/%s", rcsd, file);
if (!fexist(cmd))
return;
}
parse_conf(cmd);
return;
}
if (MATCH_CMD(line, "startx ", x)) {
svc_register(SVC_TYPE_SERVICE, strip_line(x), 0, username);
return;
}
if (MATCH_CMD(line, "shutdown ", x)) {
if (sdown) free(sdown);
sdown = strdup(strip_line(x));
return;
}
/* The desired runlevel to start when leaving
* bootstrap (S). Finit supports 1-9, but most
* systems only use 1-6, where 6 is reserved for
* reboot */
if (MATCH_CMD(line, "runlevel ", x)) {
char *token = strip_line(x);
const char *err = NULL;
cfglevel = strtonum(token, 1, 9, &err);
if (err)
cfglevel = RUNLEVEL;
if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6)
cfglevel = 2; /* Fallback */
return;
}
/* TODO: Make console & tty dynamically loadable from /etc/finit.d */
if (MATCH_CMD(line, "console ", x)) {
if (console) free(console);
console = strdup(strip_line(x));
return;
}
/* TODO: Make console & tty dynamically loadable from /etc/finit.d */
if (MATCH_CMD(line, "tty ", x)) {
tty_register(strip_line(x));
return;
}
}
static void parse_dynamic(char *line, time_t mtime)
{
char *x;
/* Skip comments. */
if (MATCH_CMD(line, "#", x))
return;
/* Monitored daemon, will be respawned on exit, as
* long as the (optional) service callback returns
* non-zero */
if (MATCH_CMD(line, "service ", x)) {
svc_register(SVC_TYPE_SERVICE, x, mtime, NULL);
return;
}
/* One-shot task, will not be respawned. Only runs if
* the (optional) service callback returns true */
if (MATCH_CMD(line, "task ", x)) {
svc_register(SVC_TYPE_TASK, x, mtime, NULL);
return;
}
/* Like task but waits for completion, useful w/ [S] */
if (MATCH_CMD(line, "run ", x)) {
svc_register(SVC_TYPE_RUN, x, mtime, NULL);
return;
}
/* Classic inetd service */
if (MATCH_CMD(line, "inetd ", x)) {
#ifndef INETD_DISABLED
svc_register(SVC_TYPE_INETD, x, mtime, NULL);
#else
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
#endif
return;
}
}
static int parse_conf_dynamic(char *file, time_t mtime)
{
FILE *fp = fopen(file, "r");
if (!fp) {
_pe("Failed opening %s", file);
return 1;
}
while (!feof(fp)) {
char line[LINE_SIZE] = "";
if (!fgets(line, sizeof(line), fp))
continue;
chomp(line);
_d("dyn conf: %s", line);
parse_dynamic(line, mtime);
}
fclose(fp);
return 0;
}
static int parse_conf(char *file)
{
FILE *fp;
char line[LINE_SIZE] = "";
char cmd[CMD_SIZE];
char *x;
const char *err = NULL;
fp = fopen(file, "r");
if (!fp)
@@ -123,146 +311,10 @@ static int parse_conf(char *file)
if (!fgets(line, sizeof(line), fp))
continue;
chomp(line);
_d("conf: %s", line);
/* Skip comments. */
if (MATCH_CMD(line, "#", x))
continue;
/* Do this before mounting / read-write
* XXX: Move to plugin which checks /etc/fstab instead */
if (MATCH_CMD(line, "check ", x)) {
char *dev = strip_line(x);
strcpy(cmd, "/sbin/fsck -C -a ");
strlcat(cmd, dev, sizeof(cmd));
run_interactive(cmd, "Checking file system %s", dev);
continue;
}
if (MATCH_CMD(line, "user ", x)) {
if (username) free(username);
username = strdup(strip_line(x));
continue;
}
if (MATCH_CMD(line, "host ", x)) {
if (hostname) free(hostname);
hostname = strdup(strip_line(x));
continue;
}
if (MATCH_CMD(line, "module ", x)) {
char *mod = strip_line(x);
strcpy(cmd, "/sbin/modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
continue;
}
if (MATCH_CMD(line, "mknod ", x)) {
char *dev = strip_line(x);
strcpy(cmd, "/bin/mknod ");
strlcat(cmd, dev, sizeof(cmd));
run_interactive(cmd, "Creating device node %s", dev);
continue;
}
if (MATCH_CMD(line, "network ", x)) {
if (network) free(network);
network = strdup(strip_line(x));
continue;
}
/* This is the directory from which executable scripts and
* any finit include files are read, default /etc/finit.d/ */
if (MATCH_CMD(line, "runparts ", x)) {
if (rcsd) free(rcsd);
rcsd = strdup(strip_line(x));
continue;
}
/* Parse any include file, use rcsd if absolute path not given */
if (MATCH_CMD(line, "include ", x)) {
char *file = strip_line(x);
strlcpy(cmd, file, sizeof(cmd));
if (!fexist(cmd)) {
/* ... try /etc/finit.d/ as prefix */
snprintf(cmd, sizeof(cmd), "%s/%s", rcsd, file);
if (!fexist(cmd))
continue;
}
parse_conf(cmd);
continue;
}
if (MATCH_CMD(line, "startx ", x)) {
svc_register(SVC_CMD_SERVICE, strip_line(x), username);
continue;
}
if (MATCH_CMD(line, "shutdown ", x)) {
if (sdown) free(sdown);
sdown = strdup(strip_line(x));
continue;
}
/* The desired runlevel to start when leaving
* bootstrap (S). Finit supports 1-9, but most
* systems only use 1-6, where 6 is reserved for
* reboot */
if (MATCH_CMD(line, "runlevel ", x)) {
char *token = strip_line(x);
cfglevel = strtonum(token, 1, 9, &err);
if (err)
cfglevel = RUNLEVEL;
if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6)
cfglevel = 2; /* Fallback */
continue;
}
/* Monitored daemon, will be respawned on exit, as
* long as the (optional) service callback returns
* non-zero */
if (MATCH_CMD(line, "service ", x)) {
svc_register(SVC_CMD_SERVICE, x, NULL);
continue;
}
/* One-shot task, will not be respawned. Only runs if
* the (optional) service callback returns true */
if (MATCH_CMD(line, "task ", x)) {
svc_register(SVC_CMD_TASK, x, NULL);
continue;
}
/* Like task but waits for completion, useful w/ [S] */
if (MATCH_CMD(line, "run ", x)) {
svc_register(SVC_CMD_RUN, x, NULL);
continue;
}
/* Classic inetd service */
if (MATCH_CMD(line, "inetd ", x)) {
#ifndef INETD_DISABLED
svc_register(SVC_CMD_INETD, x, NULL);
#else
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
#endif
continue;
}
if (MATCH_CMD(line, "console ", x)) {
if (console) free(console);
console = strdup(strip_line(x));
continue;
}
if (MATCH_CMD(line, "tty ", x)) {
tty_register(strip_line(x));
continue;
}
parse_static(line);
parse_dynamic(line, 0);
}
fclose(fp);
@@ -270,13 +322,54 @@ static int parse_conf(char *file)
return 0;
}
int parse_finit_d(char *dir)
{
int i, num;
struct dirent **e;
num = scandir(dir, &e, NULL, alphasort);
if (num < 0) {
_d("No files found in %s, skipping ...", dir);
return -1;
}
for (i = 0; i < num; i++) {
char *name = e[i]->d_name;
char path[CMD_SIZE];
struct stat st;
snprintf(path, sizeof(path), "%s/%s", dir, name);
/* Check that it's an actual file ... */
if (stat(path, &st)) {
_d("Cannot even read .conf file %s, skipping ...", path);
continue;
}
if (S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode))
continue;
/* Check that file ends with '.conf' */
if (strcmp(&path[strlen(path) - 5], ".conf")) {
_d("File %s is not a .conf, skipping ... ", path);
continue;
}
parse_conf_dynamic(path, st.st_mtime);
}
while (num--)
free(e[num]);
free(e);
return 0;
}
int parse_finit_conf(char *file)
{
int result;
username = strdup(DEFUSER);
hostname = strdup(DEFHOST);
rcsd = strdup(FINIT_RCSD);
result = parse_conf(file);
if (!tty_num()) {