Improve runlevel support by adding a bootstrap phase, also adds task/run cmd

Most systems need an early bootstrap phase for one-shot tasks and
necessary one-time probing.  This phase usually runs well before any
networking is setup.  This commit adds support for that 'S' runlevel,
and also extends the number of possible total runlevels to nine.

For the one-shot tasks that run in bootstrap two new finit.conf cmds
have been added:

   - task: For one-shot tasks that can be started in parallell
   - run:  For one-shot tasks that must run in sequence

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2013-06-04 09:25:05 +02:00
1 parent e776838de9
commit 25cc9f3b9b
9 files changed
+163 -85

No files matched your search

+34 -11
View File
@@ -61,20 +61,22 @@ void parse_finit_conf(char *file)
if ((fp = fopen(file, "r")) != NULL) {
char *x;
const char *err = NULL;
_d("Parse %s ...", file);
while (!feof(fp)) {
if (!fgets(line, sizeof(line), fp))
continue;
chomp(line);
_d("conf: %s", line);
chomp(line);
/* Skip comments. */
if (MATCH_CMD(line, "#", x)) {
if (MATCH_CMD(line, "#", x))
continue;
}
/* Do this before mounting / read-write */
/* 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);
@@ -126,7 +128,7 @@ void parse_finit_conf(char *file)
continue;
}
if (MATCH_CMD(line, "startx ", x)) {
svc_register(strip_line(x), username);
svc_register(SVC_CMD_SERVICE, strip_line(x), username);
continue;
}
if (MATCH_CMD(line, "shutdown ", x)) {
@@ -135,18 +137,39 @@ void parse_finit_conf(char *file)
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);
runlevel = strtonum(token, 1, 5, NULL);
if (!runlevel)
runlevel = RUNLEVEL;
if (runlevel < 1 || runlevel > 5)
runlevel = 3; /* Fallback */
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(x, NULL);
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;
}
+17 -17
View File
@@ -38,8 +38,9 @@
int debug = 0;
int verbose = 1;
int runlevel = 3;
int prevlevel = 1;
int runlevel = RUNLEVEL_BOOT; /* Bootstrap */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int prevlevel = 0; /* HALT */
char *sdown = NULL;
char *network = NULL;
char *username = NULL;
@@ -131,6 +132,7 @@ static int client(int argc, char *argv[])
static int run_loop(void)
{
_d("Entering main loop ...");
while (1) {
svc_monitor();
plugin_monitor();
@@ -204,8 +206,6 @@ int main(int argc, char* argv[])
/*
* Mount filesystems
*/
_d("Mount filesystems in /etc/fstab ...");
#ifdef REMOUNT_ROOTFS_RW
run("/bin/mount -n -o remount,rw /");
#endif
@@ -224,14 +224,17 @@ int main(int argc, char* argv[])
/* Cleanup stale files, if any still linger on. */
run_interactive("rm -rf /tmp/* /var/run/* /var/lock/*", "Cleanup temporary directories");
/*
* Base FS up, enable standard SysV init signals
*/
/* Base FS up, enable standard SysV init signals */
sig_setup();
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
/*
* Start all bootstrap tasks, no network available!
*/
svc_bootstrap();
/*
* Network stuff
*/
@@ -247,16 +250,16 @@ int main(int argc, char* argv[])
run_interactive(network, "Starting networking: %s", network);
umask(022);
/*
* Hooks that rely on loopback, or basic networking being up.
*/
/* Hooks that rely on loopback, or basic networking being up. */
plugin_run_hooks(HOOK_NETWORK_UP);
/*
* Start service monitor framework
* Start all tasks/services in the configured runlevel
*/
_d("Starting all static services from %s", FINIT_CONF);
svc_start_all();
svc_runlevel(cfglevel);
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
/*
* Run startup scripts in /etc/finit.d/, if any.
@@ -266,9 +269,7 @@ int main(int argc, char* argv[])
run_parts(rcsd, NULL);
}
/*
* Hooks that should run at the very end
*/
/* Hooks that should run at the very end */
plugin_run_hooks(HOOK_SYSTEM_UP);
/* Start GETTY on console(s) */
@@ -277,7 +278,6 @@ int main(int argc, char* argv[])
/*
* Enter main loop to monior /dev/initctl and services
*/
_d("Entering main loop ...");
return run_loop();
}
+1
View File
@@ -73,6 +73,7 @@ struct init_request {
};
extern int runlevel;
extern int cfglevel;
extern int prevlevel;
extern char *sdown;
extern char *network;
+24 -13
View File
@@ -309,7 +309,7 @@ int procname_kill (char *name, int signo)
}
static int print_uptime(void)
static int print_timestamp(void)
{
#if defined(CONFIG_PRINTK_TIME)
FILE * uptimefile;
@@ -332,12 +332,10 @@ static int print_uptime(void)
void print_desc(char *action, char *desc)
{
const char home[] = "\r\e[K";
const char dots[] = " .....................................................................";
write(STDERR_FILENO, home, strlen(home));
print_uptime();
delline();
print_timestamp();
write(STDERR_FILENO, action, strlen(action));
write(STDERR_FILENO, desc, strlen(desc));
@@ -354,6 +352,25 @@ int print_result(int fail)
return fail;
}
/* Wait for process completion, returns status of waitpid(2) syscall */
int complete(char *cmd, int pid)
{
int status = 0;
if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR)
_e("Caught unblocked signal waiting for %s, aborting.", cmd);
else if (errno == ECHILD)
_e("Caught SIGCHLD waiting for %s, aborting.", cmd);
else
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
return -1;
}
return status;
}
int run(char *cmd)
{
int status, result, i = 0;
@@ -423,14 +440,8 @@ int run(char *cmd)
return -1;
}
if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR)
_e("Caught unblocked signal waiting for %s, aborting.", args[0]);
else if (errno == ECHILD)
_e("Caught SIGCHLD waiting for %s, aborting.", args[0]);
else
_e("Failed starting %s, error %d: %s", args[0], errno, strerror (errno));
status = complete(args[0], pid);
if (-1 == status) {
if (fp) fclose(fp);
free(backup);
+1
View File
@@ -97,6 +97,7 @@ int getuser (char *username);
int getgroup (char *group);
void set_hostname (char *hostname);
int complete (char *cmd, int pid);
int run (char *cmd);
int run_interactive (char *cmd, char *fmt, ...);
pid_t run_getty (char *cmd, int console);
+4 -1
View File
@@ -91,10 +91,13 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
case 's':
case 'S':
_d("Cannot enter bootstrap after boot ...");
rq.runlevel = '1';
/* Fall through to regular processing */
case '1'...'5':
_d("Setting new runlevel %c ...", rq.runlevel);
case '7'...'9':
_d("Setting new runlevel %c", rq.runlevel);
svc_runlevel(rq.runlevel - '0');
break;
+2 -2
View File
@@ -1,7 +1,7 @@
/* Private header file for main finit daemon, not for plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2008-2013 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
@@ -28,7 +28,7 @@
#include "svc.h"
#include "plugin.h"
void svc_start_all (void);
void svc_bootstrap (void);
void svc_monitor (void);
void plugin_run_hooks (hook_point_t no);
+70 -39
View File
@@ -1,7 +1,7 @@
/* Finit service monitor and heneric API for managing svc_t structures
/* Finit service monitor, task starter and generic API for managing svc_t
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2008-2013 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
@@ -125,6 +125,25 @@ svc_t *svc_iterator(int restart)
return &services[i++];
}
/**
* svc_bootstrap - Start bootstrap services and tasks
*
* System startup, runlevel S, where only services, tasks and
* run commands absolutely essential to bootstrap are located.
*/
void svc_bootstrap(void)
{
svc_t *svc;
svc_cmd_t cmd;
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
cmd = svc_enabled(svc, 0, NULL);
if (SVC_START == cmd || (SVC_RELOAD == cmd))
svc_start(svc);
}
}
/**
* svc_runlevel - Change to a new runlevel
* @newlevel: New runlevel to activate
@@ -164,22 +183,36 @@ void svc_runlevel (int newlevel)
}
/**
* svc_register - Register a non-backgrounding service to be monitored
* svc_register - Register service, task or run commands
* @type: %SVC_CMD_SERVICE(0), %SVC_CMD_TASK(1), %SVC_CMD_RUN(2)
* @line: A complete command line with -- separated description text
* @username: Optional username to run service as, or %NULL to run as root
*
* Actually, the @line can optionally start with a username, denoted by
* an @ character. Like this:
* This function is used to register commands to be run on different
* system runlevels with optional username. The @type argument details
* if it's service to bo monitored/respawned (daemon), a one-shot task
* or a command that must run in sequence and not in parallell, like
* service and task commands do.
*
* "service @username [!0-6] /path/to/daemon -- Description text"
* The @line can optionally start with a username, denoted by an @
* character. Like this:
*
* If the username is left out the daemon is started as root. The []
* brackets are there to denote the allowed runlevels.
* "service @username [!0-6,S] /path/to/daemon arg -- Description text"
* "task @username [!0-6,S] /path/to/task arg -- Description text"
* "run @username [!0-6,S] /path/to/cmd arg -- Description text"
*
* If the username is left out the command is started as root.
* The [] brackets are there to denote the allowed runlevels. Allowed
* runlevels mimic that of SysV init with the addition of the 'S'
* runlevel, which is only run once at startup. It can be seen as the
* system bootstrap. If a task or run command is listed in more than the
* [S] runlevel they will be called when changing runlevel.
*
* Returns:
* POSIX OK(0) on success, or non-zero errno exit status on failure.
*/
int svc_register(char *line, char *username)
int svc_register(int type, char *line, char *username)
{
int i = 0, not = 0;
char *cmd, *desc, *runlevels = NULL;
@@ -221,13 +254,17 @@ int svc_register(char *line, char *username)
return errno = ENOMEM;
}
svc->type = type;
if (desc)
strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
if (username)
strlcpy(svc->username, username, sizeof(svc->username));
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while ((cmd = strtok(NULL, " ")))
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
svc->args[i][0] = 0;
@@ -243,14 +280,15 @@ int svc_register(char *line, char *username)
break;
if ('!' == lvl) {
not = 1;
svc->runlevels = 0x7E;
svc->runlevels = 0x3FE;
continue;
}
if ('s' == lvl || 'S' == lvl)
lvl = '1';
lvl = ':'; /* RUNLEVEL_BOOT */
level = lvl - '0';
if (level > 6 || level < 0)
if (level > RUNLEVEL_BOOT || level < 0)
continue;
if (not)
@@ -258,6 +296,7 @@ int svc_register(char *line, char *username)
else
SETBIT(svc->runlevels, level);
}
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
return 0;
@@ -320,12 +359,15 @@ void svc_monitor(void)
return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
char *name = basename(svc->cmd);
if (lost != svc->pid)
continue;
_d("Ouch, lost pid %d - %s(%d)", lost, name, svc->pid);
if (SVC_CMD_SERVICE != svc->type) {
svc->pid = 0;
continue;
}
_d("Ouch, lost pid %d - %s(%d)", lost, basename(svc->cmd), svc->pid);
/* No longer running, update books. */
svc->pid = 0;
@@ -336,8 +378,8 @@ void svc_monitor(void)
}
/* Cleanup any lingering or semi-restarting tasks before respawning */
_d("Sending SIGTERM to service group %s", name);
procname_kill(name, SIGTERM);
_d("Sending SIGTERM to service group %s", basename(svc->cmd));
procname_kill(basename(svc->cmd), SIGTERM);
/* Restarting lost service. */
if (svc_enabled(svc, 0, NULL)) {
@@ -375,7 +417,9 @@ int svc_start(svc_t *svc)
if (is_norespawn())
return 0;
if (!respawn)
if (SVC_CMD_SERVICE != svc->type)
print_desc("", svc->desc);
else if (!respawn)
print_desc("Starting ", svc->desc);
/* Block sigchild while forking. */
@@ -435,7 +479,9 @@ int svc_start(svc_t *svc)
}
svc->pid = pid;
if (!respawn)
if (SVC_CMD_RUN == svc->type)
print_result(WEXITSTATUS(complete(svc->cmd, pid)));
else if (!respawn)
print_result(svc->pid > 1 ? 0 : 1);
return 0;
@@ -467,6 +513,9 @@ int svc_stop(svc_t *svc)
return 1;
}
if (SVC_CMD_SERVICE != svc->type)
return 0;
if (runlevel != 1)
print_desc("Stopping ", svc->desc);
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, pid_get_name(svc->pid, NULL, 0));
@@ -500,24 +549,6 @@ int svc_reload(svc_t *svc)
return kill(svc->pid, SIGHUP);
}
void svc_start_all(void)
{
svc_t *svc;
svc_cmd_t cmd;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
cmd = svc_enabled(svc, 0, NULL);
if (SVC_START == cmd || (SVC_RELOAD == cmd && svc->pid == 0))
svc_start(svc);
else if (SVC_RELOAD == cmd)
svc_reload(svc);
}
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
}
/**
* Local Variables:
* compile-command: "gcc -g -DUNITTEST -o unittest svc.c"
+10 -2
View File
@@ -1,4 +1,4 @@
/* Finit service monitor and heneric API for managing svc_t structures
/* Finit service monitor and generic API for managing svc_t structures
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -36,6 +36,13 @@ typedef enum {
SVC_RELOAD /* Enabled, needs restart */
} svc_cmd_t;
typedef enum {
SVC_CMD_SERVICE = 0, /* Monitored, will be respawned */
SVC_CMD_TASK, /* One-shot, runs in parallell */
SVC_CMD_RUN /* Like task, but wait for completion */
} svc_type_t;
#define RUNLEVEL_BOOT 10
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
#define MAX_ARG_LEN 64
#define MAX_STR_LEN 64
@@ -49,6 +56,7 @@ typedef enum {
* initctl <stop|start|restart> service */
typedef struct svc {
pid_t pid;
svc_type_t type;
int reload;
int runlevels;
unsigned int stat_restart_counter; /* Incremented for each restart by service monitor. */
@@ -90,7 +98,7 @@ svc_t *svc_find (char *name);
svc_t *svc_iterator (int restart);
void svc_runlevel (int newlevel);
int svc_register (char *line, char *username);
int svc_register (int type, char *line, char *username);
int svc_id_by_name (char *name);
svc_cmd_t svc_enabled (svc_t *svc, int event, void *arg);
int svc_start (svc_t *svc);