Compare commits

..
11 Commits
Author SHA1 Message Date
Joachim Wiberg 58e31dbd22 .github: try running with older Ubuntu version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-06 19:57:46 +02:00
Joachim Wiberg 85058b54a8 plugins: bootmisc: use relative symlink for /var/run
In an upside world, much like the Finit test cases, the root may be
relocated.  This make /var/run relative to /var, instead of /.  Which
hopefully is safer and covers more use-cases.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 17:46:43 +02:00
Joachim Wiberg f16df142d7 Minor shellcheck fixes to debug code
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 17:46:20 +02:00
Joachim Wiberg f9f52a46a6 Debug sys plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 07:27:22 +02:00
Joachim Wiberg ef1bfb869f test: HUP Finit from inside container instead ...
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 06:49:52 +02:00
Joachim Wiberg 5002c74876 test: force enable debug logs and HUP Finit before test
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 06:39:23 +02:00
Joachim Wiberg a0bc23c48f test: enable finit debug to see why API socket isn't created
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:52:08 +02:00
Joachim Wiberg 350cf6e578 test: dump contents of /var/run/finit/ to check for socket
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:46:48 +02:00
Joachim Wiberg b96a8d8766 test: dump correct root dir and add more PID 1 debug
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:31:13 +02:00
Joachim Wiberg a419828dea .github: no ~/.cache, check rest
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:22:05 +02:00
Joachim Wiberg 702b22d4a3 .github: check if ~/.cache exists, dump test root files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:19:13 +02:00
253 changed files with 3387 additions and 13000 deletions
+4 -3
View File
@@ -9,9 +9,9 @@ For email questions/requests/alerts there is always the risk of memory
exhaustion on the part of the maintainer(s). exhaustion on the part of the maintainer(s).
If you are unsure of what to do, or how to implement an idea or bugfix, If you are unsure of what to do, or how to implement an idea or bugfix,
open an issue with `"[RFC: Unsure if this is a bug ... ?"`, or use the open an issue with `"[RFC: Unsure if this is a bug ... ?"`, or similar,
GitHub discussions forum, so we can discuss it. Talking about the code so we can discuss it. Talking about the code first is the best way to
first is the best way to get started before submitting a pull request. get started before submitting a pull request.
Either way, when sending an email, patch, or pull request, start by Either way, when sending an email, patch, or pull request, start by
stating the version the change is made against, what it does, and why. stating the version the change is made against, what it does, and why.
@@ -73,6 +73,7 @@ It is expected of everyone engaging in the project to, in the words of
Bill & Ted; [be excellent to each other][conduct]. Bill & Ted; [be excellent to each other][conduct].
[github]: https://github.com/troglobit/finit/
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
[gitbook]: https://git-scm.com/book/ch5-2.html [gitbook]: https://git-scm.com/book/ch5-2.html
[conduct]: CODE-OF-CONDUCT.md [conduct]: CODE-OF-CONDUCT.md
+24 -23
View File
@@ -15,55 +15,56 @@ jobs:
build: build:
# Verify we can build on latest Ubuntu with both gcc and clang # Verify we can build on latest Ubuntu with both gcc and clang
name: ${{ matrix.compiler }} name: ${{ matrix.compiler }}
runs-on: ubuntu-latest runs-on: ubuntu-18.04
strategy: strategy:
matrix: matrix:
compiler: [gcc, clang] compiler: [gcc, clang]
fail-fast: false fail-fast: false
env: env:
MAKEFLAGS: -j3
CC: ${{ matrix.compiler }} CC: ${{ matrix.compiler }}
steps: steps:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install pkg-config tree jq libuev-dev libite-dev sudo apt-get -y install pkg-config
- uses: actions/checkout@v4 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 - name: Static Finit
run: | run: |
./autogen.sh ./autogen.sh
./configure --prefix= --enable-static ./configure --prefix= --enable-static
make -j3 V=1 make V=1
- name: Regular Finit - name: Regular Finit
run: | run: |
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ ./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \ make clean
--with-keventd make V=1
make -j3 clean - name: Install to ~/tmp and Inspect
make -j3 V=1
- name: Install to /tmp
run: | run: |
DESTDIR=/tmp make install-strip DESTDIR=/tmp make install-strip
tree /tmp || true tree /tmp
- name: Check dependencies
run: |
ldd /tmp/sbin/finit ldd /tmp/sbin/finit
size /tmp/sbin/finit size /tmp/sbin/finit
ldd /tmp/sbin/initctl ldd /tmp/sbin/initctl
size /tmp/sbin/initctl size /tmp/sbin/initctl
ldd /tmp/sbin/reboot ldd /tmp/sbin/reboot
size /tmp/sbin/reboot size /tmp/sbin/reboot
- name: Verify starting and showing usage text /tmp/sbin/finit -h
run: | /tmp/sbin/initctl -h
sudo /tmp/sbin/finit -h
sudo /tmp/sbin/initctl -h
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Run Unit Tests - name: Run Unit Tests
run: | run: |
make check || (cat test/test-suite.log; false) 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 - name: Upload Test Results
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: finit-test-${{ matrix.compiler }} name: finit-test-${{ matrix.compiler }}
path: test/*.log path: test/*
+20 -44
View File
@@ -15,59 +15,40 @@ jobs:
coverity: coverity:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Fetch latest Coverity Scan MD5 - uses: actions/cache@v2
id: var id: coverity-toolchain-cache
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
run: |
wget -q https://scan.coverity.com/download/cxx/linux64 \
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
-O coverity-latest.tar.gz.md5
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
- uses: actions/cache@v4
id: cache
with: with:
path: coverity-latest.tar.gz path: cov-analysis-linux64
key: ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }} key: ${{ runner.os }}-coverity
restore-keys: |
${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }}
${{ runner.os }}-coverity-
${{ runner.os }}-coverity
- name: Download Coverity Scan - name: Download Coverity Scan
if: steps.coverity-toolchain-cache.outputs.cache-hit != 'true'
env: env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
run: | run: |
if [ ! -f coverity-latest.tar.gz ]; then wget -q https://scan.coverity.com/download/cxx/linux64 \
wget -q https://scan.coverity.com/download/cxx/linux64 \ --post-data "token=$TOKEN&project=${COVERITY_PROJ}" \
--post-data "token=$TOKEN&project=${COVERITY_PROJ}" \ -O cov-analysis-linux64.tar.gz
-O coverity-latest.tar.gz mkdir cov-analysis-linux64
else tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
echo "Latest Coverity Scan available from cache :-)"
md5sum coverity-latest.tar.gz
fi
mkdir coverity
tar xzf coverity-latest.tar.gz --strip 1 -C coverity
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install pkg-config sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.4.0/libuev-2.4.0.tar.xz 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.5.2/libite-2.5.2.tar.gz wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
tar xf libuev-2.4.0.tar.xz tar xf libuev-2.3.2.tar.xz
tar xf libite-2.5.2.tar.gz tar xf libite-2.3.1.tar.gz
(cd libuev-2.4.0 && ./configure --prefix=/usr && make && sudo make install-strip) (cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip) (cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig sudo ldconfig
- name: Configure - name: Build
run: | run: |
./autogen.sh ./autogen.sh
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd ./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
- name: Build export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
run: |
export PATH=`pwd`/coverity/bin:$PATH
cov-build --dir cov-int make cov-build --dir cov-int make
- name: Submit results to Coverity Scan - name: Submit result to Coverity Scan
env: env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
run: | run: |
@@ -80,8 +61,3 @@ jobs:
--form version=trunk \ --form version=trunk \
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \ --form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
https://scan.coverity.com/builds?project=${COVERITY_PROJ} https://scan.coverity.com/builds?project=${COVERITY_PROJ}
- name: Upload build.log
uses: actions/upload-artifact@v4
with:
name: coverity-build.log
path: cov-int/build-log.txt
+53 -46
View File
@@ -3,60 +3,67 @@ name: Release General
on: on:
push: push:
tags: tags:
- '[0-9]+.[0-9]+*' - '[0-9]+.[0-9]+'
jobs: jobs:
release: release:
name: Build and upload release tarball name: Create GitHub release
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: if: startsWith(github.ref, 'refs/tags/')
MAKEFLAGS: -j3 outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Installing dependencies ...
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
- name: Setting release variables ...
id: build
run: |
ver=${GITHUB_REF#refs/tags/}
echo "ver=${ver}" >> $GITHUB_OUTPUT
if echo $ver | grep -qE '^[0-9]+\.[0-9]+(\.[0-9]+)?(-alpha|-beta|-rc)[0-9]*$'; then
echo "pre=true" >> $GITHUB_OUTPUT
else
echo "pre=false" >> $GITHUB_OUTPUT
fi
if echo $ver | grep -qE '^[0-9.]+\.[0-9.]+(\.[0-9]+)?$'; then
echo "latest=true" >> $GITHUB_OUTPUT
else
echo "latest=false" >> $GITHUB_OUTPUT
fi
- name: Creating Makefiles ...
run: |
./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Build release ...
run: |
make release || (cat test/test-suite.log; false)
ls -lF ../
mkdir -p artifacts/
mv ../*.tar.* artifacts/
- name: Extract ChangeLog entry ... - name: Extract ChangeLog entry ...
# Hack to extract latest entry for body_path below
run: | run: |
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \ awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|head -n -1 > release.md |head -n -1 > release.md
cat release.md cat release.md
- uses: ncipollo/release-action@v1 - name: Create release ...
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
name: Finit v${{ github.ref_name }} tag_name: ${{ github.ref }}
prerelease: ${{ steps.build.outputs.pre }} release_name: Finit v${{ github.ref }}
makeLatest: ${{ steps.build.outputs.latest }} body_path: release.md
bodyFile: "release.md" draft: false
artifacts: "artifacts/*" 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/*
-51
View File
@@ -1,51 +0,0 @@
name: Weekly Distcheck
# Verify that `make distcheck` works as intended: all tests pass, and
# any new files are distributed in the tarballs
on:
schedule:
- cron: '0 0 * * 0'
workflow_dispatch:
jobs:
schedule:
runs-on: ubuntu-latest
outputs:
run: ${{ steps.check.outputs.run }}
steps:
- uses: actions/checkout@v4
- name: Check for any commits since last week
id: schedule
run: |
commits="$(git log --format=%H --since='1 week ago')"
if [ -n "$commits" ]; then
echo "run=true" >> $GITHUB_OUTPUT
else
echo "run=false" >> $GITHUB_OUTPUT
fi
distcheck:
needs: schedule
if: ${{ needs.schedule.outputs.run == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
- uses: actions/checkout@v4
- name: Base run
run: |
./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
make -j9 V=1
- name: Install to /tmp
run: |
DESTDIR=/tmp make install-strip
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Check dist tarball
run: |
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
dir=finit-$ver/_build/sub
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
-4
View File
@@ -14,7 +14,6 @@ GTAGS
/autom4te.cache /autom4te.cache
/aclocal.m4 /aclocal.m4
/ar-lib /ar-lib
/backup/
/compile /compile
/config.cache /config.cache
/config.h /config.h
@@ -37,9 +36,6 @@ GTAGS
/test-driver /test-driver
/test-suite.log /test-suite.log
# Misc
/.gdb_history
# TroglOS build system # TroglOS build system
/.config /.config
/.stamp /.stamp
-7
View File
@@ -1,7 +0,0 @@
# GNU Global .conf file, see /etc/gtags/gtags.conf for details.
default:\
:tc=default@/etc/gtags/gtags.conf:\
:tc=common:
common:\
:skip=tags,TAGS,ID,y.tab.c,y.tab.h,gtags.files,cscope.files,cscope.out,cscope.po.out,cscope.in.out,,{arch}/,autom4te.cache/,*.orig,*.rej,*.bak,*~,#*#,*.swp,*.tmp,*_flymake.*,*_flymake,*.o,*.a,*.so,*.lo,*.zip,*.gz,*.bz2,*.xz,*.lzh,*.Z,*.tgz,*.min.js,*min.css,sysroot/:
+7 -690
View File
@@ -4,681 +4,20 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.9][UNRELEASED] [4.3][UNRELEASED]
--------------------
> [!CAUTION]
> This release changes how Finit reacts to the exit status of `pre:`
> scripts. Finit will now no longer start the main process if its
> `pre:` script fails for any reason. So, if you have pre scripts that,
> e.g., create directories, make sure they are idempotent.
### Changes
- Add individual timeout (optional) support for pre/post/ready scripts
- Add support for systems with broken RTC, i.e., those that at power-on
may reset the RTC to a random date instead of zero, issue #418
- Add support for detecting "in-container" when in `systemd-nspawn`
- Check exit status of `pre:` scripts, on failure drive service/sysv to
`crashed` state. (The exit code of `post:` scripts remain ignored)
- All pre/post/ready scripts are now also searched for in `$PATH`
- Reset color attributes and clear screen when starting up. This is
for boot loaders like GRUB, which leave background color artifacts
### Fixes
- Fix #415: only mark reverse-dependencies "dirty" if the main service
does not support SIGHUP. This helps avoid unnecessary restarts of
services that depend on a service that supports SIGHUP
- Fix #417: leaking inotify watchers in the cgroup subsystem
- Fix #422: honor `notfail` flag in `/etc/fstab`
- Fix #425: ordering bug in detecting .conf file changes to services
and `initctl reload`, or runlevel changes
- Fix `initctl touch` of template services, previously marking a
service created from a template as "dirty" did not take
- Fix unintended restart of template "siblings". I.e., `initctl touch`
of instantiated template service A affected B from same template
- Fix buggy `--with-rtc-date=DATE` configure option
- Fix kill on timeout of pre/post/ready scripts
[4.8][] - 2024-10-13
--------------------
### Changes
- Avoid remounting already mounted `/run` and `/tmp` directories. This
extends the existing support for detecting mounted directories to
include complex mount hierarchies are in use, overlayfs and tmpfs
mounts. Feature by Mathias Thore, Atlas Copco
- getty: trigger /etc/issue compat mode for Alpine Linux
- tmpfiles.d: skip `x11.conf` unless X11 common plugin is enabled
- tmpfiles.d: ignore x/X command, nos support for cleanup at runtime
- Drop debug mode `-D` from `udevd` in `hotplug.conf.in`, allow the
user to set this in `/etc/default/udevd` instead
- Certain initctl APIs at bootstrap are not supported, update warning
log to include command (number) for troubleshooting, issue #398
- Add support for hwrng to urandom plugin and check for empty seed
- Add `runparts -b` (batch mode) support, disables escape sequences
- New configure `--without-rc-local`, disables `/etc/rc.local` support
- New configure `--disable-cgroup` option, disables cgroup v2 detection
- `initctl show template@foo.conf` now shows how an enabled template
service has been evaluated by Finit, issue #411
- Extend `initctl` timeout connecting and waiting for Finit reply. The
previous 2 + 2 second poll timeout has proved to be too short on more
complex systems. Now a 15 + 15 second timeout is applied which should
be more resistant to temporary overload scenarios, issue #407
### Fixes
- Fix #397: system shutdown/reboot can block on console input if action
is started remotely (ssh). Caused by legact TTY screen size probing,
removed from both bootstrap and shutdown/reboot
- Fix #400: both `HOOK_MOUNT_ERROR` and `sulogin()` fail to trigger on
either mount or `fsck` errors. Problem caused by unresolved status
from pipe, calling `pclose()` without extracting exit status
- Fix #402: `initctl touch` does not respect `-n` (no error) flag
- Fix #403: `initctl touch` does not support template services
- Fix #404: possible undefined behavior when `--with-fstab=no` is set
- Fix #405: `@console` getty does not work with `tty0 ttyS0`
- Fix #409: prevent tmpfiles from following symlinks for `L+` and `R`,
otherwise symlink targets would also be removed. Found and fixed by
Mathias Thore and Ming Liu, Atlas Copco
- Fix #414: Frr Zebra immediately restarts on `initctl stop zebra`.
The fix likely works for all Frr/Quagga services due to the way they
create and delete their pid file
- Cosmetic issue with `[ OK ]` messages being printed out of order at
shutdown/reboot. Caused by nested calls to `service_stop()`
- Cosmetic issue with duplicate "Restoring RTC" message at bootstrap
[4.7][] - 2024-01-07
--------------------
### Changes
- Silence "not available" messages for run/task/service with `nowarn`
- Update docs, `cgroup.root` workaround for `SCHED_RR` tasks
- Drop runlevels 0 and 6 from `keventd`, not needed at reboot/poweroff
- Mount `/dev/shm` with mode 1777 (sticky bit)
- Mount `/dev/mqueue` if available, inspiration from Alpine Linux
- Update Alpine Linux installer and configuration files, tested on
Alpine v3.19, some assembly still required
### Fixes
- Fix confusing warning message when tmpfiles.d fails to install symlink
- Fix tmpfiles.d legacy `/run/lock/subsys`, ordering
- Add missing `/var/tmp` and `/var/lock -> /run/lock` (tmpfiles.d)
- Fix #388: log redirection broken unless Finit runs in debug mode.
Found and fixed by Ryan Rorison
- Fix #389: must libc requires `libgen.h` for `basename()` function.
Reported and worked around with new `basenm()` function by Stargirl
- Fix #392: `service/foo/ready` condition reasserted on pidfile removal
[4.6][] - 2023-11-13
--------------------
### Changes
- Add support for service `notify:pid` and `readiness none` global
option to change how Finit expects readiness notification, issue #386
### Fixes
- Fix #383: dbus and runparts regression in Finit v4.5. The configure
script must expand `FINIT_RUNPATH_` before defining it in `config.h`
- Fix #384: service environment variables drop everything but the first
argument, e.g., `VAR="foo bar qux"` drops everything but `foo`
- Fix #385: internal conditions, e.g., `<int/bootstrap>` turn into flux
when leaving bootstrap, causing depending services to stop
- Fix #387: global environment variables declared with `set VAR=NAME` do
not drop leading `set `, causing `'set VAR'='NAME'` in env.
- Sanity check environment variables, for services and globally. Ensure
the variable name (key) does not contain spaces, or a leading `set `
[4.5][] - 2023-10-30
--------------------
### Changes
- Refactor `runparts` and `/etc/rc.local` to no longer block the main
loop, allowing `initctl` calls to interact with Finit. Issue #356
- Refactor the `run` stanza to no longer block the main loop, issue #362
- Allow `sulogin` with a user different from `root`, issue #357
- Allow disabling invocation of rescue mode from kernel command line
- Add `initctl -f` to force-skip asking Finit for existing services
when creating new services during bootstrap, e.g. `/etc/rc.local`
- `initctl runlevel` now returns `N S` instead of `N 10` in bootstrap
- `initctl runlevel N` during bootstrap is now allowed. It changes
the next runlevel to go to when bootstrap has completed. Effectively
overriding the `runlevel N` statement in `/etc/finit.conf`
- Improved logging on failure to `execvp()` a run/task/service, now
with `errno`, e.g., "No such file or directory" when the command
is missing from `$PATH`
- Add Bash completion support for `initctl`, configurable, issue #360
- Handle absolute path to `initctl [enable|disable]`, not supported
- Update `finit.conf(5)` man page with the recommended directory
hierarchy in `/etc/finit.d/`
- The `runparts` code has been split into `/libexec/finit/runparts`
- The `runparts` command now takes two options: `sysv` and `progress`.
The former ensures only `SNNfoo` and `KNNfoo` scripts are run.
- Add SysV Init Compatibility section to documentation
- Increased MAX path for commands, and arguments: 64 -> 256
- The bundled watchdog daemon no longer tries to log at startup, because
syslog is not available yet, any startup message leak to console
- Extend `if:` option with runtime evaluation of conditions. E.g., start
a task only `if:<run/foo/failure>` (here the run task 'foo' failed)
- Document new `if:`, `conflict:`, `nowarn` options for run/task/service
first introduced in v4.4
- Failure to open fstab should log to console, not just log to `/dev/kmsg`
- Rename `/lib/finit/system/*.conf`, added numbered prefix to ensure
proper execution order, e.g., `udevd` should always run first
- Plugins and bundled services: dbus, keventd, watchdogd, and runparts,
are now loaded *after* all services in `/lib/finit/system/`. A new
runtime-only path (for inspection) in `/run/finit/system/` is used
- Redirect `log*` output to console when `finit.debug` is enabled
- Assert `<int/container>` condition if we detect running in container
- Add support for mdev's netlink daemon mode, issue #367
- Add support for mdevd in `10-hotplug.conf`, preferred over plain mdev
- Disable modprobe plugin by default, udevd and mdev/mdevd loads modules
- Update documentation for run/task shell limitations, issue #376
- Update documentation regarding automount of `/run` and `/tmp`
- Update plugin documentation, add section about limited tmpfiles.d(5) support
- Skip registering service when `if:!name` matches a known service.
This allows conditional loading of alternative services, e.g. if udevd
is already loaded we do not need mdevd
- Drop `doc/bootstrap.md`, inaccurate and confusing to users
### Fixes
- Fix #227: believed to have been fixed in v4.3, the root cause was
actually that Finit was waiting for a process that was no longer in
the system. The fix is to ask the kernel on process-stop-timeout and
replay the lost PID so that Finit can continue with reboot/shutdown
- Fix #358: fix inotify events for `/etc/finit.conf`, improved log
messages and error handling
- Fix #361: cgroup move fail if run/task/services start as non-root.
Regression in the v4.4 release cycle while adding support for the
pre:/post:/ready: scripts. Now the latter scripts also properly run
in their correct cgroup
- Fix #366: document `fsck.*` kernel command line options and simplify
the configure flags `--enable-fsckfix` and `--enable-fastboot` to
only adjust the default values for the `fsck.*` options.
- Fix #371: swap load order of `/lib/finit/system/*` vs `/etc/finit.d/*`
We must run `10-hotplug.conf` first to ensure devices and modules are
up and loaded before the user's run/task/services are called. The order
at bootstrap is now saved in `/run/finit/conf.order` for inspection,
`/run/finit/exec.order` shows the start order of each process
- Fix #372: lost `udevadm` calls due to overloading
- Adjust final `udevadm settle` timeout: 5 -> 30 sec
- Fixed dbus plugin, the function that located `<pidfile> ...` in the
`dbus/system.conf` caused spurious line breaks which led to the
service not being loaded properly
- The `runparts` executor now skips backup files (`foo~`)
- The `runparts` stanza now properly appends ` start` to scripts that
start with `S[0-9]+`. This has been broken for a very long time.
- Fix #377: expand service `env:file` variables, allow constructs like:
RUNDIR=/var/run/somesvc
DAEMON_ARGS=--workdir $RUNDIR --other-args...
- Fix #378: warn on console if run/task times out during bootstrap
- Fix #378: add run/task support for `<!>` to allow transition from
bootstrap to multi-user runlevel even though task has not run yet.
- Fix #382: do not clear `<service/foo/STATE>` conditions on reload.
Introduced back in v4.3-rc2, 82cc10be8, the support for automatic
service conditions have had a weird and unintended behavior. Any
change in state (see `doc/svc-machine.png`) caused Finit to clear
out *all* previously acquired service conditions.
However, when moving between RUNNING and PAUSED states, a service
should not have its conditions cleared. The PAUSED state, seen
also by all conditions moving to FLUX, is only temporary while an
`initctl reload` is processed. If a service has no changes to be
applied it will move back to RUNNING.
Also, we cannot clear the service conditions because other run/task
or services may depend on it and clearing them would cause Finit to
`SIGTERM` these processes (since they are no longer eligible to run).
[4.4][] - 2023-05-15
--------------------
> **Note 1:** this release contains changes to the `.conf` parser. If you
> have .conf file statements with comment character (`#`) in the command
> options or description, you must now escape them (`\#`). Issue #186
>
> **Note 2:** prior to this release, runlevel `S` and `0` were after
> boot treated as the same runlevel. This caused `task [06] ...` to
> also run at bootstrap instead of just at shutdown and reboot. The
> changes made to Finit to separate `S` from `0` require you to update
> the allowed runlevels for services that are allowed to continue
> running at shutdown. I.e., change `[S123456789]` to `[S0123456789]`
> for, e.g., `sysklogd`. Issue #352
### Changes
* Add limited tmpfiles.d(5) support
This change adds very basic `tmpfiles.d/` support to Finit. Most of
the basic types are supported, but not all, so for now, please check
the code and examples for details on what is working.
* If a run/task/service command does not exist, skip registering it
This changes the semantics of Finit a bit by checking for the command
to run when registering it, skipping commands that cannot be found in
the absolute path provided in the command, or in `$PATH`
This change includes a new `nowarn` flag that can be used to prevent
Finit from warning for missing commands. See below for an example.
* Add run/task/service support for `conflict:foo` handling
* Add run/task/service support for `if:[!]ident` and `if:<[!]cond>`
Conditional loading of stanza depending on ident is already loaded (or
not), or condition satisfied (or not). E.g., do not run `mdev` if we
found and registered `udevd`, or load service only if `<boot/testing>`
condition is set.
The optional leading `!` negates the comparison, if NOT foo then ...
* Add support for static services in `/lib/finit/system/*.conf`
Slowly migrating away from hard-coded services in plugins. This way
it's possible for the user to both inspect and override as needed.
* Migrate hotplug plugin to a conditional `/lib/finit/system/hotplug.conf`
This is the first example of the just minted advanced stanza syntax with
`if:`-statements, `conflict:` handling, and `nowarn` flags.
* Initial support for template services, `foo@.conf`, similar to systemd
$ initctl show avahi-autoipd@
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
To enable ZeroConf for, e.g., `eth0`, use
$ initctl enable avahi-autoipd@eth0.conf
The enabled symlink will be set up to `avahi-autoipd@.conf` and every
instance of `%i` will in the instantiated directive be replaced with
`eth0`. Inspect the result with:
$ initctl status avahi-autoipd:eth0
* Add `devmon`, a `<dev/foo>` condition provider, issue #185
* Support for line continuation character `\` in .conf files, issue #186
service name:sysklogd [S123456789] \
env:-/etc/default/sysklogd \
syslogd -F $SYSLOGD_ARGS \
-- System log daemon
* `HOOK_BASEFS_UP` has been moved! External plugins that need to call
`service_register()`, please use `HOOK_SVC_PLUGIN` from now on.
Apologies for any inconveniences this might cause!
* getty: add support for `/etc/os-release` to replace `uname` output
This change, which has a fallback to `/usr/lib/os-release`, overrides
traditional modifiers with the os-release variant. These values were
taken from `uname`, which on Linux systems are pretty useless since
they always return the kernel name and version instead of the
distro/OS values.
E.g., \s becomes `PRETTY_NAME` instead of 'Linux' and \v becomes the
pretty `VERSION`, while \r becomes `VERSION_ID`.
* Support for overriding `/etc/finit.conf` and `/etc/finit.d` issue #235
New (kernel) command line option `finit.config=PATH` which can be used
to redirect Finit to start up with, e.g., `/etc/factory.conf` instead of
`/etc/finit.conf`.
For the complete experience a new top-level configuration file directive
`rcsd PATH` has aslo been added. It in turn can be used by `factory.conf`
as follows to override `/etc/finit.d`:
rcsd /etc/factory.d
* Support for overriding `/etc/finit.d` from the alternate `finit.conf`
with a new `rcsd /path/to/dot.d/` .conf file directive
* Support for `fsck_mode=[auto,skip,force]` + `fsck_repair=[preen,no,yes]`
* Add `set` keyword for environment variables set in `/etc/finit.conf`
* Support `finit.cond=foo` cmdline `<boot/foo>` conditions, issue #250
* `initctl` JSON output support for status and conditions, issue #273
Example:
root@infix:~$ initctl status -j resolvconf
{
"identity": "resolvconf",
"description": "Update DNS configuration",
"type": "task",
"forking": false,
"status": "done",
"exit": { "code": 0 },
"origin": "/etc/finit.d/enabled/sysrepo.conf",
"command": "resolvconf -u",
"restarts": 0,
"pidfile": "none",
"pid": 0,
"user": "root",
"group": "root",
"uptime": 0,
"runlevels": [ 1, 2, 3, 4, 5, 7, 8, 9 ]
}
The excellent tool `jq` can be used to extract parts of the output for
further scripting. E.g. `initctl status -j foo | jq .exit.status`
* Add JSON support to `initctl ls` command
This allows for easy access to the *disabled* services:
root@anarchy:~# initctl ls --json |jq '.available - .enabled'
[
"chronyd.conf",
"dnsmasq.conf",
"gdbserver.conf",
"inadyn.conf",
"inetd.conf",
"isisd.conf",
"lldpd.conf",
"mstpd.conf",
"ntpd.conf",
"ospf6d.conf",
"ospfd.conf",
"querierd.conf",
"ripd.conf",
"ripng.conf",
"sshd.conf",
"syslogd.conf",
"telnetd.conf",
"uftpd.conf",
"wpa_supplicant.conf",
"zebra.conf"
]
* Allow `manual:yes` on sysv/service/run/task stanzas, issue #274
* Add support for `oncrash:script` to call the `post:script` action, if
defined, for a crashing service. The `EXIT_CODE` variable sent to the
script is set to `crashed`. Issue #282
* Search for plugins in `/usr` and `/usr/local` as well, issue #284
* tty: add support for `passenv` flag to `/bin/login`, issue #286
* Add reboot/shutdown/poweroff timeout `-t SEC` to initctl, issue #295
* Add support for s6 and systemd readiness notification, issue #299.
Service readiness notification to support daemons employing systemd
and s6 notification. Complementing the native Finit readiness support
using PID files that exist already.
The two have slightly different ways of implementing readiness:
- https://www.freedesktop.org/software/systemd/man/sd_notify.html
- https://skarnet.org/software/s6/notifywhenup.html
Finit now provides both a `NOTIFY_SOCKET` environment variable, for
systemd, and a way to start s6 daemons with a descriptor argument.
For details on the syntax, see the `service` documentation.
This change also renames internal states for run/task/services to
avoid any confusion with the introduction of `ready:scripts`:
- `WAITING -> PAUSED`
- `READY -> WAITING`
A service condition that used, e.g., `<service/foo/ready>` should now
instead use `<service/foo/waiting>`
* Add `ready:script` for services, called when daemon is ready, issue #300
* Add support for running scripts at shutdown at two new hook points
during the shutdown process, issue #302. See doc/plugins.md for details:
- `HOOK_SVC_DN`: after all services and non-reserved processes have been
killed (and collected)
- `HOOK_SYS_DN`: after all file systems have been unmounted, *just prior*
to Finit calling `reboot()` to shut down or reboot the system
* The `modules-load` plugin now default to runlevel `[S]`, in previous
releases it was `[2345]`. This breaking change is to align it more
with what users mostly want (modules loaded before services start) and
can be changed back to the old behavior with a per-file setting:
set runlevel 2345
* The `modules-load` plugin now adds silent tasks for modprobe. This to
prevent confusing `[ OK ]` boot messages when in fact modprobe failed.
* The `modules-load` plugin now support `set modprobe /path/to/modprobe`
* The header files `finit/conf.h` and `finit/service.h` are now exported
for external plugins
* Add support for multiple args to `initctl cond set/clr`, issue #329
* Silence confusing `[ OK ]` progress from modules-load plugin, issue #332
This change drops the confusing status progress output, which was always
OK since the actual modprobe operation runs in the background. No need
to show status of the "added a task to finit, found modprobe" command.
* dbus plugin: adapt to other operating systems
Not all Linux systems are based on Debian, and even if they are inspired
by Debian (Buildroot), they do not necessarily use the same defaults.
Probes the system at runtime for:
- dbus user and group
- dbus PID file
If the user/group cannot be found we fall back to `root`, if the PID
file cannot be determined we ignore PID file readiness.
* Improve documentation for runparts and hook scripts. Issue #315, #320
* Add `HOOK_NETWORK_DN`, called after change to runlevel 6 or 0, issue #319
* Use sysklogd `logger` tool instead of legacy `logit` tool, issue #344
For log redirection Finit has the legacy `logit` tool. This change
allows Finit to use the `sysklogd` project's extended `logger` tool
instead, when available. Allowing logging with the process' PID.
* Add `initctl` aliases: `cat -> show`, `kill -> signal`
* Add `initctl -n,--noerr` to return OK(0) if services do not exist, for
integration with openresolv and scripts with similar requirements
* Add `initctl plugins`, list installed plugins
* Add timestamp to log messages in fallback and logging to `stderr`.
When there is no log daemon, and we are running in a container, or we
cannot log to the kernel ring buffer, then we log to `stderr`. This
change improves log output by prefixing each message with a timestamp.
### Fixes
* Fix #254: document limitations in `rc.local` and `runparts` scripts
* Fix #269: add compulsory kernel symlinks in `/dev`
* Fix #275: `initctl status foo` should list all instances, regression
introduced in v4.3
* Fix #278: enforce conditions also for running `pre:` scripts
* Fix #279: allow `restart:always`, of crashing services. Similar to
`respawn` but honors `restart_sec`
* Fix #280: allow calling `initctl restart foo` from within foo
* Fix #283: too quick timeout at bootstrap of lingering tasks
* Fix #285: `initctl restart` should start crashed service
* Fix #288: enable built-in `sulogin` in Alpine and Void Linux builds
* Fix #293: modprobe plugin: support for coldplugging more devices. It
turns out, not all buses in Linux add `modalias` attributes to their
devices in sysfs. One notable exception are MDIO buses. The plugin's
scan routine would thus not pick them up.
* Fix #294: `shutdown --help` mistakenly shuts down system
* Fix #310: Always use configured restart delay for crashing services.
If no delay is configured, default to an initial 2000 msec for forking
daemons and start-stop scripts, and 1 msec for non-forking daemons
* Fix #311: document how to combine device tree with conditions
* Fix #312: restart services with respawn set, e.g. ttys, immediately
* Fix #313: Cancel pending restart timer on stop/start/restart/reload
* Fix #314: skip run/task/service restart if conditions are lost
* Fix #315: add environment variables to hook scripts
All hook scripts are called with at least one environment variable
set, `FINIT_HOOK_NAME`, useful when reusing the same hook script for
multiple hook points. It is set to the string name, also used by the
path, e.g., `hook/net/up`
For all hook points from hook/sys/shutdown and later, `FINIT_SHUTDOWN`
is also set, to one of: `halt`, `poweroff`, `reboot`
* Fix #317: make sure hook scripts don't run twice, also fixes #316
* Fix #318: only show `[ OK ] Calling foo` progress for `runparts ...`
* Fix #320: the API/IPC socket is closed immediately at shutdown/reboot
to protect hook scripts or services calling initctl. There is no way
to service these requests safely at that time
* Fix #333: consider a service dirty if command line args have changed
This fixes `initctl reload` correctly restarting all daemons that have
new command line arguments.
Previously command line arguments changes were only acted upon if the
service was explicitly reloaded `initctl reload myservice`.
Found and fixed by Jack Newman
* Fix #338: ensure shutdown hooks are called properly; `hook/sys/down`
and `hook/svc/down` hook scripts, found and fixed by Jack Newman
* Fix #339: use absolute path in `/etc/finit.d/enabled/` symlinks, for
use-cases when `/etc` is read-only and `/etc/finit.d/enabled ->
/mnt/finit.d/enabled`, reported by Jack Newman
* Fix #340: Finit ignores deleted/moved `.conf` file sin `/etc/finit.d`
* Fix #342: in runlevel S (bootstrap), not all `initctl` commands can be
supported, block the following: runlevel, reload, start/stop, restart,
reload, halt, poweroff, suspend. Also, prevent `SIGHUP` and `SIGUSR1`
signals when in shutdown or reboot
* Fix #352: separate runlevel S from runlevel 0
* Fix #355: regression in v3.2 stopping a process and its group
In Finit v3.2 a regression was introduced that affects the way Finit
stops a supervised process and its process group.
Instead of sending SIGTERM to the process, and thus delegating the
responsibility to that process to inform any children it may have, as
of commit 91a9c83 Finit sends SIGTERM to the entire process group.
For SIGKILL this is fine, SIGKILL only runs as cleanup and as a last
ditch effort if the process doesn't respond to SIGTERM.
This regression, introduced in v3.2, directly affects services like
`avahi-autoipd` that have forked off children that it needs to tell to
exit cleanly before it returns. With the patch in question these
children are never allowed to complete, which in turn causes lingering
169.254 link-local addresses on interfaces.
* Fix bootmisc plugin: octal permission on `/run/lock` and `/var/lock`
* Ensure `initctl cond get` support the flux state (exit code 255)
* Fix potential socket leak at bootstrap and shutdown
* Fix potential NULL pointer deref in kernel command line parser
* Fix lingering condition in service after reload of service with new
config that has no condition
* Fix wrong path to command in service after reload, may have changed
* `logit`: fall back to package name if `$LOGNAME` and `$USER` are
unset. **Note:** you should probably not use `logit` in your own
scripts, it is only meant for internal use by Finit. We recommend
using `logger` from the `bsdutils` or `sysklogd` packages instead
* Fix issue where `env:`/`pre:`/`post:`/etc. is removed from a service
The trick is when reloading a service like this:
service env:/etc/env serv -np -e foo:bar
into this:
service pre:/bin/pre.sh serv -np
In the second the `env:` has been removed and `pre:` added. Prior to
this patch, `env:` was kept leading to unintended behavior.
* Fix parse/diff of command line args, e.g., `nginx -g 'daemon off;'`
Starting a service like this works fine:
service [2345789] env:-/etc/default/nginx nginx -g 'daemon off;'
However, on `initctl reload` (and no change to .conf files) the arg
list was lost while parsing the .conf files. Leading to a false
positive 'diff' in args causing nginx to be unnecessarily restarted.
* Fix issue with disabled "linewrap" on the console TTY after login.
The root cause is `qemu-system-x86_64 -nographics` disabling it when
starting up. The correct `\e[?7h` escape code is now used.
[4.3][] - 2022-05-15
-------------------- --------------------
Critical bug fix release. If you run a 32-bit target with GLIBC 2.34 Critical bug fix release. If you run a 32-bit target with GLIBC 2.34
you *need* to upgrade! you *need* to upgrade!
> **Note:** system verbosity on console at start and shutdown has been
> increased. Now the output of all commands is logged to the
> system logger, for early services `/dev/kmsg` is used.
>
> **Also:** please notice the updated support for enabling and disabling
> kernel and Finit debug messages on the system console. Very
> useful when debugging either of them, e.g., a kernel module.
> For details, see [cmdline.md](doc/cmdline.md).
### Changes ### Changes
* Support for overriding default runlevel from kernel command line. Any
runlevel `[1-9]` may be selected, except 6 (reboot). Issue #261
* New command line option: `finit.fstab=/etc/fstab.custom`, with full
support for mounting, mount helpers, fsck, and swapon/off, issue #224
* Support for special device `/dev/root`, which may not exist in `/dev`.
Finit now looks up the matching block device for `/` in `/sys/block/`
* Loading `module`s no longer shows arguments in progress output * Loading `module`s no longer shows arguments in progress output
* Warning messages in progress output now in yellow, not red, issue #214 * Warning messages in progress output now in yellow, not red, issue 214
* `initctl`, new command line option `-V,--version` for ease of use * `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 * New condition `done` for run task, issue #207 by Ming Liu, Atlas Copco
* Refactor parts of shutdown and reboot sequence for PREEMPT-RT kernels, * Refactor parts of shutdown and reboot sequence for PREEMPT-RT kernels,
by Robert Andersson, Mathias Thore, and Ming Liu, Atlas Copco by Robert Andersson, Mathias Thore, and Ming Liu, Atlas Copco
* Conditions for run/task/sysv status, e.g. `run/foo/success` and
`task/bar/failure`. Issue #232, by Ming Liu, Atlas Copco
* Conditions for services, can be used to synchronize other stanzas:
- `service/foo/running`
- `service/foo/halted`
- `service/foo/missing`
- `service/foo/crashed`
- `service/foo/stopped`
- `service/foo/busy`
- `service/foo/restart`
* `initctl signal` support, by Jörgen Sigvardsson, issue #225
* `initctl cond get` support to match `cond [set | clear]`, issue #255
* `[WARN]` messages on console now printed in yellow, issue #214
* Network services now also stopped when going to runlevel 6 (reboot),
not just runlevel 0 (shutdown) or 1 (single-user)
* When `ifup` is missing on the system, bring at least `lo` up at boot
* Log output from `ifup -a` (and `ifdown -a`), to syslog
* Avoid blocking PID 1 when starting SysV init scripts
* Allow custom `pid:` for SysV init scripts
* Document supported types of forking/non-forking services
* Auto-detect running in some common forms of containers
* Simplify shutdown/reboot when running in a container
* Log to `stderr` when running in a container w/o syslog daemon
* Add support for `type:forking` to services, already supported but
with a very difficult `pid:` syntax, issue #223. Docs updated
* Support for setting global environment variables in `finit.conf`,
please note: this also affects Finit itself, be careful!
* Extended environment variables for pre/post scripts, issue #189
* Document secret service option `respawn`, which bypasses the crash
semantics, allowing endless restarts
* Document secret `HOOK_BANNER`, the first hook point before the banner
* Document slightly confusing `initctl reload foo` command. It does
*not* reload the service's `.conf` file! Issue #263
* Log changes; all instances where previously the `basename cmd` of a
service was used to identify the service, now the proper `name:id` is
used instead. Meaning, a service without a custom `:ID` or `name:`
will display the same as before, but with any of those customizations
the name and name:id will now be shown. **Note:** this may affect any
log scrapers out there!
* New plugin: `hook-scripts`, allows [run-parts(8)][] style scripts to
run on any hook point. Contributed by Tobias Waldekranz
* `initctl` (`reboot`) falls back to `-f` when it detects it is in
`sulogin` recovery mode, issue #247
* The bundled `sulogin` is no longer enabled by default, in favor of
distribution versions. Enable with `./configure --with-sulogin`
* Support args to sysv-like scripts, e.g. `bridge-stp br0 start`
* The `modules-load` plugin now skips all lines starting with `#` and
`;`. Furthermore, files in `/etc/modules-load.d/*.conf` are now read
in lexicographic order and UNIX backup files (`foo.conf~`) are skipped
* The `name:id` tuple is now more consistently used in all log and debug
messages instead of the basename of the command
* Simplify error output of `initctl start/stop/restart/signal`, no more
extra usage help, just a plain error message
* Exit codes of `initctl` have changed to use LSB script standard and BSD
sysexits.h exit codes. As before, a non-zero exit is error or missing
* Add support for `initctl -q` to more commands: stop, start, restart,
reload, signal, etc.
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
### Fixes ### Fixes
* Fix nasty 32/64-bit alignment issue between finit and its plugins, * Fix nasty 32/64-bit alignment issue between finit and its plugins,
@@ -688,22 +27,6 @@ you *need* to upgrade!
or does not have the required controllers (cpu) or does not have the required controllers (cpu)
* Fix #217: iwatcher initialization issue, by Ming Liu, Atlas Copco * Fix #217: iwatcher initialization issue, by Ming Liu, Atlas Copco
* Fix #218: initctl matches too many services, by Ming Liu, Atlas Copco * Fix #218: initctl matches too many services, by Ming Liu, Atlas Copco
* Fix #219: not all filesystems unmounted at shutdown, by Ming Liu,
Mathias Thore, and Robert Andersson, Atlas Copco
* Fix #226: initctl shows wrong PID for crashing services
* Fix #227: reboot stalls if process stopped with `[WARN]`
* Fix #233: initctl shows wrong status for run/task, by Sergio Morlans
and Ming Liu, Atlas Copco
* Fix #248: source `env:file` also in `pre:` and `post` scripts
* Fix #260: drop limit on device name in `Checking filesystem...` output
* Fix start/stop and monitoring (restart) of SysV init scripts and
forking services, see the updated documentation for details
* Fix call to `swapoff` at shutdown, does not support `-e` flag
* Fix suspend to RAM issue. Previously `reboot(RB_SW_SUSPEND)` was
used, now the modern `/sys/power/state` API is used instead.
* Fix nasty run/task/service matcher bug, triggered by stanzas using the
same basename of a command but different `:ID`. Caused Finit to match
with already registered but different run/task/service
[4.2][] - 2022-01-16 [4.2][] - 2022-01-16
@@ -1458,7 +781,7 @@ The inetd release.
### Changes ### Changes
* Add support for built-in inetd super server -- launch services on * Add support for built-in inetd super server -- launch services on
demand. Supports filtering per interface and custom Inet ports. demand. Supports filtering per interface and custom Inet ports.
* Upgrade to [libuEv][] v1.1.0 to better handle error conditions. * Upgrade to [libuEv] v1.1.0 to better handle error conditions.
* Allow mixed case config directives in `finit.conf` * Allow mixed case config directives in `finit.conf`
* Add support for RFC 868 (rdate) time plugin, start as inetd service. * Add support for RFC 868 (rdate) time plugin, start as inetd service.
* Load plugins before parsing `finit.conf`, this makes it possible to * Load plugins before parsing `finit.conf`, this makes it possible to
@@ -1489,7 +812,7 @@ The inetd release.
[1.11][] - 2015-01-24 [YANKED] [1.11][] - 2015-01-24 [YANKED]
------------------------------ ------------------------------
The [libuEv][] release. The [libuEv] release.
**Note:** This release has been *yanked* from distribution due to a **Note:** This release has been *yanked* from distribution due to a
regression in launching background processes and TTY's. Fixed in regression in launching background processes and TTY's. Fixed in
@@ -1740,13 +1063,7 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.8...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD
[4.9]: https://github.com/troglobit/finit/compare/4.8...4.9
[4.8]: https://github.com/troglobit/finit/compare/4.7...4.8
[4.7]: https://github.com/troglobit/finit/compare/4.6...4.7
[4.6]: https://github.com/troglobit/finit/compare/4.5...4.6
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3 [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.2]: https://github.com/troglobit/finit/compare/4.1...4.2
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1 [4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
+1 -1
View File
@@ -1,5 +1,5 @@
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+1 -10
View File
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man plugins src system test tmpfiles.d SUBDIRS = man 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
@@ -10,10 +10,6 @@ if DOC
SUBDIRS += doc SUBDIRS += doc
endif endif
if BASH
bashdir = $(BASH_DIR)
dist_bash_DATA = initctl
endif
# Compat hook # Compat hook
install-dev: install-dev:
@make -C src install-pkgincludeHEADERS @make -C src install-pkgincludeHEADERS
@@ -33,8 +29,3 @@ 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
DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
--enable-testserv-plugin --enable-x11-common-plugin \
--with-watchdog --with-keventd --with-fstab=/etc/fstab \
--with-bash-completion-dir=no
+36 -151
View File
@@ -3,6 +3,7 @@
* [Introduction](#introduction) * [Introduction](#introduction)
* [Features](#features) * [Features](#features)
* [Bootstrap](doc/bootstrap.md#bootstrap)
* [Runlevels](#runlevels) * [Runlevels](#runlevels)
* [Syntax](doc/config.md#syntax) * [Syntax](doc/config.md#syntax)
* [Runparts & /etc/rc.local](#runparts--etcrclocal) * [Runparts & /etc/rc.local](#runparts--etcrclocal)
@@ -22,10 +23,11 @@ Introduction
------------ ------------
Fast init for Linux systems. Reverse engineered from the [EeePC Fast init for Linux systems. Reverse engineered from the [EeePC
fastinit][] by [Claudio Matsuoka][] — "gaps filled with frog DNA …" fastinit][] over ten years ago by [Claudio Matsuoka][] — "gaps filled
with frog DNA …"
![Alpine screenshot](img/alpine-screenshot2.png) ![Alpine screenshot](img/finit4-screenshot.png)
*Figure 1: Screenshot of Finit booting [Alpine Linux (HowTo)](https://github.com/troglobit/finit/tree/master/contrib/alpine).* *Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
Features include: Features include:
@@ -35,11 +37,7 @@ Features include:
* Process supervision similar to [systemd][] * Process supervision similar to [systemd][]
* Sourcing environment files * Sourcing environment files
* Conditions for network/process/custom dependencies * Conditions for network/process/custom dependencies
* Readiness notification; PID files (native) for synchronizing system
startup, support for systemd [sd_notify()][], or [s6 style][] too
* Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes)
* Pre/Post script actions * Pre/Post script actions
* Rudimentary [templating support](doc/config.md#templating)
* Tooling to enable/disable services * Tooling to enable/disable services
* Built-in getty * Built-in getty
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html) * Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
@@ -47,26 +45,14 @@ Features include:
* Cgroups v2, both configuration and monitoring in `initctl top` * Cgroups v2, both configuration and monitoring in `initctl top`
* Plugin support for customization * Plugin support for customization
* Proper rescue mode with bundled `sulogin` for protected maintenance shell * Proper rescue mode with bundled `sulogin` for protected maintenance shell
* Integration with [watchdogd][] for full system supervision
* Logging to kernel ring buffer before `syslogd` has started, see the
recommended [sysklogd][] project for complete logging integration
and how to log to the kernel ring buffer from scripts using `logger`
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
[contrib/](contrib/) section with tutorials for the following Linux [contrib/](contrib/) section with tutorials for:
distributions:
* [Void Linux](contrib/void/), * [Void Linux](contrib/void/),
* [Alpine Linux](contrib/alpine/), and * [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/), also works on Ubuntu/Linux Mint * [Debian GNU/Linux](contrib/debian/)
> **Note:** support for various Linux distributions does not mean Finit
> installs easily on all architectures. The bundled install scripts are
> examples for standard installations, tested on amd64 (x86_64) systems.
> Custom setups, e.g., for embedded systems, can be found in any of the
> following [Buildroot][] based examples: [myLinux][], [Infix][], or the
> plain [br2-finit-demo](https://github.com/troglobit/br2-finit-demo).
Example Example
@@ -75,25 +61,14 @@ Example
This example `/etc/finit.conf` can also be split up in multiple `.conf` This example `/etc/finit.conf` can also be split up in multiple `.conf`
files in `/etc/finit.d`. Available, but not yet enabled, services can files in `/etc/finit.d`. Available, but not yet enabled, services can
be placed in `/etc/finit.d/available` and enabled by an operator using be placed in `/etc/finit.d/available` and enabled by an operator using
the [initctl](#commands--status) tool. See the above mentioned Linux the [initctl](#commands--status) tool.
distributions, or [myLinux][].
> **Note:** as of Finit v4.4, .conf lines can be broken up using the
> standard UNIX continuation character (`\`), also trailing comments are
> now supported. The latter means you need to escape any hashes used in
> directives and descriptions (`\#`). For more on this and examples,
> see the [finit.conf(5)][] manual or [doc/config.md](doc/config.md).
```ApacheConf ```ApacheConf
# Fallback if /etc/hostname is missing # Fallback if /etc/hostname is missing
host default host default
# Runlevel to start after bootstrap, 'S', default: 2 # Runlevel to start after bootstrap, 'S', default: 2
#runlevel 2 runlevel 2
# Support for setting global environment variables, using foo=bar syntax
# be careful though with variables like PATH, SHELL, LOGNAME, etc.
#PATH=/usr/bin:/bin:/usr/sbin:/sbin
# 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
@@ -122,11 +97,6 @@ service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon #sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
#task [S] /etc/init.d/kbd -- Preparing console #task [S] /etc/init.d/kbd -- Preparing console
# Hidden from boot progress, using empty `--` description
#sysv [S] /etc/init.d/keyboard-setup --
#sysv [S] /etc/init.d/acpid --
#task [S] /etc/init.d/kbd --
# Run start scripts from this directory # Run start scripts from this directory
# runparts /etc/start.d # runparts /etc/start.d
@@ -245,50 +215,13 @@ networking here. The configured `runlevel NUM` from `/etc/finit.conf`
is what Finit changes to after bootstrap, unless 'single' (or 'S') is is what Finit changes to after bootstrap, unless 'single' (or 'S') is
given on the kernel cmdline, in which case runlevel 1 is started. given on the kernel cmdline, in which case runlevel 1 is started.
All services in runlevel S) are started first, followed by the desired All services in runlevel S(0) are started first, followed by the desired
run-time runlevel. Run tasks in runlevel S can be started in sequence run-time runlevel. Run tasks in runlevel S can be started in sequence
by using `run [S] cmd`. Changing runlevels at runtime is done like any by using `run [S] cmd`. Changing runlevels at runtime is done like any
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
`intictl` tool. `intictl` tool.
**Conditions**
As mentioned previously, Finit has an advanced dependency system to
handle synchronization, called [conditions](doc/conditions.md). It can
be used in many ways; depend on another service, network availability,
etc.
One *really cool* example useful for embedded systems is to run certain
scripts if a board has a certain feature encoded in its device tree. At
bootstrap we run the following `ident` script:
```sh
#!/bin/sh
conddir=/var/run/finit/cond/hw/model
dtmodel=/sys/firmware/devicetree/base/model
if ! test -e $dtmodel; then
exit 0
fi
model=$(cat $dtmodel | tr "[A-Z] " "[a-z]-")
mkdir -p $conddir && ln -s ../../reconf $conddir/$model
```
Provided the device tree node exists, and is a string, we can then use
the condition `<hw/model/foo>` when starting other scripts. Here is an
example:
```
run [S] /path/to/ident --
task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board
```
> Notice the trick with an empty description to hide the call to `ident`
> in the Finit progress output.
**Plugins** **Plugins**
Plugins can *extend* the functionality of Finit and *hook into* the Plugins can *extend* the functionality of Finit and *hook into* the
@@ -330,8 +263,8 @@ unnecessary overhead, which can be removed at build-time using:
**Cgroups** **Cgroups**
Finit supports cgroups v2 and comes with the following default groups in Finit supports cgroups v2 and comes with the following default groups
which services and user sessions are placed in: in which services and user sessions are placed in:
/sys/fs/cgroup /sys/fs/cgroup
|-- init/ # cpu.weight:100 |-- init/ # cpu.weight:100
@@ -367,10 +300,6 @@ The `initctl` tool has three commands to help debug and optimize the
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup` setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
commands for details. commands for details.
> **Note:** systems that do not support cgroups, specifically version 2,
> are automatically detected. On such systems the above functionality
> is disabled early at boot.
Runparts & /etc/rc.local Runparts & /etc/rc.local
------------------------ ------------------------
@@ -391,33 +320,8 @@ Right after the runlevel change when all services have started properly,
No configuration stanza in `/etc/finit.conf` is required for `rc.local`. No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
If it exists and is an executable shell script Finit calls it at the very If it exists and is an executable shell script Finit calls it at the very
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
in [doc/plugins.md](doc/plugins.md#hooks). in [doc/plugins.md](doc/plugins.md#hooks), and about the system bootstrap
in [doc/bootstrap.md](doc/bootstrap.md).
### Limitations
It is not possible to call Finit via signals or use `initctl` in any
runparts or `/etc/rc.local` script. This because Finit is single
threaded and is calling these scripts in a blocking fashion at the end
of runlevel S, at which point the event loop has not yet been started.
The event loop is the whole thing which Finit is built around, except
for runlevel S, which remains a slow procession through a lot of set up,
with a few hooks and blocking call outs to external scripts.
However, not all `initctl` commands are prohibited. Supported commands:
- `inictl cond`: only operate of files in `/run/finit/cond`
- `initctl enable/disable`: enabled run/task/service is activated on
the runlevel change from S to 2
- `initctl touch/show/create/delete/list`: `create`, provided the
non-interactive mode is used, again changes take effect in the
runlevel change directly after bootstrap
- `initctl -f reboot/poweroff/halt`: provided the `-f` flag is used to
force direct kernel commands
**Example:** you can set a `usr/` condition in `/etc/rc.local` and have
a service/task in runlevel 2 depend on it to execute.
Runlevels Runlevels
@@ -505,25 +409,24 @@ 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.
``` ```
alpine:~# initctl help
Usage: initctl [OPTIONS] [COMMAND] Usage: initctl [OPTIONS] [COMMAND]
Options: Options:
-b, --batch Batch mode, no screen size probing -b, --batch Batch mode, no screen size probing
-c, --create Create missing paths (and files) as needed -c, --create Create missing paths (and files) as needed
-f, --force Ignore missing files and arguments, never prompt -f, --force Ignore missing files and arguments, never prompt
-h, --help This help text
-j, --json JSON output in 'status' and 'cond' commands
-1, --once Only one lap in commands like 'top' -1, --once Only one lap in commands like 'top'
-p, --plain Use plain table headings, no ctrl chars -p, --plain Use plain table headings, no ctrl chars
-q, --quiet Silent, only return status of command -q, --quiet Silent, only return status of command
-t, --no-heading Skip table headings -t, --no-heading Skip table headings
-v, --verbose Verbose output -v, --verbose Verbose output
-V, --version Show program version -h, --help This help text
Commands: Commands:
debug Toggle Finit (daemon) debug debug Toggle Finit (daemon) debug
help This help text help This help text
version Show program version version Show Finit version
ls | list List all .conf in /etc/finit.d ls | list List all .conf in /etc/finit.d
create <CONF> Create .conf in /etc/finit.d/available create <CONF> Create .conf in /etc/finit.d/available
@@ -535,21 +438,16 @@ Commands:
disable <CONF> Disable .conf in /etc/finit.d/enabled disable <CONF> Disable .conf in /etc/finit.d/enabled
reload Reload *.conf in /etc/finit.d (activate changes) reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined conditions +usr/COND cond set <COND> Set (assert) user-defined condition +usr/COND
cond get <COND> Get status of user-defined condition, see $? and -v cond clear <COND> Clear (deassert) user-defined condition -usr/COND
cond clear <COND> Clear (deassert) user-defined conditions -usr/COND
cond status Show condition status, default cond command cond status Show condition status, default cond command
cond dump [TYPE] Dump all, or a type of, 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 <NAME>[:ID] Start service by name, with optional ID
stop <NAME>[:ID] Stop/Pause a running service by name stop <NAME>[:ID] Stop/Pause a running service by name
reload <NAME>[:ID] Reload service as if .conf changed (SIGHUP or restart) reload <NAME>[:ID] Reload service by name (SIGHUP or restart)
This allows restart of run/tasks that have already run
Note: Finit .conf file(s) are *not* reloaded!
restart <NAME>[:ID] Restart (stop/start) service by name restart <NAME>[:ID] Restart (stop/start) service by name
signal <NAME>[:ID] <S> Send signal S to service by name, with optional ID
ident [NAME] Show matching identities for NAME, or all
status <NAME>[:ID] Show service status, by name status <NAME>[:ID] Show service status, by name
status Show status of services, default command status Show status of services, default command
@@ -557,15 +455,11 @@ Commands:
ps List processes based on cgroups ps List processes based on cgroups
top Show top-like listing based on cgroups top Show top-like listing based on cgroups
plugins List installed plugins
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 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
@@ -584,24 +478,24 @@ scripting and documentation:
``` ```
alpine:~# initctl -p alpine:~# initctl -p
PID IDENT STATUS RUNLEVELS DESCRIPTION PID IDENT STATUS RUNLEVELS DESCRIPTION
====================================================================== ======================================================================
1506 acpid running [---2345----] ACPI daemon 1506 acpid running [--2345----] ACPI daemon
1509 crond running [---2345----] Cron daemon 1509 crond running [--2345----] Cron daemon
1489 dropbear running [---2345----] Dropbear SSH daemon 1489 dropbear running [--2345----] Dropbear SSH daemon
1511 klogd running [S-12345----] Kernel log daemon 1511 klogd running [S12345----] Kernel log daemon
1512 ntpd running [---2345----] NTP daemon 1512 ntpd running [--2345----] NTP daemon
1473 syslogd running [S-12345----] Syslog daemon 1473 syslogd running [S12345----] Syslog daemon
alpine:~# initctl -pv alpine:~# initctl -pv
PID IDENT STATUS RUNLEVELS COMMAND PID IDENT STATUS RUNLEVELS COMMAND
====================================================================== ======================================================================
1506 acpid running [---2345----] acpid -f 1506 acpid running [--2345----] acpid -f
1509 crond running [---2345----] crond -f -S $CRON_OPTS 1509 crond running [--2345----] crond -f -S $CRON_OPTS
1489 dropbear running [---2345----] dropbear -R -F $DROPBEAR_OPTS 1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
1511 klogd running [S-12345----] klogd -n $KLOGD_OPTS 1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
1512 ntpd running [---2345----] ntpd -n $NTPD_OPTS 1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
1473 syslogd running [S-12345----] syslogd -n 1473 syslogd running [S12345----] syslogd -n
``` ```
The environment variables to each of the services above are read from, The environment variables to each of the services above are read from,
@@ -626,7 +520,7 @@ Condition(s):
User : root User : root
Group : root Group : root
Uptime : 2 hour 46 min 56 sec Uptime : 2 hour 46 min 56 sec
Runlevels : [---2345----] Runlevels : [--2345----]
Memory : 1.2M Memory : 1.2M
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max] CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|- 1485 dropbear -R -F |- 1485 dropbear -R -F
@@ -697,22 +591,13 @@ and proposed extensions.
[upstart]: https://upstart.ubuntu.com/ [upstart]: https://upstart.ubuntu.com/
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/ [systemd]: https://www.freedesktop.org/wiki/Software/systemd/
[openrc]: https://www.gentoo.org/proj/en/base/openrc/ [openrc]: https://www.gentoo.org/proj/en/base/openrc/
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
[s6 style]: https://skarnet.org/software/s6/notifywhenup.html
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[tmpfiles.d(5)]: https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
[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]: https://troglobit.com
[Buildroot]: https://buildroot.org
[Infix]: https://kernelkit.github.io
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
[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
[myLinux]: https://github.com/troglobit/myLinux/
[watchdogd]: https://github.com/troglobit/watchdogd/
[sysklogd]: https://github.com/troglobit/sysklogd/
[GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/ [GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg [GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
[Coverity Scan]: https://scan.coverity.com/projects/3545 [Coverity Scan]: https://scan.coverity.com/projects/3545
-7
View File
@@ -1,7 +0,0 @@
#!/bin/sh
# Run test suite with the correct configure params
make distclean
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --with-watchdog --with-keventd
make -j17 clean check
+25 -157
View File
@@ -1,11 +1,10 @@
AC_INIT([Finit], [4.9-rc1], [https://github.com/troglobit/finit/issues], AC_INIT(Finit, 4.3-dev, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
[finit], [https://troglobit.com/projects/finit/])
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])
AC_CONFIG_SRCDIR([src/finit.c]) AC_CONFIG_SRCDIR([src/finit.c])
AC_CONFIG_HEADERS([config.h]) 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
@@ -15,10 +14,8 @@ AC_CONFIG_FILES([Makefile
man/Makefile man/Makefile
plugins/Makefile plugins/Makefile
src/Makefile src/Makefile
system/Makefile system/10-hotplug.conf
test/test.env test/Makefile test/test.env test/Makefile
test/lib/Makefile test/src/Makefile test/tenv/Makefile test/common/Makefile])
tmpfiles.d/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])
@@ -35,8 +32,9 @@ AC_PROG_INSTALL
AC_PROG_MKDIR_P AC_PROG_MKDIR_P
# Configuration. # Configuration.
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h]) AC_HEADER_STDC
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r]) AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h])
AC_CHECK_FUNCS([strstr getopt getfsenty])
# Check for uint[8,16,32]_t # Check for uint[8,16,32]_t
AC_TYPE_UINT8_T AC_TYPE_UINT8_T
@@ -67,20 +65,12 @@ AC_ARG_ENABLE(fsckfix,
AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[ AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[
enable_fsckfix=no]) enable_fsckfix=no])
AC_ARG_ENABLE(cgroup,
AS_HELP_STRING([--disable-cgroup], [Disable cgroup v2 support, default: autodetect from /sys/fs/cgroup]),,[
enable_cgroup=yes])
AC_ARG_ENABLE(redirect, AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[ AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
enable_redirect=yes]) enable_redirect=yes])
AC_ARG_ENABLE(kernel_logging,
AS_HELP_STRING([--disable-kernel-logging], [Disable kernel logging to console (use 'quiet' instead!]),,[
enable_kernel_logging=yes])
AC_ARG_ENABLE(logrotate, AC_ARG_ENABLE(logrotate,
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp]),,[ AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
enable_logrotate=yes]) enable_logrotate=yes])
AC_ARG_ENABLE(doc, AC_ARG_ENABLE(doc,
@@ -91,37 +81,21 @@ AC_ARG_ENABLE(contrib,
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[ AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
enable_contrib=yes]) enable_contrib=yes])
AC_ARG_ENABLE([rescue],
AS_HELP_STRING([--disable-rescue], [Disable potentially unsafe rescue mode]),,
[enable_rescue=yes])
# Check for extra plugins to enable # Check for extra plugins to enable
AC_ARG_ENABLE(all_plugins, enable_all_plugins=auto
AS_HELP_STRING([--enable-all-plugins], [Enable all plugins, default: auto]),
enable_all_plugins=$enableval, 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], [no], [Coldplug modules using modalias magic]) 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([hook-scripts], [no], [Trigger script execution from hook points]) AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
AC_PLUGIN([hotplug], [yes], [Start udevd or mdev kernel event datamon])
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock]) 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([tty], [yes], [Automatically activate new TTYs, e.g. USB-to-serial])
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown]) AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
AC_PLUGIN([testserv], [no], [Test plugin to start test serv daemon])
# Check for extra arguments or packages # Check for extra arguments or packages
AC_ARG_WITH([bash-completion-dir],
AS_HELP_STRING([--with-bash-completion-dir[=PATH]], [Directory to install Bash completion, default: auto]),
[bash_dir=$withval], [bash_dir=yes])
AC_ARG_WITH(fstab,
AS_HELP_STRING([--with-fstab=FILE], [System default fstab, default $sysconfdir/fstab]),
[fstab=$withval], [fstab=yes])
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], [Heading for boot progress. Default: PRETTY_NAME from /etc/os-release, fallback to "Finit vX.YY"]),
[heading=$withval], [heading=yes]) [heading=$withval], [heading=yes])
@@ -151,7 +125,7 @@ AC_ARG_WITH(hostname,
[hostname=$withval], [hostname=yes]) [hostname=$withval], [hostname=yes])
AC_ARG_WITH(runlevel, AC_ARG_WITH(runlevel,
AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap [1-9], default: 2]), AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap, default: 2]),
[runlevel=$withval], [runlevel=yes]) [runlevel=$withval], [runlevel=yes])
AC_ARG_WITH(random-seed, AC_ARG_WITH(random-seed,
@@ -161,29 +135,10 @@ AC_ARG_WITH(random-seed,
AC_ARG_WITH(keventd, AC_ARG_WITH(keventd,
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no]) AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
AC_ARG_WITH(sulogin,
AS_HELP_STRING([--with-sulogin@<:@=USER@:>@], [Enable built-in sulogin, optional USER to request password for (default root), default: no.]),[sulogin=$withval],[with_sulogin=no])
AC_ARG_WITH(watchdog, AC_ARG_WITH(watchdog,
AS_HELP_STRING([--with-watchdog@<:@=DEV@:>@], [Enable built-in watchdog, default: /dev/watchdog]), AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval], [with_watchdog=no watchdog=]) [watchdog=$withval], [with_watchdog=no watchdog=])
AC_ARG_WITH(hook-scripts-path,
AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]),
[hook_scripts_path=$withval], [hook_scripts_path=yes])
AC_ARG_WITH(plugin-path,
AS_HELP_STRING([--with-plugin-path=DIR], [Search path for external plugins, default /usr/lib/finit/plugins:/usr/local/lib/finit/plugins]),
[plugin_path=$withval], [plugin_path=yes])
AC_ARG_WITH(rtc-date,
AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM:SS", default "2000-01-01 00:00:00"]),
[rtc_date=$withval], [rtc_date=no])
AC_ARG_WITH(rtc-file,
AS_HELP_STRING([--with-rtc-file=FILE], [If RTC is missing, save and restore system clock from this file, default: no]),
[rtc_file=$withval], [rtc_file=no])
### Enable features ########################################################################### ### Enable features ###########################################################################
# Create config.h from selected features and fallback defaults # Create config.h from selected features and fallback defaults
@@ -193,12 +148,6 @@ AS_IF([test "x$enable_auto_reload" = "xyes"], [
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [ AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [
AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])]) AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])])
AS_IF([test "x$enable_kernel_logging" = "xyes"], [
AC_DEFINE(KERNEL_LOGGING, 1, [Keep kernel warn/err logs to console])])
AS_IF([test "x$enable_cgroup" = "xyes"], [
AC_DEFINE(CGROUP2_ENABLED, 1, [Autodetect cgroup v2 support from /sys/fs/cgroup])])
AS_IF([test "x$enable_fastboot" = "xyes"], [ AS_IF([test "x$enable_fastboot" = "xyes"], [
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])]) AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
@@ -212,24 +161,9 @@ AS_IF([test "x$enable_redirect" = "xyes"], [
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.])])
AS_IF([test "x$enable_rescue" != "xno"], [
AC_DEFINE([RESCUE_MODE], 1, [Define to enable support for rescue mode.])])
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"]) AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
### With features ############################################################################## ### With features ##############################################################################
AS_IF([test "x$bash_dir" = "xyes"], [
PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
[BASH_DIR="$(pkg-config --variable=completionsdir bash-completion)"],
[BASH_DIR="$datadir/bash-completion/completions"])], [
BASH_DIR="$bash_dir"])
AS_IF([test "x$with_fstab" != "xno"], [
AS_IF([test "x$fstab" = "xyes"], [fstab=$sysconfdir/fstab])
AC_EXPAND_DIR(fstab_path, "$fstab")
AC_DEFINE_UNQUOTED(FINIT_FSTAB, "$fstab_path", [Built-in default: /etc/fstab])],[
AC_DEFINE_UNQUOTED(FINIT_FSTAB, NULL, [No built-in default fstab])])
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"], [
conf=$sysconfdir/finit.conf])]) conf=$sysconfdir/finit.conf])])
@@ -245,9 +179,10 @@ AC_DEFINE_UNQUOTED(FINIT_RCSD, "$rcsd_path", [Finit rcS.d/, default /etc/finit.d
AC_SUBST(FINIT_RCSD, "$rcsd_path") AC_SUBST(FINIT_RCSD, "$rcsd_path")
AS_IF([test "x$with_rc_local" != "xno"], [ AS_IF([test "x$with_rc_local" != "xno"], [
AS_IF([test "x$rclocal" = "xyes"], [rclocal=$sysconfdir/rc.local]) AS_IF([test "x$rclocal" = "xyes"], [
AC_EXPAND_DIR(rclocal_path, "$rclocal") rclocal=$sysconfdir/rc.local])])
AC_DEFINE_UNQUOTED(FINIT_RC_LOCAL, "$rclocal_path", [Compat SysV /etc/rc.local file to run last at boot.])]) AC_EXPAND_DIR(rclocal_path, "$rclocal")
AC_DEFINE_UNQUOTED(FINIT_RC_LOCAL, "$rclocal_path", [Compat SysV /etc/rc.local file to run last at boot.])
AS_IF([test "x$with_sysconfig" != "xno"], [ AS_IF([test "x$with_sysconfig" != "xno"], [
AS_IF([test "x$sysconfig" != "xdefault"], [ AS_IF([test "x$sysconfig" != "xdefault"], [
@@ -281,26 +216,8 @@ 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$rtc_date" != "xno"], [
AC_DEFINE_UNQUOTED(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [
rtc_date=""])
AS_IF([test "x$rtc_file" != "xno"], [
AS_IF([test "x$rtc_file" = "xyes"], [
rtc_file=/var/lib/misc/rtc])
AC_EXPAND_DIR(rtcfile_path, "$rtc_file")
AC_DEFINE_UNQUOTED(RTC_FILE, "$rtcfile_path", [Save and restore system time from this file if /dev/rtc is missing.])],[
AC_DEFINE_UNQUOTED(RTC_FILE, NULL)])
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes]) AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
AS_IF([test "x$with_sulogin" != "xno"], [
AS_IF([test "x$sulogin" = "xyes"], [
sulogin=root])
with_sulogin=yes
AC_DEFINE_UNQUOTED(SULOGIN_USER, "$sulogin", [Built-in sulogin user, default: root])], [
sulogin=])
AS_IF([test "x$with_watchdog" != "xno"], [ AS_IF([test "x$with_watchdog" != "xno"], [
AS_IF([test "x$watchdog" = "xyes"], [ AS_IF([test "x$watchdog" = "xyes"], [
watchdog=/dev/watchdog]) watchdog=/dev/watchdog])
@@ -308,70 +225,31 @@ AS_IF([test "x$with_watchdog" != "xno"], [
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [ AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
watchdog=]) watchdog=])
AS_IF([test "x$enable_hook_scripts_plugin" != "xno"], [
AS_IF([test "x$hook_scripts_path" = "xyes"], [hook_scripts_path=$libexecdir/finit/hook])
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
AS_IF([test "x$with_plugin_path" != "xno"], [
AS_IF([test "x$plugin_path" = "xyes"], [plugin_path=/usr/lib/finit/plugins:/usr/local/lib/finit/plugins])
AC_EXPAND_DIR(plugin_path, "$plugin_path")
AC_DEFINE_UNQUOTED(EXTERNAL_PLUGIN_PATH, "$plugin_path", [Built-in default: /usr/lib/finit/plugins:/usr/local/lib/finit/plugins])])
# Control build with automake flags # Control build with automake flags
AM_CONDITIONAL(BASH, [test "x$bash_dir" != "xno"]) 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(KEVENTD, [test "x$with_keventd" != "xno"])
AM_CONDITIONAL(SULOGIN, [test "x$with_sulogin" != "xno"])
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"]) AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
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"])
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"])
# Workaround for as-of-yet unreleased runstatedir support, planned for
# autoconf 2.70, which some major distros have backported.
AS_IF([test -z "$runstatedir"], runstatedir="$localstatedir/run")
AC_SUBST(runstatedir)
# Override default libdir, used for plugins and rescue.conf # Override default libdir, used for plugins and rescue.conf
pkglibdir=$libdir/finit pkglibdir=$libdir/finit
AC_SUBST(pkglibexecdir) AC_SUBST(pkglibexecdir)
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit") AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
AC_DEFINE_UNQUOTED(FINIT_EXECPATH_, "$pkglibexecdir", [Finit libexec path]) AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path])
AC_EXPAND_DIR(finit_runpath, "$runstatedir/finit/system")
AC_DEFINE_UNQUOTED(FINIT_RUNPATH_, "$finit_runpath", [Finit runtime dynamic system services])
AC_EXPAND_DIR(plugin_path, "$pkglibdir/plugins") AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
AC_SUBST(plugin_path) AC_SUBST(plugin_path)
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path]) AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
AC_EXPAND_DIR(tmpfiles_path, "$libdir/tmpfiles.d") AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf")
AC_SUBST(tmpfiles_path)
AC_DEFINE_UNQUOTED(TMPFILES_PATH_, "$tmpfiles_path", [Main tmpfiles.d/, override in /etc])
AC_EXPAND_DIR(finit_tmpfiles, "$pkglibdir/tmpfiles.d")
AC_SUBST(finit_tmpfiles)
AC_DEFINE_UNQUOTED(FINIT_TMPFILES, "$finit_tmpfiles", [Finit tmpfiles.d/ .conf path])
AC_EXPAND_DIR(system_path, "$pkglibdir/system")
AC_SUBST(system_path)
AC_DEFINE_UNQUOTED(FINIT_SYSPATH_, "$system_path", [Static services, previously generated at runtime by plugins])
AC_EXPAND_DIR(rescue_conf, "$pkglibdir/rescue.conf")
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf]) AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
AC_EXPAND_DIR(sample_conf, "$pkglibdir/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])
AC_EXPAND_DIR(bash_completion_dir, "$BASH_DIR")
AC_SUBST(BASH_DIR, "$bash_completion_dir")
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"]) AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
AS_IF([test "$heading" = "no"], [heading="<NONE>"]) AS_IF([test "$heading" = "no"], [heading="<NONE>"])
RTC_DATE=$rtc_date
AS_IF([test "$rtc_date" = ""], [RTC_DATE="<DEFAULT>"])
AS_IF([test "$rtc_date" = "no"], [RTC_DATE="<DEFAULT>"])
# Generate all files # Generate all files
AC_OUTPUT AC_OUTPUT
@@ -379,16 +257,14 @@ cat <<EOF
------------------ Summary ------------------ ------------------ Summary ------------------
$PACKAGE_NAME version $PACKAGE_VERSION $PACKAGE_NAME version $PACKAGE_VERSION
Prefix................: $(eval echo $prefix) Prefix................: $prefix
Exec prefix...........: $(eval echo $exec_prefix) Exec prefix...........: $eprefix
Libdir................: $(eval echo $libdir)
Sysconfdir............: `eval echo $sysconfdir` Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir` Localstatedir.........: `eval echo $localstatedir`
Default fstab.........: `eval echo $fstab`
Finit config file.....: $conf_path Finit config file.....: $conf_path
Finit config.d path...: $rcsd_path Finit config.d path...: $rcsd_path
Finit plugin path.....: $plugin_path Finit plugin path.....: $plugin_path
Compat rc.local path..: `eval echo $rclocal` Compat rc.local path..: $rclocal_path
System environment....: ${sysconfig_path:-${sysconfig}} System environment....: ${sysconfig_path:-${sysconfig}}
Random seed path......: $random_path Random seed path......: $random_path
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
@@ -397,24 +273,16 @@ cat <<EOF
Behavior: Behavior:
Boot heading..........: $heading Boot heading..........: $heading
Plugins...............: $plugins Plugins...............: $plugins
RTC restore date......: $RTC_DATE
RTC fallback file.....: $rtc_file
Optional features: Optional features:
Install doc/..........: $enable_doc Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib Install contrib/......: $enable_contrib
Bash completion.......: $bash_completion_dir
Built-in keventd......: $with_keventd Built-in keventd......: $with_keventd
Built-in sulogin......: $with_sulogin $sulogin
Built-in watchdogd....: $with_watchdog $watchdog Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate Built-in logrotate....: $enable_logrotate
Use cgroup v2.........: $enable_cgroup
Parse kernel cmdline..: $enable_kernel_cmdline
Keep kernel logging...: $enable_kernel_logging
Skip fsck check.......: $enable_fastboot Skip fsck check.......: $enable_fastboot
Run fsck fix mode.....: $enable_fsckfix Run fsck fix mode.....: $enable_fsckfix
Redirect output.......: $enable_redirect Redirect output.......: $enable_redirect
Rescue mode...........: $enable_rescue
Default hostname......: $hostname Default hostname......: $hostname
Default group.........: $group Default group.........: $group
Default runlevel......: $runlevel Default runlevel......: $runlevel
+2 -4
View File
@@ -1,7 +1,5 @@
HowTo: Finit on Alpine Linux 3.4...3.19 HowTo: Finit on Alpine Linux 3.4-3.6
======================================= ====================================
> **Blog** https://troglobit.com/post/2021-02-12-alpine-linux-with-finit/
HowTo use Finit to boot an [Alpine Linux][] system. It is assumed that HowTo use Finit to boot an [Alpine Linux][] system. It is assumed that
the user has already installed make, a compiler, C library header files, the user has already installed make, a compiler, C library header files,
+3 -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 --without-watchdog \ --enable-alsa-utils-plugin --with-watchdog \
--with-keventd --with-sulogin --with-keventd
echo echo
echo "*** Building ..." echo "*** Building ..."
@@ -35,3 +35,4 @@ fi
echo echo
echo "*** Done" echo "*** Done"
echo echo
+2 -1
View File
@@ -1,7 +1,8 @@
SUBDIRS = available SUBDIRS = available
docsdir := @docdir@/contrib/alpine/finit.d docsdir := @docdir@/contrib/alpine/finit.d
d_LINKS = acpid.conf crond.conf dropbear.conf getty.conf \ d_LINKS = acpid.conf crond.conf dropbear.conf getty.conf \
keymap.conf modules.conf ntpd.conf syslogd.conf keymap.conf modules.conf ntpd.conf \
klogd.conf syslogd.conf
install-exec-hook: install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available @$(MKDIR_P) $(DESTDIR)$(docsdir)/available
+1 -1
View File
@@ -1,4 +1,4 @@
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 syslogd.conf watchdog.conf lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
@@ -4,4 +4,4 @@
#module evdev #module evdev
#module mousedev #module mousedev
#module 8139cp #module 8139cp
#module softdog module softdog
+29 -35
View File
@@ -1,53 +1,45 @@
#!/bin/sh #!/bin/sh
if [ "$(id -u)" != "0" ]; then if [ "x`id -u`" != "x0" ]; then
printf "\n\e[1m*** This script must run as root\e[0m\n\n" echo
echo "*** This script must run as root"
echo
exit 1 exit 1
fi fi
# shellcheck disable=SC3037,SC3045,SC2154
yorn()
{
printf "\e[1m> %s (y/N)?\e[0m " "$*"
read -rn1 yorn
echo
if [ "$yorn" = "y" ] || [ "$yorn" = "Y" ]; then
return 0
fi
return 1
}
# Adjust base directory # Adjust base directory
if grep -q contrib/alpine Makefile 2>/dev/null; then grep -q alpine Makefile 2>/dev/null
if [ $? -eq 0 ]; then
cd ../.. cd ../..
fi elif [ ! -e configure.ac ]; then
echo "*** Please run this script from the Finit base directory."
if [ ! -e configure.ac ]; then
printf "\n\e[1m*** Please run this script from the Finit base directory.\e[0m\n"
exit 1 exit 1
fi fi
printf "\n\e[1mInstall Finit on Alpine Linux\n" echo
printf "========================================================================\e[0m\n" echo "*** Install Finit on Alpine Linux"
printf "/sbin/finit - PID 1\n" echo "========================================================================"
printf "/lib/finit/plugins/* - All enabled Finit plugins\n" echo "/sbin/finit - PID 1"
printf "/etc/finit.conf - Finit configuration file\n" echo "/lib/finit/plugins/* - All enabled Finit plugins"
printf "/etc/finit.d/ - Finit services\n" echo "/etc/finit.conf - Finit configuration file"
echo "/etc/finit.d/ - Finit services"
echo
read -p "Do you want to continue (y/N)? " yorn
if yorn "Do you want to continue"; then if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
printf "\n\e[1m*** Installing Finit files ...\e[0m\n" echo
echo "*** Installing Finit files ..."
make install make install
cd /usr/share/doc/finit/contrib/alpine || exit 1 cd /usr/share/doc/finit/contrib/alpine
for file in finit.conf rc.local; do for file in finit.conf rc.local; do
install -vbD $file /etc/$file install -vbD $file /etc/$file
done done
cp -va finit.d /etc/ cp -va finit.d /etc/
if yorn "Install Finit as the system default Init"; then read -p "*** Install Finit as the system default Init (y/N)? " yorn
printf "\e[1m*** Updating /sbin/init symlink --> finit ...\e[0m\n" if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
cd /sbin || exit 1 echo "*** Updating /sbin/init symlink --> finit ..."
cd /sbin
rm init rm init
ln -s finit init ln -s finit init
@@ -56,8 +48,10 @@ if yorn "Do you want to continue"; then
ln -s reboot shutdown ln -s reboot shutdown
ln -s reboot suspend ln -s reboot suspend
fi fi
printf "\n\e[1m*** Done\e[0m\n" echo "*** Done"
echo echo
else else
printf "\n\e[1m*** Aborting install.\e[0m\n\n" echo
echo "*** Aborting install."
echo
fi fi
+3
View File
@@ -59,3 +59,6 @@ else
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit" echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
echo echo
fi fi
+2 -1
View File
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--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 --with-watchdog \
--with-keventd --with-sulogin --with-keventd
echo echo
echo "*** Building ..." echo "*** Building ..."
@@ -35,3 +35,4 @@ fi
echo echo
echo "*** Done" echo "*** Done"
echo echo
+57
View File
@@ -0,0 +1,57 @@
Bootstrap
=========
1. Prepare emergency shell, if enabled
2. Set up initial signal handlers
3. Set up default $PATH early
4. Mount `/proc` and `/sys`
5. Check kernel command line for `debug` to figure out log level
6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type
9. Parse `/etc/finit.conf`
10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This
means any WDT that requires a kernel module need to be either
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`
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
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
16. Call 2nd level hooks, `HOOK_BASEFS_UP`
17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin
18. Load kernel params from `/etc/sysctl.d/*.conf`, `/etc/sysctl.conf`
et al. (Supports all locations that SysV init does.), handled by
`procps` plugin
19. Start all 'S' runlevel tasks and services
20. Bring up loopback interface and all `/etc/network/interfaces`, if
the `.conf` setting `network <SCRIPT>` is set, it is called instead
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
(re)loaded and new services, tasks, and blocking run commands are
started. Provided they are allowed in the new runlevel and all of
their conditions, if any, are set.
24. Call 4th level hooks, `HOOK_SVC_UP`
25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs
In (19) and (23) tasks and services defined in `/etc/finit.conf` and
`/etc/finit.d/*.conf` are started.
Notice the seven hook points that are called at various point in the
bootstrap process. This is where plugins can extend the boot in any
way they please. There are other hook points available, for more on
this, see [plugins.md](plugins.md).
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,
well before all 'S' runlevel tasks are started. This could be used with
system images that are created read-only and all configuration is stored
on external media.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
+10 -26
View File
@@ -29,8 +29,8 @@ optional plugins to enable. It depends on two external libraries:
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 required versions:
- libite v2.2.0, or later - libite v2.2.0
- libuEv v2.2.0, or later - libuEv v2.2.0
Configure Configure
@@ -54,7 +54,7 @@ Below are a few of the main switches to configure:
will be linked statically. will be linked statically.
* `--enable-kernel-cmdline`: Enable Finit pre-4.1 parsing of init args from * `--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 `/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 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.
@@ -65,19 +65,8 @@ Below are a few of the main switches to configure:
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin. * `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
* `--with-sulogin`: Enable bundled `sulogin` program. Default is to use the
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
login if the `root` user is disabled or has no password at all.
For more configure flags, see <kbd>./configure --help</kbd> For more configure flags, see <kbd>./configure --help</kbd>
> **Note:** the configure script is not available in the GIT sources. It is
> however included in (officially supported) released tarballs. The
> idea is that you should not need GNU autotools to build, only the
> above mentioned dependencies, a POSIX shell, a C compiler and make.
> Any contributing to Finit can generate it from `configure.ac` using
> the `autogen.sh` script.
Example Example
------- -------
@@ -85,8 +74,8 @@ Example
First, unpack the archive: First, unpack the archive:
```shell ```shell
$ tar xf finit-4.3.tar.gz $ tar xf finit-4.1.tar.gz
$ cd finit-4.3/ $ cd finit-4.1/
``` ```
Then configure, build and install: Then configure, build and install:
@@ -94,7 +83,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-keventd --with-watchdog
$ make $ make
. .
. .
@@ -102,7 +91,7 @@ $ make
$ DESTDIR=/tmp/finit make install $ DESTDIR=/tmp/finit make install
``` ```
In this example the [finit-4.3.tar.gz][1] archive is unpacked to the In this example the [finit-4.1.tar.gz][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
@@ -157,22 +146,17 @@ enabled. The default Finit rescue mode configuration is installed into
By default the a root shell, without login, is started. By default the a root shell, without login, is started.
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to
> force `reboot`, `shutdown`, or `poweroff`.
Debugging Debugging
--------- ---------
Edit, or append to, the kernel command line: remove `quiet` to enable Edit, or append to, the kernel command line: remove `quiet` to enable
kernel messages and add `finit.debug` to enable Finit debug messages. kernel messages and add `debug` to enable Finit debug messages.
```shell ```shell
append="init=/sbin/finit -- finit.debug" append="init=/sbin/finit debug"
``` ```
Notice the `--` separator.
To debug startup issues, in particular issues with getty/login, add To debug startup issues, in particular issues with getty/login, add
the following to your Finit .conf file: the following to your Finit .conf file:
@@ -191,6 +175,6 @@ it only for debugging start up issues when Finit crashes.
systems since they can potentially give a user root access. systems since they can potentially give a user root access.
[1]: ftp://troglobit.com/finit/finit-4.3.tar.gz [1]: ftp://troglobit.com/finit/finit-4.1.tar.gz
[libuEv]: https://github.com/troglobit/libuev [libuEv]: https://github.com/troglobit/libuev
[libite]: https://github.com/troglobit/libite [libite]: https://github.com/troglobit/libite
+11 -79
View File
@@ -10,62 +10,13 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
> parameters to init. E.g., `init=/sbin/finit -- finit.debug rescue` > 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, unless `loglevel=7` (below) is used. console until Finit starts up.
* `fsck.mode=<auto,force,skip>`, default: `auto`, unless built with
`configure --enable-fastboot`, in which case the default is `skip`
* `fsck.repair=<preen,yes,no>`, default: `preen`, unless built with
`configure --enable-fsckfix`, in which case the default is `yes`.
This configure option also sets `fsck.mode=force`, unless fastboot
is selected at the same time. I.e., the `--enable-fastboot` build
option overrides the `fsck.mode` default value.
* `finit.cond=foo[,bar[,baz]]`: set `<boot/foo>` condition, optionally
multiple conditions can be set using the same option, separated with a
comma. Alternatively, multiple `foo.cond=arg` can be given. Each will
result in a `<boot/arg>` condition being set to control the rest of the
system bootstrap.
Very useful for selecting different boot modes, e.g. manufacturing test,
firmware upgrade, or rescue mode.
> Note: `<boot/...>` conditions cannot be cleared with `initctl`!
* `finit.config=/path/to/alternative/finit.conf`: override the
compile-time bootstrap config file, default:
./configure --with-config=/etc/finit.conf
Useful when starting up in various rescue mode, factory, or
production test setups. Use the top-level configuration file
directive `rcsd /path/to/finit.d` to override the default
rcS.d directory.
* `finit.debug[=bool]`: Enable finit debug. This is operated * `finit.debug[=bool]`: Enable finit debug. This is operated
independently of the kernel `debug` setting. New as of Finit v4. independently of the kernel debug setting. New as of Finit v4.
* `finit.fstab=</path/to/etc/fstab.alternative>`: Tell Finit to use an * `finit.show_status[=bool]`: Control finit boot progress, including
alternate `fstab` to mount the file system from. Remember, this file banner.
must be on the `root=...` file system provided to Finit from the
kernel. By default the built-in fstab is used, which itself defaults
to `/etc/fstab`, but can be changed at build time with:
./configure --with-fstab=/path/to/fstab
It is even possible to disable a built-in default using:
./configure --without-fstab
Making `finit.fstab=/path/to/fstab` a *mandatory* command line option.
Note, if the command line fstab is missing, Finit falls back to the
built-in fstab, and if both are missing, the system treats this as a
bad `fsck` and thus calls `sulogin`. If, in turn, `sulogin` is not
available on the system, Finit calls reboot, which is also what will
happen when a user exits from `sulogin`.
* `finit.status[=bool]`: Control finit boot progress, including banner.
(Used to be `finit.show_status`, which works but is deprecated.)
* `finit.status_style=<classic,modern>`: Set Finit boot progress style, * `finit.status_style=<classic,modern>`: Set Finit boot progress style,
when enabled. when enabled.
@@ -75,44 +26,25 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
signals and has no job control. Recommend using, and modifying, signals and has no job control. Recommend using, and modifying,
`rescue` mode instead. `rescue` mode instead.
* `loglevel=<0-7>`, sets the kernel's log level, which is more granular
than `debug`. Also, when `loglevel=7`, Finit will *not disable*
kernel logs to the console. This is very useful when debugging the
kernel at system bring-up. Since `loglevel=7` is the same as `debug`
this means you have to use `quiet` for a quiet boot, until sysklogd
takes over logging of kernel events.
* `panic=SEC`: By default the kernel does not reboot after a kernel * `panic=SEC`: By default the kernel does not reboot after a kernel
panic. This setting will cause a kernel reboot after SEC seconds. panic. This setting will cause a kernel reboot after SEC seconds.
* `quiet`: Suppress kernel logging to console, except for warnings and * `quiet`: Suppress kernel logging to console, except for warnings and
errors. Also, see `loglevel` and `quiet` above. errors.
* `rescue`: Start rescue/maintenance mode. If your system comes with * `rescue`: Start rescue/maintenance mode. If your system comes with
the bundled `sulogin` program (Finit, or from util-linux/Busybox), the bundled `sulogin` program (Finit, or from util-linux/Busybox),
you will be given a root login to a maintenance shell. However, if you will be given a root login to a maintenance shell. However, if
`sulogin` is missing, the file `/lib/finit/rescue.conf` is read and `sulogin` is missing, the file `/lib/finit/rescue.conf` is read and
the system booted in a limited fallback mode. See [config.md][] the system booted in a limited fallback mode. See [config.md][]
for more information. for more information.
This option can be disabled with `configure --without-rescue` * `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
debug problems with the regular runlevel. All services and TTYs in
**Note:** in this mode `initctl` will not work. Correct the problem `[1]` will be started, so a `tty [1] @console nologin` configuration
and use `reboot -f` to force reboot. presents you with a root console without login.
* `single`, or `S`: Overrides the configured runlevel (default: 2) to go
to after bootstrap by forcing it to runlevel 1, this is also known
as single user mode. Useful to debug startup problems. All services
and TTYs in `[1]` will be started, so a `tty [1] @console nologin`
configuration presents you with a root console without login.
* `1-9`, except `6`: override the configured `runlevel`. Like the `S`
and `rescue`, giving a single number on the kernel command line tells
Finit to ignore any `runlevel` in `/etc/finit.conf` as well as the
configure fallback `--with-runlevel=N` setting. Remember, `6` is the
reboot runlevel and is not permitted. Any other values are ignored.
For more on kernel boot parameters, see the man page [bootparam(7)][]. For more on kernel boot parameters, see the man page [bootparam(7)][].
[config.md]: config.md#rescue-mode [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
+19 -73
View File
@@ -13,9 +13,9 @@ Finit Conditions
Introduction Introduction
------------ ------------
Conditions are a relatively new addition to Finit, introduced in v3, Conditions are a new addition to Finit, introduced in v3, with the
with the intention of providing a mechanism for common synchronization intention of providing a mechanism for common synchronization problems.
problems. For example: For example:
- *"wait for service A to start before starting service B"*, or - *"wait for service A to start before starting service B"*, or
- *"wait for basic network access to be available"* - *"wait for basic network access to be available"*
@@ -27,16 +27,6 @@ specified separated by comma. Multiple conditions are logically AND'ed
during evaluation, i.e. all conditions must be satisfied in order for a during evaluation, i.e. all conditions must be satisfied in order for a
service to run. service to run.
A special syntax, using a leading `!` in run/task/service conditions,
denote if a:
- service does not support `SIGHUP`
- run/task should not block runlevel changes (i.e., bootstrap)
Finit guarantees by default that all run/tasks run (at least) once
per runlevel. For most tasks this is a good default, for example
checking SSH host keys or loading keymap. However, for conditions
that are unlikely to happen it is not. (See example below.)
### Example ### Example
@@ -45,40 +35,22 @@ that are unlikely to happen it is not. (See example below.)
In this example the Network monitor daemon `netd` is not started until In this example the Network monitor daemon `netd` is not started until
both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A
`pid/` condition is satisfied by the corresponding service's PID file `pid/` condition is satisfied by the corresponding service's PID file
being created, i.e., the service's default readiness notification. being created.
**NOTE:** Conditions also stop services when a condition is no longer **NOTE:** Conditions also stop services when a condition is no longer
asserted. I.e., if the Zebra process above stops or restarts, netd asserted. I.e., if the Zebra process above stops or restarts, netd
will also stop or restart. will also stop or restart.
Another example is `dropbear`, it does not support `SIGHUP`, but we can
also see optional sourcing of arguments from an environment file:
service [2345789] <!> env:-/etc/default/dropbear dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
Finally, the weird "block runlevel changes" example. Here we see what
happens when Finit receives `SIGPWR`, sent from a power daemon like
[powstatd(8)][]. A condition is asserted and a user can set up their
own task to act on it. We do not want this task to block Finit from
moving to the next runlevel after bootstrap, so we set `<!>`:
task [S0123456789] <!sys/pwr/fail> name:pwrfail initctl poweroff -- Power failure, shutting down
Triggering Triggering
---------- ----------
Conditions are mainly triggered (asserted) by built-in plugins, e.g., Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
- `netlink.so`: provides `<net/...>` also supports user-defined conditions, sometimes referred to as static
- `pidfile.so`: provides `<pid/...>` or one-shot conditions. They live in the `<usr/...` namespace and are
- Cmdline `finit.cond=arg`: provides `<boot/arg>` constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
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 User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands: `initctl cond clear` commands:
@@ -115,35 +87,15 @@ 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: Finit comes with a set of plugins for conditions: `pidfile`, `netlink`,
`sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/`
- `devmon` (built-in) (recursively) for PID files created by the monitored services, and sets
- `netlink` a corresponding condition in the `pid/` namespace. Similarly, the
- `pidfile` `netlink` plugin provides basic conditions for when an interface is
- `sys` brought up/down and when a default route (gateway) is set, in the `net/`
- `usr` namespace. The `sys` and `usr` plugins monitor are passive condition
monitors where the action is provided by `keventd`, signal handlers,
The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device and in the case of `usr`, the end-user via the `initctl` tool
nodes being created and removed. It is active only when a run, task, or
service has declared a `<dev/foo>` or `<dev/dir/bar>` condition.
The `pidfile` plugin (recursively) watches `/run/` (recursively) for PID
files created by the monitored services, and sets a corresponding
condition in the `pid/` namespace.
Similarly, the `netlink` plugin provides basic conditions for when an
interface is brought up/down and when a default route (gateway) is set,
in the `net/` 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.
Additionally, the various states of a run/task/sysv/service can also be
used as conditions, the image above shows the state names. The syntax
for a `service` type process: `<service/foo/STATE>`. The other types,
in particular run/task/sysv, there are the additional states `success`
and `failure`.
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
@@ -162,14 +114,10 @@ Built-in conditions:
- `net/<IFNAME>/exist` - `net/<IFNAME>/exist`
- `net/<IFNAME>/up` - `net/<IFNAME>/up`
- `net/<IFNAME>/running` - `net/<IFNAME>/running`
- `service/<NAME[:ID]>/<STATE>`
- `{run, task, sysv}/<NAME[:ID]>/{<STATE>, success, failure}`
- `sys/pwr/ac` - `sys/pwr/ac`
- `sys/pwr/fail` - `sys/pwr/fail`
- `sys/key/ctrlaltdel` - `sys/key/ctrlaltdel`
- `usr/foo` - `usr/foo`
- `boot/arg`
- `dev/node` and `dev/dir/node`
**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
@@ -215,7 +163,7 @@ Debugging
If a service is not being started as it should, the problem might be If a service is not being started as it should, the problem might be
that one of its conditions is not in the expected state. Use the that one of its conditions is not in the expected state. Use the
command `initctl status` to inspect service status. Services in the command `initctl status` to inspect service status. Services in the
`waiting` state are pending a condition. `ready` state are pending a condition.
In that situation, running `initctl cond show` reveals which of the In that situation, running `initctl cond show` reveals which of the
conditions that are not satisfied. Listed as `off` below. conditions that are not satisfied. Listed as `off` below.
@@ -283,5 +231,3 @@ Therefore, any plugin that supplies Finit with conditions must ensure
that their state is updated after each reconfiguration. This can be that their state is updated after each reconfiguration. This can be
done by binding to the `HOOK_SVC_RECONF` hook. For an example of how done by binding to the `HOOK_SVC_RECONF` hook. For an example of how
to do this, see `plugins/pidfile.c`. to do this, see `plugins/pidfile.c`.
[powstatd(8)]: https://manpages.ubuntu.com/manpages/trusty/en/man8/powstatd.8.html
+100 -778
View File
File diff suppressed because it is too large Load Diff
+29 -154
View File
@@ -8,8 +8,7 @@ Hooks & Plugins
* [Shutdown Hooks](#shutdown-hooks) * [Shutdown Hooks](#shutdown-hooks)
Finit can be extended to add general functionality in the form of I/O Finit can be extended to add general functionality in the form of I/O
monitors, or hook plugins. It is even possible to run scripts at these monitors, or hook plugins.
hook points, see below.
The following sections detail existing plugins and hook points. For The following sections detail existing plugins and hook points. For
more information, see the plugins listed below. more information, see the plugins listed below.
@@ -28,92 +27,33 @@ For your convenience a set of *optional* plugins are available:
system and runs at `HOOK_BASEFS_UP`. The `/var`, `/run`, and `/dev` system and runs at `HOOK_BASEFS_UP`. The `/var`, `/run`, and `/dev`
file systems must be writable for this plugin to work. file systems must be writable for this plugin to work.
This plugin is a wrapper for the [tmpfiles.d(5)][] implementation that Note: On an embedded system both `/var` and `/run` can be `tmpfs` RAM
Finit has. Capable but limited: no aging, attributes, or subvolumes. disks and `/dev` is usually a `devtmpfs`. This must be defined in the
`/etc/fstab` file and in the Linux kernel config.
By default, `/lib/finit/tmpfiles.d` carries all the default .conf
files distributed with Finit. It is read first but can be overridden
by any of the standard tmpfiles.d directories, e.g. `/etc/tmpfiles.d`.
> **Note:** On an embedded system both `/var` and `/run` can be `tmpfs`
> RAM disks and `/dev` is usually a `devtmpfs`. This must be defined
> in the `/etc/fstab` file and in the Linux kernel config.
* *dbus.so*: Setup and start system message bus, D-Bus, at boot. * *dbus.so*: Setup and start system message bus, D-Bus, at boot.
_Optional plugin._ _Optional plugin._
* *hook-scripts.so*: Trigger the execution of scripts from plugin hook * *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
points (see [Hooks](#hooks)). _Optional plugin._ available.
This plugin is particularly useful for early boot debugging that needs
to take place before regular services are available.
For example, say that you want to enable some kernel tracing before
modules are loaded. With hook scripts, you can just drop in a shell
script in `/libexec/finit/hook/mount/all/` that will poke the right
control files in tracefs.
Scripts are located in `/libexec/finit/hook` by default, this can be
customized at build-time using the `--with-hook-scripts-path=PATH`
argument to `configure`.
* *hotplug.so*: Replaced with `/lib/finit/system/10-hotplug.conf`, which
checks for `udevd` and `mdev` at boot. This file can be overridden by a
file in `/etc/finit.d/10-hotplug.conf`.
Enabled by default.
> See the [Services](config.md#services) section in the configuration
> guide for an example how to run `mdevd`, alternative to plain mdev.
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt. * *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
Enabled by default.
* *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to * *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
load using `modprobe`. Each file can contain multiple lines with the
name of the module to load. Any line starting with the standard UNIX
comment character, `#`, or `;`, is skipped.
Modules are by default loaded in runlevel `S` using the `task` stanza.
Each module gets a unique `name:modprobe.foo`, and optional`:ID`. The
runlevel can be changed per file using:
set runlevel 2345
The `:ID` is a globally incremented index, which can be disabled per
file (anywhere) using the following config line:
set noindex
To change the index used by the plugin:
set index 1234
**Note:** unlike the traditional .conf `module` directive, which load
any listed module immediately, this plugin creates a background `task`
which load the module(s) in the background. The program is modprobe,
`/sbin/modprobe`, which you can override per .conf file:
set modprobe /path/to/maybe-a-modprobe-wrapper
Since these tasks run in the background, they usually return `[ OK ]`
at boot, unless the modprobe tool does not exist. Check syslog for
warnings and the actual status of the operation using `initctl`.
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and * *netlink.so*: Listens to Linux kernel Netlink events for gateway and
interfaces. These events are then sent to the Finit service monitor interfaces. These events are then sent to the Finit service monitor
for services that may want to be SIGHUP'ed on new default route or for services that may want to be SIGHUP'ed on new default route or
interfaces going up/down. Enabled by default. interfaces going up/down.
* *resolvconf.so*: Setup necessary files for `resolvconf` at startup. * *resolvconf.so*: Setup necessary files for `resolvconf` at startup.
_Optional plugin._ _Optional plugin._
* *tty.so*: Watches `/dev`, using inotify, for new device nodes (TTY's) * *tty.so*: Watches `/dev`, using inotify, for new device nodes (TTY's)
to start/stop getty consoles on them on demand. Useful when plugging to start/stop getty consoles on them on demand. Useful when plugging
in a usb2serial converter to login to your embedded device. Enabled in a usb2serial converter to login to your embedded device.
by default.
* *urandom.so*: Setup random seed at startup. Enabled by default. * *urandom.so*: Setup random seed at startup.
* *x11-common.so*: Setup necessary files for X-Window. _Optional plugin._ * *x11-common.so*: Setup necessary files for X-Window. _Optional plugin._
@@ -127,105 +67,40 @@ state, etc. Available hook points are:
Hooks Hooks
----- -----
In the below listings, the first label is the hook point for a C plugin,
the second is the condition name and hook script path. A hook script is
a plain shell script, or program, that does a very small dedicated job
at the below hook points.
See [Run-parts Scripts](config.md#run-parts-scripts) for details on the
requirements, possibilities, and *limitations* of hook scripts.
All hook scripts are called with at least one environment variable set,
the hook name, useful when reusing the same hook script for multiple
hook points:
- `FINIT_HOOK_NAME`: set to the second label, e.g., `hook/net/up`
- `FINIT_SHUTDOWN`: set for `hook/sys/shutdown` and later to one
of `halt`, `poweroff`, or `reboot`.
**Example:**
$ mkdir -p /libexec/finit/hook/sys/down
$ cat <<EOF >/libexec/finit/hook/sys/down/foo.sh
#!/bin/sh
echo 'I run just before the reboot() syscall at shutdown/reboot'
echo 'I have access to /dev since devtmpfs is exempt from umount'
echo "FINIT_HOOK_NAME: $FINIT_HOOK_NAME"
if [ -n "$FINIT_SHUTDOWN" ]; then
echo "FINIT_SHUTDOWN: $FINIT_SHUTDOWN"
fi
exit 0
EOF
$ chmod +x /libexec/finit/hook/sys/down/foo.sh
> **Note:** to use hook scripts, even for pre-bootstrap and pre-shutdown
> tasks, you must build with `configure --enable-hook-scripts-plugin`.
### Bootstrap Hooks ### Bootstrap Hooks
* `HOOK_BANNER`, `hook/sys/banner`: The very first point at which a * `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
plugin can run. Intended to be used as a banner replacement. mounted — very early
Essentially this runs just before entering runlevel S. Assume nothing
is available, so be prepared to use absolute paths, etc.
* `HOOK_ROOTFS_UP`, `hook/mount/root`: When `finit.conf` has been read * `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
and `/` has is mounted — very early and default init signals are setup
* `HOOK_MOUNT_ERROR`, `hook/mount/error`: executed if `mount -a` fails * `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
networking is up (`lo` is up and the `network` script has run)
* `HOOK_MOUNT_POST`, `hook/mount/post`: always executed after `mount -a` * `HOOK_SVC_UP`: All services in the active runlevel has been launched
* `HOOK_BASEFS_UP`, `hook/mount/all`: All of `/etc/fstab` is mounted, * `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d`
swap is available and default init signals are setup has been launched
* `HOOK_SVC_PLUGIN`, `hook/svc/plugin`: Called in `conf_init()` right
before loading `/etc/finit.conf`. For plugins to register any early
run/task/services, please do NOT use any earlier hook point. That
will cause uninitialized rlimits that lead to unpredictable results
when Finit later tries to start the run/task/service.
* `HOOK_NETWORK_UP`, `hook/net/up`: System bootstrap, runlevel S, has
completed and networking is up (`lo` is up and the `network` script
has run)
* `HOOK_SVC_UP`, `hook/svc/up`: All services in the active runlevel have
been launched
* `HOOK_SYSTEM_UP`, `hook/sys/up`: All services *and* everything in
`/etc/finit.d` has been launched
### Runtime Hooks ### Runtime Hooks
* `HOOK_SVC_RECONF`, N/A: Called when the user has changed something in * `HOOK_SVC_RECONF`: Called when the user has changed something in the
the `/etc/finit.d` directory and issued `SIGHUP`. The hook is called `/etc/finit.d` directory and issued `SIGHUP`. The hook is called when
when all modified/removed services have been stopped. When the hook all modified/removed services have been stopped. When the hook has
has completed, Finit continues to start all modified and new services. completed, Finit continues to start all modified and new services.
* `HOOK_RUNLEVEL_CHANGE`, N/A: Called when the user has issued a * `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
runlevel change. The hook is called when services not matching the change. The hook is called when services not matching the new
new runlevel have been been stopped. When the hook has completed, runlevel have been been stopped. When the hook has completed, Finit
Finit continues to start all services in the new runlevel. continues to start all services in the new runlevel.
### Shutdown Hooks ### Shutdown Hooks
* `HOOK_NETWORK_DN`, `hook/net/down`: Called right after having changed * `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
to runlevel 6, or 0, when all services have received their 'stop' signal. services are sent `SIGTERM`
* `HOOK_SHUTDOWN`, `hook/sys/shutdown`: Called at shutdown/reboot, right
before all services are sent `SIGTERM`
* `HOOK_SVC_DN`, `hook/svc/down`: In shutdown/reboot, all services and
non-reserved processes have been killed. **Note:** only hook scripts
can run here!
* `HOOK_SYSTEM_DN`, `hook/sys/down`: In shutdown/reboot, called after
all non-reserved file systems have been unmounted, just before Finit
tells the kernel to reboot or shut down. **Note:** only hook scripts
can run here!
Plugins like `tty.so` extend finit by acting on events, they are called Plugins like `tty.so` extend finit by acting on events, they are called
I/O plugins and are called from the finit main loop when `poll()` I/O plugins and are called from the finit main loop when `poll()`
detects an event. See the source code for `plugins/*.c` for more help detects an event. See the source code for `plugins/*.c` for more help
and ideas. and ideas.
[tmpfiles.d(5)]: https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
+1 -11
View File
@@ -27,17 +27,7 @@ Finit can *not* start and monitor a daemon that:
### Forks to Background w/ PID File ### Forks to Background w/ PID File
There are two syntax variants, type 1 and type 2. The former is the service pid:!/run/serv.pid serv -- Forking service
traditional one used also for `sysv` start/stop scripts, and the latter
is inspired by systemd, with a twist -- it lets Finit guess the pifdile
to look for based on the standard path and the basename of the command.
service pid:!/run/serv.pid serv -- Forking service, type 1
service type:forking serv -- Forking service, type 2
In this example the resulting files to watch for are `/run/serv.pid` and
`/var/run/serv.pid`, respectively. On most modern Linux systems this is
the same directory (`/var/run` is a symlink to `../run`).
### Runs in Foreground w/ PID File ### Runs in Foreground w/ PID File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 138 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

-141
View File
@@ -1,141 +0,0 @@
_cond()
{
initctl -pt cond dump | awk '{print $4}' | sed 's/<\(.*\)>/\1/'
}
_ident()
{
if [ -n "$1" ]; then
initctl ident | grep -q $1
else
initctl ident
fi
}
_svc()
{
initctl ls -pt | grep $1 | sed "s/.*\/\(.*\)/\1/g" | sort -u
}
_enabled()
{
echo "$(_svc enabled)"
}
_available()
{
all=$(mktemp)
ena=$(mktemp)
echo "$(_svc available)" >$all
echo "$(_svc enabled)" >$ena
grep -v -f $ena $all
rm $all $ena
}
# Determine first non-option word. Usually the command
_firstword() {
local firstword i
firstword=
for ((i = 1; i < ${#COMP_WORDS[@]}; ++i)); do
if [[ ${COMP_WORDS[i]} != -* ]]; then
firstword=${COMP_WORDS[i]}
break
fi
done
echo $firstword
}
# Determine last non-option word. Uusally a sub-command
_lastword() {
local lastword i
lastword=
for ((i = 1; i < ${#COMP_WORDS[@]}; ++i)); do
if [[ ${COMP_WORDS[i]} != -* ]] && [[ -n ${COMP_WORDS[i]} ]] && [[ ${COMP_WORDS[i]} != $cur ]]; then
lastword=${COMP_WORDS[i]}
fi
done
echo $lastword
}
_initctl()
{
local cur command
cur=${COMP_WORDS[COMP_CWORD]}
prev="${COMP_WORDS[COMP_CWORD-1]}"
firstword=$(_firstword)
lastword=$(_lastword)
commands="status cond debug help kill ls log version list enable \
disable touch show cat edit create delete reload start \
stop restart signal cgroup ps top plugins runlevel reboot \
halt poweroff suspend utmp"
cond_cmds="set get clear status dump"
cond_types="hook net pid service task usr"
signals="int term hup stop tstp cont usr1 usr2 pwr"
options="-b --batch \
-c --create \
-d --debug \
-f --force \
-h --help \
-j --json \
-n --noerr \
-1 --once \
-p --plain \
-q --quiet \
-t --no-heading \
-v --verbose \
-V --version"
case "${firstword}" in
enable)
COMPREPLY=($(compgen -W "$(_available)" -- $cur))
;;
disable|touch)
COMPREPLY=($(compgen -W "$(_enabled)" -- $cur))
;;
show|cat|edit|delete)
COMPREPLY=($(compgen -W "$(_svc .)" -- $cur))
;;
start|stop|restart|log|status)
COMPREPLY=($(compgen -W "$(_ident)" -- $cur))
;;
signal|kill)
if $(_ident "${prev}"); then
COMPREPLY=($(compgen -W "$signals" -- $cur))
else
COMPREPLY=($(compgen -W "$(_ident)" -- $cur))
fi
;;
cond)
case "${lastword}" in
set|clear)
compopt -o nospace
COMPREPLY=($(compgen -W "usr/" -- $cur))
;;
get)
COMPREPLY=($(compgen -W "$(_cond)" -- $cur))
;;
dump)
COMPREPLY=($(compgen -W "$cond_types" -- $cur))
;;
*)
COMPREPLY=($(compgen -W "$cond_cmds" -- $cur))
;;
esac
;;
*)
COMPREPLY=($(compgen -W "$commands" -- $cur))
;;
esac
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W "$options" -- $cur))
fi
}
complete -F _initctl initctl
+23 -19
View File
@@ -1,6 +1,8 @@
# From https://github.com/collectd/collectd/blob/master/configure.ac # From https://github.com/collectd/collectd/blob/master/configure.ac
# Dependency handling currently unused # Dependency handling currently unused
define([toupper], [translit([$1], [a-z], [A-Z])])
define([sanitize], [translit([$1], [-], [_])])
define([enadis], [ifelse([$1], [yes], [disable], [enable])]) define([enadis], [ifelse([$1], [yes], [disable], [enable])])
# AC_PLUGIN(name, default, info) # AC_PLUGIN(name, default, info)
@@ -8,25 +10,27 @@ define([enadis], [ifelse([$1], [yes], [disable], [enable])])
AC_DEFUN([AC_PLUGIN],[ AC_DEFUN([AC_PLUGIN],[
m4_divert_once([HELP_ENABLE], [ m4_divert_once([HELP_ENABLE], [
Optional Plugins:]) Optional Plugins:])
define(PLUGGY, m4_translit([$1],[a-z-][A-Z-]))dnl
AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [
if test "x$enableval" = "xyes"; then
enable_plugin="yes"
else
enable_plugin="no" enable_plugin="no"
fi], [ define(pluggy, sanitize($1))
if test "x$enable_all_plugins" = "xauto"; then define(PLUGGY, toupper(pluggy))
if test "x$2" = "xyes"; then AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [
enable_plugin="yes" if test "x$enableval" = "xyes"; then
enable_plugin="yes"
else else
enable_plugin="no" enable_plugin="no"
fi], [
if test "x$enable_all_plugins" = "xauto"; then
if test "x$2" = "xyes"; then
enable_plugin="yes"
else
enable_plugin="no"
fi
else
enable_plugin="$enable_all_plugins"
fi])
if test "x$enable_plugin" = "xyes"; then
AC_DEFINE([HAVE_]PLUGGY[_PLUGIN], 1, [Define to 1 if the $1 plugin is enabled.])
plugins="$1 $plugins"
fi fi
else AM_CONDITIONAL([BUILD_]PLUGGY[_PLUGIN], test "x$enable_plugin" = "xyes")
enable_plugin="$enable_all_plugins" enable_pluggy="$enable_plugin"])
fi])
if test "x$enable_plugin" = "xyes"; then
AC_DEFINE(HAVE_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, 1, [Define to 1 if the $1 plugin is enabled.])
plugins="$1 $plugins"
fi
AM_CONDITIONAL(BUILD_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, test "x$enable_plugin" = "xyes")])
+5 -175
View File
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps .\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1) .\" other parameters are allowed: see man(7), man(1)
.Dd Apr 22, 2023 .Dd June 6, 2021
.\" Please adjust this date whenever revising the manpage. .\" Please adjust this date whenever revising the manpage.
.Dt FINIT 8 SMM .Dt FINIT 8 SMM
.Os Linux .Os Linux
@@ -11,17 +11,9 @@
.Nd Fast init for Linux systems .Nd Fast init for Linux systems
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm /sbin/finit .Nm /sbin/finit
.Op -- .Op finit.status_style=classic
.Op debug .Op finit.show_status=[on,off]
.Op finit.cond=foo[,bar[,baz]]
.Op finit.config=/path/to/alt/finit.conf
.Op finit.debug .Op finit.debug
.Op finit.fstab=/etc/fstab.alt
.Op finit.status=<on,off>
.Op finit.status_style=<classic,modern>
.Op loglevel=<0-7>
.Op panic=SEC
.Op quiet
.Op rescue | recover .Op rescue | recover
.Op single | S .Op single | S
.Nm /sbin/telinit .Nm /sbin/telinit
@@ -51,15 +43,6 @@ Sourcing environment files
.It .It
Conditions for network/process/custom dependencies Conditions for network/process/custom dependencies
.It .It
Process readiness notification for synchronizing system startup as well
as reconfiguration at runtime. Natively PID files are used, but systemd
.Cm sd_notify()
and s6 notification is also supported.
.It
Limited support for
.Xr tmpfiles.d 5 ,
no aging, attributes, or subvolumes
.It
Pre/Post script actions Pre/Post script actions
.It .It
Tooling to enable/disable services Tooling to enable/disable services
@@ -78,156 +61,12 @@ Plugin support for customization
.It .It
Proper rescue mode with bundled `sulogin` for protected maintenance shell Proper rescue mode with bundled `sulogin` for protected maintenance shell
.El .El
.Sh BOOTPARAM
.Nm
support the following command line parameters to alter the behavior
at boot. Notice the
.Cm --
separator needed to tell the kernel which paramters should be given to
PID 1. For more information on this, and available kernel command line
options, see
.Xr bootparam 7 .
.Bl -tag -width pid -offset indent
.It Cm debug
Enable kernel debug. Debug messages are printed to the console until
Finit starts up, unless
.Cm loglevel=7
(below) is used.
.It Cm finit.cond=foo[,bar[,baz]]
set
.Cm <boot/foo>
condition, optionally multiple conditions can be set using the same
option, separated with a comma. Alternatively, multiple
.Cm foo.cond=arg
can be given. Each will result in a
.Cm <boot/arg>
condition being set to control the rest of the system bootstrap.
.Pp
Very useful for selecting different boot modes, e.g. manufacturing test,
firmware upgrade, or rescue mode.
.Pp
.Sy Note:
.Cm <boot/...>
conditions cannot be cleared with
.Nm initctl !
.It Cm finit.config=/path/to/alternative/finit.conf
override the compile-time bootstrap config file, default:
.Bd -unfilled -offset indent
./configure --with-config=/etc/finit.conf
.Ed
Useful when starting up in various rescue mode, factory, or
production test setups. Use the top-level configuration file
directive
.Cm rcsd Ar /path/to/finit.d
to override the default rcS.d directory.
.It Cm finit.debug
Enable Finit debug messages on system console and log. Sometimes useful
when doing board bringup. Before the system log daemon has started,
Finit use the kernel ring buffer,
.Pa /dev/kmsg .
.It Cm finit.fstab=/path/to/fstab.alt
Tell
.Nm
to use an alternate
.Cm fstab
to mount the file system from. Remember, this file must be on the
.Cm root=...
file system provided to Finit from the kernel. If this file is missing,
.Nm
falls back to its built-in default, which can be changed at build time
using the
.Cm --with-fstab=/path/to/footab
configure option. It is even possible to completely disable the
built-in using
.Cm --without-fstab ,
meaning
.Nm
.Sy must
be started with the
.Cm finit.fstab
option.
.Pp
Should
.Nm
discover that there is no fstab file available, including the fallback,
it triggers the same failure mode as when
.Cm fsck
fails, starting
.Xr sulogin 8 ,
if that is availble. When
.Cm sulogin
is missing, or when the user exits from
.Cm sulogin ,
the system is rebooted.
.Pp
Default fstab, unless changed at build-time:
.Pa /etc/fstab
.It Cm finit.status=<on,off>
Control finit boot progress, including banner. This used to be called
.Cm finit.show_status ,
which still works but is deprecated.
.It Cm finit.status_style=<classic,modern>
Set Finit boot progress style, when enabled.
.It Cm loglevel=<0-7>
set the kernel's log level, which is more granular than
.Cm debug .
Also, when
.Cm loglevel=7 ,
Finit will
.Em not disable
kernel logs to the console. This is very useful when debugging the
kernel at system bring-up. Since
.Cm loglevel=7
is the same as
.Cm debug
this means you have to use
.Cm quiet
for a quiet boot, until sysklogd takes over logging of kernel events.
.It Cm panic=SEC
By default the kernel does not reboot after a kernel panic. This
setting will cause a kernel reboot after
.Ar SEC
seconds.
.It Cm quiet
Suppress kernel logging to console, except for warnings and errors.
Also, see
.Cm loglevel
and
.Cm quiet
above.
.It Cm rescue | recover
Start rescue/maintenance mode. If your system comes with the bundled
.Cm sulogin
program (Finit, or from util-linux/Busybox), you will be given a root
login to a maintenance shell. However, if
.Cm sulogin
is missing, the file
.Pa /lib/finit/rescue.conf
is read and the system booted in a limited fallback mode. See
.Xr finit.conf 5
for more information.
.It Cm single | S
Single user mode, runlevel 1, in Finit. Useful to debug problems with
the regular runlevel. All services and TTYs in
.Cm [1]
will be started, e.g., a
.Cm tty [1] @console nologin
configuration presents you with a root console without login.
.Pp
.Sy Note:
by definition, single user mode does not have networking enabled. This
is also true for runlevel 1.
.El
.Sh BOOTING .Sh BOOTING
.Pp .Pp
Before laucnhing services, the system needs to be bootstrapped. This Before laucnhing services, the system needs to be bootstrapped. This
involves mounting all filesystems (not already mounted by an initramfs) involves mounting all filesystems (not already mounted by an initramfs)
in in
.Pa /etc/fstab , .Pa /etc/fstab ,
or any alternate
.Pa fstab
file provided on the Finit command line using
.Cm finit.fstab ,
ensuring that necessary filesystems like: ensuring that necessary filesystems like:
.Pa /dev , .Pa /dev ,
.Pa /proc , .Pa /proc ,
@@ -270,22 +109,14 @@ file removed, service B is also stopped.
.Pp .Pp
The following condition families are available today: The following condition families are available today:
.Pp .Pp
.Bl -tag -width service -offset indent .Bl -tag -width pid -offset indent
.It Cm net .It Cm net
Linux netlink events, e.g. net/route/default, net/eth0/up, and Linux netlink events, e.g. net/route/default, net/eth0/up, and
net/et0/running net/et0/running
.It Cm pid .It Cm pid
PID files based on the service declaration PID files basd on the service declaration
.Cm name:id , .Cm name:id ,
gives the condition pid/name:id gives the condition pid/name:id
.It Cm service
Tracks run/task/service state stansitions, including readiness. E.g.,
.Cm service/foo/ready
can be used as a condition for service
.Cm bar ,
provided
.Cm foo
properly signals its readiness to Finit.
.It Cm sys .It Cm sys
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
.It Cm usr .It Cm usr
@@ -341,7 +172,6 @@ Runtime files, including the condition subsystem
.Sh SEE ALSO .Sh SEE ALSO
.Xr finit.conf 5 .Xr finit.conf 5
.Xr initctl 8 .Xr initctl 8
.Xr tmpfiles.d 5
.Sh AUTHORS .Sh AUTHORS
.Nm .Nm
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0, was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
+47 -189
View File
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps .\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1) .\" other parameters are allowed: see man(7), man(1)
.Dd July 11, 2023 .Dd Oct 17, 2020
.Dt FINIT.CONF 5 SMM .Dt FINIT.CONF 5 SMM
.Os Linux .Os Linux
.Sh NAME .Sh NAME
@@ -12,79 +12,48 @@
.Nm /etc/finit.conf .Nm /etc/finit.conf
.Nm /etc/finit.d/*.conf .Nm /etc/finit.d/*.conf
.Nm /etc/finit.d/available/*.conf .Nm /etc/finit.d/available/*.conf
.Nm /etc/finit.d/enabled/*.conf
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm Finit .Nm finit
based systems can be set up with a single file: can be configured using only the original
.Pa /etc/finit.conf . .Pa /etc/finit.conf
This is the traditional way of doing it which can be ideal for some file or in combination with
setups since it gives a great overview of the system configuration. .Pa /etc/finit.d/*.conf .
Useful for package-based Linux distributions \(em each package can
provide its own "script" file.
.Pp .Pp
More comprehensive setups, however, require more careful planning. .Bl -tag -width /etc/finit.d/*.conf -offset indent -compact
The recommendation is per-package .It Pa /etc/finit.conf
.Cm *.conf Main configuration file
files in .It Pa /etc/finit.d/*.conf
.Pa /etc/finit.d/available/ . Snippets, usually one service per file
This allows end users to enable and disable parts of the system .El
configuration at runtime. Finit tracks which tasks and services belong
to a given file, so that when the user calls
.Cm initctl reload
to activate the changes they have made, only the affected tasks and
services are stopped, started, or restarted. Hence, this approach is
useful for modern package-based Linux distributions.
.Pp .Pp
The following sections describe the more of this in detail, starting Not all configuration directives are available in
with files, the file format, and available directives. Remember to .Pa /etc/finit.d/*.conf
also visit the other manual pages (references at the bottom). and some directives are only available at bootstrap, runlevel
.Cm S ,
see the section
.Sx Limitations
below for details.
.Sh FILES .Sh FILES
.Bl -tag -width /etc/finit.d/available/*.conf -compact .Bl -tag -width /etc/finit.d/available/*.conf -compact
.It Pa /etc/finit.conf .It Pa /etc/finit.conf
Main configuration file, optional Main configuration file
.It Pa /etc/finit.d/*.conf .It Pa /etc/finit.d/*.conf
Static (system) service definitions Static service definitions
.It Pa /etc/finit.d/available/*.conf .It Pa /etc/finit.d/available/*.conf
Available (installed) services Available services
.It Pa /etc/finit.d/enabled/*.conf .It Pa /etc/finit.d/enabled/*.conf
Enabled services (symlink back) Enabled services, symlinks from available
.El .El
.Pp
Static services,
.Cm .conf
files in
.Pa /etc/finit.d/ ,
cannot be enabled or disabled using
.Cm initctl [enable | disable] service[.conf] .
An enabled service is a symlink back to the corresponding
.Cm ../available/
service
.Cm .conf
file. Please use the
.Cm initctl
tool to manage these symlinks to ensure proper operation.
.Pp
.Sy NOTE:
Previous versions of
.Nm Finit
created symlinks in
.Pa /etc/finit.d/
if
.Pa /etc/finit.d/enabled/
was missing, this is as of v4.4 not supported.
.Sh FILE FORMAT .Sh FILE FORMAT
The file format is line based, empty lines and comments, lines starting Lines starting with `#' are ignored. Note that end-of-line comments are
with `#', are ignored. A configuration directive starts with a keyword NOT supported, comments must be on a line of their own.
followed by a space and the rest of the line is treated as the value.
.Pp .Pp
As of Finit v4.4, configuration directives can be broken up in multiple A configuration stanza must be on a single (long) line and
lines using the continuation character `\\', and trailing comments are .Em cannot
also allowed. Example: be broken up in multiple lines by use of a backslash as the last
.Bd -unfilled -offset indent character.
# Escape \\# chars if you want them literal in, e.g., descriptions
service name:sysklogd [S123456789] \\
env:-/etc/default/sysklogd \\
syslogd -F $SYSLOGD_ARGS \\
-- System log daemon \\# 1 # Comments allowed now
.Ed
.Sh DIRECTIVES .Sh DIRECTIVES
This section lists all supported configuration directives. There also exist This section lists all supported configuration directives. There also exist
deprecated directives, see the Markdown documentation for details on these. deprecated directives, see the Markdown documentation for details on these.
@@ -139,10 +108,6 @@ 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. halt. All other can be used by operating system administrators.
Default: 2 Default: 2
.Pp .Pp
It s recommended to keep runlevel 1 as single-user mode, because
.Nm
disables networking in this mode.
.Pp
.Sy Note: .Sy Note:
only read and executed in runlevel S (bootstrap). 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 .It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
@@ -216,12 +181,6 @@ After which they have to be restarted manually using
.Cm initctl restart NAME . .Cm initctl restart NAME .
The number of restarts, the delay between each restart, and more is The number of restarts, the delay between each restart, and more is
configurable, see the options below. configurable, see the options below.
.Pp
.Bd -filled -offset indent
.Sy Tip:
to allow endless restarts, see below option
.Cm respawn
.Ed
.Pp .Pp
For daemons that support it, we recommend appending For daemons that support it, we recommend appending
.Cm --foreground , --no-background , -n , -F , .Cm --foreground , --no-background , -n , -F ,
@@ -235,21 +194,7 @@ services and are supported using the same syntax as forking
.Cm sysv .Cm sysv
services, using the services, using the
.Cm pid:!/path/to/pidfile.pid .Cm pid:!/path/to/pidfile.pid
command modifier syntax. There is an alternative syntax that may be command modifier syntax.
more intuitive, where Finit can also guess the PID file based on the
daemon's command name:
.Bd -unfilled -offset indent
service type:forking ntpd -- NTP daemon
.Ed
.Pp
Here we let BusyBox
.Nm ntpd
daemonize itself. Finit uses the basename of the binary to guess the
PID file to watch for the PID:
.Pa /var/run/ntpd.pid .
If Finit guesses wrong, you have to submit the full
.Cm pid:!/path/to/file.pid
option to your service stanza.
.Pp .Pp
.Sy Example: in the case of .Sy Example: in the case of
.Cm ospfd .Cm ospfd
@@ -310,51 +255,6 @@ can depend on the
.Cm <pid/bar> .Cm <pid/bar>
condition. condition.
.Pp .Pp
As an alternative "readiness" notification, Finit supports both systemd
and s6 style notification. This can be enabled by using the `notify`
option:
.Bl -tag -width 1n
.It Cm notify:systemd
tells Finit the service uses the
.Cm sd_notify()
API to signal PID 1 when it has completed its startup and is ready
to service events. This API expects
the environment variable
.Cm NOTIFY_SOCKET
to be set to the socket where the application can send
.Cm "READY=1\n"
when it is starting up or has processed a
.Cm SIGHUP .
For details, see:
.Pp
.Lk https://www.freedesktop.org/software/systemd/man/sd_notify.html
.It Cm notify:s6
puts Finit in s6 compatibility mode. Compared to the systemd
notification, s6 expect compliant daemons to send
.Cm "\\n"
and then close their socket. For details, see:
.Pp
.Lk https://skarnet.org/software/s6/notifywhenup.html
.Pp
Finit takes care of "hard-wiring" the READY state as long as the
application is running, events across any `SIGHUP`. Since s6 can give
its applications the descriptor number (must be >3) on then command
line, Finit provides the following syntax (
.Cm %n
is replaced by Finit with then descriptor number):
.Bd -unfilled -offset indent
service notify:s6 mdevd -C -O 4 -D %n
.Ed
.Pp
When a service is ready, either by Finit detecting its PID file, or
their respective readiness mechanism has been triggered, Finit creates
then service's ready condition which other services can depend on:
.Bd -unfilled -offset indent
$ initctl -v cond get service/mdevd/ready
on
.Ed
.El
.Pp
If a service should not be automatically started, it can be configured If a service should not be automatically started, it can be configured
as manual with the as manual with the
.Cm manual:yes .Cm manual:yes
@@ -384,39 +284,17 @@ number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries, a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used and the above (2 and 5) will be used
.It Cm restart:always
no upper limit on the number of times Finit tries to restart a crashing
service. Same as
.Cm restart:-1
.It Cm norestart .It Cm norestart
dont restart on failures, same as dont restart on failures, same as
.Cm restart:0 .Cm restart:0
.It Cm respawn
bypasses the
.Cm restart
mechanism completely, allows endless restarts. Useful in many
use-cases, but not what
.Cm service
was originally designed for so not the default behavior.
.It Cm oncrash:reboot .It Cm oncrash:reboot
when all retries have failed, and the service when all retries have failed, and the service
has has
.Em crashed , .Em crashed ,
if this option is set the system is rebooted. if this option is set the system is rebooted. Note, future releases
.It Cm oncrash:script may include other
Similar to .Cm oncrash:
.Cm oncrash:reboot , actions.
but instead of rebooting this action calls the
.Cm post:script
(see below) to let the operator decide the best course of action. If
the post:script option is not set, this is a no-op.
.Pp
The post:script is called with the same environment variables
.Sy except
for the
.Cm EXIT_CODE
variable which is set to
.Cm "crashed" .
.El .El
.Pp .Pp
When stopping a service (run/task/sysv/service), either manually or when When stopping a service (run/task/sysv/service), either manually or when
@@ -459,13 +337,11 @@ or any unique identifier specified in the .conf file. The
.Cm post:script .Cm post:script
is called with an additional set of environment variables: is called with an additional set of environment variables:
.Bl -tag -offset indent -width 1n .Bl -tag -offset indent -width 1n
.It Cm EXIT_CODE=[exited,signal,crashed] .It Cm EXIT_CODE=[exited,signal]
set to one of set to one of
.Cm exited , .Cm exited
.Cm signal ,
or or
.Cm crashed .Cm signal
(see above).
.It Cm EXIT_STATUS=[num,SIGNAME] .It Cm EXIT_STATUS=[num,SIGNAME]
set to one of exit status code from the program, if it exited normally, 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 or the signal name (HUP, TERM, etc.) if it exited due to signal
@@ -475,6 +351,13 @@ The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the SIGKILLed, this can be adjusted using the
.Cm kill:SEC .Cm kill:SEC
modifier syntax. 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 .It Cm runparts Aq DIR
Call Call
.Xr run-parts 8 .Xr run-parts 8
@@ -843,31 +726,6 @@ the example sets
to denote that it doesn't support SIGHUP. That way Finit will to denote that it doesn't support SIGHUP. That way Finit will
stop/start the service instead of sending SIGHUP at restart/reload stop/start the service instead of sending SIGHUP at restart/reload
events. events.
.Sh TEMPLATING
Finit comes with rudimentary support for templating, similar to that of
systemd. Best illustrated with an example:
.Bd -unfilled -offset indent
$ initctl show avahi-autoipd@
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
.Ed
.Pp
To enable ZeroConf for, e.g.,
.Cm eth0 ,
use
.Bd -unfilled -offset indent
$ initctl enable avahi-autoipd@eth0.conf
.Ed
.Pp
The enabled symlink will be set up to
.Cm avahi-autoipd@.conf
and every instance of
.Cm %i
will in the instantiated directive be replaced with
.Cm eth0 .
Inspect the result with:
.Bd -unfilled -offset indent
$ initctl status avahi-autoipd:eth0
.Ed
.Sh CGROUPS .Sh CGROUPS
There are three major cgroup configuration directives: There are three major cgroup configuration directives:
.Pp .Pp
@@ -984,6 +842,6 @@ settings in
.Xr finit 8 , .Xr finit 8 ,
.Xr initctl 8 .Xr initctl 8
.Sh AUTHORS .Sh AUTHORS
.Nm Finit .Nm finit
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0, was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
maintained by Joachim Wiberg, with contributions by many others. maintained by Joachim Wiberg, with contributions by many others.
+30 -113
View File
@@ -11,7 +11,7 @@
.Nd Control tool for Finit .Nd Control tool for Finit
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm /sbin/initctl .Nm /sbin/initctl
.Op Fl bcfhjlpqtvV .Op Fl bcfhlpqtvV
.Op COMMAND .Op COMMAND
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@@ -24,27 +24,13 @@ This program follows the usual UNIX command line syntax, with long
options starting with two dashes (`-'). The options are as follows: options starting with two dashes (`-'). The options are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl b, -batch .It Fl b, -batch
Batch mode, no screen size probing. Useful for scripting. Batch mode, no screen size probing.
.It Fl c, -create .It Fl c, -create
Create missing paths (and files) as needed. Useful with the Create missing paths (and files) as needed. Useful with the edit command.
.Cm edit
command.
.It Fl f, -force .It Fl f, -force
Ignore missing files and arguments, never prompt. Ignore missing files and arguments, never prompt.
.It Fl h, -help .It Fl h, -help
Show built-in help text. Show built-in help text.
.It Fl j, -json
JSON output in
.Ar status
and
.Ar cond
commands.
.It Fl n, -noerr
When scripting
.Nm
to stop, start, restart, or signal a task or service, this option can be
used to ignore error 69 (no such task or service) and instead return 0
(OK).
.It Fl 1, -once .It Fl 1, -once
Only one lap in commands like top. Only one lap in commands like top.
.It Fl p, -plain .It Fl p, -plain
@@ -66,9 +52,9 @@ Toggle
(daemon) debug to (daemon) debug to
.Pa /dev/console .Pa /dev/console
.It Nm Ar help .It Nm Ar help
Show built-in help text. Show built-in help text
.It Nm Ar version .It Nm Ar version
Show program version. Show program version
.It Nm Ar ls | list .It Nm Ar ls | list
List all List all
.Cm *.conf .Cm *.conf
@@ -78,16 +64,7 @@ files in
Create Create
.Cm .conf .Cm .conf
in in
.Pa /etc/finit.d/available . .Pa /etc/finit.d/available
.Pp
Piping the contents into
.Nm
works as one would expect:
.Bd -literal -offset indent
echo "task name:foo /tmp/foo.sh -- Foo logger" | initctl create foo
initctl enable foo
initctl reload
.Ed
.It Nm Ar delete Ar CONF .It Nm Ar delete Ar CONF
Delete Delete
.Cm .conf .Cm .conf
@@ -127,146 +104,86 @@ Disable
.Cm .conf .Cm .conf
in in
.Pa /etc/finit.d/enabled , .Pa /etc/finit.d/enabled ,
i.e., removes symlink. i.e., removes symlink
.It Nm Ar reload .It Nm Ar reload
Reload Reload
.Cm *.conf in .Cm *.conf in
.Pa /etc/finit.d , .Pa /etc/finit.d ,
i.e., activates changes. i.e., activates changes
.It Nm Ar cond set Ar COND Op COND ... .It Nm Ar cond set Ar COND
Set (assert) user-defined condition, Set (assert) user-defined condition,
.Cm +usr/COND .Cm +usr/COND
.It Nm Ar cond get Ar COND .It Nm Ar cond clr | clear Ar COND
Get (quietly) the status of any condition. Defaults to user-defined
condions, but if a slash is detected, e.g.,
.Cm pid/foo
or
.Cm net/eth0/exist ,
then any condition can be read.
.Pp
The command is geared for scripting, check the exit code to get the
status of the condition: 0 - on, 1 - off, 255 - flux. For a more
verbose output, use the
.Fl v
option.
.It Nm Ar cond clr | clear Ar COND Op COND ...
Clear (deassert) user-defined condition, Clear (deassert) user-defined condition,
.Cm -usr/COND .Cm -usr/COND
.It Nm Ar cond status .It Nm Ar cond status
Show condition status, default Show condition status, default
.Cm cond .Cm cond
command. Also supports the command.
.Fl j .It Nm Ar cond dump
option for detailed JSON output. Dump all conditions and their status
.It Nm Ar cond dump Op Cm TYPE
Dump all, or a given type of, conditions and their status. Also
supports the
.Fl j
option for detailed JSON output.
.It Nm Ar ident Op Cm NAME .It Nm Ar ident Op Cm NAME
Display indentities of all run/task/services, or only instances Display indentities of all run/task/services, or only instances
matching matching
.Cm NAME . .Cm NAME .
A partial string, e.g., A partial string, e.g.,
.Cm NAM , .Cm NAM ,
will not match anything. will not match anything
.It Nm Ar log Op Cm NAME .It Nm Ar log Op Cm NAME
Show ten last Finit, or Show ten last Finit, or
.Cm NAME , .Cm NAME ,
messages from syslog. messages from syslog
.It Nm Ar start Cm NAME[:ID] .It Nm Ar start Cm NAME[:ID]
Start service by name, with optional ID, e.g., Start service by name, with optional ID, e.g.,
.Cm initctl start tty:1 .Cm initctl start tty:1
.It Nm Ar stop Cm NAME[:ID] .It Nm Ar stop Cm NAME[:ID]
Stop/Pause a running service by name. Stop/Pause a running service by name
.It Nm Ar reload Cm NAME[:ID] .It Nm Ar reload Cm NAME[:ID]
Reload service as if its configuration had changed. Internally, Reload service by name (SIGHUP or restart)
.Nm finit
marks the named service as "dirty" and turns the state machine,
resulting in a SIGHUP or restart of (at least) the named service.
Any dependant services are also restarted.
.Pp
.Sy Note:
no .conf file is reloaded with this variant of the command. Essentially
it is the same as calling
.Cm restart .
Except for two things:
.Bl -enum -offset indent -compact
.It
if the service supports HUP, it's signaled instead of stop-started, and
.It
it allows restarting run/task in the same runlevel -- which is otherwise
restricted.
.El
.It Nm Ar signal Cm NAME:[ID] S .It Nm Ar signal Cm NAME:[ID] S
Send signal S to service, by name. Send signal S to service, by name.
.Cm S .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). 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] .It Nm Ar restart Cm NAME[:ID]
Restart (stop/start) service by name. Restart (stop/start) service by name
.It Nm Ar status Cm NAME[:ID] .It Nm Ar status Cm NAME[:ID]
Show service status, by name. If only Show service status, by name. If only
.Cm NAME .Cm NAME
is given and multiple instances exits, a summary of all matching is given and multiple instances exits, a summary of all matching
instances are shown. Only an exact match displays detailed status instances are shown. Only an exact match displays the detailed status
for a given instance. for a particular instance
.Pp
With the
.Fl q
option this command is silent, provided the
.Ar NAME[:ID]
selection matches a single run/task/service. The exit code of
.Nm
is non-zero if there is a problem. Zero is returned if a run/task has
run (at least) once in the current runlevel, and when a service is running
(as expected). See the
.Fl j
option for detailed JSON output suitable for machine parsing.
.Pp
.Sy Tip:
.Xr jq 1
is a useful scripting tool in combination with JSON output.
.It Nm Ar status .It Nm Ar status
Show status of all services, default command. Also supports the Show status of all services, default command
.Fl j
option for detailed JSON output.
.It Nm Ar cgroup .It Nm Ar cgroup
List cgroup config overview. List cgroup config overview
.It Nm Ar ps .It Nm Ar ps
List processes based on cgroups. List processes based on cgroups
.It Nm Ar top .It Nm Ar top
Show top-like listing based on cgroups. Show top-like listing based on cgroups
.It Nm Ar plugins
List installed plugins.
.It Nm Ar runlevel Op Ar 0-9 .It Nm Ar runlevel Op Ar 0-9
Show or set runlevel: 0 halt, 6 reboot. Show or set runlevel: 0 halt, 6 reboot
.Pp
If called at boot (runlevel S) to set the runlevel, Finit only schedules
the change, effectively overriding the configured runlevel from
.Pa /etc/finit.conf .
Useful to trigger a fail-safe mode, or similar.
.It Nm Ar reboot .It Nm Ar reboot
Reboot system, default if Reboot system, default if
.Cm reboot .Cm reboot
is symlinked to is symlinked to
.Nm . .Nm
.It Nm Ar halt .It Nm Ar halt
Halt system, default if Halt system, default if
.Cm halt .Cm halt
is symlinked to is symlinked to
.Nm . .Nm
.It Nm Ar poweroff .It Nm Ar poweroff
Power-off system, default if Power-off system, default if
.Cm poweroff .Cm poweroff
is symlinked to is symlinked to
.Nm . .Nm
.It Nm Ar suspend .It Nm Ar suspend
Suspend system, default if Suspend system, default if
.Cm suspend .Cm suspend
is symlinked to is symlinked to
.Nm . .Nm
.It Nm Ar utmp show .It Nm Ar utmp show
Raw dump of UTMP/WTMP db. Raw dump of UTMP/WTMP db
.El .El
.Sh SEE ALSO .Sh SEE ALSO
.Xr finit.conf 5 .Xr finit.conf 5
+8 -4
View File
@@ -1,9 +1,9 @@
pkglibdir = $(plugin_path) pkglibdir = $(plugin_path)
AM_LDFLAGS = -module -avoid-version -shared 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 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 += -D__FINIT__ $(lite_CFLAGS) $(uev_CFLAGS) AM_CPPFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
if STATIC if STATIC
noinst_LTLIBRARIES = libplug.la noinst_LTLIBRARIES = libplug.la
@@ -17,6 +17,10 @@ if BUILD_DBUS_PLUGIN
libplug_la_SOURCES += dbus.c libplug_la_SOURCES += dbus.c
endif endif
if BUILD_HOTPLUG_PLUGIN
libplug_la_SOURCES += hotplug.c
endif
if BUILD_MODULES_LOAD_PLUGIN if BUILD_MODULES_LOAD_PLUGIN
libplug_la_SOURCES += modules-load.c libplug_la_SOURCES += modules-load.c
endif endif
@@ -60,8 +64,8 @@ if BUILD_DBUS_PLUGIN
pkglib_LTLIBRARIES += dbus.la pkglib_LTLIBRARIES += dbus.la
endif endif
if BUILD_HOOK_SCRIPTS_PLUGIN if BUILD_HOTPLUG_PLUGIN
pkglib_LTLIBRARIES += hook-scripts.la pkglib_LTLIBRARIES += hotplug.la
endif endif
if BUILD_MODULES_LOAD_PLUGIN if BUILD_MODULES_LOAD_PLUGIN
+5 -5
View File
@@ -1,6 +1,6 @@
/* Save and restore ALSA sound settings using alsactl /* Save and restore ALSA sound settings using alsactl
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -36,12 +36,12 @@
static void save(void *arg) static void save(void *arg)
{ {
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
dbg("Saving sound settings ..."); _d("Saving sound settings ...");
run_interactive(ALSACTL " -g store", "Saving sound settings"); run_interactive(ALSACTL " -g store", "Saving sound settings");
} }
} }
@@ -49,12 +49,12 @@ static void save(void *arg)
static void restore(void *arg) static void restore(void *arg)
{ {
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
dbg("Restoring sound settings ..."); _d("Restoring sound settings ...");
run_interactive(ALSACTL " -g restore", "Restoring sound settings"); run_interactive(ALSACTL " -g restore", "Restoring sound settings");
} }
} }
+83 -51
View File
@@ -1,6 +1,6 @@
/* Setup necessary system files for, e.g. UTMP (tracking logins) /* Setup necessary system files for, e.g. UTMP (tracking logins)
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,12 +21,8 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */
#include <ftw.h> #include <ftw.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h> #include <mntent.h>
#endif
#include <string.h> #include <string.h>
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
@@ -38,18 +34,15 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "tmpfiles.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
static int is_tmpfs(char *path) static int is_tmpfs(char *path)
{ {
struct mntent mount;
struct mntent *mnt;
char buf[256];
int tmpfs = 0; int tmpfs = 0;
FILE *fp; FILE *fp;
char *dir; char *dir;
struct mntent *mnt;
/* If path is a symlink, check what it resolves to */ /* If path is a symlink, check what it resolves to */
dir = realpath(path, NULL); dir = realpath(path, NULL);
@@ -62,7 +55,7 @@ static int is_tmpfs(char *path)
return 0; /* Dunno, maybe not */ return 0; /* Dunno, maybe not */
} }
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) { while ((mnt = getmntent(fp))) {
if (!strcmp(dir, mnt->mnt_dir)) if (!strcmp(dir, mnt->mnt_dir))
break; break;
} }
@@ -81,7 +74,7 @@ static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct
if (ftw->level == 0) if (ftw->level == 0)
return 1; return 1;
dbg("Removing %s ...", fpath); _d("Removing %s ...", fpath);
(void)remove(fpath); (void)remove(fpath);
return 0; return 0;
@@ -111,43 +104,10 @@ static void clean(void *arg)
} }
} }
/* Kernel defines the following compulsory and recommended links static void ln(const char *target, const char *linkpath)
* https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links
*/
static void kernel_links(void)
{ {
struct { if (symlink(target, linkpath) && errno != EEXIST)
char *tgt, *lnk; _pe("Failed creating %s -> %s symlink", target, linkpath);
int optional;
} k[] = {
{ "/proc/self/fd", "/dev/fd", 0 },
{ "fd/0", "/dev/stdin", 0 },
{ "fd/1", "/dev/stdout", 0 },
{ "fd/2", "/dev/stderr", 0 },
{ "socksys", "/dev/nfsd", 0 },
{ "null", "/dev/X0R", 0 },
{ "/proc/kcore", "/dev/core", 1 },
{ "ram0", "/dev/ramdisk", 1 },
{ "qft0", "/dev/ftape", 1 },
{ "video0", "/dev/bttv0", 1 },
{ "radio0", "/dev/radio", 1 },
};
char *fn, buf[80];
size_t i;
for (i = 0; i < NELEMS(k); i++) {
fn = k[i].tgt;
if (k[i].optional) {
if (fn[0] != '/') {
snprintf(buf, sizeof(buf), "/dev/%s", k[i].tgt);
fn = buf;
}
if (!fexist(fn))
continue;
}
ln(k[i].tgt, k[i].lnk);
}
} }
/* /*
@@ -159,15 +119,87 @@ static void setup(void *arg)
prev = umask(0); prev = umask(0);
/* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */ _d("Setting up FHS structure in /var ...");
kernel_links(); makedir("/var/cache", 0755);
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755);
makedir("/var/lib", 0755);
makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
makedir("/var/lib/alarm", 0755);
makedir("/var/lib/urandom",0755);
if (fisdir("/run")) {
_d("System with new /run tmpfs ...");
if (!fisdir("/run/lock"))
makedir("/run/lock", 1777);
ln("/run/lock", "/var/lock");
ln("/dev/shm", "/run/shm");
/* Create all system tmpfiles.d(5) */ /* compat only, should really be set up by OS/dist */
tmpfilesd(); ln("../run", "/var/run");
} else {
makedir("/var/lock", 1777);
makedir("/var/run", 0755);
}
makedir("/var/log", 0755);
makedir("/var/mail", 0755);
makedir("/var/opt", 0755);
makedir("/var/spool", 0755);
makedir("/var/spool/cron", 0755);
makedir("/var/tmp", 0755);
makedir("/var/empty", 0755);
/*
* UTMP actually needs multiple db files
*/
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 ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
create("/var/log/lastlog", 0644, 0, gid);
}
/* Set BOOT_TIME UTMP entry */ /* Set BOOT_TIME UTMP entry */
utmp_set_boot(); utmp_set_boot();
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
touch("/etc/network/run/ifstate");
#else
erase("/etc/network/run/ifstate");
#endif
_d("Setting up misc files ...");
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
mksubsys("/var/run/frr", 0755, "frr", "frr");
mksubsys("/var/log/frr", 0755, "frr", "frr");
makedir("/var/run/sshd", 01755); /* OpenSSH */
if (!fexist("/etc/mtab"))
ln("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
/* Debian has /run/sudo, ensure correct perms and SELinux label */
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); umask(prev);
} }
+28 -103
View File
@@ -1,6 +1,6 @@
/* Setup and start system message bus, D-Bus /* Setup and start system message bus, D-Bus
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -35,135 +35,60 @@
#include "service.h" #include "service.h"
#include "conf.h" #include "conf.h"
#define DBUS_DAEMON "dbus-daemon" #define DAEMON "dbus-daemon"
#define DBUS_ARGS "--nofork --system --syslog-only" #define ARGS "--nofork --system --syslog-only"
#define DBUS_DESC "D-Bus message bus daemon" #define DESC "D-Bus message bus daemon"
#ifndef DBUS_DAEMONUSER #ifndef DAEMONUSER
#define DBUS_DAEMONUSER "messagebus" #define DAEMONUSER "messagebus"
#endif #endif
#ifndef DBUS_DAEMONGROUP #ifndef DAEMONPIDFILE
#define DBUS_DAEMONGROUP "messagebus" #define DAEMONPIDFILE "/var/run/dbus/pid"
#endif #endif
/*
* Dumnpster diving for the D-Bus main configuration file
* where <pidfile> is defined.
*/
static char *dbus_pidfn(void)
{
char *alt[] = {
"/usr/share/dbus-1/system.conf",
"/etc/dbus-1/system.conf",
NULL
};
int i;
for (i = 0; alt[i]; i++) {
char *fn = alt[i];
char buf[256];
FILE *fp;
fp = fopen(fn, "r");
if (!fp)
continue;
fn = NULL;
while (fgets(buf, sizeof(buf), fp)) {
char *pos;
pos = strstr(buf, "<pidfile>");
if (!pos)
continue;
fn = pos + strlen("<pidfile>");
pos = strstr(fn, "</pidfile>");
if (!pos) {
fn = NULL;
break;
}
*pos = 0;
break;
}
fclose(fp);
if (fn)
return strdup(fn);
}
return NULL;
}
static void setup(void *arg) static void setup(void *arg)
{ {
char *group = DBUS_DAEMONGROUP;
char *user = DBUS_DAEMONUSER;
char line[256]; char line[256];
char *pidfn;
mode_t prev; mode_t prev;
char *cmd; char *cmd;
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
cmd = which(DBUS_DAEMON); cmd = which(DAEMON);
if (!cmd) { if (!cmd) {
dbg("Skipping plugin, %s is not installed.", DBUS_DAEMON); _d("Skipping plugin, %s is not installed.", DAEMON);
return; return;
} }
if (getuser(user, NULL) == -1) { prev =umask(0);
if (getuser("dbus", NULL) == -1)
user = "root"; /* fallback */
else
user = "dbus"; /* e.g., Buildroot */
}
if (getgroup(group) == -1) { mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
if (getgroup("dbus") == -1) if (whichp("dbus-uuidgen"))
group = "root"; /* fallback */ run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
else
group = "dbus"; /* e.g., Buildroot */
}
/* Clean up from any previous pre-bootstrap run */ /* Clean up from any previous pre-bootstrap run */
pidfn = dbus_pidfn(); remove(DAEMONPIDFILE);
if (pidfn)
remove(pidfn);
dbg("Creating D-Bus Required Directories ..."); /* Register service with Finit */
prev = umask(0); snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
mksubsys("/var/run/dbus", 0755, user, group); DAEMONPIDFILE, DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
mksubsys("/var/run/lock/subsys", 0755, user, group); if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
mksubsys("/var/lib/dbus", 0755, user, group); _pe("Failed registering %s", DAEMON);
mksubsys("/tmp/dbus", 0755, user, group);
umask(prev);
/* Generate machine id for dbus */
if (whichp("dbus-uuidgen"))
run_interactive("dbus-uuidgen --ensure", "Verifying D-Bus machine UUID");
/*
* Register service with Finit
* Note: dbus drops privs after starting up.
*/
if (pidfn) {
snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus pid:!%s %s %s -- %s",
pidfn, cmd, DBUS_ARGS, DBUS_DESC);
free(pidfn);
} else
snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus %s %s -- %s",
cmd, DBUS_ARGS, DBUS_DESC);
conf_save_service(SVC_TYPE_SERVICE, line, "dbus.conf");
free(cmd); free(cmd);
umask(prev);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_SVC_PLUGIN] = { .cb = setup }, .hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
-117
View File
@@ -1,117 +0,0 @@
/* Trigger scripts to run from finit's hooks
*
* Copyright (c) 2022 Tobias Waldekranz <tobias@waldekranz.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 "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "config.h"
extern void plugin_script_run(hook_point_t no);
static void hscript_run_parts(hook_point_t no)
{
plugin_script_run(no);
}
static void hscript_banner(void *arg)
{
hscript_run_parts(HOOK_BANNER);
}
static void hscript_rootfs_up(void *arg)
{
hscript_run_parts(HOOK_ROOTFS_UP);
}
static void hscript_mount_error(void *arg)
{
hscript_run_parts(HOOK_MOUNT_ERROR);
}
static void hscript_mount_post(void *arg)
{
hscript_run_parts(HOOK_MOUNT_POST);
}
static void hscript_basefs_up(void *arg)
{
hscript_run_parts(HOOK_BASEFS_UP);
}
static void hscript_network_up(void *arg)
{
hscript_run_parts(HOOK_NETWORK_UP);
}
static void hscript_svc_up(void *arg)
{
hscript_run_parts(HOOK_SVC_UP);
}
static void hscript_system_up(void *arg)
{
hscript_run_parts(HOOK_SYSTEM_UP);
}
static void hscript_shutdown(void *arg)
{
hscript_run_parts(HOOK_SHUTDOWN);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BANNER] = { .cb = hscript_banner },
.hook[HOOK_ROOTFS_UP] = { .cb = hscript_rootfs_up },
.hook[HOOK_MOUNT_ERROR] = { .cb = hscript_mount_error },
.hook[HOOK_MOUNT_POST] = { .cb = hscript_mount_post },
.hook[HOOK_BASEFS_UP] = { .cb = hscript_basefs_up },
.hook[HOOK_NETWORK_UP] = { .cb = hscript_network_up },
.hook[HOOK_SVC_UP] = { .cb = hscript_svc_up },
.hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up },
.hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown },
/*
* We have exited here, so these are handled a bit differently by Finit,
* when hook scripts are enabled, just to provide the user with the same
* interface:
*
* .hook[HOOK_SVC_DN] = { .cb = hscript_svc_down },
* .hook[HOOK_SYS_DN] = { .cb = hscript_system_down },
*/
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+116
View File
@@ -0,0 +1,116 @@
/* Heuristically find and initialize a suitable hotplug daemon
*
* Copyright (c) 2012-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 <stdio.h>
#include "config.h"
#include "conf.h"
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "service.h"
static void setup(void *arg)
{
char cmd[256];
char *path;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
/*
* Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system.
*/
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
/* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s "
"-- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
} else {
snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log "
"udevadm trigger -c add -t devices "
"-- Requesting device events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log "
"udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
}
free(path);
/* Debian has this little script to copy generated
* rules while the system was read-only. TODO: When
* this functionality was hardcoded in finit.c, this
* call was made in crank_worker(). Now that
* filesystems are mounted earlier we should be able
* to make this call directly after the triggers have
* run, but this has not been tested AT ALL. */
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
} else {
path = which("mdev");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (debug)
touch("/dev/mdev.log");
snprintf(cmd, sizeof(cmd), "%s -s", path);
free(path);
run_interactive(cmd, "Populating device tree");
}
}
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc" },
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+13 -72
View File
@@ -1,6 +1,6 @@
/* Coldplug modules using modalias magic /* Coldplug modules using modalias magic
* *
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -71,14 +71,14 @@ static int modprobe(char *alias)
pid = fork(); pid = fork();
switch (pid) { switch (pid) {
case -1: case -1:
err(1, "Failed forking modprobe child"); _pe("Failed forking modprobe child");
return 1; return 1;
case 0: case 0:
execvp(args[0], args); execvp(args[0], args);
break; break;
default: default:
if (!complete(args[0], pid)) if (!complete(args[0], pid))
dbg("Successful modprobe of %s", alias); _d("Successful modprobe of %s", alias);
break; break;
} }
@@ -106,7 +106,7 @@ static void alias_remove(struct module *m)
TAILQ_REMOVE(&modules, m, link); TAILQ_REMOVE(&modules, m, link);
free(m->alias); free(m->alias);
free(m); free(m);
} }
static int alias_exist(char *alias) static int alias_exist(char *alias)
{ {
@@ -120,86 +120,27 @@ static int alias_exist(char *alias)
return 0; return 0;
} }
static void alias_add_uniq(char *alias) static int scan_alias(const char *path, const struct stat *st, int flag, struct FTW *unused)
{ {
if (alias_exist(alias))
return;
alias_add(alias);
}
static FILE *maybe_fopen_alias(const char *file, const char *path)
{
const char *basename;
basename = basenm(path);
if (!strcmp(basename, path))
return NULL;
if (strcmp(file, basename))
return NULL;
return fopen(path, "r");
}
static int scan_modalias(const char *path)
{
char buf[256];
FILE *fp; FILE *fp;
char buf[256];
fp = maybe_fopen_alias("modalias", path); if (fnmatch("*/modalias", path, 0))
return 0;
fp = fopen(path, "r");
if (!fp) if (!fp)
return 1; return 0;
if (fgets(buf, sizeof(buf), fp)) { if (fgets(buf, sizeof(buf), fp)) {
chomp(buf); chomp(buf);
alias_add_uniq(buf); if (!alias_exist(buf))
alias_add(buf);
} }
fclose(fp); fclose(fp);
return 0; return 0;
} }
static int scan_uevent(const char *path)
{
char buf[256];
FILE *fp;
fp = maybe_fopen_alias("uevent", path);
if (!fp)
return 1;
while (fgets(buf, sizeof(buf), fp)) {
if (strstr(buf, "MODALIAS=") != buf)
continue;
chomp(buf);
alias_add_uniq(buf + strlen("MODALIAS="));
break;
}
fclose(fp);
return 0;
}
static int scan_alias(const char *path, const struct stat *st,
int flag, struct FTW *unused)
{
/* Sanity check: is this a regular file we can read? */
if (!(S_ISREG(st->st_mode) && (st->st_mode & S_IRUSR)))
return 0;
if (!scan_uevent(path))
return 0;
scan_modalias(path);
return 0;
}
static void coldplug(void *arg) static void coldplug(void *arg)
{ {
struct module *m, *tmp; struct module *m, *tmp;
+48 -164
View File
@@ -21,29 +21,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/*
* This plugin scans files in /etc/modules-load.d/, for each line in the
* file it assumes the name of a kernel module, with optional arguments.
* Each module is loaded when entering runlevel 2, 3, 4, or 5, using the
* modprobe tool.
*
* Loading is done by inserting a `task` stanza in Finit, ensuring all
* modules are loaded in parallel, with other modules and programs.
* Each `task` stanza is by default given the name:modprobe.module and
* indexed with `:ID`, starting with 1.
*
* Indexing can be disabled per file in /etc/modules-load.d/, anywwhere
* in a file, by putting the keyword `noindex` on a line of its own. A
* noindex command can appear anywhere in the file, and up to the point
* it is read, tasks will be indexed with an incrementing `:ID`. When
* `noindex` is read indexing is disabled and all subseequent tasks will
* not have any `:ID` at all.
*
* Please note, the :ID is there for your benefit, it ensures that tasks
* in Finit are unique. If you have two tasks with the same name and ID
* (or no ID), the last one read replaces any preceeding one!
*/
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
@@ -62,158 +39,65 @@
#ifndef MODULES_LOAD_PATH #ifndef MODULES_LOAD_PATH
#define MODULES_LOAD_PATH "/etc/modules-load.d" #define MODULES_LOAD_PATH "/etc/modules-load.d"
#endif #endif
#define MODPROBE_PATH "/sbin/modprobe"
#define SERVICE_LINE \ #define SERVICE_LINE \
"cgroup.init name:modprobe.%s :%d [%s] %s %s %s --" "cgroup.init :%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
#define SERVICE_LINE_NOINDEX \
"cgroup.init name:modprobe.%s [%s] %s %s %s --"
static int modules_load(const char *file, int index)
{
char module_path[PATH_MAX];
char *modprobe_path;
int num = 0;
char *line;
char *lvl;
FILE *fp;
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, file, sizeof(module_path));
fp = fopen(module_path, "r");
if (!fp) {
warnx("failed opening %s for reading, skipping ...", module_path);
return 0;
}
modprobe_path = strdup(MODPROBE_PATH);
if (!modprobe_path) {
fail:
warnx("failed allocating memory in modules-load plugin.");
fclose(fp);
return -1;
}
lvl = strdup("S");
if (!lvl) {
free(modprobe_path);
goto fail;
}
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
char cmd[CMD_SIZE], *mod, *args, *set;
/*
* fparseln() skips regular UNIX comments only.
* This is for modules-load.d(5) compat.
*/
if (line[0] == ';')
goto next;
/* trim whitespace */
mod = strip_line(line);
if (!mod[0])
goto next;
/* Finit extension 'set foo bar' */
if ((set = fgetval(mod, "set", "= \t"))) {
char *val = mod;
if (!strcmp(set, "noindex")) {
index = 0;
free(set);
goto next;
}
if ((val = fgetval(set, "index", "= \t"))) {
index = atoi(val);
free(set);
free(val);
goto next;
}
if ((val = fgetval(set, "runlevel", "= \t"))) {
free(lvl);
lvl = val;
free(set);
goto next;
}
if ((val = fgetval(set, "modprobe", "= \t"))) {
if (access(val, X_OK)) {
warn("%s: cannot use %s", module_path, val);
free(set);
free(val);
free(line);
goto skip;
} else {
free(modprobe_path);
modprobe_path = val;
}
free(set);
goto next;
}
/* unknown 'set' command, ignore. */
goto next;
}
mod = strtok_r(mod, " ", &args);
if (!mod)
goto next;
if (!index)
snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, lvl, modprobe_path, mod, args);
else
snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, lvl, modprobe_path, mod, args);
dbg("task %s", cmd);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
num++;
next:
free(line);
}
skip:
free(modprobe_path);
free(lvl);
fclose(fp);
return num;
}
/* XXX: check here for .conf only? */
static int module_filter(const struct dirent *d)
{
if (d->d_type == DT_DIR)
return 0;
if (d->d_name[0] == '.')
return 0; /* skip dot files */
if (d->d_name[strlen(d->d_name) - 1] == '~')
return 0; /* skip backup files */
return 1;
}
static void load(void *arg) static void load(void *arg)
{ {
struct dirent **dentry; struct dirent *d;
char line[256];
int index = 1; int index = 1;
int i, num; FILE *fp;
DIR *dir;
dbg("Scanning " MODULES_LOAD_PATH " for config files ..."); _d("Scanning " MODULES_LOAD_PATH " for config files ...");
num = scandir(MODULES_LOAD_PATH, &dentry, module_filter, alphasort);
if (num > 0) { dir = opendir(MODULES_LOAD_PATH);
for (i = 0; i < num; i++) { if (!dir)
index += modules_load(dentry[i]->d_name, index); return;
free(dentry[i]);
while ((d = readdir(dir))) {
char module_path[PATH_MAX];
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, d->d_name, sizeof(module_path));
fp = fopen(module_path, "r");
if (!fp)
continue;
while (fgets(line, sizeof(line), fp)) {
char cmd[CMD_SIZE], *mod, *args;
mod = strip_line(line);
if (!*mod)
continue;
mod = chomp(mod);
if (!mod || !*mod)
continue;
mod = strtok_r(mod, " ", &args);
if (!mod)
continue;
snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
index++;
} }
free(dentry);
fclose(fp);
} }
closedir(dir);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_SVC_PLUGIN] = { .cb = load }, .hook[HOOK_BASEFS_UP] = {
.cb = load
},
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+36 -38
View File
@@ -1,8 +1,7 @@
/* Netlink plugin for IFUP/IFDN and GW events /* Netlink plugin for IFUP/IFDN and GW events
* *
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se> * Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
* Copyright (C) 2009-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (C) 2009-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -62,7 +61,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
int la; int la;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
errx(1, "Packet too small or truncated!"); _e("Packet too small or truncated!");
return; return;
} }
@@ -70,7 +69,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
a = RTM_RTA(r); a = RTM_RTA(r);
la = RTM_PAYLOAD(nlmsg); la = RTM_PAYLOAD(nlmsg);
if (la >= len) { if (la >= len) {
errx(1, "Packet too large!"); _e("Packet too large!");
return; return;
} }
@@ -80,18 +79,18 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
switch (a->rta_type) { switch (a->rta_type) {
case RTA_GATEWAY: case RTA_GATEWAY:
gw = *((int *)data); gw = *((int *)data);
//dbg("GW: 0x%04x", gw); //_d("GW: 0x%04x", gw);
break; break;
case RTA_DST: case RTA_DST:
dst = *((int *)data); dst = *((int *)data);
plen = r->rtm_dst_len; plen = r->rtm_dst_len;
//dbg("Prefix LEN: 0x%04x", plen); //_d("Prefix LEN: 0x%04x", plen);
break; break;
case RTA_OIF: case RTA_OIF:
idx = *((int *)data); idx = *((int *)data);
//dbg("IDX: 0x%04x", idx); //_d("IDX: 0x%04x", idx);
break; break;
} }
@@ -102,7 +101,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
ing.s_addr = gw; ing.s_addr = gw;
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr)); inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr)); inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
dbg("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx); _d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
if ((!dst && !plen) && (gw || idx)) { if ((!dst && !plen) && (gw || idx)) {
if (nlmsg->nlmsg_type == RTM_DELROUTE) { if (nlmsg->nlmsg_type == RTM_DELROUTE) {
@@ -167,7 +166,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
int la; int la;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
errx(1, "Packet too small or truncated!"); _e("Packet too small or truncated!");
return; return;
} }
@@ -175,7 +174,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
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) { if (la >= len) {
errx(1, "Packet too large!"); _e("Packet too large!");
return; return;
} }
@@ -185,7 +184,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
strlcpy(ifname, RTA_DATA(a), sizeof(ifname)); strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
if (validate_ifname(ifname)) { if (validate_ifname(ifname)) {
dbg("Invalid interface name '%s', skipping ...", ifname); _d("Invalid interface name '%s', skipping ...", ifname);
continue; continue;
} }
@@ -195,7 +194,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
* New interface has appeared, or interface flags has changed. * New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN * Check ifi_flags here to see if the interface is UP/DOWN
*/ */
dbg("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change); _d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
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);
@@ -205,7 +204,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
case RTM_DELLINK: case RTM_DELLINK:
/* NOTE: Interface has disappeared, not link down ... */ /* NOTE: Interface has disappeared, not link down ... */
dbg("%s: Delete link", ifname); _d("%s: Delete link", ifname);
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);
@@ -213,15 +212,15 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
break; break;
case RTM_NEWADDR: case RTM_NEWADDR:
dbg("%s: New Address", ifname); _d("%s: New Address", ifname);
break; break;
case RTM_DELADDR: case RTM_DELADDR:
dbg("%s: Deconfig Address", ifname); _d("%s: Deconfig Address", ifname);
break; break;
default: default:
dbg("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type); _d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
break; break;
} }
} }
@@ -246,46 +245,46 @@ static int nl_parse(int sd)
break; break;
default: default:
err(1, "recv()"); _pe("recv()");
break; break;
} }
return -1; return -1;
} }
// dbg("recv %zd bytes", len); _d("recv %lld bytes", len);
l = (size_t)len; l = (size_t)len;
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) { for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
struct nlmsgerr *nle; struct nlmsgerr *nle;
switch (nh->nlmsg_type) { switch (nh->nlmsg_type) {
case NLMSG_DONE: case NLMSG_DONE:
// dbg("Done with netlink messages."); _d("Done with netlink messages.");
return 0; return 0;
case NLMSG_ERROR: case NLMSG_ERROR:
// dbg("Kernel netlink comm. error."); _d("Kernel netlink comm. error.");
nle = NLMSG_DATA(nh); nle = NLMSG_DATA(nh);
if (nle) { if (nle) {
errno = -nle->error; errno = -nle->error;
err(1, "Kernel netlink error %d", errno); _pe("Kernel netlink error %d", errno);
} }
return -1; return -1;
case RTM_NEWROUTE: case RTM_NEWROUTE:
case RTM_DELROUTE: case RTM_DELROUTE:
// dbg("Netlink route ..."); _d("Netlink route ...");
nl_route(nh, len); nl_route(nh, len);
break; break;
case RTM_NEWLINK: case RTM_NEWLINK:
case RTM_DELLINK: case RTM_DELLINK:
// dbg("Netlink link ..."); _d("Netlink link ...");
nl_link(nh, len); nl_link(nh, len);
break; break;
default: default:
warnx("unhandled netlink message, type %d", nh->nlmsg_type); _w("unhandled netlink message, type %d", nh->nlmsg_type);
break; break;
} }
} }
@@ -304,21 +303,21 @@ static int nl_request(int sd, unsigned int seq, int type)
switch (type) { switch (type) {
case RTM_GETROUTE: case RTM_GETROUTE:
// dbg("RTM_GETROUTE"); _d("RTM_GETROUTE");
nlr->rtm.rtm_family = AF_INET; nlr->rtm.rtm_family = AF_INET;
nlr->rtm.rtm_table = RT_TABLE_MAIN; nlr->rtm.rtm_table = RT_TABLE_MAIN;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
break; break;
case RTM_GETLINK: case RTM_GETLINK:
// dbg("RTM_GETLINK"); _d("RTM_GETLINK");
nlr->ifi.ifi_family = AF_UNSPEC; nlr->ifi.ifi_family = AF_UNSPEC;
nlr->ifi.ifi_change = 0xFFFFFFFF; nlr->ifi.ifi_change = 0xFFFFFFFF;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
break; break;
default: default:
warnx("Cannot resync, unhandled message type %d", type); _w("Cannot resync, unhandled message type %d", type);
return -1; return -1;
} }
@@ -331,13 +330,13 @@ static int nl_request(int sd, unsigned int seq, int type)
static void nl_resync_routes(int sd, unsigned int seq) static void nl_resync_routes(int sd, unsigned int seq)
{ {
if (nl_request(sd, seq, RTM_GETROUTE)) if (nl_request(sd, seq, RTM_GETROUTE))
err(1, "Failed netlink route request"); _pe("Failed netlink route request");
} }
static void nl_resync_ifaces(int sd, unsigned int seq) static void nl_resync_ifaces(int sd, unsigned int seq)
{ {
if (nl_request(sd, seq, RTM_GETLINK)) if (nl_request(sd, seq, RTM_GETLINK))
err(1, "Failed netlink link request"); _pe("Failed netlink link request");
} }
/* /*
@@ -350,12 +349,12 @@ static void nl_resync(int all)
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (sd < 0) { if (sd < 0) {
err(1, "netlink socket"); _pe("netlink socket");
return; return;
} }
if (all) { if (all) {
dbg("============================ RESYNC ================================="); _d("============================ RESYNC =================================");
/* this doesn't update conditions, and thus does not stop services */ /* this doesn't update conditions, and thus does not stop services */
cond_deassert("net/"); cond_deassert("net/");
@@ -364,7 +363,7 @@ static void nl_resync(int all)
/* delayed update after we've corrected things */ /* delayed update after we've corrected things */
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
dbg("=========================== RESYNCED ================================"); _d("=========================== RESYNCED ================================");
} else } else
nl_resync_routes(sd, seq++); nl_resync_routes(sd, seq++);
@@ -375,7 +374,7 @@ static void nl_callback(void *arg, int sd, int events)
{ {
if (nl_parse(sd) < 0) { if (nl_parse(sd) < 0) {
if (errno == ENOBUFS) { /* netlink(7) */ if (errno == ENOBUFS) { /* netlink(7) */
warnx("busy system, resynchronizing with kernel."); _w("busy system, resynchronizing with kernel.");
nl_resync(1); nl_resync(1);
return; return;
} }
@@ -386,7 +385,7 @@ static void nl_callback(void *arg, int sd, int events)
* we need to check ourselves, e.g. for loss of default route. * we need to check ourselves, e.g. for loss of default route.
*/ */
if (nl_ifdown) { if (nl_ifdown) {
dbg("interface down, checking default route."); _d("interface down, checking default route.");
if (nl_defidx > 0) { if (nl_defidx > 0) {
nl_defidx = 0; nl_defidx = 0;
nl_resync(0); nl_resync(0);
@@ -421,7 +420,7 @@ PLUGIN_INIT(plugin_init)
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) {
err(1, "socket()"); _pe("socket()");
return; return;
} }
@@ -431,14 +430,14 @@ PLUGIN_INIT(plugin_init)
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) {
err(1, "bind()"); _pe("bind()");
close(sd); close(sd);
return; return;
} }
nl_buf = malloc(NL_BUFSZ); nl_buf = malloc(NL_BUFSZ);
if (!nl_buf) { if (!nl_buf) {
err(1, "malloc()"); _pe("malloc()");
close(sd); close(sd);
return; return;
} }
@@ -450,7 +449,6 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(plugin_exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
close(plugin.io.fd);
} }
/** /**
+28 -61
View File
@@ -1,7 +1,7 @@
/* Simple pidfile event monitor for the Finit condition engine /* Simple pidfile event monitor for the Finit condition engine
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
ptr = slash++; ptr = slash++;
slash = strchr(ptr, '/'); slash = strchr(ptr, '/');
if (slash) { if (slash) {
dbg("Path too deep, skipping %s", path); _d("Path too deep, skipping.");
return -1; return -1;
} }
} }
@@ -66,78 +66,48 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
char fn[PATH_MAX]; char fn[PATH_MAX];
svc_t *svc; svc_t *svc;
char *nm;
paste(fn, sizeof(fn), dir, name); paste(fn, sizeof(fn), dir, name);
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0)) if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
return; return;
dbg("path: %s, mask: %08x", fn, mask); _d("path: %s, mask: %08x", fn, mask);
svc = svc_find_by_pidfile(fn); svc = svc_find_by_pidfile(fn);
if (!svc) { if (!svc) {
dbg("No matching svc for %s", fn); _d("No matching svc for %s", fn);
return; return;
} }
/* Service condistion to set/clear */ _d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
nm = svc_ident(svc, NULL, 0);
dbg("Found svc %s for %s with pid %d", nm, fn, svc->pid);
if (svc_is_stopping(svc)) {
dbg("Ignoring pidfile death rattles while service %s is %s", nm, svc_status(svc));
goto sneaky_zebra;
}
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) { if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
/* svc_started(svc);
* only mark native services as started, the s6 and
* systemd style services rely on their respective
* readiness notification. Issue #343
*/
if (svc->notify == SVC_NOTIFY_PID)
svc_started(svc);
if (!svc_has_pidfile(svc)) { if (!svc_has_pidfile(svc)) {
dbg("Setting %s PID file to %s", svc->name, fn); _d("Setting %s PID file to %s", svc->name, fn);
pid_file_set(svc, fn, 1); 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(fn); pid = pid_file_read(pid_file(svc));
if (pid == -1) { if (pid == -1) {
/* handle case when service dies immediately */ cond_clear(cond);
if (!fexist(fn))
cond_clear(cond);
return; return;
} }
/* Service is not crashing, let's tell state machine! */
service_forked(svc);
if (pid != svc->pid) { if (pid != svc->pid) {
dbg("Forking service %s (cmd %s) changed PID from %d to %d", _d("Forking service %s changed PID from %d to %d",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid); svc->cmd, svc->pid, pid);
svc->pid = pid; svc->pid = pid;
/* Complement log in service.c for non-forking services */
logit(LOG_CONSOLE | LOG_NOTICE, "Started %s[%d]", svc_ident(svc, NULL, 0), pid);
} }
} }
if (svc->notify == SVC_NOTIFY_PID) cond_set(cond);
cond_set(cond); } else if (mask & IN_DELETE)
if (svc->notify == SVC_NOTIFY_PID || svc->notify == SVC_NOTIFY_NONE)
service_ready(svc, 1);
} else if (mask & IN_DELETE) {
sneaky_zebra:
cond_clear(cond); cond_clear(cond);
if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc, 0);
}
} }
/* synthesize events in case of new run dirs */ /* synthesize events in case of new run dirs */
@@ -159,7 +129,7 @@ static void pidfile_scandir(struct iwatch *iw, char *dir, int len)
return; return;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
dbg("scan found %s", gl.gl_pathv[i]); _d("scan found %s", gl.gl_pathv[i]);
pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE); pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
} }
globfree(&gl); globfree(&gl);
@@ -174,7 +144,7 @@ static void pidfile_handle_dir(struct iwatch *iw, char *dir, char *name, int mas
struct iwatch_path *iwp; struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name); paste(path, sizeof(path), dir, name);
dbg("path: %s", path); _d("path: %s", path);
iwp = iwatch_find_by_path(iw, path); iwp = iwatch_find_by_path(iw, path);
@@ -198,7 +168,7 @@ static void pidfile_callback(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
err(1, "invalid inotify event"); _pe("invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -250,9 +220,6 @@ static void pidfile_reconf(void *arg)
if (svc_is_changed(svc) || svc_is_starting(svc)) if (svc_is_changed(svc) || svc_is_starting(svc))
continue; continue;
if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc, 1);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
if (cond_get(cond) == COND_ON) if (cond_get(cond) == COND_ON)
continue; continue;
@@ -261,9 +228,9 @@ static void pidfile_reconf(void *arg)
} }
/* /*
* This calls service_step(), which in turn schedules itself as * This will call service_step(), which in turn will schedule itself
* long as stepped services change state. Services going from * as long as stepped services change state. Services going from
* PAUSED to RUNNING reassert their conditions in that loop, * WAITING to RUNNING will reassert their conditions in that loop,
* which in turn may unlock other services, and so on. * which in turn may unlock other services, and so on.
*/ */
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK); service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
@@ -275,7 +242,7 @@ static void pidfile_init(void *arg)
char *path; char *path;
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) { if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
err(1, "Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID); _pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
return; return;
} }
@@ -286,10 +253,10 @@ static void pidfile_init(void *arg)
*/ */
path = realpath(_PATH_VARRUN, NULL); path = realpath(_PATH_VARRUN, NULL);
if (!path) { if (!path) {
dbg("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno)); _d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
path = realpath("/run", NULL); path = realpath("/run", NULL);
if (!path) { if (!path) {
errx(1, "System does not have %s or /run, aborting.", _PATH_VARRUN); _e("System does not have %s or /run, aborting.", _PATH_VARRUN);
return; return;
} }
} }
@@ -310,15 +277,15 @@ static void pidfile_init(void *arg)
* *
* service <net/iface/lo> /sbin/dropbear ... * service <net/iface/lo> /sbin/dropbear ...
* *
* This service provides the <pid/dropbear> condition, which will not be * Which provides the <pid/dropbear> condition, will not be set by
* set by pidfile.so during `initctl reload` because dropbear is still * pidfile.so during `initctl reload` because dropbear is still
* SIGSTP:ed (in state PAUSED) waiting for <net/iface/lo>. * SIGSTP:ed waiting for <net/iface/lo>.
*/ */
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init }, .hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf }, .hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
.depends = { "netlink" }, /* bootmisc depends on us */ .depends = { "netlink" },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
@@ -338,8 +305,8 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(plugin_exit)
{ {
plugin_unregister(&plugin);
iwatch_exit(&iw_pidfile); iwatch_exit(&iw_pidfile);
plugin_unregister(&plugin);
} }
/** /**
+2 -2
View File
@@ -1,6 +1,6 @@
/* Set kernel variables from /etc/sysctl.conf et al /* Set kernel variables from /etc/sysctl.conf et al
* *
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@ static void setup(void *arg)
glob_t gl; glob_t gl;
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
+3 -3
View File
@@ -1,6 +1,6 @@
/* Setup necessary files for resolvconf /* Setup necessary files for resolvconf
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@
*/ */
static void setup(void *arg) static void setup(void *arg)
{ {
dbg("Setting up the resolver ..."); _d("Setting up the resolver ...");
mkpath("/etc/resolvconf", 0755); mkpath("/etc/resolvconf", 0755);
mkpath("/etc/resolvconf/run", 0755); mkpath("/etc/resolvconf/run", 0755);
mkpath("/var/run/resolvconf", 0755); mkpath("/var/run/resolvconf", 0755);
@@ -45,7 +45,7 @@ static void setup(void *arg)
touch("/etc/resolvconf/run/enable-updates"); touch("/etc/resolvconf/run/enable-updates");
chdir("/etc/resolvconf/run/interface"); chdir("/etc/resolvconf/run/interface");
run_parts("/etc/resolvconf/update.d", "-i", NULL, 0, 0); run_parts("/etc/resolvconf/update.d", "-i");
chdir("/"); chdir("/");
} }
+64 -198
View File
@@ -1,6 +1,6 @@
/* Save and restore RTC using hwclock /* Save and restore RTC using hwclock
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,8 +21,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h"
#include <time.h> #include <time.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/rtc.h> #include <linux/rtc.h>
@@ -36,25 +34,8 @@
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
/* /* Kernel RTC driver validates against this date for sanity check */
* Kernel RTC driver validates against this date for sanity check. The #define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */
* on NTP sync the driver can also update the RTC every 11 mins. We use
* the same update interval to handle manual time set and file save.
*/
#define RTC_TIMESTAMP_BEGIN_2000 "2000-01-01 00:00:00"
#define RTC_FMT "%Y-%m-%d %H:%M:%S"
#define RTC_PERIOD (11 * 60 * 1000)
#ifdef RTC_TIMESTAMP_CUSTOM
static char *rtc_timestamp = RTC_TIMESTAMP_CUSTOM;
#else
static char *rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
#endif
static time_t rtc_date_fallback = 946684800LL;
static char *rtc_file = RTC_FILE;
static uev_t rtc_timer;
static void tz_set(char *tz, size_t len) static void tz_set(char *tz, size_t len)
{ {
@@ -79,125 +60,6 @@ static void tz_restore(char *tz)
tzset(); tzset();
} }
static int time_set(struct tm *tm)
{
struct tm fallback = { 0 };
struct timeval tv = { 0 };
char tz[128];
int rc = 0;
tz_set(tz, sizeof(tz));
if (!tm) {
logit(LOG_NOTICE, "Resetting system clock to kernel default, %s.", rtc_timestamp);
tm = &fallback;
/* Attempt to set RTC to a sane value ... */
tv.tv_sec = rtc_date_fallback;
if (!gmtime_r(&tv.tv_sec, tm)) {
rc = 1;
goto out;
}
}
tm->tm_isdst = -1; /* Use tzdata to figure it out, please. */
tv.tv_sec = mktime(tm);
if (tv.tv_sec == (time_t)-1 || tv.tv_sec < rtc_date_fallback) {
errno = EINVAL;
rc = 2;
} else {
if (settimeofday(&tv, NULL) == -1)
rc = 1;
}
out:
tz_restore(tz);
return rc;
}
static int time_get(struct tm *tm)
{
struct timeval tv = { 0 };
char tz[128];
int rc = 0;
tz_set(tz, sizeof(tz));
rc = gettimeofday(&tv, NULL);
if (rc < 0 || tv.tv_sec < rtc_date_fallback)
rc = 2;
else
gmtime_r(&tv.tv_sec, tm);
tz_restore(tz);
return rc;
}
static void file_save(void *arg)
{
struct tm tm = { 0 };
int rc = 0;
FILE *fp;
if (!rtc_file)
return;
fp = fopen(rtc_file, "w");
if (!fp) {
logit(LOG_WARNING, "Failed saving system clock to %s, code %d: %s",
rtc_file, errno, strerror(errno));
return;
}
if ((rc = time_get(&tm))) {
logit(LOG_WARNING, "System clock invalid, before %s, not saving", rtc_timestamp);
print_desc(NULL, "System clock invalid, skipping");
} else {
char buf[32] = { 0 };
print_desc(NULL, "Saving system clock to file");
strftime(buf, sizeof(buf), RTC_FMT, &tm);
fprintf(fp, "%s\n", buf);
}
print(rc, NULL);
fclose(fp);
}
static void file_restore(void *arg)
{
struct tm tm = { 0 };
int rc = 1;
FILE *fp;
if (!rtc_file) {
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore.");
return;
}
print_desc(NULL, "Restoring system clock from backup");
fp = fopen(rtc_file, "r");
if (fp) {
char buf[32];
if (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
strptime(buf, RTC_FMT, &tm);
rc = time_set(&tm);
}
fclose(fp);
} else
logit(LOG_WARNING, "Missing %s", rtc_file);
if (rc) {
time_set(NULL);
rc = 2;
}
print(rc, NULL);
}
static int rtc_open(void) static int rtc_open(void)
{ {
char *alt[] = { char *alt[] = {
@@ -221,11 +83,13 @@ static int rtc_open(void)
static void rtc_save(void *arg) static void rtc_save(void *arg)
{ {
struct timeval tv = { 0 };
struct tm tm = { 0 }; struct tm tm = { 0 };
int fd, rc = 0; int fd, rc = 0;
char tz[128];
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
@@ -233,106 +97,108 @@ static void rtc_save(void *arg)
if (fd < 0) if (fd < 0)
return; return;
if ((rc = time_get(&tm))) { tz_set(tz, sizeof(tz));
rc = gettimeofday(&tv, NULL);
if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
print_desc(NULL, "System clock invalid, not saving to RTC"); print_desc(NULL, "System clock invalid, not saving to RTC");
} else { invalid:
print_desc(NULL, "Saving system clock (UTC) to RTC"); logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC");
rc = ioctl(fd, RTC_SET_TIME, &tm);
}
if (rc && errno == EINVAL) {
logit(LOG_WARNING, "System clock invalid, before %s, not saving to RTC", rtc_timestamp);
rc = 2; rc = 2;
goto out;
} }
print_desc(NULL, "Saving system time (UTC) to RTC");
gmtime_r(&tv.tv_sec, &tm);
if (ioctl(fd, RTC_SET_TIME, &tm) < 0) {
if (EINVAL == errno)
goto invalid;
rc = 1;
goto out;
}
out:
tz_restore(tz);
print(rc, NULL); print(rc, NULL);
close(fd); close(fd);
} }
static void rtc_restore(void *arg) static void rtc_restore(void *arg)
{ {
struct timeval tv = { 0 };
struct tm tm = { 0 }; struct tm tm = { 0 };
int fd, rc = 0; int fd, rc = 0;
char tz[128];
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
fd = rtc_open(); fd = rtc_open();
if (fd < 0) { if (fd < 0)
file_restore(arg);
return; return;
}
if ((rc = ioctl(fd, RTC_RD_TIME, &tm)) < 0) { tz_set(tz, sizeof(tz));
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
char msg[120]; char msg[120];
snprintf(msg, sizeof(msg), "Failed restoring system clock, %s", snprintf(msg, sizeof(msg), "Failed restoring system clock, %s",
EINVAL == errno ? "RTC time is too old" : EINVAL == errno ? "RTC time is too old" :
ENOENT == errno ? "RTC has no saved time" : "see log for details"); ENOENT == errno ? "RTC has no saved time" : "see log for details");
print_desc(NULL, msg); print_desc(NULL, msg);
} else {
print_desc(NULL, "Restoring system clock (UTC) from RTC"); invalid:
rc = time_set(&tm); logit(LOG_ERR, "Failed restoring system clock from RTC.");
if (EINVAL == errno)
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)");
else if (ENOENT == errno)
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
else
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;
} }
if (rc) { print_desc(NULL, "Restoring system clock (UTC) from RTC");
logit(LOG_WARNING, "Failed restoring system clock from RTC."); tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */
if (EINVAL == errno) tv.tv_sec = mktime(&tm);
logit(LOG_WARNING, "RTC time is too old (before %s)", rtc_timestamp); if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
else if (ENOENT == errno) errno = EINVAL;
logit(LOG_WARNING, "RTC has no previously saved (valid) time."); goto invalid;
else }
logit(LOG_WARNING, "RTC error code %d: %s", errno, strerror(errno));
print(2, NULL); if (settimeofday(&tv, NULL) == -1)
rc = 1;
/* Try restoring from last save game */
if (rtc_file)
file_restore(arg);
} else
print(0, NULL);
out:
tz_restore(tz);
print(rc, NULL);
close(fd); close(fd);
} }
static void save(void *arg)
{
rtc_save(arg);
file_save(arg);
}
static void update(uev_t *w, void *arg, int events)
{
save(arg);
}
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = rtc_restore .cb = rtc_restore
}, },
.hook[HOOK_SHUTDOWN] = { .hook[HOOK_SHUTDOWN] = {
.cb = save .cb = rtc_save
} }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
{ {
struct tm tm = { 0 };
if (!strptime(rtc_timestamp, RTC_FMT, &tm)) {
logit(LOG_WARNING, "Invalid restore date '%s', reverting to '%s'",
rtc_timestamp, RTC_TIMESTAMP_BEGIN_2000);
rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
} else
rtc_date_fallback = mktime(&tm);
uev_timer_init(ctx, &rtc_timer, update, NULL, RTC_PERIOD, RTC_PERIOD);
plugin_register(&plugin); plugin_register(&plugin);
} }
+13 -13
View File
@@ -1,6 +1,6 @@
/* System condition event monitor for the Finit condition engine /* System condition event monitor for the Finit condition engine
* *
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -63,7 +63,7 @@ static void sys_update_conds(char *dir, char *name, uint32_t mask)
return; return;
cond += strlen(COND_BASE) + 1; cond += strlen(COND_BASE) + 1;
dbg("cond: %s set: %d", cond, (mask & IN_CREATE) ? 1 : 0); _d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0);
if (!cond_update(cond)) if (!cond_update(cond))
unlink(path); unlink(path);
} }
@@ -82,7 +82,7 @@ static void sys_scandir(struct iwatch *iw, char *dir, int len)
return; return;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
dbg("scan found %s", gl.gl_pathv[i]); _d("scan found %s", gl.gl_pathv[i]);
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE); sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
} }
globfree(&gl); globfree(&gl);
@@ -97,7 +97,7 @@ static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
struct iwatch_path *iwp; struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name); paste(path, sizeof(path), dir, name);
dbg("path: %s", path); _d("path: %s", path);
iwp = iwatch_find_by_path(iw, path); iwp = iwatch_find_by_path(iw, path);
@@ -121,7 +121,7 @@ static void sys_callback(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
err(1, "invalid inotify event"); _pe("invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -136,7 +136,7 @@ static void sys_callback(void *arg, int fd, int events)
if (off + sizeof(*ev) + ev->len > (size_t)sz) if (off + sizeof(*ev) + ev->len > (size_t)sz)
break; break;
dbg("name %s, event: 0x%08x", ev->name, ev->mask); _d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask) if (!ev->mask)
continue; continue;
@@ -160,28 +160,28 @@ static void sys_init(void *arg)
char sysdir[MAX_ARG_LEN]; char sysdir[MAX_ARG_LEN];
char *path; char *path;
mkpath(_PATH_COND, 0755); _d("Calling mkpath() on sys conds");
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) { if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
err(1, "Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS); _pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
return; return;
} }
_d("Calling realpth() on condsys");
path = realpath(_PATH_CONDSYS, NULL); path = realpath(_PATH_CONDSYS, NULL);
if (!path) { if (!path) {
err(1, "Cannot figure out real path to %s, aborting", _PATH_CONDSYS); _pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
return; return;
} }
_d("Calling iwatch_add()");
if (iwatch_add(&iw_sys, path, IN_ONLYDIR)) if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
iwatch_exit(&iw_sys); iwatch_exit(&iw_sys);
_d("Done.");
free(path);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = sys_init }, .hook[HOOK_BASEFS_UP] = { .cb = sys_init },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
@@ -201,8 +201,8 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(plugin_exit)
{ {
plugin_unregister(&plugin);
iwatch_exit(&iw_sys); iwatch_exit(&iw_sys);
plugin_unregister(&plugin);
} }
/** /**
+8 -7
View File
@@ -1,7 +1,7 @@
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB /* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
* *
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com> * Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
* Copyright (c) 2013-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2013-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -55,7 +55,7 @@ static void setup(void)
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0) if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0)
err(1, "Failed starting TTY watcher"); _pe("Failed starting TTY watcher");
} }
static void do_tty(char *tty, size_t len, int creat) static void do_tty(char *tty, size_t len, int creat)
@@ -67,10 +67,10 @@ static void do_tty(char *tty, size_t len, int creat)
svc = svc_find_by_tty(name); svc = svc_find_by_tty(name);
if (svc) { if (svc) {
if (svc_is_blocked(svc) && creat) { if (svc_is_blocked(svc) && creat) {
dbg("%s: blocked, re-enabling", svc_ident(svc, NULL, 0)); _d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
svc_start(svc); svc_start(svc);
} else if (svc->pid) { } else if (svc->pid) {
dbg("%s: missing ...", svc_ident(svc, NULL, 0)); _d("%s: missing ...", svc_ident(svc, NULL, 0));
svc_missing(svc); svc_missing(svc);
} }
@@ -87,7 +87,7 @@ static void tty_watcher(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
err(1, "invalid inotify event"); _pe("invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -103,7 +103,7 @@ static void tty_watcher(void *arg, int fd, int events)
if (!ev->mask) if (!ev->mask)
continue; continue;
dbg("tty %s, event: 0x%08x", ev->name, ev->mask); _d("tty %s, event: 0x%08x", ev->name, ev->mask);
do_tty(ev->name, ev->len, ev->mask & IN_CREATE); do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
} }
} }
@@ -116,9 +116,10 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(plugin_exit)
{ {
plugin_unregister(&plugin);
if (plugin.io.fd) if (plugin.io.fd)
close(plugin.io.fd); close(plugin.io.fd);
plugin_unregister(&plugin);
} }
/** /**
+14 -38
View File
@@ -1,6 +1,6 @@
/* Setup and save random seed at boot/shutdown /* Setup and save random seed at boot/shutdown
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -42,38 +42,20 @@
#define RANDOM_BYTES (32*1024) #define RANDOM_BYTES (32*1024)
#endif #endif
#ifdef RANDOMSEED
static void fallback(FILE *fp)
{
struct timeval tv;
int iter = 128;
gettimeofday(&tv, NULL);
srandom(tv.tv_sec % 3600);
while (iter--) {
uint32_t i, prng = random();
for (i = 0; i < sizeof(prng); i++)
fputc((prng >> (i * CHAR_BIT)) & UCHAR_MAX, fp);
}
}
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
struct rand_pool_info *rpi; struct rand_pool_info *rpi;
ssize_t len = 0; ssize_t len = 0;
struct stat st;
int rc = -1; int rc = -1;
int fd, err; int fd, err;
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
if (stat(RANDOMSEED, &st) || st.st_size < 512) { if (!fexist(RANDOMSEED)) {
int ret = 1; int ret = 1;
mode_t prev; mode_t prev;
FILE *fp; FILE *fp;
@@ -82,22 +64,16 @@ static void setup(void *arg)
prev = umask(077); prev = umask(077);
fp = fopen(RANDOMSEED, "w"); fp = fopen(RANDOMSEED, "w");
if (fp) { if (fp) {
const char *hwrng = "/dev/hwrng"; int iter = 128;
FILE *hw; struct timeval tv;
hw = fopen(hwrng, "r"); gettimeofday(&tv, NULL);
if (hw) { srandom(tv.tv_sec % 3600);
char buf[512]; while (iter--) {
size_t len; uint32_t i, prng = random();
len = fread(buf, sizeof(buf[0]), sizeof(buf), hw); for (i = 0; i < sizeof(prng); i++)
if (len == 0) fputc((prng >> (i * CHAR_BIT)) & UCHAR_MAX, fp);
fallback(fp);
else
len = fwrite(buf, sizeof(buf[0]), len, fp);
fclose(hw);
} else {
fallback(fp);
} }
ret = fclose(fp); ret = fclose(fp);
} }
@@ -105,7 +81,7 @@ static void setup(void *arg)
umask(prev); umask(prev);
} }
print_desc("Seeding random number generator", NULL); print_desc("Initializing random number generator", NULL);
/* /*
* Simply copying our saved entropy to /dev/urandom doesn't * Simply copying our saved entropy to /dev/urandom doesn't
@@ -154,7 +130,7 @@ static void setup(void *arg)
close(fd); close(fd);
free(rpi); free(rpi);
if (rc < 0) if (rc < 0)
logit(LOG_WARNING, "Failed adding entropy to kernel random pool: %s", strerror(err)); logit(LOG_ERR, "Failed adding entropy to kernel random pool: %s", strerror(err));
print_result(rc < 0); print_result(rc < 0);
return; return;
fallback: fallback:
@@ -168,7 +144,7 @@ static void save(void *arg)
mode_t prev; mode_t prev;
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
+6 -8
View File
@@ -1,6 +1,6 @@
/* User-defined condition event monitor for the Finit condition engine /* User-defined condition event monitor for the Finit condition engine
* *
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,7 @@ static void usr_callback(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
err(1, "invalid inotify event"); _pe("invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -65,7 +65,7 @@ static void usr_callback(void *arg, int fd, int events)
if (off + sizeof(*ev) + ev->len > (size_t)sz) if (off + sizeof(*ev) + ev->len > (size_t)sz)
break; break;
dbg("name %s, event: 0x%08x", ev->name, ev->mask); _d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask) if (!ev->mask)
continue; continue;
@@ -84,15 +84,14 @@ static void usr_init(void *arg)
char usrdir[MAX_ARG_LEN]; char usrdir[MAX_ARG_LEN];
char *path; char *path;
mkpath(_PATH_COND, 0755);
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) { if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
err(1, "Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR); _pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
return; return;
} }
path = realpath(_PATH_CONDUSR, NULL); path = realpath(_PATH_CONDUSR, NULL);
if (!path) { if (!path) {
err(1, "Cannot figure out real path to %s, aborting", _PATH_CONDUSR); _pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
return; return;
} }
@@ -105,7 +104,6 @@ static void usr_init(void *arg)
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = usr_init }, .hook[HOOK_BASEFS_UP] = { .cb = usr_init },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
@@ -125,8 +123,8 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(plugin_exit)
{ {
plugin_unregister(&plugin);
iwatch_exit(&iw_usr); iwatch_exit(&iw_usr);
plugin_unregister(&plugin);
} }
/** /**
+33 -4
View File
@@ -1,6 +1,6 @@
/* Console setup (for X) /* Console setup (for X)
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -36,18 +36,47 @@
static void setup(void *arg) static void setup(void *arg)
{ {
const char *username = "nobody";
char line[LINE_SIZE];
mode_t prev;
#ifdef PAM_CONSOLE
int fd;
#endif
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
if (fexist("/var/run/console/console.lock") && which("pam_console_apply")) prev = umask(0);
makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username);
touch(line);
#ifdef PAM_CONSOLE
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, username, strlen(username));
close(fd);
run("pam_console_apply", "pam-console"); run("pam_console_apply", "pam-console");
}
#endif
makedir("/tmp/.X11-unix", 01777);
makedir("/tmp/.ICE-unix", 01777);
if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix", "restorecon");
umask(prev);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_SVC_PLUGIN] = { .cb = setup }, .hook[HOOK_NETWORK_UP] = {
.cb = setup
},
.depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
-1
View File
@@ -5,7 +5,6 @@ initctl
keventd keventd
logit logit
reboot reboot
runparts
sulogin sulogin
watchdogd watchdogd
Makefile Makefile
+4 -19
View File
@@ -8,10 +8,7 @@ AM_LDFLAGS = -export-dynamic
endif endif
sbin_PROGRAMS = finit initctl sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = getty logit runparts pkglibexec_PROGRAMS = getty logit sulogin
if SULOGIN
pkglibexec_PROGRAMS += sulogin
endif
if KEVENTD if KEVENTD
pkglibexec_PROGRAMS += keventd pkglibexec_PROGRAMS += keventd
endif endif
@@ -19,8 +16,6 @@ if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd pkglibexec_PROGRAMS += watchdogd
endif endif
dist_pkglibexec_SCRIPTS = coldplug
finit_pkglibdir = $(pkglibdir) finit_pkglibdir = $(pkglibdir)
finit_pkglib_SCRIPTS = rescue.conf sample.conf finit_pkglib_SCRIPTS = rescue.conf sample.conf
@@ -34,11 +29,6 @@ keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
keventd_CFLAGS += $(lite_CFLAGS) keventd_CFLAGS += $(lite_CFLAGS)
keventd_LDADD = $(lite_LIBS) keventd_LDADD = $(lite_LIBS)
runparts_SOURCES = runparts.c
runparts_CFLAGS = -W -Wall -Wextra -std=gnu99
runparts_CFLAGS += $(lite_CFLAGS)
runparts_LDADD = $(lite_LIBS)
sulogin_SOURCES = sulogin.c sulogin_SOURCES = sulogin.c
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99 sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
sulogin_LDADD = -lcrypt sulogin_LDADD = -lcrypt
@@ -49,10 +39,8 @@ logit_CFLAGS += $(lite_CFLAGS)
logit_LDADD = $(lite_LIBS) logit_LDADD = $(lite_LIBS)
finit_SOURCES = api.c cgroup.c cgroup.h \ finit_SOURCES = api.c cgroup.c cgroup.h \
client.c client.h \
cond.c cond-w.c cond.h \ cond.c cond-w.c cond.h \
conf.c conf.h \ conf.c conf.h \
devmon.c devmon.h \
exec.c finit.c finit.h \ exec.c finit.c finit.h \
stty.c \ stty.c \
helpers.c helpers.h \ helpers.c helpers.h \
@@ -61,12 +49,11 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
mdadm.c mount.c \ mdadm.c mount.c \
pid.c pid.h \ pid.c pid.h \
plugin.c plugin.h private.h \ plugin.c plugin.h private.h \
runparts.c schedule.c schedule.h \ schedule.c schedule.h \
service.c service.h \ service.c service.h \
sig.c sig.h \ sig.c sig.h \
sm.c sm.h \ sm.c sm.h \
svc.c svc.h \ svc.c svc.h \
tmpfiles.c tmpfiles.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
@@ -74,10 +61,8 @@ if LOGROTATE
finit_SOURCES += logrotate.c finit_SOURCES += logrotate.c
endif endif
pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.h \ pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
plugin.h svc.h service.h
finit_CPPFLAGS = $(AM_CPPFLAGS) -D__FINIT__
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)
finit_LDADD = $(lite_LIBS) $(uev_LIBS) finit_LDADD = $(lite_LIBS) $(uev_LIBS)
@@ -89,7 +74,7 @@ endif
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \ initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
client.c client.h cond.c cond.h reboot.c \ client.c client.h cond.c cond.h reboot.c \
serv.c serv.h svc.h util.c util.h log.h serv.c serv.h svc.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) $(uev_CFLAGS)
initctl_LDADD = $(lite_LIBS) $(uev_LIBS) initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
+58 -150
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket. /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,8 +21,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@@ -38,6 +36,7 @@
#endif #endif
#include <uev/uev.h> #include <uev/uev.h>
#include "config.h"
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "conf.h" #include "conf.h"
@@ -45,11 +44,11 @@
#include "log.h" #include "log.h"
#include "plugin.h" #include "plugin.h"
#include "private.h" #include "private.h"
#include "schedule.h"
#include "service.h"
#include "sig.h" #include "sig.h"
#include "service.h"
#include "util.h" #include "util.h"
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 *, void *), char *buf, size_t len)
@@ -62,10 +61,8 @@ static int stop(svc_t *svc, void *user_data)
if (!svc) if (!svc)
return 1; return 1;
service_timeout_cancel(svc);
svc_stop(svc); svc_stop(svc);
service_step(svc); service_step(svc);
service_step_all(SVC_TYPE_ANY);
return 0; return 0;
} }
@@ -75,10 +72,8 @@ static int start(svc_t *svc, void *user_data)
if (!svc) if (!svc)
return 1; return 1;
service_timeout_cancel(svc);
svc_start(svc); svc_start(svc);
service_step(svc); service_step(svc);
service_step_all(SVC_TYPE_ANY);
return 0; return 0;
} }
@@ -93,11 +88,8 @@ static int restart(svc_t *svc, void *user_data)
if (!svc) if (!svc)
return 1; return 1;
if (!svc_is_running(svc)) svc_mark_dirty(svc);
return start(svc, user_data); svc_start(svc);
service_timeout_cancel(svc);
service_stop(svc);
service_step(svc); service_step(svc);
return 0; return 0;
@@ -110,8 +102,6 @@ static int reload(svc_t *svc, void *user_data)
if (svc_is_blocked(svc)) if (svc_is_blocked(svc))
svc_start(svc); svc_start(svc);
else
service_timeout_cancel(svc);
svc_mark_dirty(svc); svc_mark_dirty(svc);
service_step(svc); service_step(svc);
@@ -207,93 +197,70 @@ static svc_t *do_find(char *buf, size_t len)
if (!id) if (!id)
return svc_named_iterator(&iter, 1, input); return svc_named_iterator(&iter, 1, input);
return svc_find(input, id); return svc_find_by_nameid(input, id);
} }
static svc_t *do_find_byc(char *buf, size_t len) static svc_t *do_find_byc(char *buf, size_t len)
{ {
svc_t *svc, *iter = NULL;
char *input; char *input;
input = sanitize(buf, len); input = sanitize(buf, len);
if (!input) { if (!input) {
dbg("Invalid input"); _d("Invalid input");
return NULL; return NULL;
} }
return svc_find_by_cond(input); for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char cond[MAX_COND_LEN];
mkcond(svc, cond, sizeof(cond));
_d("Comparing input %s with cond %s", input, cond);
if (string_compare(cond, input))
return svc;
}
_d("None found");
return NULL;
} }
static void bypass_shutdown(void *); static int do_reboot(int cmd, char *buf, size_t len)
struct wq emergency = { .cb = bypass_shutdown };
static void bypass_shutdown(void *unused)
{
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
do_shutdown(halt);
}
static int do_reboot(int cmd, int timeout, char *buf, size_t len)
{ {
int rc = 1; int rc = 1;
switch (cmd) { switch (cmd) {
case INIT_CMD_REBOOT: case INIT_CMD_REBOOT:
halt = SHUT_REBOOT; _d("reboot");
break;
case INIT_CMD_HALT:
halt = SHUT_HALT;
break;
case INIT_CMD_POWEROFF:
halt = SHUT_OFF;
break;
case INIT_CMD_SUSPEND:
break;
default:
return 255;
}
if (timeout > 0) {
emergency.delay = timeout * 1000;
schedule_work(&emergency);
}
switch (cmd) {
case INIT_CMD_REBOOT:
dbg("reboot");
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); service_runlevel(6);
break; break;
case INIT_CMD_HALT: case INIT_CMD_HALT:
dbg("halt"); _d("halt");
halt = SHUT_HALT; halt = SHUT_HALT;
service_runlevel(0); service_runlevel(0);
break; break;
case INIT_CMD_POWEROFF: case INIT_CMD_POWEROFF:
dbg("poweroff"); _d("poweroff");
halt = SHUT_OFF; halt = SHUT_OFF;
service_runlevel(0); service_runlevel(0);
break; break;
case INIT_CMD_SUSPEND: case INIT_CMD_SUSPEND:
dbg("suspend"); _d("suspend");
sync(); sync();
rc = suspend(); rc = reboot(RB_SW_SUSPEND);
if (rc) { if (rc) {
if (errno == EINVAL) if (errno == EINVAL)
snprintf(buf, len, "Kernel does not support suspend to RAM."); snprintf(buf, len, "Kernel does not support suspend.");
else else
snprintf(buf, len, "Failed: %s", strerror(errno)); snprintf(buf, len, "Failed: %s", strerror(errno));
} }
break; break;
default: default:
/* Handled previously */ rc = 255;
break; break;
} }
@@ -322,7 +289,7 @@ static void send_svc(int sd, svc_t *svc)
len = write(sd, svc, sizeof(*svc)); len = write(sd, svc, sizeof(*svc));
if (len != sizeof(*svc)) if (len != sizeof(*svc))
dbg("Failed sending svc_t to client"); _d("Failed sending svc_t to client");
} }
static void api_cb(uev_t *w, void *arg, int events) static void api_cb(uev_t *w, void *arg, int events)
@@ -334,7 +301,7 @@ static void api_cb(uev_t *w, void *arg, int events)
sd = accept(w->fd, NULL, NULL); sd = accept(w->fd, NULL, NULL);
if (sd < 0) { if (sd < 0) {
err(1, "Failed serving API request"); _pe("Failed serving API request");
goto error; goto error;
} }
@@ -351,51 +318,19 @@ static void api_cb(uev_t *w, void *arg, int events)
if (EAGAIN == errno) if (EAGAIN == errno)
break; break;
/* we get here when client restarts itself */ _e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
if (ECONNRESET == errno)
break;
errx(1, "Failed reading initctl request, error %d: %s", errno, strerror(errno));
} }
break; break;
} }
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) { if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
errx(1, "Invalid initctl request"); _e("Invalid initctl request");
break;
}
switch (rq.cmd) {
case INIT_CMD_RELOAD:
case INIT_CMD_START_SVC:
case INIT_CMD_RESTART_SVC:
case INIT_CMD_STOP_SVC:
case INIT_CMD_RELOAD_SVC:
case INIT_CMD_REBOOT:
case INIT_CMD_HALT:
case INIT_CMD_POWEROFF:
case INIT_CMD_SUSPEND:
if (IS_RESERVED_RUNLEVEL(runlevel)) {
strterm(rq.data, sizeof(rq.data));
warnx("Unsupported command (cmd: %d, data: %s) in runlevel S and 6/0.",
rq.cmd, rq.data);
goto leave;
}
default:
break; break;
} }
switch (rq.cmd) { switch (rq.cmd) {
case INIT_CMD_RUNLVL: case INIT_CMD_RUNLVL:
/* Allow changing cfglevel in runlevel S */
if (IS_RESERVED_RUNLEVEL(runlevel)) {
if (runlevel != INIT_LEVEL) {
warnx("Cannot abort runlevel 6/0.");
break;
}
}
switch (rq.runlevel) { switch (rq.runlevel) {
case 's': case 's':
case 'S': case 'S':
@@ -403,70 +338,57 @@ static void api_cb(uev_t *w, void *arg, int events)
/* fallthrough */ /* fallthrough */
case '0'...'9': case '0'...'9':
dbg("Setting new runlevel %c", rq.runlevel); _d("Setting new runlevel %c", rq.runlevel);
lvl = rq.runlevel - '0'; lvl = rq.runlevel - '0';
if (lvl == 0) if (lvl == 0)
halt = SHUT_OFF; halt = SHUT_OFF;
if (lvl == 6) if (lvl == 6)
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(lvl);
/* User requested change in next runlevel */
if (runlevel == INIT_LEVEL)
cfglevel = lvl;
else
service_runlevel(lvl);
break; break;
default: default:
dbg("Unsupported runlevel: %d", rq.runlevel); _d("Unsupported runlevel: %d", rq.runlevel);
break; break;
} }
break; break;
case INIT_CMD_DEBUG: case INIT_CMD_DEBUG:
dbg("debug"); _d("debug");
log_debug(); log_debug();
break; break;
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */ case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
dbg("reload"); _d("reload");
service_reload_dynamic(); service_reload_dynamic();
break; break;
case INIT_CMD_START_SVC: case INIT_CMD_START_SVC:
dbg("start %s", rq.data); _d("start %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_start(rq.data, sizeof(rq.data)); result = do_start(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RESTART_SVC: case INIT_CMD_RESTART_SVC:
dbg("restart %s", rq.data); _d("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_restart(rq.data, sizeof(rq.data)); result = do_restart(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_STOP_SVC: case INIT_CMD_STOP_SVC:
dbg("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_RELOAD_SVC:
dbg("reload %s", rq.data); _d("reload %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_reload(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_GET_PLUGINS:
result = plugin_list(rq.data, sizeof(rq.data));
break;
case INIT_CMD_PLUGIN_DEPS:
result = plugin_deps(rq.data, sizeof(rq.data));
break;
case INIT_CMD_GET_RUNLEVEL: case INIT_CMD_GET_RUNLEVEL:
dbg("get runlevel"); _d("get runlevel");
rq.runlevel = runlevel; rq.runlevel = runlevel;
rq.sleeptime = prevlevel; rq.sleeptime = prevlevel;
break; break;
@@ -475,21 +397,21 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_HALT: case INIT_CMD_HALT:
case INIT_CMD_POWEROFF: case INIT_CMD_POWEROFF:
case INIT_CMD_SUSPEND: case INIT_CMD_SUSPEND:
result = do_reboot(rq.cmd, rq.sleeptime, rq.data, sizeof(rq.data)); result = do_reboot(rq.cmd, rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_ACK: case INIT_CMD_ACK:
dbg("Client failed reading ACK"); _d("Client failed reading ACK");
goto leave; goto leave;
case INIT_CMD_WDOG_HELLO: case INIT_CMD_WDOG_HELLO:
dbg("wdog hello"); _d("wdog hello");
if (rq.runlevel <= 0) { if (rq.runlevel <= 0) {
result = 1; result = 1;
break; break;
} }
dbg("Request to hand-over wdog ... to PID %d", rq.runlevel); _d("Request to hand-over wdog ... to PID %d", rq.runlevel);
svc = svc_find_by_pid(rq.runlevel); svc = svc_find_by_pid(rq.runlevel);
if (!svc) { if (!svc) {
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel); logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
@@ -513,7 +435,7 @@ static void api_cb(uev_t *w, void *arg, int events)
break; break;
case INIT_CMD_SVC_ITER: case INIT_CMD_SVC_ITER:
// dbg("svc iter, first: %d", rq.runlevel); // _d("svc iter, first: %d", rq.runlevel);
/* /*
* XXX: This severely limits the number of * XXX: This severely limits the number of
* simultaneous client connections, but will * simultaneous client connections, but will
@@ -524,46 +446,32 @@ static void api_cb(uev_t *w, void *arg, int events)
goto leave; goto leave;
case INIT_CMD_SVC_QUERY: case INIT_CMD_SVC_QUERY:
dbg("svc query: %s", rq.data); _d("svc query: %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_query(rq.data, sizeof(rq.data)); result = do_query(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_SVC_FIND: case INIT_CMD_SVC_FIND:
dbg("svc find: %s", rq.data); _d("svc find: %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
send_svc(sd, do_find(rq.data, sizeof(rq.data))); send_svc(sd, do_find(rq.data, sizeof(rq.data)));
goto leave; goto leave;
case INIT_CMD_SVC_FIND_BYC: case INIT_CMD_SVC_FIND_BYC:
dbg("svc find by cond: %s", rq.data); _d("svc find by cond: %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
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: case INIT_CMD_SIGNAL:
/* runlevel is reused for signal */ /* runlevel is reused for signal */
dbg("svc signal %d: %s", rq.runlevel, rq.data); _d("svc signal %d: %s", rq.runlevel, rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel); result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
break; break;
case INIT_CMD_NOTIFY_SOCKET:
svc = svc_find_by_pid(rq.runlevel);
if (!svc) {
errx(1, "Unknown PID, cannot register notify socket");
result = 1;
break;
}
if (uev_io_init(ctx, &svc->notify_watcher, service_notify_cb, svc, sd, UEV_READ)) {
err(1, "Falied initializing %s readiness notifier", svc_ident(svc, NULL, 0));
break;
}
return; /* Don't close sd, used for notify */
default: default:
dbg("Unsupported cmd: %d", rq.cmd); _d("Unsupported cmd: %d", rq.cmd);
break; break;
} }
@@ -573,7 +481,7 @@ static void api_cb(uev_t *w, void *arg, int events)
rq.cmd = INIT_CMD_ACK; rq.cmd = INIT_CMD_ACK;
len = write(sd, &rq, sizeof(rq)); len = write(sd, &rq, sizeof(rq));
if (len != sizeof(rq)) if (len != sizeof(rq))
dbg("Failed sending ACK/NACK back to client"); _d("Failed sending ACK/NACK back to client");
} }
leave: leave:
@@ -584,7 +492,7 @@ leave:
error: error:
api_exit(); api_exit();
if (api_init(w->ctx)) if (api_init(w->ctx))
errx(1, "Unrecoverable error on API socket"); _e("Unrecoverable error on API socket");
} }
int api_init(uev_ctx_t *ctx) int api_init(uev_ctx_t *ctx)
@@ -597,10 +505,10 @@ int api_init(uev_ctx_t *ctx)
int uid, gid; int uid, gid;
int sd; int sd;
dbg("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_SEQPACKET | SOCK_CLOEXEC, 0);
if (-1 == sd) { if (-1 == sd) {
err(1, "Failed starting external API socket"); _pe("Failed starting external API socket");
return 1; return 1;
} }
@@ -616,14 +524,14 @@ int api_init(uev_ctx_t *ctx)
goto error; goto error;
if (chown(INIT_SOCKET, uid, gid)) if (chown(INIT_SOCKET, uid, gid))
err(1, "Failed setting group %s on %s", DEFGROUP, INIT_SOCKET); _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:
err(1, "Failed initializing API socket"); _pe("Failed initializing API socket");
umask(oldmask); umask(oldmask);
close(sd); close(sd);
return 1; return 1;
+31 -39
View File
@@ -1,6 +1,6 @@
/* Finit control group support functions /* Finit control group support functions
* *
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -63,16 +63,16 @@ static void cgset(const char *path, char *ctrl, char *prop)
{ {
char *val; char *val;
dbg("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL"); _d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
if (!path || !ctrl) { if (!path || !ctrl) {
errx(1, "Missing path or controller, skipping!"); _e("Missing path or controller, skipping!");
return; return;
} }
if (!prop) { if (!prop) {
prop = strchr(ctrl, '.'); prop = strchr(ctrl, '.');
if (!prop) { if (!prop) {
errx(1, "Invalid cgroup ctrl syntax: %s", ctrl); _e("Invalid cgroup ctrl syntax: %s", ctrl);
return; return;
} }
@@ -81,20 +81,20 @@ static void cgset(const char *path, char *ctrl, char *prop)
val = strchr(prop, ':'); val = strchr(prop, ':');
if (!val) { if (!val) {
errx(1, "Missing cgroup ctrl value, prop %s", prop); _e("Missing cgroup ctrl value, prop %s", prop);
return; return;
} }
*val++ = 0; *val++ = 0;
/* disallow sneaky relative paths */ /* disallow sneaky relative paths */
if (strstr(ctrl, "..") || strstr(prop, "..")) { if (strstr(ctrl, "..") || strstr(prop, "..")) {
errx(1, "Possible security violation; '..' not allowed in cgroup config!"); _e("Possible security violation; '..' not allowed in cgroup config!");
return; return;
} }
dbg("%s/%s.%s <= %s", path, ctrl, prop, val); _d("%s/%s.%s <= %s", path, ctrl, prop, val);
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop)) if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
err(1, "Failed setting %s/%s.%s = %s", path, ctrl, prop, val); _pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
} }
/* /*
@@ -106,16 +106,16 @@ static void group_init(char *path, int leaf, const char *cfg)
{ {
char *ptr, *s; char *ptr, *s;
dbg("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL"); _d("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
if (!fisdir(path)) { if (!fisdir(path)) {
if (mkdir(path, 0755)) { if (mkdir(path, 0755)) {
err(1, "Failed creating cgroup %s", path); _pe("Failed creating cgroup %s", path);
return; return;
} }
/* enable detected controllers on domain groups */ /* enable detected controllers on domain groups */
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path)) if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
err(1, "Failed enabling %s for %s", controllers, path); _pe("Failed enabling %s for %s", controllers, path);
} }
if (!cfg || !cfg[0]) if (!cfg || !cfg[0])
@@ -123,14 +123,14 @@ static void group_init(char *path, int leaf, const char *cfg)
s = strdupa(cfg); s = strdupa(cfg);
if (!s) { if (!s) {
err(1, "Failed activating cgroup cfg for %s", path); _pe("Failed activating cgroup cfg for %s", path);
return; return;
} }
dbg("%s <=> %s", path, s); _d("%s <=> %s", path, s);
ptr = strtok(s, ","); ptr = strtok(s, ",");
while (ptr) { while (ptr) {
dbg("ptr: %s", ptr); _d("ptr: %s", ptr);
if (!strncmp("mem.", ptr, 4)) if (!strncmp("mem.", ptr, 4))
cgset(path, "memory", &ptr[4]); cgset(path, "memory", &ptr[4]);
else else
@@ -144,7 +144,7 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
{ {
char path[256]; char path[256];
dbg("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL"); _d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
if (pid < 0 || pid == 1) { if (pid < 0 || pid == 1) {
errno = EINVAL; errno = EINVAL;
return 1; return 1;
@@ -156,7 +156,7 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
/* move process to new group */ /* move process to new group */
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path)) if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
err(1, "Failed moving pid %d to group %s", pid, path); _pe("Failed moving pid %d to group %s", pid, path);
strlcat(path, "/cgroup.events", sizeof(path)); strlcat(path, "/cgroup.events", sizeof(path));
@@ -211,13 +211,13 @@ static void cgroup_handle_event(char *event, uint32_t mask)
char *ptr; char *ptr;
FILE *fp; FILE *fp;
dbg("event: '%s', mask: %08x", event, mask); _d("event: '%s', mask: %08x", event, mask);
if (!(mask & IN_MODIFY)) if (!(mask & IN_MODIFY))
return; return;
fp = fopen(event, "r"); fp = fopen(event, "r");
if (!fp) { if (!fp) {
dbg("Failed opening %s, skipping ...", event); _d("Failed opening %s, skipping ...", event);
return; return;
} }
@@ -261,7 +261,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
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) {
err(1, "invalid inotify event"); _pe("invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -287,7 +287,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
cgroup_handle_event(iwp->path, ev->mask); cgroup_handle_event(iwp->path, ev->mask);
} }
#ifdef AUTO_RELOAD #ifdef ENABLE_AUTO_RELOAD
if (conf_any_change()) if (conf_any_change())
service_reload_dynamic(); service_reload_dynamic();
#endif #endif
@@ -364,12 +364,12 @@ int cgroup_add(char *name, char *cfg, int is_protected)
if (!cg) { if (!cg) {
cg = malloc(sizeof(struct cg)); cg = malloc(sizeof(struct cg));
if (!cg) { if (!cg) {
err(1, "Failed allocating 'struct cg' for %s", name); _pe("Failed allocating 'struct cg' for %s", name);
return -1; return -1;
} }
cg->name = strdup(name); cg->name = strdup(name);
if (!cg->name) { if (!cg->name) {
err(1, "Failed setting cgroup name %s", name); _pe("Failed setting cgroup name %s", name);
free(cg); free(cg);
return -1; return -1;
} }
@@ -379,7 +379,7 @@ int cgroup_add(char *name, char *cfg, int is_protected)
cg->cfg = strdup(cfg); cg->cfg = strdup(cfg);
if (!cg->cfg) { if (!cg->cfg) {
err(1, "Failed add/update of cgroup %s", name); _pe("Failed add/update of cgroup %s", name);
TAILQ_REMOVE(&cgroups, cg, link); TAILQ_REMOVE(&cgroups, cg, link);
free(cg->name); free(cg->name);
free(cg); free(cg);
@@ -414,7 +414,7 @@ int cgroup_del(char *dir)
} }
if (rmdir(dir) && errno != ENOENT) { if (rmdir(dir) && errno != ENOENT) {
dbg("Failed removing %s: %s", dir, strerror(errno)); _d("Failed removing %s: %s", dir, strerror(errno));
return -1; return -1;
} }
@@ -463,18 +463,13 @@ void cgroup_init(uev_ctx_t *ctx)
FILE *fp; FILE *fp;
int fd; int fd;
#ifndef CGROUP2_ENABLED
avail = 0;
return;
#endif
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
if (errno == ENOENT) if (errno == ENOENT)
logit(LOG_INFO, "Kernel does not support cgroups v2, disabling."); logit(LOG_NOTICE, "Kernel does not support cgroups v2, disabling.");
else if (errno == EPERM) /* Probably inside an unpriviliged container */ else if (errno == EPERM) /* Probably inside an unpriviliged container */
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling."); logit(LOG_NOTICE, "Not allowed to mount cgroups v2, disabling.");
else else
err(1, "Failed mounting cgroup v2"); _pe("Failed mounting cgroup v2");
avail = 0; avail = 0;
return; return;
} }
@@ -483,7 +478,7 @@ void cgroup_init(uev_ctx_t *ctx)
/* Find available controllers */ /* Find available controllers */
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r"); fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
if (!fp) { if (!fp) {
err(1, "Failed opening %s", FINIT_CGPATH "/cgroup.controllers"); _pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
abort: abort:
umount(FINIT_CGPATH); umount(FINIT_CGPATH);
avail = 0; avail = 0;
@@ -510,17 +505,14 @@ void cgroup_init(uev_ctx_t *ctx)
/* Enable all controllers */ /* Enable all controllers */
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) { if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) {
err(1, "Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control"); _pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
goto abort; goto abort;
} }
/* prepare cgroup.events watcher */ /* prepare cgroup.events watcher */
fd = iwatch_init(&iw_cgroup); fd = iwatch_init(&iw_cgroup);
if (fd == -1)
goto abort;
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) { if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
err(1, "Failed setting up cgroup.events watcher"); _pe("Failed setting up cgroup.events watcher");
iwatch_exit(&iw_cgroup); iwatch_exit(&iw_cgroup);
close(fd); close(fd);
goto abort; goto abort;
@@ -534,7 +526,7 @@ void cgroup_init(uev_ctx_t *ctx)
/* Move ourselves to init (best effort, otherwise run in 'root' group */ /* Move ourselves to init (best effort, otherwise run in 'root' group */
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) { if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) {
err(1, "Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init"); _pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init");
uev_io_stop(&cgw); uev_io_stop(&cgw);
iwatch_exit(&iw_cgroup); iwatch_exit(&iw_cgroup);
close(fd); close(fd);
+1 -1
View File
@@ -1,6 +1,6 @@
/* Finit control group support functions /* Finit control group support functions
* *
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+9 -11
View File
@@ -1,6 +1,6 @@
/* New client tool, replaces old /dev/initctl API and telinit tool /* New client tool, replaces old /dev/initctl API and telinit tool
* *
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include <err.h>
#include <dirent.h> #include <dirent.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
@@ -38,7 +39,6 @@
#include "cgutil.h" #include "cgutil.h"
#include "initctl.h" #include "initctl.h"
#include "log.h"
#define CDIM plain ? "" : "\e[2m" #define CDIM plain ? "" : "\e[2m"
#define CRST plain ? "" : "\e[0m" #define CRST plain ? "" : "\e[0m"
@@ -244,7 +244,7 @@ static float cgroup_cpuload(struct cg *cg)
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path); snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
fp = fopen(fn, "r"); fp = fopen(fn, "r");
if (!fp) if (!fp)
ERR(72, "Cannot open %s", fn); err(1, "Cannot open %s", fn);
while (fgets(buf, sizeof(buf), fp)) { while (fgets(buf, sizeof(buf), fp)) {
uint64_t curr; uint64_t curr;
@@ -280,13 +280,13 @@ static struct cg *append(char *path)
if (access(fn, F_OK)) { if (access(fn, F_OK)) {
/* older kernels, 4.19, don't have summary cpu.stat in root */ /* older kernels, 4.19, don't have summary cpu.stat in root */
if (strcmp(path, FINIT_CGPATH)) if (strcmp(path, FINIT_CGPATH))
WARN("not a cgroup path with cpu controller, %s", path); warn("not a cgroup path with cpu controller, %s", path);
return NULL; return NULL;
} }
cg = calloc(1, sizeof(struct cg)); cg = calloc(1, sizeof(struct cg));
if (!cg) if (!cg)
ERR(71, "failed allocating struct cg"); err(1, "failed allocating struct cg");
cg->cg_path = strdup(path); cg->cg_path = strdup(path);
if (list) if (list)
@@ -296,7 +296,7 @@ static struct cg *append(char *path)
item.key = cg->cg_path; item.key = cg->cg_path;
item.data = cg; item.data = cg;
if (!hsearch(item, ENTER)) if (!hsearch(item, ENTER))
ERR(70, "failed adding to hash table"); err(1, "failed adding to hash table");
return cg; return cg;
} }
@@ -434,6 +434,7 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
pid_comm(pid, comm, sizeof(comm)); pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf))) { if (pid_cmdline(pid, buf, sizeof(buf))) {
char proc[ttcols]; char proc[ttcols];
int len;
switch (mode) { switch (mode) {
case 1: case 1:
@@ -455,8 +456,6 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
if (plain) { if (plain) {
strlcat(row, proc, rplen); strlcat(row, proc, rplen);
} else { } else {
int len;
strlcat(row, CDIM, rlen); strlcat(row, CDIM, rlen);
strlcat(row, proc, rlen); strlcat(row, proc, rlen);
@@ -601,6 +600,7 @@ int show_cgtop(char *arg)
uev_t timer, input, sigint, sigterm, sigquit; uev_t timer, input, sigint, sigterm, sigquit;
char path[512]; char path[512];
uev_ctx_t ctx; uev_ctx_t ctx;
int flags;
if (!arg) if (!arg)
arg = FINIT_CGPATH; arg = FINIT_CGPATH;
@@ -610,7 +610,7 @@ int show_cgtop(char *arg)
} }
if (!hcreate(ttrows + 25)) if (!hcreate(ttrows + 25))
ERR(70, "failed creating hash table"); err(1, "failed creating hash table");
sysinfo(&si); sysinfo(&si);
total_ram = si.totalram * si.mem_unit; total_ram = si.totalram * si.mem_unit;
@@ -619,8 +619,6 @@ int show_cgtop(char *arg)
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000); uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
if (!ionce && !plain) { if (!ionce && !plain) {
int flags;
atexit(cleanup); atexit(cleanup);
ttraw(); ttraw();
hidecursor(); hidecursor();
+1 -1
View File
@@ -1,6 +1,6 @@
/* Cgroup utility functions for initctl /* Cgroup utility functions for initctl
* *
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+34 -70
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket. /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,15 +21,13 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <err.h>
#include <errno.h> #include <errno.h>
#include <poll.h> #include <poll.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include "client.h" #include "client.h"
#include "log.h"
#define REQUEST_TIMEOUT 15000
static int sd = -1; static int sd = -1;
@@ -41,17 +39,11 @@ int client_connect(void)
}; };
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0); sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (-1 == sd) { if (-1 == sd)
warn("Failed creating UNIX domain socket"); err(1, "Failed creating UNIX domain socket");
return -1;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
if (errno != ENOENT) err(1, "Failed connecting to finit");
warnx("Failed connecting to finit");
close(sd);
sd = -1;
}
return sd; return sd;
} }
@@ -71,97 +63,66 @@ int client_disconnect(void)
return rc; return rc;
} }
/* Only valid after client_connect() */ int client_send(struct init_request *rq, ssize_t len)
int client_socket(void)
{
return sd;
}
int client_request(struct init_request *rq, ssize_t len)
{ {
struct pollfd pfd = { 0 }; struct pollfd pfd = { 0 };
int sd, result = 255;
int rc; int rc;
if (client_connect() == -1) sd = client_connect();
if (-1 == sd)
return -1; return -1;
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLOUT; pfd.events = POLLOUT;
if (poll(&pfd, 1, REQUEST_TIMEOUT) <= 0) { if (poll(&pfd, 1, 2000) <= 0) {
warn("Timed out waiting for Finit, errno %d", errno); warn("Timed out waiting for Finit, errno %d", errno);
return -1; goto exit;
} }
if (write(sd, rq, len) != len) { if (write(sd, rq, len) != len) {
warn("Failed communicating with Finit, errno %d", errno); warn("Failed communicating with Finit, errno %d", errno);
return -1; goto exit;
}
/*
* Exception, only command that don't need to wait for a
* response from Finit. Registers the client socket as a
* readiness notification socket with Finit
*/
if (rq->cmd == INIT_CMD_NOTIFY_SOCKET) {
rq->cmd = INIT_CMD_ACK;
return 0;
} }
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLIN | POLLERR | POLLHUP; pfd.events = POLLIN | POLLERR | POLLHUP;
if ((rc = poll(&pfd, 1, REQUEST_TIMEOUT)) <= 0) { if ((rc = poll(&pfd, 1, 2000)) <= 0) {
if (rc) { if (rc) {
if (errno == EINTR) /* shutdown/reboot */ if (errno == EINTR) /* shutdown/reboot */
return -1; goto exit;
warn("poll(), errno %d", errno); warn("poll(), errno %d", errno);
} else } else
warnx("Timed out waiting for reply from Finit."); warnx("Timed out waiting for reply from Finit.");
return -1; goto exit;
} }
if (read(sd, rq, len) != len) { if (read(sd, rq, len) != len) {
warn("Failed reading reply from Finit, errno %d", errno); warn("Failed reading reply from Finit, errno %d", errno);
return -1; goto exit;
} }
if (rq->cmd == INIT_CMD_NACK) if (rq->cmd == INIT_CMD_NACK)
return -1; result = 1;
else
return 0; result = 0;
} exit:
int client_send(struct init_request *rq, ssize_t len)
{
int rc = 0;
if (client_request(rq, len))
rc = 255;
client_disconnect(); client_disconnect();
return rc; return result;
}
int client_command(int cmd)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.runlevel = getpid(),
.cmd = cmd,
};
return client_request(&rq, sizeof(rq));
} }
svc_t *client_svc_iterator(int first) svc_t *client_svc_iterator(int first)
{ {
int sd = -1;
struct init_request rq = { struct init_request rq = {
.magic = INIT_MAGIC, .magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_ITER, .cmd = INIT_CMD_SVC_ITER,
}; };
static svc_t svc; static svc_t svc;
if (client_connect() == -1) sd = client_connect();
if (sd == -1)
return NULL; return NULL;
if (first) if (first)
@@ -180,22 +141,24 @@ svc_t *client_svc_iterator(int first)
return &svc; return &svc;
error: error:
warn("Failed communicating with finit, error %d", errno); perror("Failed communicating with finit");
client_disconnect(); client_disconnect();
sd = -1; sd = -1;
return NULL; return NULL;
} }
static svc_t *do_find(int cmd, const char *arg) svc_t *do_cmd(int cmd, const char *arg)
{ {
struct init_request rq = { struct init_request rq = {
.magic = INIT_MAGIC, .magic = INIT_MAGIC,
.cmd = cmd, .cmd = cmd,
}; };
static svc_t svc; static svc_t svc;
int sd = -1;
if (client_connect() == -1) sd = client_connect();
if (sd == -1)
return NULL; return NULL;
strlcpy(rq.data, arg, sizeof(rq.data)); strlcpy(rq.data, arg, sizeof(rq.data));
@@ -211,19 +174,20 @@ static svc_t *do_find(int cmd, const char *arg)
return &svc; return &svc;
error: error:
client_disconnect(); client_disconnect();
warn("Failed communicating with finit, error %d", errno); perror("Failed communicating with finit");
return NULL; return NULL;
} }
svc_t *client_svc_find(const char *arg) svc_t *client_svc_find(const char *arg)
{ {
return do_find(INIT_CMD_SVC_FIND, arg); return do_cmd(INIT_CMD_SVC_FIND, arg);
} }
svc_t *client_svc_find_by_cond(const char *arg) svc_t *client_svc_find_by_cond(const char *arg)
{ {
return do_find(INIT_CMD_SVC_FIND_BYC, arg); return do_cmd(INIT_CMD_SVC_FIND_BYC, arg);
} }
/** /**
+1 -5
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket. /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,12 +29,8 @@
int client_connect (void); int client_connect (void);
int client_disconnect (void); int client_disconnect (void);
int client_socket (void);
int client_request (struct init_request *rq, ssize_t len);
int client_send (struct init_request *rq, ssize_t len); int client_send (struct init_request *rq, ssize_t len);
int client_command (int cmd);
svc_t *client_svc_iterator (int first); svc_t *client_svc_iterator (int first);
svc_t *client_svc_find (const char *arg); svc_t *client_svc_find (const char *arg);
svc_t *client_svc_find_by_cond (const char *arg); svc_t *client_svc_find_by_cond (const char *arg);
-11
View File
@@ -1,11 +0,0 @@
#!/bin/sh
# With the daemon mode ('mdev -df') we no longer need the hotplug helper
echo "" > /proc/sys/kernel/hotplug
# Wait for 'mdev -df' to set up its event queue.
sleep 1
# Trigger 'add' event for all devices and subsystems to load all modules
# and any firmware needed by the hardware.
find /sys/devices -name uevent -print | while IFS='' read X; do echo add > $X; done
+29 -165
View File
@@ -21,7 +21,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <dirent.h>
#include <ftw.h> #include <ftw.h>
#include <libgen.h> #include <libgen.h>
#include <stdio.h> #include <stdio.h>
@@ -35,71 +34,6 @@
#include "cond.h" #include "cond.h"
#include "pid.h" #include "pid.h"
#include "service.h" #include "service.h"
#include "sm.h"
struct cond_boot {
TAILQ_ENTRY(cond_boot) link;
char *name;
};
static TAILQ_HEAD(, cond_boot) cond_boot_list = TAILQ_HEAD_INITIALIZER(cond_boot_list);
/*
* Parse finit.cond=cond[,cond[,...]] from command line. It creates a
* temporary linked list of conds which are freed when finit reaches
* cond_init(), which calls cond_boot_strap() to realize the conditions
* in the file system as regular static conditions. Policy, however,
* dictates that these are read-only and should not be possible to
* manage from initctl.
*/
void cond_boot_parse(char *arg)
{
struct cond_boot *node;
if (!arg)
return;
node = malloc(sizeof(*node));
if (!node) {
err(1, "Out of memory cannot track boot conditions");
return;
}
node->name = strdup(arg);
TAILQ_INSERT_HEAD(&cond_boot_list, node, link);
}
/*
* Convert all conditions read from the kernel command line (above) into
* proper finit conditions.
*/
static void cond_boot_strap(void)
{
struct cond_boot *node, *tmp;
TAILQ_FOREACH_SAFE(node, &cond_boot_list, link, tmp) {
TAILQ_REMOVE(&cond_boot_list, node, link);
if (node->name) {
char *ptr;
ptr = strtok(node->name, ",");
while (ptr) {
char cond[strlen(ptr) + 6];
if (strncmp(ptr, "boot/", 5))
snprintf(cond, sizeof(cond), "boot/%s", ptr);
else
snprintf(cond, sizeof(cond), "%s", ptr);
cond_set_oneshot_noupdate(cond);
ptr = strtok(NULL, ",");
}
free(node->name);
}
free(node);
}
}
/* /*
* The service condition name is constructed from the 'pid/' prefix and * The service condition name is constructed from the 'pid/' prefix and
@@ -176,7 +110,7 @@ static void cond_bump_reconf(void)
rgen++; rgen++;
if (cond_set_gen(_PATH_RECONF, rgen)) if (cond_set_gen(_PATH_RECONF, rgen))
err(1, "Failed setting %s to gen %d", _PATH_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)
@@ -186,96 +120,47 @@ static int cond_checkpath(const char *path)
strlcpy(buf, path, sizeof(buf)); strlcpy(buf, path, sizeof(buf));
dir = dirname(buf); dir = dirname(buf);
if (!dir) { if (!dir) {
errx(1, "Invalid path '%s' for condition", path); _e("Invalid path '%s' for condition", path);
return 1; return 1;
} }
if (mkpath(dir, 0755) && errno != EEXIST) { if (mkpath(dir, 0755) && errno != EEXIST) {
err(1, "Failed creating dir '%s' for condition '%s'", dir, path); _pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1; return 1;
} }
return 0; return 0;
} }
static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
{
const char *cond, *ptr;
if (ftw->level == 0)
return 1;
ptr = strstr(fpath, COND_BASE);
if (!ptr) {
warnx("%s does not seem to be a Finit condition, skipping", fpath);
return 0;
}
if (remove(fpath))
err(1, "Failed removing condition %s", fpath);
cond = ptr + strlen(COND_BASE) + 1;
if (sm_is_in_teardown(&sm))
cond_clear_noupdate(cond);
else
cond_update(cond);
return 0;
}
static void cond_delpath(const char *path)
{
nftw(path, do_delete, 20, FTW_DEPTH);
if (remove(path)) {
switch (errno) {
case ENOENT: /* Ignore */
case ENOTEMPTY: /* Only at shutdown and for directories */
break;
default:
err(1, "Failed removing condition path %s", path);
break;
}
}
}
int cond_set_path(const char *path, enum cond_state next) int cond_set_path(const char *path, enum cond_state next)
{ {
enum cond_state prev; enum cond_state prev;
unsigned int rgen; unsigned int rgen;
dbg("%s <= %d", path, next); _d("%s <= %d", path, next);
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
}
prev = cond_get_path(path); prev = cond_get_path(path);
switch (next) { switch (next) {
case COND_ON: case COND_ON:
if (cond_checkpath(path)) if (cond_checkpath(path))
return 0; return 0;
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen) {
errx(1, "Unable to read configuration generation (%s)", path);
return -1;
}
cond_set_gen(path, rgen); cond_set_gen(path, rgen);
break; break;
case COND_OFF: case COND_OFF:
if (unlink(path)) { if (unlink(path) && errno != ENOENT)
switch (errno) { _pe("Failed removing condition '%s'", path);
case ENOENT:
break;
case EISDIR:
cond_delpath(path);
break;
default:
err(1, "Failed removing condition '%s'", path);
break;
}
}
break; break;
default: default:
errx(1, "Invalid condition state"); _e("Invalid condition state");
return 0; return 0;
} }
@@ -288,19 +173,13 @@ int cond_update(const char *name)
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
int affects = 0; int affects = 0;
// dbg("%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++; affects++;
dbg("%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);
/* Fix bug #314: race condition between crashing services and conditions */
if (svc_is_restart(svc) && cond_get_agg(svc->cond) == COND_OFF) {
dbg("%s: cancel timer & unblock => WAITING state.", name ?: "nil");
service_timeout_cancel(svc);
svc_unblock(svc);
}
service_step(svc); service_step(svc);
} }
@@ -309,7 +188,7 @@ int cond_update(const char *name)
int cond_set_noupdate(const char *name) int cond_set_noupdate(const char *name)
{ {
dbg("%s", name); _d("%s", name);
if (string_compare(name, "nop")) if (string_compare(name, "nop"))
return 1; return 1;
@@ -321,7 +200,7 @@ int cond_set_noupdate(const char *name)
void cond_set(const char *name) void cond_set(const char *name)
{ {
// dbg("%s", name); _d("%s", name);
if (cond_set_noupdate(name)) if (cond_set_noupdate(name))
return; return;
@@ -336,13 +215,13 @@ int cond_set_oneshot_noupdate(const char *name)
return 1; return 1;
path = cond_path(name); path = cond_path(name);
dbg("%s => %s", name, path); _d("%s => %s", name, path);
if (cond_checkpath(path)) if (cond_checkpath(path))
return 1; return 1;
if (symlink(_PATH_RECONF, path) && errno != EEXIST) { if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
err(1, "Failed creating onshot cond %s", name); _pe("Failed creating onshot cond %s", name);
return 1; return 1;
} }
@@ -351,7 +230,7 @@ int cond_set_oneshot_noupdate(const char *name)
void cond_set_oneshot(const char *name) void cond_set_oneshot(const char *name)
{ {
dbg("%s", name); _d("%s", name);
if (cond_set_oneshot_noupdate(name)) if (cond_set_oneshot_noupdate(name))
return; return;
@@ -360,7 +239,7 @@ void cond_set_oneshot(const char *name)
int cond_clear_noupdate(const char *name) int cond_clear_noupdate(const char *name)
{ {
dbg("%s", name); _d("%s", name);
if (string_compare(name, "nop")) if (string_compare(name, "nop"))
return 1; return 1;
@@ -372,7 +251,7 @@ int cond_clear_noupdate(const char *name)
void cond_clear(const char *name) void cond_clear(const char *name)
{ {
// dbg("%s", name); _d("%s", name);
if (cond_clear_noupdate(name)) if (cond_clear_noupdate(name))
return; return;
@@ -381,7 +260,7 @@ void cond_clear(const char *name)
void cond_reload(void) void cond_reload(void)
{ {
dbg(""); _d("");
cond_bump_reconf(); cond_bump_reconf();
} }
@@ -398,12 +277,12 @@ static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct
nm = strstr((char *)fpath, COND_BASE); nm = strstr((char *)fpath, COND_BASE);
if (!nm) { if (!nm) {
errx(1, "Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de"); _e("Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de");
return 1; return 1;
} }
nm += strlen(COND_BASE); nm += strlen(COND_BASE);
dbg("%sasserting %s => %s", set ? "Re" : "De", fpath, nm); _d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
if (set) if (set)
cond_set(nm); cond_set(nm);
else else
@@ -428,7 +307,7 @@ static int deassert(const char *fpath, const struct stat *sb, int tflg, struct F
*/ */
void cond_reassert(const char *pat) void cond_reassert(const char *pat)
{ {
dbg("%s", pat); _d("%s", pat);
nftw(cond_path(pat), reassert, 20, FTW_DEPTH); nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
} }
@@ -437,35 +316,20 @@ void cond_reassert(const char *pat)
*/ */
void cond_deassert(const char *pat) void cond_deassert(const char *pat)
{ {
dbg("%s", pat); _d("%s", pat);
nftw(cond_path(pat), deassert, 20, FTW_DEPTH); nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
} }
/*
* Check if we have bootstrapped enough of the system to use conditions.
* Will answer 'No' before bootstrap done *and* at shutdown/reboot.
*/
int cond_is_available(void)
{
return fisdir(_PATH_COND);
}
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 (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
err(1, "Failed creating condition base directory '%s'", _PATH_COND); _pe("Failed creating condition base directory '%s'", _PATH_COND);
return; return;
} }
cond_bump_reconf(); cond_bump_reconf();
cond_boot_strap();
}
void cond_exit(void)
{
cond_delpath(_PATH_COND);
} }
/** /**
+1 -1
View File
@@ -112,7 +112,7 @@ 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)
{ {
char conds[MAX_COND_LEN]; static char conds[MAX_COND_LEN];
char *cond; char *cond;
if (!name || !names) if (!name || !names)
+3 -9
View File
@@ -6,13 +6,11 @@
#include "svc.h" #include "svc.h"
#define COND_BASE "finit/cond" #define COND_BASE "finit/cond"
#define COND_DEV "dev/"
#define COND_PID "pid/" #define COND_PID "pid/"
#define COND_SYS "sys/" #define COND_SYS "sys/"
#define COND_USR "usr/" #define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/" #define _PATH_COND _PATH_VARRUN COND_BASE "/"
#define _PATH_CONDDEV _PATH_COND COND_DEV
#define _PATH_CONDPID _PATH_COND COND_PID #define _PATH_CONDPID _PATH_COND COND_PID
#define _PATH_CONDSYS _PATH_COND COND_SYS #define _PATH_CONDSYS _PATH_COND COND_SYS
#define _PATH_CONDUSR _PATH_COND COND_USR #define _PATH_CONDUSR _PATH_COND COND_USR
@@ -33,7 +31,6 @@ 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);
void cond_boot_parse (char *arg);
int cond_update (const char *name); 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);
@@ -41,17 +38,14 @@ 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_noupdate (const char *name);
int cond_set_oneshot_noupdate(const char *name); int cond_set_oneshot_noupdate(const char *name);
int cond_clear_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_deassert (const char *pat);
int cond_is_available(void);
void cond_init (void); void cond_init (void);
void cond_exit (void);
#endif /* FINIT_COND_H_ */ #endif /* FINIT_COND_H_ */
+124 -772
View File
File diff suppressed because it is too large Load Diff
+1 -5
View File
@@ -1,6 +1,6 @@
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf /* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -42,10 +42,6 @@ int conf_any_change (void);
int conf_changed (char *file); int conf_changed (char *file);
int conf_monitor (void); int conf_monitor (void);
void conf_reset_env (void);
void conf_saverc (void);
void conf_save_exec_order (svc_t *svc, char *cmdline, int result);
void conf_save_service (int type, char *cfg, char *file);
void conf_parse_cmdline (int argc, char *argv[]); void conf_parse_cmdline (int argc, char *argv[]);
int conf_parse_runlevels (char *runlevels); int conf_parse_runlevels (char *runlevels);
void conf_parse_cond (svc_t *svc, char *cond); void conf_parse_cond (svc_t *svc, char *cond);
-275
View File
@@ -1,275 +0,0 @@
/* Device node monitor for the Finit condition engine
*
* Copyright (c) 2022-2024 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 "helpers.h"
#include "pid.h"
#include "plugin.h"
#include "service.h"
#include "iwatch.h"
static struct iwatch iw_devmon;
static uev_t devw;
static int fd;
struct dev_node {
TAILQ_ENTRY(dev_node) link;
char *name;
size_t refcnt;
};
static TAILQ_HEAD(, dev_node) dev_node_list = TAILQ_HEAD_INITIALIZER(dev_node_list);
static struct dev_node *find_node(const char *cond)
{
struct dev_node *node, *tmp;
TAILQ_FOREACH_SAFE(node, &dev_node_list, link, tmp) {
if (string_compare(node->name, cond))
return node;
}
return NULL;
}
static void drop_node(struct dev_node *node)
{
if (!node)
return;
node->refcnt--;
if (node->refcnt)
return;
TAILQ_REMOVE(&dev_node_list, node, link);
free(node->name);
free(node);
}
void devmon_add_cond(const char *cond)
{
struct dev_node *node;
if (!cond || strncmp(cond, "dev/", 4)) {
dbg("no match");
return;
}
// dbg("%s", cond);
node = find_node(cond);
if (node) {
node->refcnt++;
return;
}
node = malloc(sizeof(*node));
if (!node) {
fail:
warn("failed allocating node %s", cond);
return;
}
node->name = strdup(cond);
if (!node->name) {
free(node);
goto fail;
}
node->refcnt = 1;
TAILQ_INSERT_TAIL(&dev_node_list, node, link);
}
void devmon_del_cond(const char *cond)
{
if (!cond || strcmp(cond, "dev/"))
return;
drop_node(find_node(cond));
}
static int devmon_add_path(struct iwatch *iw, char *path)
{
char *ptr;
ptr = strstr(path, "dev/");
if (ptr) {
char *slash;
ptr += 4;
slash = strchr(ptr, '/');
if (slash) {
ptr = slash++;
slash = strchr(ptr, '/');
if (slash) {
dbg("Path too deep, skipping.");
return -1;
}
}
}
return iwatch_add1(iw, path, IN_CREATE | IN_DELETE);
}
static void devmon_update_conds(char *dir, char *name, uint32_t mask)
{
char fn[PATH_MAX];
char *cond;
paste(fn, sizeof(fn), dir, name);
cond = &fn[1];
// dbg("path: %s, mask: %08x, cond: %s", fn, mask, cond);
if (!find_node(cond)) {
// dbg("unregistered, skipping %s", cond);
return;
}
if (mask & IN_CREATE)
cond_set(cond);
else if (mask & IN_DELETE)
cond_clear(cond);
}
/* synthesize events in case of new run dirs */
static void devmon_scandir(struct iwatch *iw, char *dir, int len)
{
char path[len + 6];
glob_t gl;
size_t i;
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++) {
// dbg("scan found %s", gl.gl_pathv[i]);
devmon_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
}
globfree(&gl);
}
/*
* create/remove sub-directory in monitored directory
*/
static void devmon_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);
dbg("path: %s", path);
iwp = iwatch_find_by_path(iw, path);
if (mask & IN_CREATE) {
if (!iwp) {
if (!devmon_add_path(iw, path))
devmon_scandir(iw, path, sizeof(path));
}
} else if (mask & IN_DELETE) {
if (iwp)
iwatch_del(&iw_devmon, iwp);
}
}
static void devmon_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) {
err(1, "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_devmon, ev->wd);
if (!iwp)
continue;
if (ev->mask & IN_ISDIR) {
devmon_handle_dir(&iw_devmon, iwp->path, ev->name, ev->mask);
continue;
}
// dbg("Got %s", ev->name);
if (ev->mask & (IN_CREATE | IN_DELETE))
devmon_update_conds(iwp->path, ev->name, ev->mask);
}
}
void devmon_init(uev_ctx_t *ctx)
{
char dir[MAX_ARG_LEN];
fd = iwatch_init(&iw_devmon);
if (fd < 0)
return;
if (mkpath(pid_runpath(_PATH_CONDDEV, dir, sizeof(dir)), 0755) && errno != EEXIST) {
err(1, "Failed creating %s condition directory, %s", COND_DEV, _PATH_CONDDEV);
return;
}
if (uev_io_init(ctx, &devw, devmon_cb, NULL, fd, UEV_READ)) {
err(1, "Failed setting up I/O callback for /dev watcher");
close(fd);
return;
}
strlcpy(dir, _PATH_DEV, sizeof(dir));
if (devmon_add_path(&iw_devmon, dir))
iwatch_exit(&iw_devmon);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
-32
View File
@@ -1,32 +0,0 @@
/* Device node monitor for the Finit condition engine
*
* Copyright (c) 2022-2024 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_DEVMON_H_
#define FINIT_DEVMON_H_
void devmon_add_cond(const char *cond);
void devmon_del_cond(const char *cond);
void devmon_init(uev_ctx_t *ctx);
#endif /* FINIT_DEVMON_H_ */
+99 -47
View File
@@ -1,7 +1,7 @@
/* Functions for exec'ing processes /* Functions for exec'ing processes
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,8 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <ctype.h> /* isdigit() */
#include <dirent.h>
#include <err.h> #include <err.h>
#include <stdarg.h> #include <stdarg.h>
#include <sysexits.h> #include <sysexits.h>
@@ -57,11 +59,11 @@ int complete(char *cmd, int pid)
if (waitpid(pid, &status, 0) == -1) { if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR) if (errno == EINTR)
warnx("Caught unblocked signal waiting for %s, aborting", cmd); _e("Caught unblocked signal waiting for %s, aborting", cmd);
else if (errno == ECHILD) else if (errno == ECHILD)
warnx("Caught SIGCHLD waiting for %s, aborting", cmd); _e("Caught SIGCHLD waiting for %s, aborting", cmd);
else else
warnx("Failed starting %s, error %d: %s", cmd, errno, strerror(errno)); _e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
return -1; return -1;
} }
@@ -95,14 +97,12 @@ static int do_redirect(void)
*/ */
int run(char *cmd, char *log) int run(char *cmd, char *log)
{ {
char *args[NUM_ARGS + 1] = { 0 };
int status, result, i = 0; int status, result, i = 0;
char *args[NUM_ARGS + 1], *arg;
char *backup = NULL; char *backup = NULL;
pid_t pid; pid_t pid;
if (!log) { if (!log) {
char *arg;
/* 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)
@@ -129,7 +129,7 @@ int run(char *cmd, char *log)
args[i] = NULL; args[i] = NULL;
if (i == NUM_ARGS && arg) { if (i == NUM_ARGS && arg) {
errx(1, "Command too long: %s", cmd); _e("Command too long: %s", cmd);
free(backup); free(backup);
errno = EOVERFLOW; errno = EOVERFLOW;
return 1; return 1;
@@ -138,34 +138,33 @@ int run(char *cmd, char *log)
pid = fork(); pid = fork();
if (0 == pid) { if (0 == pid) {
char *pfx, buf[256]; int rc = EX_OSERR;
FILE *pp;
int rc;
setsid(); setsid();
sig_unblock(); sig_unblock();
if (!log) { if (!log) {
do_redirect(); do_redirect();
execvp(args[0], args); execvp(args[0], args);
_exit(EX_OSERR); } else {
char *pfx = *log ? ": " : "";
FILE *pp;
pp = popen(cmd, "r");
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);
}
} }
pp = popen(cmd, "r"); _exit(rc);
if (!pp)
_exit(EX_OSERR);
pfx = *log ? ": " : "";
while (fgets(buf, sizeof(buf), pp)) {
chomp(buf);
logit(LOG_NOTICE, "%s%s%s", log, pfx, buf);
}
rc = pclose(pp);
if (rc == -1)
_exit(EX_OSERR);
_exit(WEXITSTATUS(rc));
} else if (-1 == pid) { } else if (-1 == pid) {
err(1, "%s", !log ? args[0] : cmd); _pe("%s", args[0]);
if (backup) if (backup)
free(backup); free(backup);
@@ -181,11 +180,11 @@ int run(char *cmd, char *log)
result = WEXITSTATUS(status); result = WEXITSTATUS(status);
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
dbg("Started '%s' and exit without signal, status: %d", cmd, result); _d("Started '%s' and exit without signal, status: %d", cmd, result);
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
dbg("Process '%s' terminated by signal %d", cmd, WTERMSIG(status)); _d("Process '%s' terminated by signal %d", cmd, WTERMSIG(status));
if (!result) if (!result)
result = 1; /* Must alert callee the command did not 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 */
} }
@@ -271,12 +270,12 @@ int exec_runtask(char *cmd, char *args[])
size_t i; size_t i;
strlcat(buf, cmd, sizeof(buf)); strlcat(buf, cmd, sizeof(buf));
for (i = 1; args && args[i]; i++) { for (i = 1; args[i]; i++) {
strlcat(buf, " ", sizeof(buf)); strlcat(buf, " ", sizeof(buf));
strlcat(buf, args[i], sizeof(buf)); strlcat(buf, args[i], sizeof(buf));
} }
logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf); logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf);
dbg("Calling %s %s", _PATH_BSHELL, buf); _d("Calling %s %s", _PATH_BSHELL, buf);
return execvp(_PATH_BSHELL, argv); return execvp(_PATH_BSHELL, argv);
} }
@@ -328,7 +327,7 @@ static int activate_console(int noclear, int nowait)
static const char clr[] = "\r\e[K"; static const char clr[] = "\r\e[K";
static const char cup[] = "\e[A"; static const char cup[] = "\e[A";
struct termios orig; struct termios orig;
char ch; char c;
int rc; int rc;
if (nowait || rescue) if (nowait || rescue)
@@ -350,7 +349,7 @@ static int activate_console(int noclear, int nowait)
dprint(STDERR_FILENO, clr, strlen(clr)); dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, msg, strlen(msg)); dprint(STDERR_FILENO, msg, strlen(msg));
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n') while ((rc = read(STDIN_FILENO, &c, 1)) > 0 && c != '\r' && c != '\n')
continue; continue;
/* On any error (likely EINTR), we avoid starting getty */ /* On any error (likely EINTR), we avoid starting getty */
@@ -418,10 +417,8 @@ int sh(char *tty)
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */ /* Start /bin/sh as a login shell, i.e. with a prefix '-' */
len = strlen(_PATH_BSHELL) + 2; len = strlen(_PATH_BSHELL) + 2;
arg0 = malloc(len); arg0 = malloc(len);
if (!arg0) { if (!arg0)
err(1, "Failed allocating memory"); err(1, "Failed allocating memory");
return 1;
}
snprintf(arg0, len, "-%s", _PATH_BSHELL); snprintf(arg0, len, "-%s", _PATH_BSHELL);
args[0] = arg0; args[0] = arg0;
@@ -442,20 +439,75 @@ pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
return rc; return rc;
} }
pid_t run_bg(char *cmd, char *args[]) int run_parts(char *dir, char *cmd)
{ {
pid_t pid; struct dirent **e;
int i, num;
if (access(cmd, X_OK)) num = scandir(dir, &e, NULL, alphasort);
return 0; if (num < 0) {
_d("No files found in %s, skipping ...", dir);
return -1;
}
print(-1, "Calling %s in the background", cmd); for (i = 0; i < num; i++) {
pid = fork(); const char *name = e[i]->d_name;
if (!pid) char path[strlen(dir) + strlen(name) + 2];
_exit(exec_runtask(cmd, args)); struct stat st;
print_result(pid == -1); char *argv[4] = {
"sh",
"-c",
path,
NULL
};
pid_t pid = 0;
int status;
int exit_status;
return pid; paste(path, sizeof(path), dir, name);
if (stat(path, &st)) {
_d("Failed stat(%s): %s", path, strerror(errno));
continue;
}
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
_d("Skipping %s ...", path);
continue;
}
/* If the callee didn't supply a run_parts() argument */
if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */
_d("Checking if %s is a sysvinit startstop script ...", name);
if (name[0] == 'S' && isdigit(name[1]))
strlcat(path, " start", sizeof(path));
else if (name[0] == 'K' && isdigit(name[1]))
strlcat(path, " stop", sizeof(path));
} else {
strlcat(path, cmd, sizeof(path));
}
print_desc("Calling ", path);
pid = fork();
if (!pid) {
sig_unblock();
return execvp(_PATH_BSHELL, argv);
}
status = complete(path, pid);
exit_status = WEXITSTATUS(status);
if (WIFEXITED(status) && exit_status)
_w("%s exited with status %d", path, exit_status);
else if (WIFSIGNALED(status))
_w("%s terminated by signad %d", path, WTERMSIG(status));
print_result(status);
}
while (num--)
free(e[num]);
free(e);
return 0;
} }
/** /**
+253 -290
View File
@@ -1,7 +1,7 @@
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins /* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,18 +26,16 @@
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <getopt.h> #ifdef HAVE_FSTAB_H
#ifdef HAVE_MNTENT_H #include <fstab.h>
#include <mntent.h>
#endif #endif
#include <getopt.h>
#include <mntent.h>
#include <time.h> /* tzet() */ #include <time.h> /* tzet() */
#include <sys/klog.h> #include <sys/klog.h>
#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 HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
#else #else
@@ -48,7 +46,6 @@
#include "cgroup.h" #include "cgroup.h"
#include "cond.h" #include "cond.h"
#include "conf.h" #include "conf.h"
#include "devmon.h"
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
#include "plugin.h" #include "plugin.h"
@@ -58,24 +55,22 @@
#include "tty.h" #include "tty.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
#include "schedule.h"
#include "watchdog.h"
int runlevel = INIT_LEVEL; /* Bootstrap 'S' */ int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */ int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int cmdlevel = 0; /* runlevel override from cmdline */
int prevlevel = -1; 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 bootstrap = 1; /* set while bootrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */ int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
int syncsec = 0; /* reboot delay */
int readiness = SVC_NOTIFY_PID;
char *finit_conf= NULL;
char *finit_rcsd= NULL;
char *fstab = NULL;
char *sdown = NULL; char *sdown = NULL;
char *network = NULL; char *network = NULL;
char *hostname = NULL; char *hostname = NULL;
char *rcsd = FINIT_RCSD;
char *runparts = NULL;
char *osheading = NULL; char *osheading = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */ uev_ctx_t *ctx = NULL; /* Main loop context */
@@ -87,22 +82,14 @@ svc_t *wdog = NULL; /* No watchdog by default */
*/ */
static void banner(void) static void banner(void)
{ {
#ifndef KERNEL_LOGGING
/* /*
* Silence kernel logs, assuming users have sysklogd or * Silence kernel logs, assuming users have sysklogd or
* similar enabled to start emptying /dev/kmsg. * similar enabled to start emptying /dev/kmsg, but for
* * our progress we want to own the console.
* Instead of using `configure --disable-kernel-logging`, we
* recommend adjusting the kernel log level in the kernel's
* menuconfig, or using sysctl kernel.printk, or setting the
* desired log level, on the kernel cmdline, e.g. 'quiet'.
*
* By default KERNEL_LOGGING is enabled so you can see any
* warnings/errors or higher on your system console.
*/ */
if (!debug && !kerndebug) if (!debug && !kerndebug)
klogctl(6, NULL, 0); klogctl(6, NULL, 0);
#endif
/* /*
* First level hooks, if you want to run here, you're * First level hooks, if you want to run here, you're
* pretty much on your own. Nothing's up yet ... * pretty much on your own. Nothing's up yet ...
@@ -119,171 +106,70 @@ static void banner(void)
#endif #endif
} }
static int sulogin(int do_reboot)
{
int rc = EX_OSFILE;
char *cmd[] = {
"sulogin",
_PATH_SULOGIN,
};
size_t i;
for (i = 0; i < NELEMS(cmd); i++) {
char *path = which(cmd[i]);
if (!path)
continue;
if (access(path, X_OK)) {
free(path);
continue;
}
rc = systemf("%s", path);
free(path);
break;
}
if (do_reboot) {
do_shutdown(SHUT_REBOOT);
exit(rc);
}
return rc;
}
char *fs_root_dev(char *real, size_t len)
{
struct dirent *d;
struct stat st;
int found = 0;
dev_t dev;
DIR *dir;
if (stat("/", &st))
return NULL;
if (S_ISBLK(st.st_mode))
dev = st.st_rdev;
else
dev = st.st_dev;
dir = opendir("/sys/block");
if (!dir)
return NULL;
while ((d = readdir(dir))) {
char buf[10];
char *ptr;
if (fnread(buf, sizeof(buf), "/sys/block/%s/dev", d->d_name) == -1)
continue;
ptr = strchr(buf, ':');
if (!ptr)
continue;
*ptr++ = 0;
if (atoi(buf) == (int)major(dev) && atoi(ptr) == (int)minor(dev)) {
/* Guess name, assume no renaming */
snprintf(real, len, "/dev/%s", d->d_name);
found = 1;
break;
}
}
closedir(dir);
if (!found)
return NULL;
return real;
}
/* /*
* Check all filesystems in /etc/fstab with a fs_passno > 0 * Check all filesystems in /etc/fstab with a fs_passno > 0
*/ */
static int fsck(int pass) static int fsck(int pass)
{ {
struct mntent mount; struct fstab *fs;
struct mntent *mnt;
char real[192];
char buf[256];
int rc = 0; int rc = 0;
FILE *fp;
fp = setmntent(fstab, "r"); if (!setfsent()) {
if (!fp) { _pe("Failed opening fstab");
logit(LOG_CONSOLE | LOG_ALERT, "Failed opening fstab: %s", fstab); return 1;
sulogin(1);
} }
dbg("Opened %s, pass %d", fstab, pass);
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) { while ((fs = getfsent())) {
int fsck_rc = 0; char cmd[80];
struct stat st; struct stat st;
char cmd[256]; int fsck_rc = 0;
char *dev;
dbg("got: fsname '%s' dir '%s' type '%s' opts '%s' freq '%d' passno '%d'", if (fs->fs_passno != pass)
mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts,
mnt->mnt_freq, mnt->mnt_passno);
if (mnt->mnt_passno == 0 || mnt->mnt_passno != pass)
continue; continue;
/* Device to maybe fsck, */
dev = mnt->mnt_fsname;
errno = 0; errno = 0;
if (stat(dev, &st) || !S_ISBLK(st.st_mode)) { if (stat(fs->fs_spec, &st) || !S_ISBLK(st.st_mode)) {
int skip = 1; if (!string_match(fs->fs_spec, "UUID=") && !string_match(fs->fs_spec, "LABEL=")) {
_d("Cannot fsck %s, not a block device: %s", fs->fs_spec, strerror(errno));
if (string_match(dev, "UUID=") || string_match(dev, "LABEL="))
skip = 0;
/*
* Kernel short form for root= device, figure out
* actual device since we cannot rely on symlinks
* https://bugs.busybox.net/show_bug.cgi?id=8891
*/
else if (string_compare(dev, "/dev/root")) {
if (fs_root_dev(real, sizeof(real))) {
dev = real;
skip = 0;
}
}
if (skip) {
dbg("Cannot fsck %s, not a block device: %s", dev, strerror(errno));
continue; continue;
} }
} }
if (ismnt("/proc/mounts", mnt->mnt_dir, "rw")) { if (ismnt("/proc/mounts", fs->fs_file, "rw")) {
dbg("Skipping fsck of %s, already mounted rw on %s.", dev, mnt->mnt_dir); _d("Skipping fsck of %s, already mounted rw on %s.", fs->fs_spec, fs->fs_file);
continue; continue;
} }
snprintf(cmd, sizeof(cmd), "fsck %s %s %s", fsck_mode, fsck_repair, dev); #ifdef FSCK_FIX
dbg("Running pass %d fsck command %s", pass, cmd); snprintf(cmd, sizeof(cmd), "fsck -yf %s", fs->fs_spec);
fsck_rc = run_interactive(cmd, "Checking filesystem %s", dev); #else
snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec);
#endif
fsck_rc = run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
/* /*
* "failure" is defined as exiting with a return code of * "failure" is defined as exiting with a return code of
* 2 or larger. A return code of 1 indicates that filesystem * 2 or larger. A return code of 1 indicates that filesystem
* errors were corrected but that the boot may proceed. * errors were corrected but that the boot may proceed.
*/ */
if (fsck_rc > 1) { if (fsck_rc > 1) {
if (hasmntopt(mnt,"nofail")) { char *sulogin[] = {
logit(LOG_CONSOLE | LOG_WARNING, "Ignoring failed fsck %s because of nofail option", dev); _PATH_SULOGIN,
fsck_rc = 0; "sulogin",
} else { };
logit(LOG_CONSOLE | LOG_ALERT, "Failed fsck %s, attempting sulogin ...", dev); size_t i;
sulogin(1);
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
break;
} }
do_shutdown(SHUT_REBOOT);
} }
rc += fsck_rc; rc += fsck_rc;
} }
endmntent(fp); endfsent();
return rc; return rc;
} }
@@ -291,17 +177,15 @@ static int fsck(int pass)
static int fsck_all(void) static int fsck_all(void)
{ {
int rc = 0; int rc = 0;
#ifndef FAST_BOOT
int pass; int pass;
if (!fsck_mode)
return 0;
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;
} }
@@ -314,26 +198,42 @@ static void fs_mount(const char *src, const char *tgt, const char *fstype,
rc = mount(src, tgt, fstype, flags, data); rc = mount(src, tgt, fstype, flags, data);
if (rc && errno != EBUSY) if (rc && errno != EBUSY)
err(1, "Failed %s %s on %s", msg, src, tgt); _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 mntent *mnt; struct fstab *fs;
FILE *fp;
fp = setmntent(fstab, "r"); if (!setfsent())
if (!fp)
return; return;
while ((mnt = getmntent(fp))) { while ((fs = getfsent())) {
if (!strcmp(mnt->mnt_dir, "/")) if (!strcmp(fs->fs_file, "/"))
break; break;
} }
/* If / is not listed in fstab, or listed as 'ro', leave it alone */ if (fs_readonly_root(fs))
if (!mnt || hasmntopt(mnt, "ro"))
goto out; goto out;
if (fsckerr) if (fsckerr)
@@ -343,7 +243,7 @@ static void fs_remount_root(int fsckerr)
"Remounting / as read-write"); "Remounting / as read-write");
out: out:
endmntent(fp); endfsent();
} }
#else #else
static void fs_remount_root(int fsckerr) static void fs_remount_root(int fsckerr)
@@ -368,8 +268,6 @@ static void fs_remount_root(int fsckerr)
*/ */
static void fs_finalize(void) static void fs_finalize(void)
{ {
int flags = MS_NOEXEC | MS_NOSUID;
/* /*
* Some systems rely on us to both create /dev/shm and, to mount * Some systems rely on us to both create /dev/shm and, to mount
* a tmpfs there. Any system with dbus needs shared memory, so * a tmpfs there. Any system with dbus needs shared memory, so
@@ -377,8 +275,8 @@ static void fs_finalize(void)
* the /etc/fstab file already. * the /etc/fstab file already.
*/ */
if (!fismnt("/dev/shm")) { if (!fismnt("/dev/shm")) {
makedir("/dev/shm", 1777); makedir("/dev/shm", 0777);
fs_mount("shm", "/dev/shm", "tmpfs", flags | MS_NODEV, "mode=1777"); fs_mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
} }
/* Modern systems use /dev/pts */ /* Modern systems use /dev/pts */
@@ -396,13 +294,7 @@ static void fs_finalize(void)
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode); snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
makedir("/dev/pts", 0755); makedir("/dev/pts", 0755);
fs_mount("devpts", "/dev/pts", "devpts", flags, opts); fs_mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
}
/* Needed on systems like Alpine Linux */
if (!fismnt("/dev/mqueue")) {
makedir("/dev/mqueue", 1777);
fs_mount("mqueue", "/dev/mqueue", "tmpfs", flags | MS_NODEV, NULL);
} }
/* /*
@@ -414,7 +306,7 @@ static void fs_finalize(void)
* To override any of this behavior, add entries to /etc/fstab * To override any of this behavior, add entries to /etc/fstab
* for /run (and optionally /run/lock). * for /run (and optionally /run/lock).
*/ */
if (fisdir("/run") && !fismnt("/run") && !fistmpfs("/run")) { if (fisdir("/run") && !fismnt("/run")) {
fs_mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%"); 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 */ /* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
@@ -423,79 +315,29 @@ static void fs_finalize(void)
} }
/* Modern systems use tmpfs for /tmp */ /* Modern systems use tmpfs for /tmp */
if (!fismnt("/tmp") && !fistmpfs("/tmp")) if (!fismnt("/tmp"))
fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777"); fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
} }
static void fs_swapon(char *cmd, size_t len)
{
struct mntent *mnt;
FILE *fp;
if (!whichp("swapon"))
return;
fp = setmntent(fstab, "r");
if (!fp)
return;
while ((mnt = getmntent(fp))) {
if (strcmp(mnt->mnt_type, MNTTYPE_SWAP))
continue;
snprintf(cmd, len, "swapon %s", mnt->mnt_fsname);
run_interactive(cmd, "Enabling swap %s", mnt->mnt_fsname);
}
endmntent(fp);
}
static void fs_mount_all(void) static void fs_mount_all(void)
{ {
char cmd[256] = "mount -na";
if (!fstab || !fexist(fstab)) {
logit(LOG_CONSOLE | LOG_NOTICE, "%s system fstab %s, trying fallback ...",
!fstab ? "Missing" : "Cannot find", fstab ?: "\b");
fstab = FINIT_FSTAB;
}
if (!fstab || !fexist(fstab)) {
logit(LOG_CONSOLE | LOG_EMERG, "%s system fstab %s, attempting sulogin ...",
!fstab ? "Missing" : "Cannot find", fstab ?: "\b");
sulogin(1);
}
/*
* Needed by fsck, both BusyBox and util-linux support this.
* We leave it set in the env. for the benefit of any mount
* helpers and other system tools that the user expects to
* behave even if we've booted with a different fstab.
*/
setenv("FSTAB_FILE", fstab, 1);
if (!rescue) if (!rescue)
fs_remount_root(fsck_all()); fs_remount_root(fsck_all());
dbg("Root FS up, calling hooks ..."); _d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP); plugin_run_hooks(HOOK_ROOTFS_UP);
if (fstab && strcmp(fstab, "/etc/fstab")) if (run_interactive("mount -na", "Mounting filesystems"))
snprintf(cmd, sizeof(cmd), "mount -na -T %s", fstab);
if (run_interactive(cmd, "Mounting filesystems from %s", fstab))
plugin_run_hooks(HOOK_MOUNT_ERROR); plugin_run_hooks(HOOK_MOUNT_ERROR);
dbg("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);
dbg("Enable any swap ..."); if (whichp("swapon"))
fs_swapon(cmd, sizeof(cmd)); run("swapon -ea", "swapon");
dbg("Finalize, ensure common file systems are available ..."); _d("Finalize, ensure common file systems are available ...");
fs_finalize(); fs_finalize();
dbg("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
} }
/* /*
@@ -536,6 +378,102 @@ static void fs_init(void)
} }
/*
* Handle bootstrap transition to configured runlevel, start TTYs
*
* This is the final stage of bootstrap. It changes to the default
* (configured) runlevel, calls all external start scripts and final
* bootstrap hooks before bringing up TTYs.
*
* We must ensure that all declared `task [S]` and `run [S]` jobs in
* finit.conf, or *.conf in finit.d/, run to completion before we
* finalize the bootstrap process by calling this function.
*/
static void finalize(void *unused)
{
/* Clean up bootstrap-only tasks/services that never started */
_d("Clean up all bootstrap-only tasks/services ...");
svc_prune_bootstrap();
/* All services/tasks/etc. in configure runlevel have started */
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
service_step_all(SVC_TYPE_ANY);
/* Convenient SysV compat for when you just don't care ... */
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
/* Hooks that should run at the very end */
_d("Calling all system up hooks ...");
plugin_run_hooks(HOOK_SYSTEM_UP);
service_step_all(SVC_TYPE_ANY);
/* Disable progress output at normal runtime */
enable_progress(0);
/* System bootrapped, launch TTYs et al */
bootstrap = 0;
service_step_all(SVC_TYPE_RESPAWN);
}
/*
* Start cranking the big state machine
*/
static void crank_worker(void *unused)
{
/*
* Initialize state machine and start all bootstrap tasks
* NOTE: no network available!
*/
sm_init(&sm);
sm_step(&sm);
}
/*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
* allowed to complete their work in [S], or timeout, before we switch
* to the configured runlevel and call finalize(), should not take more
* than 120 sec.
*/
static void bootstrap_worker(void *work)
{
static int cnt = 120;
static struct wq final = {
.cb = finalize,
.delay = 10
};
_d("Step all services ...");
service_step_all(SVC_TYPE_ANY);
if (cnt-- > 0 && !service_completed()) {
_d("Not all bootstrap run/tasks have completed yet ... %d", cnt);
schedule_work(work);
return;
}
if (cnt > 0)
_d("All run/task have completed, resuming bootstrap.");
else
_d("Timeout, resuming bootstrap.");
_d("Starting runlevel change finalize ...");
schedule_work(&final);
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* 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) static int version(int rc)
{ {
puts(PACKAGE_STRING); puts(PACKAGE_STRING);
@@ -566,7 +504,7 @@ static int usage(int rc)
" 2-9 Change runlevel\n" " 2-9 Change runlevel\n"
" 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 single user mode, no networking, runlevel 1\n" " 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", prognm); "\n", prognm);
return rc; return rc;
@@ -611,26 +549,30 @@ static int telinit(int argc, char *argv[])
return systemf("initctl -b runlevel %c", req); return systemf("initctl -b runlevel %c", req);
} }
/* XXX: add non-pid1 process monitor here
*
* finit -f ~/.config/finit.conf &
*
*/
return usage(1); return usage(1);
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct wq crank_work = {
.cb = crank_worker,
.delay = 10
};
struct wq bootstrap_work = {
.cb = bootstrap_worker,
.delay = 100
};
uev_ctx_t loop; uev_ctx_t loop;
/* user calling telinit or init */ /* telinit or stand-alone process monitor */
if (getpid() != 1) { if (getpid() != 1)
if (geteuid() == 0) return telinit(argc, argv);
return telinit(argc, argv);
/*
* Issue #301: add non-pid1 process monitor here.
* Default to read ~/.config/finit.conf with the
* API socket in /run/user/$UID/. Lot of things
* to refactor before then though.
*/
return EX_NOPERM;
}
/* /*
* Need /dev, /proc, and /sys for console=, remount and cgroups * Need /dev, /proc, and /sys for console=, remount and cgroups
@@ -639,12 +581,12 @@ int main(int argc, char *argv[])
/* /*
* Parse /proc/cmdline (debug, rescue, console=, etc.) * Parse /proc/cmdline (debug, rescue, console=, etc.)
* Also calls log_init() to set correct log level
*/ */
conf_parse_cmdline(argc, argv); conf_parse_cmdline(argc, argv);
/* /*
* Figure out system console(s) and call log_init() to set * Figure out system console(s)
* correct log level, possibly finit.debug enabled.
*/ */
console_init(); console_init();
@@ -657,18 +599,32 @@ int main(int argc, char *argv[])
/* /*
* Set PATH, SHELL, and PWD early to something sane * Set PATH, SHELL, and PWD early to something sane
*/ */
conf_reset_env(); setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
setenv("LOGNAME", "root", 1);
setenv("USER", "root", 1);
if (chdir("/")) if (chdir("/"))
err(1, "Failed cd /"); _pe("Failed cd /");
/* /*
* In case of emergency. * In case of emergency.
*/ */
#ifdef RESCUE_MODE if (rescue) {
if (rescue) char *sulogin[] = {
rescue = sulogin(0); _PATH_SULOGIN,
#endif "sulogin",
};
size_t i;
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
rescue = 0;
break;
}
}
/* /*
* Load plugins early, the first hook is in banner(), so we * Load plugins early, the first hook is in banner(), so we
@@ -697,17 +653,10 @@ int main(int argc, char *argv[])
*/ */
cgroup_init(&loop); cgroup_init(&loop);
/* /* Check and mount filesystems. */
* Check custom fstab from cmdline, including fallback, then run
* fsck before mounting all filesystems, on error call sulogin.
*/
fs_mount_all(); fs_mount_all();
/* /* Bootstrap conditions, needed for hooks */
* Base FS up, enable standard SysV init signals and
* Bootstrap conditions, needed for hooks
*/
sig_setup(&loop);
cond_init(); cond_init();
/* /*
@@ -717,17 +666,30 @@ int main(int argc, char *argv[])
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));
/* Some bootstrap tasks may need to know if we're in a container. */ /*
if (in_container()) * Initialize .conf system and load static /etc/finit.conf.
cond_set_oneshot("int/container"); */
conf_init(&loop);
/* /*
* Initialize .conf system and load static /etc/finit.conf then * Start built-in watchdogd as soon as possible, if enabled
* tell the world what we used.
*/ */
devmon_init(&loop); if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
conf_init(&loop); service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
conf_saverc(); 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 */
sig_setup(&loop);
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
/* /*
* Set up inotify watcher for /etc/finit.conf, /etc/finit.d, and * Set up inotify watcher for /etc/finit.conf, /etc/finit.d, and
@@ -735,23 +697,24 @@ int main(int argc, char *argv[])
*/ */
conf_monitor(); conf_monitor();
dbg("Starting initctl API responder ..."); _d("Starting initctl API responder ...");
api_init(&loop); api_init(&loop);
dbg("Starting service interval monitor ..."); _d("Starting the big state machine ...");
service_init(&loop); schedule_work(&crank_work);
_d("Starting bootstrap finalize timer ...");
schedule_work(&bootstrap_work);
/* /*
* Initialize state machine and start all bootstrap tasks * Background service tasks
* NOTE: no network available!
*/ */
sm_init(&sm); service_init();
sm_step(&sm);
/* /*
* Enter main loop to monitor /dev/initctl and services * Enter main loop to monitor /dev/initctl and services
*/ */
dbg("Entering main loop ..."); _d("Entering main loop ...");
return uev_run(&loop, 0); return uev_run(&loop, 0);
} }
+10 -16
View File
@@ -1,7 +1,7 @@
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins /* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -54,21 +54,19 @@
#define FINIT_CGPATH "/sys/fs/cgroup" #define FINIT_CGPATH "/sys/fs/cgroup"
#endif #endif
/* To use these, include config.h, or define FINIT_EXECPATH_ */ /* To use these, include config.h, or define FINIT_LIBPATH_ */
#define _PATH_LOGIT FINIT_EXECPATH_ "/logit" #define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
#define _PATH_RUNPARTS FINIT_EXECPATH_ "/runparts" #define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
#define _PATH_SULOGIN FINIT_EXECPATH_ "/sulogin" #define _PATH_GETTY FINIT_LIBPATH_ "/getty"
#define _PATH_GETTY FINIT_EXECPATH_ "/getty"
#define CMD_SIZE 1024 #define CMD_SIZE 256
#define LINE_SIZE 1024 #define LINE_SIZE 1024
#define BUF_SIZE 4096 #define BUF_SIZE 4096
/* We extend the INIT_CMD_ range for the new initctl tool. */
#define INIT_SOCKET _PATH_VARRUN "finit/socket" #define INIT_SOCKET _PATH_VARRUN "finit/socket"
#define INIT_MAGIC 0x03091969 #define INIT_MAGIC 0x03091969
#define INIT_LEVEL 10
/* We extend the INIT_CMD_ range for the new initctl tool. */
#define INIT_CMD_START 0 #define INIT_CMD_START 0
#define INIT_CMD_RUNLVL 1 #define INIT_CMD_RUNLVL 1
#define INIT_CMD_POWERFAIL 2 #define INIT_CMD_POWERFAIL 2
@@ -85,8 +83,8 @@
#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 /* START service, clearing blocks */
#define INIT_CMD_GET_PLUGINS 14 /* Fill data[] with loaded plugins */ #define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
#define INIT_CMD_PLUGIN_DEPS 15 /* Fill data[] with plugin deps */ #define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
#define INIT_CMD_GET_RUNLEVEL 16 #define INIT_CMD_GET_RUNLEVEL 16
#define INIT_CMD_REBOOT 20 #define INIT_CMD_REBOOT 20
#define INIT_CMD_HALT 21 #define INIT_CMD_HALT 21
@@ -98,7 +96,6 @@
#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_SIGNAL 133
#define INIT_CMD_NOTIFY_SOCKET 200 /* For readiness notification socket */
#define INIT_CMD_NACK 254 #define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255 #define INIT_CMD_ACK 255
@@ -113,16 +110,13 @@ struct init_request {
extern int runlevel; extern int runlevel;
extern int cfglevel; extern int cfglevel;
extern int cmdlevel;
extern int prevlevel; 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 bootstrap;
extern int kerndebug; extern int kerndebug;
extern int syncsec; extern char *rcsd;
extern int readiness;
extern char *fstab;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
extern char *hostname; extern char *hostname;
+20 -152
View File
@@ -1,6 +1,6 @@
/* Initialize and serve a login terminal /* Initialize and serve a login terminal
* *
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */ #include "config.h"
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
@@ -34,7 +34,6 @@
#include <sys/utsname.h> #include <sys/utsname.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>
#include <time.h>
#include <unistd.h> /* sysconf() */ #include <unistd.h> /* sysconf() */
#include "finit.h" #include "finit.h"
@@ -49,83 +48,7 @@
#define LOGIN_NAME_MIN 64 #define LOGIN_NAME_MIN 64
#endif #endif
struct osrel {
char name[32];
char pretty[64];
char id[32];
char version[32];
char version_id[32];
char home_url[128];
char doc_url[128];
char bug_url[128];
char support_url[128];
};
static long logname_len = 32; /* useradd(1) limit at 32 chars */ static long logname_len = 32; /* useradd(1) limit at 32 chars */
static int passenv = 0; /* Set /bin/login -p or not */
static void get_val(char *line, const char *key, char *buf, size_t len)
{
char *val;
if ((val = fgetval(line, key, "="))) {
strlcpy(buf, val, len);
free(val);
}
}
/*
* Parse os-release
*/
static int osrel(struct osrel *rel)
{
char codename[30];
char *line;
FILE *fp;
fp = fopen("/etc/os-release", "r");
if (!fp) {
fp = fopen("/usr/lib/os-release", "r");
if (!fp)
return -1;
}
memset(rel->version, 0, sizeof(rel->version));
memset(codename, 0, sizeof(codename));
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
get_val(line, "NAME", rel->name, sizeof(rel->name));
get_val(line, "PRETTY_NAME", rel->pretty, sizeof(rel->pretty));
get_val(line, "ID", rel->id, sizeof(rel->id));
get_val(line, "VERSION", rel->version, sizeof(rel->version));
get_val(line, "VERSION_ID", rel->version_id, sizeof(rel->version_id));
get_val(line, "VERSION_CODENAME", codename, sizeof(codename));
get_val(line, "HOME_URL", rel->home_url, sizeof(rel->home_url));
get_val(line, "DOCUMENTATION_URL", rel->doc_url, sizeof(rel->doc_url));
get_val(line, "SUPPORT_URL", rel->support_url, sizeof(rel->support_url));
get_val(line, "BUG_REPORT_URL", rel->bug_url, sizeof(rel->bug_url));
free(line);
}
fclose(fp);
/*
* Many distros don't set VERSION or VERSION_ID for their
* rolling, or unstable, branches. However, most set the
* version code name, so we use that as VERSION.
*/
if (!rel->version[0] && codename[0]) {
if (codename[0] == '(')
strlcpy(rel->version, codename, sizeof(rel->version));
else
snprintf(rel->version, sizeof(rel->version), "(%s)", codename);
}
if (rel->id[0] && !strcmp(rel->id, "alpine"))
return 1; /* compat mode for \r et al. */
return 0;
}
/* /*
* Read one character from stdin. * Read one character from stdin.
@@ -144,19 +67,10 @@ static int readch(void)
/* /*
* Parse and display a line from /etc/issue * Parse and display a line from /etc/issue
*
* Includes Finit-specific extensions to display information from
* /etc/os-release instead of relying on legacy uname. Activated
* only if /etc/os-release or /usr/lib/os-release exist.
*
* https://www.unix.com/man-page/Linux/8/getty/
* https://www.systutorials.com/docs/linux/man/8-mingetty/
*/ */
static void parseln(char *line, struct utsname *uts, struct osrel *rel, char *tty, int compat) static void do_parse(char *line, struct utsname *uts, char *tty)
{ {
char buf[32] = { 0 };
char *s, *s0; char *s, *s0;
time_t now;
s0 = line; s0 = line;
for (s = line; *s != 0; s++) { for (s = line; *s != 0; s++) {
@@ -165,32 +79,12 @@ static void parseln(char *line, struct utsname *uts, struct osrel *rel, char *tt
dprint(STDOUT_FILENO, s0, s - s0); dprint(STDOUT_FILENO, s0, s - s0);
s0 = s + 2; s0 = s + 2;
switch (*++s) { switch (*++s) {
case 'B':
if (!compat)
dprint(STDOUT_FILENO, rel->bug_url, 0);
break;
case 'D':
if (!compat)
dprint(STDOUT_FILENO, rel->doc_url, 0);
break;
case 'H':
if (!compat)
dprint(STDOUT_FILENO, rel->home_url, 0);
break;
case 'I':
if (!compat)
dprint(STDOUT_FILENO, rel->id, 0);
break;
case 'l': case 'l':
dprint(STDOUT_FILENO, tty, 0); dprint(STDOUT_FILENO, tty, 0);
break; break;
case 'm': case 'm':
dprint(STDOUT_FILENO, uts->machine, 0); dprint(STDOUT_FILENO, uts->machine, 0);
break; break;
case 'N':
if (!compat)
dprint(STDOUT_FILENO, rel->name, 0);
break;
case 'n': case 'n':
dprint(STDOUT_FILENO, uts->nodename, 0); dprint(STDOUT_FILENO, uts->nodename, 0);
break; break;
@@ -200,31 +94,13 @@ static void parseln(char *line, struct utsname *uts, struct osrel *rel, char *tt
break; break;
#endif #endif
case 'r': case 'r':
if (compat) dprint(STDOUT_FILENO, uts->release, 0);
dprint(STDOUT_FILENO, uts->release, 0);
else
dprint(STDOUT_FILENO, rel->version_id, 0);
break;
case 'S':
if (!compat)
dprint(STDOUT_FILENO, rel->support_url, 0);
break; break;
case 's': case 's':
if (compat) dprint(STDOUT_FILENO, uts->sysname, 0);
dprint(STDOUT_FILENO, uts->sysname, 0);
else
dprint(STDOUT_FILENO, rel->pretty, 0);
break;
case 't':
now = time(NULL);
ctime_r(&now, buf);
dprint(STDOUT_FILENO, chomp(buf), 0);
break; break;
case 'v': case 'v':
if (compat) dprint(STDOUT_FILENO, uts->version, 0);
dprint(STDOUT_FILENO, uts->version, 0);
else
dprint(STDOUT_FILENO, rel->version, 0);
break; break;
case 0: case 0:
goto leave; goto leave;
@@ -242,31 +118,28 @@ leave:
/* /*
* Parse and display /etc/issue * Parse and display /etc/issue
*/ */
static void issue(char *tty) static void do_issue(char *tty)
{ {
FILE *fp;
char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n"; char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n";
struct utsname uts; struct utsname uts;
struct osrel rel;
int compat;
FILE *fp;
/* /*
* Get data about this machine. * Get data about this machine.
*/ */
uname(&uts); uname(&uts);
compat = osrel(&rel);
fp = fopen("/etc/issue", "r"); fp = fopen("/etc/issue", "r");
if (fp) { if (fp) {
while (fgets(buf, sizeof(buf), fp)) while (fgets(buf, sizeof(buf), fp))
parseln(buf, &uts, &rel, tty, compat); do_parse(buf, &uts, tty);
fclose(fp); fclose(fp);
} else { } else {
parseln(buf, &uts, &rel, tty, compat); do_parse(buf, &uts, tty);
} }
parseln("\\n login: ", &uts, &rel, tty, compat); do_parse("\\n login: ", &uts, tty);
} }
/* /*
@@ -283,7 +156,7 @@ static int get_logname(char *tty, char *name, size_t len)
ch = ' '; ch = ' ';
*name = '\0'; *name = '\0';
while (ch != '\n') { while (ch != '\n') {
issue(tty); do_issue(tty);
np = name; np = name;
while ((ch = readch()) != '\n') { while ((ch = readch()) != '\n') {
@@ -304,17 +177,15 @@ static int get_logname(char *tty, char *name, size_t len)
} }
/* /*
* Start login(1) with the current username as its argument. It replies * Execute the login(1) command with the current
* to the calling user by typing "Password: " ... * username as its argument. It will reply to the
* calling user by typing "Password: "...
*/ */
static int exec_login(char *name) static int do_login(char *name)
{ {
struct stat st; struct stat st;
if (passenv) execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL);
else
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/* /*
* Failed to exec login, should not happen on normal systems. * Failed to exec login, should not happen on normal systems.
@@ -367,12 +238,12 @@ restart:
if (term && term[0]) if (term && term[0])
setenv("TERM", term, 1); setenv("TERM", term, 1);
return exec_login(name); return do_login(name);
} }
static int usage(int rc) static int usage(int rc)
{ {
warnx("usage: getty [-h?p] tty [speed [term]]"); warnx("usage: getty [-h?] tty [speed [term]]");
return rc; return rc;
} }
@@ -381,14 +252,11 @@ int main(int argc, char *argv[])
char *tty, *speed = "0", *term = NULL; char *tty, *speed = "0", *term = NULL;
int c; int c;
while ((c = getopt(argc, argv, "h?p")) != EOF) { while ((c = getopt(argc, argv, "h?")) != EOF) {
switch(c) { switch(c) {
case 'h': case 'h':
case '?': case '?':
return usage(0); return usage(0);
case 'p':
passenv = 1;
break;
default: default:
return usage(1); return usage(1);
} }
+66 -58
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions for finit and its plugins /* Misc. utility functions for finit and its plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */ #include <ctype.h> /* isblank() */
#include <errno.h> #include <errno.h>
#include <grp.h> #include <grp.h>
@@ -42,7 +40,6 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "log.h"
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
#include "util.h" #include "util.h"
@@ -84,13 +81,7 @@ char *console(void)
void console_init(void) void console_init(void)
{ {
/* Enable line wrap, if disabled previously, e.g., qemu */ ttinit();
dprint(STDOUT_FILENO, "\033[?7h", 5);
/* Reset atttributes, background and foreground color */
dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14);
log_init();
} }
ssize_t cprintf(const char *fmt, ...) ssize_t cprintf(const char *fmt, ...)
@@ -109,17 +100,6 @@ ssize_t cprintf(const char *fmt, ...)
return size; return size;
} }
void tabstospaces(char *line)
{
if (!line)
return;
for (int i = 0; line[i]; i++) {
if (line[i] == '\t')
line[i] = ' ';
}
}
char *strip_line(char *line) char *strip_line(char *line)
{ {
char *ptr; char *ptr;
@@ -373,8 +353,7 @@ int print_result(int fail)
int getuser(char *username, char **home) int getuser(char *username, char **home)
{ {
#ifdef ENABLE_STATIC #ifdef ENABLE_STATIC
if (home) *home = "/"; /* XXX: Fixme */
*home = "/"; /* XXX: Fixme */
return fgetint("/etc/passwd", "x:\n", username); return fgetint("/etc/passwd", "x:\n", username);
#else #else
struct passwd *usr; struct passwd *usr;
@@ -451,10 +430,8 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
gid = 0; gid = 0;
rc = makedir(dir, mode); rc = makedir(dir, mode);
if (rc && errno == EEXIST)
rc = chmod(dir, mode);
if (chown(dir, uid, gid)) if (chown(dir, uid, gid))
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid); _pe("Failed chown(%s, %d, %d)", dir, uid, gid);
} }
umask(omask); umask(omask);
@@ -492,7 +469,7 @@ void set_hostname(char **hostname)
done: done:
if (*hostname) { if (*hostname) {
if (sethostname(*hostname, strlen(*hostname))) if (sethostname(*hostname, strlen(*hostname)))
err(1, "Failed sethostname(%s)", *hostname); _pe("Failed sethostnaem(%s)", *hostname);
} }
} }
@@ -506,9 +483,9 @@ void networking(int updown)
return; return;
if (updown) if (updown)
dbg("Setting up networking ..."); _d("Setting up networking ...");
else else
dbg("Taking down networking ..."); _d("Taking down networking ...");
/* Run user network start script if enabled */ /* Run user network start script if enabled */
if (updown && network) { if (updown && network) {
@@ -523,40 +500,33 @@ void networking(int updown)
if (fexist("/etc/network/interfaces")) { if (fexist("/etc/network/interfaces")) {
pid_t pid; pid_t pid;
if (!updown) {
run_interactive("ifdown -a", "Stopping networking");
goto done;
}
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
const char *cmd; int rc = EX_OSERR;
char buf[256];
FILE *pp; FILE *pp;
int rc;
setsid(); setsid();
sig_unblock(); sig_unblock();
if (updown) pp = popen("ifup -a 2>&1", "r");
cmd = "ifup -a 2>&1"; if (pp) {
else if (whichp("ifquery")) char buf[256];
/* Regular ifodwn supports --force but not -f */
cmd = "ifdown -a --force 2>&1";
else
/* Busybox ifdown support -f, but not --force */
cmd = "ifdown -a -f 2>&1";
pp = popen(cmd, "r"); while (fgets(buf, sizeof(buf), pp))
if (!pp) logit(LOG_NOTICE, "network: %s", buf);
_exit(EX_OSERR);
while (fgets(buf, sizeof(buf), pp)) rc = pclose(pp);
logit(LOG_NOTICE, "network: %s", chomp(buf)); }
rc = pclose(pp); _exit(rc);
if (rc == -1)
_exit(EX_OSERR);
_exit(WEXITSTATUS(rc));
} }
cgroup_service("network", pid, NULL); cgroup_service("network", pid, NULL);
print(pid > 0 ? 0 : 1, "%s network interfaces ...", print(pid > 0 ? 0 : 1, "Bringing up network interfaces ...");
updown ? "Bringing up" : "Taking down");
goto done; goto done;
} }
@@ -568,11 +538,8 @@ done:
/* Hooks that rely on loopback, or basic networking being up. */ /* Hooks that rely on loopback, or basic networking being up. */
if (updown) { if (updown) {
dbg("Calling all network up hooks ..."); _d("Calling all network up hooks ...");
plugin_run_hooks(HOOK_NETWORK_UP); plugin_run_hooks(HOOK_NETWORK_UP);
} else {
dbg("Calling all network down hooks ...");
plugin_run_hooks(HOOK_NETWORK_DN);
} }
} }
@@ -585,8 +552,7 @@ int in_container(void)
"lxc", "lxc",
"docker", "docker",
"kubepod", "kubepod",
"unshare", "unshare"
"systemd-nspawn"
}; };
const char *files[] = { const char *files[] = {
"/run/.containerenv", "/run/.containerenv",
@@ -615,6 +581,48 @@ int in_container(void)
return 0; return 0;
} }
#ifndef HAVE_GETFSENT
static lfile_t *fstab = NULL;
int setfsent(void)
{
if (fstab)
lfclose(fstab);
fstab = lfopen("/etc/fstab", " \t\n");
if (!fstab)
return 0;
return 1;
}
struct fstab *getfsent(void)
{
static struct fstab fs;
fs.fs_spec = lftok(fstab);
if (fs.fs_spec == NULL)
return NULL;
fs.fs_file = lftok(fstab);
fs.fs_vfstype = lftok(fstab);
fs.fs_mntops = lftok(fstab);
fs.fs_type = "rw";
fs.fs_freq = atoi(lftok(fstab) ?: "0");
fs.fs_passno = atoi(lftok(fstab) ?: "0");
return &fs;
}
void endfsent(void)
{
if (fstab)
lfclose(fstab);
fstab = NULL;
}
#endif /* HAVE_GETFSENT */
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+26 -100
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions and C-library extensions for finit and its plugins /* Misc. utility functions and C-library extensions for finit and its plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,12 +25,10 @@
#ifndef FINIT_HELPERS_H_ #ifndef FINIT_HELPERS_H_
#define FINIT_HELPERS_H_ #define FINIT_HELPERS_H_
#include <ctype.h> #include <mntent.h>
#include <fcntl.h> #include <sched.h> /* sched_yield() */
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.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>
@@ -49,11 +47,26 @@ typedef enum {
PROGRESS_MODERN, PROGRESS_MODERN,
} pstyle_t; } pstyle_t;
#ifndef HAVE_GETFSENT
struct fstab {
char *fs_spec; /* block device name */
char *fs_file; /* mount point */
char *fs_vfstype; /* file-system type */
char *fs_mntops; /* mount options */
const char *fs_type; /* rw/rq/ro/sw/xx option */
int fs_freq; /* dump frequency, in days */
int fs_passno; /* pass number on parallel dump */
};
int setfsent (void);
void endfsent (void);
struct fstab *getfsent (void);
#endif /* HAVE_GETFSENT */
char *console (void); char *console (void);
void console_init (void); void console_init (void);
ssize_t cprintf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); ssize_t cprintf (const char *fmt, ...);
void tabstospaces (char *line);
char *strip_line (char *line); char *strip_line (char *line);
void enable_progress (int onoff); void enable_progress (int onoff);
@@ -66,7 +79,7 @@ void stty (int fd, speed_t speed);
void print_banner (const char *heading); void print_banner (const char *heading);
void printv (const char *fmt, va_list ap); void printv (const char *fmt, va_list ap);
void print (int action, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void print (int action, const char *fmt, ...);
void print_desc (char *action, char *desc); void print_desc (char *action, char *desc);
int print_result (int fail); int print_result (int fail);
@@ -84,63 +97,22 @@ int in_container (void);
int complete (char *cmd, int pid); int complete (char *cmd, int pid);
int run (char *cmd, char *log); int run (char *cmd, char *log);
int run_interactive (char *cmd, char *fmt, ...) __attribute__ ((format (printf, 2, 3))); int run_interactive (char *cmd, char *fmt, ...);
int exec_runtask (char *cmd, char *args[]); int exec_runtask (char *cmd, char *args[]);
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]); pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]); pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_bg (char *cmd, char *args[]); int run_parts (char *dir, char *cmd);
int run_parts (char *dir, char *cmd, const char *env[], int progress, int sysv);
/* static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
* Defaults to user "root" and group "wheel" (root) if:
* 1) the user is missing
* 2) the group or the file /etc/group is missing
*/
static inline int create(char *path, mode_t mode, char *user, char *group)
{ {
int fd, uid, gid; if (touch(path) || chmod(path, mode) || chown(path, uid, gid)) {
_w("Failed creating %s properly.", path);
if (!path) {
errno = EINVAL;
return -1;
}
uid = getuser(user, NULL);
if (uid < 0)
uid = 0;
gid = getgroup(group);
if (gid < 0)
gid = 0;
fd = creat(path, mode);
if (fd == -1) {
warn("Failed creating %s properly", path);
return -1;
}
close(fd);
if (chown(path, uid, gid)) {
warn("Failed chowning %s properly", path);
return -1; return -1;
} }
return 0; return 0;
} }
static inline int ln(const char *target, const char *linkpath)
{
if (!target || !linkpath) {
errno = EINVAL;
return -1;
}
if (symlink(target, linkpath)) {
if (errno != EEXIST)
warn("Failed creating %s -> %s symlink", target, linkpath);
return -1;
}
return 0;
}
static inline int dprint(int fd, const char *s, size_t len) static inline int dprint(int fd, const char *s, size_t len)
{ {
size_t loop = 3; size_t loop = 3;
@@ -159,52 +131,6 @@ static inline int dprint(int fd, const char *s, size_t len)
return rc; return rc;
} }
static inline char *fgetval(char *line, const char *key, char *sep)
{
char *ptr, *str, *copy;
size_t len;
str = copy = strdup(line);
if (!copy)
return NULL;
ptr = strsep(&str, sep);
if (ptr && strcmp(ptr, key)) {
fail:
free(copy);
return NULL;
}
/*
* Check if we should strip optional separator, e.g.,
* 'foo = bar' => '= bar' should be stripped to 'bar'
*/
while (str && strchr(sep, str[0])) {
ptr = strsep(&str, sep);
if (!ptr)
goto fail;
}
ptr = str;
if (*ptr == '"' || *ptr == '\'') {
char *end = &ptr[strlen(ptr) - 1];
char q = *ptr;
while (end > ptr && isspace(*end))
*end-- = 0;
if (*end == q) {
*end = 0;
ptr++;
}
}
len = strlen(ptr) + 1;
memmove(copy, ptr, len);
return realloc(copy, len);
}
#endif /* FINIT_HELPERS_H_ */ #endif /* FINIT_HELPERS_H_ */
/** /**
+169 -568
View File
File diff suppressed because it is too large Load Diff
+2 -15
View File
@@ -1,6 +1,6 @@
/* New client tool, replaces old /dev/initctl API and telinit tool /* New client tool, replaces old /dev/initctl API and telinit tool
* *
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -24,30 +24,17 @@
#ifndef FINIT_INITCTL_H_ #ifndef FINIT_INITCTL_H_
#define FINIT_INITCTL_H_ #define FINIT_INITCTL_H_
#include <err.h>
#include "finit.h" #include "finit.h"
#include "util.h" #include "util.h"
extern char *finit_conf;
extern char *finit_rcsd;
extern int icreate; /* initctl -c */ extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */ extern int iforce; /* initctl -f */
extern int ionce; /* initctl -1 */ extern int ionce; /* initctl -1 */
extern int heading; /* initctl -t */ extern int heading; /* initctl -t */
extern int json; /* initctl -j */
extern int noerr; /* initctl -n */
extern int verbose; /* initctl -v */ extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */ extern int plain; /* initctl -p */
extern int quiet; /* initctl -q */
#define ERR(rc, fmt, args...) do { if (!quiet) err(rc, fmt, ##args); else exit(rc); } while (0) extern void print_header(const char *fmt, ...);
#define ERRX(rc, fmt, args...) do { if (!quiet) errx(rc, fmt, ##args); else exit(rc); } while (0)
#define WARN(fmt, args...) do { if (!quiet) warn(fmt, ##args); } while (0)
#define WARNX(fmt, args...) do { if (!quiet) warnx(fmt, ##args); } while (0)
extern void print_header(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
#endif /* FINIT_INITCTL_H_ */ #endif /* FINIT_INITCTL_H_ */
+12 -25
View File
@@ -1,7 +1,7 @@
/* inotify watcher for files or directories /* inotify watcher for files or directories
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,7 @@ int iwatch_init(struct iwatch *iw)
iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (iw->fd < 0) { if (iw->fd < 0) {
warn("Failed creating inotify descriptor"); _pe("Failed creating inotify descriptor");
return -1; return -1;
} }
@@ -62,10 +62,8 @@ int iwatch_init(struct iwatch *iw)
* reconf of systems with lots of services. * reconf of systems with lots of services.
*/ */
len = sizeof(sz); len = sizeof(sz);
if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len)) { if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len))
if (setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz))) setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz));
warn("Failed increasing size of inotify socket");
}
initialized = 1; initialized = 1;
@@ -87,7 +85,7 @@ void iwatch_exit(struct iwatch *iw)
initialized = 0; initialized = 0;
} }
int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask) int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
{ {
struct iwatch_path *iwp; struct iwatch_path *iwp;
char *path; char *path;
@@ -96,25 +94,19 @@ int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
if (!initialized) if (!initialized)
return -1; return -1;
iwp = iwatch_find_by_path(iw, file);
if (iwp) {
dbg("skipping, %s already registered", file);
return 0;
}
if (!fexist(file)) { if (!fexist(file)) {
dbg("skipping %s: no such file or directory", file); _d("skipping %s: no such file or directory", file);
return 0; return 0;
} }
dbg("adding new watcher for path %s", file); _d("adding new watcher for path %s", file);
path = strdup(file); path = strdup(file);
if (!path) { if (!path) {
warn("Out of memory"); _pe("Out of memory");
return -1; return -1;
} }
wd = inotify_add_watch(iw->fd, path, mask); wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
if (wd < 0) { if (wd < 0) {
switch (errno) { switch (errno) {
case EEXIST: case EEXIST:
@@ -130,7 +122,7 @@ int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
/* fallthrough */ /* fallthrough */
default: default:
warn("Failed adding watcher for %s", path); _pe("Failed adding watcher for %s", path);
break; break;
} }
@@ -140,7 +132,7 @@ int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
iwp = malloc(sizeof(struct iwatch_path)); iwp = malloc(sizeof(struct iwatch_path));
if (!iwp) { if (!iwp) {
warn("Failed allocating new `struct iwatch_path`"); _pe("Failed allocating new `struct iwatch_path`");
inotify_rm_watch(iw->fd, wd); inotify_rm_watch(iw->fd, wd);
free(path); free(path);
return -1; return -1;
@@ -153,17 +145,12 @@ int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
return 0; return 0;
} }
int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
{
return iwatch_add1(iw, file, IWATCH_MASK | mask);
}
int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp) int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp)
{ {
if (!initialized) if (!initialized)
return -1; return -1;
dbg("Removing watcher for removed path %s", iwp->path); _d("Removing watcher for removed path %s", iwp->path);
TAILQ_REMOVE(&iw->iwp_list, iwp, link); TAILQ_REMOVE(&iw->iwp_list, iwp, link);
inotify_rm_watch(iw->fd, iwp->wd); inotify_rm_watch(iw->fd, iwp->wd);
+1 -2
View File
@@ -1,7 +1,7 @@
/* inotify watcher for files or directories /* inotify watcher for files or directories
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -59,7 +59,6 @@ struct iwatch {
int iwatch_init (struct iwatch *iw); int iwatch_init (struct iwatch *iw);
void iwatch_exit (struct iwatch *iw); void iwatch_exit (struct iwatch *iw);
int iwatch_add1 (struct iwatch *iw, char *file, uint32_t mask);
int iwatch_add (struct iwatch *iw, char *file, uint32_t mask); int iwatch_add (struct iwatch *iw, char *file, uint32_t mask);
int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp); int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp);
+7 -43
View File
@@ -1,6 +1,6 @@
/* Listens to kernel events like AC power status and manages sys/ conditions /* Listens to kernel events like AC power status and manages sys/ conditions
* *
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,6 @@
#include <errno.h> #include <errno.h>
#include <dirent.h> #include <dirent.h>
#include <poll.h> #include <poll.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
@@ -53,10 +52,6 @@
static int num_ac_online; static int num_ac_online;
static int num_ac; static int num_ac;
static int running = 1;
static int level;
static int logon;
int debug; /* debug in other modules as well */ int debug; /* debug in other modules as well */
void logit(int prio, const char *fmt, ...) void logit(int prio, const char *fmt, ...)
@@ -64,9 +59,9 @@ void logit(int prio, const char *fmt, ...)
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
if (logon) if (!debug)
vsyslog(prio, fmt, ap); vsyslog(prio, fmt, ap);
else if (prio <= level) { else {
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
@@ -152,7 +147,7 @@ static void init(void)
for (i = 0; i < (int)NELEMS(cond_dirs); i++) { for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) { if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
warn("Failed creating %s condition directory, %s", COND_SYS, _pe("Failed creating %s condition directory, %s", COND_SYS,
cond_dirs[i]); cond_dirs[i]);
return; return;
} }
@@ -184,26 +179,6 @@ static void init(void)
sys_cond("pwr/ac", 1); sys_cond("pwr/ac", 1);
} }
static void set_logging(int prio)
{
setlogmask(LOG_UPTO(prio));
level = prio;
}
static void toggle_debug(int signo)
{
(void)signo;
debug ^= 1;
set_logging(debug ? LOG_DEBUG : LOG_NOTICE);
}
static void shut_down(int signo)
{
(void)signo;
running = 0;
}
/* /*
* Started by Finit as soon as possible when base filesystem is up, * Started by Finit as soon as possible when base filesystem is up,
* modules have been probed, or insmodded from /etc/finit.conf, so by * modules have been probed, or insmodded from /etc/finit.conf, so by
@@ -223,13 +198,10 @@ int main(int argc, char *argv[])
} }
if (!debug) { if (!debug) {
openlog("keventd", LOG_PID, LOG_DAEMON); openlog("keventd", LOG_CONS | LOG_PID, LOG_DAEMON);
set_logging(LOG_NOTICE); setlogmask(LOG_UPTO(LOG_NOTICE));
logon = 1;
} }
signal(SIGUSR1, toggle_debug);
signal(SIGTERM, shut_down);
init(); init();
pfd.events = POLLIN; pfd.events = POLLIN;
@@ -244,18 +216,11 @@ int main(int argc, char *argv[])
panic("bind failed"); panic("bind failed");
logit(LOG_DEBUG, "Waiting for events ..."); logit(LOG_DEBUG, "Waiting for events ...");
while (running) { while (-1 != poll(&pfd, 1, -1)) {
char *path = NULL; char *path = NULL;
int ac = 0; int ac = 0;
int i, len; int i, len;
if (-1 == poll(&pfd, 1, -1)) {
if (errno == EINTR)
continue;
break;
}
len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT); len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
if (len == -1) { if (len == -1) {
switch (errno) { switch (errno) {
@@ -270,7 +235,6 @@ int main(int argc, char *argv[])
} }
} }
buf[len] = 0; buf[len] = 0;
logit(LOG_DEBUG, "%s", buf);
/* skip libusb events, focus on kernel events/changes */ /* skip libusb events, focus on kernel events/changes */
if (strncmp(buf, "change@", 7)) if (strncmp(buf, "change@", 7))
+18 -37
View File
@@ -1,6 +1,6 @@
/* Finit daemon log functions /* Finit daemon log functions
* *
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,11 +21,9 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */ #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <time.h>
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
#else #else
@@ -51,6 +49,13 @@ void log_init(void)
/* If we enabled terse mode at boot, restore to previous setting at shutdown */ /* If we enabled terse mode at boot, restore to previous setting at shutdown */
void log_exit(void) void log_exit(void)
{ {
/*
* Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime
*/
if (!debug)
ttinit();
enable_progress(1); enable_progress(1);
} }
@@ -67,45 +72,29 @@ static int log_open(void)
if (debug) if (debug)
opts |= LOG_PERROR; opts |= LOG_PERROR;
closelog();
openlog("finit", opts, LOG_DAEMON); openlog("finit", opts, LOG_DAEMON);
setlogmask(LOG_UPTO(loglevel)); setlogmask(LOG_UPTO(loglevel));
return up = 1; return up = 1;
} }
static void log_close(void)
{
closelog();
up = 0;
}
/* Toggle debug mode */ /* Toggle debug mode */
void log_debug(void) void log_debug(void)
{ {
debug = !debug; debug = !debug;
log_close(); if (debug) {
log_init(); loglevel = LOG_DEBUG;
} else {
loglevel = LOG_NOTICE;
ttinit();
}
log_open(); log_open();
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled"); logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
} }
static const char *l2s(int prio)
{
int level = prio & ~LOG_CONSOLE;
switch (level) {
case LOG_ERR: return "ERR";
case LOG_WARNING: return "WRN";
case LOG_NOTICE: return "NOT";
case LOG_INFO: return "NFO";
case LOG_DEBUG: return "DBG";
default: break;
}
return "UNK";
}
/* /*
* Log to /dev/kmsg until syslogd has started, then openlog() * Log to /dev/kmsg until syslogd has started, then openlog()
* and continue logging as a regular daemon. * and continue logging as a regular daemon.
@@ -126,16 +115,8 @@ void logit(int prio, const char *fmt, ...)
goto done; goto done;
if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) { if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) {
static char buf[512]; vfprintf(stderr, fmt, ap);
time_t now; fputs("\n", stderr);
size_t pos;
now = time(NULL);
pos = (int)strftime(buf, sizeof(buf), "%FT%T", localtime(&now));
pos += snprintf(&buf[pos], sizeof(buf) - pos, " [%s]: ", l2s(prio));
vsnprintf(&buf[pos], sizeof(buf) - pos, fmt, ap);
strlcat(buf, "\n", sizeof(buf));
fputs(buf, stderr);
goto done; goto done;
} }
+14 -44
View File
@@ -1,6 +1,6 @@
/* Finit daemon log functions /* Finit daemon log functions
* *
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -31,56 +31,26 @@
#define LOG_CONSOLE (14<<3) #define LOG_CONSOLE (14<<3)
#endif #endif
#ifndef __FINIT__
#include <err.h>
#include <stdarg.h>
#include <stdio.h>
extern int debug;
static __attribute__ ((format (printf, 1, 2))) inline void dbg(char *fmt, ...)
{
va_list ap;
if (!debug)
return;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
va_end(ap);
}
#define info(fmt, args...) warnx(fmt, ##args)
#define note(fmt, args...) warnx(fmt, ##args)
#else
/* /*
* General log macros, similar to those used by initctl. Initially intended * Developer error and debug messages, otherwise --> use logit() <--
* only for bridging client.c in Finit and initctl. * ~~~~~~~~~~~
*/ * All of these prepend the function, so only use for critical warnings
#define dbg(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args) * errors and debug messages. For all other user messages, see logit()
#define info(fmt, args...) logit(LOG_INFO, "%s():" fmt, __func__, ##args) *
#define note(fmt, args...) logit(LOG_NOTICE, "%s():" fmt, __func__, ##args) * The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
#define warnx(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args) * use `initctl debug` or add `debug` to the kernel cmdline.
#define warn(fmt, args...) logit(LOG_WARNING, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
#define errx(rc, fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
#define err(rc, fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
/*
* DEPRECATED developer error and debug messages, please migrate to above!
* Will be removed without further warning in a future Finit release.
*/ */
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args) #define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args) #define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
#define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args) #define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
#define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno)) #define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %n", __func__, ##args, strerror(errno))
void log_init (void); void log_init (void);
void log_exit (void); void log_exit (void);
void log_debug(void); void log_debug (void);
void logit (int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void logit (int prio, const char *fmt, ...);
void flog (char *file, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void flog (char *file, const char *fmt, ...);
#endif
#endif /* FINIT_LOG_H_ */ #endif /* FINIT_LOG_H_ */
+3 -8
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -208,13 +208,8 @@ int main(int argc, char *argv[])
} }
} }
if (!ident) { if (!ident)
ident = getenv("LOGNAME"); ident = getenv("LOGNAME") ?: getenv("USER");
if (!ident)
ident = getenv("USER");
if (!ident)
ident = PACKAGE_NAME;
}
if (optind < argc) { if (optind < argc) {
size_t pos = 0, len = sizeof(buf); size_t pos = 0, len = sizeof(buf);
+4 -3
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -49,6 +49,7 @@ static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
*/ */
int logrotate(char *file, int num, off_t sz) int logrotate(char *file, int num, off_t sz)
{ {
int cnt;
struct stat st; struct stat st;
if (stat(file, &st)) if (stat(file, &st))
@@ -59,7 +60,6 @@ int logrotate(char *file, int num, off_t sz)
size_t len = strlen(file) + 10 + 1; size_t len = strlen(file) + 10 + 1;
char ofile[len]; char ofile[len];
char nfile[len]; char nfile[len];
int cnt;
/* First age zipped log files */ /* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) { for (cnt = num; cnt > 2; cnt--) {
@@ -97,7 +97,8 @@ int logrotate(char *file, int num, off_t sz)
} else { } else {
fallback: fallback:
if (truncate(file, 0)) if (truncate(file, 0))
syslog(LOG_ERR, "Failed truncating %s: %s", file, strerror(errno)); syslog(LOG_ERR, "Failed truncating %s during logrotate: %s",
file, strerror(errno));
} }
} }
+5 -9
View File
@@ -1,6 +1,6 @@
/* Handle MD arrays /* Handle MD arrays
* *
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,14 +21,11 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */
#include <glob.h> #include <glob.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "helpers.h" #include "helpers.h"
#include "util.h"
static glob_t *get_arrays(void) static glob_t *get_arrays(void)
@@ -49,18 +46,17 @@ static glob_t *get_arrays(void)
*/ */
void mdadm_wait(void) void mdadm_wait(void)
{ {
size_t i;
glob_t *gl; glob_t *gl;
char cmd[160];
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */ /* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
gl = get_arrays(); gl = get_arrays();
if (gl) { if (gl) {
size_t i;
for (i = 0; i < gl->gl_pathc; i++) { for (i = 0; i < gl->gl_pathc; i++) {
const char *array; char *array;
char cmd[160];
array = basenm(gl->gl_pathv[i]); array = basename(gl->gl_pathv[i]);
snprintf(cmd, sizeof(cmd), "mdadm --wait-clean /dev/%s >/dev/null", array); snprintf(cmd, sizeof(cmd), "mdadm --wait-clean /dev/%s >/dev/null", array);
run_interactive(cmd, "Marking MD array %s as clean", array); run_interactive(cmd, "Marking MD array %s as clean", array);
} }
+6 -29
View File
@@ -1,6 +1,6 @@
/* Mount and unmount helpers /* Mount and unmount helpers
* *
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,17 +21,10 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */
#include <errno.h>
#include <string.h> #include <string.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h> #include <mntent.h>
#endif
#include <sys/mount.h> #include <sys/mount.h>
#include "helpers.h"
/* /*
* SysV init on Debian/Ubuntu skips these protected mount points * SysV init on Debian/Ubuntu skips these protected mount points
* *
@@ -72,12 +65,12 @@ static void iterator_end(FILE **fp)
*fp = NULL; *fp = NULL;
} }
static struct mntent *iterator(char *filename, FILE **fp) static struct mntent *iterator(char *fstab, FILE **fp)
{ {
static struct mntent *mnt; static struct mntent *mnt;
if (!*fp && filename) { if (!*fp && fstab) {
*fp = setmntent(filename, "r"); *fp = setmntent(fstab, "r");
if (!*fp) if (!*fp)
return NULL; return NULL;
} }
@@ -93,29 +86,13 @@ static struct mntent *iterator(char *filename, FILE **fp)
return NULL; return NULL;
} }
static int unmount(const char *target)
{
int rc;
dbg("Unmounting %s", target);
rc = umount(target);
if (rc) {
if (errno != EBUSY)
print(2, "Failed unmounting %s, error %d: %s", target, errno, strerror(errno));
return rc;
}
return 0;
}
void unmount_tmpfs(void) void unmount_tmpfs(void)
{ {
struct mntent *mnt; struct mntent *mnt;
FILE *fp = NULL; FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts", &fp))) { while ((mnt = iterator("/proc/mounts", &fp))) {
if (!strcmp("tmpfs", mnt->mnt_fsname) && !unmount(mnt->mnt_dir)) if (!strcmp("tmpfs", mnt->mnt_fsname) && !umount(mnt->mnt_dir))
iterator_end(&fp); /* Restart iteration */ iterator_end(&fp); /* Restart iteration */
} }
} }
@@ -126,7 +103,7 @@ void unmount_regular(void)
FILE *fp = NULL; FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts", &fp))) { while ((mnt = iterator("/proc/mounts", &fp))) {
if (!unmount(mnt->mnt_dir)) if (!umount(mnt->mnt_dir))
iterator_end(&fp); /* Restart iteration */ iterator_end(&fp); /* Restart iteration */
} }
} }
+6 -18
View File
@@ -1,7 +1,7 @@
/* PID and PID file helpers /* PID and PID file helpers
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */
#include <errno.h> #include <errno.h>
#include <paths.h> #include <paths.h>
#include <stdio.h> #include <stdio.h>
@@ -71,11 +69,10 @@ int pid_alive(pid_t pid)
*/ */
char *pid_get_name(pid_t pid, char *name, size_t len) char *pid_get_name(pid_t pid, char *name, size_t len)
{ {
static char line[64];
char *pname = NULL;
char path[32];
int ret = 1; int ret = 1;
FILE *fp; FILE *fp;
char *pname, path[32];
static char line[64];
snprintf(path, sizeof(path), "/proc/%d/status", pid); snprintf(path, sizeof(path), "/proc/%d/status", pid);
fp = fopen(path, "r"); fp = fopen(path, "r");
@@ -116,8 +113,8 @@ char *pid_file(svc_t *svc)
pid_t pid_file_read(const char *fn) pid_t pid_file_read(const char *fn)
{ {
pid_t pid = 0; pid_t pid = 0;
char buf[42];
FILE *fp; FILE *fp;
char buf[42];
fp = fopen(fn, "r"); fp = fopen(fn, "r");
if (!fp) if (!fp)
@@ -149,8 +146,6 @@ int pid_file_create(svc_t *svc)
int pid_file_set(svc_t *svc, char *file, int not) int pid_file_set(svc_t *svc, char *file, int not)
{ {
char buf[sizeof(svc->pidfile)];
if (!file) { if (!file) {
file = pid_file(svc); file = pid_file(svc);
if (!file) if (!file)
@@ -162,14 +157,7 @@ int pid_file_set(svc_t *svc, char *file, int not)
file++; file++;
} }
if (svc_is_forking(svc) && !not) { pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not);
logit(LOG_WARNING, "Service %s is forking, adjusting to pid:!%s", svc->name, file);
not = 1;
}
de_dotdot(file);
pid_runpath(file, buf, sizeof(buf));
strlcpy(&svc->pidfile[not], buf, sizeof(svc->pidfile) - not);
if (not) if (not)
svc->pidfile[0] = '!'; svc->pidfile[0] = '!';
@@ -211,8 +199,8 @@ int pid_file_parse(svc_t *svc, char *arg)
/* 'pid:' implies argument following*/ /* 'pid:' implies argument following*/
if (!strncmp(arg, "pid:", 4)) { if (!strncmp(arg, "pid:", 4)) {
char path[MAX_ARG_LEN];
int len, not = 0; int len, not = 0;
char path[MAX_ARG_LEN];
arg += 4; arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/') if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
+6 -6
View File
@@ -1,7 +1,7 @@
/* PID and PID file helpers /* PID and PID file helpers
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -51,7 +51,6 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
{ {
static char *prefix = "/var/run"; static char *prefix = "/var/run";
static int unknown = 1; static int unknown = 1;
char *ptr;
int rc; int rc;
if (unknown) { if (unknown) {
@@ -60,13 +59,14 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
unknown = 0; unknown = 0;
} }
ptr = strstr(file, "/run/"); if (!strncmp(file, "/var/run/", 9))
if (ptr) file += 9;
file = ptr + 5; else if (!strncmp(file, "/run/", 5))
file += 5;
rc = paste(path, len, prefix, file); rc = paste(path, len, prefix, file);
if (rc < 0 || (size_t)rc >= len) if (rc < 0 || (size_t)rc >= len)
err(1, "File path '%s' truncated, should end with '%s'", path, file); _e("File path '%s' truncated, should end with '%s'", path, file);
return path; return path;
} }
+33 -155
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit /* Plugin based services architecture for finit
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,8 +21,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */
#include <errno.h> #include <errno.h>
#include <dlfcn.h> /* dlopen() et al */ #include <dlfcn.h> /* dlopen() et al */
#include <dirent.h> /* readdir() et al */ #include <dirent.h> /* readdir() et al */
@@ -36,14 +34,13 @@
# include <lite/queue.h> /* BSD sys/queue.h API */ # include <lite/queue.h> /* BSD sys/queue.h API */
#endif #endif
#include "config.h"
#include "cond.h" #include "cond.h"
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "private.h" #include "private.h"
#include "service.h" #include "service.h"
#include "sig.h"
#include "util.h"
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0) #define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
#define SEARCH_PLUGIN(str) \ #define SEARCH_PLUGIN(str) \
@@ -52,8 +49,7 @@
return p; \ return p; \
} }
static int plugloaded = 0; /* Set while plugins are loaded */ static char *plugpath = NULL; /* Set by first load. */
static char *plugpath = NULL; /* Set by first load. */
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins); static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
#ifndef ENABLE_STATIC #ifndef ENABLE_STATIC
@@ -78,30 +74,27 @@ static char *trim_ext(char *name)
int plugin_register(plugin_t *plugin) int plugin_register(plugin_t *plugin)
{ {
const char *nm;
if (!plugin) { if (!plugin) {
errno = EINVAL; errno = EINVAL;
return 1; return 1;
} }
/* Setup default name if none is provided */ /* Setup default name if none is provided */
nm = plugin->name; if (!plugin->name) {
if (!nm) {
#ifndef ENABLE_STATIC #ifndef ENABLE_STATIC
Dl_info info; Dl_info info;
if (dladdr(plugin, &info) && info.dli_fname) if (dladdr(plugin, &info) && info.dli_fname)
nm = basenm(info.dli_fname); plugin->name = basename(info.dli_fname);
#endif #endif
if (!nm) if (!plugin->name)
nm = "unknown"; plugin->name = "unknown";
} }
plugin->name = trim_ext(strdup(nm)); plugin->name = trim_ext(strdup(plugin->name));
/* Already registered? */ /* Already registered? */
if (plugin_find(plugin->name)) { if (plugin_find(plugin->name)) {
dbg("... %s already loaded", plugin->name); _d("... %s already loaded", plugin->name);
free(plugin->name); free(plugin->name);
return 0; return 0;
@@ -126,10 +119,10 @@ int plugin_unregister(plugin_t *plugin)
#ifndef ENABLE_STATIC #ifndef ENABLE_STATIC
TAILQ_REMOVE(&plugins, plugin, link); TAILQ_REMOVE(&plugins, plugin, link);
dbg("%s exiting ...", plugin->name); _d("%s exiting ...", plugin->name);
free(plugin->name); free(plugin->name);
#else #else
dbg("Finit built statically, cannot unload %s ...", plugin->name); _d("Finit built statically, cannot unload %s ...", plugin->name);
#endif #endif
return 0; return 0;
@@ -164,8 +157,8 @@ plugin_t *plugin_find(char *name)
SEARCH_PLUGIN(name); SEARCH_PLUGIN(name);
if (plugpath && name[0] != '/') { if (plugpath && name[0] != '/') {
char path[CMD_SIZE];
int noext; int noext;
char path[CMD_SIZE];
noext = strcmp(name + strlen(name) - 3, ".so"); noext = strcmp(name + strlen(name) - 3, ".so");
snprintf(path, sizeof(path), "%s%s%s%s", plugpath, snprintf(path, sizeof(path), "%s%s%s%s", plugpath,
@@ -201,73 +194,22 @@ int plugin_exists(hook_point_t no)
return 0; return 0;
} }
#if defined(HAVE_HOOK_SCRIPTS_PLUGIN) && defined(PLUGIN_HOOK_SCRIPTS_PATH)
#define CHOOSE(x, y) y
static const char *hscript_paths[] = HOOK_TYPES;
#undef CHOOSE
void plugin_script_run(hook_point_t no)
{
const char *hook_name = hscript_paths[no];
const char *env[] = {
"FINIT_HOOK_NAME", hook_name,
"FINIT_SHUTDOWN", NULL,
NULL,
};
char path[CMD_SIZE] = "";
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
strlcat(path, hook_name + 4, sizeof(path));
if (no >= HOOK_SHUTDOWN) {
switch (halt) {
case SHUT_OFF:
env[3] = "poweroff";
break;
case SHUT_HALT:
env[3] = "halt";
break;
case SHUT_REBOOT:
env[3] = "reboot";
break;
}
} else
env[2] = NULL;
run_parts(path, NULL, env, 0, 0);
}
#else
void plugin_script_run(hook_point_t no)
{
(void)no;
}
#endif
/* Some hooks are called with a fixed argument */ /* Some hooks are called with a fixed argument */
void plugin_run_hook(hook_point_t no, void *arg) void plugin_run_hook(hook_point_t no, void *arg)
{ {
plugin_t *p, *tmp; plugin_t *p, *tmp;
#ifdef HAVE_HOOK_SCRIPTS_PLUGIN
if (!cond_is_available() && !plugloaded) {
dbg("conditions not available, calling script based hooks only!");
plugin_script_run(no);
}
#endif
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) { if (p->hook[no].cb) {
dbg("Calling %s hook n:o %d (arg: %p) ...", basenm(p->name), no, arg ?: "NIL"); _d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL");
p->hook[no].cb(arg ? arg : p->hook[no].arg); p->hook[no].cb(arg ? arg : p->hook[no].arg);
} }
} }
/* /* Conditions are stored in /run, so don't try to signal
* Conditions are stored in /run, so don't try to signal
* conditions for any hooks before filesystems have been * conditions for any hooks before filesystems have been
* mounted. * mounted. */
*/ if (no >= HOOK_MOUNT_ERROR)
if (cond_is_available() && no >= HOOK_BASEFS_UP && no <= HOOK_SHUTDOWN)
cond_set_oneshot(hook_cond[no]); cond_set_oneshot(hook_cond[no]);
service_step_all(SVC_TYPE_RUNTASK); service_step_all(SVC_TYPE_RUNTASK);
@@ -292,7 +234,7 @@ static void generic_io_cb(uev_t *w, void *arg, int events)
/* Stop watcher, callback may close descriptor on us ... */ /* Stop watcher, callback may close descriptor on us ... */
uev_io_stop(w); uev_io_stop(w);
// dbg("Calling I/O %s from runloop...", basename(p->name)); _d("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, w->fd, events); p->io.cb(p->io.arg, w->fd, events);
/* Update fd, may be changed by plugin callback, e.g., if FIFO */ /* Update fd, may be changed by plugin callback, e.g., if FIFO */
@@ -305,9 +247,9 @@ int plugin_io_init(plugin_t *p)
if (!is_io_plugin(p)) if (!is_io_plugin(p))
return 0; return 0;
dbg("Initializing plugin %s for I/O", basenm(p->name)); _d("Initializing plugin %s for I/O", basename(p->name));
if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) { if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) {
warn("Failed setting up I/O plugin %s", basenm(p->name)); _e("Failed setting up I/O plugin %s", basename(p->name));
return 1; return 1;
} }
@@ -317,8 +259,8 @@ int plugin_io_init(plugin_t *p)
/* Setup any I/O callbacks for plugins that use them */ /* Setup any I/O callbacks for plugins that use them */
static int init_plugins(uev_ctx_t *ctx) static int init_plugins(uev_ctx_t *ctx)
{ {
plugin_t *p, *tmp;
int fail = 0; int fail = 0;
plugin_t *p, *tmp;
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (plugin_io_init(p)) if (plugin_io_init(p))
@@ -359,18 +301,16 @@ static int load_one(char *path, char *name)
noext = strcmp(name + strlen(name) - 3, ".so"); noext = strcmp(name + strlen(name) - 3, ".so");
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : ""); snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
dbg("Loading plugin %s ...", sofile); _d("Loading plugin %s ...", basename(sofile));
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL); handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
if (!handle) { if (!handle) {
char *error = dlerror(); _e("Failed loading plugin %s: %s", sofile, dlerror());
warn("Failed loading plugin %s: %s", sofile, error ? error : "unknown error");
return 1; return 1;
} }
plugin = TAILQ_LAST(&plugins, plugin_head); plugin = TAILQ_LAST(&plugins, plugin_head);
if (!plugin) { if (!plugin) {
warn("Plugin %s failed to register, unloading from memory", sofile); _e("Plugin %s failed to register, unloading from memory", sofile);
dlclose(handle); dlclose(handle);
return 1; return 1;
} }
@@ -397,9 +337,9 @@ static void check_plugin_depends(plugin_t *plugin)
int i; int i;
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) { for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
// dbg("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]); // _d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
if (plugin_find(plugin->depends[i])) { if (plugin_find(plugin->depends[i])) {
// dbg("OK plugin %s was already loaded.", plugin->depends[i]); // _d("OK plugin %s was already loaded.", plugin->depends[i]);
continue; continue;
} }
@@ -409,15 +349,13 @@ static void check_plugin_depends(plugin_t *plugin)
static int load_plugins(char *path) static int load_plugins(char *path)
{ {
struct dirent *entry;
int fail = 0; int fail = 0;
DIR *dp; DIR *dp;
struct dirent *entry;
dp = opendir(path); dp = opendir(path);
if (!dp) { if (!dp) {
if (errno == ENOENT) _e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 0;
warn("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 1; return 1;
} }
plugpath = path; plugpath = path;
@@ -442,70 +380,14 @@ static int load_plugins(char *path)
} }
#endif /* ENABLE_STATIC */ #endif /* ENABLE_STATIC */
int plugin_list(char *buf, size_t len)
{
#ifndef ENABLE_STATIC
plugin_t *p, *tmp;
buf[0] = 0;
PLUGIN_ITERATOR(p, tmp) {
if (buf[0])
strlcat(buf, " ", len);
strlcat(buf, p->name, len);
}
#else
buf[0] = 0;
#endif
return 0;
}
int plugin_deps(char *buf, size_t len)
{
#ifndef ENABLE_STATIC
plugin_t *p;
p = plugin_find(buf);
buf[0] = 0;
if (p) {
int i;
for (i = 0; i < PLUGIN_DEP_MAX; i++) {
if (!p->depends[i])
continue;
if (buf[0])
strlcat(buf, " ", len);
strlcat(buf, p->depends[i], len);
}
}
#else
buf[0] = 0;
#endif
return 0;
}
int plugin_init(uev_ctx_t *ctx) int plugin_init(uev_ctx_t *ctx)
{ {
load_plugins(PLUGIN_PATH); int fail = 1;
#ifdef EXTERNAL_PLUGIN_PATH if (!load_plugins(PLUGIN_PATH))
char *paths = strdup(EXTERNAL_PLUGIN_PATH); fail = init_plugins(ctx);
char *path;
if (paths) { return fail;
dbg("Loading external plugins from %s ...", paths);
path = strtok(paths, ":");
while (path) {
load_plugins(path);
path = strtok(NULL, ":");
}
free(paths);
}
plugloaded = 1;
#endif
return init_plugins(ctx);
} }
void plugin_exit(void) void plugin_exit(void)
@@ -514,13 +396,9 @@ void plugin_exit(void)
plugin_t *p, *tmp; plugin_t *p, *tmp;
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (dlclose(p->handle)) { if (dlclose(p->handle))
char *error = dlerror(); _e("Failed: unloading plugin %s: %s", p->name, dlerror());
warn("Failed unloading plugin %s: %s", p->name, error ? error : "unknown error");
}
} }
plugloaded = 0;
#endif #endif
} }
+1 -8
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit /* Plugin based services architecture for finit
* *
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -72,7 +72,6 @@
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \ CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \ CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \ CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
CHOOSE(HOOK_SVC_PLUGIN, "hook/svc/plugin"), \
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \ CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \ CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \ CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
@@ -82,10 +81,7 @@
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \ CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
\ \
/* Shutdown hooks, runlevel [06] */ \ /* Shutdown hooks, runlevel [06] */ \
CHOOSE(HOOK_NETWORK_DN, "hook/net/down"), \
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \ CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
CHOOSE(HOOK_SVC_DN, "hook/svc/down"), \
CHOOSE(HOOK_SYS_DN, "hook/sys/down"), \
CHOOSE(HOOK_MAX_NUM, "nop") \ CHOOSE(HOOK_MAX_NUM, "nop") \
} }
@@ -150,9 +146,6 @@ typedef struct plugin {
/* Public plugin API */ /* Public plugin API */
int plugin_io_init (plugin_t *plugin); int plugin_io_init (plugin_t *plugin);
int plugin_list (char *buf, size_t len);
int plugin_deps (char *buf, size_t len);
int plugin_register (plugin_t *plugin); int plugin_register (plugin_t *plugin);
int plugin_unregister (plugin_t *plugin); int plugin_unregister (plugin_t *plugin);
+11 -25
View File
@@ -1,7 +1,7 @@
/* Private header file for main finit daemon, not for plugins /* Private header file for main finit daemon, not for plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,34 +29,20 @@
#include "plugin.h" #include "plugin.h"
#define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */ #define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */
#define MATCH_CMD(l, c, x) \ extern int service_interval;
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
#define IS_RESERVED_RUNLEVEL(l) (l == 0 || l == 6 || l == INIT_LEVEL) int api_init (uev_ctx_t *ctx);
int api_exit (void);
extern char *finit_conf; void service_monitor (pid_t lost, int status);
extern char *finit_rcsd;
extern svc_t *wdog;
extern int service_interval;
extern char *fsck_mode;
extern char *fsck_repair;
int api_init (uev_ctx_t *ctx); const char *plugin_hook_str(hook_point_t no);
int api_exit (void); int plugin_exists (hook_point_t no);
void conf_flush_events(void); void plugin_run_hook (hook_point_t no, void *arg);
void plugin_run_hooks (hook_point_t no);
void service_monitor (pid_t lost, int status); int plugin_init (uev_ctx_t *ctx);
void service_notify_cb(uev_t *w, void *arg, int events); void plugin_exit (void);
const char *plugin_hook_str (hook_point_t no);
int plugin_exists (hook_point_t no);
void plugin_run_hook (hook_point_t no, void *arg);
void plugin_run_hooks (hook_point_t no);
void plugin_script_run(hook_point_t no);
int plugin_init (uev_ctx_t *ctx);
void plugin_exit (void);
#endif /* FINIT_PRIVATE_H_ */ #endif /* FINIT_PRIVATE_H_ */
+12 -31
View File
@@ -1,6 +1,6 @@
/* reboot, halt, poweroff, shutdown, suspend for finit /* reboot, halt, poweroff, shutdown, suspend for finit
* *
* Copyright (c) 2009-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2009-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +26,6 @@
#include <getopt.h> #include <getopt.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "util.h" #include "util.h"
@@ -43,8 +42,6 @@ static cmd_t cmd = CMD_UNKNOWN;
static char *msg = NULL; static char *msg = NULL;
/* initctl API */ /* initctl API */
extern int timeout;
extern int do_reboot (char *arg); extern int do_reboot (char *arg);
extern int do_halt (char *arg); extern int do_halt (char *arg);
extern int do_poweroff(char *arg); extern int do_poweroff(char *arg);
@@ -105,13 +102,12 @@ static int usage(int rc)
{ {
fprintf(stderr, "Usage: %s [OPTIONS]\n\n" fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
"Options:\n" "Options:\n"
" -?, --help This help text\n" " --help This help text\n"
" -f, --force Force unsafe %s now, do not contact the init system.\n" " -f, --force Force unsafe %s now, do not contact the init system.\n"
" --halt Halt system, regardless of how the command is called.\n" " --halt Halt system, regardless of how the command is called.\n"
" -h Halt or power off after shutdown.\n" " -h Halt or power off after shutdown.\n"
" -P, --poweroff Power-off system, regardless of how the command is called.\n" " -P, --poweroff Power-off system, regardless of how the command is called.\n"
" -r, --reboot Reboot system, regardless of how the command is called.\n" " -r, --reboot Reboot system, regardless of how the command is called.\n"
" -t, --timeout=SEC Force reboot/shutdown after a given timeout\n"
"\n", prognm, msg); "\n", prognm, msg);
return rc; return rc;
@@ -119,22 +115,20 @@ static int usage(int rc)
int reboot_main(int argc, char *argv[]) int reboot_main(int argc, char *argv[])
{ {
int c, force = 0;
struct option long_options[] = { struct option long_options[] = {
{"help", 0, NULL, '?'}, {"help", 0, NULL, 'h'},
{"force", 0, NULL, 'f'}, {"force", 0, NULL, 'f'},
{"halt", 0, NULL, 'H'}, {"halt", 0, NULL, 'H'},
{"poweroff", 0, NULL, 'p'}, {"poweroff", 0, NULL, 'p'},
{"reboot", 0, NULL, 'r'}, {"reboot", 0, NULL, 'r'},
{"timeout", 1, NULL, 't'},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };
int c, force = 0;
int tmp;
/* Initial command taken from program name */ /* Initial command taken from program name */
transform(prognm); transform(prognm);
while ((c = getopt_long(argc, argv, "h?fHPprt:", long_options, NULL)) != EOF) { while ((c = getopt_long(argc, argv, "h?fHPpr", long_options, NULL)) != EOF) {
switch(c) { switch(c) {
case '?': case '?':
return usage(0); return usage(0);
@@ -157,13 +151,6 @@ int reboot_main(int argc, char *argv[])
cmd = CMD_REBOOT; cmd = CMD_REBOOT;
break; break;
case 't':
tmp = atoi(optarg);
if (tmp < 1)
errx(1, "Invalid forced timeout value.");
timeout = tmp;
break;
default: default:
return usage(1); return usage(1);
} }
@@ -172,13 +159,7 @@ int reboot_main(int argc, char *argv[])
/* Check for any overrides */ /* Check for any overrides */
transform(NULL); transform(NULL);
/* Check if we're in sulogin shell */
if (getenv("SULOGIN"))
force = 1;
if (force) { if (force) {
sync();
switch (cmd) { switch (cmd) {
case CMD_REBOOT: case CMD_REBOOT:
c = reboot(RB_AUTOBOOT); c = reboot(RB_AUTOBOOT);
@@ -193,9 +174,9 @@ int reboot_main(int argc, char *argv[])
break; break;
case CMD_SUSPEND: case CMD_SUSPEND:
c = suspend(); c = reboot(RB_SW_SUSPEND);
if (c && errno == EINVAL) if (c && errno == EINVAL)
errx(1, "Kernel does not support suspend to RAM."); errx(1, "Kernel does not support suspend.");
break; break;
case CMD_UNKNOWN: case CMD_UNKNOWN:
-242
View File
@@ -1,242 +0,0 @@
#include <ctype.h> /* isdigit() */
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#ifdef _LIBITE_LITE
# include <libite/conio.h>
# include <libite/lite.h>
#else
# include <lite/conio.h>
# include <lite/lite.h>
#endif
#include "log.h"
#include "sig.h"
#include "util.h"
#ifdef __FINIT__
#include "helpers.h"
#else
#include <sys/prctl.h>
const char *active_msg = NULL;
int interactive = 1;
int debug;
static void print_desc(const char *prefix, const char *msg)
{
if (interactive)
fprintf(stderr, "\e[1m[\e[0m\e[1;33m ⋯ \e[0m\e[1m]\e[0m %s %s", prefix, msg);
else
fprintf(stderr, "%s %s ...\n", prefix, msg);
active_msg = msg;
}
static void print_result(int rc)
{
if (interactive) {
if (rc)
fputs("\r\e[1m[\e[0m\e[1;31mFAIL\e[0m\e[1m]\e[0m\n", stderr);
else
fputs("\r\e[1m[\e[0m\e[1;32m OK \e[0m\e[1m]\e[0m\n", stderr);
} else
fprintf(stderr, "[%s] %s\n", rc ? "FAIL" : " OK ", active_msg ?: "");
}
void sig_unblock(void)
{
struct sigaction sa;
sigset_t nmask;
int i;
sigemptyset(&nmask);
sigaddset(&nmask, SIGHUP);
sigaddset(&nmask, SIGCHLD);
sigaddset(&nmask, SIGINT);
sigaddset(&nmask, SIGPWR);
sigaddset(&nmask, SIGSTOP);
sigaddset(&nmask, SIGTSTP);
sigaddset(&nmask, SIGCONT);
sigaddset(&nmask, SIGTERM);
sigaddset(&nmask, SIGUSR1);
sigaddset(&nmask, SIGUSR2);
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
/* Reset signal handlers that were set by the parent process */
for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
}
#endif /* __FINIT__ */
static void run_env(const char *env[])
{
size_t i = 0;
if (!env)
return;
while (env[i]) {
size_t j = i + 1;
if (!env[j])
break;
setenv(env[i], env[j], 1);
i += 2;
}
}
int run_parts(char *dir, char *cmd, const char *env[], int progress, int sysv)
{
size_t cmdlen = cmd ? strlen(cmd) : strlen("start");
struct dirent **d;
int i, num;
int rc = 0;
num = scandir(dir, &d, NULL, alphasort);
if (num < 0) {
dbg("No files found in %s, skipping ...", dir);
return -1;
}
for (i = 0; i < num; i++) {
char path[strlen(dir) + strlen(d[i]->d_name) + 3 + cmdlen];
const char *name = d[i]->d_name;
char *argv[4] = {
"sh",
"-c",
path,
NULL
};
struct stat st;
int result = 0;
pid_t pid = 0;
int status;
/* skip backup files */
if (name[strlen(name) - 1] == '~')
continue;
/* cannot rely on d_type, not supported on all filesystems */
paste(path, sizeof(path), dir, name);
if (stat(path, &st)) {
warn("failed stat(%s)", path);
continue;
}
/* skip non-executable files and directories */
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
dbg("skipping %s not an executable or is a directory", path);
continue;
}
if (sysv && name[0] != 'S' && name[0] != 'K') {
dbg("S-only flag set, skipping non-SysV script: %s", name);
continue;
}
/* If the callee didn't supply a run_parts() argument */
if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */
if (name[0] == 'S' && isdigit(name[1]))
strlcat(path, " start", sizeof(path));
else if (name[0] == 'K' && isdigit(name[1]))
strlcat(path, " stop", sizeof(path));
} else {
strlcat(path, " ", sizeof(path));
strlcat(path, cmd, sizeof(path));
}
if (progress)
print_desc("Calling", path);
pid = fork();
if (!pid) {
sig_unblock();
run_env(env);
_exit(execvp(_PATH_BSHELL, argv));
}
if (waitpid(pid, &status, 0) == -1) {
warnx("failed starting %s, error %d: %s", path, errno, strerror(errno));
result = 1;
} else {
if (WIFEXITED(status)) {
result = WEXITSTATUS(status);
dbg("%s exited with status %d", path, result);
} else if (WIFSIGNALED(status)) {
warnx("%s terminated by signal %d", path, WTERMSIG(status));
result = 1;
} else
result = 1;
}
if (progress)
print_result(result);
rc += result;
}
while (num--)
free(d[num]);
free(d);
return rc;
}
#ifndef __FINIT__
static int usage(int rc)
{
warnx("usage: runparts [-dhps?] DIRECTORY");
return rc;
}
int main(int argc, char *argv[])
{
int rc, c, progress = 0, sysv = 0;
char *dir;
while ((c = getopt(argc, argv, "bdh?ps")) != EOF) {
switch(c) {
case 'b': /* batch mode */
interactive = 0;
break;
case 'd':
debug = 1;
break;
case 'h':
case '?':
return usage(0);
case 'p':
progress = 1;
break;
case 's':
sysv = 1;
break;
default:
return usage(1);
}
}
if (optind >= argc)
return usage(1);
dir = argv[optind];
if (!fisdir(dir))
return usage(1);
prctl(PR_SET_CHILD_SUBREAPER, 1);
rc = run_parts(dir, NULL, NULL, progress, sysv);
if (rc == -1)
err(1, "failed run-parts %s", dir);
return rc;
}
#endif /* __FINIT__ */
+1 -1
View File
@@ -1,6 +1,6 @@
/* Work queue helper functions /* Work queue helper functions
* *
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,6 +1,6 @@
/* Work queue helper functions /* Work queue helper functions
* *
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+86 -206
View File
@@ -1,6 +1,6 @@
/* List and enable/disable service configurations /* List and enable/disable service configurations
* *
* Copyright (c) 2017-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2017-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include <err.h>
#include <fcntl.h> /* Definition of AT_* constants */ #include <fcntl.h> /* Definition of AT_* constants */
#include <glob.h> #include <glob.h>
#include <stdio.h> #include <stdio.h>
@@ -33,28 +34,9 @@
# include <lite/lite.h> # include <lite/lite.h>
#endif #endif
#include "client.h"
#include "initctl.h" #include "initctl.h"
static int is_builtin(char *arg)
{
svc_t *svc;
/* skip check, we may be in non-responsive bootstrap */
if (iforce)
return 0;
svc = client_svc_find(arg);
if (!svc)
return 0;
if (svc->file[0])
return 0;
return 1;
}
static int calc_width(char *arr[], size_t len) static int calc_width(char *arr[], size_t len)
{ {
int width = 0; int width = 0;
@@ -70,7 +52,7 @@ static int calc_width(char *arr[], size_t len)
return width; return width;
} }
static void do_list(const char *path, char *title, int comma) static void do_list(const char *path)
{ {
static int once = 0; static int once = 0;
int width, num, prev; int width, num, prev;
@@ -82,7 +64,6 @@ static void do_list(const char *path, char *title, int comma)
if (!fexist(path)) if (!fexist(path))
return; return;
dir = strdupa(path); dir = strdupa(path);
if (!dir) if (!dir)
return; return;
@@ -93,11 +74,6 @@ static void do_list(const char *path, char *title, int comma)
else else
ptr = dir; ptr = dir;
if (json) {
printf("%s\"%s\": \"%s\"", comma ? "," :"", title, path);
return;
}
if (heading) if (heading)
print_header("%s%s ", once ? "\n" : "", dir); print_header("%s%s ", once ? "\n" : "", dir);
puts(ptr); puts(ptr);
@@ -111,7 +87,7 @@ static void do_list(const char *path, char *title, int comma)
if (gl.gl_pathc <= 0) if (gl.gl_pathc <= 0)
goto done; goto done;
if (plain && !json) { if (plain) {
if (heading) if (heading)
print_header("%s%s ", once ? "\n" : "", path); print_header("%s%s ", once ? "\n" : "", path);
once++; once++;
@@ -129,10 +105,8 @@ static void do_list(const char *path, char *title, int comma)
goto done; goto done;
} }
if (heading && !json) if (heading)
print_header("%s ", path); print_header("%s ", path);
if (json)
printf("%s\"%s\": [", comma ? "," : "", title);
width = calc_width(gl.gl_pathv, gl.gl_pathc); width = calc_width(gl.gl_pathv, gl.gl_pathc);
if (width <= 0) if (width <= 0)
@@ -144,24 +118,17 @@ static void do_list(const char *path, char *title, int comma)
prev = 0; prev = 0;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
if (i > 0 && !(i % num) && !json) { if (i > 0 && !(i % num)) {
puts(""); puts("");
prev = 0; prev = 0;
} }
if (prev) if (prev)
printf("%s", json ? "," : " "); printf(" ");
if (json) printf("%-*s", width, gl.gl_pathv[i]);
printf("\"%s\"", gl.gl_pathv[i]);
else
printf("%-*s", width, gl.gl_pathv[i]);
prev++; prev++;
} }
puts("\n");
if (json)
fputs("]", stdout);
else
puts("\n");
done: done:
globfree(&gl); globfree(&gl);
@@ -170,40 +137,29 @@ done:
int serv_list(char *arg) int serv_list(char *arg)
{ {
char path[256]; char path[256];
int pre = 0;
if (arg && arg[0]) { if (arg && arg[0]) {
paste(path, sizeof(path), finit_rcsd, arg); paste(path, sizeof(path), FINIT_RCSD, arg);
if (fisdir(path)) { if (fisdir(path)) {
if (json) do_list(path);
fputs("[", stdout);
do_list(path, NULL, 0);
if (json)
fputs("]\n", stdout);
return 0; return 0;
} }
/* fall back to list all */ /* fall back to list all */
} }
if (json) paste(path, sizeof(path), FINIT_RCSD, "available");
fputs("{", stdout);
paste(path, sizeof(path), finit_rcsd, "available");
if (fisdir(path)) if (fisdir(path))
do_list(path, "available", pre++); do_list(path);
paste(path, sizeof(path), finit_rcsd, "enabled"); paste(path, sizeof(path), FINIT_RCSD, "enabled");
if (fisdir(path)) if (fisdir(path))
do_list(path, "enabled", pre++); do_list(path);
if (fisdir(finit_rcsd)) if (fisdir(FINIT_RCSD))
do_list(finit_rcsd, "static", pre++); do_list(FINIT_RCSD);
if (fexist(finit_conf)) if (fexist(FINIT_CONF))
do_list(finit_conf, "finit.conf", pre++); do_list(FINIT_CONF);
if (json)
fputs("}\n", stdout);
return 0; return 0;
} }
@@ -222,145 +178,99 @@ int serv_list(char *arg)
* If the resulting file doesn't exist, and creat is not set, *or* * If the resulting file doesn't exist, and creat is not set, *or*
* the base directory doesn't exist, we return NULL. * the base directory doesn't exist, we return NULL.
.*/ .*/
static char *conf(char *path, size_t len, char *name, int creat, int template) static char *conf(char *path, size_t len, char *name, int creat)
{ {
char corr[40];
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) { if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
strlcpy(path, finit_conf, len); strlcpy(path, FINIT_CONF, len);
return path; return path;
} }
if (!fisdir(finit_rcsd)) if (!strstr(name, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", name);
name = corr;
}
if (!fisdir(FINIT_RCSD))
return NULL; return NULL;
paste(path, len, finit_rcsd, "available/"); paste(path, len, FINIT_RCSD, "available/");
if (!fisdir(path)) { if (!fisdir(path)) {
if (creat && mkdir(path, 0755) && errno != EEXIST) if (creat && mkdir(path, 0755) && errno != EEXIST)
return NULL; return NULL;
paste(path, len, finit_rcsd, name); else
paste(path, len, FINIT_RCSD, name);
} else } else
strlcat(path, name, len); strlcat(path, name, len);
if (suffix(path, len, ".conf"))
return NULL;
/* fall back to static service unless edit/create */ /* fall back to static service unless edit/create */
if (!creat && !fexist(path)) { if (!creat && !fexist(path))
char *at = strchr(path, '@'); paste(path, len, FINIT_RCSD, name);
if (template && at)
*++at = 0;
else
paste(path, len, finit_rcsd, name);
if (suffix(path, len, ".conf"))
return NULL;
}
return path; return path;
} }
int serv_enable(char *arg) int serv_enable(char *arg)
{ {
char *serv, *ptr, *src, *dst; char corr[40];
size_t len; char path[256];
int rc; int ena;
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
WARNX("missing argument to enable, may be one of:"); warnx("missing argument to enable, may be one of:");
return serv_list("available"); return serv_list("available");
} }
if (arg[0] == '/') { if (!strstr(arg, ".conf")) {
WARNX("absolute paths not supported, this command manages %s/available/", finit_rcsd); snprintf(corr, sizeof(corr), "%s.conf", arg);
return serv_list("available"); arg = corr;
} }
len = strlen(arg); if (chdir(FINIT_RCSD))
serv = alloca(len + 6); err(1, "failed cd %s", FINIT_RCSD);
if (!serv)
ERR(71, "failed allocating stack");
strlcpy(serv, arg, len + 6);
if (len < 6 || strcmp(&serv[len - 5], ".conf"))
strlcat(serv, ".conf", len + 6);
if (chdir(finit_rcsd))
ERR(72, "failed cd %s", finit_rcsd);
if (icreate && mkdir("enabled", 0755) && EEXIST != errno) if (icreate && mkdir("enabled", 0755) && EEXIST != errno)
ERR(73, "failed creating %s/enabled directory", finit_rcsd); err(1, "failed creating %s/enabled directory", FINIT_RCSD);
ena = !chdir("enabled"); /* System *may* have enabled/ dir. */
if (asprintf(&src, "%s/available/%s", finit_rcsd, serv) == -1) snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg);
goto fail; if (!fexist(path))
if (asprintf(&dst, "%s/enabled/%s", finit_rcsd, serv) == -1) { errx(1, "cannot find %s", conf(path, sizeof(path), arg, 0));
free(src);
fail:
ERR(71, "failed allocating memory");
}
if (fexist(dst)) { if (fexist(arg))
WARNX("%s already enabled", arg); errx(1, "%s already enabled", arg);
rc = noerr ? 0 : 1;
goto done;
}
/* check for template instantiation */ return symlink(path, arg) != 0;
ptr = strchr(src, '@');
if (ptr)
strcpy(++ptr, ".conf");
if (!fexist(src)) {
WARNX("cannot find %s%s", ptr ? "template ": "", src);
rc = 72;
goto done;
}
rc = symlink(src, dst);
if (rc)
WARN("failed enabling %s", arg);
done:
free(src);
free(dst);
return rc;
} }
int do_disable(char *arg, int check) int do_disable(char *arg, int check)
{ {
struct stat st; struct stat st;
size_t len; char corr[40];
char *serv;
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
WARNX("missing argument to disable, may be one of:"); warnx("missing argument to disable, may be one of:");
return serv_list("enabled"); return serv_list("enabled");
} }
if (arg[0] == '/') { if (!strstr(arg, ".conf")) {
WARNX("absolute paths not supported, this command manages %s/enabled/", finit_rcsd); snprintf(corr, sizeof(corr), "%s.conf", arg);
return serv_list("available"); arg = corr;
} }
len = strlen(arg) + 7; if (chdir(FINIT_RCSD))
serv = alloca(len); err(1, "failed cd %s", FINIT_RCSD);
if (!serv)
ERR(71, "failed allocating stack");
strlcpy(serv, arg, len);
if (suffix(serv, len, ".conf"))
ERR(71, "failed composing service name (%s)", serv);
if (chdir(finit_rcsd))
ERR(72, "failed cd %s", finit_rcsd);
if (chdir("enabled")) /* System *may* have enabled/ dir. */ if (chdir("enabled")) /* System *may* have enabled/ dir. */
dbg("Failed changing to %s/enabled/: %s", finit_rcsd, strerror(errno)); dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
if (check && stat(serv, &st)) if (check && stat(arg, &st))
ERRX(noerr ? 0 : 6, "%s not (an) enabled (service).", serv); errx(1, "%s not (an) enabled (service).", arg);
if (check && (st.st_mode & S_IFMT) == S_IFLNK) if (check && (st.st_mode & S_IFMT) == S_IFLNK)
ERRX(noerr ? 0 : 1, "cannot disable %s, not a symlink.", serv); errx(1, "cannot disable %s, not a symlink.", arg);
return remove(serv) != 0; return remove(arg) != 0;
} }
int serv_disable(char *arg) int serv_disable(char *arg)
@@ -374,34 +284,22 @@ int serv_touch(char *arg)
char *fn; char *fn;
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
WARNX("missing argument to touch, may be one of:"); warnx("missing argument to touch, may be one of:");
return serv_list("enabled"); return serv_list("enabled");
} }
/* 1. Try /etc/finit.d/enabled/$arg.conf to handle template@.conf */ fn = conf(path, sizeof(path), arg, 0);
paste(path, sizeof(path), finit_rcsd, "enabled/");
strlcat(path, arg, sizeof(path));
if (!suffix(path, sizeof(path), ".conf") && fexist(path)) {
fn = path;
goto touchit;
}
/* 2. Try /etc/finit.d/available/$arg.conf and other combos (legacy) */
fn = conf(path, sizeof(path), arg, 0, 0);
if (!fexist(fn)) { if (!fexist(fn)) {
if (!strstr(arg, "finit.conf")) if (!strstr(arg, "finit.conf"))
ERRX(noerr ? 0 : 72, "%s not available.", arg); errx(1, "%s not available.", arg);
if (is_builtin(arg))
ERRX(noerr ? 0 : 4, "%s is a built-in service.", arg);
strlcpy(path, finit_conf, sizeof(path)); strlcpy(path, FINIT_CONF, sizeof(path));
fn = path; fn = path;
} }
touchit:
/* libite:touch() follows symlinks */ /* libite:touch() follows symlinks */
if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW)) if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW))
ERR(noerr ? 0 : 71, "failed marking %s for reload", fn); err(1, "failed marking %s for reload", fn);
return 0; return 0;
} }
@@ -411,18 +309,10 @@ int serv_show(char *arg)
char path[256]; char path[256];
char *fn; char *fn;
fn = conf(path, sizeof(path), arg, 0, 0); fn = conf(path, sizeof(path), arg, 0);
if (!fexist(fn)) { if (!fexist(fn)) {
if (is_builtin(arg)) warnx("Cannot find %s", arg);
ERRX(4, "%s is a built-in service.", arg); return 1;
fn = conf(path, sizeof(path), arg, 0, 1);
if (!fn || !fexist(fn)) {
WARNX("Cannot find %s", arg);
return 1;
}
return systemf("cat %s | sed 's/%%i/system/g'", fn);
} }
return systemf("cat %s", fn); return systemf("cat %s", fn);
@@ -453,13 +343,10 @@ static int do_edit(char *arg, int creat)
char path[256]; char path[256];
char *fn; char *fn;
fn = conf(path, sizeof(path), arg, creat, 0); fn = conf(path, sizeof(path), arg, creat);
if (!fexist(fn)) { if (!fexist(fn)) {
if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg);
if (!creat) { if (!creat) {
WARNX("Cannot find %s, use -c flag, create command, or select one of:", arg); warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
return serv_list(NULL); return serv_list(NULL);
} }
@@ -468,7 +355,7 @@ static int do_edit(char *arg, int creat)
copyfile(SAMPLE_CONF, fn, 0, 0); copyfile(SAMPLE_CONF, fn, 0, 0);
#endif #endif
} else if (creat) } else if (creat)
WARNX("the file %s already exists, falling back to edit.", fn); warnx("the file %s already exists, falling back to edit.", fn);
for (size_t i = 0; i < NELEMS(editor); i++) { for (size_t i = 0; i < NELEMS(editor); i++) {
if (systemf("%s %s 2>/dev/null", editor[i], path)) if (systemf("%s %s 2>/dev/null", editor[i], path))
@@ -482,9 +369,8 @@ static int do_edit(char *arg, int creat)
int serv_edit(char *arg) int serv_edit(char *arg)
{ {
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
if (!yorn("Do you want to edit %s (y/N)? ", finit_conf)) if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF))
return 0; return 0;
arg = "";
} }
return do_edit(arg, icreate); return do_edit(arg, icreate);
@@ -497,28 +383,25 @@ int serv_creat(char *arg)
FILE *fp; FILE *fp;
if (!arg || !arg[0]) if (!arg || !arg[0])
ERRX(2, "missing argument to create"); errx(1, "missing argument to create");
if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg);
/* Input from a pipe or a proper TTY? */ /* Input from a pipe or a proper TTY? */
if (isatty(STDIN_FILENO)) if (isatty(STDIN_FILENO))
return do_edit(arg, 1); return do_edit(arg, 1);
/* Open fn for writing from pipe */ /* Open fn for writing from pipe */
fn = conf(buf, sizeof(buf), arg, 1, 0); fn = conf(buf, sizeof(buf), arg, 1);
if (!fn) if (!fn)
ERR(73, "failed creating conf %s", arg); err(1, "failed creating conf %s", arg);
if (!icreate && fexist(fn)) { if (!icreate && fexist(fn)) {
WARNX("%s already exists, skipping (use -c to override)", fn); warnx("%s already exists, skipping (use -c to override)", fn);
fn = "/dev/null"; fn = "/dev/null";
} }
fp = fopen(fn, "w"); fp = fopen(fn, "w");
if (!fp) if (!fp)
ERR(73, "failed opening %s for writing", fn); err(1, "failed opening %s for writing", fn);
while (fgets(buf, sizeof(buf), stdin)) while (fgets(buf, sizeof(buf), stdin))
fputs(buf, fp); fputs(buf, fp);
@@ -532,24 +415,21 @@ int serv_delete(char *arg)
char *fn; char *fn;
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
WARNX("missing argument to delete, may be one of:"); warnx("missing argument to delete, may be one of:");
return serv_list("available"); return serv_list("available");
} }
fn = conf(buf, sizeof(buf), arg, 0, 0); fn = conf(buf, sizeof(buf), arg, 0);
if (!fn) { if (!fn)
if (is_builtin(arg)) errx(1, FINIT_RCSD " missing on system.");
ERRX(4, "%s is a built-in service.", arg);
ERRX(72, "%s missing on system.", finit_rcsd);
}
if (!fexist(fn)) if (!fexist(fn))
WARNX("cannot find %s", fn); warnx("cannot find %s", fn);
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) { if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
do_disable(arg, 0); do_disable(arg, 0);
if (remove(fn)) if (remove(fn))
ERR(1, "Failed removing %s", fn); err(1, "Failed removing %s", fn);
} }
return 0; return 0;
+1 -1
View File
@@ -1,6 +1,6 @@
/* List and enable/disable service configurations /* List and enable/disable service configurations
* *
* Copyright (c) 2017-2024 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2017-2021 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal

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