Merge pull request #138 from wkz/boot-refactor

Bootstrap refactor
This commit is contained in:
Joachim Wiberg authored and GitHub committed 2020-10-20 19:21:15 +02:00
commit 378456b6bd
8 files changed
+251 -204

No files matched your search

+1
View File
@@ -101,6 +101,7 @@ AC_ARG_ENABLE(contrib,
enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus])
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
AC_PLUGIN([inetd-echo], [no], [Inetd plugin: echo server, RFC862])
AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864])
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
+15
View File
@@ -0,0 +1,15 @@
# This is a basic /etc/fstab that mounts up all of the virtual filesystems
# that older versions of Finit used to mount internally. It can serve as a
# starting point for systems running Busybox's mount implementation. Make
# sure that CONFIG_FEATURE_MOUNT_HELPERS enabled; as this is required
# create the /dev/{pts,shm} directories.
devtmpfs /dev devtmpfs defaults 0 0
mkdir#-p /dev/pts helper none 0 0
devpts /dev/pts devpts mode=620,ptmxmode=0666 0 0
mkdir#-p /dev/shm helper none 0 0
tmpfs /dev/shm tmpfs mode=0777 0 0
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs mode=1777,nosuid,nodev 0 0
tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0
sysfs /sys sysfs defaults 0 0
+3
View File
@@ -42,6 +42,9 @@ For your convenience a set of *optional* plugins are available:
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
* *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
available.
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
+8
View File
@@ -17,6 +17,10 @@ if BUILD_DBUS_PLUGIN
libplug_la_SOURCES += dbus.c
endif
if BUILD_HOTPLUG_PLUGIN
libplug_la_SOURCES += hotplug.c
endif
if INETD
if BUILD_INETD_ECHO_PLUGIN
libplug_la_SOURCES += echo.c
@@ -62,6 +66,10 @@ if BUILD_DBUS_PLUGIN
pkglib_LTLIBRARIES += dbus.la
endif
if BUILD_HOTPLUG_PLUGIN
pkglib_LTLIBRARIES += hotplug.la
endif
if INETD
if BUILD_INETD_ECHO_PLUGIN
pkglib_LTLIBRARIES += echo.la
+110
View File
@@ -0,0 +1,110 @@
/* Heuristically find and initialize a suitable hotplug daemon
*
* Copyright (c) 2012-2020 Joachim Wiberg <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
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include "config.h"
#include "conf.h"
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "service.h"
static void setup(void *arg)
{
char cmd[256];
char *path;
/*
* Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system.
*/
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
/* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s "
"-- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
} else {
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
"udevadm trigger -c add -t devices "
"-- Requesting device events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
"udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
}
free(path);
/* Debian has this little script to copy generated
* rules while the system was read-only. TODO: When
* this functionality was hardcoded in finit.c, this
* call was made in crank_worker(). Now that
* filesystems are mounted earlier we should be able
* to make this call directly after the triggers have
* run, but this has not been tested AT ALL. */
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
} else {
path = which("mdev");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (log_is_debug())
touch("/dev/mdev.log");
snprintf(cmd, sizeof(cmd), "%s -s", path);
free(path);
run_interactive(cmd, "Populating device tree");
}
}
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+18
View File
@@ -59,6 +59,22 @@ static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(c
static int parse_conf(char *file);
static void drop_changes(void);
static void hide_args(int argc, char *argv[])
{
/*
* Hide command line arguments from ps (in particular for
* forked children that don't execv()). This is an ugly
* hack that only works on Linux.
* https://web.archive.org/web/20110227041321/http://netsplit.com/2007/01/10/hiding-arguments-from-ps/
*/
if (argc > 1) {
char *arg_end;
arg_end = argv[argc-1] + strlen (argv[argc-1]);
*arg_end = ' ';
}
}
static void parse_arg(char *arg, int *dbg)
{
/* Catches finit_debug (deprecated), --debug, and debug */
@@ -84,6 +100,8 @@ void conf_parse_cmdline(int argc, char *argv[])
for (int i = 1; i < argc; i++)
parse_arg(argv[i], &dbg);
hide_args(argc, argv);
fp = fopen("/proc/cmdline", "r");
if (!fp)
goto done;
+90 -203
View File
@@ -66,18 +66,13 @@ char *runparts = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
svc_t *wdog = NULL; /* No watchdog by default */
static int udev = 0; /* Runtime detection of udev */
/*
* Show user configured banner before service bootstrap progress
*/
static void banner(void)
{
if (plugin_exists(HOOK_BANNER)) {
plugin_run_hooks(HOOK_BANNER);
return;
}
plugin_run_hooks(HOOK_BANNER);
if (log_is_silent())
return;
@@ -91,7 +86,6 @@ static void banner(void)
static int fsck(int pass)
{
struct fstab *fs;
// int save;
int rc = 0;
if (!setfsent()) {
@@ -99,9 +93,6 @@ static int fsck(int pass)
return 1;
}
// if ((save = log_is_debug()))
// log_debug();
while ((fs = getfsent())) {
char cmd[80];
struct stat st;
@@ -126,13 +117,85 @@ static int fsck(int pass)
rc += run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
}
// if (save)
// log_debug();
endfsent();
return rc;
}
static int fsck_all(void)
{
int pass, rc = 0;
for (pass = 1; pass < 10; pass++) {
rc = fsck(pass);
if (rc)
break;
}
return rc;
}
#ifndef SYSROOT
static void fs_remount_root(int fsckerr)
{
struct fstab *fs;
if (!setfsent())
return;
while ((fs = getfsent())) {
if (!strcmp(fs->fs_file, "/"))
break;
}
/* If / is not listed in fstab, or listed as 'ro', leave it
* alone. */
if (!fs || !strcmp(fs->fs_type, "ro"))
goto out;
if (fsckerr)
print(1, "Cannot remount / as read-write, fsck failed before");
else
run_interactive("mount -n -o remount,rw /",
"Remounting / as read-write");
out:
endfsent();
}
#else
static void fs_remount_root(int fsckerr)
{
/*
* XXX: Untested, in the initramfs age we should
* probably use switch_root instead.
*/
rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
}
#endif /* SYSROOT */
static void fs_init(void)
{
int fsckerr;
if (!rescue) {
fsckerr = fsck_all();
fs_remount_root(fsckerr);
}
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
if (run_interactive("mount -na", "Mounting filesystems"))
plugin_run_hooks(HOOK_MOUNT_ERROR);
_d("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
umask(0022);
}
/*
* If everything goes south we can use this to give the operator an
* emergency shell to debug the problem -- Finit should not crash!
@@ -239,10 +302,6 @@ static void crank_worker(void *unused)
*/
sm_init(&sm);
sm_step(&sm);
/* Debian has this little script to copy generated rules while the system was read-only */
if (udev && fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
}
/*
@@ -281,9 +340,6 @@ int main(int argc, char *argv[])
.delay = 1000
};
uev_ctx_t loop;
char cmd[256];
char *path;
int rc = 0;
/*
* finit/init/telinit client tool uses /dev/initctl pipe
@@ -298,19 +354,6 @@ int main(int argc, char *argv[])
*/
conf_parse_cmdline(argc, argv);
/*
* Hide command line arguments from ps (in particular for
* forked children that don't execv()). This is an ugly
* hack that only works on Linux.
* https://web.archive.org/web/20110227041321/http://netsplit.com/2007/01/10/hiding-arguments-from-ps/
*/
if (argc > 1) {
char *arg_end;
arg_end = argv[argc-1] + strlen (argv[argc-1]);
*arg_end = ' ';
}
/*
* Initalize event context.
*/
@@ -335,23 +378,8 @@ int main(int argc, char *argv[])
*/
emergency_shell();
/*
* Initial setup of signals, ignore all until we're up.
*/
sig_init();
/*
* Mount base file system
*/
if (!fismnt("/proc") && mount("none", "/proc", "proc", 0, NULL))
_pe("Failed mounting /proc");
if (fisdir("/proc/bus/usb"))
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
/*
* Load plugins early, finit.conf may contain references to
* features implemented by plugins.
*/
/* Load plugins early, the first hook is in banner(), so we
* need plugins loaded before calling it. */
plugin_init(&loop);
/*
@@ -360,178 +388,37 @@ int main(int argc, char *argv[])
banner();
/*
* Check file systems listed with pass > 0 in /etc/fstab
* Initial setup of signals, ignore all until we're up.
*/
rc = 0;
for (int pass = 1; pass < 10 && !rescue; pass++) {
rc = fsck(pass);
if (rc)
break;
}
/*
* Mount filesystems
*/
if (!rescue) {
#ifndef SYSROOT
/*
* Remount / read-write if it exists in fstab is not 'ro'.
* This is what the Debian sysv initscripts does.
*/
if (setfsent()) {
struct fstab *fs;
while ((fs = getfsent())) {
if (strcmp(fs->fs_file, "/"))
continue;
if (strcmp(fs->fs_type, "ro")) {
if (rc)
print(1, "Cannot remount / as read-write, fsck failed before");
else
rc = run_interactive("mount -n -o remount,rw /", "Remounting / as read-write");
}
break;
}
endfsent();
}
#else
/*
* XXX: Untested, in the initramfs age we should
* probably use switch_root instead.
*/
rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif
}
/* Create /sys if missing, some systems don't include it in their rootfs skeleton */
if (!rc && !fisdir("/sys"))
mkdir("/sys", 0755);
/* Only mount /sys if it's not already mounted */
if (!fismnt("/sys") && mount("none", "/sys", "sysfs", 0, NULL)) {
print(1, "Cannot mount /sys, your system may behave unusually");
_pe("Failed mounting /sys");
}
sig_init();
/*
* Initialize default control groups, if available
*/
cgroup_init();
/* Check and mount filesystems. */
fs_init();
/*
* Initialize .conf system and load static /etc/finit.conf
* Also initializes global_rlimit[] for udevd, below.
* Initialize .conf system and load static /etc/finit.conf.
*/
conf_init();
/*
* Some non-embedded systems without an initramfs may not have /dev mounted yet
* If they do, check if system has udevadm and perform cleanup from initramfs
*/
if (!fismnt("/dev"))
mount("udev", "/dev", "devtmpfs", MS_RELATIME, "size=10%,nr_inodes=61156,mode=755");
else if (whichp("udevadm"))
run_interactive("udevadm info --cleanup-db", "Cleaning up udev db");
/* Modern systems use /dev/pts */
makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620,ptmxmode=0666");
/*
* Some systems rely on us to both create /dev/shm and, to mount
* a tmpfs there. Any system with dbus needs shared memory, so
* mount it, unless its already mounted, but not if listed in
* the /etc/fstab file already.
*/
makedir("/dev/shm", 0755);
if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm", NULL))
mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
/*
* New tmpfs based /run for volatile runtime data
* For details, see http://lwn.net/Articles/436012/
*/
if (fisdir("/run") && !fismnt("/run"))
mount("tmpfs", "/run", "tmpfs", MS_NODEV | MS_NOSUID | MS_NOEXEC, "mode=0755,size=10%");
umask(022);
/* Bootstrap conditions, needed for hooks */
cond_init();
/*
* Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system.
*/
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
/* Desktop and server distros usually have a variant of udev */
udev = 1;
/* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
udev = 0;
} else {
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
"udevadm trigger -c add -t devices "
"-- Requesting device events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
"udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
}
free(path);
} else {
path = which("mdev");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (log_is_debug())
touch("/dev/mdev.log");
snprintf(cmd, sizeof(cmd), "%s -s", path);
free(path);
run_interactive(cmd, "Populating device tree");
}
}
/*
* Start bundled watchdogd as soon as possible, if enabled
*/
if (which(FINIT_LIBPATH_ "/watchdogd")) {
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
wdog = svc_find(FINIT_LIBPATH_ "/watchdogd", NULL);
}
if (!rescue) {
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
umask(0);
if (run_interactive("mount -na", "Mounting filesystems"))
plugin_run_hooks(HOOK_MOUNT_ERROR);
_d("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
umask(0022);
}
/* Emit conditions for early hooks that ran before the
* condition system was initialized in case anyone . */
cond_set_oneshot(plugin_hook_str(HOOK_BANNER));
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
/* Base FS up, enable standard SysV init signals */
sig_setup(&loop);
if (!rescue) {
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
}
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
/*
* Set up inotify watcher for /etc/finit.d and read all .conf
+6 -1
View File
@@ -201,7 +201,12 @@ void plugin_run_hook(hook_point_t no, void *arg)
}
}
cond_set_oneshot(hook_cond[no]);
/* Conditions are stored in /run, so don't try to signal
* conditions for any hooks before filesystems have been
* mounted. */
if (no >= HOOK_MOUNT_ERROR)
cond_set_oneshot(hook_cond[no]);
service_step_all(SVC_TYPE_RUNTASK);
}