mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Refactor initial startup, run runparts and rc.local in background
The code refactored in this commit has long been an eyesore. The
hand-over, from the sequential main() function, to the event loop
was clumsy at best and was very difficult to debug.
Instead of using a series of workers we now delegate everything to
the big state machine by introducing a new SM_BOOTSRTAP_WAIT_STATE.
Both the runparts and rc.local scripts now run in the background, no
longer blocking the event loop from responding to initctl calls. To
ensure they run at the same point in time two things have been done:
1. A new <int/bootstrap> condition has been added which triggers
runparts, which now is a regular task created by conf_init()
2. /etc/rc.local is started from SM_RUNLEVEL_WAIT_STATE when we
transition from runlevel S to any other runlevel.
Fixes #356
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
+7
-2
@@ -8,7 +8,7 @@ AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
sbin_PROGRAMS = finit initctl
|
||||
pkglibexec_PROGRAMS = getty logit
|
||||
pkglibexec_PROGRAMS = getty logit runparts
|
||||
if SULOGIN
|
||||
pkglibexec_PROGRAMS += sulogin
|
||||
endif
|
||||
@@ -32,6 +32,11 @@ keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
keventd_CFLAGS += $(lite_CFLAGS)
|
||||
keventd_LDADD = $(lite_LIBS)
|
||||
|
||||
runparts_SOURCES = runparts.c
|
||||
runparts_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
runparts_CFLAGS += $(lite_CFLAGS)
|
||||
runparts_LDADD = $(lite_LIBS)
|
||||
|
||||
sulogin_SOURCES = sulogin.c
|
||||
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
sulogin_LDADD = -lcrypt
|
||||
@@ -54,7 +59,7 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
mdadm.c mount.c \
|
||||
pid.c pid.h \
|
||||
plugin.c plugin.h private.h \
|
||||
schedule.c schedule.h \
|
||||
runparts.c schedule.c schedule.h \
|
||||
service.c service.h \
|
||||
sig.c sig.h \
|
||||
sm.c sm.h \
|
||||
|
||||
+13
-1
@@ -862,7 +862,7 @@ static int parse_static(char *line, int is_rcsd)
|
||||
|
||||
service_interval = val * 1000; /* to milliseconds */
|
||||
if (disabled)
|
||||
service_init();
|
||||
service_init(NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1448,6 +1448,18 @@ int conf_init(uev_ctx_t *ctx)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Background startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts) && !rescue) {
|
||||
char conf[sizeof(_PATH_RUNPARTS) + strlen(runparts) + 100];
|
||||
|
||||
snprintf(conf, sizeof(conf), "[S] <int/bootstrap> %s %s"
|
||||
" -- Calling runparts %s in the background",
|
||||
_PATH_RUNPARTS, runparts, runparts);
|
||||
service_register(SVC_TYPE_TASK, conf, global_rlimit, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -243,7 +243,7 @@ static void devmon_cb(uev_t *w, void *arg, int events)
|
||||
}
|
||||
}
|
||||
|
||||
void devmon_init(void)
|
||||
void devmon_init(uev_ctx_t *ctx)
|
||||
{
|
||||
char dir[MAX_ARG_LEN];
|
||||
|
||||
|
||||
+1
-1
@@ -27,6 +27,6 @@
|
||||
void devmon_add_cond(const char *cond);
|
||||
void devmon_del_cond(const char *cond);
|
||||
|
||||
void devmon_init(void);
|
||||
void devmon_init(uev_ctx_t *ctx);
|
||||
|
||||
#endif /* FINIT_DEVMON_H_ */
|
||||
|
||||
+10
-88
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <sysexits.h>
|
||||
@@ -443,96 +441,20 @@ pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void run_env(const char *env[])
|
||||
pid_t run_bg(char *cmd, char *args[])
|
||||
{
|
||||
size_t i = 0;
|
||||
pid_t pid;
|
||||
|
||||
if (!env)
|
||||
return;
|
||||
if (access(cmd, X_OK))
|
||||
return 0;
|
||||
|
||||
while (env[i]) {
|
||||
size_t j = i + 1;
|
||||
print(-1, "Calling %s in the background", cmd);
|
||||
pid = fork();
|
||||
if (!pid)
|
||||
_exit(exec_runtask(cmd, args));
|
||||
print_result(pid == -1);
|
||||
|
||||
if (!env[j])
|
||||
break;
|
||||
|
||||
setenv(env[i], env[j], 1);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
int run_parts(char *dir, char *cmd, const char *env[], int progress)
|
||||
{
|
||||
struct dirent **e;
|
||||
int i, num;
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
dbg("No files found in %s, skipping ...", dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
const char *name = e[i]->d_name;
|
||||
char path[strlen(dir) + strlen(name) + 2];
|
||||
struct stat st;
|
||||
char *argv[4] = {
|
||||
"sh",
|
||||
"-c",
|
||||
path,
|
||||
NULL
|
||||
};
|
||||
pid_t pid = 0;
|
||||
int status;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
if (stat(path, &st)) {
|
||||
dbg("Failed stat(%s): %s", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||
if (strcmp(name, ".") && strcmp(name, ".."))
|
||||
dbg("Skipping %s ...", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the callee didn't supply a run_parts() argument */
|
||||
if (!cmd) {
|
||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||
// dbg("Checking if %s is a sysvinit startstop script ...", name);
|
||||
if (name[0] == 'S' && isdigit(name[1]))
|
||||
strlcat(path, " start", sizeof(path));
|
||||
else if (name[0] == 'K' && isdigit(name[1]))
|
||||
strlcat(path, " stop", sizeof(path));
|
||||
} else {
|
||||
strlcat(path, cmd, sizeof(path));
|
||||
}
|
||||
|
||||
if (progress)
|
||||
print_desc("Calling ", path);
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
sig_unblock();
|
||||
run_env(env);
|
||||
|
||||
return execvp(_PATH_BSHELL, argv);
|
||||
}
|
||||
|
||||
status = complete(path, pid);
|
||||
if (WIFEXITED(status))
|
||||
warnx("%s exited with status %d", path, WEXITSTATUS(status));
|
||||
else if (WIFSIGNALED(status))
|
||||
warnx("%s terminated by signad %d", path, WTERMSIG(status));
|
||||
if (progress)
|
||||
print_result(WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
return 0;
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-124
@@ -58,7 +58,6 @@
|
||||
#include "tty.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
#include "schedule.h"
|
||||
|
||||
int runlevel = INIT_LEVEL; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
@@ -521,113 +520,6 @@ static void fs_init(void)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle bootstrap transition to configured runlevel, start TTYs
|
||||
*
|
||||
* This is the final stage of bootstrap. It changes to the default
|
||||
* (configured) runlevel, calls all external start scripts and final
|
||||
* bootstrap hooks before bringing up TTYs.
|
||||
*
|
||||
* We must ensure that all declared `task [S]` and `run [S]` jobs in
|
||||
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||
* finalize the bootstrap process by calling this function.
|
||||
*/
|
||||
static void finalize(void *unused)
|
||||
{
|
||||
/* Clean up bootstrap-only tasks/services that never started */
|
||||
dbg("Clean up all bootstrap-only tasks/services ...");
|
||||
svc_prune_bootstrap();
|
||||
|
||||
/* All services/tasks/etc. in configure runlevel have started */
|
||||
dbg("Running svc up hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Convenient SysV compat for when you just don't care ... */
|
||||
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
|
||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
dbg("Calling all system up hooks ...");
|
||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Disable progress output at normal runtime */
|
||||
enable_progress(0);
|
||||
|
||||
/* System bootrapped, launch TTYs et al */
|
||||
bootstrap = 0;
|
||||
service_step_all(SVC_TYPE_RESPAWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start cranking the big state machine
|
||||
*/
|
||||
static void crank_worker(void *unused)
|
||||
{
|
||||
/*
|
||||
* Initialize state machine and start all bootstrap tasks
|
||||
* NOTE: no network available!
|
||||
*/
|
||||
sm_init(&sm);
|
||||
sm_step(&sm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||
* allowed to complete their work in [S], or timeout, before we switch
|
||||
* to the configured runlevel and call finalize(), should not take more
|
||||
* than 120 sec.
|
||||
*/
|
||||
static void bootstrap_worker(void *work)
|
||||
{
|
||||
static struct wq final = {
|
||||
.cb = finalize,
|
||||
.delay = 10
|
||||
};
|
||||
static int cnt = 120 * 10; /* We run with 100ms period */
|
||||
int level = cfglevel;
|
||||
|
||||
dbg("Step all services ...");
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
if (cnt-- > 0 && !service_completed()) {
|
||||
dbg("Not all bootstrap run/tasks have completed yet ... %d", cnt);
|
||||
schedule_work(work);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cnt > 0)
|
||||
dbg("All run/task have completed, resuming bootstrap.");
|
||||
else
|
||||
dbg("Timeout, resuming bootstrap.");
|
||||
|
||||
dbg("Starting runlevel change finalize ...");
|
||||
schedule_work(&final);
|
||||
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts) && !rescue)
|
||||
run_parts(runparts, NULL, NULL, 1);
|
||||
|
||||
|
||||
dbg("Flushing pending .conf file events ...");
|
||||
conf_flush_events();
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel, or jump
|
||||
* into the runlevel selected from the command line.
|
||||
*/
|
||||
if (cmdlevel) {
|
||||
dbg("Runlevel %d requested from command line, starting all services ...", cmdlevel);
|
||||
level = cmdlevel;
|
||||
} else
|
||||
dbg("Change to default runlevel(%d), starting all services ...", cfglevel);
|
||||
|
||||
service_runlevel(level);
|
||||
}
|
||||
|
||||
static int version(int rc)
|
||||
{
|
||||
puts(PACKAGE_STRING);
|
||||
@@ -708,14 +600,6 @@ static int telinit(int argc, char *argv[])
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct wq crank_work = {
|
||||
.cb = crank_worker,
|
||||
.delay = 10
|
||||
};
|
||||
struct wq bootstrap_work = {
|
||||
.cb = bootstrap_worker,
|
||||
.delay = 100
|
||||
};
|
||||
uev_ctx_t loop;
|
||||
|
||||
/* user calling telinit or init */
|
||||
@@ -819,7 +703,7 @@ int main(int argc, char *argv[])
|
||||
* Initialize .conf system and load static /etc/finit.conf then
|
||||
* tell the world what we used.
|
||||
*/
|
||||
devmon_init();
|
||||
devmon_init(&loop);
|
||||
conf_init(&loop);
|
||||
conf_saverc();
|
||||
|
||||
@@ -832,16 +716,15 @@ int main(int argc, char *argv[])
|
||||
dbg("Starting initctl API responder ...");
|
||||
api_init(&loop);
|
||||
|
||||
dbg("Starting the big state machine ...");
|
||||
schedule_work(&crank_work);
|
||||
|
||||
dbg("Starting bootstrap finalize timer ...");
|
||||
schedule_work(&bootstrap_work);
|
||||
dbg("Starting service interval monitor ...");
|
||||
service_init(&loop);
|
||||
|
||||
/*
|
||||
* Background service tasks
|
||||
* Initialize state machine and start all bootstrap tasks
|
||||
* NOTE: no network available!
|
||||
*/
|
||||
service_init();
|
||||
sm_init(&sm);
|
||||
sm_step(&sm);
|
||||
|
||||
/*
|
||||
* Enter main loop to monitor /dev/initctl and services
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
|
||||
/* To use these, include config.h, or define FINIT_EXECPATH_ */
|
||||
#define _PATH_LOGIT FINIT_EXECPATH_ "/logit"
|
||||
#define _PATH_RUNPARTS FINIT_EXECPATH_ "/runparts"
|
||||
#define _PATH_SULOGIN FINIT_EXECPATH_ "/sulogin"
|
||||
#define _PATH_GETTY FINIT_EXECPATH_ "/getty"
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ int run_interactive (char *cmd, char *fmt, ...) __attribute__ ((format (prin
|
||||
int exec_runtask (char *cmd, char *args[]);
|
||||
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_bg (char *cmd, char *args[]);
|
||||
int run_parts (char *dir, char *cmd, const char *env[], int progress);
|
||||
|
||||
/*
|
||||
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/conio.h>
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/conio.h>
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "log.h"
|
||||
#include "sig.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef __FINIT__
|
||||
#include "helpers.h"
|
||||
#else
|
||||
#include <sys/prctl.h>
|
||||
|
||||
int debug;
|
||||
|
||||
static void print_desc(const char *prefix, const char *msg)
|
||||
{
|
||||
printf("\e[1m[\e[0m\e[1;33m ⋯ \e[0m\e[1m]\e[0m %s %s", prefix, msg);
|
||||
}
|
||||
static void print_result(int rc)
|
||||
{
|
||||
if (rc)
|
||||
puts("\r\e[1m[\e[0m\e[1;31mFAIL\e[0m\e[1m]\e[0m");
|
||||
else
|
||||
puts("\r\e[1m[\e[0m\e[1;32m OK \e[0m\e[1m]\e[0m");
|
||||
}
|
||||
|
||||
void sig_unblock(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
sigset_t nmask;
|
||||
int i;
|
||||
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGHUP);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigaddset(&nmask, SIGINT);
|
||||
sigaddset(&nmask, SIGPWR);
|
||||
sigaddset(&nmask, SIGSTOP);
|
||||
sigaddset(&nmask, SIGTSTP);
|
||||
sigaddset(&nmask, SIGCONT);
|
||||
sigaddset(&nmask, SIGTERM);
|
||||
sigaddset(&nmask, SIGUSR1);
|
||||
sigaddset(&nmask, SIGUSR2);
|
||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
}
|
||||
#endif /* __FINIT__ */
|
||||
|
||||
static void run_env(const char *env[])
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
while (env[i]) {
|
||||
size_t j = i + 1;
|
||||
|
||||
if (!env[j])
|
||||
break;
|
||||
|
||||
setenv(env[i], env[j], 1);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
int run_parts(char *dir, char *cmd, const char *env[], int progress)
|
||||
{
|
||||
struct dirent **e;
|
||||
int i, num;
|
||||
int rc = 0;
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
dbg("No files found in %s, skipping ...", dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
const char *name = e[i]->d_name;
|
||||
char path[strlen(dir) + strlen(name) + 2];
|
||||
struct stat st;
|
||||
char *argv[4] = {
|
||||
"sh",
|
||||
"-c",
|
||||
path,
|
||||
NULL
|
||||
};
|
||||
int result = 0;
|
||||
pid_t pid = 0;
|
||||
int status;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
if (stat(path, &st)) {
|
||||
dbg("Failed stat(%s): %s", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||
if (strcmp(name, ".") && strcmp(name, ".."))
|
||||
dbg("Skipping %s ...", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the callee didn't supply a run_parts() argument */
|
||||
if (!cmd) {
|
||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||
// dbg("Checking if %s is a sysvinit startstop script ...", name);
|
||||
if (name[0] == 'S' && isdigit(name[1]))
|
||||
strlcat(path, " start", sizeof(path));
|
||||
else if (name[0] == 'K' && isdigit(name[1]))
|
||||
strlcat(path, " stop", sizeof(path));
|
||||
} else {
|
||||
strlcat(path, cmd, sizeof(path));
|
||||
}
|
||||
|
||||
if (progress)
|
||||
print_desc("Calling ", path);
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
sig_unblock();
|
||||
run_env(env);
|
||||
|
||||
_exit(execvp(_PATH_BSHELL, argv));
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
warnx("failed starting %s, error %d: %s", path, errno, strerror(errno));
|
||||
result = 1;
|
||||
} else {
|
||||
if (WIFEXITED(status)) {
|
||||
result = WEXITSTATUS(status);
|
||||
warnx("%s exited with status %d", path, result);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
warnx("%s terminated by signal %d", path, WTERMSIG(status));
|
||||
result = 1;
|
||||
} else
|
||||
result = 1;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
print_result(result);
|
||||
rc += result;
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef __FINIT__
|
||||
static int usage(int rc)
|
||||
{
|
||||
warnx("usage: runparts [-dh?] DIRECTORY");
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc, c, progress = 0;
|
||||
char *dir;
|
||||
|
||||
while ((c = getopt(argc, argv, "dh?p")) != EOF) {
|
||||
switch(c) {
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
case 'p':
|
||||
progress = 1;
|
||||
break;
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
return usage(1);
|
||||
|
||||
dir = argv[optind];
|
||||
if (!fisdir(dir))
|
||||
return usage(1);
|
||||
|
||||
prctl(PR_SET_CHILD_SUBREAPER, 1);
|
||||
|
||||
rc = run_parts(dir, NULL, NULL, progress);
|
||||
if (rc == -1)
|
||||
err(1, "failed run-parts %s", dir);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* __FINIT__ */
|
||||
+6
-5
@@ -2714,23 +2714,24 @@ static void service_interval_cb(uev_t *w, void *arg, int events)
|
||||
}
|
||||
}
|
||||
|
||||
service_init();
|
||||
service_init(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* The service_interval may change (conf) between invocations, so we
|
||||
* periodically reset the one-shot timer instead of using a periodic.
|
||||
*/
|
||||
void service_init(void)
|
||||
void service_init(uev_ctx_t *ctx)
|
||||
{
|
||||
static int initialized = 0;
|
||||
static uev_t watcher;
|
||||
|
||||
if (!initialized) {
|
||||
if (!initialized)
|
||||
uev_timer_init(ctx, &watcher, service_interval_cb, NULL, service_interval, 0);
|
||||
initialized = 1;
|
||||
} else
|
||||
else
|
||||
uev_timer_set(&watcher, service_interval, 0);
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ void service_worker (void *unused);
|
||||
int service_completed (void);
|
||||
void service_notify_reconf (void);
|
||||
|
||||
void service_init (void);
|
||||
void service_init (uev_ctx_t *ctx);
|
||||
|
||||
#endif /* FINIT_SERVICE_H_ */
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "schedule.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "tty.h"
|
||||
@@ -43,12 +44,52 @@ sm_t sm;
|
||||
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */
|
||||
#endif
|
||||
|
||||
void sm_init(sm_t *sm)
|
||||
/*
|
||||
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||
* allowed to complete their work in [S], or timeout, before we switch
|
||||
* to the configured runlevel and call finalize(), should not take more
|
||||
* than 120 sec.
|
||||
*/
|
||||
static void sm_check_bootstrap(void *work)
|
||||
{
|
||||
sm->state = SM_BOOTSTRAP_STATE;
|
||||
sm->newlevel = -1;
|
||||
sm->reload = 0;
|
||||
sm->in_teardown = 0;
|
||||
static int cnt = 120 * 10; /* We run with 100ms period */
|
||||
int level = cfglevel;
|
||||
|
||||
dbg("Step all services ...");
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
if (cnt-- > 0 && !service_completed()) {
|
||||
dbg("Not all bootstrap run/tasks have completed yet ... %d", cnt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cnt > 0)
|
||||
dbg("All run/task have completed, resuming bootstrap.");
|
||||
else
|
||||
dbg("Timeout, resuming bootstrap.");
|
||||
|
||||
dbg("Flushing pending .conf file events ...");
|
||||
conf_flush_events();
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel, or jump
|
||||
* into the runlevel selected from the command line.
|
||||
*/
|
||||
if (cmdlevel) {
|
||||
dbg("Runlevel %d requested from command line, starting all services ...", cmdlevel);
|
||||
level = cmdlevel;
|
||||
} else
|
||||
dbg("Change to default runlevel(%d), starting all services ...", cfglevel);
|
||||
|
||||
service_runlevel(level);
|
||||
|
||||
/* Clean up bootstrap-only tasks/services that never started */
|
||||
dbg("Clean up all bootstrap-only tasks/services ...");
|
||||
svc_prune_bootstrap();
|
||||
|
||||
/* All services/tasks/etc. in configure runlevel have started */
|
||||
dbg("Running svc up hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
}
|
||||
|
||||
static char *sm_status(sm_state_t state)
|
||||
@@ -100,6 +141,22 @@ static void nologin(void)
|
||||
erase(FINIT_NOLOGIN_PATH);
|
||||
}
|
||||
|
||||
void sm_init(sm_t *sm)
|
||||
{
|
||||
static struct wq work = {
|
||||
.cb = sm_check_bootstrap,
|
||||
.delay = 1000
|
||||
};
|
||||
|
||||
sm->state = SM_BOOTSTRAP_STATE;
|
||||
sm->newlevel = -1;
|
||||
sm->reload = 0;
|
||||
sm->in_teardown = 0;
|
||||
|
||||
dbg("Starting bootstrap finalize timer ...");
|
||||
schedule_work(&work);
|
||||
}
|
||||
|
||||
void sm_set_runlevel(sm_t *sm, int newlevel)
|
||||
{
|
||||
sm->newlevel = newlevel;
|
||||
@@ -117,8 +174,8 @@ int sm_is_in_teardown(sm_t *sm)
|
||||
|
||||
void sm_step(sm_t *sm)
|
||||
{
|
||||
svc_t *svc;
|
||||
sm_state_t old_state;
|
||||
svc_t *svc;
|
||||
|
||||
restart:
|
||||
old_state = sm->state;
|
||||
@@ -130,10 +187,49 @@ restart:
|
||||
case SM_BOOTSTRAP_STATE:
|
||||
dbg("Bootstrapping all services in runlevel S from %s", finit_conf);
|
||||
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE);
|
||||
|
||||
sm->state = SM_BOOTSTRAP_WAIT_STATE;
|
||||
break;
|
||||
|
||||
/*
|
||||
* Handle bootstrap transition to configured runlevel, start TTYs
|
||||
*
|
||||
* This is the final stage of bootstrap. It changes to the default
|
||||
* (configured) runlevel, calls all external start scripts and final
|
||||
* bootstrap hooks before bringing up TTYs.
|
||||
*
|
||||
* We must ensure that all declared `task [S]` and `run [S]` jobs in
|
||||
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||
* finalize the bootstrap process by calling this function.
|
||||
*/
|
||||
case SM_BOOTSTRAP_WAIT_STATE:
|
||||
/* We come here from bootstrap, runlevel change and conf reload */
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Allow runparts to start */
|
||||
cond_set("int/bootstrap");
|
||||
|
||||
if (sm->newlevel == -1)
|
||||
break;
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
dbg("Calling all system up hooks ...");
|
||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Disable progress output at normal runtime */
|
||||
enable_progress(0);
|
||||
|
||||
/* System bootrapped, launch TTYs et al */
|
||||
bootstrap = 0;
|
||||
service_step_all(SVC_TYPE_RESPAWN);
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
|
||||
case SM_RUNNING_STATE:
|
||||
/* We come here from bootstrap, runlevel change and conf reload */
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* runlevel changed? */
|
||||
if (sm->newlevel >= 0 && sm->newlevel <= 9) {
|
||||
if (runlevel == sm->newlevel) {
|
||||
@@ -143,6 +239,7 @@ restart:
|
||||
sm->state = SM_RUNLEVEL_CHANGE_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* reload ? */
|
||||
if (sm->reload) {
|
||||
sm->reload = 0;
|
||||
@@ -210,17 +307,18 @@ restart:
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
|
||||
/* Compat SysV init */
|
||||
if (prevlevel == INIT_LEVEL && !rescue)
|
||||
run_bg(FINIT_RC_LOCAL, NULL);
|
||||
|
||||
/*
|
||||
* "I've seen things you people wouldn't believe. Attack ships on fire off
|
||||
* the shoulder of Orion. I watched C-beams glitter in the dark near the
|
||||
* Tannhäuser Gate. All those .. moments .. will be lost in time, like
|
||||
* tears ... in ... rain."
|
||||
*/
|
||||
if (runlevel == 0 || runlevel == 6) {
|
||||
if (runlevel == 0 || runlevel == 6)
|
||||
do_shutdown(halt);
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
@@ -266,9 +364,7 @@ restart:
|
||||
dbg("Update configuration generation of unmodified non-native services ...");
|
||||
service_notify_reconf();
|
||||
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
dbg("Reconfiguration done");
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
typedef enum {
|
||||
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
|
||||
SM_BOOTSTRAP_WAIT_STATE, /* Waiting for bootstrap to complete */
|
||||
SM_RUNNING_STATE, /* Normal state, services running */
|
||||
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
|
||||
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */
|
||||
|
||||
Reference in New Issue
Block a user