mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e74dff96ba | ||
|
|
9ed4407b20 | ||
|
|
eb913e8bf2 | ||
|
|
17d04779fb | ||
|
|
ab661423ba | ||
|
|
8e77ea68b2 | ||
|
|
b0e672bc9a | ||
|
|
ad76e15949 | ||
|
|
b8735cc893 | ||
|
|
c058b3f9ce | ||
|
|
8f6c61606a | ||
|
|
a81530754b | ||
|
|
1518eb9466 |
+2
-1
@@ -1,4 +1,4 @@
|
||||
AC_INIT([Finit], [4.16], [https://github.com/finit-project/finit/issues],
|
||||
AC_INIT([Finit], [4.17], [https://github.com/finit-project/finit/issues],
|
||||
[finit], [https://http://finit-project.github.io/])
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
|
||||
@@ -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
|
||||
|
||||
+37
-1
@@ -3,6 +3,41 @@ Change Log
|
||||
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
[4.17][] - 2026-04-28
|
||||
---------------------
|
||||
|
||||
### Changes
|
||||
|
||||
- Add Plymouth boot splash plugin to manage the `plymouthd` lifecycle across boot,
|
||||
`switch_root`, and shutdown. Activated by the `splash` kernel command line
|
||||
argument. By Aaron Andersen
|
||||
- Add `tty:` option for run/task/service stanzas to acquire a controlling
|
||||
terminal. The device is opened as stdin, set as the session's controlling TTY
|
||||
via `TIOCSCTTY`, and `TERM` is set automatically — `linux` for virtual terminals
|
||||
and `vt102` for serial lines. By Ollie Gutierrez
|
||||
- Improve console output atomicity at boot and shutdown: description and status
|
||||
(`[ OK ]` / `[FAIL]`) are emitted in a single write so kernel messages cannot
|
||||
split them. If output has been scrolled away, the description is re-printed
|
||||
before the final status line. Console buffer is drained with `tcdrain(2)` and
|
||||
ANSI attributes are reset before handing control back to the kernel at
|
||||
shutdown/reboot
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix reverse-dependency condition handling on reload: when a service without
|
||||
SIGHUP support is reloaded, its condition is now cleared immediately so
|
||||
dependents transition to `STOPPING` before `SIGTERM` is sent to the parent,
|
||||
preventing them from crashing with an incremented restart counter
|
||||
- Fix `/var/tmp` mode in `tmpfiles.d/var.conf` from `0777` to `1777` (sticky bit)
|
||||
to match `/tmp`, by Aaron Andersen
|
||||
- Fix potential memory corruption in config parser: stale pointer captures of
|
||||
`PATH` and `SHELL` at startup could dangle after environment modification;
|
||||
`conf_reset_env()` now reads from the live environment instead, by Vasily Zubko
|
||||
- Fix plugin name registration: replace `__FILE__` macro with explicit string
|
||||
literals in all plugin structs and debug messages, preventing subtle build-
|
||||
system–dependent macro expansion issues, by Vasily Zubko
|
||||
|
||||
|
||||
[4.16][] - 2026-02-27
|
||||
---------------------
|
||||
|
||||
@@ -1968,7 +2003,8 @@ Major bug fix release.
|
||||
|
||||
* Initial release
|
||||
|
||||
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.15...HEAD
|
||||
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.17...HEAD
|
||||
[4.17]: https://github.com/finit-project/finit/compare/4.16...4.17
|
||||
[4.16]: https://github.com/finit-project/finit/compare/4.15...4.16
|
||||
[4.15]: https://github.com/finit-project/finit/compare/4.14...4.15
|
||||
[4.14]: https://github.com/finit-project/finit/compare/4.13...4.14
|
||||
|
||||
@@ -46,6 +46,7 @@ Other run/task/service options are:
|
||||
* `cgroup.NAME[,opts]` or `cgroup:opts` -- see the [Cgroups](cgroups.md) section
|
||||
* `env:[-]/path/to/env` -- see the [Service Environment](service-env.md) section
|
||||
* `log:...` -- see [Redirecting Output](logging.md#redirecting-output)
|
||||
* `tty:<dev>` -- see [Controlling TTY](tty.md#controlling-tty)
|
||||
* `nowarn` -- see [Conditional Loading](services.md#conditional-loading)
|
||||
* `notify:...` -- see [Service Synchronization](service-sync.md)
|
||||
* `if:...` -- see [Conditional Execution](services.md#conditional-execution)
|
||||
|
||||
+28
-1
@@ -83,4 +83,31 @@ board bringup and system debugging it can come in handy.
|
||||
|
||||
One can also use the `service` stanza to start a stand-alone shell:
|
||||
|
||||
service [12345] /bin/sh -l
|
||||
service [12345] /bin/sh -l
|
||||
|
||||
Controlling TTY for Services
|
||||
----------------------------
|
||||
|
||||
The `tty:<dev>` option gives a `run`, `task`, or `service` a controlling
|
||||
terminal on the given device. The device is opened, set as the
|
||||
controlling terminal for the session (after `setsid()`), and connected to
|
||||
the process's stdin, stdout, and stderr. A default `TERM` environment
|
||||
variable is set based on the device type: `vt102` for serial lines and
|
||||
`linux` for virtual terminals.
|
||||
|
||||
`<dev>` may be a device node like `/dev/ttyS0`, or the special keyword
|
||||
`@console` (see above). Note that `@console` expands only to the
|
||||
first console, not all.
|
||||
|
||||
When `tty:` is combined with `log:`, stdout and stderr are redirected
|
||||
to the log sink instead of the TTY, but stdin remains connected to the
|
||||
TTY device.
|
||||
|
||||
> The `tty:<dev>` option is for `run`, `task`, and `service` stanzas only.
|
||||
> The `tty` directive itself (for getty/login) has its own syntax, see
|
||||
> above.
|
||||
|
||||
**Example:**
|
||||
|
||||
service [2345] tty:/dev/ttyS0 /usr/sbin/foo -- Foo on serial console
|
||||
task [S] tty:@console my-setup-script -- Board bringup on console
|
||||
+2
-1
@@ -61,7 +61,8 @@ For more complex setups (LUKS, LVM, etc.):
|
||||
|
||||
```
|
||||
# Unlock LUKS volume
|
||||
run [S] name:cryptsetup /sbin/cryptsetup open /dev/sda2 cryptroot -- Unlocking encrypted root
|
||||
# The tty:@console stanza is required so cryptsetup can prompt for a passphrase
|
||||
run [S] name:cryptsetup tty:@console /sbin/cryptsetup open /dev/sda2 cryptroot -- Unlocking encrypted root
|
||||
|
||||
# Activate LVM
|
||||
run [S] name:lvm /sbin/lvm vgchange -ay -- Activating LVM volumes
|
||||
|
||||
@@ -744,6 +744,37 @@ is
|
||||
and the default
|
||||
.Cm tag
|
||||
identity is the basename of the service or run/task command.
|
||||
.It Cm tty:\&<dev>
|
||||
Give the
|
||||
.Cm run , task ,
|
||||
or
|
||||
.Cm service
|
||||
a controlling terminal on
|
||||
.Cm <dev> .
|
||||
The device is opened, set as the controlling terminal for the session
|
||||
(after
|
||||
.Fn setsid ) ,
|
||||
and connected to stdin, stdout, and stderr.
|
||||
A default
|
||||
.Ev TERM
|
||||
variable is set based on the device type:
|
||||
.Cm vt102
|
||||
for serial lines and
|
||||
.Cm linux
|
||||
for virtual terminals.
|
||||
.Cm <dev>
|
||||
may be a device node like
|
||||
.Pa /dev/ttyS0 ,
|
||||
or the special keyword
|
||||
.Cm @console
|
||||
(see TTYs and Consoles above).
|
||||
When combined with
|
||||
.Cm log: ,
|
||||
stdout and stderr are redirected to the log sink instead, but stdin
|
||||
remains connected to the TTY device.
|
||||
.Bd -unfilled -offset indent
|
||||
service [2345] tty:/dev/ttyS0 /usr/sbin/foo -- Foo on serial console
|
||||
.Ed
|
||||
.El
|
||||
.Sh RESCUE MODE
|
||||
Finit supports a rescue mode which is activated by the
|
||||
|
||||
@@ -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 <aaron@fosslib.net>
|
||||
*
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+9
-4
@@ -379,7 +379,6 @@ void print(int rc, const char *fmt, ...)
|
||||
/* Pending state: show description with spinner, no final status yet */
|
||||
if (!buf[0])
|
||||
return;
|
||||
delline();
|
||||
if (progress_style == PROGRESS_CLASSIC)
|
||||
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
|
||||
else
|
||||
@@ -391,15 +390,21 @@ void print(int rc, const char *fmt, ...)
|
||||
* Final status. Emit description + status in a single cprintf() so
|
||||
* both reach the console in one write(), preventing kernel messages or
|
||||
* command output from splitting the description from its [ OK ]/[FAIL].
|
||||
*
|
||||
* status() returns a pointer to a static buffer, so we must copy the
|
||||
* result for rc before calling status(3) — the second call overwrites
|
||||
* the same buffer, corrupting the first result.
|
||||
*/
|
||||
last_desc[0] = 0;
|
||||
|
||||
if (buf[0]) {
|
||||
delline();
|
||||
char st[64];
|
||||
|
||||
strlcpy(st, status(rc), sizeof(st));
|
||||
if (progress_style == PROGRESS_CLASSIC)
|
||||
cprintf("\r%s %s\n", pad(buf, sizeof(buf), ".", sizeof(buf)), status(rc));
|
||||
cprintf("\r%s %s\n", pad(buf, sizeof(buf), ".", sizeof(buf)), st);
|
||||
else
|
||||
cprintf("\r\e[K%s%s\r%s\n", status(3), buf, status(rc));
|
||||
cprintf("\r\e[K%s%s\r%s\n", status(3), buf, st);
|
||||
} else {
|
||||
if (progress_style == PROGRESS_CLASSIC)
|
||||
cprintf("%s\n", status(rc));
|
||||
|
||||
+22
-10
@@ -73,7 +73,8 @@ static int delete_cb(const char *fpath, const struct stat *sb, int tflag, struct
|
||||
if (sb->st_dev != delete_rootdev)
|
||||
return 0;
|
||||
|
||||
remove(fpath);
|
||||
if (remove(fpath))
|
||||
dbg("Failed to remove %s: %s", fpath, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -146,6 +147,7 @@ int switch_root(const char *newroot, const char *newinit)
|
||||
struct stat newroot_st, oldroot_st;
|
||||
char init_path[PATH_MAX];
|
||||
int console_fd;
|
||||
int fd;
|
||||
dev_t rootdev;
|
||||
int signo;
|
||||
|
||||
@@ -166,17 +168,31 @@ int switch_root(const char *newroot, const char *newinit)
|
||||
}
|
||||
|
||||
/* Verify newroot exists and is a directory */
|
||||
if (stat(newroot, &newroot_st) || !S_ISDIR(newroot_st.st_mode)) {
|
||||
fd = open(newroot, O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0) {
|
||||
logit(LOG_ERR, "switch_root: %s is not a directory", newroot);
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
if (fstat(fd, &newroot_st)) {
|
||||
close(fd);
|
||||
logit(LOG_ERR, "switch_root: cannot stat %s", newroot);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
/* Verify newroot is a mount point (different device than parent) */
|
||||
if (stat("/", &oldroot_st)) {
|
||||
fd = open("/", O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0) {
|
||||
logit(LOG_ERR, "switch_root: cannot open /");
|
||||
return -1;
|
||||
}
|
||||
if (fstat(fd, &oldroot_st)) {
|
||||
close(fd);
|
||||
logit(LOG_ERR, "switch_root: cannot stat /");
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (newroot_st.st_dev == oldroot_st.st_dev) {
|
||||
logit(LOG_ERR, "switch_root: %s is not a mount point", newroot);
|
||||
@@ -259,18 +275,14 @@ int switch_root(const char *newroot, const char *newinit)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reopen console */
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
/* Reopen console in new root. dup2() closes the old fds itself,
|
||||
* so open() returns a fd > STDERR_FILENO that we can always close. */
|
||||
console_fd = open("/dev/console", O_RDWR);
|
||||
if (console_fd >= 0) {
|
||||
dup2(console_fd, STDIN_FILENO);
|
||||
dup2(console_fd, STDOUT_FILENO);
|
||||
dup2(console_fd, STDERR_FILENO);
|
||||
if (console_fd > STDERR_FILENO)
|
||||
close(console_fd);
|
||||
close(console_fd);
|
||||
}
|
||||
|
||||
/* Reset signals to default */
|
||||
|
||||
@@ -29,10 +29,12 @@
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#include <string.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <linux/vt.h>
|
||||
#include <net/if.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
@@ -387,6 +389,43 @@ static int lredirect(svc_t *svc)
|
||||
return close(pipefd[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a controlling TTY for run/task/service.
|
||||
* Opens the TTY device, dups to 0/1/2, resets termios to
|
||||
* sane defaults, sets TERM env, and updates procname.
|
||||
*/
|
||||
static void svc_prepare_ctty(const char *tty, const char *procname, int log)
|
||||
{
|
||||
int fd, dummy;
|
||||
|
||||
fd = open(tty, O_RDWR);
|
||||
if (fd < 0) {
|
||||
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* Acquire as controlling terminal for the session */
|
||||
ioctl(fd, TIOCSCTTY, 0);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
if (!log) {
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
/* Set default TERM */
|
||||
if (ioctl(fd, VT_OPENQRY, &dummy) == -1)
|
||||
setenv("TERM", "vt102", 1); /* likely a serial line */
|
||||
else
|
||||
setenv("TERM", "linux", 1);
|
||||
|
||||
/* Reset to sane defaults */
|
||||
stty(fd, B0);
|
||||
close(fd);
|
||||
|
||||
prctl(PR_SET_NAME, procname, 0, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle redirection of process output, if enabled
|
||||
*/
|
||||
@@ -946,6 +985,14 @@ static int service_start(svc_t *svc)
|
||||
|
||||
sig_unblock();
|
||||
|
||||
/*
|
||||
* If the stanza declared tty:<dev>, set up a controlling
|
||||
* terminal on that device now that we're a session leader.
|
||||
* Must run after setsid() to acquire the controlling TTY.
|
||||
*/
|
||||
if (!svc_is_tty(svc) && svc_has_ctty(svc))
|
||||
svc_prepare_ctty(svc->log.ctty, svc->cmd, !!svc->log.enabled);
|
||||
|
||||
if (svc_is_runtask(svc))
|
||||
status = exec_runtask(args[0], &args[1]);
|
||||
else if (svc_is_tty(svc))
|
||||
@@ -1795,6 +1842,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
char ident[MAX_IDENT_LEN];
|
||||
char *ifstmt = NULL;
|
||||
char *notify = NULL;
|
||||
char *ctty = NULL;
|
||||
struct tty tty = { 0 };
|
||||
char *dev = NULL;
|
||||
int respawn = 0;
|
||||
@@ -1911,6 +1959,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
env = arg;
|
||||
else if (MATCH_CMD(cmd, "caps:", arg))
|
||||
caps = arg;
|
||||
else if (MATCH_CMD(cmd, "tty:", arg))
|
||||
ctty = arg;
|
||||
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
|
||||
else if (MATCH_CMD(cmd, "cgroup", arg))
|
||||
cgroup = arg;
|
||||
@@ -2160,6 +2210,22 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
parse_caps(svc, caps);
|
||||
else
|
||||
memset(svc->capabilities, 0, sizeof(svc->capabilities));
|
||||
|
||||
if (!svc_is_tty(svc) && ctty) {
|
||||
char *dev = ctty;
|
||||
|
||||
/* NOTE: @console expands only to first console, not all */
|
||||
if (tty_isatcon(ctty))
|
||||
dev = tty_atcon();
|
||||
dev = tty_canonicalize(dev);
|
||||
if (dev)
|
||||
strlcpy(svc->log.ctty, dev, sizeof(svc->log.ctty));
|
||||
else
|
||||
logit(LOG_WARNING, "%s: tty: %s not found, skipping ctty", svc_ident(svc, NULL, 0), ctty);
|
||||
} else if (!svc_is_tty(svc)) {
|
||||
memset(svc->log.ctty, 0, sizeof(svc->log.ctty));
|
||||
}
|
||||
|
||||
if (file)
|
||||
strlcpy(svc->file, file, sizeof(svc->file));
|
||||
else
|
||||
|
||||
@@ -176,6 +176,7 @@ typedef struct svc {
|
||||
char file[64];
|
||||
char prio[20];
|
||||
char ident[20];
|
||||
char ctty[32];
|
||||
} log;
|
||||
|
||||
/* Only for TTY type services */
|
||||
@@ -291,6 +292,7 @@ static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && IS
|
||||
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
||||
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
|
||||
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
|
||||
static inline int svc_has_ctty (svc_t *svc) { return svc && svc->log.ctty[0]; }
|
||||
static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];}
|
||||
static inline int svc_has_cleanup (svc_t *svc) { return svc->cleanup_script[0]; }
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ d /var/lib/alarm 0755 - - -
|
||||
d /var/lib/misc 0755 - - -
|
||||
d /var/lib/urandom 0755 - - -
|
||||
d /var/spool 0755 - - -
|
||||
d /var/tmp 0777 - - -
|
||||
d /var/tmp 1777 - - -
|
||||
|
||||
# Used by pam_console(8)
|
||||
d /run/console 1777 - - -
|
||||
|
||||
Reference in New Issue
Block a user