Compare commits

..
Author SHA1 Message Date
Jacques de Laval 50c99762f1 Add run target for running Finit as a stand alone unprivileged process
It could be very useful, during development, to be able to run Finit on a
host system without having to provision a full VM. We have all the building
blocks for easily containerizing Finit - in the test framework - so adding
a "run" target that does the orchestration makes sense.

Signed-off-by: Jacques de Laval <jacques@de-laval.se>
2021-03-05 01:39:42 +01:00
Jacques de Laval 067043b298 test: Use STDOUT of parent process instead of a pipe as tty for finit
Signed-off-by: Jacques de Laval <jacques@de-laval.se>
2021-03-05 01:24:31 +01:00
166 changed files with 3049 additions and 10351 deletions
-70
View File
@@ -1,70 +0,0 @@
name: Bob the Builder
# Run on all branches, including all pull requests, except the 'dev'
# branch since that's where we run Coverity Scan (limited tokens/day)
on:
push:
branches:
- '**'
- '!dev'
pull_request:
branches:
- '**'
jobs:
build:
# Verify we can build on latest Ubuntu with both gcc and clang
name: ${{ matrix.compiler }}
runs-on: ubuntu-18.04
strategy:
matrix:
compiler: [gcc, clang]
fail-fast: false
env:
MAKEFLAGS: -j3
CC: ${{ matrix.compiler }}
steps:
- name: Install dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
tar xf libuev-2.3.2.tar.xz
tar xf libite-2.3.1.tar.gz
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v2
- name: Static Finit
run: |
./autogen.sh
./configure --prefix= --enable-static
make V=1
- name: Regular Finit
run: |
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
make clean
make V=1
- name: Install to ~/tmp and Inspect
run: |
DESTDIR=/tmp make install-strip
tree /tmp
ldd /tmp/sbin/finit
size /tmp/sbin/finit
ldd /tmp/sbin/initctl
size /tmp/sbin/initctl
ldd /tmp/sbin/reboot
size /tmp/sbin/reboot
/tmp/sbin/finit -h
/tmp/sbin/initctl -h
- name: Run Unit Tests
run: |
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
dir=finit-$ver/_build/sub
make -j1 distcheck || (cat $dir/test/setup-root.log; cat $dir/test/test-suite.log; false)
- name: Upload Test Results
uses: actions/upload-artifact@v2
with:
name: finit-test-${{ matrix.compiler }}
path: test/*
-63
View File
@@ -1,63 +0,0 @@
name: Coverity Scan
on:
push:
branches:
- 'dev'
env:
PROJECT_NAME: finit
CONTACT_EMAIL: troglobit@gmail.com
COVERITY_NAME: troglobit-finit
COVERITY_PROJ: troglobit%2Ffinit
jobs:
coverity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
id: coverity-toolchain-cache
with:
path: cov-analysis-linux64
key: ${{ runner.os }}-coverity
- name: Download Coverity Scan
if: steps.coverity-toolchain-cache.outputs.cache-hit != 'true'
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
run: |
wget -q https://scan.coverity.com/download/cxx/linux64 \
--post-data "token=$TOKEN&project=${COVERITY_PROJ}" \
-O cov-analysis-linux64.tar.gz
mkdir cov-analysis-linux64
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
- name: Install dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
tar xf libuev-2.3.2.tar.xz
tar xf libite-2.3.1.tar.gz
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig
- name: Build
run: |
./autogen.sh
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
cov-build --dir cov-int make
- name: Submit result to Coverity Scan
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
run: |
tar czvf ${PROJECT_NAME}.tgz cov-int
curl \
--form project=${COVERITY_NAME} \
--form token=$TOKEN \
--form email=${CONTACT_EMAIL} \
--form file=@${PROJECT_NAME}.tgz \
--form version=trunk \
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
-69
View File
@@ -1,69 +0,0 @@
name: Release General
on:
push:
tags:
- '[0-9]+.[0-9]+'
jobs:
release:
name: Create GitHub release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
steps:
- uses: actions/checkout@v2
- name: Extract ChangeLog entry ...
# Hack to extract latest entry for body_path below
run: |
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|head -n -1 > release.md
cat release.md
- name: Create release ...
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Finit v${{ github.ref }}
body_path: release.md
draft: false
prerelease: false
tarball:
name: Build and upload release tarball
needs: release
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Installing dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
tar xf libuev-2.3.2.tar.xz
tar xf libite-2.3.1.tar.gz
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig
- name: Creating Makefiles
run: |
./autogen.sh
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
- name: Building release
run: |
make release
ls -lF ../
mkdir -p artifacts/
mv ../*.tar.* artifacts/
- name: Uploading release artifacts
uses: skx/github-action-publish-binaries@release-0.15
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
releaseId: ${{ needs.release.outputs.release_id }}
args: artifacts/*
-3
View File
@@ -45,6 +45,3 @@ GTAGS
/.autoconf /.autoconf
/.automake /.automake
/.built /.built
# VS Code user settings
.vscode
+65
View File
@@ -0,0 +1,65 @@
# Travis CI integration -- https://travis-ci.org/troglobit/finit
# Defaults to GNU GCC and autotools: ./configure && make && make test
language: c
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
sudo: false
# Use non-docker builds atm. to install libuEv and libite from source
#sudo: true
# Test build with both GCC and Clang (LLVM)
compiler:
- gcc
- clang
# The secure: declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
env:
global:
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig
- CFLAGS="-W -Wall -Wextra"
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
addons:
apt:
packages:
- tree
coverity_scan:
project:
name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
build_command: "make"
branch_pattern: dev
install:
- wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
- wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
- tar xf libuev-2.3.2.tar.xz
- tar xf libite-2.3.1.tar.gz
- (cd libuev-2.3.2 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libite-2.3.1 && ./configure --prefix=/tmp && make && make install-strip)
# Custom build script for the time being, no "make test" yet
script:
- ./autogen.sh
- ./configure --prefix=/tmp --enable-static
- make clean
- make V=1 -j5
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog --enable-logit
- cat config.log
- make clean
- make V=1 -j5
- make install-strip
- tree /tmp
- ldd /tmp/sbin/finit
- size /tmp/sbin/finit
- size /tmp/sbin/initctl
- size /tmp/sbin/reboot
- LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
- unshare -V
- nsenter -V
- chroot --version
- 'LD_LIBRARY_PATH=/tmp/lib make distcheck || (cat finit-4.0-beta1/_build/sub/test/test-suite.log; exit 1)'
+26 -242
View File
@@ -4,196 +4,8 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.3][UNRELEASED] [4.0][UNRELEASED] - 2021-02-21
-------------------- ------------------------------
Critical bug fix release. If you run a 32-bit target with GLIBC 2.34
you *need* to upgrade!
### Changes
* Loading `module`s no longer shows arguments in progress output
* Warning messages in progress output now in yellow, not red, issue 214
* `initctl`, new command line option `-v,--version` for ease of use
* Extended environment variables for pre/post scripts, issue #189
* New condition `done` for run task, issue #207 by Ming Liu, Atlas Copco
* Refactor parts of shutdown and reboot sequence for PREEMPT-RT kernels,
by Robert Andersson, Mathias Thore, and Ming Liu, Atlas Copco
### Fixes
* Fix nasty 32/64-bit alignment issue between finit and its plugins,
applicable to 32-bit targets with GLIBC 2.34 and later. External
plugins must make sure to use, at least: `-D_TIME_BITS=64`
* Fix #215: disable cgroup support at runtime if kernel lacks support
or does not have the required controllers (cpu)
* Fix #217: iwatcher initialization issue, by Ming Liu, Atlas Copco
* Fix #218: initctl matches too many services, by Ming Liu, Atlas Copco
[4.2][] - 2022-01-16
--------------------
### Changes
* Support for non-root users to use `initctl`, e.g. group wheel
* Support for new libite (-lite) header namespace
* RTC plugin now reset an invalid RTC time to the kernel default
time, 2000-01-01 00:00, prevents errors and is less crazy than
some systems coming with with <= Jan 1, 1970
* urandom plugin now use RNDADDENTROPY ioctl to seed kernel rng,
incrementing entropy count. Also, 32 kiB instead of 512 bytes are
now saved (and restored) on reboot. This should greatly improve
reliability of systems with none or poor HWRNG
* Kernel logging to console (loglevel >= 7, debug, when quiet mode is
not used) is now honored by Finit, regardless of the finit.debug
command line option
* Reduced default log level from `LOG_NOTICE` to `LOG_INFO`
* Wrapped all calls to mount(2) to add logging in case of failure
* New configure options to control fastboot (no fsck) and fsck fix
options, by Ming Liu, Atlas Copco
* Support for overriding default `/etc/nologin` file with an external
`#define`, by Ming Liu, Atlas Copco
* Support for overriding default `/var/run/dbus/pid` file with an
external `#define`, by Ming Liu, Atlas Copco
* Support for more service options to control respawn behavior of
crashing services, by Robert Andersson and Ming Liu, Atlas Copco
* Support for `initctl ident [NAME]` which lists all instances of
`NAME`, or all enabled system run/tasks and services
* Show number of total restarts and current respawn count for a
service in `initctl status foo`
* Crashing services no longer have the crash/restart counter reset as
soon as they have stabilized. Instead, a background timer will
slowly (every 300 sec) age (decrement) the counter. This will still
catch services that "rage quit", but also those that crash after a
longer period of activity
### Fixes
* Fix #180: user managed (`manual:yes`) services accidentally started
by `initctl reload`, regression introduced in Finit v4.0
* Fix #181: lots of typos all over the tree, by David Yang, Debian
* Fix #187: fix typos, incl. small cleanup, in doc/bootstrap.md
* Fix #188: support running on kernels that do not have cgroups v2.
When this is detect, all functions related to cgroups support in
Finit are disabled, *except* the .conf file parser. Hence, you
may get parse error if you have invalid cgroup configuration in
your Finit .conf files
* Fix #197: `initctl status foo` now shows a focused overview of all
matching instances; foo:1, foo:2 -- if only one instance matches the
command line argument, or if onle one instance exists, the detailed
view is shown, as before
* Fix #198: a few typos found by Tim Gates
* Fix #199: avoid using C++ reserved keywords
* Fix #201: memory leak in usr condition plugin, by Ming Liu, Atlas Copco
* Fix #203: ensure all filesystems listed in /proc/mounts are properly
unumounted on shutdown/reboot, by Robert Andersson, Atlas Copco
* Fix #210: resizing terminal (smaller) after boot causes empty lines
to be inserted between boot progress
* Fix #211: drop hard-coded 32 character limit in getty, now reads
`_SC_LOGIN_NAME_MAX` from `sysconf(3)`
* Fix #212: service PID file lost after inictl reload, visible from
the output from `initctl status foo`
[4.1][] - 2021-06-06
--------------------
Bug fix release. Also disables handlers for `SIGINT` and `SIGPWR`, a
new set of `sys` conditions are instead generated which can be used to
trigger external programs.
### Changes
* Change behavior on SIGUSR1 to be compatible with sysvinit and systemd.
Previously SIGUSR1 caused Finit to halt, like BusyBox init. This had
"interesting" side effects on Debian systems when coexisting with
sysvinit (upgrading/reinstalling causes scripts to `kill -USR1 1`)
* Change how `contrib/debian/install.sh` sets up a Grub boot entry for
finit. We now modify the $SUPPORTED_INITS variable in `10_linux`
* Disable default kernel ctrl-alt-delete handler and let Finit instead
catch `SIGINT` from kernel to be able to perform a proper reboot.
There is no default command for this, you need to set up a task that
triggers on `<sys/key/ctrlaltdel>` to issue `initctl reboot`
* Added keventd to provide `<sys/pwr/ac>` condition to Finit. keventd
is currently only responsible for monitoring `/sys/class/power_supply`
for changes to active AC mains power online status. Enable keventd
with `configure --with-keventd`
* For handling power fail events (from UPS and similar) a process may
send `SIGPWR` to PID 1. Finit no longer redirects this to `SIGUSR1`
(poweroff). There is no default command for this, you need to set up
a task that triggers on `<sys/pwr/fail>` to issue `initctl poweroff`
* Built-in Finit getty is now a standalone program
* Default termios for TTYs now enable `IUTF8` on input
* If `/bin/login` is not found, Finit now tries `sulogin` before it
falls back to an unauthenticated `/bin/sh`
* Dropped (broken) support for multiple consoles. Finit now follows
the default console selected by the kernel, `/dev/console`
* Dropped signal handlers for SIGSTOP/TSTP and SIGCONT
* Added support for `\n`, in addition to `\r`, in "Please press Enter"
prompt before starting getty
* Finit no longer parses `/proc/cmdline` for its options. Instead all
options are by default now read from `argv[]`, like a normal program,
this is also what the kernel does by default. Please note, this may
not work if your systems boots with an initramfs (ymmv), for such
cases, see `configure --enable-kernel-cmdline`
* The following plugins are now possible to disable (for containers):
`rtc.so`, `urandom.so`, you may also want to disable `hotplug.so`.
They are all enabled by default, as in Finit 4.0, but may be moved
to external tools or entries in `finit.conf` in Finit 5.0
* Added support for reading `PRETTY_NAME` from `/etc/os-release` to use
as heading in progress output, unless `--with-heading=GREET` is used.
* Added manual pages for finit(8), initctl(8), and finit.conf(5)
### Fixes
* Stricter interface name validation in netlink plugin, modeled after
the kernel. Suggested by Coverity Scan
* Fix problem of re-registering a service as a task. Previously, if a
fundamental change, like type, was made to an active service/run/task
it did not take. Only possible workaround was to remove from config
* initctl: drop warning when removing a non-existing usr condition
* initctl: drop confusing `errno 0` when timing out waiting for reply
* Ensure services in plugins and from Finit main belong to a cgroup
* Ensure init top-level cgroup remains a leaf group
* Fix tty parse error for detecting use of external getty
* Fix default `name:` and `:ID` for tty's, e.g. `ttyS0` now gives
`tty:S0` as expected. This was default for built-in getty already
* Fix max username (32 chars) in bundled Finit getty
* The `contrib/*/install.sh` scripts failed to run from tarball
* Finit no longer forcibly mounts; `/dev`, `/proc`, or `/sys`, instead
it checks first if they are already mounted (devtmpfs or container)
* Fix `/etc/fstab` parser to properly check for 'ro' to not remount the
root filesystem at boot. The wrong field was read, so a root mounted
by an initramfs, or by lxc for a container, had their root remounted
* Fix SIGCHLD handler, `waitpd()` may be interrupted by a signal
* Reset `starting` flag of services being stopped. When a service
is started and then stopped before it has created its pid file,
it could be left forever in the stopping state, unless we reset
the starting flag.
* Fix #170: detect loss of default route when interfaces go down. This
emulates the missing kernel netlink message to remove the condition
net/default/route to allow stopping dependent services
* Fix #171: restore automatic mount of `/dev/shm`, `/dev/pts`, `/run`
and `/tmp`, unless mounted already by `/etc/fstab`. This is what most
desktop systems expect PID 1 to do. Here we also make sure to mount
`/run/lock` as a tmpfs as well, with write perms for regular users,
this prevents regular users from filling up `/run` and causing DoS.
* Fix #173: netlink plugin runs out of socket buffer space;
finit[1]: nl_callback():recv(): No buffer space available
Fixed by adding support for resync with kernel on `ENOBUFS`. See
netlink(7) for details. As a spin-off the plugin now supports any
number of interfaces and routes on a system. On resync, the following
message is now logged, as a warning:
finit[1]: nl_callback():busy system, resynchronizing with kernel.
* Fix #174: loss of log messages using combo of prio and facility, e.g.,
`logit(LOG_CONSOLE | LOG_NOTICE, ...)`, by Jacques de Laval, Westermo
* Fix #175: ensure Finit does not acquire a controlling TTY when checking
if a device is a TTY before starting a getty. This fixes an old bug
where Ctrl-C after logout from a shell could cause PID 1 to get SIGINT,
which in turn could lead to a system reboot
[4.0][] - 2021-04-26
--------------------
This release became v4.0, and not v3.2, because of incompatible changes This release became v4.0, and not v3.2, because of incompatible changes
to service conditions. There are other significant changes as well, so to service conditions. There are other significant changes as well, so
@@ -206,53 +18,31 @@ make sure to read the whole change log when upgrading.
with a fallback to sending signals as per traditional SysV init. The with a fallback to sending signals as per traditional SysV init. The
`-f` (force) flag remains, where `reboot(2)` is called directly `-f` (force) flag remains, where `reboot(2)` is called directly
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷 * Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
* The `inictl cond set|clear COND` have changed completely. Constrained
to a flat `<usr/...>` namespace and automatically activated by a new
`usr.so` plugin that checks services for usr condition changes
* Removed built-in inetd super server. If you need this functionality, * Removed built-in inetd super server. If you need this functionality,
use an external inetd, like xinetd, instead. A pull request for a use an external inetd, like xinetd, instead. A pull request for a
stand-alone inetd, like watchdogd and getty, is most welcome! stand-alone inetd, like watchdogd and getty, is most welcome!
* Incompatible `configure` script changes, i.e., no guessing `--prefix` * Incompatible `configure` script changes, i.e., you must give proper
and other paths. Also, many options have been changed, renamed, or path arguments to the script, no more guessing just GNU defaults.
flipped defaults, or even dropped altogether. There are examples in There are examples in the documentation and the `contrib/` section
the documentation and the `contrib/` section * Change service conditions from the non-obvious `<svc/path/to/foo>` to
* Service conditions change from the non-obvious `<svc/path/to/foo>` to `<pid/foo:id>`. This to hopefully make it clear that one service's
`<pid/foo:id>`. Not only does this give simpler internal semantics, 'pid:!foo' pidfile is another service's `<pid/foo>` condition
it hopefully also makes it clear that one service's `pid:!foo` pidfile * Initial support for cgroups, every service now runs in its own cgroup
is another service's `<pid/foo>` condition, issue #143 https://twitter.com/b0rk/status/1214341831049252870?s=20
* Initial support for cgroups v2:
- services runs in a cgroup named after their respective *.conf file
- top-level groups are; init, user, and system
- all top-level groups can be configured from finit *.conf files
- each service can tweak the cgroup settings
- Use `initctl [top|ps|cgroup]` commands to inspect runtime state
- https://twitter.com/b0rk/status/1214341831049252870?s=20
* Major refactor of Finit's `main()` function to be able to start the * Major refactor of Finit's `main()` function to be able to start the
event loop earlier. This also facilitated factoring out functionality event loop earlier. This also facilitated factoring out functionality
previously hard-coded in Finit, e.g., starting the bundled watchdogd, previously hard-coded in Finit, e.g., starting the bundled watchdogd,
various distro packed udevd and other hotplugging tools various distro packed udevd and other hotplugging tools
* A proper rescue mode has been added. It is started extremely early
and is protected with a bundled `suslogin`. Exiting rescue mode now
brings up the system as a normal boot, as one expects
* Support for `sysv` start/stop scripts as well as monitoring forking * Support for `sysv` start/stop scripts as well as monitoring forking
services, stared using `sysv` or `service` stanza services, stared using `sysv` or `service` stanza
* Support for custom `kill:DELAY`, default 3 sec. * Support for custom `kill:DELAY`, default 3 sec.
* Support for custom `halt:SIGNAL`, default SIGTERM * Support for custom `halt:SIGNAL`, default SIGTERM
* Support for `pre:script` and `post:script`, allows for setup and
teardown/cleanup before and after a service runs, issue #129
* Support for `env:file` in `/etc/default/foo` or `/etc/conf.d/foo`, see
the contrib section for examples that utilize this feature. Variables
expanded from env files, and the env files themselves, are tracked for
changes to see if a service .conf file is "dirty" and needs restart on
`initctl reload`
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`, * Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
useful with new `sysv` or `service` which fork to background useful with new `sysv` or `service` which fork to background
* Support starting run/task/services without absolute path, trust `$PATH` * Support starting run/task/services without absolute path, trust `$PATH`
* Add support for `--disable-doc` and `--disable-contrib` to speed up * Add support for `--disable-doc` and `--disable-contrib` to speed up
builds and work around issue with massively parallel builds builds and work around issue with massively parallel builds
* Support for `@console` also for external getty * Add support for `@console` also for external getty
* Support for `notty` option to built-in getty, for board bring-up
* Support for `rescue` option to built-in getty, for rescue shells
* Add `-b`, batch mode, for non-interactive use to `initctl` * Add `-b`, batch mode, for non-interactive use to `initctl`
* Prefer udev to handle `/dev/` if mdev is also available * Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog * Redirect dbus daemon output to syslog
@@ -272,7 +62,7 @@ make sure to read the whole change log when upgrading.
- As long as a stepped service changes state we queue another step all - As long as a stepped service changes state we queue another step all
event, because services may depend on each other event, because services may depend on each other
* Require new libuEv API: `uev_init1()` to reduce event cache so that * Require new libuEv API: `uev_init1()` to reduce event cache so that
the kernel can invalidate deleted events before enqueuing to userspace the kernel can invalidate deleted events before enqueing to userspace
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone * Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
from the `hwclock` tool. Note: the kernel can also be set to load from the `hwclock` tool. Note: the kernel can also be set to load
and store RTC to/from system clock at boot/halt as well, issue #110 and store RTC to/from system clock at boot/halt as well, issue #110
@@ -302,8 +92,6 @@ make sure to read the whole change log when upgrading.
* Always append `:ID` qualifier to conditions if set for a service * Always append `:ID` qualifier to conditions if set for a service
* The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket` * The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket`
officially only supported for use by the `initctl` tool officially only supported for use by the `initctl` tool
* The IPC socket now uses `SOCK_SEQPACKET` instead of `SOCK_STREAM`.
Recommend using watchdogd v3.4, or later, which support this
* Improved support for modern `/etc/network/interfaces`, which has * Improved support for modern `/etc/network/interfaces`, which has
include statements. No more native `ifup` of individual interfaces, include statements. No more native `ifup` of individual interfaces,
Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to
@@ -321,21 +109,16 @@ make sure to read the whole change log when upgrading.
that do not have a watchdog that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does * Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access not yet exist (symlink to `/run`). Added compat layer for access
* Fix #101: Built-in inetd removed
* Fix #102: Start built-in watchdogd as a regular service
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY, * Fix #103: Register multiple getty if `@console` resolves to >1 TTY,
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6 * Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
Fixed by Jonas Johansson, Westermo Fixed by Jonas Johansson, Westermo
* Fix #109: Support for PID files in sub-directories to `/var/run` * Fix #109: Support for PID files in sub-directories to `/var/run`
* Handle rename of PID files, by Robert Andersson, Atlas Copco * Handle rename of PID files, by Robert Andersson, Atlas Copco
* Fix #110: automatic modprobe of RTC devices, built-in hwclock
* Fix #120: Redirect `stdin` to `/dev/null` for services by default * Fix #120: Redirect `stdin` to `/dev/null` for services by default
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep, * Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
fixed by Jacques de Laval, Westermo fixed by Jacques de Laval, Westermo
* Fix #124: Lingering processes in process group when session leader * Fix #124: Lingering processes in process group when session leader
exits. E.g., lingering `logit` processes when parent dies exits. E.g., lingering `logit` processes when parent dies
* Fix #127: Show all runparts scripts as they start, like rc.local, fixed
by Jacques de Laval, Westermo
* Fix service name matching, e.g. for condition handling, may match with * Fix service name matching, e.g. for condition handling, may match with
wrong service, by Jonas Holmberg, Westermo wrong service, by Jonas Holmberg, Westermo
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard * Run all run-parts scripts using `/bin/sh -c foo` just like the standard
@@ -354,8 +137,6 @@ make sure to read the whole change log when upgrading.
* Provide service description for built-in watchdog daemon * Provide service description for built-in watchdog daemon
* Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system * Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system
* Drop the '%m' GNUism, for compat with older musl libc * Drop the '%m' GNUism, for compat with older musl libc
* Fix #139: call `tzset()` on `initctl reload` to activate system
timezone changes (for logging)
[3.1][] - 2018-01-23 [3.1][] - 2018-01-23
@@ -594,7 +375,7 @@ Bug fix release.
* Add support for status/show service by `name:id` * Add support for status/show service by `name:id`
* Enforce terse mode after boot, if verbose mode is disabled * Enforce terse mode after boot, if verbose mode is disabled
* Re-enable verbose mode at reboot, if disabled at boot * Reenable verbose mode at reboot, if disabled at boot
* Update section mentioning BusyBox getty * Update section mentioning BusyBox getty
* Update debugging documentation * Update debugging documentation
* Allow debug to override terse mode * Allow debug to override terse mode
@@ -649,7 +430,7 @@ handling and reconfiguration at runtime.
* Clarify how to select different plugins with the configure script * Clarify how to select different plugins with the configure script
* Improve urandom plugin for embedded systems w/o random seed * Improve urandom plugin for embedded systems w/o random seed
* Add `--debug` flag to `initctl` * Add `--debug` flag to `initctl`
* The runlevels listed for services in `initctl show` now highlight the * The runlevels listed for services in `initctl show` now hightlight the
active runlevel. active runlevel.
* Clarify in the README and in `initctl help` that the GW event to * Clarify in the README and in `initctl help` that the GW event to
listen for in service declarations is `GW:UP` listen for in service declarations is `GW:UP`
@@ -681,7 +462,7 @@ handling and reconfiguration at runtime.
* Do not use `-Os` use `-O2` as default optimization level. Many cross * Do not use `-Os` use `-O2` as default optimization level. Many cross
compiler toolchains are known to have problems with `-Os` compiler toolchains are known to have problems with `-Os`
* Do not allow build VERSION to be overloaded by an environment variable * Do not allow build VERSION to be overloaded by an environment variable
* Fix too small MAX arguments and too few arguments in `svc_t` for * Fix too small MAX arguments and too few argments in `svc_t` for
reading currently running services with `initctl show` reading currently running services with `initctl show`
* Unblock blocked signals after forking off a child * Unblock blocked signals after forking off a child
@@ -799,7 +580,7 @@ The inetd release.
turned out to not work so well after all. For instance, launching turned out to not work so well after all. For instance, launching
TTYs in a background process completely blocked inetd services from TTYs in a background process completely blocked inetd services from
even starting up listening sockets ... proper fork seems to work fine even starting up listening sockets ... proper fork seems to work fine
though. This is the cause for *yanking* the [1.11] release, below. though. This is the casue for *yanking* the [1.11] release, below.
* Trap segfaults caused by external plugins/callbacks in a sub-process. * Trap segfaults caused by external plugins/callbacks in a sub-process.
This prevents a single programming mistake in by a 3rd party developer This prevents a single programming mistake in by a 3rd party developer
from taking down the entire system. from taking down the entire system.
@@ -895,7 +676,7 @@ Major bug fix release.
### Changes ### Changes
* Show `__FILE__` in `_d()` debug messages, useful for plugins with * Show `__FILE__` in `_d()` debug messages, useful for plugins with
similarly named callbacks. Also, only in debug mode anyway similarily named callbacks. Also, only in debug mode anyway
* Make sure to cleanup recorded PID when a service is lost. Required by * Make sure to cleanup recorded PID when a service is lost. Required by
service plugins for their callbacks to work. service plugins for their callbacks to work.
* Only clear screen when in verbose mode. Maybe this should be removed * Only clear screen when in verbose mode. Maybe this should be removed
@@ -964,14 +745,14 @@ Major bug fix release.
* Makefile fixes for installation, paths encoded wrong * Makefile fixes for installation, paths encoded wrong
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip * Strip binaries + .so files, support for `$(CROSS)` toolchain strip
* Default install is now to `/sbin/finit` and `/usr/` * Default install is now to `/sbin/finit` and `/usr/`
* Note change in `$PLUGIN_DIR` environment variable to `$plugindir` * Note change in `$PLUGIN_DIR` environemnt variable to `$plugindir`
[1.2][] - 2012-09-27 [1.2][] - 2012-09-27
-------------------- --------------------
### Changes ### Changes
* Update README with section on building and environment variables * Update README with section on building and enviroment variables
### Fixes ### Fixes
* Fix installation paths encoded in finit binary * Fix installation paths encoded in finit binary
@@ -1063,10 +844,7 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0 [4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1 [3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0 [3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
@@ -1098,3 +876,9 @@ Major bug fix release.
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2 [0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
[libuEv]: https://github.com/troglobit/libuev [libuEv]: https://github.com/troglobit/libuev
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8 [dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+17 -1
View File
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man plugins src test SUBDIRS = plugins src test
doc_DATA = README.md LICENSE contrib/finit.conf doc_DATA = README.md LICENSE contrib/finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
@@ -29,3 +29,19 @@ release: distcheck
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \ printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \ printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
done done
run:
srcdir=@srcdir@/test \
top_builddir=@top_builddir@ \
TENV_ROOT=$(tenv_root) \
@srcdir@/test/setup-root.sh
mkdir -p $(dir $(tenv_root)/@FINIT_CONF@)
echo 'tty [12345] @console linux noclear nologin' > $(tenv_root)/@FINIT_CONF@
-TENV_ROOT=$(tenv_root) \
TESTENV_PATH=@sbindir@:/bin \
FINIT_RCSD=@FINIT_RCSD@ \
test/tenv/start.sh finit
run : tenv_root ?= @abs_top_builddir@/run-rootfs
+73 -199
View File
@@ -1,4 +1,4 @@
[![License Badge][]][License] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan] [![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
<img align="right" src="img/finit3.png" alt="Finit: Fast Init"> <img align="right" src="img/finit3.png" alt="Finit: Fast Init">
* [Introduction](#introduction) * [Introduction](#introduction)
@@ -22,29 +22,9 @@
Introduction Introduction
------------ ------------
Fast init for Linux systems. Reverse engineered from the [EeePC Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
fastinit][] over ten years ago by [Claudio Matsuoka][] — "gaps filled was reverse engineered from the [EeePC fastinit][] over ten years ago
with frog DNA …" by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
![Alpine screenshot](img/finit4-screenshot.png)
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
Features include:
* Runlevels, defined per service
* One-shot tasks, services (daemons), or [SysV init][4] start/stop scripts
* Runparts and `/etc/rc.local` support
* Process supervision similar to [systemd][]
* Sourcing environment files
* Conditions for network/process/custom dependencies
* Pre/Post script actions
* Tooling to enable/disable services
* Built-in getty
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
* Built-in support for Debian/BusyBox `/etc/network/interfaces`
* Cgroups v2, both configuration and monitoring in `initctl top`
* Plugin support for customization
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
Focus is on small and embedded systems, although Finit is fully usable Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the on server and desktop systems as well. For working examples, see the
@@ -54,6 +34,9 @@ on server and desktop systems as well. For working examples, see the
* [Alpine Linux](contrib/alpine/), and * [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/) * [Debian GNU/Linux](contrib/debian/)
![Alpine screenshot](img/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
Example Example
------- -------
@@ -75,10 +58,10 @@ runlevel 2
log size=100k count=4 log size=100k count=4
# Services to be monitored and respawned as needed # Services to be monitored and respawned as needed
service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon service [S12345] watchdogd -L -f -- System watchdog daemon
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon service [S12345] syslogd -n -b 3 -D -- System log daemon
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab) service [2345] lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
# The BusyBox ntpd does not use syslog when running in the foreground # The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The # So we use this trick to redirect stdout/stderr to a log file. The
@@ -93,24 +76,24 @@ service :80 [2345] merecat -n -p 80 /var/www -- Web server
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
# Alternative method instead of below runparts, can also use /etc/rc.local # Alternative method instead of below runparts, can also use /etc/rc.local
#sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap #task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon #task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
#task [S] /etc/init.d/kbd -- Preparing console #task [S] /etc/init.d/kbd start -- Preparing console
# Run start scripts from this directory # Run start scripts from this directory
# runparts /etc/start.d # runparts /etc/start.d
# Virtual consoles run BusyBox getty, keep kernel default speed # Virtual consoles run BusyBox getty, keep kernel default speed
tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear tty [12345] /sbin/getty -L 0 /dev/tty1 linux
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear tty [12345] /sbin/getty -L 0 /dev/tty2 linux
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear tty [12345] /sbin/getty -L 0 /dev/tty3 linux
# Use built-in getty for serial port and USB serial # Use built-in getty for serial port and USB serial
#tty [12345] /dev/ttyAMA0 noclear nowait tty [12345] /dev/ttyAMA0 noclear nowait
#tty [12345] /dev/ttyUSB0 noclear tty [12345] /dev/ttyUSB0 noclear
# Just give me a shell, I need to debug this embedded system! # Just give me a shell, I need to debug this embedded system!
#tty [12345] console noclear nologin tty [12345] @console noclear nologin
``` ```
The `service` stanza, as well as `task`, `run` and others are described The `service` stanza, as well as `task`, `run` and others are described
@@ -120,9 +103,9 @@ some of the most common components needed to start a UNIX daemon:
``` ```
service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
| | | | | | | `---------- Optional description | | | | | | | `-- Optional description
| | | | | | `------------------ Daemon arguments | | | | | | `----------- Daemon arguments
| | | | | `------------------------- Path to daemon | | | | | `-------------------------- Path to daemon
| | | | `---------------------------------------------------- Optional env. file | | | | `---------------------------------------------------- Optional env. file
| | | `-------------------------------------------------------- Redirect output to log | | | `-------------------------------------------------------- Redirect output to log
| | `--------------------------------------------------------------- Optional conditions | | `--------------------------------------------------------------- Optional conditions
@@ -134,13 +117,13 @@ Some components are optional: runlevel(s), condition(s) and description,
making it easy to create simple start scripts and still possible for more making it easy to create simple start scripts and still possible for more
advanced uses as well: advanced uses as well:
service /usr/sbin/sshd -D service sshd -D
Dependencies are handled using [conditions](doc/conditions.md). One of Dependencies are handled using [conditions](doc/conditions.md). One of
the most common conditions is to wait for basic networking to become the most common conditions is to wait for basic networking to become
available: available:
service <net/route/default> nginx -- High performance HTTP server service <net/route/default> nginx -- High performace HTTP server
Here is another example where we instruct Finit to not start BusyBox Here is another example where we instruct Finit to not start BusyBox
`ntpd` until `syslogd` has started properly. Finit waits for `syslogd` `ntpd` until `syslogd` has started properly. Finit waits for `syslogd`
@@ -249,11 +232,8 @@ For more information, see [doc/plugins.md](doc/plugins.md).
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/` By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
registering any changes to `.conf` files. To activate a change the user registering any changes to `.conf` files. To activate a change the user
must call `initctl reload`, which reloads all modified files, stops any must call `initctl reload`, which reloads all modified files, stops any
removed services, starts new ones, and restarts any modified ones. If the removed services, starts new ones, and restarts any modified ones, with
command line arguments of a service have changed, the process will be SIGHUP if the process supports it.
terminated and then started again with the updated arguments. If the arguments
have not been modified and the process supports SIGHUP, the process will
receive a SIGHUP rather than being terminated and started.
For some use-cases the extra step of calling `initctl reload` creates an For some use-cases the extra step of calling `initctl reload` creates an
unnecessary overhead, which can be removed at build-time using: unnecessary overhead, which can be removed at build-time using:
@@ -261,46 +241,6 @@ unnecessary overhead, which can be removed at build-time using:
configure --enable-auto-reload configure --enable-auto-reload
**Cgroups**
Finit supports cgroups v2 and comes with the following default groups
in which services and user sessions are placed in:
/sys/fs/cgroup
|-- init/ # cpu.weight:100
|-- system/ # cpu.weight:9800
`-- user/ # cpu.weight:100
Finit itself and its helper scripts and services are placed in the
top-level leaf-node group `init/`, which also is _reserved_.
All run/task/service/sysv processes are placed in their own sub-group
in `system/`. The name of each sub-group is taken from the respective
`.conf` file from `/etc/finit.d`.
All getty/tty processes are placed in their own sub-group in `user/`.
The name of each sub-group is taken from the username.
A fourth group also exists, the `root` group. It is also _reserved_ and
primarily intended for RT tasks. If you have RT tasks they need to be
declared as such in their service stanza like this:
service [...] <...> cgroup.root /path/to/foo args -- description
or
cgroup.root
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
See [doc/config.md](doc/config.md#cgroups) for more information, e.g.,
how to configure per-group limits.
The `initctl` tool has three commands to help debug and optimize the
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
commands for details.
Runparts & /etc/rc.local Runparts & /etc/rc.local
------------------------ ------------------------
@@ -408,18 +348,11 @@ Finit also implements a modern API to query status, and start/stop
services, called `initctl`. Unlike `telinit` the `initctl` tool does services, called `initctl`. Unlike `telinit` the `initctl` tool does
not return until the given command has fully completed. not return until the given command has fully completed.
``` ```shell
alpine:~# initctl help ~ $ initctl -h
Usage: initctl [OPTIONS] [COMMAND] Usage: initctl [OPTIONS] [COMMAND]
Options: Options:
-b, --batch Batch mode, no screen size probing
-c, --create Create missing paths (and files) as needed
-f, --force Ignore missing files and arguments, never prompt
-1, --once Only one lap in commands like 'top'
-p, --plain Use plain table headings, no ctrl chars
-q, --quiet Silent, only return status of command
-t, --no-heading Skip table headings
-v, --verbose Verbose output -v, --verbose Verbose output
-h, --help This help text -h, --help This help text
@@ -428,38 +361,28 @@ Commands:
help This help text help This help text
version Show Finit version version Show Finit version
ls | list List all .conf in /etc/finit.d list List all .conf in /etc/finit.d/
create <CONF> Create .conf in /etc/finit.d/available enable <CONF> Enable .conf in /etc/finit.d/available/
delete <CONF> Delete .conf in /etc/finit.d/available disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
show <CONF> Show .conf in /etc/finit.d/available touch <CONF> Mark .conf in /etc/finit.d/ for reload
edit <CONF> Edit .conf in /etc/finit.d/available reload Reload *.conf in /etc/finit.d/ (activates changes)
touch <CONF> Change .conf in /etc/finit.d/available
enable <CONF> Enable .conf in /etc/finit.d/available
disable <CONF> Disable .conf in /etc/finit.d/enabled
reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined condition +usr/COND cond show Show condition status
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
cond status Show condition status, default cond command
cond dump Dump all conditions and their status cond dump Dump all conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog log [NAME] Show ten last Finit, or NAME, messages from syslog
start <NAME>[:ID] Start service by name, with optional ID start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <NAME>[:ID] Stop/Pause a running service by name stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
reload <NAME>[:ID] Reload service by name (SIGHUP or restart) restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
restart <NAME>[:ID] Restart (stop/start) service by name status <JOB|NAME>[:ID] Show service status, by job# or name
status <NAME>[:ID] Show service status, by name status | show Show status of services, default command
status Show status of services, default command
cgroup List cgroup config overview
ps List processes based on cgroups
top Show top-like listing based on cgroups
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system reboot Reboot system
halt Halt system halt Halt system
poweroff Halt and power off system poweroff Halt and power off system
suspend Suspend system
utmp show Raw dump of UTMP/WTMP db
``` ```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
@@ -467,73 +390,22 @@ file must be used to tell Finit to stop and start it on `reload` and
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md), `runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
these will also affect how a service is maintained. these will also affect how a service is maintained.
> **Note:** even though it is possible to start services not belonging in **Note:** even though it is possible to start services not belonging in
> the current runlevel these services will not be respawned automatically the current runlevel these services will not be respawned automatically
> by Finit if they exit (crash). Hence, if the runlevel is 2, the below by Finit if they exit (crash). Hence, if the runlevel is 2, the below
> Dropbear SSH service will not be restarted if it is killed or exits. Dropbear SSH service will not be restarted if it is killed or exits.
The `status` command is the default, it displays a quick overview of all ```shell
monitored run/task/services. Here we call `initctl -p`, suitable for ~ $ initctl status -v
scripting and documentation: 1 running 476 [S12345] /sbin/watchdog -T 16 -t 2 -F /dev/watchdog
2 running 477 [S12345] /sbin/syslogd -n -b 3 -D
``` 3 running 478 [S12345] /sbin/klogd -n
alpine:~# initctl -p 4:1 inetd 0 [2345] internal time allow *:37,!eth0
PID IDENT STATUS RUNLEVELS DESCRIPTION 4:2 inetd 0 [2345] internal time allow eth0:3737
====================================================================== 5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
1506 acpid running [--2345----] ACPI daemon 5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
1509 crond running [--2345----] Cron daemon 6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
1489 dropbear running [--2345----] Dropbear SSH daemon 6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
1511 klogd running [S12345----] Kernel log daemon
1512 ntpd running [--2345----] NTP daemon
1473 syslogd running [S12345----] Syslog daemon
alpine:~# initctl -pv
PID IDENT STATUS RUNLEVELS COMMAND
======================================================================
1506 acpid running [--2345----] acpid -f
1509 crond running [--2345----] crond -f -S $CRON_OPTS
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
1473 syslogd running [S12345----] syslogd -n
```
The environment variables to each of the services above are read from,
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
have other directories, e.g., Debian use `/etc/default/`.
The `status` command takes an optional `NAME:ID` argument. Here we
check the status of `dropbear`, which only has one instance in this
system:
```
alpine:~# initctl -p status dropbear
Status : running
Identity : dropbear
Description : Dropbear SSH daemon
Origin : /etc/finit.d/enabled/dropbear.conf
Environment : -/etc/conf.d/dropbear
Condition(s):
Command : dropbear -R -F $DROPBEAR_OPTS
PID file : !/run/dropbear.pid
PID : 1485
User : root
Group : root
Uptime : 2 hour 46 min 56 sec
Runlevels : [--2345----]
Memory : 1.2M
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|- 1485 dropbear -R -F
|- 2634 dropbear -R -F
|- 2635 ash
`- 2652 initctl -p status dropbear
Apr 8 12:19:49 alpine authpriv.info dropbear[1485]: Not backgrounding
Apr 8 12:37:45 alpine authpriv.info dropbear[2300]: Child connection from 192.168.121.1:47834
Apr 8 12:37:46 alpine authpriv.notice dropbear[2300]: Password auth succeeded for 'root' from 192.168.121.1:47834
Apr 8 12:37:46 alpine authpriv.info dropbear[2300]: Exit (root) from <192.168.121.1:47834>: Disconnect received
Apr 8 15:02:11 alpine authpriv.info dropbear[2634]: Child connection from 192.168.121.1:48576
Apr 8 15:02:12 alpine authpriv.notice dropbear[2634]: Password auth succeeded for 'root' from 192.168.121.1:48576
``` ```
@@ -571,34 +443,36 @@ This project is based on the [original finit][] by [Claudio Matsuoka][]
which was reverse engineered from syscalls of the [EeePC fastinit][] — which was reverse engineered from syscalls of the [EeePC fastinit][] —
"gaps filled with frog DNA …" "gaps filled with frog DNA …"
Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][6]. Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][].
Please file bug reports, clone it, or send pull requests for bug fixes Please file bug reports, clone it, or send pull requests for bug fixes
and proposed extensions. and proposed extensions.
[1]: https://en.wikipedia.org/wiki/Process_supervision [1]: https://en.wikipedia.org/wiki/Process_supervision
[2]: https://cr.yp.to/daemontools.html [2]: http://cr.yp.to/daemontools.html
[3]: https://smarden.org/runit/ [3]: http://smarden.org/runit/
[4]: https://en.wikipedia.org/wiki/Init [4]: http://en.wikipedia.org/wiki/Init
[5]: https://en.wikipedia.org/wiki/Runlevel [5]: http://en.wikipedia.org/wiki/Runlevel
[6]: https://github.com/troglobit/finit [6]: https://www.freedesktop.org/wiki/Software/systemd/
[9]: https://github.com/troglobit/troglos
[RFC862]: https://tools.ietf.org/html/rfc862 [RFC862]: https://tools.ietf.org/html/rfc862
[RFC863]: https://tools.ietf.org/html/rfc863 [RFC863]: https://tools.ietf.org/html/rfc863
[RFC864]: https://tools.ietf.org/html/rfc864 [RFC864]: https://tools.ietf.org/html/rfc864
[RFC867]: https://tools.ietf.org/html/rfc867 [RFC867]: https://tools.ietf.org/html/rfc867
[RFC868]: https://tools.ietf.org/html/rfc868 [RFC868]: https://tools.ietf.org/html/rfc868
[init]: https://en.wikipedia.org/wiki/Init [init]: http://en.wikipedia.org/wiki/Init
[upstart]: https://upstart.ubuntu.com/ [upstart]: http://upstart.ubuntu.com/
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/ [systemd]: http://www.freedesktop.org/wiki/Software/systemd/
[openrc]: https://www.gentoo.org/proj/en/base/openrc/ [openrc]: http://www.gentoo.org/proj/en/base/openrc/
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[original finit]: http://helllabs.org/finit/ [original finit]: http://helllabs.org/finit/
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process [EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
[Claudio Matsuoka]: https://github.com/cmatsuoka [Claudio Matsuoka]: https://github.com/cmatsuoka
[Joachim Wiberg]: https://troglobit.com [Joachim Wiberg]: http://troglobit.com
[GitHub]: https://github.com/troglobit/finit
[License]: https://en.wikipedia.org/wiki/MIT_License [License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg [License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/ [Travis]: https://travis-ci.org/troglobit/finit
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg [Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545 [Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg [Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
+48 -73
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.3-dev, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html) AC_INIT(Finit, 4.0-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
@@ -8,14 +8,13 @@ AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
contrib/Makefile contrib/Makefile
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
doc/Makefile doc/Makefile
man/Makefile
plugins/Makefile plugins/Makefile
src/Makefile src/Makefile
test/test.env test/Makefile test/test.env
test/tenv/Makefile test/common/Makefile]) test/Makefile])
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR() # Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4]) #m4_include([m4/plugin.m4])
@@ -33,7 +32,7 @@ AC_PROG_MKDIR_P
# Configuration. # Configuration.
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h]) AC_CHECK_HEADERS([fstaby.h termios.h])
AC_CHECK_FUNCS([strstr getopt getfsenty]) AC_CHECK_FUNCS([strstr getopt getfsenty])
# Check for uint[8,16,32]_t # Check for uint[8,16,32]_t
@@ -49,25 +48,30 @@ PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.2.0]) PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
# Check for configured Finit features # Check for configured Finit features
AC_ARG_ENABLE(emergency_shell,
AS_HELP_STRING([--enable-emergency-shell], [Experimental emergency fallback at boot.
Use only for debugging very early boot problems with Finit]),,[
enable_emergency_shell=no])
AC_ARG_ENABLE(fallback_shell,
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
enable_fallback_shell=no])
AC_ARG_ENABLE(auto_reload, AC_ARG_ENABLE(auto_reload,
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[ AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
enable_auto_reload=no]) enable_auto_reload=no])
AC_ARG_ENABLE(kernel_cmdline, AC_ARG_ENABLE(watchdog,
AS_HELP_STRING([--enable-kernel-cmdline], [Parse init args from /proc/cmdline (don't use!)]),,[ AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
enable_kernel_cmdline=no]) enable_watchdog=no])
AC_ARG_ENABLE(fastboot,
AS_HELP_STRING([--enable-fastboot], [Skip fsck check on filesystems listed in /etc/fstab]),,[
enable_fastboot=no])
AC_ARG_ENABLE(fsckfix,
AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[
enable_fsckfix=no])
AC_ARG_ENABLE(redirect, AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[ AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
enable_redirect=yes]) enable_redirect_output=no])
AC_ARG_ENABLE(logit,
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
enable_logit=yes])
AC_ARG_ENABLE(logrotate, AC_ARG_ENABLE(logrotate,
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[ AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
@@ -86,18 +90,14 @@ enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl]) 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([dbus], [no], [Setup and start system message bus, D-Bus])
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load]) AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
AC_PLUGIN([modprobe], [yes], [Coldplug modules using modalias magic])
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf]) AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)]) AC_PLUGIN([x11-common], [no], [Console setup (for X)])
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.]) AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon]) AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
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])
# Check for extra arguments or packages # Check for extra arguments or packages
AC_ARG_WITH(heading, AC_ARG_WITH(heading,
AS_HELP_STRING([--with-heading=GREET], [Heading for boot progress. Default: PRETTY_NAME from /etc/os-release, fallback to "Finit vX.YY"]), AS_HELP_STRING([--with-heading=GREET], [Display GREET instead of Finit version at boot]),
[heading=$withval], [heading=yes]) [heading=$withval], [heading=yes])
AC_ARG_WITH(config, AC_ARG_WITH(config,
@@ -116,10 +116,6 @@ AC_ARG_WITH(sysconfig,
AS_HELP_STRING([--with-sysconfig=DIR], [System environment directory, sourced by .conf files, default /etc/default & /etc/conf.d]), AS_HELP_STRING([--with-sysconfig=DIR], [System environment directory, sourced by .conf files, default /etc/default & /etc/conf.d]),
[sysconfig=$withval], [sysconfig=default]) [sysconfig=$withval], [sysconfig=default])
AC_ARG_WITH(group,
AS_HELP_STRING([--with-group=NAME], [Group for /run/finit/socket (initctl), default: root]),
[group=$withval], [group=root])
AC_ARG_WITH(hostname, AC_ARG_WITH(hostname,
AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]), AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]),
[hostname=$withval], [hostname=yes]) [hostname=$withval], [hostname=yes])
@@ -132,37 +128,31 @@ AC_ARG_WITH(random-seed,
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]), AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
[random_seed=$withval], [random_seed=yes]) [random_seed=$withval], [random_seed=yes])
AC_ARG_WITH(keventd,
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
AC_ARG_WITH(watchdog,
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval], [with_watchdog=no watchdog=])
### Enable features ########################################################################### ### Enable features ###########################################################################
# Create config.h from selected features and fallback defaults # Create config.h from selected features and fallback defautls
AS_IF([test "x$enable_emergency_shell" = "xyes"], [
AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
AS_IF([test "x$enable_auto_reload" = "xyes"], [ AS_IF([test "x$enable_auto_reload" = "xyes"], [
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])]) AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [ AS_IF([test "x$enable_watchdog" = "xyes"], [
AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])]) AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
AS_IF([test "x$enable_fastboot" = "xyes"], [
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
AS_IF([test "x$enable_fsckfix" = "xyes"], [
AC_DEFINE(FSCK_FIX, 1, [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab])])
AS_IF([test "x$enable_redirect" = "xyes"], [ AS_IF([test "x$enable_redirect" = "xyes"], [
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])]) AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
### Disable features ########################################################################### ### Disable features ###########################################################################
AS_IF([test "x$enable_logit" != "xno"], [
AC_DEFINE(LOGIT_ENABLED, 1, [Enable logit tool, used for log redirection by Finit])])
AS_IF([test "x$enable_logrotate" != "xno"], [ AS_IF([test "x$enable_logrotate" != "xno"], [
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])]) AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
### With features ############################################################################## ### With features ##############################################################################
AS_IF([test "x$with_config" != "xno"], [ AS_IF([test "x$with_config" != "xno"], [
AS_IF([test "x$conf" = "xyes"], [ AS_IF([test "x$conf" = "xyes"], [
@@ -191,13 +181,9 @@ AS_IF([test "x$with_sysconfig" != "xno"], [
[System environment, default /etc/default & /etc/conf.d])])]) [System environment, default /etc/default & /etc/conf.d])])])
AS_IF([test "x$with_heading" != "xno"], [ AS_IF([test "x$with_heading" != "xno"], [
AS_IF([test "x$heading" = "xyes"], [heading=""]) AS_IF([test "x$heading" = "xyes"], [
AC_DEFINE_UNQUOTED(INIT_OSHEADING, "$heading", [Displayed by Finit at boot, defaults to /etc/os-release PRETTY_NAME])]) heading="$PACKAGE_NAME v$PACKAGE_VERSION"])])
AC_DEFINE_UNQUOTED(INIT_HEADING, "$heading", [Displayed by Finit at boot])
AS_IF([test "x$with_group" != "xno"], [
AS_IF([test "x$group" = "xyes"], [
group=root])])
AC_DEFINE_UNQUOTED(DEFGROUP, "$group", [For /run/finit/socket])
AS_IF([test "x$with_hostname" != "xno"], [ AS_IF([test "x$with_hostname" != "xno"], [
AS_IF([test "x$hostname" = "xyes"], [ AS_IF([test "x$hostname" = "xyes"], [
@@ -216,19 +202,10 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AC_EXPAND_DIR(random_path, "$random_seed") AC_EXPAND_DIR(random_path, "$random_seed")
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])]) AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
AS_IF([test "x$with_watchdog" != "xno"], [
AS_IF([test "x$watchdog" = "xyes"], [
watchdog=/dev/watchdog])
with_watchdog=yes
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
watchdog=])
# Control build with automake flags # Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"]) AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"]) AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"]) AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"]) AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"]) AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
@@ -247,8 +224,8 @@ AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf") AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf")
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create]) AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"]) AC_EXPAND_DIR(path_stdpath, "$sbindir:$bindir:/usr/sbin:/usr/bin:/sbin:/bin")
AS_IF([test "$heading" = "no"], [heading="<NONE>"]) AC_DEFINE_UNQUOTED(_PATH_STDPATH, "$path_stdpath", [Default PATH])
# Generate all files # Generate all files
AC_OUTPUT AC_OUTPUT
@@ -258,7 +235,7 @@ cat <<EOF
------------------ Summary ------------------ ------------------ Summary ------------------
$PACKAGE_NAME version $PACKAGE_VERSION $PACKAGE_NAME version $PACKAGE_VERSION
Prefix................: $prefix Prefix................: $prefix
Exec prefix...........: $eprefix Exec prefix...........: `eval echo $exec_prefix`
Sysconfdir............: `eval echo $sysconfdir` Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir` Localstatedir.........: `eval echo $localstatedir`
Finit config file.....: $conf_path Finit config file.....: $conf_path
@@ -277,14 +254,12 @@ Behavior:
Optional features: Optional features:
Install doc/..........: $enable_doc Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib Install contrib/......: $enable_contrib
Built-in keventd......: $with_keventd Built-in watchdogd....: $enable_watchdog
Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate Built-in logrotate....: $enable_logrotate
Skip fsck check.......: $enable_fastboot Scripting tool logit..: $enable_logit
Run fsck fix mode.....: $enable_fsckfix Emergency shell.......: $enable_emergency_shell
Redirect output.......: $enable_redirect Fallback shell........: $enable_fallback_shell
Default hostname......: $hostname Default hostname......: $hostname
Default group.........: $group
Default runlevel......: $runlevel Default runlevel......: $runlevel
------------- Compiler version -------------- ------------- Compiler version --------------
+1 -1
View File
@@ -1,5 +1,5 @@
SUBDIRS = finit.d SUBDIRS = finit.d
docsdir := @docdir@/contrib/alpine docsdir := @docdir@/contrib/alpine
docs_DATA = README.md finit.conf build.sh install.sh rc.local docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
+1
View File
@@ -0,0 +1 @@
3.6.2
+2 -2
View File
@@ -17,8 +17,8 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--prefix=/usr --exec-prefix= \ --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --enable-watchdog \
--with-keventd --with-heading="Alpine Linux 3.13"
echo echo
echo "*** Building ..." echo "*** Building ..."
+1 -17
View File
@@ -5,22 +5,6 @@
# Default is 2 # Default is 2
#runlevel 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
#cgroup init cpu.weight:100
#cgroup user cpu.weight:100
#cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+2 -1
View File
@@ -1,4 +1,5 @@
docsdir := @docdir@/contrib/alpine/finit.d/available docsdir := @docdir@/contrib/alpine/finit.d/available
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \ docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf lxdm.conf modules.conf ntpd.conf klogd.conf syslog.conf \
watchdog.conf
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
@@ -1,5 +1,4 @@
# Consoles to start getty on when system is up # Consoles to start getty on when system is up
tty [12345] /dev/ttyS0 noclear
tty [12345] /dev/tty1 noclear nowait tty [12345] /dev/tty1 noclear nowait
tty [2345] /dev/tty2 noclear nowait tty [2345] /dev/tty2 noclear nowait
tty [2345] /dev/tty3 noclear nowait tty [2345] /dev/tty3 noclear nowait
@@ -0,0 +1,2 @@
# Start klogd as soon as possible, after syslogd, should always run
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
@@ -4,4 +4,3 @@
#module evdev #module evdev
#module mousedev #module mousedev
#module 8139cp #module 8139cp
module softdog
@@ -1,4 +1,2 @@
# Start syslogd as soon as possible, should always run # Start syslogd as soon as possible, should always run
# Start klogd as soon as possible, but after syslogd
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
+2 -3
View File
@@ -8,10 +8,9 @@ if [ "x`id -u`" != "x0" ]; then
fi fi
# Adjust base directory # Adjust base directory
grep -q alpine Makefile 2>/dev/null if [ -e alpine-release ]; then
if [ $? -eq 0 ]; then
cd ../.. cd ../..
elif [ ! -e configure.ac ]; then elif [ ! -e autogen.sh ]; then
echo "*** Please run this script from the Finit base directory." echo "*** Please run this script from the Finit base directory."
exit 1 exit 1
fi fi
+1 -1
View File
@@ -4,5 +4,5 @@
# #
# Each line is run in sequence. # Each line is run in sequence.
# #
# NOTE: Remember to set the executable bit! # NOTE: Remeber to set the executable bit!
# #
+2 -2
View File
@@ -1,4 +1,4 @@
SUBDIRS = finit.d SUBDIRS = finit.d grub.d
docsdir := @docdir@/contrib/debian docsdir := @docdir@/contrib/debian
docs_DATA = README.md finit.conf build.sh install.sh docs_DATA = README.md finit.conf os-release build.sh install.sh
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
+7 -20
View File
@@ -20,42 +20,29 @@ tools pre-systemd work as intended.
root@debian:~# apt install initscripts console-setup root@debian:~# apt install initscripts console-setup
> **Note:** as of Debian 11 (Bullseye), libuev and libite are part of
> the main section of Debian. So just install the -dev packages :)
The following script can then be used to configure, build, install and The following script can then be used to configure, build, install and
set up your system to run Finit: set up your system to run Finit:
user@debian:~/finit$ contrib/debian/build.sh user@debian:~/finit$ contrib/debian/build.sh
However, since `/sbin/init` already exists on your system, the script However, since `/sbin/init` already exists on your system, the script
creates another entry in your GRUB config by updating `$SUPPORTED_INITS` creates another entry in your GRUB config, in `/etc/grub.d/40_custom`,
in `/etc/grub.d/10_inux` and run `update-grub`. On reboot you will find where `init=/sbin/finit` is added to your kernel line. It is of course
a (finit) entry in the "Advanced options for Debian GNU/Linux" section. also possible to change the default init to Finit, if you do you can
It is of course also possible to change the default init to Finit: remove the custom Grub entry:
user@debian:~/finit# cd /sbin user@debian:~/finit# cd /sbin
user@debian:/sbin# sudo mv init oldinit user@debian:/sbin# sudo rm init
user@debian:/sbin# sudo ln -s finit init user@debian:/sbin# sudo ln -s finit init
Before rebooting, check the default [/etc/finit.conf](finit.conf) and Before rebooting, check the default [/etc/finit.conf](finit.conf) and
`/etc/finit.d/*.conf` files. The build + install script above provides `/etc/finit.d/*.conf` files. The build + install script above provides
a few sample `.conf` files. See `initctl ls` after boot for a list of a few sample `.conf` files. See `initctl list` after boot for a list of
enabled and available services, you can then use `enable` and `disable` enabled and available services.
comands to `initctl`, followed by `reload` to activate your changes.
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
and initialization like keyboard language etc. and initialization like keyboard language etc.
> **NOTE:** X Window system, you may need to `sudo apt install elogind`
> (Bullseye and later), followed by `initctl reload` to activate it (it
> is enabled by default), and logout/login again. The elogind daemon
> ensures a regular non-root user can start and interact with an X
> session, otherwise keyboard and mouse won't work. When you're happy,
> you can enable the lightdm.conf, change the default runlevel to 3, and
> presto! you have a desktop again.
Have fun! Have fun!
/Joachim ツ /Joachim ツ
+3 -2
View File
@@ -25,8 +25,9 @@ echo
--prefix=/usr --exec-prefix= \ --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-keventd \ --enable-alsa-utils-plugin \
--with-random-seed=/var/lib/urandom/random-seed --with-random-seed=/var/lib/urandom/random-seed \
--with-heading="Debian GNU/Linux"
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo echo
+1 -17
View File
@@ -5,22 +5,6 @@
# Default is 2 # Default is 2
#runlevel 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
#cgroup init cpu.weight:100
#cgroup user cpu.weight:100
#cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+2 -4
View File
@@ -1,9 +1,7 @@
SUBDIRS = available SUBDIRS = available
docsdir := @docdir@/contrib/debian/finit.d docsdir := @docdir@/contrib/debian/finit.d
d_LINKS = acpid.conf apparmor.conf avahi-daemon.conf \ d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
bluetooth.conf console-setup.conf elogind.conf \ modules.conf rsyslogd.conf sshd.conf
getty.conf keyboard-setup.conf modules.conf \
plymouth-quit.conf rsyslogd.conf sshd.conf
install-exec-hook: install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available @$(MKDIR_P) $(DESTDIR)$(docsdir)/available
+3 -6
View File
@@ -1,8 +1,5 @@
docsdir := @docdir@/contrib/debian/finit.d/available docsdir := @docdir@/contrib/debian/finit.d/available
docs_DATA = acpid.conf apparmor.conf avahi-daemon.conf bluetooth.conf \ docs_DATA = acpid.conf console-setup.conf getty.conf keyboard-setup.conf \
console-setup.conf elogind.conf getty.conf \ lightdm.conf modules.conf rsyslogd.conf sntpd.conf \
keyboard-setup.conf lightdm.conf modules.conf sntpd.conf \ sshd.conf syslogd.conf xdm.conf
plymouth-quit.conf rsyslogd.conf sshd.conf syslogd.conf \
xdm.conf
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
@@ -1 +0,0 @@
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
@@ -1,2 +0,0 @@
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
@@ -1 +0,0 @@
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
@@ -1,3 +0,0 @@
# To ignore errors like "org.freedesktop.hostname1", update the env file with
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
@@ -1 +0,0 @@
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
@@ -1,4 +0,0 @@
# On some systems, bootstrap can be really quick and cause elogind
# to not start before a user has managed to log in. To prevent
# this, add <pid/elogind> as condition to your TTYs in getty.conf
service [2345] <pid/syslogd> /lib/elogind/elogind -- Login manager
+2 -5
View File
@@ -1,7 +1,4 @@
# Consoles to start getty on when system is up # Consoles to start getty on when system is up
tty [12345] /dev/tty1 linux noclear nowait tty [12345] /dev/tty1 linux noclear nowait
tty [2345] /dev/tty2 linux nowait tty [2345] /dev/tty2 linux noclear nowait
tty [2345] /dev/tty3 linux nowait tty [2345] /dev/tty3 linux noclear nowait
tty [2345] /dev/tty4 linux nowait
tty [2345] /dev/tty5 linux nowait
tty [2345] /dev/tty6 linux nowait
@@ -1,2 +0,0 @@
# Need to stop plymouth, or it'll block stdin on tty1
task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
menuentry '$MENU with Finit' --class $TYPE --class gnu-linux --class gnu --class os --user {
insmod gzio
insmod part_msdos
insmod ext2
search --no-floppy --fs-uuid --set=root $BOOT
linux /vmlinuz root=UUID=$ROOT ro quiet init=/sbin/finit
initrd /initrd.img
}
+3
View File
@@ -0,0 +1,3 @@
docsdir := @docdir@/contrib/debian/grub.d
docs_DATA = 40_custom
EXTRA_DIST = $(docs_DATA)
+8 -14
View File
@@ -8,10 +8,10 @@ if [ "x`id -u`" != "x0" ]; then
fi fi
# Adjust base directory # Adjust base directory
grep -q debian Makefile 2>/dev/null grep -q Debian os-release 2>/dev/null
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
cd ../.. cd ../..
elif [ ! -e configure.ac ]; then elif [ ! -e autogen.sh ]; then
echo "*** Please run this script from the Finit base directory." echo "*** Please run this script from the Finit base directory."
exit 1 exit 1
fi fi
@@ -23,7 +23,7 @@ echo "/sbin/finit - PID 1"
echo "/lib/finit/plugins/* - All enabled Finit plugins" echo "/lib/finit/plugins/* - All enabled Finit plugins"
echo "/etc/finit.conf - Finit configuration file" echo "/etc/finit.conf - Finit configuration file"
echo "/etc/finit.d/ - Finit services" echo "/etc/finit.d/ - Finit services"
echo "/etc/grub.d/10_linux - Add Finit to Grub's SUPPORTED_INITS" echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader"
echo echo
read -p "Do you want to continue (y/N)? " yorn read -p "Do you want to continue (y/N)? " yorn
echo echo
@@ -38,18 +38,12 @@ if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
cp -va finit.d /etc/ cp -va finit.d /etc/
echo "*** Setting up a GRUB boot entry ..." echo "*** Setting up a GRUB boot entry ..."
fn=/etc/grub.d/10_linux MENU=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/menuentry '\([^']*\).*/\1/"`
if [ -e $fn ]; then TYPE=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/.*' --class \([a-z]*\).*/\1/"`
if `grep SUPPORTED_INITS $fn |head -1 |grep -q finit`; then BOOT=`grep set=root /boot/grub/grub.cfg |head -1 |sed 's/.* \([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
echo "Already installed, done." ROOT=`grep root=UUID /boot/grub/grub.cfg |head -1 |sed 's/.*UUID=\([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
else cat grub.d/40_custom | sed "s,\$BOOT,$BOOT,;s,\$ROOT,$ROOT,;s,\$TYPE,$TYPE,;s,\$MENU,$MENU," >/etc/grub.d/40_custom
echo "Adding Finit to list of SUPPORTED_INITS ..."
sed -i 's/SUPPORTED_INITS="[^"]*/& finit:\/sbin\/finit/' $fn
update-grub update-grub
fi
else
echo "Cannot find $fn, you'll have to set up your bootloader on your own."
fi
echo echo
echo "*** Done" echo "*** Done"
+8
View File
@@ -0,0 +1,8 @@
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=debian
HOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
+2 -37
View File
@@ -8,18 +8,6 @@ module evdev
module loop module loop
module psmouse module psmouse
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9700 cpu.max:50000
# Example extra cgroup
cgroup maint cpu.weight:100
# Runlevel to start after bootstrap, runlevel 'S' # Runlevel to start after bootstrap, runlevel 'S'
runlevel 2 runlevel 2
@@ -28,21 +16,13 @@ network service networking start
# Max file size for each log file: 100 kiB, rotate max 4 copies: # Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz # log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size:100k count:4 log size=100k count=4
# Virtual consoles to start getty on # Virtual consoles to start getty on
tty /dev/tty1 tty /dev/tty1
tty /dev/tty2 tty /dev/tty2
tty /dev/tty3 tty /dev/tty3
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
# Alternative method instead of runparts # Alternative method instead of runparts
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap #task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon #task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
@@ -50,24 +30,9 @@ task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
#run [2] /etc/init.d/networking start -- Start networking #run [2] /etc/init.d/networking start -- Start networking
# Services to be monitored and respawned as needed # Services to be monitored and respawned as needed
# klgod and syslogd are placed in the maint cgroup
cgroup.maint
service [2345] <pid/syslogd> klogd -n -- Kernel logging server service [2345] <pid/syslogd> klogd -n -- Kernel logging server
service [2345] syslogd -n -- Syslog server service [2345] syslogd -n -- Syslog server
service [3] gdm -- GNOME Display Manager
# gdm in the system cgroup (default when read from it's own .conf file)
cgroup.system
service [3] cgroup:cpu.weight:250,mem.max:10M gdm -- GNOME Display Manager
# Start SSH daemon with opts from /etc/default/ssh (if available), move
# to user cgroup with cpu.weight:250. Default for services is system
# group. Default weight in user group is 100 (above). Max length of
# cgroup argument is (currently) 63 chars.
#
# Notice alternative syntax where current cgroup is only set for this
# particular service.
service [2345] cgroup.user:cpu.weight:250,mem.max:10M env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
# The BusyBox ntpd does not use syslog when running in the foreground # The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The # So we use this trick to redirect stdout/stderr to a log file. The
@@ -1,75 +0,0 @@
In Finit issue #196 we were reminded [1] of a missing inotify flag in
Linux older kernels, IN_MASK_CREATE. Finit makes use of this internally
for monitoring PID conditions, and more. This is a central mechanism to
synchronize services at boot and reload at runtime.
The following patch for Linux 4.9.0, was provided by Andy Savage for
other people that may want to run Finit on slightly older kernels.
[1]: https://github.com/troglobit/finit/issues/196
Index: fs/notify/inotify/inotify_user.c
===================================================================
--- fs.orig/notify/inotify/inotify_user.c
+++ fs/notify/inotify/inotify_user.c
@@ -522,6 +522,7 @@ static int inotify_update_existing_watch
__u32 old_mask, new_mask;
__u32 mask;
int add = (arg & IN_MASK_ADD);
+ int create = (arg & IN_MASK_CREATE);
int ret;
mask = inotify_arg_to_mask(arg);
@@ -529,6 +530,8 @@ static int inotify_update_existing_watch
fsn_mark = fsnotify_find_inode_mark(group, inode);
if (!fsn_mark)
return -ENOENT;
+ else if (create)
+ return -EEXIST;
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
@@ -726,6 +729,10 @@ SYSCALL_DEFINE3(inotify_add_watch, int,
if (unlikely(!f.file))
return -EBADF;
+ /* IN_MASK_ADD and IN_MASK_CREATE don't make sense together */
+ if (unlikely((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE)))
+ return -EINVAL;
+
/* verify that this is indeed an inotify instance */
if (unlikely(f.file->f_op != &inotify_fops)) {
ret = -EINVAL;
@@ -814,7 +821,7 @@ static int __init inotify_user_setup(voi
BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
- BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
+ BUG_ON(hweight32(ALL_INOTIFY_BITS) != 22);
inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC);
Index: include/linux/inotify.h
===================================================================
--- include.orig/linux/inotify.h
+++ include/linux/inotify.h
@@ -17,6 +17,6 @@ extern struct ctl_table inotify_table[];
IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | \
IN_Q_OVERFLOW | IN_IGNORED | IN_ONLYDIR | \
IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_MASK_ADD | \
- IN_ISDIR | IN_ONESHOT)
+ IN_MASK_CREATE | IN_ISDIR | IN_ONESHOT)
#endif /* _LINUX_INOTIFY_H */
Index: include/uapi/linux/inotify.h
===================================================================
--- include.orig/uapi/linux/inotify.h
+++ include/uapi/linux/inotify.h
@@ -52,6 +52,7 @@ struct inotify_event {
#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */
#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */
#define IN_EXCL_UNLINK 0x04000000 /* exclude events on unlinked objects */
+#define IN_MASK_CREATE 0x10000000 /* only create watches */
#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */
#define IN_ISDIR 0x40000000 /* event occurred against dir */
#define IN_ONESHOT 0x80000000 /* only send event once */
+1 -1
View File
@@ -1,4 +1,4 @@
SUBDIRS = finit.d SUBDIRS = finit.d
docsdir := @docdir@/contrib/void docsdir := @docdir@/contrib/void
docs_DATA = README.md finit.conf build.sh install.sh rc.local docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
+2 -2
View File
@@ -17,8 +17,8 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--prefix=/usr --exec-prefix= \ --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --enable-watchdog \
--with-keventd --with-heading="Void Linux"
echo echo
echo "*** Building ..." echo "*** Building ..."
-17
View File
@@ -5,22 +5,5 @@
# Default is 2 # Default is 2
#runlevel 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
#cgroup init cpu.weight:100
#cgroup user cpu.weight:100
#cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+2 -2
View File
@@ -1,5 +1,5 @@
docsdir := @docdir@/contrib/void/finit.d/available docsdir := @docdir@/contrib/void/finit.d/available
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \ docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
keymap.conf lxdm.conf modules.conf ntpd.conf \ keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
sshd.conf syslog.conf uuidd.conf sshd.conf syslogd.conf uuidd.conf
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
@@ -0,0 +1,2 @@
# Start klogd as soon as possible, should always run
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
@@ -4,4 +4,3 @@
#module evdev #module evdev
#module mousedev #module mousedev
#module 8139cp #module 8139cp
module softdog
@@ -1,4 +0,0 @@
# Start syslogd as soon as possible, should always run
# Start klogd as soon as possible, but after syslogd
service [S12345] syslogd -n -- Syslog daemon
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
@@ -0,0 +1,2 @@
# Start syslogd as soon as possible, should always run
service [S12345] syslogd -n -- Syslog daemon
+2 -3
View File
@@ -8,10 +8,9 @@ if [ "x`id -u`" != "x0" ]; then
fi fi
# Adjust base directory # Adjust base directory
grep -q void Makefile 2>/dev/null if [ -e xbps ]; then
if [ $? -eq 0 ]; then
cd ../.. cd ../..
elif [ ! -e configure.ac ]; then elif [ ! -e autogen.sh ]; then
echo "*** Please run this script from the Finit base directory." echo "*** Please run this script from the Finit base directory."
exit 1 exit 1
fi fi
+1 -1
View File
@@ -4,4 +4,4 @@
# #
# Each line is run in sequence. # Each line is run in sequence.
# #
# NOTE: Remember to set the executable bit! # NOTE: Remeber to set the executable bit!
View File
+6 -18
View File
@@ -19,31 +19,19 @@ Some of these are also registered in the issue tracker on GitHub.
* Add new (complimentary) .svc file format to slowly migrate away * Add new (complimentary) .svc file format to slowly migrate away
from the very terse one-liner format currently used from the very terse one-liner format currently used
* Allow running as non-pid1 => read .conf and RCSD form cmdline * Allow running as non-pid1 => read .conf and RCSD form cmdline
* When a process dies, and Finit does not restart it, we should collect
a `siginfo_t` from the SIGCHLD signal and supply to the user in case
`initctl status <process>` is called to debug the issue.
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task * Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
As of Finit v4.1 SIGPWR generates <sys/power/fail> condition and it
is up to a task to check why and take appropriate action.
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest, * Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task, i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling. e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Write man pages for finit and `finit.conf`, steal from the excellent
As of Finit v4.1 SIGINT generates <sys/key/ctrlaltdel>, but there is `pimd` man pages ...
no check if the signal actually originates from the kernel. To get * Add support for timed shutdown in Finit, including cancelled shutdown
this verification, a new libuEv release is needed. As of June 6 -21
that functionality is not yet released.
Initctl
-------
* Add support for timed shutdown in Finit, including cancelled shutdown:
shutdown -h 03:35 "foobar was here"
* Add support for JSON output, or similar, from `initctl show`, e.g. * Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json` `initctl show --json`
* Add support for `-s` to redirect all output to syslog, for scripting
Init Init
+14 -15
View File
@@ -9,16 +9,12 @@ Bootstrap
6. Load all `.so` plugins 6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab` 7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type 8. Populate `/dev` using either udev or mdev, depending on system type
9. Parse `/etc/finit.conf` 9. Parse `/etc/finit.conf` and all `/etc/finit.d/*.conf` files
10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This 10. Start built-in watchdog, if enabled
means any WDT that requires a kernel module need to be either 11. Set hostname
compiled into the kernel, or insmod'ed in `/etc/finit.conf`
11. Load all `/etc/finit.d/*.conf` files and set hostname
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro` 12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
13. Call 1st level hooks, `HOOK_ROOTFS_UP` 13. Call 1st level hooks, `HOOK_ROOTFS_UP`
14. Mount all file systems listed in `/etc/fstab` and swap, if available. 14. Mount all file systems listed in `/etc/fstab` and swap, if available
On mount error `HOOK_MOUNT_ERROR` is called. After mount, regardless
of error, `HOOK_MOUNT_POST` is called
15. Enable SysV init signals 15. Enable SysV init signals
16. Call 2nd level hooks, `HOOK_BASEFS_UP` 16. Call 2nd level hooks, `HOOK_BASEFS_UP`
17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin 17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin
@@ -38,15 +34,18 @@ Bootstrap
24. Call 4th level hooks, `HOOK_SVC_UP` 24. Call 4th level hooks, `HOOK_SVC_UP`
25. Call `/etc/rc.local`, if it exists and is an executable shell script 25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP` 26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs 27. Start all configured TTYs, or a fallback shell on `/dev/console`
In (19) and (23) tasks and services defined in `/etc/finit.conf` and In (19) and (22) tasks and services defined in `/etc/finit.conf` and
`/etc/finit.d/*.conf` are started. `/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task`
stanzas are started in parallel and `run` in sequence, and in the order
listed. Hence, to emulate a SysV `/etc/init.d/rcS` one could write a
long `finit.conf` with only `run` statements.
Notice the seven hook points that are called at various point in the Notice the five hook points that are called at various point in the
bootstrap process. This is where plugins can extend the boot in any bootstrap process. This is where plugins can extend the boot in any way
way they please. There are other hook points available, for more on they please. There are other hook points, e.g. `HOOK_MOUNT_ERROR`, for
this, see [plugins.md](plugins.md). more on this see [plugins.md](plugins.md).
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
USB stick, convert/copy its contents to the system's `/etc/` directory, USB stick, convert/copy its contents to the system's `/etc/` directory,
+14 -21
View File
@@ -27,7 +27,7 @@ optional plugins to enable. It depends on two external libraries:
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
The configure script checks for all dependencies, including the correct The configure script checks for all dependencies, including the correct
version of the above mentioned libraries. Currently required versions: version of the above mentioned libraries. Currently requried versions:
- libite v2.2.0 - libite v2.2.0
- libuEv v2.2.0 - libuEv v2.2.0
@@ -53,10 +53,6 @@ Below are a few of the main switches to configure:
built-ins (.o files) and all external libraries, except the C library built-ins (.o files) and all external libraries, except the C library
will be linked statically. will be linked statically.
* `--enable-kernel-cmdline`: Enable Finit pre-4.1 parsing of init args from
`/proc/cmdline`, this is not recommended since Finit may be running as the
init for container apps that can see the host's `/proc` filesystem
* `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin. * `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin.
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin. * `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
@@ -74,8 +70,8 @@ Example
First, unpack the archive: First, unpack the archive:
```shell ```shell
$ tar xf finit-4.1.tar.gz $ tar xf finit-3.0.tar.xz
$ cd finit-4.1/ $ cd finit-3.0/
``` ```
Then configure, build and install: Then configure, build and install:
@@ -83,7 +79,7 @@ Then configure, build and install:
```shell ```shell
$ ./configure --prefix=/usr --exec-prefix= \ $ ./configure --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--with-keventd --with-watchdog --with-heading="Alpine Linux 3.13" --with-hostname=alpine
$ make $ make
. .
. .
@@ -91,13 +87,13 @@ $ make
$ DESTDIR=/tmp/finit make install $ DESTDIR=/tmp/finit make install
``` ```
In this example the [finit-4.1.tar.gz][1] archive is unpacked to the In this example the [finit-3.0.tar.xz][1] archive is unpacked to the
user's home directory, configured, built and installed to a temporary user's home directory, configured, built and installed to a temporary
staging directory. The environment variable `DESTDIR` controls the staging directory. The environment variable `DESTDIR` controls the
destination directory when installing, very useful for building binary destination directory when installing, very useful for building binary
standalone packages. standalone packages.
Finit 4.1 and later can detect if it runs on an embedded system, or a Finit 3.0 and later can detect if it runs on an embedded system, or a
system that use BusyBox tools instead of udev & C:o. On such systems system that use BusyBox tools instead of udev & C:o. On such systems
`mdev` instead of `udev` is used. However, remember to also change the `mdev` instead of `udev` is used. However, remember to also change the
Linux config to: Linux config to:
@@ -157,24 +153,21 @@ kernel messages and add `debug` to enable Finit debug messages.
append="init=/sbin/finit debug" append="init=/sbin/finit debug"
``` ```
To debug startup issues, in particular issues with getty/login, add To debug startup issues, in particular issues with getty/login, try
the following to your Finit .conf file: `configure --enable-fallback-shell`. When no TTYs are detected, and
Finit is configured with this option, Finit will try to start a bare
tty [12345789] notty noclear `/bin/sh` on the boot console. Remember, this is only for debugging
and would leave your production system potentially wide open.
The `notty` option ensures reusing the stdin/stdout set up by the
kernel. Remember, this is only for debugging and would leave your
production system potentially wide open.
There is also a rescue shell available, in case Finit crashes and the There is also a rescue shell available, in case Finit crashes and the
kernel usually reboots: `configure --enable-emergency-shell`. However, kernel usually reboots: `configure --enable-emergency-shell`. However,
the behavior of Finit is severely limited when this is enabled, so use the behavior of Finit is severely limited when this is enabled, so use
it only for debugging start up issues when Finit crashes. it only for debugging start up issues when Finit crashes.
**NOTE:** Neither of these options should be enabled on production **NOTE:** Neither of these two configure options should be enabled on
systems since they can potentially give a user root access. production systems since they can potentially give a user root access.
[1]: ftp://troglobit.com/finit/finit-4.1.tar.gz [1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
[libuEv]: https://github.com/troglobit/libuev [libuEv]: https://github.com/troglobit/libuev
[libite]: https://github.com/troglobit/libite [libite]: https://github.com/troglobit/libite
+6 -10
View File
@@ -6,8 +6,6 @@ passed on the Linux kernel command line. Not limited to Finit.
The `bool` setting is one of `on, off, true false, 1, 0`. The `bool` setting is one of `on, off, true false, 1, 0`.
> **NOTE:** remember to use `--` to separate kernel parameters from
> parameters to init. E.g., `init=/sbin/finit -- finit.debug rescue`
* `debug`: Enable kernel debug. Debug messages are printed to the * `debug`: Enable kernel debug. Debug messages are printed to the
console until Finit starts up. console until Finit starts up.
@@ -32,19 +30,17 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
* `quiet`: Suppress kernel logging to console, except for warnings and * `quiet`: Suppress kernel logging to console, except for warnings and
errors. errors.
* `rescue`: Start rescue/maintenance mode. If your system comes with * `rescue`: Start Finit rescue mode; no network, no `fsck` of file
the bundled `sulogin` program (Finit, or from util-linux/Busybox), systems in `/etc/fstab`, no `/etc/rc.local`, no `runparts`. The
you will be given a root login to a maintenance shell. However, if configuration is read from `/lib/finit/rescue.conf`, and can be
`sulogin` is missing, the file `/lib/finit/rescue.conf` is read and modified by the operator.
the system booted in a limited fallback mode. See [config.md][]
for more information.
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to * `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
debug problems with the regular runlevel. All services and TTYs in debug problems with the regular runlevel. All services and TTYs in
`[1]` will be started, so a `tty [1] @console nologin` configuration `[1]` will be started, so a `tty [1] @console nologin` configuration
presents you with a root console without login. presents you with a root console without login.
For more on kernel boot parameters, see the man page [bootparam(7)][]. For more on kernel boot paramaters, see the man page [bootparam(7)][].
[config.md][]: config.md#rescue-mode
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html [bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
+8 -41
View File
@@ -45,31 +45,7 @@ being created.
Triggering Triggering
---------- ----------
Conditions are mainly triggered (asserted) by built-in plugins, e.g., Conditions can only be triggered by built-in plugins.
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static
or one-shot conditions. They live in the `<usr/...` namespace and are
constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands:
initctl cond set foo
initctl cond clear foo
The purpose of user-defined conditions is to be able to start or stop
services, or run/task jobs, on external site-dependent stimuli.
Example:
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
For convenience, prefixing with `usr/` is allowed, but any other slashes
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
the same as above, can also be achieved like this:
initctl cond set usr/foo
initctl cond clear usr/foo
Conditions retain their current state until the next reconfiguration or Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the runlevel change. At that point all set conditions transition into the
@@ -81,21 +57,16 @@ unchanged) state of it.
> **Note:** For `pid/` conditions it is expected that services "touch" > **Note:** For `pid/` conditions it is expected that services "touch"
> or recreate their PID file on `SIGHUP`. > or recreate their PID file on `SIGHUP`.
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
Built-in Conditions Built-in Conditions
------------------- -------------------
Finit comes with a set of plugins for conditions: `pidfile`, `netlink`, Finit comes with a `pidfile` and `netlink` plugin. When enabled, the
`sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/` `pidfile` plugin watches `/var/run/` (recursively) for PID files created
(recursively) for PID files created by the monitored services, and sets by the monitored services, and sets a corresponding condition in the
a corresponding condition in the `pid/` namespace. Similarly, the `pid/` namespace. Similarily, the `netlink` plugin provides basic
`netlink` plugin provides basic conditions for when an interface is conditions for when an interface is brought up/down and when a default
brought up/down and when a default route (gateway) is set, in the `net/` route (gateway) is set, in the `net/` namespace.
namespace. The `sys` and `usr` plugins monitor are passive condition
monitors where the action is provided by `keventd`, signal handlers,
and in the case of `usr`, the end-user via the `initctl` tool
With the example listed above, finit does not start the `/sbin/netd` With the example listed above, finit does not start the `/sbin/netd`
daemon until `setupd` and `zebra` has started *and* created their PID daemon until `setupd` and `zebra` has started *and* created their PID
@@ -114,10 +85,6 @@ Built-in conditions:
- `net/<IFNAME>/exist` - `net/<IFNAME>/exist`
- `net/<IFNAME>/up` - `net/<IFNAME>/up`
- `net/<IFNAME>/running` - `net/<IFNAME>/running`
- `sys/pwr/ac`
- `sys/pwr/fail`
- `sys/key/ctrlaltdel`
- `usr/foo`
**Note:** `up` means administratively up, the interface flag `IFF_UP`. **Note:** `up` means administratively up, the interface flag `IFF_UP`.
`running` is the `IFF_RUNNING` flag, meaning operatively up. The `running` is the `IFF_RUNNING` flag, meaning operatively up. The
@@ -180,7 +147,7 @@ PID IDENT STATUS CONDITION (+ ON, ~ FLUX, - OFF)
Here we can see that `netd` is allowed to run since both its conditions Here we can see that `netd` is allowed to run since both its conditions
are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however
is not allowed to run since `net/vlan1/exist` condition is not satisfied. is not allowed to run since `net/vlan1/exist` condition is not satsifed.
As indicated by the `-`-prefix. As indicated by the `-`-prefix.
To fake interface `vlan1` suddenly appearing, and test what happens to To fake interface `vlan1` suddenly appearing, and test what happens to
+43 -276
View File
@@ -2,9 +2,7 @@ Configuration
============= =============
* [Introduction](#introduction) * [Introduction](#introduction)
* [Service Environment](#service-environment)
* [Service Wrapper Scripts](#service-wrapper-scripts) * [Service Wrapper Scripts](#service-wrapper-scripts)
* [Cgroups](#cgroups)
* [Configuration File Syntax](#configuration-file-syntax) * [Configuration File Syntax](#configuration-file-syntax)
* [Hostname](#hostname) * [Hostname](#hostname)
* [Kernel Modules](#kernel-modules) * [Kernel Modules](#kernel-modules)
@@ -21,12 +19,9 @@ Configuration
* [TTYs and Consoles](#ttys-and-consoles) * [TTYs and Consoles](#ttys-and-consoles)
* [Non-privileged Services](#non-privileged-services) * [Non-privileged Services](#non-privileged-services)
* [Redirecting Output](#redirecting-output) * [Redirecting Output](#redirecting-output)
* [Rescue Mode](#rescue-mode)
* [Limitations](#limitations) * [Limitations](#limitations)
* [/etc/finit.conf](#etcfinitconf) * [/etc/finit.conf](#etcfinitconf)
* [/etc/finit.d](#etcfinitd) * [/etc/finit.d](#etcfinitd)
* [Watchdog](#watchdog)
* [keventd](#keventd)
Introduction Introduction
@@ -40,16 +35,16 @@ Linux distributions -- each package can provide its own "script" file.
- `/etc/finit.d/*.conf`: snippets, usually one service per file - `/etc/finit.d/*.conf`: snippets, usually one service per file
Not all configuration directives are available in `/etc/finit.d/*.conf` Not all configuration directives are available in `/etc/finit.d/*.conf`
and some directives are only available at [bootstrap][], runlevel `S`, and some directives are only available at bootstrap, runlevel `S`, see
see the section [Limitations](#limitations) below for details. the section [Limitations](#limitations) below for details.
To add a new service, drop a `.conf` file in `/etc/finit.d` and run To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
`initctl reload`. (It is also possible to `SIGHUP` to PID 1, or call run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
`finit q`, but that has been deprecated with the `initctl` tool). Finit call `finit q`, but that has been deprecated with the `initctl` tool).
monitors all known active `.conf` files, so if you want to force a Finit monitors all known active `.conf` files, so if you want to force
restart of any service you can touch its corresponding `.conf` file in a restart of any service you can simply touch its corresponding `.conf`
`/etc/finit.d` and call `initctl reload`. Finit handle any and all file in `/etc/finit.d` and call `initctl reload`. Finit handle any
conditions and dependencies between services automatically. and all conditions and dependencies between services automatically.
It is also possible to drop `.conf` files in `/etc/finit.d/available/` It is also possible to drop `.conf` files in `/etc/finit.d/available/`
and use `initctl enable` to enable a service `.conf` file. This may be and use `initctl enable` to enable a service `.conf` file. This may be
@@ -59,7 +54,7 @@ files for a package and let the user decide when to enable a service.
On `initctl reload` the following is checked for all services: On `initctl reload` the following is checked for all services:
- If a service's `.conf` file has been removed, or its conditions are no - If a service's `.conf` file has been removed, or its conditions are no
longer satisfied, the service is stopped. longer satisifed, the service is stopped.
- If the file is modified, or a service it depends on has been reloaded, - If the file is modified, or a service it depends on has been reloaded,
the service is reloaded (stopped and started). the service is reloaded (stopped and started).
- If a new service is added it is automatically started — respecting - If a new service is added it is automatically started — respecting
@@ -133,67 +128,6 @@ example employs a wrapper script in `/etc/start.d`.
> instead of sending SIGHUP at restart/reload events. > instead of sending SIGHUP at restart/reload events.
Cgroups
-------
There are three major cgroup configuration directives:
1. Global top-level group: init, system, user, or a custom group
2. Selecting a top-level group for a set of run/task/services
3. Per run/task/service limits
Top-level group configuration.
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9800
Adding an extra cgroup `maint/` will require you to adjust the weight of
the above three. We leave `init/` and `user/` as-is reducing weight of
`system/` to 9700.
cgroup system cpu.weight:9700
# Example extra cgroup 'maint'
cgroup maint cpu.weight:100
By default, the `system/` cgroup is selected for almost everything. The
`init/` cgroup is reserved for PID 1 itself and its closest relatives.
The `user/` cgroup is for local TTY logins spawned by getty.
To select a different top-level cgroup, e.g. `maint/`, one can either
define it for a group of run/task/service directives in a `.conf` or per
each stanza:
cgroup.maint
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
or
service [...] <...> cgroup.maint /path/to/foo args -- description
The latter form also allows per-stanza limits on the form:
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
Notice the comma separation and the `mem.` exception to the rule: every
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no filtering,
except for expanding the shorthand `mem.` to `memory.`, if the file is
not available, either the cgroup controller is not available in your
Linux kernel, or the name is misspelled.
> Linux cgroups and details surrounding values are not explained in the
> Finit documentation. The Linux admin-guide cover this well:
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
Configuration File Syntax Configuration File Syntax
------------------------- -------------------------
@@ -206,7 +140,7 @@ the contents of that file is used.
Deprecated. We recommend using `/etc/hostname` instead. Deprecated. We recommend using `/etc/hostname` instead.
> **Note:** only read and executed in runlevel S ([bootstrap][]). > **Note:** only read and executed in runlevel S (bootstrap).
### Kernel Modules ### Kernel Modules
@@ -224,7 +158,7 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS" $MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
> **Note:** only read and executed in runlevel S ([bootstrap][]). > **Note:** only read and executed in runlevel S (bootstrap).
### Networking ### Networking
@@ -237,7 +171,7 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
or `/etc/network/interfaces` if you have a system with `ifupdown`, like or `/etc/network/interfaces` if you have a system with `ifupdown`, like
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system. Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
> **Note:** only read and executed in runlevel S ([bootstrap][]). > **Note:** only read and executed in runlevel S (bootstrap).
### Resource Limits ### Resource Limits
@@ -271,13 +205,13 @@ stanzas can share the same rlimits if they are in the same .conf.
**Syntax:** `runlevel <N>` **Syntax:** `runlevel <N>`
The system runlevel to go to after [bootstrap][] (S) has completed. `N` Defines the system runlevel to go to after bootstrap (S) has completed.
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for N is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt. halt.
Default: 2 Default: 2
> **Note:** only read and executed in runlevel S ([bootstrap][]). > **Note:** only read and executed in runlevel S (bootstrap).
### One-shot Commands (sequence) ### One-shot Commands (sequence)
@@ -332,11 +266,8 @@ by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
**Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description` **Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Finit tries to restart services that die, by exits prematurely. Finit tries to restart services that die 10 times
default 10 times before giving up and marking them as *crashed*. before giving up, then you have to `initctl restart NAME` it manually.
After which they have to be manually restarted with `initctl restart
NAME`. The limits controling this are configurable, see the options
below.
For daemons that support it, we recommend appending `--foreground`, For daemons that support it, we recommend appending `--foreground`,
`--no-background`, `-n`, `-F`, or similar command line argument to `--no-background`, `-n`, `-F`, or similar command line argument to
@@ -393,21 +324,6 @@ optional `name` argument:
name:<service-name> name:<service-name>
As mentioned previously, services are automatically restarted, this is
configurable with the following options:
* `restart:NUM` -- number of times Finit tries to restart a crashing
service, default: 10. When this limit is reached the service is
marked *crashed* and must be restarted manually with `initctl`
* `restart_sec:SEC` -- number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used
* `norestart` -- dont restart on failures, same as `restart:0`
* `oncrash:reboot` -- when all retries have failed, and the service
has *crashed*, if this option is set the system is rebooted.
Note, future releases may include other `oncrash:` actions
When stopping a service (run/task/sysv/service), either manually or When stopping a service (run/task/sysv/service), either manually or
when moving to another runlevel, Finit starts by sending `SIGTERM`, to when moving to another runlevel, Finit starts by sending `SIGTERM`, to
allow the process to shut down gracefully. If the process has not allow the process to shut down gracefully. If the process has not
@@ -417,28 +333,6 @@ process using a different signal, use the option `halt:SIGNAL`, e.g.,
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
sending `SIGKILL`. sending `SIGKILL`.
Services support `pre:script` and `post:script` actions as well. These
run as the same `@USER:GROUP` as the service itself, with any `env:file`
sourced. The scripts must use an absolute path, but are executed from
the `$HOME` of the given user. The scripts are not called with any
argument (currently), but both get the `SERVICE_IDENT=foo` environment
variable set. Here `foo` denotes the identity of the service, which if
there are multiple services named `foo`, may be `foo:1`, or any unique
identifier specified in the .conf file. The `post:script` is called
with an additional set of environment variables:
- `EXIT_CODE=[exited,signal]`: set to one of `exited` or `signal`
- `EXIT_STATUS=[num,SIGNAME]`: set to one of exit status code from
the program, if it exited normally, or the signal name (`HUP`,
`TERM`, etc.) if it exited due to signal
The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
> **Note:** the `pre:script` _must_ be idempotent, because a service
> can transition between READY and HALTED states any number
> of times before going to RUNNING.
### Run-parts Scripts ### Run-parts Scripts
@@ -447,7 +341,7 @@ SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
Call [run-parts(8)][] on `DIR` to run start scripts. All executable Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order. files, or scripts, in the directory are called, in alphabetic order.
The scripts in this directory are executed at the very end of runlevel The scripts in this directory are executed at the very end of runlevel
`S`, [bootstrap][]. `S`, bootstrap.
It can be beneficial to use `S01name`, `S02othername`, etc. if there It can be beneficial to use `S01name`, `S02othername`, etc. if there
is a dependency order between the scripts. Symlinks to existing is a dependency order between the scripts. Symlinks to existing
@@ -457,10 +351,7 @@ Similar to the `/etc/rc.local` shell script, make sure that all your
services and programs either terminate or start in the background or services and programs either terminate or start in the background or
you will block Finit. you will block Finit.
> **Note:** only read and executed in runlevel S ([bootstrap][]). > **Note:** only read and executed in runlevel S (bootstrap).
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
### Including Finit Configs ### Including Finit Configs
@@ -486,22 +377,15 @@ size limit is reached.
### TTYs and Consoles ### TTYs and Consoles
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]` **Syntax:** `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]` `tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
`tty [LVLS] <COND> [notty] [rescue]`
The first variant of this option uses the built-in getty on the given The first variant of this option uses the built-in getty on the given
TTY device DEV, in the given runlevels. DEV may be the special keyword TTY device DEV, in the given runlevels. The DEV may be the special
`@console`, which is expanded from `/sys/class/tty/console/active`, keyword `@console`, or `console`, useful on embedded systems.
useful on embedded systems.
The default baud rate is 0, i.e., keep kernel default. The default baud rate is 0, i.e., keep kernel default.
> The `tty` stanza inherits runlevel, condition (and other feature)
> parsing from the `service` stanza. So TTYs can run in one or many
> runlevels and depend on any condition supported by Finit. This is
> useful e.g. to depend on `<pid/elogind>` before starting a TTY.
**Example:** **Example:**
tty [12345] /dev/ttyAMA0 115200 noclear vt220 tty [12345] /dev/ttyAMA0 115200 noclear vt220
@@ -509,17 +393,8 @@ The default baud rate is 0, i.e., keep kernel default.
The second `tty` syntax variant is for using an external getty, like The second `tty` syntax variant is for using an external getty, like
agetty or the BusyBox getty. agetty or the BusyBox getty.
The third variant is for board bringup and the `rescue` boot mode. No By default both variants *clear* the TTY and *wait* for the user to
device node is required in this variant, the same output that the kernel press enter before starting getty.
uses is reused for stdio. If the `rescue` option is omitted, a shell is
started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
option is set the bundled `/libexec/finit/sulogin` is started to present
a bare-bones root login prompt. If the root (uid:0, gid:0) user does
not have a password set, no rescue is possible. For more information,
see the [Rescue Mode](#rescue-mode) section.
By default, the first two syntax variants *clear* the TTY and *wait* for
the user to press enter before starting getty.
**Example:** **Example:**
@@ -548,23 +423,16 @@ uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
it out by querying sysfs: `/sys/class/tty/console/active`. The speed it out by querying sysfs: `/sys/class/tty/console/active`. The speed
can be omitted to keep the kernel default. can be omitted to keep the kernel default.
> Most systems get by fine by just using `console`, which will evaluate
> to `/dev/console`. If you have to use `@console` to get any output,
> you may have some issue with your kernel config.
**Example:** **Example:**
tty [12345] @console noclear vt220 tty [12345] @console noclear vt220
On really bare bones systems, or for board bringup, Finit can give you a On really bare bones systems Finit offers a fallback shell, which
shell prompt as soon as [bootstrap][] is done, without opening any should not be enabled on production systems since. This because it
device node:
tty [12345789] notty
This should of course not be enabled on production systems. Because it
may give a user root access without having to log in. However, for may give a user root access without having to log in. However, for
board bringup and system debugging it can come in handy. bringup and system debugging it can come in handy:
configure --enable-fallback-shell
One can also use the `service` stanza to start a stand-alone shell: One can also use the `service` stanza to start a stand-alone shell:
@@ -574,8 +442,8 @@ One can also use the `service` stanza to start a stand-alone shell:
### Non-privileged Services ### Non-privileged Services
Every `run`, `task`, or `service` can also list the privileges the Every `run`, `task`, or `service` can also list the privileges the
`/path/to/cmd` should be executed with. Prefix the command with `/path/to/cmd` should be executed with. Simply prefix the path with
`@USR[:GRP]`, group is optional, like this: `[@USR[:GRP]]` like this:
run [2345] @joe:users logger "Hello world" run [2345] @joe:users logger "Hello world"
@@ -615,129 +483,28 @@ Log rotation is controlled using the global `log` setting.
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Worth noting is that conditions is allowed for all these stanzas. For a
Rescue Mode detailed description, see the [Conditions](conditions.md) document.
-----------
Finit supports a rescue mode which is activated by the `rescue` option
on the kernel command line. See [cmdline docs](cmdline.md) for how to
activate it.
The rescue mode comes in two flavors; *traditional* and *fallback*.
### Traditional
This is what most users expect. A very early maintenance login prompt,
served by the bundled `/libexec/finit/sulogin` program, or the standard
`sulogin` from util-linux or BusyBox is searched for in the UNIX default
`$PATH`. If a successful login is made, or the user exits (Ctrl-D), the
rescue mode is ended and the system boots up normally.
> **Note:** if the user (UID 0 and GID 0) does not have a password, or
> __the account is locked__, the user is presented with a password-less
> `"Press enter to enter maintenance mode."`, prompt which opens up a
> root shell.
### Fallback
If no `sulogin` program is found, Finit tries to bring up as much of its
own functionality as possible, yet limiting many aspects, meaning; no
network, no `fsck` of file systems in `/etc/fstab`, no `/etc/rc.local`,
no `runparts`, and most plugins are skipped (except those that provide
functionality for the condition subsystem).
Instead of reading `/etc/finit.conf` et al, system configuration is read
from `/lib/finit/rescue.conf`, which can be freely modified by the
system administrator.
The bundled default `rescue.conf` contains nothing more than:
runlevel 1
tty [12345] rescue
The `tty` has the `rescue` option set, which works similar to the board
bring-up tty option `notty`. The major difference being that `sulogin`
is started to query for root/admin password. If `sulogin` is not found,
`rescue` behaves like `notty` and gives a plain root shell prompt.
If Finit cannot find `/lib/finit/rescue.conf` it defaults to:
tty [12345] rescue
There is no way to exit the *fallback* rescue mode.
Limitations Limitations
----------- -----------
As of Finit v4 there are no limitations to where `.conf` settings can be As of Finit v4 there are no limitations to where `.conf` settings can be
placed. Except for the system/global `rlimit` and `cgroup` top-level placed. Except for the system/global `rlimit`, which can only be set
group declarations, which can only be set from `/etc/finit.conf`, since from `/etc/finit.conf`, since it is the first `.conf` file Finit reads.
it is the first `.conf` file Finit reads.
Originally, `/etc/finit.conf` was the only way to set up a Finit system. Originally, `/etc/finit.conf` was the only way to set up a Finit system.
Today it is mainly used for bootstrap settings like system hostname, Today it is mainly used for bootstrap settings like system hostname,
early module loading for watchdogd, network bringup and system shutdown. network bringup and system shutdown. These can now also be set in any
These can now also be set in any `.conf` file in `/etc/finit.d`. `.conf` file in `/etc/finit.d`.
There is, however, nothing preventing you from having all configuration There is, however, nothing preventing you from having all configuration
settings in `/etc/finit.conf`. settings in `/etc/finit.conf`.
> **Note:** The `/etc/finit.d` directory was previously the default > **Note:** The `/etc/finit.d` directory was previously the default
> Finit [runparts](#run-parts-scripts) directory. Finit no > Finit `runparts` directory. Finit no longer has a default
> longer has a default `runparts`, make sure to update your > `runparts`, make sure to update your setup, or the finit
> setup, or the finit configuration, accordingly. > configuration, accordingly.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
Watchdog
--------
When built `--with-watchdog` a separate service is built and installed
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
device node exists, Finit will start it and treat it as the elected
watchdog service to delegate its reboot to. See [bootstrap][] for
details. This delegation is to ensure that the system is rebooted by a
hardware watchdog timer -- on many embedded systems this is crucial to
ensure all circuits on the board are properly reset for the next boot,
in effect ensuring the system works the same after both a power-on and
reboot event.
The delegation is performed at the very last steps of system shutdown,
if reboot has been selected and an elected watchdog is known, first a
`SIGPWR` is sent to advise watchdogd of the pending reboot. Then, when
the necessary steps of preparing the system for shutdown (umount etc.)
are completed, Finit sends `SIGTERM` to watchdogd and puts itself in a
10 sec timeout loop waiting for the WDT to reset the board. If a reset
is not done before the timeout, Finit falls back to`reboot(RB_AUTOBOOT)`
which tells the kernel to do the reboot.
An external watchdog service can also be used. The more advanced cousin
[watchdogd](https://github.com/troglobit/watchdogd/) is the recommended
option here. It can register itself with Finit using the same IPC as
`initctl`. If the bundled watchdogd is running a hand-over takes place,
so it's safe to have both services installed on a system. For the
hand-over to work it requires that the WDT driver supports the safe exit
functionality where `"V"` is written to the device before closing the
device descriptor. If the kernel driver has been built without this,
the only option is to remove `/libexec/finit/watchdogd` or build without
it at configure time.
[bootstrap]: bootstrap.md
keventd
-------
The kernel event daemon bundled with Finit is a simple uevent monitor
for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition,
which can be useful to prevent power hungry services like anacron to run
when a laptop is only running on battery, for instance.
Since keventd is not an integral part of Finit yet it is not enabled by
default. Enable it using `./configuure --with-keventd`. The bundled
contrib build scripts for Debian, Alpine, and Void have this enabled.
This daemon is planned to be extended with monitoring of other uevents,
patches and ideas are welcome in the issue tracker.
+2 -46
View File
@@ -1,49 +1,7 @@
Finit Services Finit Services
============== ==============
Starting & Monitoring ![The service state machine](svc-machine.png "The service state machine")
---------------------
Finit can start and monitor the following types of daemons:
* Forks to background, creates a PID file
* Runs in foreground:
- creates a PID file
- does not create a PID file -- Finit can create it for you (optional)
Finit can *not* start and monitor a daemon that:
* Forks to background and does *not* create a PID file
| | Forking | Creates PID File | Finit creates PID File |
|---|---------|------------------|------------------------|
| ✔ | Yes | Yes | No |
| ✔ | No | Yes | No |
| ✔ | No | No | Yes, optionally |
| ✘ | Yes | No | No |
> **Note:** PID files is one mechanism used to assert conditions to
> synchronize the start and stop of other, dependent, services.
### Forks to Background w/ PID File
service pid:!/run/serv.pid serv -- Forking service
### Runs in Foreground w/ PID File
service serv -n -p -- Foreground service w/ PID file
### Runs in Foreground w/o PID File
Same as previous, but we tell Finit to create the PID file, because we
need it to synchronize start/stop of a dependent service.
service pid:/run/serv.pid serv -n -- Foreground service w/o PID file
### Runs in Foreground w/ Custom PID File
service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file
State Machine State Machine
------------- -------------
@@ -53,8 +11,6 @@ For `run`s and `task`s there is an additional end state called `DONE`,
not shown in the diagram for brevity. Services start in the `HALTED` not shown in the diagram for brevity. Services start in the `HALTED`
state. state.
![The service state machine](svc-machine.png "The service state machine")
The current state depends on the two following conditions: The current state depends on the two following conditions:
* `E`: Service enabled. In order for `E` to be satisfied, the service * `E`: Service enabled. In order for `E` to be satisfied, the service
@@ -62,7 +18,7 @@ The current state depends on the two following conditions:
A service may be stopped, or blocked, for several reasons: A service may be stopped, or blocked, for several reasons:
- The user has manually stopped the service using `initctl stop NAME` - The user has manually stopped the service using `initctl stop JOB`
- The program exits immediately. I.e. keeps crashing (make sure to use - The program exits immediately. I.e. keeps crashing (make sure to use
the 'run this service in the foreground' command line option) the 'run this service in the foreground' command line option)
- The binary is missing in the filesystem - The binary is missing in the filesystem
+8 -16
View File
@@ -12,36 +12,28 @@ have been adopted:
Same effect as `finit q`, `init q`, or `initctl reload`, reloads all Same effect as `finit q`, `init q`, or `initctl reload`, reloads all
*.conf files in `/etc/finit.d/` *.conf files in `/etc/finit.d/`
This also restarts the API (initctl) socket, like SysV init and systemd
does on USR1 with their FIFO/D-Bus.
`SIGUSR1` `SIGUSR1`
--------- ---------
Since Finit 4.1 this signal causes Finit to restart its API (initctl) Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
socket, like SysV init and systemd does on USR1 with their FIFO/D-Bus. processes, and unmounts all file systems. Then tells kernel to halt.
Most people these days want `SIGUSR2` though.
Finit <= 4.0 performed a system halt (like USR2 without power-off), but
this caused compatibility problems with systemd and sysvinit on desktop
systems. Hence, since Finit 4.1 it is no longer possible to halt a
system with a signal.
SysV init and systemd use this to re-open their FIFO/D-Bus.
`SIGUSR2` `SIGUSR2`
--------- ---------
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running Like SIGUSR1, but tell kernel to power-off the system, if ACPI or
processes, and unmounts all file systems. Then tells kernel to power similar exists to actually do this. If the kernel fails power-off,
off the system, if ACPI or similar exists to actually do this. If the Finit falls back to halt.
kernel fails power-off, Finit falls back to halt.
SysV init N/A, systemd dumps its internal state to log. SysV init N/A, systemd dumps its internal state to log.
`SIGTERM` `SIGTERM`
--------- ---------
Like `SIGUSR2`, but tell kernel to reboot the system when done. Like `SIGUSR1`, but tell kernel to reboot the system when done.
SysV init N/A, systemd rexecutes itself. SysV init N/A, systemd rexecutes itself.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

-2
View File
@@ -1,2 +0,0 @@
Makefile
Makefile.in
-2
View File
@@ -1,2 +0,0 @@
dist_man8_MANS = finit.8 initctl.8
dist_man5_MANS = finit.conf.5
-178
View File
@@ -1,178 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.Dd June 6, 2021
.\" Please adjust this date whenever revising the manpage.
.Dt FINIT 8 SMM
.Os Linux
.Sh NAME
.Nm finit
.Nd Fast init for Linux systems
.Sh SYNOPSIS
.Nm /sbin/finit
.Op finit.status_style=classic
.Op finit.show_status=[on,off]
.Op finit.debug
.Op rescue | recover
.Op single | S
.Nm /sbin/telinit
.Op Fl hv
.Op q | Q | 0-9
.Sh DESCRIPTION
Fast init for Linux systems. Reverse engineered from the EeePC
fastinit, 10+ years ago by Claudio Matsuoka, "gaps filled with frog
DNA ...". Focus is on small and embedded systems, although Finit is
fully usable on server and desktop systems as well.
.Pp
Features include:
.Bl -bullet -width 1n -compact
.It
Runlevels, defined per service
.It
One-shot tasks, services (daemons), or SysV init start/stop scripts
.It
Runparts and
.Pa /etc/rc.local
support
.It
Process supervision similar to
.Xr systemd 8
.It
Sourcing environment files
.It
Conditions for network/process/custom dependencies
.It
Pre/Post script actions
.It
Tooling to enable/disable services
.It
Built-in getty
.It
Built-in watchdog, with support for hand-over to
.Lk https://troglobit.com/watchdogd.html watchdogd
.It
Built-in support for Debian/BusyBox
.Pa /etc/network/interfaces
.It
Cgroups v2, both configuration and monitoring in `initctl top`
.It
Plugin support for customization
.It
Proper rescue mode with bundled `sulogin` for protected maintenance shell
.El
.Sh BOOTING
.Pp
Before laucnhing services, the system needs to be bootstrapped. This
involves mounting all filesystems (not already mounted by an initramfs)
in
.Pa /etc/fstab ,
ensuring that necessary filesystems like:
.Pa /dev ,
.Pa /proc ,
.Pa /sys ,
and
.Pa /run
are available and can provide the system with the services required.
.Pp
With that out of the way, a set of plugins take over:
.Pp
.Bl -tag -width bootmisc.so -offset indent
.It Cm bootmisc.so
Cleans up stale files from previous boots and default directories are
created in tmpfs filesystems
.It Cm modprobe.so
Probes kernel modules required by the system
.It Cm procps.so
Set kernel variables from
.Pa /etc/sysctl.conf
et al
.El
.Pp
When the plugins are done, finit reads
.Pa /etc/finit.conf
and all other enabled services in
.Pa /etc/finit.d/* .
First all services, run/tasks in runlevel S (bootStrap) are started,
When all run/tasks have completed, Finit changes to the configured
runlevel (default 2), Processes are started in parallel, in dependency
order according to their conditions.
.Pp
When all services and run/tasks have been started, the console progress
is disabled and all configured getty services are started.
.Sh CONDITIONS
The condition subysystem in Finit was initially created to track PID
files of daemons. If service/task B requires service A to have started,
Finit waits for the PID file of service A to be created/updated before
it starts service B. Conversely, if service A is stopped and its PID
file removed, service B is also stopped.
.Pp
The following condition families are available today:
.Pp
.Bl -tag -width pid -offset indent
.It Cm net
Linux netlink events, e.g. net/route/default, net/eth0/up, and
net/et0/running
.It Cm pid
PID files basd on the service declaration
.Cm name:id ,
gives the condition pid/name:id
.It Cm sys
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
.It Cm usr
User conditions, assert and deassert using
.Cm initctl cond [set|clr] foo
.El
.Sh SIGNALS
PID 1 is the only process that by default blocks all signals. Finit
allows for the following signals to control operation, but recommends
instead using the
.Xr initctl 8
tool.
.Pp
.Bl -tag -width TERM
.It HUP
Tell
.Nm
to reload its configuration file(s), same as
.Cm initctl reload
.It USR1
Restart API
.Cm ( initctl )
socket.
.It USR2
Tell
.Nm
to perform a system shutdown, same as
.Cm initctl poweroff
.It TERM
Like SIGUSR2 but performs a reboot, same as
.Cm initctl reboot
.It INT
Sent from kernel when Ctrl-Alt-Del is pressed (on the console), this
asserts the sys/key/ctrlaltdel condition, which can be used to start
tasks or services
.It PWR
Sent from a a power daemon on changes to UPS status, Finit asserts
the sys/pwr/fail condition
.El
.Sh FILES
.Bl -tag -width /etc/finit.d/available/*.conf -compact
.It Pa /etc/finit.conf
Main configuration file
.It Pa /etc/finit.d/*.conf
Static service definitions
.It Pa /etc/finit.d/available/*.conf
Available services
.It Pa /etc/finit.d/enabled/*.conf
Symlinks from available
.It Pa /run/finit/
Runtime files, including the condition subsystem
.El
.Sh SEE ALSO
.Xr finit.conf 5
.Xr initctl 8
.Sh AUTHORS
.Nm
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
maintained by Joachim Wiberg, with contributions by many others.
-847
View File
@@ -1,847 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.Dd Oct 17, 2020
.Dt FINIT.CONF 5 SMM
.Os Linux
.Sh NAME
.Nm finit.conf
.Nd Finit configuration file format
.Sh SYNOPSIS
.Nm /etc/finit.conf
.Nm /etc/finit.d/*.conf
.Nm /etc/finit.d/available/*.conf
.Sh DESCRIPTION
.Nm finit
can be configured using only the original
.Pa /etc/finit.conf
file or in combination with
.Pa /etc/finit.d/*.conf .
Useful for package-based Linux distributions \(em each package can
provide its own "script" file.
.Pp
.Bl -tag -width /etc/finit.d/*.conf -offset indent -compact
.It Pa /etc/finit.conf
Main configuration file
.It Pa /etc/finit.d/*.conf
Snippets, usually one service per file
.El
.Pp
Not all configuration directives are available in
.Pa /etc/finit.d/*.conf
and some directives are only available at bootstrap, runlevel
.Cm S ,
see the section
.Sx Limitations
below for details.
.Sh FILES
.Bl -tag -width /etc/finit.d/available/*.conf -compact
.It Pa /etc/finit.conf
Main configuration file
.It Pa /etc/finit.d/*.conf
Static service definitions
.It Pa /etc/finit.d/available/*.conf
Available services
.It Pa /etc/finit.d/enabled/*.conf
Enabled services, symlinks from available
.El
.Sh FILE FORMAT
Lines starting with `#' are ignored. Note that end-of-line comments are
NOT supported, comments must be on a line of their own.
.Pp
A configuration stanza must be on a single (long) line and
.Em cannot
be broken up in multiple lines by use of a backslash as the last
character.
.Sh DIRECTIVES
This section lists all supported configuration directives. There also exist
deprecated directives, see the Markdown documentation for details on these.
.Pp
.Bl -tag -width 1n
.It Cm rlimit Oo hard|soft Oc Ar RESOURCE Aq LIMIT | unlimited
.Pp
Set the hard or soft limit for a resource, or both if that argument is
omitted.
.Ar RESOURCE
is the lower-case
.Cm RLIMIT_
string constants from
.Xr setrlimit 2 ,
without prefix. E.g. to set
.Cm RLIMIT_CPU ,
use
.Cm cpu .
.Pp
.Ar LIMIT
is an integer that depends on the resource being modified, see
the man page, or the kernel
.Pa /proc/PID/limits
file, for details.
Finit versions before v3.1 used
.Cm infinity
for
.Cm unlimited ,
which is still supported, albeit deprecated.
.Bd -unfilled -offset indent
# No process is allowed more than 8MB of address space
rlimit hard as 8388608
# Core dumps may be arbitrarily large
rlimit soft core infinity
# CPU limit for all services, soft & hard = 10 sec
rlimit cpu 10
.Ed
.Pp
.Cm rlimit
can be set globally, in
.Pa /etc/finit.conf ,
or locally per each
.Pa /etc/finit.d/*.conf
read. I.e., a set of task/run/service stanzas can share the same
rlimits if they are in the same .conf.
.It Cm runlevel Aq N
The system runlevel to go to after bootstrap (S) has completed.
.Cm N
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt. All other can be used by operating system administrators.
Default: 2
.Pp
.Sy Note:
only read and executed in runlevel S (bootstrap).
.It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
One-shot command to run in sequence when entering a runlevel, with
optional arguments and description.
.Pp
.Cm run
commands are guaranteed to be completed before running the next
command. Highly useful if true serialization is needed. Usually
only used in the bootstrap (S) runlevel.
.Pp
.Cm Aq COND
conditions are described in
.Xr finit 8 ,
see also the
.Sx Examples
section below.
.It Cm task Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
One-shot like
.Cm run ,
but starts in parallel with the next command.
.Pp
Both
.Cm run
and
.Cm task
commands are run in a shell, so pipes and redirects can be freely used:
.Bd -unfilled -offset indent
task [s] echo "foo" | cat >/tmp/bar
.Ed
.It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description
Similar to
.Cm task
is the
.Cm sysv
stanza, which can be used to call SysV style start/stop scripts. The
primary intention for this command is to be able to re-use much of
existing setup and init scripts in Linux distributions.
.Pp
When entering an allowed runlevel, Finit calls
.Cm init-script start ,
when entering a disallowed runlevel, Finit calls
.Cm init-script stop ,
and if the Finit .conf, where the
.Cm sysv
stanza is declared, is modified, Finit calls
.Cm init-script restart
on
.Cm initctl reload .
Similar to how
.Cm service
stanzas work.
.Pp
Forking services started with
.Cm sysv
scripts can be monitored by Finit by declaring the PID file to look for:
.Bd -unfilled -offset indent
sysv pid:!/path/to/pidfile.pid /path/to/script ...
.Ed
.Pp
The leading '!' is to prevent Finit from managing the PID file, which is
the default behavior for the
.Cm pid:
command modifier.
.It Cm service Oo LVLS Oc Ao COND Ac Ar /path/to/daemon ARGS Op -- Optional description
Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Finit tries to restart services that die, by
default 10 times, before giving up and marking them as
.Em crashed .
After which they have to be restarted manually using
.Cm initctl restart NAME .
The number of restarts, the delay between each restart, and more is
configurable, see the options below.
.Pp
For daemons that support it, we recommend appending
.Cm --foreground , --no-background , -n , -F ,
or similar command line argument to prevent them from forking off a
sub-process in the background. This is the most reliable way to monitor
a service.
.Pp
However, not all daemons support running in the foreground, or they may
start logging to the foreground as well, these are called forking
services and are supported using the same syntax as forking
.Cm sysv
services, using the
.Cm pid:!/path/to/pidfile.pid
command modifier syntax.
.Pp
.Sy Example: in the case of
.Cm ospfd
(below), we omit the
.Cm -d
flag (daemonize) to prevent it from forking to the background:
.Bd -unfilled -offset indent
service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
.Ed
.Pp
.Cm [2345]
denote the runlevels
.Cm ospfd
is allowed to run in, they are optional and default to runlevel 2-5 if
omitted.
.Pp
.Cm <pid/zebra>
is the condition for starting
.Cm ospfd .
In this example Finit waits for another service,
.Cm zebra ,
to have created its PID file in
.Pa /var/run/quagga/zebra.pid
before starting
.Cm ospfd .
Finit watches *all* files in
.Pa /var/run ,
for each file named
.Cm *.pid ,
.Cm */pid ,
Finit opens it and find the matching
.Cm NAME:ID
using the PID.
.Pp
Some services do not maintain a PID file and rather than patching each
application Finit provides a workaround. A
.Cm pid
modifier keyword can be set to have Finit automatically create (when
starting) and later remove (when stopping) the PID file. The file is
created in the
.Pa /var/run
directory using the
.Xr basename 3
of the service. The full syntax of the
.Cm pid
modifier is:
.Bd -unfilled -offset indent
pid[:[!][/path/to/]filename[.pid]]
.Ed
.Pp
For example, by adding
.Cm pid:/run/foo.pid
to the service
.Cm /sbin/bar ,
that PID file will, not only be created and removed automatically, but
also be used by the Finit condition subsystem. So a service/run/task
can depend on the
.Cm <pid/bar>
condition.
.Pp
If a service should not be automatically started, it can be configured
as manual with the
.Cm manual:yes
command modifier. The service can then be started at any time by
running
.Cm initctl start NAME
.Pp
The name of a service, shown by the
.Cm initctl
tool, defaults to the basename of the service executable. It can be
changed with the
.Cm name:foo
command modifier.
.Pp
As mentioned previously, services are automatically restarted should
they crash, this is configurable with the following options:
.Bl -tag -width 1n
.It Cm restart:NUM
number of times Finit tries to restart a crashing
service, default: 10. When this limit is reached the service is
marked
.Em crashed
and must be restarted manually with
.Xr initctl 8 .
.It Cm restart_sec:SEC
number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used
.It Cm norestart
dont restart on failures, same as
.Cm restart:0
.It Cm oncrash:reboot
when all retries have failed, and the service
has
.Em crashed ,
if this option is set the system is rebooted. Note, future releases
may include other
.Cm oncrash:
actions.
.El
.Pp
When stopping a service (run/task/sysv/service), either manually or when
moving to another runlevel, Finit starts by sending SIGTERM, to allow
the process to shut down gracefully. If the process has not been
collected within 3 seconds, Finit sends SIGKILL. To halt the process
using a different signal, use the command modifier
.Cm halt:SIGNAL ,
e.g.,
.Cm halt:SIGPWR .
To change the delay between your halt signal and KILL, use the command
modifier
.Cm kill:SEC ,
e.g.,
.Cm kill:10
to wait 10 seconds before sending SIGKILL.
.Pp
Services support the
.Cm pre:script
and
.Cm post:script
command actions as well. These run as the same
.Cm @USER:GROUP
as the service itself, with any
.Cm env:file
sourced. The scripts must use an absolute path, but are executed from
the
.Cm $HOME
of the given user. The scripts are not called with any argument
(currently), but both get the
.Cm SERVICE_IDENT=foo
environment variable set. Here
.Cm foo
denotes the identity of the service, which if there are multiple
services named
.Cm foo ,
may be
.Cm foo:1 ,
or any unique identifier specified in the .conf file. The
.Cm post:script
is called with an additional set of environment variables:
.Bl -tag -offset indent -width 1n
.It Cm EXIT_CODE=[exited,signal]
set to one of
.Cm exited
or
.Cm signal
.It Cm EXIT_STATUS=[num,SIGNAME]
set to one of exit status code from the program, if it exited normally,
or the signal name (HUP, TERM, etc.) if it exited due to signal
.El
.Pp
The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the
.Cm kill:SEC
modifier syntax.
.Pp
.Sy Note:
the
.Cm pre:script
.Em must
be idempotent, because a service can transition between READY and HALTED
states any number of times before going to RUNNING.
.It Cm runparts Aq DIR
Call
.Xr run-parts 8
on
.Cm DIR
to run start scripts. All executable files, or scripts, in the
directory are called, in alphabetic order. The scripts in this
directory are executed at the very end of bootstrap, runlevel S.
.Pp
It can be beneficial to use
.Cm S01name ,
.Cm S02othername ,
etc. if there is a dependency order between the scripts. Symlinks to
existing daemons can talso be used, but make sure they daemonize by
default.
.Pp
Similar to the
.Pa /etc/rc.local
shell script, make sure that all your services and programs either
terminate or start in the background or you will block Finit.
.Sy Note:
only read and executed in runlevel S (bootstrap).
.It Cm include Aq CONF
Include another configuration file. Absolute path required.
.It Cm log size:BYTES count:NUM
Log rotation for run/task/services using the
.Cm log
command modifier with redirection to a log file. Global setting,
applies to all services.
.Pp
The size can be given as bytes, without a specifier, or in `k`, `M`,
or `G`, e.g.
.Cm size:10M ,
or
.Cm size:3G .
A value of
.Cm size:0
disables log rotation. The default is
.Cm size:200k .
.Pp
The count value is recommended to be between 1-5, with a default 5.
Setting count to 0 means the logfile will be truncated when the MAX
size limit is reached.
.It Cm tty Oo LVLS Oc Ao COND Ac Ar DEV Oo BAUD Oc Oo noclear Oc Oo nowait Oc Oo nologin Oc Oo TERM Oc
This form of the
.Cm tty
stanza uses the built-in getty on the given TTY device
.Ar DEV ,
in the given runlevels.
.Ar DEV may be the special keyword
.Cm @console ,
or `console`, which is expanded from `/sys/class/tty/console/active`,
useful on embedded systems.
.Pp
The default baud rate is 0, i.e., keep kernel default.
.Pp
The `tty` stanza inherits runlevel, condition (and other feature)
parsing from the `service` stanza. So TTYs can run in one or many
runlevels and depend on any condition supported by Finit. This is
useful e.g. to depend on `<pid/elogind>` before starting a TTY.
.Bd -unfilled -offset indent
tty [12345] /dev/ttyAMA0 115200 noclear vt220
.Ed
.It Cm tty Oo LVLS Oc Ao COND Ac Ar CMD DEV Oo noclear Oc Oo nowait Oc
This form of the
.Cm tty
stanza is for using an external getty, like agetty or the BusyBox getty.
.Pp
By default, these first two syntax variants
.Em clear
the TTY and
.Em wait
for the user to press enter before starting getty.
.Bd -unfilled -offset indent
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
.Ed
.Pp
The
.Cm noclear
option disables clearing the TTY after each session. Clearing the TTY
when a user logs out is usually preferable.
.Pp
The
.Cm nowait
option disables the
.Cm Please press Enter to activate console
message before actually starting the getty program. On small and
embedded systems running multiple unused getty wastes both memory and
CPU cycles, so `wait` is the preferred default.
.Pp
The
.Cm nologin
option disables getty and
.Pa /bin/login ,
and gives the user a root (login) shell on the given TTY
.Cm DEV
immediately. Needless to say, this is a rather insecure option, but can
be very useful for developer builds, during board bringup, or similar.
.Pp
Notice the ordering, the
.Cm TERM
option to the built-in getty must be the last argument.
.Pp
Embedded systems may want to enable automatic `DEV` by supplying the
special
.Cm @console
device. This works regardless weather the system uses
.Cm ttyS0 , ttyAMA0 , ttyMXC0 ,
or anything else. Finit figures it out by querying sysfs:
.Pa /sys/class/tty/console/active .
The speed can be omitted to keep the kernel default.
.Pp
Most systems get by fine by just using `console`, which will evaluate
to
.Pa /dev/console .
If you have to use
.Cm @console
to get any output, you may have some issue with your kernel config.
.Bd -unfilled -offset indent
tty [12345] @console noclear vt220
.Ed
.Pp
On really bare bones systems, or for board bringup, Finit can give you a
shell prompt as soon as bootstrap is done, without opening any device
node:
.Bd -unfilled -offset indent
tty [12345789] notty
.Ed
.Pp
This should of course not be enabled on production systems. Because it
may give a user root access without having to log in. However, for
board bringup and system debugging it can come in handy.
.Pp
One can also use the
.Cm service
stanza to start a stand-alone shell:
.Bd -unfilled -offset indent
service [12345] /bin/sh -l
.Ed
.Pp
.It Cm tty Oo LVLS Oc Ao COND Ac Oo notty Oc Oo rescue Oc
The third
.Cm tty
form is for board bringup and the
.Cm rescue
boot mode. No device node is required in this variant, the same output
that the kernel uses is reused for stdio. If the
.Cm rescue
option is omitted, a shell is started. The flags
.Cm nologin , noclear ,
and
.Cm nowait
are implied. If the
.Cm rescue
option is set the bundled
.Pa /libexec/finit/sulogin
is started to present a bare-bones root login prompt. If the root
(uid:0, gid:0) user does not have a password set, no rescue is possible.
.El
.Sh COMMAND MODIFIERS
The run/task/tty/service/sysv stanzas take modifiers, or options, to
control their behavior. This section lists them with their limitations.
All modifiers must be placed between the stanza and its command.
.Bl -tag -width 1n
.It Cm @user:group
Every
.Cm run , task ,
or
.Cm service
can also list the privileges the
.Cm /path/to/cmd
should be executed with. Prefix the command with
.Cm @USR[:GRP] ,
group is optional, like this:
.Bd -unfilled -offset indent
run [2345] @joe:users logger "Hello world"
.Ed
.Pp
For multiple instances of the same command, e.g. a DHCP client or
multiple web servers, add
.Cm :ID
somewhere between the
.Cm run , task , service
keyword and the command, like this:
.Bd -unfilled -offset indent
service :80 [2345] httpd -f -h /http -p 80 -- Web server
service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
.Ed
.Pp
Without the
.Cm :ID
to the service the latter will overwrite the former and only the old web
server would be started and supervised.
.It Cm log:/path/to/file
Redirect stdout/stderr of a command to the given log file. See the
global log directive, above, for details on log rotation.
.It Cm log:console
Redirect stdout/stderr of a command to
.Pa /dev/console ,
only use this for debugging or bringup.
.It Cm log:null
Redirect stdout/stderr of a command to
.Pa /dev/null .
.It Cm log:prio:facility.level,tag:ident
Redirect stdout/stderr of a command to syslog using the given priority
and tag identity.
.Bd -unfilled -offset indent
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
.Ed
.It Cm log
Default
.Cm prio
is
.Cm daemon.info
and the default
.Cm tag
identity is the basename of the service or run/task command.
.El
.Sh RESCUE MODE
Finit supports a rescue mode which is activated by the
.Cm rescue
option on the kernel command line. The rescue mode comes in two
flavors:
.Em traditional
and
.Em fallback .
.Ss Traditional
This is what most users expect. A very early maintenance login prompt,
served by the bundled
.Pa /libexec/finit/sulogin
program, or the standard
.Cm sulogin
from util-linux or BusyBox is searched for in the UNIX default
.Cm $PATH .
If a successful login is made, or the user exits (Ctrl-D), the rescue
mode is ended and the system boots up normally.
.Pp
.Sy Note:
if the user (UID 0 and GID 0) does not have a password, or
.Em the account is locked ,
the user is presented with a password-less prompt:
.Cm "Press enter to enter maintenance mode." ,
which opens up a root shell.
.Ss Fallback
If no
.Cm sulogin
program is found, Finit tries to bring up as much of its own
functionality as possible, yet limiting many aspects, meaning; no
network, no`fsck` of file systems in
.Pa /etc/fstab ,
no
.Pa /etc/rc.local ,
no
.Cm runparts ,
and most plugins are skipped (except those that provide functionality
for the condition subsystem).
.Pp
Instead of reading
.Pa /etc/finit.conf
et al, system configuration is read from
.Pa /lib/finit/rescue.conf ,
which can be freely modified by the system administrator.
.Pp
The bundled default `rescue.conf` contains nothing more than:
.Bd -unfilled -offset indent
runlevel 1
tty [12345] rescue
.Ed
.Pp
The
.Cm tty
has the
.Cm rescue
option set, which works similar to the board bring-up tty option
.Cm notty .
The major difference being that `sulogin` is started to query for
root/admin password. If
.Cm sulogin
is not found,
.Cm rescue
behaves like
.Cm notty
and gives a plain root shell prompt.
.Pp
If Finit cannot find
.Pa /lib/finit/rescue.conf
it defaults to:
.Bd -unfilled -offset indent
tty [12345] rescue
.Ed
.Pp
There is no way to exit the
.Em fallback
rescue mode.
.Sh SERVICE ENVIRONMENT
Finit supports sourcing environment variables from
.Pa /etc/default/* ,
or similar. This is a common pattern from SysV init scripts, where the
start/stop script is a generic script for the given service,
.Cm foo ,
and the options for the service are sourced from the file
.Pa /etc/default/foo .
Like this:
.Bd -unfilled -offset indent
/etc/default/foo:
FOO_OPTIONS=--extra-arg="bar" -s -x
/etc/finit.conf:
service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon
.Ed
.Pp
Here the service
.Cm foo
is started with
.Op Fl -n ,
to make sure it runs in the foreground, and the with the options found
in the environment file. With the
.Cm ps
command we can see that the process is started with:
.Bd -unfilled -offset indent
foo -n --extra-arg=bar -s -x
.Ed
.Pp
.Sy Note:
the leading `-` determines if Finit should treat a missing environment
file as blocking the start of the service or not. When `-` is used, a
missing environment file does
.Em not
block the start.
.Sh SERVICE WRAPPER SCRIPTS
If your service requires to run additional commands, executed before the
service is actually started, like the systemd `ExecStartPre`, you can
use a wrapper shell script to start your service.
.Pp
The Finit service
.Cm .conf
file can be put into
.Pa /etc/finit.d/available ,
so you can control the service using
.Cm initctl .
Then use the path to the wrapper script in the Finit
.Cm .conf
service stanza. The following example employs a wrapper script in
.Pa /etc/start.d .
.Bd -unfilled
/etc/finit.d/available/program.conf:
service [235] <!> /etc/start.d/program -- Example Program
/etc/start.d/program:
#!/bin/sh
# Prepare the command line options
OPTIONS="-u $(cat /etc/username)"
# Execute the program
exec /usr/bin/program $OPTIONS
.Ed
.Pp
.Sy Note:
the example sets
.Cm <!>
to denote that it doesn't support SIGHUP. That way Finit will
stop/start the service instead of sending SIGHUP at restart/reload
events.
.Sh CGROUPS
There are three major cgroup configuration directives:
.Pp
.Bl -enum -offset indent -compact
.It
Global top-level group: init, system, user, or a custom group
.It
Selecting a top-level group for a set of run/task/services
.It
Per run/task/service limits
.El
.Pp
Top-level group configuration.
.Bd -unfilled -offset indent
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9800
.Ed
.Pp
Adding an extra cgroup
.Cm maint/
will require you to adjust the weight of the above three. We leave
.Cm init/
and
.Cm user/
as-is reducing weight of
.Cm system/
to 9700.
.Bd -unfilled -offset indent
cgroup system cpu.weight:9700
# Example extra cgroup 'maint'
cgroup maint cpu.weight:100
.Ed
.Pp
By default, the
.Cm system/
cgroup is selected for almost everything. The
.Cm init/
cgroup is reserved for PID 1 itself and its closest relatives. The
.Cm user/
cgroup is for local TTY logins spawned by getty.
.Pp
To select a different top-level cgroup, e.g.
.Cm maint/ ,
one can either define it for a group of run/task/service directives in a
.Cm .conf
or per each stanza:
.Bd -unfilled -offset indent
cgroup.maint
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
.Ed
.Pp
or
.Bd -unfilled -offset indent
service [...] <...> cgroup.maint /path/to/foo args -- description
.Ed
.Pp
The latter form also allows per-stanza limits on the form:
.Bd -unfilled -offset indent
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
.Ed
.Pp
Notice the comma separation and the
.Cm mem.
exception to the rule: every cgroup setting maps directly to cgroup v2
syntax. I.e.,
.Cm cpu.max
maps to the file
.Pm /sys/fs/cgroup/maint/foo/cpu.max .
There is no filtering, except for expanding the shorthand
.Cm mem.
to
.Cm memory. ,
if the file is not available, either the cgroup controller is not
available in your Linux kernel, or the name is misspelled.
.Pp
Linux cgroups and details surrounding values are not explained in the
Finit documentation. The Linux admin-guide cover this well:
.Lk https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
.Sh LIMITATIONS
As of Finit v4 there are no limitations to where
.Cm .conf
settings can be placed. Except for the system/global
.Cm rlimit
and
.Cm cgroup
top-level group declarations, which can only be set from
.Pa /etc/finit.conf ,
since it is the first
.Cm .conf
file Finit reads.
.Pp
Originally,
.Pp /etc/finit.conf
was the only way to set up a Finit system. Today it is mainly used for
bootstrap settings like system hostname, early module loading for
watchdogd, network bringup and system shutdown. These can now also be
set in any
.Cm .conf
file in
.Pa /etc/finit.d .
.Pp
There is, however, nothing preventing you from having all configuration
settings in
.Pa /etc/finit.conf .
.Sh SEE ALSO
.Xr finit 8 ,
.Xr initctl 8
.Sh AUTHORS
.Nm finit
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
maintained by Joachim Wiberg, with contributions by many others.
-194
View File
@@ -1,194 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.Dd June 6, 2021
.\" Please adjust this date whenever revising the manpage.
.Dt INITCTL 8 SMM
.Os Linux
.Sh NAME
.Nm initctl
.Nd Control tool for Finit
.Sh SYNOPSIS
.Nm /sbin/initctl
.Op Fl bcfhlpqtvV
.Op COMMAND
.Sh DESCRIPTION
.Nm
is the official tool for interacting with
.Xr finit 8 .
It comes with functions for showing status, querying health of services,
as well as editing, enabling and disabling services.
.Sh OPTIONS
This program follows the usual UNIX command line syntax, with long
options starting with two dashes (`-'). The options are as follows:
.Bl -tag -width Ds
.It Fl b, -batch
Batch mode, no screen size probing.
.It Fl c, -create
Create missing paths (and files) as needed. Useful with the edit command.
.It Fl f, -force
Ignore missing files and arguments, never prompt.
.It Fl h, -help
Show built-in help text.
.It Fl 1, -once
Only one lap in commands like top.
.It Fl p, -plain
Use plain table headings, no ANSI control characters.
.It Fl q, -quiet
Silent, only return status of command.
.It Fl t, -no-heading
Skip table headings.
.It Fl v, -verbose
Verbose output, where applicable.
.It Fl V, -version
Show program version.
.El
.Sh COMMANDS
.Bl -tag -width Ds
.It Nm Ar debug
Toggle
.Nm finit
(daemon) debug to
.Pa /dev/console
.It Nm Ar help
Show built-in help text
.It Nm Ar version
Show program version
.It Nm Ar ls | list
List all
.Cm *.conf
files in
.Pa /etc/finit.d
.It Nm Ar create Ar CONF
Create
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar delete Ar CONF
Delete
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar show Ar CONF
Show
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar edit Ar CONF
Edit
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar touch Ar CONF
Change (mark as modified)
.Cm .conf
in
.Pa /etc/finit.d/available ,
like
.Ar edit
this tells
.Nm finit
that a configuration has been changed and is a candidate to be restarted
(or SIGHUP'ed) on
.Cm initctl reload
.It Nm Ar enable Ar CONF
Enable
.Cm .conf
in
.Pa /etc/finit.d/available ,
i.e., add symlink in
.Pa /etc/finit.d/enabled
.It Nm Ar disable Ar CONF
Disable
.Cm .conf
in
.Pa /etc/finit.d/enabled ,
i.e., removes symlink
.It Nm Ar reload
Reload
.Cm *.conf in
.Pa /etc/finit.d ,
i.e., activates changes
.It Nm Ar cond set Ar COND
Set (assert) user-defined condition,
.Cm +usr/COND
.It Nm Ar cond clr | clear Ar COND
Clear (deassert) user-defined condition,
.Cm -usr/COND
.It Nm Ar cond status
Show condition status, default
.Cm cond
command.
.It Nm Ar cond dump
Dump all conditions and their status
.It Nm Ar ident Op Cm NAME
Display indentities of all run/task/services, or only instances
matching
.Cm NAME .
A partial string, e.g.,
.Cm NAM ,
will not match anything
.It Nm Ar log Op Cm NAME
Show ten last Finit, or
.Cm NAME ,
messages from syslog
.It Nm Ar start Cm NAME[:ID]
Start service by name, with optional ID, e.g.,
.Cm initctl start tty:1
.It Nm Ar stop Cm NAME[:ID]
Stop/Pause a running service by name
.It Nm Ar reload Cm NAME[:ID]
Reload service by name (SIGHUP or restart)
.It Nm Ar signal Cm NAME:[ID] S
Send signal S to service, by name.
.Cm S
may be a complete signal name such as SIGHUP, or short name such as HUP, or the signal number such as 1 (SIGHUP).
.It Nm Ar restart Cm NAME[:ID]
Restart (stop/start) service by name
.It Nm Ar status Cm NAME[:ID]
Show service status, by name. If only
.Cm NAME
is given and multiple instances exits, a summary of all matching
instances are shown. Only an exact match displays the detailed status
for a particular instance
.It Nm Ar status
Show status of all services, default command
.It Nm Ar cgroup
List cgroup config overview
.It Nm Ar ps
List processes based on cgroups
.It Nm Ar top
Show top-like listing based on cgroups
.It Nm Ar runlevel Op Ar 0-9
Show or set runlevel: 0 halt, 6 reboot
.It Nm Ar reboot
Reboot system, default if
.Cm reboot
is symlinked to
.Nm
.It Nm Ar halt
Halt system, default if
.Cm halt
is symlinked to
.Nm
.It Nm Ar poweroff
Power-off system, default if
.Cm poweroff
is symlinked to
.Nm
.It Nm Ar suspend
Suspend system, default if
.Cm suspend
is symlinked to
.Nm
.It Nm Ar utmp show
Raw dump of UTMP/WTMP db
.El
.Sh SEE ALSO
.Xr finit.conf 5
.Xr finit 8
.Sh AUTHORS
.Nm finit
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
maintained by Joachim Wiberg, with contributions by many others.
+3 -39
View File
@@ -3,11 +3,11 @@ AM_LDFLAGS = -module -avoid-version -shared
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
AM_CPPFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC if STATIC
noinst_LTLIBRARIES = libplug.la noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c pidfile.c procps.c sys.c usr.c libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c libplug_la_SOURCES += alsa-utils.c
@@ -25,10 +25,6 @@ if BUILD_MODULES_LOAD_PLUGIN
libplug_la_SOURCES += modules-load.c libplug_la_SOURCES += modules-load.c
endif endif
if BUILD_MODPROBE_PLUGIN
libplug_la_SOURCES += modprobe.c
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
libplug_la_SOURCES += netlink.c libplug_la_SOURCES += netlink.c
endif endif
@@ -37,24 +33,8 @@ if BUILD_RESOLVCONF_PLUGIN
libplug_la_SOURCES += resolvconf.c libplug_la_SOURCES += resolvconf.c
endif endif
if BUILD_RTC_PLUGIN
libplug_la_SOURCES += rtc.c
endif
if BUILD_TTY_PLUGIN
libplug_la_SOURCES += tty.c
endif
if BUILD_URANDOM_PLUGIN
libplug_la_SOURCES += urandom.c
endif
if BUILD_X11_COMMON_PLUGIN
libplug_la_SOURCES += x11-common.c
endif
else else
pkglib_LTLIBRARIES = bootmisc.la pidfile.la procps.la sys.la usr.la pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la pkglib_LTLIBRARIES += alsa-utils.la
@@ -72,10 +52,6 @@ if BUILD_MODULES_LOAD_PLUGIN
pkglib_LTLIBRARIES += modules-load.la pkglib_LTLIBRARIES += modules-load.la
endif endif
if BUILD_MODPROBE_PLUGIN
pkglib_LTLIBRARIES += modprobe.la
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
pkglib_LTLIBRARIES += netlink.la pkglib_LTLIBRARIES += netlink.la
endif endif
@@ -84,18 +60,6 @@ if BUILD_RESOLVCONF_PLUGIN
pkglib_LTLIBRARIES += resolvconf.la pkglib_LTLIBRARIES += resolvconf.la
endif endif
if BUILD_RTC_PLUGIN
pkglib_LTLIBRARIES += rtc.la
endif
if BUILD_TTY_PLUGIN
pkglib_LTLIBRARIES += tty.la
endif
if BUILD_URANDOM_PLUGIN
pkglib_LTLIBRARIES += urandom.la
endif
if BUILD_X11_COMMON_PLUGIN if BUILD_X11_COMMON_PLUGIN
pkglib_LTLIBRARIES += x11-common.la pkglib_LTLIBRARIES += x11-common.la
endif endif
-15
View File
@@ -21,12 +21,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
@@ -35,11 +30,6 @@
static void save(void *arg) static void save(void *arg)
{ {
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
_d("Saving sound settings ..."); _d("Saving sound settings ...");
run_interactive(ALSACTL " -g store", "Saving sound settings"); run_interactive(ALSACTL " -g store", "Saving sound settings");
@@ -48,11 +38,6 @@ static void save(void *arg)
static void restore(void *arg) static void restore(void *arg)
{ {
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
_d("Restoring sound settings ..."); _d("Restoring sound settings ...");
run_interactive(ALSACTL " -g restore", "Restoring sound settings"); run_interactive(ALSACTL " -g restore", "Restoring sound settings");
+40 -49
View File
@@ -24,11 +24,7 @@
#include <ftw.h> #include <ftw.h>
#include <mntent.h> #include <mntent.h>
#include <string.h> #include <string.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
@@ -69,7 +65,7 @@ static int is_tmpfs(char *path)
return tmpfs; return tmpfs;
} }
static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw) static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
{ {
if (ftw->level == 0) if (ftw->level == 0)
return 1; return 1;
@@ -80,14 +76,8 @@ static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct
return 0; return 0;
} }
/* /* We can safely skip tmpfs, nothing to clean from previous boot there */
* Cleanup stale files from previous boot, if any still linger on. static void bootclean(void)
* Some systems, e.g. Alpine Linux, still have a persistent /run and
* /tmp, i.e. not tmpfs.
*
* We can safely skip tmpfs, nothing to clean there.
*/
static void clean(void *arg)
{ {
char *dir[] = { char *dir[] = {
"/tmp/", "/tmp/",
@@ -100,26 +90,22 @@ static void clean(void *arg)
if (is_tmpfs(dir[i])) if (is_tmpfs(dir[i]))
continue; continue;
nftw(dir[i], bootclean, 20, FTW_DEPTH); nftw(dir[i], do_clean, 20, FTW_DEPTH);
} }
} }
static void ln(const char *target, const char *linkpath)
{
if (symlink(target, linkpath) && errno != EEXIST)
_pe("Failed creating %s -> %s symlink", target, linkpath);
}
/* /*
* Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP * Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP
*/ */
static void setup(void *arg) static void setup(void *arg)
{ {
mode_t prev; int uid, gid;
prev = umask(0); /* Cleanup stale files, if any still linger on. */
bootclean();
_d("Setting up FHS structure in /var ..."); _d("Setting up FHS structure in /var ...");
umask(0);
makedir("/var/cache", 0755); makedir("/var/cache", 0755);
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */ makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755); makedir("/var/games", 0755);
@@ -129,13 +115,12 @@ static void setup(void *arg)
makedir("/var/lib/urandom",0755); makedir("/var/lib/urandom",0755);
if (fisdir("/run")) { if (fisdir("/run")) {
_d("System with new /run tmpfs ..."); _d("System with new /run tmpfs ...");
if (!fisdir("/run/lock"))
makedir("/run/lock", 1777); makedir("/run/lock", 1777);
ln("/run/lock", "/var/lock"); symlink("/run/lock", "/var/lock");
ln("/dev/shm", "/run/shm"); symlink("/dev/shm", "/run/shm");
/* compat only, should really be set up by OS/dist */ /* compat only, should really be set up by OS/dist */
ln("../run", "/var/run"); symlink("/run", "/var/run");
} else { } else {
makedir("/var/lock", 1777); makedir("/var/lock", 1777);
makedir("/var/run", 0755); makedir("/var/run", 0755);
@@ -149,20 +134,18 @@ static void setup(void *arg)
makedir("/var/empty", 0755); makedir("/var/empty", 0755);
/* /*
* UTMP actually needs multiple db files * If /etc/group or "utmp" group is missing,
*/ * default to "root"/"wheel" group
if (has_utmp()) {
int gid;
/*
* If /etc/group or "utmp" group is missing, default to
* "root", or "wheel", group.
*/ */
_d("Setting up necessary UTMP files ..."); _d("Setting up necessary UTMP files ...");
gid = getgroup("utmp"); gid = getgroup("utmp");
if (gid < 0) if (gid < 0)
gid = 0; gid = 0;
/*
* UTMP actually needs multiple db files
*/
if (has_utmp()) {
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */ create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
create("/var/log/wtmp", 0644, 0, gid); /* Login history */ create("/var/log/wtmp", 0644, 0, gid); /* Login history */
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */ create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
@@ -182,31 +165,39 @@ static void setup(void *arg)
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */ makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */ makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */ makedir("/var/run/pluto", 0755); /* Needed by Openswan */
mksubsys("/var/run/quagga", 0755, "quagga", "quagga"); makedir("/var/run/quagga", 0755); /* Needed by Quagga */
mksubsys("/var/log/quagga", 0755, "quagga", "quagga"); makedir("/var/log/quagga", 0755); /* Needed by Quagga */
mksubsys("/var/run/frr", 0755, "frr", "frr"); makedir("/var/run/frr", 0755); /* Needed by FRR */
mksubsys("/var/log/frr", 0755, "frr", "frr"); makedir("/var/log/frr", 0755); /* Needed by FRR */
makedir("/var/run/sshd", 01755); /* OpenSSH */ makedir("/var/run/sshd", 01755); /* OpenSSH */
gid = getgroup("tty");
if (gid < 0)
gid = 0;
makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, gid);
if (!fexist("/etc/mtab")) if (!fexist("/etc/mtab"))
ln("../proc/self/mounts", "/etc/mtab"); symlink("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */ /* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd"); uid = getuser("uuidd", NULL);
if (uid >= 0) {
gid = getgroup("uuidd");
if (gid < 0)
gid = 0;
makedir("/var/run/uuidd", 0755);
chown("/var/run/uuidd", uid, gid);
}
/* Debian has /run/sudo, ensure correct perms and SELinux label */ umask(022);
mksubsys("/var/run/sudo", 0711, "root", "root");
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
if (whichp("restorecon"))
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
umask(prev);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_MOUNT_POST] = { .cb = clean }, .hook[HOOK_BASEFS_UP] = {
.hook[HOOK_BASEFS_UP] = { .cb = setup }, .cb = setup
},
.depends = { "pidfile" }, .depends = { "pidfile" },
}; };
+11 -28
View File
@@ -22,11 +22,7 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "config.h" #include "config.h"
@@ -36,27 +32,13 @@
#include "conf.h" #include "conf.h"
#define DAEMON "dbus-daemon" #define DAEMON "dbus-daemon"
#define ARGS "--nofork --system --syslog-only" #define ARGS "--nofork --system"
#define DESC "D-Bus message bus daemon" #define DESC "D-Bus message bus daemon"
#ifndef DAEMONUSER
#define DAEMONUSER "messagebus"
#endif
#ifndef DAEMONPIDFILE
#define DAEMONPIDFILE "/var/run/dbus/pid"
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
char line[256];
mode_t prev;
char *cmd; char *cmd;
char line[256];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
cmd = which(DAEMON); cmd = which(DAEMON);
if (!cmd) { if (!cmd) {
@@ -64,28 +46,29 @@ static void setup(void *arg)
return; return;
} }
prev =umask(0); umask(0);
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER); makedir("/var/run/dbus", 0755);
makedir("/var/lock/subsys", 0755);
makedir("/var/lock/subsys/messagebus", 0755);
if (whichp("dbus-uuidgen")) if (whichp("dbus-uuidgen"))
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus"); run("dbus-uuidgen --ensure");
/* Clean up from any previous pre-bootstrap run */ /* Clean up from any previous pre-bootstrap run */
remove(DAEMONPIDFILE); erase("/var/run/dbus/pid");
/* Register service with Finit */ /* Register service with Finit */
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s", snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
DAEMONPIDFILE, DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL)) if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON); _pe("Failed registering %s", DAEMON);
free(cmd); free(cmd);
umask(prev); umask(022);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_NETWORK_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", }, .depends = { "bootmisc", },
+3 -9
View File
@@ -34,11 +34,6 @@ static void setup(void *arg)
char cmd[256]; char cmd[256];
char *path; char *path;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
/* /*
* Populate /dev and prepare for runtime events from kernel. * Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system. * Prefer udev if mdev is also available on the system.
@@ -48,17 +43,17 @@ static void setup(void *arg)
path = which("/lib/systemd/systemd-udevd"); path = which("/lib/systemd/systemd-udevd");
if (path) { if (path) {
/* Register udevd as a monitored service */ /* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s " snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd name:udevd log %s "
"-- Device event managing daemon", path); "-- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) { if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path); _pe("Failed registering %s", path);
} else { } else {
snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log " snprintf(cmd, sizeof(cmd), ":1 [S] <pid/udevd> log "
"udevadm trigger -c add -t devices " "udevadm trigger -c add -t devices "
"-- Requesting device events"); "-- Requesting device events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL); service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log " snprintf(cmd, sizeof(cmd), ":2 [S] <pid/udevd> log "
"udevadm trigger -c add -t subsystems " "udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events"); "-- Requesting subsystem events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL); service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
@@ -95,7 +90,6 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc" },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+1 -5
View File
@@ -40,15 +40,11 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */ #include <sys/time.h> /* gettimeofday() */
#include <sys/types.h> #include <sys/types.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
#include "util.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
struct module { struct module {
+16 -25
View File
@@ -21,14 +21,10 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <lite/lite.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -40,48 +36,43 @@
#define MODULES_LOAD_PATH "/etc/modules-load.d" #define MODULES_LOAD_PATH "/etc/modules-load.d"
#endif #endif
#define SERVICE_LINE \ #define SERVICE_LINE \
"cgroup.init :%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s" ":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
static void load(void *arg) static void load(void *arg)
{ {
struct dirent *d; FILE *fp;
DIR *dirp;
struct dirent *direntp;
char line[256]; char line[256];
int index = 1; int index = 1;
FILE *fp;
DIR *dir;
_d("Scanning " MODULES_LOAD_PATH " for config files ..."); _d("Scanning " MODULES_LOAD_PATH " for config files ...");
dir = opendir(MODULES_LOAD_PATH); dirp = opendir(MODULES_LOAD_PATH);
if (!dir) if (!dirp) return;
return;
while ((d = readdir(dir))) { while ((direntp = readdir(dirp)))
{
char module_path[PATH_MAX]; char module_path[PATH_MAX];
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path)); strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, d->d_name, sizeof(module_path)); strlcat(module_path, direntp->d_name, sizeof(module_path));
fp = fopen(module_path, "r"); fp = fopen(module_path, "r");
if (!fp) if (!fp) continue;
continue;
while (fgets(line, sizeof(line), fp)) { while (fgets(line, sizeof(line), fp)) {
char cmd[CMD_SIZE], *mod, *args; char cmd[CMD_SIZE], *mod, *args;
mod = strip_line(line); mod = strip_line(line);
if (!*mod) if (!*mod) continue;
continue;
mod = chomp(mod); mod = chomp(mod);
if (!mod || !*mod) if (!mod || !*mod) continue;
continue;
mod = strtok_r(mod, " ", &args); mod = strtok_r(mod, " ", &args);
if (!mod)
continue;
snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod); snprintf(cmd, sizeof(cmd), SERVICE_LINE,
index, mod, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL); service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
index++; index++;
@@ -90,7 +81,7 @@ static void load(void *arg)
fclose(fp); fclose(fp);
} }
closedir(dir); closedir(dirp);
} }
static plugin_t plugin = { static plugin_t plugin = {
+46 -267
View File
@@ -17,7 +17,6 @@
*/ */
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <net/if.h> /* IFNAMSIZ */ #include <net/if.h> /* IFNAMSIZ */
#include <sys/socket.h> #include <sys/socket.h>
@@ -30,35 +29,31 @@
#include "cond.h" #include "cond.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "service.h"
#define NL_BUFSZ 4096 static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
{
if (!NLMSG_OK(nh, len))
return 1;
struct nl_request { if (nh->nlmsg_type == NLMSG_DONE) {
struct nlmsghdr nh; _d("Done with netlink messages.");
union { return 1;
struct rtmsg rtm; }
struct ifinfomsg ifi;
};
};
static int nl_defidx; if (nh->nlmsg_type == NLMSG_ERROR) {
static int nl_ifdown; _d("Netlink reports error.");
static char *nl_buf; return 1;
}
return 0;
}
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len) static void nl_route(struct nlmsghdr *nlmsg)
{ {
char daddr[INET_ADDRSTRLEN];
char gaddr[INET_ADDRSTRLEN];
struct in_addr ind, ing;
struct rtmsg *r; struct rtmsg *r;
struct rtattr *a; struct rtattr *a;
int plen = 0;
int dst = 0;
int idx = 0;
int gw = 0;
int la; int la;
int gw = 0, dst = 0, mask = 0, idx = 0;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
_e("Packet too small or truncated!"); _e("Packet too small or truncated!");
@@ -68,14 +63,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
r = NLMSG_DATA(nlmsg); r = NLMSG_DATA(nlmsg);
a = RTM_RTA(r); a = RTM_RTA(r);
la = RTM_PAYLOAD(nlmsg); la = RTM_PAYLOAD(nlmsg);
if (la >= len) {
_e("Packet too large!");
return;
}
while (RTA_OK(a, la)) { while (RTA_OK(a, la)) {
void *data = RTA_DATA(a); void *data = RTA_DATA(a);
switch (a->rta_type) { switch (a->rta_type) {
case RTA_GATEWAY: case RTA_GATEWAY:
gw = *((int *)data); gw = *((int *)data);
@@ -84,8 +73,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
case RTA_DST: case RTA_DST:
dst = *((int *)data); dst = *((int *)data);
plen = r->rtm_dst_len; mask = r->rtm_dst_len;
//_d("Prefix LEN: 0x%04x", plen); //_d("MASK: 0x%04x", mask);
break; break;
case RTA_OIF: case RTA_OIF:
@@ -97,20 +86,11 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
a = RTA_NEXT(a, la); a = RTA_NEXT(a, la);
} }
ind.s_addr = dst; if ((!dst && !mask) && (gw || idx)) {
ing.s_addr = gw; if (nlmsg->nlmsg_type == RTM_DELROUTE)
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
if ((!dst && !plen) && (gw || idx)) {
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
cond_clear("net/route/default"); cond_clear("net/route/default");
nl_defidx = 0; else
} else {
cond_set("net/route/default"); cond_set("net/route/default");
nl_defidx = idx;
}
} }
} }
@@ -125,45 +105,12 @@ static void net_cond_set(char *ifname, char *cond, int set)
cond_clear(msg); cond_clear(msg);
} }
static int validate_ifname(const char *ifname) static void nl_link(struct nlmsghdr *nlmsg)
{ {
if (!ifname || !ifname[0])
return 1;
if (strnlen(ifname, IFNAMSIZ) == IFNAMSIZ)
return 1;
if (!strcmp(ifname, ".") || !strcmp(ifname, ".."))
return 1;
while (*ifname) {
if (*ifname == '/' || *ifname == ':' || isspace(*ifname))
return 1;
ifname++;
}
return 0;
}
/*
* Check if this interface was associated with the default route
* previously, or if it's been removed. If so, trigger a recheck
* of the system default route.
*/
static void nl_check_default(char *ifname)
{
int idx = (int)if_nametoindex(ifname);
if ((nl_defidx > 0 && nl_defidx == idx) || (idx == 0 && errno == ENODEV))
nl_ifdown = 1;
}
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
{
char ifname[IFNAMSIZ + 1];
struct ifinfomsg *i;
struct rtattr *a;
int la; int la;
char ifname[IFNAMSIZ + 1];
struct rtattr *a;
struct ifinfomsg *i;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
_e("Packet too small or truncated!"); _e("Packet too small or truncated!");
@@ -173,21 +120,10 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
i = NLMSG_DATA(nlmsg); i = NLMSG_DATA(nlmsg);
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg))); a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg)); la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
if (la >= len) {
_e("Packet too large!");
return;
}
for (; RTA_OK(a, la); a = RTA_NEXT(a, la)) {
if (a->rta_type != IFLA_IFNAME)
continue;
while (RTA_OK(a, la)) {
if (a->rta_type == IFLA_IFNAME) {
strlcpy(ifname, RTA_DATA(a), sizeof(ifname)); strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
if (validate_ifname(ifname)) {
_d("Invalid interface name '%s', skipping ...", ifname);
continue;
}
switch (nlmsg->nlmsg_type) { switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK: case RTM_NEWLINK:
/* /*
@@ -198,8 +134,6 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
net_cond_set(ifname, "exist", 1); net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP); net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING); net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
if (!(i->ifi_flags & IFF_UP) || !(i->ifi_flags & IFF_RUNNING))
nl_check_default(ifname);
break; break;
case RTM_DELLINK: case RTM_DELLINK:
@@ -208,7 +142,6 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
net_cond_set(ifname, "exist", 0); net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0); net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0); net_cond_set(ifname, "running", 0);
nl_check_default(ifname);
break; break;
case RTM_NEWADDR: case RTM_NEWADDR:
@@ -224,178 +157,31 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
break; break;
} }
} }
a = RTA_NEXT(a, la);
} }
static int nl_parse(int sd)
{
while (1) {
struct nlmsghdr *nh;
ssize_t len;
size_t l;
while ((len = recv(sd, nl_buf, NL_BUFSZ, 0)) < 0) {
switch (errno) {
case EAGAIN: /* Nothing more right now. */
return 0;
case EINTR: /* Signal */
continue;
case ENOBUFS: /* netlink(7) */
break;
default:
_pe("recv()");
break;
}
return -1;
}
_d("recv %lld bytes", len);
l = (size_t)len;
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
struct nlmsgerr *nle;
switch (nh->nlmsg_type) {
case NLMSG_DONE:
_d("Done with netlink messages.");
return 0;
case NLMSG_ERROR:
_d("Kernel netlink comm. error.");
nle = NLMSG_DATA(nh);
if (nle) {
errno = -nle->error;
_pe("Kernel netlink error %d", errno);
}
return -1;
case RTM_NEWROUTE:
case RTM_DELROUTE:
_d("Netlink route ...");
nl_route(nh, len);
break;
case RTM_NEWLINK:
case RTM_DELLINK:
_d("Netlink link ...");
nl_link(nh, len);
break;
default:
_w("unhandled netlink message, type %d", nh->nlmsg_type);
break;
}
}
}
}
static int nl_request(int sd, unsigned int seq, int type)
{
struct nl_request *nlr = (struct nl_request *)nl_buf;
memset(nlr, 0, sizeof(struct nl_request));
nlr->nh.nlmsg_type = type;
nlr->nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
nlr->nh.nlmsg_seq = seq;
nlr->nh.nlmsg_pid = 1;
switch (type) {
case RTM_GETROUTE:
_d("RTM_GETROUTE");
nlr->rtm.rtm_family = AF_INET;
nlr->rtm.rtm_table = RT_TABLE_MAIN;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
break;
case RTM_GETLINK:
_d("RTM_GETLINK");
nlr->ifi.ifi_family = AF_UNSPEC;
nlr->ifi.ifi_change = 0xFFFFFFFF;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
break;
default:
_w("Cannot resync, unhandled message type %d", type);
return -1;
}
if (send(sd, nlr, nlr->nh.nlmsg_len, 0) < 0)
return 1;
return nl_parse(sd);
}
static void nl_resync_routes(int sd, unsigned int seq)
{
if (nl_request(sd, seq, RTM_GETROUTE))
_pe("Failed netlink route request");
}
static void nl_resync_ifaces(int sd, unsigned int seq)
{
if (nl_request(sd, seq, RTM_GETLINK))
_pe("Failed netlink link request");
}
/*
* We've potentially lost netlink events, let's resync with kernel.
*/
static void nl_resync(int all)
{
unsigned int seq = 0;
int sd;
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (sd < 0) {
_pe("netlink socket");
return;
}
if (all) {
_d("============================ RESYNC =================================");
/* this doesn't update conditions, and thus does not stop services */
cond_deassert("net/");
nl_resync_ifaces(sd, seq++);
nl_resync_routes(sd, seq++);
/* delayed update after we've corrected things */
service_step_all(SVC_TYPE_ANY);
_d("=========================== RESYNCED ================================");
} else
nl_resync_routes(sd, seq++);
close(sd);
} }
static void nl_callback(void *arg, int sd, int events) static void nl_callback(void *arg, int sd, int events)
{ {
if (nl_parse(sd) < 0) { ssize_t len;
if (errno == ENOBUFS) { /* netlink(7) */ static char buf[4096];
_w("busy system, resynchronizing with kernel."); struct nlmsghdr *nh;
nl_resync(1);
memset(buf, 0, sizeof(buf));
len = recv(sd, buf, sizeof(buf), 0);
if (len < 0) {
if (errno != EINTR) /* Signal */
_pe("recv()");
return; return;
} }
}
/* for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
* Linux doesn't send route changes when interfaces go down, so //_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
* we need to check ourselves, e.g. for loss of default route. if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
*/ nl_route(nh);
if (nl_ifdown) { else
_d("interface down, checking default route."); nl_link(nh);
if (nl_defidx > 0) {
nl_defidx = 0;
nl_resync(0);
if (nl_defidx <= 0) {
cond_clear("net/route/default");
nl_defidx = 0;
}
}
nl_ifdown = 0;
} }
} }
@@ -415,8 +201,8 @@ static plugin_t plugin = {
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
{ {
struct sockaddr_nl sa;
int sd; int sd;
struct sockaddr_nl sa;
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE); sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sd < 0) { if (sd < 0) {
@@ -426,7 +212,7 @@ PLUGIN_INIT(plugin_init)
memset(&sa, 0, sizeof(sa)); memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; // | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR;
sa.nl_pid = getpid(); sa.nl_pid = getpid();
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
@@ -435,13 +221,6 @@ PLUGIN_INIT(plugin_init)
return; return;
} }
nl_buf = malloc(NL_BUFSZ);
if (!nl_buf) {
_pe("malloc()");
close(sd);
return;
}
plugin.io.fd = sd; plugin.io.fd = sd;
plugin_register(&plugin); plugin_register(&plugin);
} }
+14 -35
View File
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
} }
} }
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE); return iwatch_add(iw, path, IN_ONLYDIR);
} }
static void pidfile_update_conds(char *dir, char *name, uint32_t mask) static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
svc_t *svc; svc_t *svc;
paste(fn, sizeof(fn), dir, name); paste(fn, sizeof(fn), dir, name);
_d("path: %s, mask: %08x", fn, mask);
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0)) if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
return; return;
_d("path: %s, mask: %08x", fn, mask);
svc = svc_find_by_pidfile(fn); svc = svc_find_by_pidfile(fn);
if (!svc) { if (!svc) {
_d("No matching svc for %s", fn); _d("No matching svc for %s", fn);
@@ -82,28 +82,16 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid); _d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) { if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
svc_started(svc); svc_started(svc);
if (!svc_has_pidfile(svc)) {
_d("Setting %s PID file to %s", svc->name, fn);
pid_file_set(svc, fn, 1);
}
if (svc_is_forking(svc)) { if (svc_is_forking(svc)) {
pid_t pid; pid_t pid;
pid = pid_file_read(pid_file(svc)); pid = pid_file_read(pid_file(svc));
if (pid == -1) {
cond_clear(cond);
return;
}
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d", _d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid); svc->cmd, svc->pid, pid);
svc->pid = pid; svc->pid = pid;
} }
}
cond_set(cond); cond_set(cond);
} else if (mask & IN_DELETE) } else if (mask & IN_DELETE)
@@ -163,8 +151,7 @@ static void pidfile_callback(void *arg, int fd, int events)
{ {
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev; struct inotify_event *ev;
ssize_t sz; ssize_t sz, off;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
@@ -173,19 +160,15 @@ static void pidfile_callback(void *arg, int fd, int events)
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) { for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp; struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off]; ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask) if (!ev->mask)
continue; continue;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
/* Find base path for this event */ /* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd); iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
if (!iwp) if (!iwp)
@@ -196,7 +179,12 @@ static void pidfile_callback(void *arg, int fd, int events)
continue; continue;
} }
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) if (ev->mask & IN_DELETE) {
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
continue;
}
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
pidfile_update_conds(iwp->path, ev->name, ev->mask); pidfile_update_conds(iwp->path, ev->name, ev->mask);
} }
} }
@@ -238,14 +226,8 @@ static void pidfile_reconf(void *arg)
static void pidfile_init(void *arg) static void pidfile_init(void *arg)
{ {
char piddir[MAX_ARG_LEN];
char *path; char *path;
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
return;
}
/* /*
* The bootmisc plugin is responsible for setting up /var/run. * The bootmisc plugin is responsible for setting up /var/run.
* and/or /run, with proper symlinks etc. We depend on bootmisc * and/or /run, with proper symlinks etc. We depend on bootmisc
@@ -253,7 +235,6 @@ static void pidfile_init(void *arg)
*/ */
path = realpath(_PATH_VARRUN, NULL); path = realpath(_PATH_VARRUN, NULL);
if (!path) { if (!path) {
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
path = realpath("/run", NULL); path = realpath("/run", NULL);
if (!path) { if (!path) {
_e("System does not have %s or /run, aborting.", _PATH_VARRUN); _e("System does not have %s or /run, aborting.", _PATH_VARRUN);
@@ -263,8 +244,6 @@ static void pidfile_init(void *arg)
if (pidfile_add_path(&iw_pidfile, path)) if (pidfile_add_path(&iw_pidfile, path))
iwatch_exit(&iw_pidfile); iwatch_exit(&iw_pidfile);
free(path);
} }
/* /*
+4 -18
View File
@@ -22,24 +22,14 @@
*/ */
#include <glob.h> #include <glob.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h"
#include "helpers.h"
#include "plugin.h" #include "plugin.h"
static void setup(void *arg) static void setup(void *arg)
{ {
size_t i;
glob_t gl; glob_t gl;
char buf[160];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
glob("/run/sysctl.d/*.conf", 0, NULL, &gl); glob("/run/sysctl.d/*.conf", 0, NULL, &gl);
glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl); glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
@@ -49,13 +39,9 @@ static void setup(void *arg)
glob("/mnt/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl); glob("/mnt/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
glob("/etc/sysctl.conf", GLOB_APPEND, NULL, &gl); glob("/etc/sysctl.conf", GLOB_APPEND, NULL, &gl);
if (gl.gl_pathc > 0) { if (gl.gl_pathc > 0) {
size_t i;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
char cmd[160]; snprintf(buf, sizeof(buf), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]);
run(buf);
snprintf(cmd, sizeof(cmd), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]);
run(cmd, "sysctl");
} }
globfree(&gl); globfree(&gl);
} }
-4
View File
@@ -21,11 +21,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
+1 -25
View File
@@ -24,11 +24,7 @@
#include <time.h> #include <time.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/rtc.h> #include <linux/rtc.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -88,11 +84,6 @@ static void rtc_save(void *arg)
int fd, rc = 0; int fd, rc = 0;
char tz[128]; char tz[128];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
fd = rtc_open(); fd = rtc_open();
if (fd < 0) if (fd < 0)
return; return;
@@ -130,11 +121,6 @@ static void rtc_restore(void *arg)
int fd, rc = 0; int fd, rc = 0;
char tz[128]; char tz[128];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
fd = rtc_open(); fd = rtc_open();
if (fd < 0) if (fd < 0)
return; return;
@@ -156,18 +142,8 @@ static void rtc_restore(void *arg)
logit(LOG_ERR, "RTC has no previously saved (valid) time."); logit(LOG_ERR, "RTC has no previously saved (valid) time.");
else else
logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno)); logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno));
/* Been here already? */
if (rc)
goto out;
/* Attempt to set RTC to a sane value ... */
tv.tv_sec = RTC_TIMESTAMP_BEGIN_2000;
if (!gmtime_r(&tv.tv_sec, &tm))
goto out;
logit(LOG_NOTICE, "Resetting RTC to kernel default, 2000-01-01 00:00.");
rc = 2; rc = 2;
goto out;
} }
print_desc(NULL, "Restoring system clock (UTC) from RTC"); print_desc(NULL, "Restoring system clock (UTC) from RTC");
-213
View File
@@ -1,213 +0,0 @@
/* System condition event monitor for the Finit condition engine
*
* Copyright (c) 2021 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 <dirent.h>
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "plugin.h"
#include "iwatch.h"
static struct iwatch iw_sys;
static int sys_add_path(struct iwatch *iw, char *path)
{
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
}
/*
* unlink conditions with no active task/service, otherwise they may
* trigger inadvertently when calling `initctl reload` to activate
* a new configuration.
*/
static void sys_update_conds(char *dir, char *name, uint32_t mask)
{
char path[256];
char *cond;
char *ptr;
ptr = strrchr(name, '/');
if (ptr)
snprintf(path, sizeof(path), "%s/%s", dir, ++ptr);
else
snprintf(path, sizeof(path), "%s/%s", dir, name);
cond = strstr(path, COND_BASE);
if (!cond)
return;
cond += strlen(COND_BASE) + 1;
_d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0);
if (!cond_update(cond))
unlink(path);
}
/* synthesize events in case of new run dirs */
static void sys_scandir(struct iwatch *iw, char *dir, int len)
{
glob_t gl;
size_t i;
char path[len + 6];
int rc;
snprintf(path, sizeof(path), "%s/*", dir);
rc = glob(path, GLOB_NOSORT, NULL, &gl);
if (rc && rc != GLOB_NOMATCH)
return;
for (i = 0; i < gl.gl_pathc; i++) {
_d("scan found %s", gl.gl_pathv[i]);
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
}
globfree(&gl);
}
/*
* create/remove sub-directory in monitored directory
*/
static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
{
char path[strlen(dir) + strlen(name) + 2];
struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name);
_d("path: %s", path);
iwp = iwatch_find_by_path(iw, path);
if (mask & IN_CREATE) {
if (!iwp) {
if (!sys_add_path(iw, path))
sys_scandir(iw, path, sizeof(path));
}
} else if (mask & IN_DELETE) {
if (iwp)
iwatch_del(&iw_sys, iwp);
}
}
static void sys_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_sys, ev->wd);
if (!iwp)
continue;
if (ev->mask & IN_ISDIR) {
sys_handle_dir(&iw_sys, iwp->path, ev->name, ev->mask);
continue;
}
if (ev->mask & (IN_CREATE | IN_DELETE))
sys_update_conds(iwp->path, ev->name, ev->mask);
}
}
static void sys_init(void *arg)
{
char sysdir[MAX_ARG_LEN];
char *path;
_d("Calling mkpath() on sys conds");
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
return;
}
_d("Calling realpth() on condsys");
path = realpath(_PATH_CONDSYS, NULL);
if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
return;
}
_d("Calling iwatch_add()");
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
iwatch_exit(&iw_sys);
_d("Done.");
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
};
PLUGIN_INIT(plugin_init)
{
int fd;
fd = iwatch_init(&iw_sys);
if (fd < 0)
return;
plugin.io.fd = fd;
plugin.io.cb = sys_callback;
plugin.io.flags = PLUGIN_IO_READ;
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_sys);
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+24 -49
View File
@@ -27,23 +27,18 @@
#include <unistd.h> /* read() */ #include <unistd.h> /* read() */
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "service.h"
#include "tty.h" #include "tty.h"
static void tty_watcher(void *arg, int fd, int events); static void watcher(void *arg, int fd, int events);
static plugin_t plugin = { static plugin_t plugin = {
.io = { .io = {
.cb = tty_watcher, .cb = watcher,
.flags = PLUGIN_IO_READ, .flags = PLUGIN_IO_READ,
}, },
}; };
@@ -58,53 +53,33 @@ static void setup(void)
_pe("Failed starting TTY watcher"); _pe("Failed starting TTY watcher");
} }
static void do_tty(char *tty, size_t len, int creat) static void watcher(void *arg, int fd, int events)
{ {
char name[len + 6]; char buf[EVENT_SIZE];
svc_t *svc; int len = 0;
snprintf(name, sizeof(name), "/dev/%s", tty); while ((len = read(fd, buf, sizeof(buf)))) {
svc = svc_find_by_tty(name); struct inotify_event *notified = (struct inotify_event *)buf;
if (svc) { char name[notified->len + 6];
if (svc_is_blocked(svc) && creat) { struct tty *entry;
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
svc_start(svc);
} else if (svc->pid) {
_d("%s: missing ...", svc_ident(svc, NULL, 0));
svc_missing(svc);
}
service_step_all(SVC_TYPE_TTY); if (-1 == len) {
} if (errno == EINVAL)
} setup();
if (errno == EINTR)
static void tty_watcher(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue; continue;
_d("tty %s, event: 0x%08x", ev->name, ev->mask); break; /* Likely EAGAIN */
do_tty(ev->name, ev->len, ev->mask & IN_CREATE); }
snprintf(name, sizeof(name), "/dev/%s", notified->name);
entry = tty_find(name);
if (entry && tty_enabled(entry)) {
if (notified->mask & IN_CREATE)
tty_start(entry);
else if (entry->pid)
tty_stop(entry);
}
} }
} }
+7 -89
View File
@@ -1,4 +1,4 @@
/* Setup and save random seed at boot/shutdown /* Setup random seed
* *
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
@@ -22,46 +22,25 @@
*/ */
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */ #include <sys/time.h> /* gettimeofday() */
#include <sys/types.h> #include <sys/types.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include <linux/random.h>
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#ifndef RANDOM_BYTES
#define RANDOM_BYTES (32*1024)
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
struct rand_pool_info *rpi;
ssize_t len = 0;
int rc = -1;
int fd, err;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (!fexist(RANDOMSEED)) { if (!fexist(RANDOMSEED)) {
int ret = 1; int ret = 1;
mode_t prev;
FILE *fp; FILE *fp;
print_desc("Bootstrapping random seed", NULL); print_desc("Bootstrapping random seed", NULL);
prev = umask(077); umask(077);
fp = fopen(RANDOMSEED, "w"); fp = fopen(RANDOMSEED, "w");
if (fp) { if (fp) {
int iter = 128; int iter = 128;
@@ -78,82 +57,21 @@ static void setup(void *arg)
ret = fclose(fp); ret = fclose(fp);
} }
print_result(ret); print_result(ret);
umask(prev); umask(0);
} }
print_desc("Initializing random number generator", NULL); print_desc("Initializing random number generator", NULL);
print_result(512 != copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
/*
* Simply copying our saved entropy to /dev/urandom doesn't
* increment the kernel "entropy count", instead we must use
* the ioctl below.
*/
fd = open(RANDOMSEED, O_RDONLY);
if (fd < 0)
goto fallback;
rpi = malloc(sizeof(*rpi) + RANDOM_BYTES);
if (!rpi) {
close(fd);
goto fallback;
}
do {
ssize_t num;
num = read(fd, &rpi->buf[len], RANDOM_BYTES - len);
if (num <= 0) {
if (num == -1 && errno == EINTR)
continue;
if (len > 0)
break;
close(fd);
free(rpi);
goto fallback;
}
len += num;
} while (len < RANDOM_BYTES);
close(fd);
fd = open("/dev/urandom", O_WRONLY);
if (fd < 0) {
free(rpi);
goto fallback;
}
rpi->buf_size = len;
rpi->entropy_count = len * 8;
rc = ioctl(fd, RNDADDENTROPY, rpi);
err = errno;
close(fd);
free(rpi);
if (rc < 0)
logit(LOG_ERR, "Failed adding entropy to kernel random pool: %s", strerror(err));
print_result(rc < 0);
return;
fallback:
print_result(512 > copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
#endif #endif
} }
static void save(void *arg) static void save(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
mode_t prev; umask(077);
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
prev = umask(077);
print_desc("Saving random seed", NULL); print_desc("Saving random seed", NULL);
print_result(RANDOM_BYTES != copyfile("/dev/urandom", RANDOMSEED, RANDOM_BYTES, 0)); print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
umask(0);
umask(prev);
#endif #endif
} }
-135
View File
@@ -1,135 +0,0 @@
/* User-defined condition event monitor for the Finit condition engine
*
* Copyright (c) 2021 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 <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "plugin.h"
#include "iwatch.h"
static struct iwatch iw_usr;
static void usr_cond(char *name, uint32_t mask)
{
char cond[MAX_COND_LEN] = COND_USR;
strlcat(cond, name, sizeof(cond));
cond_update(cond);
}
static void usr_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
if (ev->mask & IN_ISDIR)
continue; /* unsupported */
if (ev->mask & IN_DELETE)
sync(); /* dont ask */
usr_cond(ev->name, ev->mask);
}
}
static void usr_init(void *arg)
{
char usrdir[MAX_ARG_LEN];
char *path;
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
return;
}
path = realpath(_PATH_CONDUSR, NULL);
if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
return;
}
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
iwatch_exit(&iw_usr);
free(path);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
};
PLUGIN_INIT(plugin_init)
{
int fd;
fd = iwatch_init(&iw_usr);
if (fd < 0)
return;
plugin.io.fd = fd;
plugin.io.cb = usr_callback;
plugin.io.flags = PLUGIN_IO_READ;
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_usr);
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+6 -16
View File
@@ -24,11 +24,7 @@
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -36,19 +32,13 @@
static void setup(void *arg) static void setup(void *arg)
{ {
const char *username = "nobody";
char line[LINE_SIZE];
mode_t prev;
#ifdef PAM_CONSOLE #ifdef PAM_CONSOLE
int fd; int fd;
#endif #endif
char line[LINE_SIZE];
const char *username = "nobody";
if (rescue) { umask(0);
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
prev = umask(0);
makedir("/var/run/console", 01777); makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username); snprintf(line, sizeof(line), "/var/run/console/%s", username);
@@ -58,7 +48,7 @@ static void setup(void *arg)
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) { if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, username, strlen(username)); write(fd, username, strlen(username));
close(fd); close(fd);
run("pam_console_apply", "pam-console"); run("pam_console_apply");
} }
#endif #endif
@@ -66,9 +56,9 @@ static void setup(void *arg)
makedir("/tmp/.ICE-unix", 01777); makedir("/tmp/.ICE-unix", 01777);
if (whichp("restorecon")) if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix", "restorecon"); run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
umask(prev); umask(022);
} }
static plugin_t plugin = { static plugin_t plugin = {
-3
View File
@@ -1,11 +1,8 @@
.deps/* .deps/*
finit finit
getty
initctl initctl
keventd
logit logit
reboot reboot
sulogin
watchdogd watchdogd
Makefile Makefile
Makefile.in Makefile.in
+20 -37
View File
@@ -1,5 +1,6 @@
EXTRA_DIST = rescue.conf sample.conf EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC if STATIC
AM_CPPFLAGS += -DENABLE_STATIC AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs AM_LDFLAGS = -static-libtool-libs
@@ -8,41 +9,26 @@ AM_LDFLAGS = -export-dynamic
endif endif
sbin_PROGRAMS = finit initctl sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = getty logit sulogin pkglibexec_SCRIPTS = cgreaper.sh
if KEVENTD
pkglibexec_PROGRAMS += keventd
endif
if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd
endif
finit_pkglibdir = $(pkglibdir) finit_pkglibdir = $(pkglibdir)
finit_pkglib_SCRIPTS = rescue.conf sample.conf finit_pkglib_SCRIPTS = rescue.conf sample.conf
getty_SOURCES = finit.h getty.c helpers.h logrotate.c stty.c utmp-api.c utmp-api.h if WATCHDOGD
getty_CFLAGS = -W -Wall -Wextra -std=gnu99 pkglibexec_PROGRAMS = watchdogd
getty_CFLAGS += $(lite_CFLAGS) endif
getty_LDADD = $(lite_LIBS)
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h if LOGIT
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99 pkglibexec_PROGRAMS = logit
keventd_CFLAGS += $(lite_CFLAGS) logit_SOURCES = logit.c
keventd_LDADD = $(lite_LIBS)
sulogin_SOURCES = sulogin.c
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
sulogin_LDADD = -lcrypt
logit_SOURCES = logit.c logrotate.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
logit_CFLAGS += $(lite_CFLAGS) endif
logit_LDADD = $(lite_LIBS)
finit_SOURCES = api.c cgroup.c cgroup.h \ finit_SOURCES = api.c cgroup.c cgroup.h \
cond.c cond-w.c cond.h \ cond.c cond-w.c cond.h \
conf.c conf.h \ conf.c conf.h \
exec.c finit.c finit.h \ exec.c finit.c finit.h \
stty.c \ getty.c stty.c \
helpers.c helpers.h \ helpers.c helpers.h \
iwatch.c iwatch.h \ iwatch.c iwatch.h \
log.c log.h \ log.c log.h \
@@ -57,11 +43,7 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
tty.c tty.h \ tty.c tty.h \
util.c util.h \ util.c util.h \
utmp-api.c utmp-api.h utmp-api.c utmp-api.h
if LOGROTATE pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
finit_SOURCES += logrotate.c
endif
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
@@ -72,19 +54,20 @@ else
finit_LDADD += -ldl finit_LDADD += -ldl
endif endif
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \ initctl_SOURCES = initctl.c client.c client.h \
client.c client.h cond.c cond.h reboot.c \ reboot.c serv.c serv.h svc.h \
serv.c serv.h svc.h util.c util.h cond.c cond.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) initctl_CFLAGS += $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS) $(uev_LIBS) initctl_LDADD = $(lite_LIBS)
INIT_LNKS = init telinit INIT_LNKS = init
REBOOT_LNKS = reboot shutdown halt poweroff suspend REBOOT_LNKS = reboot shutdown halt poweroff suspend
if WATCHDOGD if WATCHDOGD
watchdogd_SOURCES = watchdog.c watchdog.h watchdogd_SOURCES = watchdog.c watchdog.h
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
#log.c log.h
endif endif
# Hook in install to add finit and reboot symlink(s) # Hook in install to add finit and reboot symlink(s)
+30 -98
View File
@@ -29,11 +29,7 @@
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include <uev/uev.h> #include <uev/uev.h>
#include "config.h" #include "config.h"
@@ -51,12 +47,12 @@
extern svc_t *wdog; extern svc_t *wdog;
static uev_t api_watcher; static uev_t api_watcher;
static int call(int (*action)(svc_t *, void *), char *buf, size_t len) static int call(int (*action)(svc_t *), char *buf, size_t len)
{ {
return svc_parse_jobstr(buf, len, NULL, action, NULL); return svc_parse_jobstr(buf, len, action, NULL);
} }
static int stop(svc_t *svc, void *user_data) static int stop(svc_t *svc)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -67,7 +63,7 @@ static int stop(svc_t *svc, void *user_data)
return 0; return 0;
} }
static int start(svc_t *svc, void *user_data) static int start(svc_t *svc)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -78,24 +74,7 @@ static int start(svc_t *svc, void *user_data)
return 0; return 0;
} }
/* static int restart(svc_t *svc)
* NOTE: this does not wait for svc to be stopped first, that is the
* responsibility of initctl to do. Otherwise we'd block PID 1,
* or introduce some nasty race conditions.
*/
static int restart(svc_t *svc, void *user_data)
{
if (!svc)
return 1;
svc_mark_dirty(svc);
svc_start(svc);
service_step(svc);
return 0;
}
static int reload(svc_t *svc, void *user_data)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -109,33 +88,12 @@ static int reload(svc_t *svc, void *user_data)
return 0; return 0;
} }
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
static int do_start (char *buf, size_t len) { return call(start, buf, len); } static int do_start (char *buf, size_t len) { return call(start, buf, len); }
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); } static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
static int do_reload (char *buf, size_t len) { return call(reload, buf, len); }
static int do_signal_svc(svc_t *svc, void *user_data)
{
int signo;
if (!svc || !user_data || !svc_is_running(svc))
return 1;
signo = *(int *)user_data;
return !!kill(svc->pid, signo);
}
static int do_signal(char *buf, size_t len, int sig)
{
/* Sanity check: do we know this signal? */
if (!*sig2str(sig))
return 1;
return svc_parse_jobstr(buf, len, &sig, do_signal_svc, NULL);
}
static char query_buf[368]; static char query_buf[368];
static int missing(char *job, char *id, void *user_data) static int missing(char *job, char *id)
{ {
char buf[20]; char buf[20];
@@ -152,8 +110,8 @@ static int missing(char *job, char *id, void *user_data)
static int do_query(char *buf, size_t len) static int do_query(char *buf, size_t len)
{ {
query_buf[0] = 0; memset(query_buf, 0, sizeof(query_buf));
if (svc_parse_jobstr(buf, len, NULL, NULL, missing)) { if (svc_parse_jobstr(buf, len, NULL, missing)) {
memcpy(buf, query_buf, len); memcpy(buf, query_buf, len);
return 1; return 1;
} }
@@ -279,7 +237,7 @@ ev_t ev_list[] = {
static void send_svc(int sd, svc_t *svc) static void send_svc(int sd, svc_t *svc)
{ {
svc_t empty = { 0 }; svc_t empty;
size_t len; size_t len;
if (!svc) { if (!svc) {
@@ -294,10 +252,10 @@ static void send_svc(int sd, svc_t *svc)
static void api_cb(uev_t *w, void *arg, int events) static void api_cb(uev_t *w, void *arg, int events)
{ {
static svc_t *iter = NULL;
struct init_request rq;
int sd, lvl; int sd, lvl;
svc_t *svc; svc_t *svc;
static svc_t *iter = NULL;
struct init_request rq;
sd = accept(w->fd, NULL, NULL); sd = accept(w->fd, NULL, NULL);
if (sd < 0) { if (sd < 0) {
@@ -369,22 +327,16 @@ static void api_cb(uev_t *w, void *arg, int events)
result = do_start(rq.data, sizeof(rq.data)); result = do_start(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_restart(rq.data, sizeof(rq.data));
break;
case INIT_CMD_STOP_SVC: case INIT_CMD_STOP_SVC:
_d("stop %s", rq.data); _d("stop %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_stop(rq.data, sizeof(rq.data)); result = do_stop(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RELOAD_SVC: case INIT_CMD_RESTART_SVC:
_d("reload %s", rq.data); _d("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_reload(rq.data, sizeof(rq.data)); result = do_restart(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_GET_RUNLEVEL: case INIT_CMD_GET_RUNLEVEL:
@@ -411,33 +363,27 @@ static void api_cb(uev_t *w, void *arg, int events)
break; break;
} }
_d("Request to hand-over wdog ... to PID %d", rq.runlevel); _e("Request to hand-over wdog ... to PID %d", rq.runlevel);
svc = svc_find_by_pid(rq.runlevel); if (!svc_find_by_pid(rq.runlevel)) {
if (!svc) {
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel); logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
break; break;
} }
if (wdog && wdog != svc) { /* Disable and allow Finit to collect bundled watchdog */
char name[32]; if (wdog) {
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
svc_ident(svc, name, sizeof(name)); stop(wdog);
logit(LOG_NOTICE, "Handing over wdog ctrl from %s[%d] to %s[%d]",
svc_ident(wdog, NULL, 0), wdog->pid, name, svc->pid);
if (wdog->protect) { if (wdog->protect) {
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog."); wdog->protect = 0;
stop(wdog, NULL); wdog->runlevels = 0;
svc_del(wdog);
} }
} }
wdog = svc;
break; break;
case INIT_CMD_SVC_ITER: case INIT_CMD_SVC_ITER:
// _d("svc iter, first: %d", rq.runlevel); _d("svc iter, first: %d", rq.runlevel);
/* /*
* XXX: This severely limits the number of * XXX: This severly limits the number of
* simultaneous client connections, but will * simultaneous client connections, but will
* have to do for now. * have to do for now.
*/ */
@@ -463,13 +409,6 @@ static void api_cb(uev_t *w, void *arg, int events)
send_svc(sd, do_find_byc(rq.data, sizeof(rq.data))); send_svc(sd, do_find_byc(rq.data, sizeof(rq.data)));
goto leave; goto leave;
case INIT_CMD_SIGNAL:
/* runlevel is reused for signal */
_d("svc signal %d: %s", rq.runlevel, rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
break;
default: default:
_d("Unsupported cmd: %d", rq.cmd); _d("Unsupported cmd: %d", rq.cmd);
break; break;
@@ -497,41 +436,34 @@ error:
int api_init(uev_ctx_t *ctx) int api_init(uev_ctx_t *ctx)
{ {
int sd;
mode_t oldmask;
struct sockaddr_un sun = { struct sockaddr_un sun = {
.sun_family = AF_UNIX, .sun_family = AF_UNIX,
.sun_path = INIT_SOCKET, .sun_path = INIT_SOCKET,
}; };
mode_t oldmask;
int uid, gid;
int sd;
_d("Setting up external API socket ..."); _d("Setting up external API socket ...");
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (-1 == sd) { if (-1 == sd) {
_pe("Failed starting external API socket"); _pe("Failed starting external API socket");
return 1; return 1;
} }
uid = geteuid();
gid = getgroup(DEFGROUP);
erase(INIT_SOCKET); erase(INIT_SOCKET);
oldmask = umask(0117); oldmask = umask(0077);
if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun))) if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun)))
goto error; goto error;
if (-1 == listen(sd, 10)) if (-1 == listen(sd, 10))
goto error; goto error;
if (chown(INIT_SOCKET, uid, gid))
_pe("Failed setting group %s on %s", DEFGROUP, INIT_SOCKET);
umask(oldmask); umask(oldmask);
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ)) if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
return 0; return 0;
error: error:
_pe("Failed initializing API socket"); _pe("Failed intializing API socket");
umask(oldmask); umask(oldmask);
close(sd); close(sd);
return 1; return 1;
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
# Called by kernel when last child in cgroup exists
BASE=/sys/fs/cgroup/finit
PROC=`basename $1`
DIR="$BASE$1"
/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
rmdir $DIR
exit 0
+76 -478
View File
@@ -24,514 +24,112 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
#include <lite/lite.h> #include <lite/lite.h>
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/sysinfo.h> /* get_nprocs_conf() */
#include "cgroup.h"
#include "finit.h"
#include "iwatch.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
struct cg { static int cg_init = 0;
TAILQ_ENTRY(cg) link;
char *name; /* top-level group name */
char *cfg; /* kernel settings */
int active; /* for mark & sweep */
int is_protected; /* for init/, user/, & system/ */
};
static TAILQ_HEAD(, cg) cgroups = TAILQ_HEAD_INITIALIZER(cgroups);
static char controllers[256];
static struct iwatch iw_cgroup;
static uev_t cgw;
static int avail;
static void cgset(const char *path, char *ctrl, char *prop)
{
char *val;
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
if (!path || !ctrl) {
_e("Missing path or controller, skipping!");
return;
}
if (!prop) {
prop = strchr(ctrl, '.');
if (!prop) {
_e("Invalid cgroup ctrl syntax: %s", ctrl);
return;
}
*prop++ = 0;
}
val = strchr(prop, ':');
if (!val) {
_e("Missing cgroup ctrl value, prop %s", prop);
return;
}
*val++ = 0;
/* disallow sneaky relative paths */
if (strstr(ctrl, "..") || strstr(prop, "..")) {
_e("Possible security violation; '..' not allowed in cgroup config!");
return;
}
_d("%s/%s.%s <= %s", path, ctrl, prop, val);
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
}
/*
* Settings for a cgroup are on the form: cpu.weight:1234,mem.max:4321,...
* Finit supports the short-form 'mem.', replacing it with 'memory.' when
* writing the setting to the file system.
*/
static void group_init(char *path, int leaf, const char *cfg)
{
char *ptr, *s;
_d("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
if (!fisdir(path)) {
if (mkdir(path, 0755)) {
_pe("Failed creating cgroup %s", path);
return;
}
/* enable detected controllers on domain groups */
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
_pe("Failed enabling %s for %s", controllers, path);
}
if (!cfg || !cfg[0])
return;
s = strdupa(cfg);
if (!s) {
_pe("Failed activating cgroup cfg for %s", path);
return;
}
_d("%s <=> %s", path, s);
ptr = strtok(s, ",");
while (ptr) {
_d("ptr: %s", ptr);
if (!strncmp("mem.", ptr, 4))
cgset(path, "memory", &ptr[4]);
else
cgset(path, ptr, NULL);
ptr = strtok(NULL, ",");
}
}
static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
{
char path[256];
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
if (pid < 0 || pid == 1) {
errno = EINVAL;
return 1;
}
/* create and initialize new group */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
group_init(path, 1, cfg);
/* move process to new group */
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
_pe("Failed moving pid %d to group %s", pid, path);
strlcat(path, "/cgroup.events", sizeof(path));
return iwatch_add(&iw_cgroup, path, 0);
}
int cgroup_user(char *name, int pid)
{
if (!avail)
return 0;
return cgroup_leaf_init("user", name, pid, NULL);
}
int cgroup_service(char *name, int pid, struct cgroup *cg)
{
char *group = "system";
if (!avail)
return 0;
if (cg && cg->name[0]) {
char path[256];
if (!strcmp(cg->name, "root"))
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
if (!strcmp(cg->name, "init"))
return fnwrite(str("%d", pid), FINIT_CGPATH "/init/cgroup.procs");
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
if (fisdir(path))
group = cg->name;
}
return cgroup_leaf_init(group, name, pid, cg ? cg->cfg : NULL);
}
static void append_ctrl(char *ctrl)
{
if (controllers[0])
strlcat(controllers, " ", sizeof(controllers));
strlcat(controllers, "+", sizeof(controllers));
strlcat(controllers, ctrl, sizeof(controllers));
}
static void cgroup_handle_event(char *event, uint32_t mask)
{
char path[strlen(event) + 1];
char buf[80];
char *ptr;
FILE *fp;
_d("event: '%s', mask: %08x", event, mask);
if (!(mask & IN_MODIFY))
return;
fp = fopen(event, "r");
if (!fp) {
_d("Failed opening %s, skipping ...", event);
return;
}
while (fgets(buf, sizeof(buf), fp)) {
if (strncmp(buf, "populated", 9))
continue;
chomp(buf);
if (atoi(&buf[10]))
break;
strlcpy(path, event, sizeof(path));
ptr = strrchr(path, '/');
if (ptr) {
*ptr = 0;
if (!cgroup_del(path)) {
/*
* try with parent, top-level group, we
* may get events out-of-order *sigh*
*/
ptr = strrchr(path, '/');
if (!ptr)
break;
*ptr = 0;
cgroup_del(path);
}
}
break;
}
fclose(fp);
}
static void cgroup_events_cb(uev_t *w, void *arg, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_cgroup, ev->wd);
if (!iwp || !iwp->path)
continue;
cgroup_handle_event(iwp->path, ev->mask);
}
#ifdef ENABLE_AUTO_RELOAD
if (conf_any_change())
service_reload_dynamic();
#endif
}
static struct cg *cgroup_find(char *name)
{
struct cg *cg;
TAILQ_FOREACH(cg, &cgroups, link) {
if (strcmp(cg->name, name))
continue;
return cg;
}
return NULL;
}
/*
* Marks all unprotected cgroups for deletion (during reload)
*/
void cgroup_mark_all(void)
{
struct cg *cg;
if (!avail)
return;
TAILQ_FOREACH(cg, &cgroups, link) {
if (cg->is_protected)
continue;
cg->active = 0;
}
}
/*
* Remove (try to) all unused cgroups
*/
void cgroup_cleanup(void)
{
struct cg *cg, *tmp;
char path[256];
if (!avail)
return;
TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) {
if (cg->active)
continue;
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
cgroup_del(path);
}
}
/*
* Add, or update, settings for top-level cgroup
*/
int cgroup_add(char *name, char *cfg, int is_protected)
{
struct cg *cg;
if (!avail)
return 0;
if (!name)
return -1;
if (!cfg)
cfg = "";
cg = cgroup_find(name);
if (!cg) {
cg = malloc(sizeof(struct cg));
if (!cg) {
_pe("Failed allocating 'struct cg' for %s", name);
return -1;
}
cg->name = strdup(name);
if (!cg->name) {
_pe("Failed setting cgroup name %s", name);
free(cg);
return -1;
}
TAILQ_INSERT_TAIL(&cgroups, cg, link);
} else
free(cg->cfg);
cg->cfg = strdup(cfg);
if (!cg->cfg) {
_pe("Failed add/update of cgroup %s", name);
TAILQ_REMOVE(&cgroups, cg, link);
free(cg->name);
free(cg);
return -1;
}
cg->is_protected = is_protected;
cg->active = 1;
return 0;
}
/*
* Remove inactive top-level cgroup
*/
int cgroup_del(char *dir)
{
struct cg *cg;
char path[256];
if (!avail)
return 0;
TAILQ_FOREACH(cg, &cgroups, link) {
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
if (strcmp(path, dir))
continue;
if (cg->active)
return -1;
break;
}
if (rmdir(dir) && errno != ENOENT) {
_d("Failed removing %s: %s", dir, strerror(errno));
return -1;
}
if (cg) {
TAILQ_REMOVE(&cgroups, cg, link);
free(cg->name);
free(cg->cfg);
free(cg);
}
return 0;
}
/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */
void cgroup_config(void)
{
struct cg *cg;
if (!avail)
return;
TAILQ_FOREACH(cg, &cgroups, link) {
char path[256];
int leaf = 0;
if (!cg->active)
continue;
if (!strcmp(cg->name, "init"))
leaf = 1; /* reserved */
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, cg->name);
group_init(path, leaf, cg->cfg);
strlcat(path, "/cgroup.events", sizeof(path));
iwatch_add(&iw_cgroup, path, 0);
}
}
/* /*
* Called by Finit at early boot to mount initial cgroups * Called by Finit at early boot to mount initial cgroups
*/ */
void cgroup_init(uev_ctx_t *ctx) void cgroup_init(void)
{ {
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
char buf[80];
FILE *fp; FILE *fp;
int fd; char buf[80];
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { fp = fopen("/proc/cgroups", "r");
if (errno == ENOENT)
logit(LOG_NOTICE, "Kernel does not support cgroups v2, disabling.");
else if (errno == EPERM) /* Probably inside an unpriviliged container */
logit(LOG_NOTICE, "Not allowed to mount cgroups v2, disabling.");
else
_pe("Failed mounting cgroup v2");
avail = 0;
return;
}
avail = 1;
/* Find available controllers */
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
if (!fp) { if (!fp) {
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers"); _d("No cgroup support");
abort:
umount(FINIT_CGPATH);
avail = 0;
return; return;
} }
if (fgets(buf, sizeof(buf), fp)) { if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
_d("Failed mounting cgroups");
goto fail;
}
/* Skip first line, header */
fgets(buf, sizeof(buf), fp);
while (fgets(buf, sizeof(buf), fp)) {
char *cgroup; char *cgroup;
char rc[80];
cgroup = strtok(chomp(buf), "\t "); cgroup = strtok(buf, "\t ");
while (cgroup) { if (!cgroup)
append_ctrl(cgroup); continue;
cgroup = strtok(NULL, "\t ");
} snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
if (mkdir(rc, 0755) && EEXIST != errno)
continue;
if (mount("cgroup", rc, "cgroup", opts, cgroup))
_d("Failed mounting %s cgroup on %s", cgroup, rc);
} }
/* Default cgroups for process monitoring */
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
goto fail;
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
_pe("Failed mounting Finit cgroup hierarchy");
goto fail;
}
/* Set up reaper and enable callbacks */
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
/* Default groups, PID 1, services, and user/login processes */
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/user", 0755) && EEXIST != errno)
goto fail;
/* Move ourselves to init */
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
/* We have signal, main screen turn on! */
cg_init = 1;
fail:
fclose(fp); fclose(fp);
/* Check for cpu controller, abort if missing */
if (!strstr(controllers, "+cpu")) {
logit(LOG_NOTICE, "Missing CPU controller, disabling cgroup support.");
goto abort;
} }
/* Enable all controllers */ static int move_pid(char *group, char *name, char *id, int pid)
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) { {
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control"); char path[256];
goto abort;
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
if (mkdir(path, 0755) && errno != EEXIST)
return 1;
strlcat(path, "/cgroup.procs", sizeof(path));
return echo(path, 0, "%d", pid);
} }
/* prepare cgroup.events watcher */ int cgroup_user(char *name)
fd = iwatch_init(&iw_cgroup); {
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) { if (!cg_init)
_pe("Failed setting up cgroup.events watcher"); return 0;
iwatch_exit(&iw_cgroup);
close(fd); return move_pid("finit/user", name, "0", getpid());
goto abort;
} }
/* Default (protected) groups, PID 1, services, and user/login processes */ int cgroup_service(char *nm, char *id, int pid)
cgroup_add("init", "cpu.weight:100", 1); {
cgroup_add("system", "cpu.weight:9800", 1); if (!cg_init)
cgroup_add("user", "cpu.weight:100", 1); return 0;
cgroup_config();
/* Move ourselves to init (best effort, otherwise run in 'root' group */ if (pid <= 0) {
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) { errno = EINVAL;
_pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init"); return 1;
uev_io_stop(&cgw);
iwatch_exit(&iw_cgroup);
close(fd);
goto abort;
} }
return move_pid("finit/system", nm, id, pid);
} }
/** /**
+3 -17
View File
@@ -24,23 +24,9 @@
#ifndef FINIT_CGROUP_H_ #ifndef FINIT_CGROUP_H_
#define FINIT_CGROUP_H_ #define FINIT_CGROUP_H_
#include <uev/uev.h> void cgroup_init (void);
struct cgroup { int cgroup_user (char *name);
char name[16]; int cgroup_service (char *cmd, char *id, int pid);
char cfg[128];
};
void cgroup_mark_all(void);
void cgroup_cleanup (void);
int cgroup_add (char *name, char *cfg, int is_protected);
int cgroup_del (char *dir);
void cgroup_config (void);
void cgroup_init (uev_ctx_t *ctx);
int cgroup_user (char *name, int pid);
int cgroup_service (char *name, int pid, struct cgroup *cg);
#endif /* FINIT_CGROUP_H_ */ #endif /* FINIT_CGROUP_H_ */
-662
View File
@@ -1,662 +0,0 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2019-2021 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 "config.h"
#include <err.h>
#include <dirent.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <search.h>
#include <inttypes.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include <sys/sysinfo.h> /* sysinfo() */
#include "cgutil.h"
#include "initctl.h"
#define CDIM plain ? "" : "\e[2m"
#define CRST plain ? "" : "\e[0m"
#define NONE " "
#define PIPE plain ? "| " : "│ "
#define FORK plain ? "|-" : "├─"
#define END plain ? "`-" : "└─"
uint64_t total_ram; /* From sysinfo() */
struct cg dummy; /* empty result "NULL" */
struct cg *list;
int cgroup_avail(void)
{
return fismnt(FINIT_CGPATH);
}
char *pid_cmdline(int pid, char *buf, size_t len)
{
size_t i, sz;
char *ptr;
FILE *fp;
fp = fopenf("r", "/proc/%d/cmdline", pid);
if (!fp) {
buf[0] = 0;
return buf;
}
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
fclose(fp);
if (!sz)
return NULL; /* kernel thread */
buf[sz] = 0;
ptr = strchr(buf, 0);
if (ptr && ptr != buf) {
ptr++;
sz -= ptr - buf;
memmove(buf, ptr, sz + 1);
}
for (i = 0; i < sz; i++) {
if (buf[i] == 0)
buf[i] = ' ';
}
return buf;
}
char *pid_comm(int pid, char *buf, size_t len)
{
char *ptr = NULL;
FILE *fp;
fp = fopenf("r", "/proc/%d/comm", pid);
if (!fp)
return NULL;
if (fgets(buf, len, fp))
ptr = chomp(buf);
fclose(fp);
return ptr;
}
char *pid_cgroup(int pid, char *buf, size_t len)
{
char *ptr = NULL;
FILE *fp;
fp = fopenf("r", "/proc/%d/cgroup", pid);
if (!fp)
return NULL;
if (fgets(buf, len, fp))
ptr = chomp(buf);
fclose(fp);
if (ptr)
ptr = strchr(buf, '/');
return ptr;
}
static char *cgroup_val(char *path, char *file, char *buf, size_t len)
{
char *val = NULL;
FILE *fp;
fp = fopenf("r", "%s/%s", path, file);
if (fp) {
if (fgets(buf, len, fp)) {
val = chomp(buf);
len = strcspn(val, " \t");
val[len] = 0;
}
fclose(fp);
}
return val;
}
static uint64_t cgroup_uint64(char *path, char *file)
{
uint64_t val = 0;
char buf[42];
if (cgroup_val(path, file, buf, sizeof(buf)))
val = strtoull(buf, NULL, 10);
return val;
}
static char *cgroup_memval(char *path, char *file, char *buf, size_t len)
{
char data[42];
if (cgroup_val(path, file, data, sizeof(data))) {
if (!strcmp(data, "max"))
strlcpy(buf, data, len);
else
memsz(strtoull(data, NULL, 10), buf, len);
} else
buf[0] = 0;
return buf;
}
static uint64_t cgroup_memuse(struct cg *cg)
{
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
if (fp) {
cg->cg_rss = 0;
cg->cg_vmlib = 0;
while (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
if (!strncmp(buf, "anon", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "slab", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "kernel_stack", 12)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "pagetables", 10)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "percpu", 6)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "sock", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "file", 4)) {
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "file_mapped", 11)) {
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
continue;
}
}
fclose(fp);
}
cg->cg_memshare = (float)(cg->cg_rss * 100 / total_ram);
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.current");
}
uint64_t cgroup_memory(char *group)
{
char path[256];
paste(path, sizeof(path), FINIT_CGPATH, group);
return cgroup_uint64(path, "memory.current");
}
static float cgroup_cpuload(struct cg *cg)
{
char fn[256];
char buf[64];
FILE *fp;
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
fp = fopen(fn, "r");
if (!fp)
err(1, "Cannot open %s", fn);
while (fgets(buf, sizeof(buf), fp)) {
uint64_t curr;
chomp(buf);
if (strncmp(buf, "usage_usec", 10))
continue;
curr = strtoull(&buf[11], NULL, 10);
if (cg->cg_prev != 0) {
uint64_t diff = curr - cg->cg_prev;
/* this expects 1 sec poll interval */
cg->cg_load = (float)(diff / 1000000);
cg->cg_load *= 100.0;
}
cg->cg_prev = curr;
break;
}
fclose(fp);
return cg->cg_load;
}
static struct cg *append(char *path)
{
struct cg *cg;
char fn[256];
ENTRY item;
snprintf(fn, sizeof(fn), "%s/cpu.stat", path);
if (access(fn, F_OK)) {
/* older kernels, 4.19, don't have summary cpu.stat in root */
if (strcmp(path, FINIT_CGPATH))
warn("not a cgroup path with cpu controller, %s", path);
return NULL;
}
cg = calloc(1, sizeof(struct cg));
if (!cg)
err(1, "failed allocating struct cg");
cg->cg_path = strdup(path);
if (list)
cg->cg_next = list;
list = cg;
item.key = cg->cg_path;
item.data = cg;
if (!hsearch(item, ENTER))
err(1, "failed adding to hash table");
return cg;
}
static struct cg *find(char *path)
{
ENTRY *ep, item = { path, NULL };
ep = hsearch(item, FIND);
if (ep)
return ep->data;
return append(path);
}
/* update stats */
struct cg *cg_stats(char *path)
{
struct cg *cg;
cg = find(path);
if (!cg)
return &dummy;
cgroup_cpuload(cg);
cgroup_memuse(cg);
return cg;
}
/* query config */
struct cg *cg_conf(char *path)
{
static struct cg cg;
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
cgroup_memval(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
return &cg;
}
static int cgroup_filter(const struct dirent *entry)
{
/* Skip current dir ".", and prev dir "..", from list of files */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
if (entry->d_name[0] == '.')
return 0;
if (entry->d_type != DT_DIR)
return 0;
return 1;
}
int cgroup_tree(char *path, char *pfx, int mode, int pos)
{
struct dirent **namelist = NULL;
char s[32], r[32], l[32];
char row[ttcols + 9]; /* + control codes */
size_t rlen = sizeof(row) - 1;
size_t rplen = rlen - 9;
struct stat st;
struct cg *cg;
char buf[512];
int rc = 0;
FILE *fp;
int i, n;
int num;
if (pos >= ttrows)
return 0;
if (-1 == lstat(path, &st))
return 1;
if ((st.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
return -1;
}
fp = fopenf("r", "%s/cgroup.procs", path);
if (!fp)
return -1;
num = 0;
while (fgets(buf, sizeof(buf), fp))
num++;
if (!pfx) {
pfx = "";
switch (mode) {
case 1:
cg = cg_stats(path);
snprintf(row, rplen, "\r %6.6s %6.6s %6.6s %5.1f %5.1f %s",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_memshare, cg->cg_load, path);
break;
case 2:
cg = cg_conf(path);
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6s [%-6.6s%6.6s] %s",
memsz(cg->cg_vmsize, s, sizeof(s)),
cg->cg_mem.min, cg->cg_mem.max, cg->cg_cpu.set,
cg->cg_cpu.weight, cg->cg_cpu.max, path);
break;
default:
strlcpy(row, "\r", rplen);
strlcat(row, path, rplen);
break;
}
puts(row);
}
if (num > 0) {
rewind(fp);
i = 0;
while (fgets(buf, sizeof(buf), fp)) {
char comm[80] = { 0 };
pid_t pid;
pid = atoi(chomp(buf));
if (pid <= 0)
continue;
/* skip kernel threads for now (no cmdline) */
pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf))) {
char proc[ttcols];
int len;
switch (mode) {
case 1:
snprintf(row, rplen, "\r%37s", " ");
break;
case 2:
snprintf(row, rplen, "\r --.-- [ ] [ ] ");
break;
default:
strlcpy(row, "\r", rplen);
break;
}
strlcat(row, pfx, rplen);
strlcat(row, ++i == num ? END : FORK, rlen);
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf);
if (plain) {
strlcat(row, proc, rplen);
} else {
strlcat(row, CDIM, rlen);
strlcat(row, proc, rlen);
len = strlen(row) + strlen(CRST);
if (len > (int)rlen)
row[rlen - strlen(CRST)] = 0;
strlcat(row, CRST, sizeof(row));
}
puts(row);
}
if (mode == 1) {
pos += i;
if (pos >= ttrows)
break;
}
}
}
fclose(fp);
n = scandir(path, &namelist, cgroup_filter, alphasort);
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char prefix[80];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
switch (mode) {
case 1:
cg = cg_stats(buf);
snprintf(row, rplen,
"\r %6.6s %6.6s %6.6s %5.1f %5.1f ",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_memshare, cg->cg_load);
break;
case 2:
cg = cg_conf(buf);
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6.6s [%-6.6s%6.6s] ",
memsz(cg->cg_vmsize, s, sizeof(s)),
cg->cg_mem.min, cg->cg_mem.max,
cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max);
break;
default:
strlcpy(row, "\r", rplen);
break;
}
strlcat(row, pfx, rplen);
if (i + 1 == n) {
strlcat(row, END, rplen);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else {
strlcat(row, FORK, rplen);
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
}
strlcat(row, " ", rplen);
strlcat(row, nm, rplen);
strlcat(row, "/ ", rplen);
puts(row);
rc += cgroup_tree(buf, prefix, mode, pos + i);
free(namelist[i]);
}
free(namelist);
}
return rc;
}
int show_cgps(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return cgroup_tree(arg, NULL, 0, 0);
}
static void cgtop(uev_t *w, void *arg, int events)
{
(void)w;
(void)events;
fputs("\e[2J\e[1;1H", stdout);
if (heading)
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
cgroup_tree(arg, NULL, 1, 0);
}
static void cleanup(void)
{
ttcooked();
showcursor();
puts("");
}
static void leave(uev_t *w, void *arg, int events)
{
(void)arg;
(void)events;
uev_exit(w->ctx);
}
static void key(uev_t *w, void *arg, int events)
{
char ch;
(void)arg;
(void)events;
if (read(w->fd, &ch, sizeof(ch)) != -1) {
switch (ch) {
case 'q':
uev_exit(w->ctx);
break;
default:
dbg("Got char 0x%02x", ch);
break;
}
}
}
int show_cgtop(char *arg)
{
struct sysinfo si = { 0 };
uev_t timer, input, sigint, sigterm, sigquit;
char path[512];
uev_ctx_t ctx;
int flags;
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
if (!hcreate(ttrows + 25))
err(1, "failed creating hash table");
sysinfo(&si);
total_ram = si.totalram * si.mem_unit;
uev_init(&ctx);
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
if (!ionce && !plain) {
atexit(cleanup);
ttraw();
hidecursor();
flags = fcntl(STDIN_FILENO, F_GETFL);
if (flags != -1)
(void)fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
(void)uev_io_init(&ctx, &input, key, NULL, STDIN_FILENO, UEV_READ);
(void)uev_signal_init(&ctx, &sigint, leave, NULL, SIGINT);
(void)uev_signal_init(&ctx, &sigterm, leave, NULL, SIGTERM);
(void)uev_signal_init(&ctx, &sigquit, leave, NULL, SIGQUIT);
}
return uev_run(&ctx, 0);
}
int show_cgroup(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
/* memory.current memory.min memory.max cpuset.cpus cpu.weight cpu.max */
if (heading)
print_header(" MEM [MIN MAX] CPU [WEIGHT MAX] GROUP");
return cgroup_tree(arg, NULL, 2, 0);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
-79
View File
@@ -1,79 +0,0 @@
/* Cgroup utility functions for initctl
*
* Copyright (c) 2019-2021 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.
*/
#ifndef FINIT_CGUTIL_H_
#define FINIT_CGUTIL_H_
#include <stdint.h>
#include <stdlib.h>
struct cg {
struct cg *cg_next;
/* stats */
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
uint64_t cg_rss; /* memory.stat */
uint64_t cg_vmlib; /* memory.stat */
uint64_t cg_vmsize; /* memory.current */
float cg_memshare; /* cg_rss / total_ram * 100 */
float cg_load; /* curr - prev / 10000000.0 */
/* config */
struct {
char min[32];
char max[32];
} cg_mem;
struct {
char set[16];
char weight[32];
char max[32];
} cg_cpu;
};
int cgroup_avail(void);
char *pid_cmdline (int pid, char *buf, size_t len);
char *pid_comm (int pid, char *buf, size_t len);
char *pid_cgroup (int pid, char *buf, size_t len);
uint64_t cgroup_memory(char *group);
struct cg *cg_stats(char *path);
struct cg *cg_conf (char *path);
int cgroup_tree (char *path, char *pfx, int mode, int pos);
int show_cgroup (char *arg);
int show_cgtop (char *arg);
int show_cgps (char *arg);
#endif /* FINIT_CGUTIL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+16 -36
View File
@@ -21,9 +21,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <err.h>
#include <errno.h> #include <errno.h>
#include <poll.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
@@ -38,14 +36,19 @@ int client_connect(void)
.sun_path = INIT_SOCKET, .sun_path = INIT_SOCKET,
}; };
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0); sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd) if (-1 == sd)
err(1, "Failed creating UNIX domain socket"); goto error;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
err(1, "Failed connecting to finit"); close(sd);
goto error;
}
return sd; return sd;
error:
perror("Failed connecting to finit");
return -1;
} }
int client_disconnect(void) int client_disconnect(void)
@@ -65,50 +68,27 @@ int client_disconnect(void)
int client_send(struct init_request *rq, ssize_t len) int client_send(struct init_request *rq, ssize_t len)
{ {
struct pollfd pfd = { 0 };
int sd, result = 255; int sd, result = 255;
int rc;
sd = client_connect(); sd = client_connect();
if (-1 == sd) if (-1 == sd)
return -1; return -1;
pfd.fd = sd; if (write(sd, rq, len) != len)
pfd.events = POLLOUT; goto error;
if (poll(&pfd, 1, 2000) <= 0) {
warn("Timed out waiting for Finit, errno %d", errno);
goto exit;
}
if (write(sd, rq, len) != len) { if (read(sd, rq, len) != len)
warn("Failed communicating with Finit, errno %d", errno); goto error;
goto exit;
}
pfd.fd = sd;
pfd.events = POLLIN | POLLERR | POLLHUP;
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
if (rc) {
if (errno == EINTR) /* shutdown/reboot */
goto exit;
warn("poll(), errno %d", errno);
} else
warnx("Timed out waiting for reply from Finit.");
goto exit;
}
if (read(sd, rq, len) != len) {
warn("Failed reading reply from Finit, errno %d", errno);
goto exit;
}
if (rq->cmd == INIT_CMD_NACK) if (rq->cmd == INIT_CMD_NACK)
result = 1; result = 1;
else else
result = 0; result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit: exit:
client_disconnect(); client_disconnect();
return result; return result;
} }
+54 -97
View File
@@ -23,12 +23,8 @@
#include <ftw.h> #include <ftw.h>
#include <libgen.h> #include <libgen.h>
#include <stdio.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif #include <stdio.h>
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
@@ -103,14 +99,13 @@ static void cond_bump_reconf(void)
unsigned int rgen; unsigned int rgen;
/* /*
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0 * If %COND_RECONF does not exist, cond_get_gen() returns 0
* meaning that rgen++ is always what we want. * meaning that rgen++ is always what we want.
*/ */
rgen = cond_get_gen(_PATH_RECONF); rgen = cond_get_gen(COND_RECONF);
rgen++; rgen++;
if (cond_set_gen(_PATH_RECONF, rgen)) cond_set_gen(COND_RECONF, rgen);
_pe("Failed setting %s to gen %d", _PATH_RECONF, rgen);
} }
static int cond_checkpath(const char *path) static int cond_checkpath(const char *path)
@@ -124,7 +119,7 @@ static int cond_checkpath(const char *path)
return 1; return 1;
} }
if (mkpath(dir, 0755) && errno != EEXIST) { if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path); _pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1; return 1;
} }
@@ -132,22 +127,22 @@ static int cond_checkpath(const char *path)
return 0; return 0;
} }
int cond_set_path(const char *path, enum cond_state next) int cond_set_path(const char *path, enum cond_state new)
{ {
enum cond_state prev; enum cond_state old;
unsigned int rgen; unsigned int rgen;
_d("%s <= %d", path, next); _d("%s", path);
rgen = cond_get_gen(_PATH_RECONF); rgen = cond_get_gen(COND_RECONF);
if (!rgen) { if (!rgen) {
_e("Unable to read configuration generation (%s)", path); _e("Unable to read configuration generation (%s)", path);
return -1; return -1;
} }
prev = cond_get_path(path); old = cond_get_path(path);
switch (next) { switch (new) {
case COND_ON: case COND_ON:
if (cond_checkpath(path)) if (cond_checkpath(path))
return 0; return 0;
@@ -164,95 +159,79 @@ int cond_set_path(const char *path, enum cond_state next)
return 0; return 0;
} }
return next != prev; return new != old;
} }
/* Should only be used by cond_set*(), cond_clear(), and usr/sys plugins! */ /* Has condition in configuration and cond is allowed? */
int cond_update(const char *name) static int svc_has_cond(svc_t *svc)
{
if (!svc->cond[0])
return 0;
switch (svc->type) {
case SVC_TYPE_SERVICE:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
return 1;
default:
break;
}
return 0;
}
static void cond_update(const char *name)
{ {
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
int affects = 0;
_d("%s", name); _d("%s", name);
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue; continue;
affects++;
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd); _d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
service_step(svc); service_step(svc);
} }
return affects;
}
int cond_set_noupdate(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return 1;
if (!cond_set_path(cond_path(name), COND_ON))
return 1;
return 0;
} }
void cond_set(const char *name) void cond_set(const char *name)
{ {
_d("%s", name); _d("%s", name);
if (cond_set_noupdate(name)) if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_ON))
return; return;
cond_update(name); cond_update(name);
} }
int cond_set_oneshot_noupdate(const char *name) void cond_set_oneshot(const char *name)
{ {
const char *path; const char *path;
if (string_compare(name, "nop")) if (string_compare(name, "nop"))
return 1; return;
path = cond_path(name); path = cond_path(name);
_d("%s => %s", name, path); _d("%s => %s", name, path);
if (cond_checkpath(path)) if (cond_checkpath(path))
return 1;
if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
_pe("Failed creating onshot cond %s", name);
return 1;
}
return 0;
}
void cond_set_oneshot(const char *name)
{
_d("%s", name);
if (cond_set_oneshot_noupdate(name))
return; return;
symlink(COND_RECONF, path);
cond_update(name); cond_update(name);
} }
int cond_clear_noupdate(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return 1;
if (!cond_set_path(cond_path(name), COND_OFF))
return 1;
return 0;
}
void cond_clear(const char *name) void cond_clear(const char *name)
{ {
_d("%s", name); _d("%s", name);
if (cond_clear_noupdate(name)) if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_OFF))
return; return;
cond_update(name); cond_update(name);
@@ -265,7 +244,7 @@ void cond_reload(void)
cond_bump_reconf(); cond_bump_reconf();
} }
static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw, int set) static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
{ {
char *nm; char *nm;
@@ -275,57 +254,35 @@ static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct
if (tflg != FTW_F) if (tflg != FTW_F)
return 0; return 0;
nm = strstr((char *)fpath, COND_BASE); nm = strstr((char *)fpath, COND_DIR);
if (!nm) { if (!nm) {
_e("Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de"); _e("Incorrect condtion path %s, cannot reassert", fpath);
return 1; return 1;
} }
nm += strlen(COND_BASE); nm += sizeof(COND_DIR);
_d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm); _d("Reasserting %s => %s", fpath, nm);
if (set)
cond_set(nm); cond_set(nm);
else
cond_clear_noupdate(nm); /* important, see netlink plugin! */
return 0; return 0;
} }
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
{
return do_assert(fpath, sb, tflg, ftw, 1);
}
static int deassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
{
return do_assert(fpath, sb, tflg, ftw, 0);
}
/* /*
* Used only by netlink plugin atm. * Used only by netlink plugin atm.
* type: is a one of pid/, net/, etc. * type: is a one of pid/, net/, etc.
*/ */
void cond_reassert(const char *pat) void cond_reassert(const char *type)
{ {
_d("%s", pat); _d("%s", type);
nftw(cond_path(pat), reassert, 20, FTW_DEPTH); nftw(cond_path(type), reassert, 20, FTW_DEPTH);
}
/*
* Used only by netlink plugin atm.
*/
void cond_deassert(const char *pat)
{
_d("%s", pat);
nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
} }
void cond_init(void) void cond_init(void)
{ {
char path[MAX_ARG_LEN]; char path[MAX_ARG_LEN];
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) { if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", _PATH_COND); _pe("Failed creating condition base directory '%s'", COND_PATH);
return; return;
} }
+7 -11
View File
@@ -21,12 +21,8 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <stdio.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif #include <stdio.h>
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
@@ -49,15 +45,15 @@ const char *cond_path(const char *name)
static char path[256]; static char path[256];
char tmp[MAX_ARG_LEN]; char tmp[MAX_ARG_LEN];
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name); snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
return pid_runpath(tmp, path, sizeof(path)); return pid_runpath(tmp, path, sizeof(path));
} }
unsigned int cond_get_gen(const char *file) unsigned int cond_get_gen(const char *file)
{ {
char *ptr, path[256];
unsigned int gen; unsigned int gen;
char *ptr, path[256];
FILE *fp; FILE *fp;
int ret; int ret;
@@ -78,12 +74,12 @@ enum cond_state cond_get_path(const char *path)
{ {
int cgen, rgen; int cgen, rgen;
cgen = cond_get_gen(path); rgen = cond_get_gen(COND_RECONF);
if (!cgen) if (!rgen)
return COND_OFF; return COND_OFF;
rgen = cond_get_gen(_PATH_RECONF); cgen = cond_get_gen(path);
if (!rgen) if (!cgen)
return COND_OFF; return COND_OFF;
return (cgen == rgen) ? COND_ON : COND_FLUX; return (cgen == rgen) ? COND_ON : COND_FLUX;
+4 -20
View File
@@ -2,19 +2,11 @@
#define FINIT_COND_H_ #define FINIT_COND_H_
#include <paths.h> #include <paths.h>
#include <svc.h>
#include "svc.h" #define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
#define COND_BASE "finit/cond" #define COND_RECONF COND_PATH "/reconf"
#define COND_PID "pid/"
#define COND_SYS "sys/"
#define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
#define _PATH_CONDPID _PATH_COND COND_PID
#define _PATH_CONDSYS _PATH_COND COND_SYS
#define _PATH_CONDUSR _PATH_COND COND_USR
#define _PATH_RECONF _PATH_COND "reconf"
typedef enum cond_state { typedef enum cond_state {
COND_OFF = 0, COND_OFF = 0,
@@ -31,20 +23,12 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names); enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names); int cond_affects (const char *name, const char *names);
int cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new); int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name); void cond_set (const char *name);
void cond_set_oneshot (const char *name); void cond_set_oneshot (const char *name);
void cond_clear (const char *name); void cond_clear (const char *name);
void cond_reload (void); void cond_reload (void);
int cond_set_noupdate (const char *name);
int cond_set_oneshot_noupdate(const char *name);
int cond_clear_noupdate(const char *name);
void cond_reassert (const char *pat); void cond_reassert (const char *pat);
void cond_deassert (const char *pat);
void cond_init (void); void cond_init (void);
#endif /* FINIT_COND_H_ */ #endif /* FINIT_COND_H_ */
+76 -202
View File
@@ -23,25 +23,18 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <string.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/resource.h> #include <sys/resource.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
#include <lite/lite.h> #include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */ #include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include <time.h> #include <time.h>
#include <glob.h> #include <glob.h>
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "iwatch.h" #include "iwatch.h"
#include "private.h"
#include "service.h" #include "service.h"
#include "tty.h" #include "tty.h"
#include "helpers.h" #include "helpers.h"
@@ -54,11 +47,8 @@
int logfile_size_max = 200000; /* 200 kB */ int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5; int logfile_count_max = 5;
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
struct rlimit global_rlimit[RLIMIT_NLIMITS]; struct rlimit global_rlimit[RLIMIT_NLIMITS];
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
struct conf_change { struct conf_change {
TAILQ_ENTRY(conf_change) link; TAILQ_ENTRY(conf_change) link;
char *name; char *name;
@@ -72,14 +62,30 @@ static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(c
static int parse_conf(char *file, int is_rcsd); static int parse_conf(char *file, int is_rcsd);
static void drop_changes(void); static void drop_changes(void);
int get_bool(char *arg, int default_value) static void hide_args(int argc, char *argv[])
{ {
if (!arg) /*
* 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 = ' ';
}
}
int get_bool(char *ptr, int default_value)
{
if (!ptr)
goto fallback; goto fallback;
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1")) if (string_compare(ptr, "true") || string_compare(ptr, "on") || string_compare(ptr, "1"))
return 1; return 1;
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0")) if (string_compare(ptr, "false") || string_compare(ptr, "off") || string_compare(ptr, "0"))
return 0; return 0;
fallback: fallback:
return default_value; return default_value;
@@ -88,28 +94,22 @@ fallback:
/* /*
* finit.debug = [on,off] * finit.debug = [on,off]
* finit.show_status = [on,off] * finit.show_status = [on,off]
* finit.status_style = [old,classic,modern]
*/ */
static void parse_finit_opts(char *opt) static void parse_finit_opts(char *opt)
{ {
char *arg; char *ptr;
arg = strchr(opt, '='); ptr = strchr(opt, '=');
if (arg) if (ptr)
*arg++ = 0; *ptr++ = 0;
if (string_compare(opt, "debug")) { if (string_compare(opt, "debug")) {
debug = get_bool(arg, 1); debug = get_bool(ptr, 1);
return; return;
} }
if (string_compare(opt, "status_style")) { if (string_compare(opt, "status_style")) {
if (!arg) { if (string_compare(ptr, "old") || string_compare(ptr, "classic"))
_e("status_style option requires an argument, skipping.");
return;
}
if (string_compare(arg, "old") || string_compare(arg, "classic"))
show_progress(PROGRESS_CLASSIC); show_progress(PROGRESS_CLASSIC);
else else
show_progress(PROGRESS_MODERN); show_progress(PROGRESS_MODERN);
@@ -117,7 +117,7 @@ static void parse_finit_opts(char *opt)
} }
if (string_compare(opt, "show_status")) { if (string_compare(opt, "show_status")) {
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT); show_progress(get_bool(ptr, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
return; return;
} }
} }
@@ -136,27 +136,24 @@ static void parse_arg(char *arg)
single = 1; single = 1;
} }
#ifdef KERNEL_CMDLINE void conf_parse_cmdline(int argc, char *argv[])
/*
* Parse /proc/cmdline to find args for init. Don't use this!
*
* Instead, rely on the kernel to give Finit its arguments as
* regular argc + argv[]. Only use this if the system you run
* on has a broken initramfs system that cannot forward args
* to Finit properly.
*/
static void parse_kernel_cmdline(void)
{ {
char line[LINE_SIZE], *cmdline, *tok; int dbg = 0;
FILE *fp; FILE *fp;
char line[LINE_SIZE], *cmdline, *tok;
for (int i = 1; i < argc; i++)
parse_arg(argv[i]);
hide_args(argc, argv);
fp = fopen("/proc/cmdline", "r"); fp = fopen("/proc/cmdline", "r");
if (!fp) if (!fp)
return; goto done;
if (!fgets(line, sizeof(line), fp)) { if (!fgets(line, sizeof(line), fp)) {
fclose(fp); fclose(fp);
return; goto done;
} }
cmdline = chomp(line); cmdline = chomp(line);
@@ -167,62 +164,29 @@ static void parse_kernel_cmdline(void)
parse_arg(tok); parse_arg(tok);
} }
fclose(fp); fclose(fp);
}
#else
#define parse_kernel_cmdline()
#endif
static void parse_kernel_loglevel(void) done:
{ log_init(dbg);
char line[LINE_SIZE], *ptr;
FILE *fp;
int val;
fp = fopen("/proc/sys/kernel/printk", "r");
if (!fp)
return;
if (!fgets(line, sizeof(line), fp)) {
fclose(fp);
return;
}
fclose(fp);
ptr = chomp(line);
_d("%s", ptr);
val = atoi(ptr);
if (val >= 7)
kerndebug = 1;
}
/*
* Kernel gives us all non-kernel options on our cmdline
*/
void conf_parse_cmdline(int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
parse_arg(argv[i]);
parse_kernel_cmdline();
parse_kernel_loglevel();
log_init();
} }
static int kmod_exists(char *mod) static int kmod_exists(char *mod)
{ {
char buf[256];
int found = 0; int found = 0;
FILE *fp; FILE *fp;
char buf[256];
size_t len;
for (len = 0; mod[len]; len++) {
if (mod[len] == ' ' || mod[len] == '\t')
break;
}
fp = fopen("/proc/modules", "r"); fp = fopen("/proc/modules", "r");
if (!fp) if (!fp)
return 0; return 0;
while (!found && fgets(buf, sizeof(buf), fp)) { while (!found && (fgets(buf, sizeof(buf), fp))) {
char *kmod = strtok(buf, " \t"); if (!strncmp(buf, mod, len))
if (kmod && !strcmp(kmod, mod))
found = 1; found = 1;
} }
fclose(fp); fclose(fp);
@@ -230,26 +194,22 @@ static int kmod_exists(char *mod)
return found; return found;
} }
static void kmod_load(char *mod) static void kmod_load(char *line)
{ {
char module[64] = { 0 }; char *mod;
char cmd[CMD_SIZE]; char cmd[CMD_SIZE];
if (runlevel != 0) if (runlevel != 0)
return; return;
/* Strip args for progress below and kmod_exists() */ mod = strip_line(line);
strlcpy(module, mod, sizeof(module)); if (kmod_exists(mod))
if (!strtok(module, " \t"))
return;
if (kmod_exists(module))
return; return;
strcpy(cmd, "modprobe "); strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd)); strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", module); run_interactive(cmd, "Loading kernel module %s", mod);
} }
/* Convert optional "[!123456789S]" string into a bitmask */ /* Convert optional "[!123456789S]" string into a bitmask */
@@ -386,7 +346,7 @@ char *lim2str(struct rlimit *rlim)
char tmp[25]; char tmp[25];
static char buf[42]; static char buf[42];
buf[0] = 0; memset(buf, 0, sizeof(buf));
if (RLIM_INFINITY == rlim->rlim_cur) if (RLIM_INFINITY == rlim->rlim_cur)
snprintf(tmp, sizeof(tmp), "unlimited, "); snprintf(tmp, sizeof(tmp), "unlimited, ");
else else
@@ -457,29 +417,6 @@ error:
logit(LOG_WARNING, "rlimit: parse error"); logit(LOG_WARNING, "rlimit: parse error");
} }
/* cgroup NAME ctrl.prop:value,ctrl.prop:value ... */
static void conf_parse_cgroup(char *line)
{
char config[strlen(line) + 1];
char *ptr, *name;
name = strtok(line, " \t");
if (!name)
return;
if (strstr(name, "..") || strchr(name, '/'))
return; /* illegal */
config[0] = 0;
while ((ptr = strtok(NULL, " \t"))) {
if (config[0])
strlcat(config, ",", sizeof(config));
strlcat(config, ptr, sizeof(config));
}
cgroup_add(name, config, 0);
}
static void parse_static(char *line, int is_rcsd) static void parse_static(char *line, int is_rcsd)
{ {
char cmd[CMD_SIZE]; char cmd[CMD_SIZE];
@@ -502,7 +439,7 @@ static void parse_static(char *line, int is_rcsd)
/* Kernel module to load */ /* Kernel module to load */
if (BOOTSTRAP && MATCH_CMD(line, "module ", x)) { if (BOOTSTRAP && MATCH_CMD(line, "module ", x)) {
kmod_load(strip_line(x)); kmod_load(x);
return; return;
} }
@@ -573,26 +510,6 @@ static void parse_static(char *line, int is_rcsd)
cfglevel = 2; /* Fallback */ cfglevel = 2; /* Fallback */
return; return;
} }
/*
* Periodic check and instability index leveler, seconds
*/
if (MATCH_CMD(line, "service-interval ", x)) {
char *token = strip_line(x);
const char *err = NULL;
int val;
/* 0 min to 1 day, should check at least daily */
val = strtonum(token, 0, 1440, &err);
if (!err) {
int disabled = !service_interval;
service_interval = val * 1000; /* to milliseconds */
if (disabled)
service_init();
}
return;
}
} }
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file) static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
@@ -629,21 +546,9 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
return; return;
} }
/* Read control group limits */
if (MATCH_CMD(line, "cgroup ", x)) {
conf_parse_cgroup(x);
return;
}
/* Set current cgroup for the following services/run/tasks */
if (MATCH_CMD(line, "cgroup.", x)) {
strlcpy(cgroup_current, x, sizeof(cgroup_current));
return;
}
/* Regular or serial TTYs to run getty */ /* Regular or serial TTYs to run getty */
if (MATCH_CMD(line, "tty ", x)) { if (MATCH_CMD(line, "tty ", x)) {
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file); tty_register(strip_line(x), rlimit, file);
return; return;
} }
} }
@@ -669,11 +574,9 @@ static int parse_conf(char *file, int is_rcsd)
if (!fp) if (!fp)
return 1; return 1;
/* Prepare default limits and group for each service in /etc/finit.d/ */ /* Prepare default limits for each service in /etc/finit.d/ */
if (is_rcsd) { if (is_rcsd)
memcpy(rlimit, global_rlimit, sizeof(rlimit)); memcpy(rlimit, global_rlimit, sizeof(rlimit));
cgroup_current[0] = 0;
}
_d("*** Parsing %s", file); _d("*** Parsing %s", file);
while (!feof(fp)) { while (!feof(fp)) {
@@ -713,22 +616,25 @@ int conf_reload(void)
daylight, timezone, tzname[0], tzname[1]); daylight, timezone, tzname[0], tzname[1]);
/* Mark and sweep */ /* Mark and sweep */
cgroup_mark_all();
svc_mark_dynamic(); svc_mark_dynamic();
tty_mark();
/* /*
* Reset global rlimit to bootstrap values from conf_init(). * Get current global limits, which may be overridden from both
* finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there.
*/ */
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit)); for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
if (rescue) { if (rescue) {
int rc; int rc;
char line[80] = "tty [12345] rescue"; char line[80] = "tty [12345] @console noclear nologin";
/* If rescue.conf is missing, fall back to a root shell */ /* If rescue.conf is missing, fall back to a root shell */
rc = parse_conf(RESCUE_CONF, 0); rc = parse_conf(RESCUE_CONF, 0);
if (rc) if (rc)
service_register(SVC_TYPE_TTY, line, global_rlimit, NULL); tty_register(line, global_rlimit, NULL);
print(rc, "Entering rescue mode"); print(rc, "Entering rescue mode");
goto done; goto done;
@@ -768,7 +674,7 @@ int conf_reload(void)
/* Check for dangling symlinks */ /* Check for dangling symlinks */
if (S_ISLNK(st.st_mode)) { if (S_ISLNK(st.st_mode)) {
rp = realpath(path, NULL); path = rp = realpath(path, NULL);
if (!rp) { if (!rp) {
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno)); logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
continue; continue;
@@ -778,7 +684,7 @@ int conf_reload(void)
/* Check that file ends with '.conf' */ /* Check that file ends with '.conf' */
len = strlen(path); len = strlen(path);
if (len < 6 || strcmp(&path[len - 5], ".conf")) if (len < 6 || strcmp(&path[len - 5], ".conf"))
_d("Skipping %s, not a Finit .conf file ... ", path); _d("Skipping %s, not a valid .conf ... ", path);
else else
parse_conf(path, 1); parse_conf(path, 1);
@@ -788,15 +694,7 @@ int conf_reload(void)
globfree(&gl); globfree(&gl);
/* Mark any reverse deps as chenaged. */
service_update_rdeps();
/* Set up top-level cgroups */
cgroup_config();
done: done:
/* Remove all unused top-level cgroups */
cgroup_cleanup();
/* Drop record of all .conf changes */ /* Drop record of all .conf changes */
drop_changes(); drop_changes();
@@ -889,29 +787,20 @@ int conf_any_change(void)
int conf_changed(char *file) int conf_changed(char *file)
{ {
int rc = 0;
char *rp;
if (!file) if (!file)
return 0; return 0;
rp = realpath(file, NULL); if (conf_find(file))
if (!rp) return 1;
return 0; return 0;
if (conf_find(rp))
rc = 1;
free(rp);
return rc;
} }
static void conf_cb(uev_t *w, void *arg, int events) static void conf_cb(uev_t *w, void *arg, int events)
{ {
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev; struct inotify_event *ev;
ssize_t sz; ssize_t sz, off;
size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
@@ -920,16 +809,10 @@ static void conf_cb(uev_t *w, void *arg, int events)
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) { for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp; struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off]; ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask) if (!ev->mask)
continue; continue;
@@ -985,7 +868,7 @@ int conf_monitor(void)
} }
/* /*
* Prepare .conf parser and load /etc/finit.conf for global settings * Prepare .conf parser and load all .conf files
*/ */
int conf_init(uev_ctx_t *ctx) int conf_init(uev_ctx_t *ctx)
{ {
@@ -999,17 +882,8 @@ int conf_init(uev_ctx_t *ctx)
* finit.conf, for Finit and its services like getty+watchdogd, * finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there. * and *.conf in finit.d/, for each service(s) listed there.
*/ */
for (int i = 0; i < RLIMIT_NLIMITS; i++) { for (int i = 0; i < RLIMIT_NLIMITS; i++)
if (getrlimit(i, &initial_rlimit[i])) getrlimit(i, &global_rlimit[i]);
logit(LOG_WARNING, "rlimit: Failed reading setting %s: %s",
rlim2str(i), strerror(errno));
}
/* Initialize global rlimits, e.g. for built-in services */
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
/* Read global rlimits and global cgroup setup from /etc/finit.conf */
parse_conf(FINIT_CONF, 0);
/* prepare /etc watcher */ /* prepare /etc watcher */
fd = iwatch_init(&iw_conf); fd = iwatch_init(&iw_conf);
-2
View File
@@ -24,14 +24,12 @@
#ifndef FINIT_CONF_H_ #ifndef FINIT_CONF_H_
#define FINIT_CONF_H_ #define FINIT_CONF_H_
#include "cgroup.h"
#include "svc.h" #include "svc.h"
extern int logfile_size_max; extern int logfile_size_max;
extern int logfile_count_max; extern int logfile_count_max;
extern struct rlimit global_rlimit[]; extern struct rlimit global_rlimit[];
extern char cgroup_current[];
int str2rlim(char *str); int str2rlim(char *str);
char *rlim2str(int rlim); char *rlim2str(int rlim);
+123 -123
View File
@@ -26,23 +26,15 @@
#include <ctype.h> /* isdigit() */ #include <ctype.h> /* isdigit() */
#include <dirent.h> #include <dirent.h>
#include <err.h>
#include <stdarg.h> #include <stdarg.h>
#include <sysexits.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */ #include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h> #include <termios.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include <linux/vt.h>
#include "finit.h" #include "finit.h"
#include "cgroup.h"
#include "conf.h" #include "conf.h"
#include "helpers.h" #include "helpers.h"
#include "sig.h" #include "sig.h"
@@ -71,42 +63,16 @@ int complete(char *cmd, int pid)
return status; return status;
} }
static int do_redirect(void) int run(char *cmd)
{
FILE *fp;
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
return fclose(fp);
}
return -1;
}
/*
* Run 'cmd' and wait for completion. If 'log' is not NULL any output
* from the command is logged with the given string as prefix, use "" to
* skip. If 'log' is NULL all output is redirected to /dev/null, which
* is what this function originally did.
*/
int run(char *cmd, char *log)
{ {
int status, result, i = 0; int status, result, i = 0;
char *args[NUM_ARGS + 1], *arg; char *args[NUM_ARGS + 1], *arg, *backup;
char *backup = NULL;
pid_t pid; pid_t pid;
if (!log) {
/* We must create a copy that is possible to modify. */ /* We must create a copy that is possible to modify. */
backup = arg = strdup(cmd); backup = arg = strdup(cmd);
if (!arg) if (!arg)
return 1; return 1; /* Failed allocating a string to be modified. */
/* Split command line into tokens of an argv[] array. */ /* Split command line into tokens of an argv[] array. */
args[i++] = strsep(&arg, "\t "); args[i++] = strsep(&arg, "\t ");
@@ -134,38 +100,31 @@ int run(char *cmd, char *log)
errno = EOVERFLOW; errno = EOVERFLOW;
return 1; return 1;
} }
}
pid = fork(); pid = fork();
if (0 == pid) { if (0 == pid) {
int rc = EX_OSERR; FILE *fp;
/* Reset signal handlers that were set by the parent process */
sig_unblock();
setsid();
/* Always redirect stdio for run() */
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
setsid();
sig_unblock(); sig_unblock();
if (!log) {
do_redirect();
execvp(args[0], args); execvp(args[0], args);
} else {
char *pfx = *log ? ": " : "";
FILE *pp;
pp = popen(cmd, "r"); _exit(1); /* Only if execv() fails. */
if (pp) {
char buf[256];
while (fgets(buf, sizeof(buf), pp)) {
chomp(buf);
logit(LOG_NOTICE, "%s%s%s", log, pfx, buf);
}
rc = pclose(pp);
}
}
_exit(rc);
} else if (-1 == pid) { } else if (-1 == pid) {
_pe("%s", args[0]); _pe("%s", args[0]);
if (backup)
free(backup); free(backup);
return -1; return -1;
@@ -173,23 +132,21 @@ int run(char *cmd, char *log)
status = complete(args[0], pid); status = complete(args[0], pid);
if (-1 == status) { if (-1 == status) {
if (backup)
free(backup); free(backup);
return 1; return 1;
} }
result = WEXITSTATUS(status); result = WEXITSTATUS(status);
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
_d("Started '%s' and exit without signal, status: %d", cmd, result); _d("Started %s and ended OK: %d", args[0], result);
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
_d("Process '%s' terminated by signal %d", cmd, WTERMSIG(status)); _d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
if (!result) if (!result)
result = 1; /* Must alert callee that the command did complete successfully. result = 1; /* Must alert callee that the command did complete successfully.
* This is necessary since not all programs trap signals and * This is necessary since not all programs trap signals and
* change their return code accordingly. --Jocke */ * change their return code accordingly. --Jocke */
} }
if (backup)
free(backup); free(backup);
return result; return result;
@@ -224,7 +181,7 @@ int run_interactive(char *cmd, char *fmt, ...)
} }
/* Run cmd ... */ /* Run cmd ... */
status = run(cmd, ""); status = run(cmd);
/* Restore stderr/stdout */ /* Restore stderr/stdout */
if (fp && !debug) { if (fp && !debug) {
@@ -260,6 +217,7 @@ int run_interactive(char *cmd, char *fmt, ...)
int exec_runtask(char *cmd, char *args[]) int exec_runtask(char *cmd, char *args[])
{ {
size_t i;
char buf[1024] = ""; char buf[1024] = "";
char *argv[4] = { char *argv[4] = {
"sh", "sh",
@@ -267,7 +225,6 @@ int exec_runtask(char *cmd, char *args[])
buf, buf,
NULL NULL
}; };
size_t i;
strlcat(buf, cmd, sizeof(buf)); strlcat(buf, cmd, sizeof(buf));
for (i = 1; args[i]; i++) { for (i = 1; args[i]; i++) {
@@ -282,8 +239,14 @@ int exec_runtask(char *cmd, char *args[])
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[]) static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
{ {
struct sigaction sa;
struct termios term;
char name[80]; char name[80];
int fd, dummy; int fd;
/* Detach from initial controlling TTY and become session leader */
vhangup();
setsid();
fd = open(tty, O_RDWR); fd = open(tty, O_RDWR);
if (fd < 0) { if (fd < 0) {
@@ -294,18 +257,40 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
dup2(fd, STDIN_FILENO); dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO); dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
close(fd);
/* Set default TERM, run_getty() below allows override */ if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
if (ioctl(fd, VT_OPENQRY, &dummy) == -1) logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
setenv("TERM", "vt102", 1); /* likely a serial line */
else
setenv("TERM", "linux", 1);
/* /*
* Reset to sane defaults in case of messup from prev. session * Reset to sane defaults in case of messup from prev. session
*/ */
stty(fd, speed); stty(STDIN_FILENO, speed);
close(fd);
/*
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
* It is up to the getty process to allow them again.
*/
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_lflag &= ~ISIG;
term.c_cc[VEOF] = _POSIX_VDISABLE;
term.c_cc[VINTR] = _POSIX_VDISABLE;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
}
/* Reset signal handlers that were set by the parent process */
sig_unblock();
/*
* Ignore a few signals, needed to prevent Ctrl-C at login:
* prompt and to prevent QUIT from dumping core.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
/* Set configured limits */ /* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) { for (int i = 0; i < RLIMIT_NLIMITS; i++) {
@@ -323,18 +308,14 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
static int activate_console(int noclear, int nowait) static int activate_console(int noclear, int nowait)
{ {
static const char msg[] = "\nPlease press Enter to activate this console.";
static const char clr[] = "\r\e[K";
static const char cup[] = "\e[A";
struct termios orig; struct termios orig;
char c; int ret = 0;
int rc;
if (nowait || rescue) if (nowait || rescue)
return 1; return 1;
if (!noclear) if (!noclear)
dprint(STDERR_FILENO, "\e[r\e[H\e[J", 9); (void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
/* Disable ECHO, XON/OFF while waiting for <CR> */ /* Disable ECHO, XON/OFF while waiting for <CR> */
if (!tcgetattr(STDIN_FILENO, &orig)) { if (!tcgetattr(STDIN_FILENO, &orig)) {
@@ -347,24 +328,36 @@ static int activate_console(int noclear, int nowait)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &c); tcsetattr(STDIN_FILENO, TCSAFLUSH, &c);
} }
dprint(STDERR_FILENO, clr, strlen(clr)); while (!fexist(SYNC_SHUTDOWN)) {
dprint(STDERR_FILENO, msg, strlen(msg)); char c;
while ((rc = read(STDIN_FILENO, &c, 1)) > 0 && c != '\r' && c != '\n') static const char clr[] = "\r\e[2K";
static const char cup[] = "\e[A";
static const char msg[] = "\nPlease press Enter to activate this console.";
if (fexist(SYNC_STOPPED)) {
sleep(5);
continue;
}
(void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, msg, strlen(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
continue; continue;
/* On any error (likely EINTR), we avoid starting getty */ if (fexist(SYNC_STOPPED))
if (rc == -1) continue;
rc = 0;
/* Clear msg and move cursor up for next message/login: */ (void)write(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, clr, strlen(clr)); (void)write(STDERR_FILENO, cup, strlen(cup));
dprint(STDERR_FILENO, cup, strlen(cup)); ret = 1;
break;
}
/* Restore TTY */ /* Restore TTY */
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig) == -1) if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig) == -1)
rc = 0; /* TTY in bad shape, restart */ ret = 0;
return rc; return ret;
} }
/* /*
@@ -386,57 +379,64 @@ static int activate_console(int noclear, int nowait)
* since /bin/login usually only disables ECHO until a password line has * since /bin/login usually only disables ECHO until a password line has
* been entered. Upon starting the user's $SHELL the ISIG flag is reset * been entered. Upon starting the user's $SHELL the ISIG flag is reset
*/ */
pid_t run_getty(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]) pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
{ {
pid_t pid;
pid = fork();
if (!pid) {
speed_t speed;
int rc = 1;
speed = stty_parse_speed(baud);
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
prepare_tty(tty, speed, "tty", rlimit);
if (activate_console(noclear, nowait))
rc = getty(tty, speed, term, NULL);
_exit(rc);
}
return pid;
}
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
pid = fork();
if (!pid) {
int rc = 1; int rc = 1;
/* Dunno speed, tell stty() to not mess with it */ /* Dunno speed, tell stty() to not mess with it */
logit(LOG_INFO, "Starting external getty on %s, speed %u", tty, B0);
prepare_tty(tty, B0, "getty", rlimit); prepare_tty(tty, B0, "getty", rlimit);
if (activate_console(noclear, nowait)) { if (activate_console(noclear, nowait))
logit(LOG_INFO, "Starting getty on %s", tty);
rc = execv(cmd, args); rc = execv(cmd, args);
}
vhangup(); vhangup();
_exit(rc);
return rc;
} }
int sh(char *tty) return pid;
{
char *args[2] = {
NULL,
NULL
};
char *arg0;
size_t len;
/* The getty process is usually responsible for the UTMP login record */
utmp_set_login(tty, NULL);
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
len = strlen(_PATH_BSHELL) + 2;
arg0 = malloc(len);
if (!arg0)
err(1, "Failed allocating memory");
snprintf(arg0, len, "-%s", _PATH_BSHELL);
args[0] = arg0;
/* Unblock signals inherited from parent */
sig_unblock();
return execv(_PATH_BSHELL, args);
} }
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[]) pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
{ {
pid_t pid;
pid = fork();
if (!pid) {
int rc = 1; int rc = 1;
prepare_tty(tty, B0, "finitsh", rlimit); prepare_tty(tty, B0, "finit-sh", rlimit);
if (activate_console(noclear, nowait)) if (activate_console(noclear, nowait))
rc = sh(tty); rc = sh(tty);
return rc; _exit(rc);
}
return pid;
} }
int run_parts(char *dir, char *cmd) int run_parts(char *dir, char *cmd)
+111 -274
View File
@@ -36,11 +36,7 @@
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/stat.h> /* umask(), mkdir() */ #include <sys/stat.h> /* umask(), mkdir() */
#include <sys/wait.h> #include <sys/wait.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "cgroup.h" #include "cgroup.h"
@@ -56,7 +52,6 @@
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
#include "schedule.h" #include "schedule.h"
#include "watchdog.h"
int runlevel = 0; /* Bootstrap 'S' */ int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */ int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
@@ -64,14 +59,11 @@ int prevlevel = -1;
int debug = 0; /* debug mode from kernel cmdline */ int debug = 0; /* debug mode from kernel cmdline */
int rescue = 0; /* rescue mode from kernel cmdline */ int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */ int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
char *sdown = NULL; char *sdown = NULL;
char *network = NULL; char *network = NULL;
char *hostname = NULL; char *hostname = NULL;
char *rcsd = FINIT_RCSD; char *rcsd = FINIT_RCSD;
char *runparts = NULL; char *runparts = NULL;
char *osheading = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */ uev_ctx_t *ctx = NULL; /* Main loop context */
svc_t *wdog = NULL; /* No watchdog by default */ svc_t *wdog = NULL; /* No watchdog by default */
@@ -87,7 +79,7 @@ static void banner(void)
* similar enabled to start emptying /dev/kmsg, but for * similar enabled to start emptying /dev/kmsg, but for
* our progress we want to own the console. * our progress we want to own the console.
*/ */
if (!debug && !kerndebug) if (!debug)
klogctl(6, NULL, 0); klogctl(6, NULL, 0);
/* /*
@@ -96,14 +88,7 @@ static void banner(void)
*/ */
plugin_run_hooks(HOOK_BANNER); plugin_run_hooks(HOOK_BANNER);
#ifdef INIT_OSHEADING print_banner(INIT_HEADING);
osheading = INIT_OSHEADING;
if (osheading) {
if (!osheading[0])
osheading = release_heading();
print_banner(osheading);
}
#endif
} }
/* /*
@@ -122,7 +107,6 @@ static int fsck(int pass)
while ((fs = getfsent())) { while ((fs = getfsent())) {
char cmd[80]; char cmd[80];
struct stat st; struct stat st;
int fsck_rc = 0;
if (fs->fs_passno != pass) if (fs->fs_passno != pass)
continue; continue;
@@ -140,33 +124,8 @@ static int fsck(int pass)
continue; continue;
} }
#ifdef FSCK_FIX
snprintf(cmd, sizeof(cmd), "fsck -yf %s", fs->fs_spec);
#else
snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec); snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec);
#endif rc += run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
fsck_rc = run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
/*
* "failure" is defined as exiting with a return code of
* 2 or larger. A return code of 1 indicates that filesystem
* errors were corrected but that the boot may proceed.
*/
if (fsck_rc > 1) {
char *sulogin[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
break;
}
do_shutdown(SHUT_REBOOT);
}
rc += fsck_rc;
} }
endfsent(); endfsent();
@@ -176,51 +135,18 @@ static int fsck(int pass)
static int fsck_all(void) static int fsck_all(void)
{ {
int rc = 0; int pass, rc = 0;
#ifndef FAST_BOOT
int pass;
for (pass = 1; pass < 10; pass++) { for (pass = 1; pass < 10; pass++) {
rc = fsck(pass); rc = fsck(pass);
if (rc) if (rc)
break; break;
} }
#endif
return rc; return rc;
} }
/* Wrapper for mount(2), logs any errors to stderr */
static void fs_mount(const char *src, const char *tgt, const char *fstype,
unsigned long flags, const void *data)
{
const char *msg = !fstype ? "MS_MOVE" : "mounting";
int rc;
rc = mount(src, tgt, fstype, flags, data);
if (rc && errno != EBUSY)
_pe("Failed %s %s on %s", msg, src, tgt);
}
#ifndef SYSROOT #ifndef SYSROOT
/* If / is not listed in fstab, or listed as 'ro', leave it alone */
static int fs_readonly_root(struct fstab *fs)
{
char *tok, *str;
if (!fs)
return 1;
str = fs->fs_mntops;
while ((tok = strtok(str, ","))) {
if (!strcmp(tok, "ro"))
return 1;
str = NULL;
}
return 0;
}
static void fs_remount_root(int fsckerr) static void fs_remount_root(int fsckerr)
{ {
struct fstab *fs; struct fstab *fs;
@@ -233,7 +159,8 @@ static void fs_remount_root(int fsckerr)
break; break;
} }
if (fs_readonly_root(fs)) /* If / is not listed in fstab, or listed as 'ro', leave it alone */
if (!fs || !strcmp(fs->fs_type, "ro"))
goto out; goto out;
if (fsckerr) if (fsckerr)
@@ -252,74 +179,11 @@ static void fs_remount_root(int fsckerr)
* XXX: Untested, in the initramfs age we should * XXX: Untested, in the initramfs age we should
* probably use switch_root instead. * probably use switch_root instead.
*/ */
fs_mount(SYSROOT, "/", NULL, MS_MOVE, NULL); rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
} }
#endif /* SYSROOT */ #endif /* SYSROOT */
/* static void fs_mount(void)
* Opinionated file system setup. Checks for critical mount points and
* mounts them as most users expect. All file systems are checked with
* /proc/mounts before mounting.
*
* Embedded systems, and other people who want full control, can set up
* their system with /etc/fstab, which is handled before this function
* is called. For systems like Debian/Ubuntu, who only have / and swap
* in their /etc/fstab, this function does all the magic necessary.
*/
static void fs_finalize(void)
{
/*
* 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.
*/
if (!fismnt("/dev/shm")) {
makedir("/dev/shm", 0777);
fs_mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
}
/* Modern systems use /dev/pts */
if (!fismnt("/dev/pts")) {
char opts[32];
int mode;
int gid;
gid = getgroup("tty");
if (gid == -1)
gid = 0;
/* 0600 is default on Debian, use 0620 to get mesg y by default */
mode = 0620;
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
makedir("/dev/pts", 0755);
fs_mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
}
/*
* Modern systems use tmpfs for /run. Fallback to /var/run if
* /run doesn't exist is handled by the bootmisc plugin. It
* also sets up compat symlinks.
*
* The unconditional mount of /run/lock is for DoS prevention.
* To override any of this behavior, add entries to /etc/fstab
* for /run (and optionally /run/lock).
*/
if (fisdir("/run") && !fismnt("/run")) {
fs_mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
makedir("/run/lock", 1777);
fs_mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880");
}
/* Modern systems use tmpfs for /tmp */
if (!fismnt("/tmp"))
fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
}
static void fs_mount_all(void)
{ {
if (!rescue) if (!rescue)
fs_remount_root(fsck_all()); fs_remount_root(fsck_all());
@@ -333,21 +197,15 @@ static void fs_mount_all(void)
_d("Calling extra mount hook, after mount -a ..."); _d("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST); plugin_run_hooks(HOOK_MOUNT_POST);
if (whichp("swapon")) run("swapon -ea");
run("swapon -ea", "swapon"); umask(0022);
_d("Finalize, ensure common file systems are available ...");
fs_finalize();
} }
/* /*
* We need /proc for rs_remount_root() and conf_parse_cmdline(), /dev * We need /proc for rs_remount_root() and conf_parse_cmdline(), /dev
* for early multi-console, and /sys for the cgroups support. Any * for early multi-console, and /sys for the cgroups support. Any
* occurrence of these file systems in /etc/fstab will replace these * occurrence of these file systems in /etc/fstab will replace these
* mounts later in fs_mount_all() * mounts later in fs_mount()
*
* Ignore any mount errors with EBUSY, kernel likely already mounted
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
*/ */
static void fs_init(void) static void fs_init(void)
{ {
@@ -360,23 +218,52 @@ static void fs_init(void)
}; };
size_t i; size_t i;
/* mask writable bit for g and o */ for (i = 0; i < NELEMS(fs); i++)
umask(022); mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
}
for (i = 0; i < NELEMS(fs); i++) {
/* /*
* Check if already mounted, we may be running in a * If everything goes south we can use this to give the operator an
* container, or an initramfs ran before us. The * emergency shell to debug the problem -- Finit should not crash!
* function fismnt() reliles on /proc/mounts being *
* unique for each chroot/container. * Note: Only use this for debugging a new Finit setup, don't use
* this in production since it gives a root shell to anyone
* if Finit crashes.
*
* This emergency shell steps in to prevent "Aieee, PID 1 crashed"
* messages from the kernel, which usually results in a reboot, so
* that the operator instead can debug the problem.
*/ */
if (fismnt(fs[i].file)) static void emergency_shell(void)
continue; {
#ifdef EMERGENCY_SHELL
pid_t pid;
fs_mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL); pid = fork();
} if (pid) {
while (1) {
pid_t id;
/* Reap 'em (prevents Zombies) */
id = waitpid(-1, NULL, WNOHANG);
if (id == pid)
break;
} }
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
/*
* Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell.
*/
setsid();
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
}
#endif /* EMERGENCY_SHELL */
}
/* /*
* Handle bootstrap transition to configured runlevel, start TTYs * Handle bootstrap transition to configured runlevel, start TTYs
@@ -389,8 +276,20 @@ static void fs_init(void)
* finit.conf, or *.conf in finit.d/, run to completion before we * finit.conf, or *.conf in finit.d/, run to completion before we
* finalize the bootstrap process by calling this function. * finalize the bootstrap process by calling this function.
*/ */
static void finalize(void *unused) static void finalize(void)
{ {
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel, start all services ...");
service_runlevel(cfglevel);
/* Clean up bootstrap-only tasks/services that never started */ /* Clean up bootstrap-only tasks/services that never started */
_d("Clean up all bootstrap-only tasks/services ..."); _d("Clean up all bootstrap-only tasks/services ...");
svc_prune_bootstrap(); svc_prune_bootstrap();
@@ -412,9 +311,9 @@ static void finalize(void *unused)
/* Disable progress output at normal runtime */ /* Disable progress output at normal runtime */
enable_progress(0); enable_progress(0);
/* System bootrapped, launch TTYs et al */ /* Delayed start of TTYs at bootstrap */
bootstrap = 0; _d("Launching all getty services ...");
service_step_all(SVC_TYPE_RESPAWN); tty_runlevel();
} }
/* /*
@@ -423,7 +322,7 @@ static void finalize(void *unused)
static void crank_worker(void *unused) static void crank_worker(void *unused)
{ {
/* /*
* Initialize state machine and start all bootstrap tasks * Initalize state machine and start all bootstrap tasks
* NOTE: no network available! * NOTE: no network available!
*/ */
sm_init(&sm); sm_init(&sm);
@@ -432,17 +331,12 @@ static void crank_worker(void *unused)
/* /*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be * Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
* allowed to complete their work in [S], or timeout, before we switch * allowed to complete their work in [S], or timeout, before we call
* to the configured runlevel and call finalize(), should not take more * finalize(), should not take more than 120 sec.
* than 120 sec.
*/ */
static void bootstrap_worker(void *work) static void final_worker(void *work)
{ {
static int cnt = 120; static int cnt = 120;
static struct wq final = {
.cb = finalize,
.delay = 10
};
_d("Step all services ..."); _d("Step all services ...");
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -458,36 +352,12 @@ static void bootstrap_worker(void *work)
else else
_d("Timeout, resuming bootstrap."); _d("Timeout, resuming bootstrap.");
_d("Starting runlevel change finalize ..."); finalize();
schedule_work(&final);
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(cfglevel);
}
static int version(int rc)
{
puts(PACKAGE_STRING);
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
return rc;
} }
static int usage(int rc) static int usage(int rc)
{ {
printf("Usage: %s [OPTIONS] [q | Q | 0-9]\n\n" printf("Usage: telinit [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n" "Options:\n"
// " -a Ignored, compat SysV init\n" // " -a Ignored, compat SysV init\n"
// " -b Ignored, compat SysV init\n" // " -b Ignored, compat SysV init\n"
@@ -505,7 +375,12 @@ static int usage(int rc)
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n" " q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
" if modified, same as initctl reload or SIGHUP to PID 1\n" " if modified, same as initctl reload or SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n" " 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", prognm); "\n");
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
return rc; return rc;
} }
@@ -518,7 +393,6 @@ static int telinit(int argc, char *argv[])
{ {
int c; int c;
progname(argv[0]);
while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) { while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) {
switch(c) { switch(c) {
case 'a': case 'b': case 'e': case 's': case 'z': case 'a': case 'b': case 'e': case 's': case 'z':
@@ -528,7 +402,7 @@ static int telinit(int argc, char *argv[])
break; break;
case 'v': case 'V': case 'v': case 'V':
return version(0); return puts("v" VERSION) == EOF;
case 'h': case 'h':
case '?': case '?':
@@ -540,13 +414,13 @@ static int telinit(int argc, char *argv[])
int req = (int)argv[optind][0]; int req = (int)argv[optind][0];
if (isdigit(req)) if (isdigit(req))
return systemf("initctl -b runlevel %c", req); return systemf("initctl runlevel %c", req);
if (req == 'q' || req == 'Q') if (req == 'q' || req == 'Q')
return systemf("initctl -b reload"); return systemf("initctl reload");
if (req == 's' || req == 'S') if (req == 's' || req == 'S')
return systemf("initctl -b runlevel %c", req); return systemf("initctl runlevel %c", req);
} }
/* XXX: add non-pid1 process monitor here /* XXX: add non-pid1 process monitor here
@@ -560,13 +434,12 @@ static int telinit(int argc, char *argv[])
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct wq crank_work = { struct wq crank = {
.cb = crank_worker, .cb = crank_worker
.delay = 10
}; };
struct wq bootstrap_work = { struct wq final = {
.cb = bootstrap_worker, .cb = final_worker,
.delay = 100 .delay = 1000
}; };
uev_ctx_t loop; uev_ctx_t loop;
@@ -591,58 +464,41 @@ int main(int argc, char *argv[])
console_init(); console_init();
/* /*
* Initialize event context. * Initalize event context.
*/ */
uev_init1(&loop, 1); uev_init1(&loop, 1);
ctx = &loop; ctx = &loop;
/* /*
* Set PATH, SHELL, and PWD early to something sane * Set PATH, SHELL, PWD, and umask early to something sane
*/ */
setenv("PATH", _PATH_STDPATH, 1); setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1); setenv("SHELL", _PATH_BSHELL, 1);
setenv("LOGNAME", "root", 1);
setenv("USER", "root", 1);
if (chdir("/")) chdir("/");
_pe("Failed cd /"); umask(0);
/* Set up canvas */
if (!rescue && !debug)
screen_init();
/* Allow progress, if enabled */
enable_progress(1);
/* /*
* In case of emergency. * In case of emergency.
*/ */
if (rescue) { emergency_shell();
char *sulogin[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
for (i = 0; i < NELEMS(sulogin); i++) { /* Load plugins early, the first hook is in banner(), so we
if (systemf(sulogin[i])) * need plugins loaded before calling it. */
continue;
rescue = 0;
break;
}
}
/*
* Load plugins early, the first hook is in banner(), so we
* need plugins loaded before calling it.
*/
plugin_init(&loop); plugin_init(&loop);
/* /*
* Hello world. * Hello world.
*/ */
enable_progress(1); /* Allow progress, if enabled */
banner(); banner();
if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel S", osheading);
else
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel S");
/* /*
* Initial setup of signals, ignore all until we're up. * Initial setup of signals, ignore all until we're up.
*/ */
@@ -651,18 +507,17 @@ int main(int argc, char *argv[])
/* /*
* Initialize default control groups, if available * Initialize default control groups, if available
*/ */
cgroup_init(&loop); cgroup_init();
/* Check and mount filesystems. */ /* Check and mount filesystems. */
fs_mount_all(); fs_mount();
/* Bootstrap conditions, needed for hooks */ /* Bootstrap conditions, needed for hooks */
cond_init(); cond_init();
/* /* Emit conditions for early hooks that ran before the
* Emit conditions for early hooks that ran before the condition * condition system was initialized in case anyone . */
* system was initialized in case anyone.
*/
cond_set_oneshot(plugin_hook_str(HOOK_BANNER)); cond_set_oneshot(plugin_hook_str(HOOK_BANNER));
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP)); cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
@@ -671,20 +526,6 @@ int main(int argc, char *argv[])
*/ */
conf_init(&loop); conf_init(&loop);
/*
* Start built-in watchdogd as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
wdog = svc_find_by_nameid("watchdog", "finit");
}
/*
* Start kernel event daemon as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/keventd"))
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init " FINIT_LIBPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
/* Base FS up, enable standard SysV init signals */ /* Base FS up, enable standard SysV init signals */
sig_setup(&loop); sig_setup(&loop);
@@ -699,17 +540,13 @@ int main(int argc, char *argv[])
_d("Starting initctl API responder ..."); _d("Starting initctl API responder ...");
api_init(&loop); api_init(&loop);
umask(022);
_d("Starting the big state machine ..."); _d("Starting the big state machine ...");
schedule_work(&crank_work); schedule_work(&crank);
_d("Starting bootstrap finalize timer ..."); _d("Starting bootstrap finalize timer ...");
schedule_work(&bootstrap_work); schedule_work(&final);
/*
* Background service tasks
*/
service_init();
/* /*
* Enter main loop to monitor /dev/initctl and services * Enter main loop to monitor /dev/initctl and services
+3 -24
View File
@@ -30,34 +30,17 @@
#include <paths.h> #include <paths.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sysexits.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
#include <lite/lite.h> #include <lite/lite.h>
#endif
#include <uev/uev.h> #include <uev/uev.h>
/* just in case */
#ifndef _PATH_STDPATH
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
#endif
#ifndef _PATH_VARRUN #ifndef _PATH_VARRUN
#define _PATH_VARRUN "/var/run/" #define _PATH_VARRUN "/var/run/"
#endif #endif
#ifndef FINIT_CGPATH #define LOGIT_PATH FINIT_LIBPATH_ "/logit"
#define FINIT_CGPATH "/sys/fs/cgroup"
#endif
/* To use these, include config.h, or define FINIT_LIBPATH_ */
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
#define _PATH_GETTY FINIT_LIBPATH_ "/getty"
#define CMD_SIZE 256 #define CMD_SIZE 256
#define LINE_SIZE 1024 #define LINE_SIZE 1024
@@ -79,10 +62,10 @@
/* Finit extensions over std SysV */ /* Finit extensions over std SysV */
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */ #define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */ #define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
#define INIT_CMD_START_SVC 10 /* START service */ #define INIT_CMD_START_SVC 10
#define INIT_CMD_STOP_SVC 11 #define INIT_CMD_STOP_SVC 11
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */ #define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */ #define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */ #define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */ #define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
#define INIT_CMD_GET_RUNLEVEL 16 #define INIT_CMD_GET_RUNLEVEL 16
@@ -95,7 +78,6 @@
#define INIT_CMD_SVC_QUERY 130 #define INIT_CMD_SVC_QUERY 130
#define INIT_CMD_SVC_FIND 131 #define INIT_CMD_SVC_FIND 131
#define INIT_CMD_SVC_FIND_BYC 132 #define INIT_CMD_SVC_FIND_BYC 132
#define INIT_CMD_SIGNAL 133
#define INIT_CMD_NACK 254 #define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255 #define INIT_CMD_ACK 255
@@ -114,14 +96,11 @@ extern int prevlevel;
extern int debug; extern int debug;
extern int rescue; extern int rescue;
extern int single; extern int single;
extern int bootstrap;
extern int kerndebug;
extern char *rcsd; extern char *rcsd;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
extern char *hostname; extern char *hostname;
extern char *runparts; extern char *runparts;
extern char *osheading;
extern uev_ctx_t *ctx; extern uev_ctx_t *ctx;
#endif /* FINIT_H_ */ #endif /* FINIT_H_ */

Some files were not shown because too many files have changed in this diff Show More