mirror of
https://github.com/troglobit/finit.git
synced 2026-10-14 10:19:36 +00:00
Merge pull request #483 from aanderse/plymouth
Add Plymouth boot splash plugin
This commit is contained in:
4 files changed
+313
-3
No files matched your search
@@ -116,6 +116,7 @@ AC_PLUGIN([hotplug], [yes], [Start udevd or mdev kernel event datamon])
|
||||
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
|
||||
AC_PLUGIN([tty], [yes], [Automatically activate new TTYs, e.g. USB-to-serial])
|
||||
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
|
||||
AC_PLUGIN([plymouth], [no], [Plymouth boot splash integration])
|
||||
AC_PLUGIN([testserv], [no], [Test plugin to start test serv daemon])
|
||||
|
||||
# Check for extra arguments or packages
|
||||
|
||||
@@ -29,6 +29,10 @@ if BUILD_NETLINK_PLUGIN
|
||||
libplug_la_SOURCES += netlink.c
|
||||
endif
|
||||
|
||||
if BUILD_PLYMOUTH_PLUGIN
|
||||
libplug_la_SOURCES += plymouth.c
|
||||
endif
|
||||
|
||||
if BUILD_RESOLVCONF_PLUGIN
|
||||
libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
@@ -76,6 +80,10 @@ if BUILD_NETLINK_PLUGIN
|
||||
pkglib_LTLIBRARIES += netlink.la
|
||||
endif
|
||||
|
||||
if BUILD_PLYMOUTH_PLUGIN
|
||||
pkglib_LTLIBRARIES += plymouth.la
|
||||
endif
|
||||
|
||||
if BUILD_RESOLVCONF_PLUGIN
|
||||
pkglib_LTLIBRARIES += resolvconf.la
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/* Plymouth boot splash plugin for Finit
|
||||
*
|
||||
* Copyright (c) 2012-2026 Aaron Andersen <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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin integrates the Plymouth boot splash screen with Finit.
|
||||
* It manages the plymouthd lifecycle across the full boot process:
|
||||
*
|
||||
* - HOOK_BANNER: Start plymouthd early, before console output.
|
||||
* In initramfs, starts fresh. In stage 2, reuses
|
||||
* the daemon carried over from initramfs if alive.
|
||||
* - HOOK_ROOTFS_UP .. HOOK_SYSTEM_UP: Display status messages.
|
||||
* - HOOK_SVC_UP: Tear down plymouth once boot is complete.
|
||||
* - HOOK_SWITCH_ROOT: Notify plymouth of root filesystem change so
|
||||
* the daemon survives the initramfs -> rootfs
|
||||
* transition.
|
||||
* - HOOK_SHUTDOWN: Restart plymouthd in shutdown mode for a
|
||||
* splash during poweroff/reboot.
|
||||
*
|
||||
* The plugin is only activated when "splash" is present on the kernel
|
||||
* command line. Plymouth requires devpts for VT takeover; fs_init()
|
||||
* mounts it early enough for HOOK_BANNER.
|
||||
*
|
||||
* NOTE: The initramfs must include /etc/initrd-release. Plymouth
|
||||
* checks for this file and, when present, prefixes its argv[0] with
|
||||
* '@' so that the process is not killed during switch_root. Without
|
||||
* it, plymouthd will not survive the initramfs-to-rootfs transition.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "conf.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "sig.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifndef PLYMOUTH_PATH
|
||||
#define PLYMOUTH_PATH "/sbin/plymouth"
|
||||
#endif
|
||||
#ifndef PLYMOUTHD_PATH
|
||||
#define PLYMOUTHD_PATH "/sbin/plymouthd"
|
||||
#endif
|
||||
|
||||
#define PLYMOUTH_PIDFILE "/run/plymouthd.pid"
|
||||
|
||||
static pid_t daemon_pid;
|
||||
static int switching_root;
|
||||
static int in_initramfs;
|
||||
|
||||
static int plymouth_cmd(const char *action)
|
||||
{
|
||||
char cmd[256];
|
||||
|
||||
snprintf(cmd, sizeof(cmd), PLYMOUTH_PATH " %s", action);
|
||||
return run(cmd, NULL);
|
||||
}
|
||||
|
||||
static void plymouth_message(const char *msg)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
sig_unblock();
|
||||
execl(PLYMOUTH_PATH, PLYMOUTH_PATH,
|
||||
"display-message", "--text", msg, NULL);
|
||||
_exit(EX_OSERR);
|
||||
}
|
||||
}
|
||||
|
||||
static int plymouth_alive(void)
|
||||
{
|
||||
return daemon_pid > 0 && pid_alive(daemon_pid);
|
||||
}
|
||||
|
||||
/* Start plymouthd in the given mode ("boot" or "shutdown"). */
|
||||
static void plymouth_start(const char *mode)
|
||||
{
|
||||
char cmd[256];
|
||||
int rc;
|
||||
|
||||
if (plymouth_alive())
|
||||
return;
|
||||
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
PLYMOUTHD_PATH " --attach-to-session --mode %s --pid-file %s",
|
||||
mode, PLYMOUTH_PIDFILE);
|
||||
rc = run(cmd, NULL);
|
||||
if (rc) {
|
||||
warnx("plymouthd failed to start (exit %d)", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
daemon_pid = pid_file_read(PLYMOUTH_PIDFILE);
|
||||
if (daemon_pid <= 0) {
|
||||
warnx("plymouthd started but no PID in %s", PLYMOUTH_PIDFILE);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = plymouth_cmd("show-splash");
|
||||
if (rc)
|
||||
warnx("plymouth show-splash failed (exit %d)", rc);
|
||||
}
|
||||
|
||||
static void plymouth_stop(void)
|
||||
{
|
||||
if (!plymouth_alive())
|
||||
return;
|
||||
|
||||
plymouth_cmd("quit");
|
||||
|
||||
/*
|
||||
* Don't poll -- we're in a finit hook, so finit's event loop
|
||||
* is blocked and can't reap children. Trust that plymouthd
|
||||
* exits after receiving the quit command.
|
||||
*/
|
||||
daemon_pid = 0;
|
||||
unlink(PLYMOUTH_PIDFILE);
|
||||
}
|
||||
|
||||
/*
|
||||
* HOOK_BANNER - earliest possible hook, before any console output.
|
||||
*
|
||||
* In initramfs: start plymouthd fresh.
|
||||
* In stage 2: reuse plymouthd from initramfs if still alive,
|
||||
* otherwise start a new instance.
|
||||
*/
|
||||
static void plymouth_boot(void *arg)
|
||||
{
|
||||
in_initramfs = fexist("/etc/initrd-release");
|
||||
|
||||
if (rescue)
|
||||
return;
|
||||
|
||||
enable_progress(0);
|
||||
|
||||
if (!in_initramfs) {
|
||||
if (plymouth_cmd("--ping") == 0) {
|
||||
daemon_pid = pid_file_read(PLYMOUTH_PIDFILE);
|
||||
if (daemon_pid <= 0)
|
||||
daemon_pid = 1; /* alive but unknown pid */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
plymouth_start("boot");
|
||||
}
|
||||
|
||||
/*
|
||||
* HOOK_SVC_UP - all services launched.
|
||||
*
|
||||
* In initramfs: keep splash alive for switch_root.
|
||||
* In stage 2: boot is done, tear down plymouth.
|
||||
*/
|
||||
static void plymouth_boot_done(void *arg)
|
||||
{
|
||||
if (in_initramfs)
|
||||
return;
|
||||
|
||||
plymouth_stop();
|
||||
enable_progress(1);
|
||||
}
|
||||
|
||||
/* HOOK_SWITCH_ROOT - initramfs transitioning to real root. */
|
||||
static void plymouth_switchroot(void *arg)
|
||||
{
|
||||
switching_root = 1;
|
||||
|
||||
plymouth_message("Switching to root filesystem...");
|
||||
|
||||
if (plymouth_alive())
|
||||
run(PLYMOUTH_PATH " update-root-fs --new-root-dir=/sysroot", NULL);
|
||||
|
||||
enable_progress(1);
|
||||
}
|
||||
|
||||
/* HOOK_SHUTDOWN - entering runlevel 0 or 6. */
|
||||
static void plymouth_shutdown(void *arg)
|
||||
{
|
||||
if (rescue || switching_root)
|
||||
return;
|
||||
|
||||
enable_progress(0);
|
||||
plymouth_start("shutdown");
|
||||
}
|
||||
|
||||
static void on_rootfs_up(void *arg)
|
||||
{
|
||||
plymouth_message("Root filesystem mounted");
|
||||
}
|
||||
|
||||
static void on_mount_post(void *arg)
|
||||
{
|
||||
plymouth_message("Mounting filesystems...");
|
||||
}
|
||||
|
||||
static void on_basefs_up(void *arg)
|
||||
{
|
||||
plymouth_message("All filesystems mounted");
|
||||
}
|
||||
|
||||
static void on_network_up(void *arg)
|
||||
{
|
||||
plymouth_message("Network is up");
|
||||
}
|
||||
|
||||
static void on_system_up(void *arg)
|
||||
{
|
||||
plymouth_message("System ready");
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = "plymouth",
|
||||
.hook[HOOK_BANNER] = { .cb = plymouth_boot },
|
||||
.hook[HOOK_ROOTFS_UP] = { .cb = on_rootfs_up },
|
||||
.hook[HOOK_MOUNT_POST] = { .cb = on_mount_post },
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = on_basefs_up },
|
||||
.hook[HOOK_NETWORK_UP] = { .cb = on_network_up },
|
||||
.hook[HOOK_SYSTEM_UP] = { .cb = on_system_up },
|
||||
.hook[HOOK_SVC_UP] = { .cb = plymouth_boot_done },
|
||||
.hook[HOOK_SWITCH_ROOT] = { .cb = plymouth_switchroot },
|
||||
.hook[HOOK_SHUTDOWN] = { .cb = plymouth_shutdown },
|
||||
};
|
||||
|
||||
/*
|
||||
* Check kernel command line for "splash" argument. Plymouth should
|
||||
* only be activated when the user explicitly requests it.
|
||||
*/
|
||||
static int has_splash_arg(void)
|
||||
{
|
||||
char line[LINE_SIZE], *tok, *saveptr;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/cmdline", "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
for (tok = strtok_r(line, " \t\n", &saveptr); tok;
|
||||
tok = strtok_r(NULL, " \t\n", &saveptr)) {
|
||||
if (!strcmp(tok, "splash"))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PLUGIN_INIT(__init)
|
||||
{
|
||||
if (!has_splash_arg())
|
||||
return;
|
||||
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(__exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+11
-3
@@ -370,8 +370,8 @@ static void fs_finalize(void)
|
||||
fs_mount("shm", "/dev/shm", "tmpfs", flags | MS_NODEV, "mode=1777");
|
||||
}
|
||||
|
||||
/* Modern systems use /dev/pts */
|
||||
if (!fismnt("/dev/pts")) {
|
||||
/* Remount devpts with proper gid/mode now that /etc/group is available */
|
||||
{
|
||||
char opts[32];
|
||||
int gid;
|
||||
|
||||
@@ -383,7 +383,8 @@ static void fs_finalize(void)
|
||||
snprintf(opts, sizeof(opts), "gid=%d,mode=0620,ptmxmode=0666", gid);
|
||||
|
||||
makedir("/dev/pts", 0755);
|
||||
fs_mount("devpts", "/dev/pts", "devpts", flags, opts);
|
||||
fs_mount("devpts", "/dev/pts", "devpts",
|
||||
flags | MS_REMOUNT, opts);
|
||||
}
|
||||
|
||||
/* Needed on systems like Alpine Linux */
|
||||
@@ -529,6 +530,13 @@ static void fs_init(void)
|
||||
|
||||
fs_mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
}
|
||||
|
||||
/* Early devpts mount for plugins that need PTY access (e.g. plymouth) */
|
||||
if (!fismnt("/dev/pts")) {
|
||||
makedir("/dev/pts", 0755);
|
||||
fs_mount("devpts", "/dev/pts", "devpts",
|
||||
MS_NOSUID | MS_NOEXEC, "ptmxmode=0666");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user