Compare commits

..
5 Commits
Author SHA1 Message Date
Joachim Wiberg 197c5767ea Switch from PTY to pipe for service log redirection
The PTY approach caused isatty() to return true for services using
the log directive, triggering programs like fprintd (using glib) to
emit ANSI escape codes and other TTY-specific formatting in syslog.

Using a standard pipe ensures isatty() correctly returns false, so
programs produce plain text output suitable for logging.

For services that require line-buffered output, users can wrap the
command with `stdbuf -oL` as documented in doc/config/logging.md.

Fixes #455

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-06 15:41:30 +01:00
Joachim Wiberg 22072e477c Change default reboot to SOC reset from WDT reset
Fixes #460

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-06 15:01:31 +01:00
Joachim Wiberg bda2f94e8a Allow skipping bootstrap wait with Ctrl-C
When setting up a new system with Finit it is very common to make small
logical mistakes that cause "hangs" at boot.  This is when Finit waits
for 180 sec. for run/tasks to complete before moving to the configured
runlevel.  This patch adds console input monitoring during bootstrap
wait that allows users to press Ctrl-C to skip waiting and proceed to
the configured runlevel.

When Ctrl-C is detected, all incomplete run/task/services are logged
to syslog for later troubleshooting after login.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-27 13:09:10 +01:00
Joachim Wiberg abb4561516 Allow more initctl commands in runlevels S/0/6
Relax the constraints introduced in a39ee0b, for issue #342, a bit on
when start/stop/restart/reload service can be called.  Also, allow
'initctl reload', but ignore it when the system is in runlevels S/0/6.

This makes it possible to start manual:yes type services at botostrap,
for example, which has been a common feature request.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-27 11:33:39 +01:00
Joachim Wiberg 309cc7f741 Ensure mount/unmount skips 'noauto' entries
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-27 11:30:27 +01:00
283 changed files with 2925 additions and 31731 deletions
+26 -30
View File
@@ -20,6 +20,7 @@ Please take care to ensure you follow the project coding style and the
commit message format. If you follow these recommendations you help commit message format. If you follow these recommendations you help
the maintainer(s) and make it easier for them to include your code. the maintainer(s) and make it easier for them to include your code.
Coding Style Coding Style
------------ ------------
@@ -30,7 +31,8 @@ First of all, lines are allowed to be longer than 72 characters these
days. In fact, there exist no enforced maximum, but keeping it around days. In fact, there exist no enforced maximum, but keeping it around
100 chars is OK. 100 chars is OK.
The coding style itself is strictly [Linux coding style][KNF]. The coding style itself is strictly Linux [KNF][].
Commit Messages Commit Messages
--------------- ---------------
@@ -39,38 +41,31 @@ Commit messages exist to track *why* a change was made. Try to be as
clear and concise as possible in your commit messages, and always, be clear and concise as possible in your commit messages, and always, be
proud of your work and set up a proper GIT identity for your commits: proud of your work and set up a proper GIT identity for your commits:
```bash git config --global user.name "Jane Doe"
$ git config --global user.name "Jane Doe" git config --global user.email jane.doe@example.com
$ git config --global user.email jane.doe@example.com
```
Example commit message from the [Pro Git][gitbook] online book, notice Example commit message from the [Pro Git][gitbook] online book, notice
how `git commit -s` is used to automatically add a `Signed-off-by`: how `git commit -s` is used to automatically add a `Signed-off-by`:
``` Brief, but clear and concise summary of changes
Brief, but clear and concise summary of changes
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as
the subject of an email and the rest of the text as the body. The
blank line separating the ummary from the body is critical (unless
you omit the body entirely); tools like rebase can get confused if
you run the two together.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded
by a single space, with blank lines in between, but conventions
vary here
Signed-off-by: Jane Doe <jane.doe@example.com>
More detailed explanatory text, if necessary. Remember, a future
reader has the diff and can see what it does, the commit message in
itself should usually talk about the *why* of a change and/or any
technical limitations and future work.
Wrap the text to about 72 characters or so. In some contexts, the
first line is treated as the subject of an email and the rest of the
text as the body. The blank line separating the summary from the body
is critical (unless you omit the body entirely); tools like rebase can
get confused if you run the two together.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded
by a single space, with blank lines in between, but conventions
vary here
Signed-off-by: Jane Doe <jane.doe@example.com>
```
Code of Conduct Code of Conduct
--------------- ---------------
@@ -78,6 +73,7 @@ Code of Conduct
It is expected of everyone engaging in the project to, in the words of 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].
[KNF]: https://www.kernel.org/doc/html/v5.8/process/coding-style.html
[gitbook]: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
[gitbook]: https://git-scm.com/book/ch5-2.html
[conduct]: CODE-OF-CONDUCT.md [conduct]: CODE-OF-CONDUCT.md
+1 -1
View File
@@ -5,7 +5,7 @@
finit is a small project, as such we have no possibility to support older versions. finit is a small project, as such we have no possibility to support older versions.
The only supported version is the latest released on GitHub: The only supported version is the latest released on GitHub:
<https://github.com/finit-project/finit/releases> <https://github.com/troglobit/finit/releases>
## Reporting a Vulnerability ## Reporting a Vulnerability
-29
View File
@@ -1,29 +0,0 @@
#!/bin/sh
#
# Extract the topmost release entry from doc/ChangeLog.md and unwrap
# its paragraphs, so the GitHub releases page can wrap them to the
# browser width instead of showing the hard 72-column breaks.
#
# Usage: .github/scripts/extract-changelog.sh < doc/ChangeLog.md > release.md
#
set -e
# The entry sits between the first two underlines, minus the blank
# line separating it from the next heading.
awk '/^-----*$/{if (x == 1) exit; x=1;next}x' | head -n -1 |
# Blank lines, headings, blockquotes, tables, and list items (at any
# indent) start a new block, everything else continues the current one.
awk '
function flush() { if (buf) print buf; buf = "" }
{
if (/^$/) { flush(); print; blank = 1; next }
if (/^ *([-*] |[0-9]+\. |[>#|])/ || blank || !buf || brk)
{ flush(); buf = $0 }
else
{ sub(/^ +/, ""); buf = buf " " $0 }
blank = 0
brk = / $/
}
END { flush() }
'
+9 -120
View File
@@ -18,71 +18,6 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
fuzz:
name: fuzz
runs-on: ubuntu-latest
if: github.event_name != 'push' || github.ref == 'refs/heads/master'
steps:
- name: Install dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config libconfuse-dev clang libblkid-dev
# clang picks the newest gcc tree it finds and needs the
# matching libstdc++ headers to link the fuzzer runtime
sudo apt-get -y install libstdc++-14-dev || true
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v7
- name: Configure
run: |
./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
- name: Build fuzz target
run: |
clang -fsanitize=fuzzer,address -DLINK_FUZZ_LIBFUZZER -D_GNU_SOURCE \
-I libink -I . -o fuzz-msg-parse \
test/src/fuzz-msg-parse.c libink/*.c
# Restores the newest corpus and saves a fresh one, since a cache
# entry is immutable once written. Caches made on a branch are
# private to it, so the corpus that accumulates on master is what
# pull requests start from, rather than nothing.
- name: Restore corpus
uses: actions/cache@v6
with:
path: .fuzz-corpus
key: fuzz-corpus-${{ github.run_id }}
restore-keys: fuzz-corpus-
- name: Fuzz
run: |
mkdir -p .fuzz-corpus
./fuzz-msg-parse .fuzz-corpus -max_total_time=120 -max_len=4096 \
-print_final_stats=1
# Without this the corpus only ever grows, and most of what it
# accumulates reaches code some earlier input already reached.
- name: Minimise corpus
if: always()
run: |
mkdir -p .fuzz-corpus-min
./fuzz-msg-parse -merge=1 .fuzz-corpus-min .fuzz-corpus
rm -rf .fuzz-corpus
mv .fuzz-corpus-min .fuzz-corpus
echo "corpus: $(ls .fuzz-corpus | wc -l) inputs"
- name: Upload crashers
if: failure()
uses: actions/upload-artifact@v7
with:
name: fuzz-crashers
path: |
crash-*
leak-*
timeout-*
if-no-files-found: ignore
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 }}
@@ -99,27 +34,21 @@ jobs:
- 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 libcap-dev libconfuse-dev libblkid-dev libpam0g-dev sudo apt-get -y install pkg-config tree jq libuev-dev libite-dev
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz - uses: actions/checkout@v4
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v7
- name: Static Finit - name: Static Finit
run: | run: |
./autogen.sh ./autogen.sh
./configure --prefix= --enable-static ./configure --prefix= --enable-static
make -j9 V=1 make -j3 V=1
- name: Regular Finit - name: Regular Finit
run: | run: |
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin \ --enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd --with-libsystemd \
CFLAGS="-fsanitize=address -ggdb" CFLAGS="-fsanitize=address -ggdb"
make -j9 clean make -j3 clean
make -j9 V=1 make -j3 V=1
- name: Install to /tmp - name: Install to /tmp
run: | run: |
DESTDIR=/tmp make install-strip DESTDIR=/tmp make install-strip
@@ -141,49 +70,9 @@ jobs:
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0 sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Run Unit Tests - name: Run Unit Tests
run: | run: |
make -j1 check || (cat test/test-suite.log; false) make check || (cat test/test-suite.log; false)
- name: Upload Test Results - name: Upload Test Results
if: always() uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with: with:
name: finit-test-${{ matrix.compiler }} name: finit-test-${{ matrix.compiler }}
path: test/*.log path: test/*.log
no-dbus:
# The D-Bus support is default-enabled, so the HAVE_DBUS paths in
# initctl and finit only bit-rot silently without this leg.
name: no-dbus
runs-on: ubuntu-latest
if: github.event_name != 'push' || github.ref == 'refs/heads/master'
steps:
- name: Install dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config tree jq libcap-dev libconfuse-dev libblkid-dev
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v7
- name: Build without D-Bus
run: |
./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--disable-dbus --enable-testserv-plugin
make -j9 V=1
- name: Verify no bus artifacts
run: |
DESTDIR=/tmp make install-strip
! strings /tmp/sbin/initctl | grep -q finit/bus
! strings /tmp/sbin/finit | grep -q org.finit
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Smoke test
run: |
make -C test setup-chroot
make -j1 -C test check TESTS='start-stop-serv.sh' \
|| (cat test/test-suite.log; false)
+12 -11
View File
@@ -19,7 +19,7 @@ jobs:
coverity: coverity:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v4
- name: Fetch latest Coverity Scan MD5 - name: Fetch latest Coverity Scan MD5
id: var id: var
env: env:
@@ -29,7 +29,7 @@ jobs:
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \ --post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
-O coverity-latest.tar.gz.md5 -O coverity-latest.tar.gz.md5
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
- uses: actions/cache@v6 - uses: actions/cache@v4
id: cache id: cache
with: with:
path: coverity-latest.tar.gz path: coverity-latest.tar.gz
@@ -55,18 +55,19 @@ jobs:
- 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 libcap-dev libconfuse-dev libblkid-dev sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz wget https://github.com/troglobit/libuev/releases/download/v2.4.0/libuev-2.4.0.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz wget https://github.com/troglobit/libite/releases/download/v2.5.2/libite-2.5.2.tar.gz
tar xf libuev-2.4.1.tar.xz tar xf libuev-2.4.0.tar.xz
tar xf libite-2.6.2.tar.gz tar xf libite-2.5.2.tar.gz
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip) (cd libuev-2.4.0 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip) (cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig sudo ldconfig
- name: Configure - name: Configure
run: | run: |
./autogen.sh ./autogen.sh
./configure --prefix= --enable-x11-common-plugin ./configure --prefix= --enable-x11-common-plugin --with-watchdog \
--with-keventd --with-libsystemd
- name: Build - name: Build
run: | run: |
export PATH=`pwd`/coverity/bin:$PATH export PATH=`pwd`/coverity/bin:$PATH
@@ -85,7 +86,7 @@ jobs:
--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 - name: Upload build.log
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: coverity-build.log name: coverity-build.log
path: cov-int/build-log.txt path: cov-int/build-log.txt
+18 -113
View File
@@ -4,12 +4,10 @@ on:
push: push:
branches: branches:
- master - master
- 4.x
paths: paths:
- 'doc/**' - 'doc/**'
- 'README.md' - 'README.md'
- 'mkdocs.yml' - 'mkdocs.yml'
- 'configure.ac'
- '.github/workflows/docs.yml' - '.github/workflows/docs.yml'
pull_request: pull_request:
types: [opened, synchronize, reopened, labeled] types: [opened, synchronize, reopened, labeled]
@@ -17,32 +15,28 @@ on:
- 'doc/**' - 'doc/**'
- 'README.md' - 'README.md'
- 'mkdocs.yml' - 'mkdocs.yml'
- 'configure.ac'
- '.github/workflows/docs.yml' - '.github/workflows/docs.yml'
permissions: permissions:
contents: read contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v7 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # Needed for git-revision-date-localized plugin fetch-depth: 0 # Needed for git-revision-date-localized plugin
- name: Determine docs version
id: version
run: |
version=$(sed -n 's/^AC_INIT(\[Finit\], *\[\([0-9]*\)\..*/\1.x/p' configure.ac)
test -n "$version"
echo "version=$version" >> $GITHUB_OUTPUT
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v7 uses: actions/setup-python@v4
with: with:
python-version: '3.x' python-version: '3.x'
@@ -52,112 +46,23 @@ jobs:
pipx inject mkdocs mkdocs-material pipx inject mkdocs mkdocs-material
pipx inject mkdocs pymdown-extensions pipx inject mkdocs pymdown-extensions
pipx inject mkdocs mkdocs-callouts pipx inject mkdocs mkdocs-callouts
pipx inject mkdocs mkdocs-glightbox
- name: Build documentation - name: Build documentation
run: mkdocs build --clean run: mkdocs build
- name: Upload site artifact - name: Upload Pages artifact
uses: actions/upload-artifact@v7 uses: actions/upload-pages-artifact@v3
with: with:
name: site
path: site/ path: site/
deploy: deploy:
if: github.event_name == 'push' if: github.ref == 'refs/heads/master' && github.event_name == 'push'
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: environment:
VERSION: ${{ needs.build.outputs.version }} name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps: steps:
- name: Download site artifact - name: Deploy to GitHub Pages
uses: actions/download-artifact@v8 id: deployment
with: uses: actions/deploy-pages@v4
name: site
path: site/
- name: Checkout pages repo
uses: actions/checkout@v7
with:
repository: finit-project/finit-project.github.io
path: pages
ssh-key: ${{ secrets.DOCS_DEPLOY_KEY }}
persist-credentials: true
- name: Setup SSH for push
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
ssh-keyscan github.com >> ~/.ssh/known_hosts
- name: Sync site to pages repo
run: |
rsync -a --delete site/ "pages/$VERSION/"
- name: Update version index and redirects
run: |
cd pages/
# The root holds only version directories and the files
# generated below, drop anything else (pre-versioned site)
find . -maxdepth 1 ! -name . ! -name .git ! -name CNAME \
! -name '[0-9]*.x' -exec rm -rf {} +
latest=$(ls -d [0-9]*.x | sort -rV | head -1)
sep=
printf '[' > versions.json
for v in $(ls -d [0-9]*.x | sort -rV); do
printf '%s{"version": "%s", "title": "%s", "aliases": []}' \
"$sep" "$v" "$v" >> versions.json
sep=', '
done
printf ']\n' >> versions.json
cat > index.html <<EOF
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=$latest/">
<link rel="canonical" href="https://finit-project.github.io/$latest/">
</head>
</html>
EOF
cat > 404.html <<EOF
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Finit &mdash; page not found</title>
<script>
var path = window.location.pathname;
if (!/^\/[0-9]+\.x(\/|$)/.test(path))
window.location.replace("/$latest" + path);
</script>
</head>
<body>
<p>Page not found. Try the <a href="/$latest/">latest User Guide</a>.</p>
</body>
</html>
EOF
- name: Commit and push
run: |
cd pages/
if [ -z "$(git status --porcelain)" ]; then
exit 0
fi
SRC_REPO="${GITHUB_REPOSITORY}"
SRC_REF="${GITHUB_SHA::7}"
SRC_URL="https://github.com/${SRC_REPO}/commit/${SRC_REF}"
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add -A
git commit -m "docs: update from ${SRC_REPO}@${SRC_REF}" \
-m "For details, see ${SRC_URL}"
git push
+10 -24
View File
@@ -13,19 +13,14 @@ jobs:
name: Build and upload release tarball name: Build and upload release tarball
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
MAKEFLAGS: -j3
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v4
- name: Installing dependencies ... - name: Installing dependencies ...
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install pkg-config jq libcap-dev libconfuse-dev libblkid-dev sudo apt-get -y install pkg-config jq libuev-dev libite-dev
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- name: Setting release variables ... - name: Setting release variables ...
id: build id: build
run: | run: |
@@ -46,30 +41,21 @@ jobs:
./autogen.sh ./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc \ ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc \
--localstatedir=/var --enable-x11-common-plugin \ --localstatedir=/var --enable-x11-common-plugin \
--enable-testserv-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd --with-libsystemd
- name: Enable unprivileged userns (unshare) - name: Enable unprivileged userns (unshare)
run: | run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0 sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Build release ... - name: Build release ...
run: | run: |
# Get version for finding distcheck build directory make release || (cat test/test-suite.log; false)
./configure --version | head -1
ver=$(./configure --version | head -1 | awk '{print $NF}')
dir=finit-$ver/_build/sub
make -j1 release || (cat $dir/test/test-suite.log; false)
ls -lF ../ ls -lF ../
mkdir -p artifacts/ mkdir -p artifacts/
mv ../*.tar.gz artifacts/ mv ../*.tar.* artifacts/
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v7
with:
name: finit-release-test-logs
path: finit-*/_build/sub/test/*.log
- name: Extract ChangeLog entry ... - name: Extract ChangeLog entry ...
run: | run: |
.github/scripts/extract-changelog.sh < doc/ChangeLog.md > release.md awk '/-----*/{if (x == 1) exit; x=1;next}x' doc/ChangeLog.md \
|head -n -1 > release.md
cat release.md cat release.md
- uses: ncipollo/release-action@v1 - uses: ncipollo/release-action@v1
with: with:
+5 -18
View File
@@ -14,14 +14,14 @@ jobs:
schedule: schedule:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
run: ${{ steps.schedule.outputs.run }} run: ${{ steps.check.outputs.run }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v4
- name: Check for any commits since last week - name: Check for any commits since last week
id: schedule id: schedule
run: | run: |
commits="$(git log --format=%H --since='1 week ago')" commits="$(git log --format=%H --since='1 week ago')"
if [ -z "$commits" ]; then if [ -n "$commits" ]; then
echo "run=true" >> $GITHUB_OUTPUT echo "run=true" >> $GITHUB_OUTPUT
else else
echo "run=false" >> $GITHUB_OUTPUT echo "run=false" >> $GITHUB_OUTPUT
@@ -34,15 +34,8 @@ jobs:
- 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 jq libcap-dev libconfuse-dev libblkid-dev sudo apt-get -y install pkg-config jq libuev-dev libite-dev
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz - uses: actions/checkout@v4
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v7
- name: Base run - name: Base run
run: | run: |
./autogen.sh ./autogen.sh
@@ -59,9 +52,3 @@ jobs:
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}') ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
dir=finit-$ver/_build/sub dir=finit-$ver/_build/sub
make -j1 distcheck || (cat $dir/test/test-suite.log; false) make -j1 distcheck || (cat $dir/test/test-suite.log; false)
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v7
with:
name: finit-weekly-test-logs
path: finit-*/_build/sub/test/*.log
-5
View File
@@ -52,8 +52,3 @@ GTAGS
# VS Code user settings # VS Code user settings
.vscode .vscode
# Fuzzing: the target is built by hand with clang, see doc/build.md,
# and the corpus it grows is a local artefact, not something to ship
/fuzz-msg-parse
/.fuzz-corpus/
+16 -29
View File
@@ -1,21 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man plugins src system tmpfiles.d
# libink must precede src in SUBDIRS because finit links against
# libink at build time. Automake recurses subdirs strictly in
# declaration order, so the typical "explicit dependency" pattern
# (foo: bar) doesn't help here — ordering is the only thing that
# does. libsystemd is consumed by test/serv only, so its position
# after src is fine.
SUBDIRS = man plugins
if DBUS
SUBDIRS += libink dbus-1
endif
SUBDIRS += src system tmpfiles.d
if KEVENTD
SUBDIRS += keventd
endif
dist_doc_DATA = README.md LICENSE contrib/finit.conf dist_doc_DATA = README.md LICENSE contrib/finit.conf
if CONTRIB if CONTRIB
@@ -40,20 +24,23 @@ endif
install-dev: install-dev:
@make -C src install-pkgincludeHEADERS @make -C src install-pkgincludeHEADERS
# The fuzz target is built by hand with clang (doc/build.md), so it is
# not in any _PROGRAMS and nothing else would clear it or the corpus
# it grows.
distclean-local:
-rm -f $(top_builddir)/fuzz-msg-parse
-rm -rf $(top_builddir)/.fuzz-corpus
# Target to run when building a release # Target to run when building a release
release: distcheck release: distcheck
@for file in $(DIST_ARCHIVES); do \
md5sum $$file > ../$$file.md5; \
sha256sum $$file > ../$$file.sha256; \
done
@mv $(DIST_ARCHIVES) ../ @mv $(DIST_ARCHIVES) ../
@echo @echo
@echo "Resulting release tarball(s): $(DIST_ARCHIVES)" @echo "Resulting release files:"
@echo "================================================================="
@for file in $(DIST_ARCHIVES); do \
printf "$$file \tDistribution tarball\n"; \
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
done
DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \ DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
--enable-testserv-plugin --enable-x11-common-plugin \ --enable-testserv-plugin --enable-x11-common-plugin \
--with-fstab=/etc/fstab --with-libsystemd \ --with-watchdog --with-keventd --with-fstab=/etc/fstab \
--with-bash-completion-dir=no --with-libsystemd --with-bash-completion-dir=no
+19 -22
View File
@@ -1,16 +1,17 @@
[![License Badge][]][License] [![Release Badge][]][Release] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan] [![License Badge][]][License] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan]
<img align="right" src="doc/img/finit3.png" alt="Finit: Fast Init"> <img align="right" src="doc/img/finit3.png" alt="Finit: Fast Init">
Finit is a fast, simple alternative to [SysV init](https://en.wikipedia.org/wiki/Init) Finit is an alternative to [SysV init](https://en.wikipedia.org/wiki/Init)
and [systemd](https://systemd.io), designed for small and embedded Linux systems. and [systemd](https://www.freedesktop.org/wiki/Software/systemd/), focused
It can also run on desktop and server systems, like [finix][]. on small and embedded Linux systems, although fully usable on server and
[desktop systems][finix].
> Reverse engineered from the [EeePC fastinit][] > Reverse engineered from the [EeePC fastinit][]
> "gaps filled with frog DNA …" > "gaps filled with frog DNA …"
> — [Claudio Matsuoka][] > — [Claudio Matsuoka][]
For detailed information, explore our extensive documentation For detailed information, explore our extensive documentation
:books: **<http://finit-project.github.io>** :books: **<https://troglobit.github.io/finit/>**
<div align="center"> <div align="center">
<img src="doc/img/alpine-screenshot2.png" alt="Alpine screenshot"><br/> <img src="doc/img/alpine-screenshot2.png" alt="Alpine screenshot"><br/>
@@ -19,32 +20,28 @@ For detailed information, explore our extensive documentation
For working examples, see the :rocket: [contrib/](contrib/) section or these For working examples, see the :rocket: [contrib/](contrib/) section or these
tutorials: tutorials:
* :hammer_and_wrench: [Buildroot embedded Linux][br2demo], * :hammer_and_wrench: [Buildroot embedded Linux](https://troglobit.com/post/2022-12-26-buildroot-demo-of-fastinit-finit/),
* :package: [Debian GNU/Linux](contrib/debian/), * :package: [Debian GNU/Linux](contrib/debian/),
* :mountain: [Alpine Linux](contrib/alpine/), and * :mountain: [Alpine Linux](contrib/alpine/), and
* :milky_way: [Void Linux](contrib/void/) * :milky_way: [Void Linux](contrib/void/)
> [!NOTE] > [!NOTE]
> Finit can run on various Linux distributions, but the bundled install > Support for various Linux distributions does not mean Finit installs
> scripts are examples only. They have been tested on amd64 (x86_64) systems > easily on all architectures. Bundled install scripts are examples for
> with standard configurations. > standard installations, tested on amd64 (x86_64) systems. Custom
> > setups, e.g., for embedded systems, can be found in the following
> For embedded systems, see these [Buildroot][]-based examples: > [Buildroot][] based examples: [myLinux][], [Infix][], or the plain
> [myLinux][], [Infix][], or [br2-finit-demo][]. > [br2-finit-demo](https://github.com/troglobit/br2-finit-demo).
[finix]: https://github.com/finix-community/finix [finix]: https://github.com/aanderse/finix
[Buildroot]: https://buildroot.org [Buildroot]: https://buildroot.org
[Infix]: https://kernelkit.github.io [Infix]: https://kernelkit.github.io
[myLinux]: https://github.com/troglobit/myLinux/ [myLinux]: https://github.com/troglobit/myLinux/
[br2demo]: https://troglobit.com/post/2022-12-26-buildroot-demo-of-fastinit-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
[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
[Release]: https://github.com/finit-project/finit/releases [GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
[Release Badge]: https://img.shields.io/github/v/release/finit-project/finit [GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
[GitHub]: https://github.com/finit-project/finit/actions/workflows/build.yml/
[GitHub Status]: https://github.com/finit-project/finit/actions/workflows/build.yml/badge.svg
[Coverity Scan]: https://scan.coverity.com/projects/3545 [Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg [Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
[br2-finit-demo]: https://github.com/finit-project/br2-finit-demo
+16 -86
View File
@@ -1,5 +1,5 @@
AC_INIT([Finit], [5.0-rc1], [https://github.com/finit-project/finit/issues], AC_INIT([Finit], [4.14], [https://github.com/troglobit/finit/issues],
[finit], [https://http://finit-project.github.io/]) [finit], [https://troglobit.com/projects/finit/])
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects]) AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
@@ -12,13 +12,10 @@ AC_CONFIG_FILES([Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
doc/Makefile doc/config/Makefile doc/Makefile doc/config/Makefile
dbus-1/Makefile
libink/Makefile
libsystemd/Makefile libsystemd/libsystemd.pc libsystemd/Makefile libsystemd/libsystemd.pc
man/Makefile man/Makefile
plugins/Makefile plugins/Makefile
src/Makefile src/Makefile
keventd/Makefile
system/Makefile system/10-hotplug.conf system/Makefile system/10-hotplug.conf
test/test.env test/Makefile test/test.env test/Makefile
test/lib/Makefile test/src/Makefile test/lib/Makefile test/src/Makefile
@@ -40,7 +37,7 @@ AC_PROG_MKDIR_P
# Configuration. # Configuration.
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h]) AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h])
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r close_range]) AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r])
# Check for uint[8,16,32]_t # Check for uint[8,16,32]_t
AC_TYPE_UINT8_T AC_TYPE_UINT8_T
@@ -51,14 +48,8 @@ AC_TYPE_UINT32_T
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
# Check for required libraries # Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 2.4.1]) PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.6.1]) PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
# 3.3 is the floor: CFGF_KEYSTRVAL, which environment {} and the
# free-form cgroup keys are built on, does not exist before it. 3.3
# parses both correctly. Two workarounds hang off this number, the
# spurious KEYSTRVAL warning and cfg_parse_buf() losing the file name
# of a template; grep the XXX notes in src/conf.c before raising it.
PKG_CHECK_MODULES([confuse], [libconfuse >= 3.3])
# Check for configured Finit features # Check for configured Finit features
AC_ARG_ENABLE(auto_reload, AC_ARG_ENABLE(auto_reload,
@@ -81,15 +72,6 @@ AC_ARG_ENABLE(cgroup,
AS_HELP_STRING([--disable-cgroup], [Disable cgroup v2 support, default: autodetect from /sys/fs/cgroup]),,[ AS_HELP_STRING([--disable-cgroup], [Disable cgroup v2 support, default: autodetect from /sys/fs/cgroup]),,[
enable_cgroup=yes]) enable_cgroup=yes])
AC_ARG_ENABLE(libcap,
AS_HELP_STRING([--disable-libcap], [Disable Linux capabilities support]),,[
enable_libcap=yes])
AC_ARG_ENABLE(pam,
AS_HELP_STRING([--disable-pam], [Disable PAM session support for services]),
[pam_explicit=yes],
[enable_pam=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])
@@ -102,10 +84,6 @@ 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]),,[
enable_logrotate=yes]) enable_logrotate=yes])
AC_ARG_ENABLE(dbus,
AS_HELP_STRING([--disable-dbus], [Disable D-Bus support (libink + Finit object tree)]),,[
enable_dbus=yes])
AC_ARG_ENABLE(doc, AC_ARG_ENABLE(doc,
AS_HELP_STRING([--disable-doc], [Disable build and install of doc/ section]),,[ AS_HELP_STRING([--disable-doc], [Disable build and install of doc/ section]),,[
enable_doc=yes]) enable_doc=yes])
@@ -123,18 +101,17 @@ AC_ARG_ENABLE(all_plugins,
AS_HELP_STRING([--enable-all-plugins], [Enable all plugins, default: auto]), AS_HELP_STRING([--enable-all-plugins], [Enable all plugins, default: auto]),
enable_all_plugins=$enableval, enable_all_plugins=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], [yes], [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], [no], [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([hook-scripts], [no], [Trigger script execution from hook points])
AC_PLUGIN([hotplug], [no], [Start udevd or mdev kernel event datamon]) 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([plymouth], [no], [Plymouth boot splash integration])
AC_PLUGIN([testserv], [no], [Test plugin to start test serv daemon]) AC_PLUGIN([testserv], [no], [Test plugin to start test serv daemon])
# Check for extra arguments or packages # Check for extra arguments or packages
@@ -167,7 +144,7 @@ AC_ARG_WITH(sysconfig,
[sysconfig=$withval], [sysconfig=default]) [sysconfig=$withval], [sysconfig=default])
AC_ARG_WITH(group, AC_ARG_WITH(group,
AS_HELP_STRING([--with-group=NAME], [Group for /run/finit/socket and /run/finit/bus, default: root]), AS_HELP_STRING([--with-group=NAME], [Group for /run/finit/socket (initctl), default: root]),
[group=$withval], [group=root]) [group=$withval], [group=root])
AC_ARG_WITH(hostname, AC_ARG_WITH(hostname,
@@ -183,20 +160,17 @@ AC_ARG_WITH(random-seed,
[random_seed=$withval], [random_seed=yes]) [random_seed=$withval], [random_seed=yes])
AC_ARG_WITH(keventd, AC_ARG_WITH(keventd,
AS_HELP_STRING([--with-keventd], [Enable built-in keventd device manager, default: yes]),, [with_keventd=yes]) AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
AC_ARG_WITH(udev-rules,
AS_HELP_STRING([--without-udev-rules], [Skip install of curated udev rules under /lib/udev/rules.d, default: yes when keventd enabled]),, [with_udev_rules=yes])
AC_ARG_WITH(sulogin, AC_ARG_WITH(sulogin,
AS_HELP_STRING([--with-sulogin@<:@=USER@:>@], [Built-in sulogin, optional USER to request password for, default: yes (root)]),[sulogin=$withval],[with_sulogin=yes sulogin=yes]) 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@:>@], [Built-in watchdog, default: yes (/dev/watchdog)]), AS_HELP_STRING([--with-watchdog@<:@=DEV@:>@], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval], [with_watchdog=yes watchdog=yes]) [watchdog=$withval], [with_watchdog=no watchdog=])
AC_ARG_WITH(libsystemd, AC_ARG_WITH(libsystemd,
AS_HELP_STRING([--with-libsystemd], [Build replacement libsystemd library, default: no]), AS_HELP_STRING([--with-libsystemd], [Build replacement libsystemd library, default: yes]),
[with_libsystemd=$withval], [with_libsystemd=no]) [with_libsystemd=$withval], [with_libsystemd=no])
AC_ARG_WITH(hook-scripts-path, AC_ARG_WITH(hook-scripts-path,
@@ -230,36 +204,6 @@ AS_IF([test "x$enable_kernel_logging" = "xyes"], [
AS_IF([test "x$enable_cgroup" = "xyes"], [ AS_IF([test "x$enable_cgroup" = "xyes"], [
AC_DEFINE(CGROUP2_ENABLED, 1, [Autodetect cgroup v2 support from /sys/fs/cgroup])]) AC_DEFINE(CGROUP2_ENABLED, 1, [Autodetect cgroup v2 support from /sys/fs/cgroup])])
AS_IF([test "x$enable_libcap" = "xyes"], [
AC_CHECK_LIB([cap], [cap_from_text], [
AC_DEFINE(HAVE_LIBCAP, 1, [Have libcap for Linux capabilities support])
LIBS="$LIBS -lcap"
], [
AC_MSG_WARN([libcap not found, capabilities support disabled])
enable_libcap=no
])
])
AS_IF([test "x$enable_pam" = "xyes"], [
AS_IF([test "x$enable_static" = "xyes"], [
AS_IF([test "x$pam_explicit" = "xyes"], [
AC_MSG_ERROR([--enable-pam does not work with --enable-static, PAM dlopen()s its modules])], [
AC_MSG_NOTICE([PAM disabled for static build, PAM dlopen()s its modules])
enable_pam=no])])
])
AS_IF([test "x$enable_pam" = "xyes"], [
AC_CHECK_HEADER([security/pam_appl.h], [], [
AC_MSG_WARN([PAM headers not found, PAM support disabled])
enable_pam=no])
AS_IF([test "x$enable_pam" = "xyes"], [
AC_CHECK_LIB([pam], [pam_start], [
AC_DEFINE(HAVE_LIBPAM, 1, [Have Linux-PAM for per-service session support])
LIBS="$LIBS -lpam"
], [
AC_MSG_WARN([libpam not found, PAM support disabled])
enable_pam=no])])
])
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])])
@@ -278,12 +222,6 @@ AS_IF([test "x$enable_rescue" != "xno"], [
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"]) AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
AS_IF([test "x$enable_dbus" = "xyes"], [
AC_DEFINE(HAVE_DBUS, 1, [Build D-Bus support via libink])])
AM_CONDITIONAL(DBUS, [test "x$enable_dbus" = "xyes"])
AM_CONDITIONAL(PAM, [test "x$enable_pam" = "xyes"])
### With features ############################################################################## ### With features ##############################################################################
AS_IF([test "x$bash_dir" = "xyes"], [ AS_IF([test "x$bash_dir" = "xyes"], [
PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
@@ -329,7 +267,7 @@ AS_IF([test "x$with_heading" != "xno"], [
AS_IF([test "x$with_group" != "xno"], [ AS_IF([test "x$with_group" != "xno"], [
AS_IF([test "x$group" = "xyes"], [ AS_IF([test "x$group" = "xyes"], [
group=root])]) group=root])])
AC_DEFINE_UNQUOTED(DEFGROUP, "$group", [For /run/finit/socket and /run/finit/bus]) AC_DEFINE_UNQUOTED(DEFGROUP, "$group", [For /run/finit/socket])
AS_IF([test "x$with_hostname" != "xno"], [ AS_IF([test "x$with_hostname" != "xno"], [
AS_IF([test "x$hostname" = "xyes"], [ AS_IF([test "x$hostname" = "xyes"], [
@@ -359,11 +297,7 @@ AS_IF([test "x$rtc_file" != "xno"], [
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, "$rtcfile_path", [Save and restore system time from this file if /dev/rtc is missing.])],[
AC_DEFINE_UNQUOTED(RTC_FILE, NULL)]) AC_DEFINE_UNQUOTED(RTC_FILE, NULL)])
AS_IF([test "x$with_keventd" != "xno"], [ AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
with_keventd=yes
PKG_CHECK_MODULES([blkid], [blkid],, [
AC_MSG_ERROR([libblkid (util-linux) is required to build keventd.
Install the dev package (e.g. libblkid-dev) or disable keventd with --without-keventd])])])
AS_IF([test "x$with_sulogin" != "xno"], [ AS_IF([test "x$with_sulogin" != "xno"], [
AS_IF([test "x$sulogin" = "xyes"], [ AS_IF([test "x$sulogin" = "xyes"], [
@@ -396,7 +330,6 @@ AS_IF([test "x$with_plugin_path" != "xno"], [
AM_CONDITIONAL(BASH, [test "x$bash_dir" != "xno"]) 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(UDEV_RULES, [test "x$with_keventd" != "xno" -a "x$with_udev_rules" != "xno"])
AM_CONDITIONAL(SULOGIN, [test "x$with_sulogin" != "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(LIBSYSTEMD, [test "x$with_libsystemd" != "xno"]) AM_CONDITIONAL(LIBSYSTEMD, [test "x$with_libsystemd" != "xno"])
@@ -480,15 +413,12 @@ Optional features:
Install doc/..........: $enable_doc Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib Install contrib/......: $enable_contrib
Bash completion.......: $bash_completion_dir Bash completion.......: $bash_completion_dir
Built-in keventd......: $with_keventd (blkid: $blkid_LIBS) Built-in keventd......: $with_keventd
Built-in sulogin......: $with_sulogin $sulogin 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
D-Bus support (libink): $enable_dbus
Replacement libsystemd: $with_libsystemd Replacement libsystemd: $with_libsystemd
Use cgroup v2.........: $enable_cgroup Use cgroup v2.........: $enable_cgroup
Use libcap............: $enable_libcap
Use PAM...............: $enable_pam
Parse kernel cmdline..: $enable_kernel_cmdline Parse kernel cmdline..: $enable_kernel_cmdline
Keep kernel logging...: $enable_kernel_logging Keep kernel logging...: $enable_kernel_logging
Skip fsck check.......: $enable_fastboot Skip fsck check.......: $enable_fastboot
+6 -16
View File
@@ -3,34 +3,24 @@
## Runlevel to start after bootstrap, runlevel 'S' ## Runlevel to start after bootstrap, runlevel 'S'
# Default is 2 # Default is 2
#runlevel = 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory # Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup # but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are # 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the # as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names. # cgroup v2 controller default names.
#cgroup init { cpu.weight = 100 } #cgroup init cpu.weight:100
#cgroup user { cpu.weight = 100 } #cgroup user cpu.weight:100
#cgroup system { cpu.weight = 9700 } #cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by # Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel). # Finit upon receiving SIGINT (from the kernel).
#task reboot { #task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# description = "Rebooting system"
# runlevel = "12345789"
# conditions = { "sys/key/ctrlaltdel" }
# command = "initctl reboot"
#}
# Task to run if the kernel gets a power fail condition is pressed. # Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel). # Asserted by Finit upon receiving SIGPWR (from the kernel).
#task poweroff { #task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
# description = "Shutting down system"
# runlevel = "12345789"
# conditions = { "sys/pwr/fail" }
# command = "initctl poweroff"
#}
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+1 -7
View File
@@ -1,9 +1,3 @@
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager) # Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
# Depends on syslogd having started. Redirect any output to log. # Depends on syslogd having started. Redirect any output to log.
service acpid { service [2345] <pid/syslogd> log acpid -f -- ACPI daemon
description = "ACPI daemon"
runlevel = "2345"
conditions = { "pid/syslogd" }
log { }
command = "acpid -f"
}
+1 -7
View File
@@ -1,7 +1 @@
service crond { service [2345] <pid/syslogd> env:-/etc/conf.d/crond crond -f -S $CRON_OPTS -- Cron daemon
description = "Cron daemon"
runlevel = "2345"
conditions = { "pid/syslogd" }
envfile = "-/etc/conf.d/crond"
command = "crond -f -S $CRON_OPTS"
}
@@ -1,7 +1,2 @@
# Start SSH daemon as soon as basic networking comes up # Start SSH daemon as soon as basic networking comes up
service dropbear { service [2345] env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
description = "Dropbear SSH daemon"
runlevel = "2345"
envfile = "-/etc/conf.d/dropbear"
command = "dropbear -R -F $DROPBEAR_OPTS"
}
+4 -23
View File
@@ -1,24 +1,5 @@
# Consoles to start getty on when system is up # Consoles to start getty on when system is up
tty ttyS0 { tty [12345] /dev/ttyS0 noclear
runlevel = "12345" tty [12345] /dev/tty1 noclear nowait
device = "/dev/ttyS0" tty [2345] /dev/tty2 noclear nowait
noclear = true tty [2345] /dev/tty3 noclear nowait
}
tty tty1 {
runlevel = "12345"
device = "/dev/tty1"
noclear = true
nowait = true
}
tty tty2 {
runlevel = "2345"
device = "/dev/tty2"
noclear = true
nowait = true
}
tty tty3 {
runlevel = "2345"
device = "/dev/tty3"
noclear = true
nowait = true
}
+1 -6
View File
@@ -1,7 +1,2 @@
# Oneshot task to run once at bootstrap # Oneshot task to run once at bootstrap
task keymap { task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
description = "Loading keymap"
runlevel = "S"
envfile = "/etc/conf.d/loadkmap"
command = "zcat $KEYMAP | loadkmap"
}
+1 -5
View File
@@ -1,5 +1 @@
service lxdm { service [2345] lxdm -- Lightweight Login Manager
description = "Lightweight Login Manager"
runlevel = "2345"
command = "lxdm"
}
@@ -1,2 +1,7 @@
## Linux kernel modules to load ## Linux kernel modules to load
#modules = { "fbcon", "button", "evdev", "mousedev", "8139cp", "softdog" } #module fbcon
#module button
#module evdev
#module mousedev
#module 8139cp
#module softdog
+1 -8
View File
@@ -1,9 +1,2 @@
# Busybox ntpd has no option to use syslog when running in foreground # Busybox ntpd has no option to use syslog when running in foreground
service ntpd { service [2345] <pid/syslogd> env:-/etc/conf.d/ntpd log ntpd -n $NTPD_OPTS -- NTP daemon
description = "NTP daemon"
runlevel = "2345"
conditions = { "pid/syslogd" }
envfile = "-/etc/conf.d/ntpd"
log { }
command = "ntpd -n $NTPD_OPTS"
}
+2 -13
View File
@@ -1,15 +1,4 @@
# Start syslogd as soon as possible, should always run # Start syslogd as soon as possible, should always run
# Start klogd as soon as possible, but after syslogd # Start klogd as soon as possible, but after syslogd
service syslogd { service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
description = "Syslog daemon" service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
runlevel = "S12345"
envfile = "-/etc/conf.d/syslog"
command = "syslogd -n $SYSLOGD_OPTS"
}
service klogd {
description = "Kernel log daemon"
runlevel = "S12345"
conditions = { "pid/syslogd" }
envfile = "-/etc/conf.d/klogd"
command = "klogd -n $KLOGD_OPTS"
}
@@ -1,6 +1 @@
service watchdog { service [S123456789] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog
description = "System watchdog"
runlevel = "S123456789"
envfile = "-/etc/conf.d/watchdog"
command = "watchdog $WATCHDOG_OPTS $WATCHDOG_DEV"
}
+6 -16
View File
@@ -3,34 +3,24 @@
## Runlevel to start after bootstrap, runlevel 'S' ## Runlevel to start after bootstrap, runlevel 'S'
# Default is 2 # Default is 2
#runlevel = 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory # Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup # but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are # 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the # as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names. # cgroup v2 controller default names.
#cgroup init { cpu.weight = 100 } #cgroup init cpu.weight:100
#cgroup user { cpu.weight = 100 } #cgroup user cpu.weight:100
#cgroup system { cpu.weight = 9700 } #cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by # Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel). # Finit upon receiving SIGINT (from the kernel).
#task reboot { #task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# description = "Rebooting system"
# runlevel = "12345789"
# conditions = { "sys/key/ctrlaltdel" }
# command = "initctl reboot"
#}
# Task to run if the kernel gets a power fail condition is pressed. # Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel). # Asserted by Finit upon receiving SIGPWR (from the kernel).
#task poweroff { #task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
# description = "Shutting down system"
# runlevel = "12345789"
# conditions = { "sys/pwr/fail" }
# command = "initctl poweroff"
#}
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+1 -8
View File
@@ -1,10 +1,3 @@
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager) # Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
# Depends on a syslog daemon having started. Redirect any output to log. # Depends on a syslog daemon having started. Redirect any output to log.
service acpid { service [2345] <pid/syslogd> env:-/etc/default/acpid log acpid -f $OPTIONS -- ACPI daemon
description = "ACPI daemon"
runlevel = "2345"
conditions = { "pid/syslogd" }
envfile = "-/etc/default/acpid"
log { }
command = "acpid -f $OPTIONS"
}
@@ -1,8 +1 @@
task anacron { task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
description = "Anacron jobs"
runlevel = "2345"
conditions = { "sys/pwr/ac", "pid/syslogd" }
envfile = "-/etc/default/anacron"
stop-signal = "usr1"
command = "anacron -d -q $ANACRON_ARGS"
}
@@ -1,6 +1,2 @@
sysv apparmor { sysv [S] /etc/init.d/apparmor -- AppArmor initialization
description = "AppArmor initialization"
runlevel = "S"
command = "/etc/init.d/apparmor"
}
@@ -1,6 +1 @@
service avahi-daemon { service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
description = "Avahi mDNS/DNS-SD Stack"
runlevel = "2345"
envfile = "-/etc/default/avahi-daemon"
command = "avahi-daemon -s $AVAHI_DAEMON_OPTS"
}
@@ -1,9 +1,3 @@
# To ignore errors like "org.freedesktop.hostname1", update the env file with # To ignore errors like "org.freedesktop.hostname1", update the env file with
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS # "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
service bluetoothd { service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
description = "Bluetooth service"
runlevel = "2345"
envfile = "-/etc/default/bluetooth"
log { file = "/dev/null" }
command = "/usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS"
}
@@ -1,6 +1,2 @@
# Oneshot task to run once at bootstrap # Oneshot task to run once at bootstrap
task console-setup { task [S] /lib/console-setup/console-setup.sh -- Setting up console font and keymap
description = "Setting up console font and keymap"
runlevel = "S"
command = "/lib/console-setup/console-setup.sh"
}
+1 -7
View File
@@ -1,7 +1 @@
service cron { service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
description = "Background job processing daemon"
runlevel = "2345"
conditions = { "sys/pwr/ac", "pid/syslogd" }
envfile = "-/etc/default/cron"
command = "cron -f $EXTRA_OPTS"
}
@@ -1,9 +1,4 @@
# On some systems, bootstrap can be really quick and cause elogind # On some systems, bootstrap can be really quick and cause elogind
# to not start before a user has managed to log in. To prevent # to not start before a user has managed to log in. To prevent
# this, add <pid/elogind> as condition to your TTYs in getty.conf # this, add <pid/elogind> as condition to your TTYs in getty.conf
service elogind { service [2345] <pid/syslogd> /lib/elogind/elogind -- Login manager
description = "Login manager"
runlevel = "2345"
conditions = { "pid/syslogd" }
command = "/usr/libexec/elogind"
}
+6 -37
View File
@@ -1,38 +1,7 @@
# Consoles to start getty on when system is up # Consoles to start getty on when system is up
tty tty1 { tty [12345] /dev/tty1 linux noclear nowait
runlevel = "12345" tty [2345] /dev/tty2 linux nowait
device = "/dev/tty1" tty [2345] /dev/tty3 linux nowait
term = "linux" tty [2345] /dev/tty4 linux nowait
noclear = true tty [2345] /dev/tty5 linux nowait
nowait = true tty [2345] /dev/tty6 linux nowait
}
tty tty2 {
runlevel = "2345"
device = "/dev/tty2"
term = "linux"
nowait = true
}
tty tty3 {
runlevel = "2345"
device = "/dev/tty3"
term = "linux"
nowait = true
}
tty tty4 {
runlevel = "2345"
device = "/dev/tty4"
term = "linux"
nowait = true
}
tty tty5 {
runlevel = "2345"
device = "/dev/tty5"
term = "linux"
nowait = true
}
tty tty6 {
runlevel = "2345"
device = "/dev/tty6"
term = "linux"
nowait = true
}
@@ -1,6 +1,2 @@
# Oneshot task to run once at bootstrap # Oneshot task to run once at bootstrap
task keyboard-setup { task [S] /lib/console-setup/keyboard-setup.sh -- Setting up console keyboard layout
description = "Setting up console keyboard layout"
runlevel = "S"
command = "/lib/console-setup/keyboard-setup.sh"
}
@@ -1,5 +1 @@
service lightdm { service [3] lightdm -- Light Display Manager
description = "Light Display Manager"
runlevel = "3"
command = "lightdm"
}
@@ -1,2 +1,8 @@
## Linux kernel modules to load ## Linux kernel modules to load
#modules = { "fbcon", "button", "evdev", "loop", "psmouse", "mousedev", "8139cp" } #module fbcon
#module button
#module evdev
#module loop
#module psmouse
#module mousedev
#module 8139cp
@@ -1,6 +1,2 @@
# Need to stop plymouth, or it'll block stdin on tty1 # Need to stop plymouth, or it'll block stdin on tty1
task plymouth { task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen
description = "Stopping Plymouth Boot Screen"
runlevel = "S"
command = "/usr/bin/plymouth quit"
}
@@ -1,8 +1,3 @@
# Start rsyslogd as soon as possible, should always run # Start rsyslogd as soon as possible, should always run
# Provides pid/syslogd condition # Provides pid/syslogd condition
service syslogd { service [S12345] name:syslogd env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_OPTIONS -- Reliable syslog daemon
description = "Reliable syslog daemon"
runlevel = "S12345"
envfile = "-/etc/default/rsyslog"
command = "rsyslogd -n $RSYSLOGD_OPTIONS"
}
+1 -7
View File
@@ -1,8 +1,2 @@
# Simple NTP client (SNTP) # Simple NTP client (SNTP)
service sntpd { service [2345] <net/route/default> env:-/etc/default/sntpd sntpd -n $SNTPD_OPTIONS -- SNTP client
description = "SNTP client"
runlevel = "2345"
conditions = { "net/route/default" }
envfile = "-/etc/default/sntpd"
command = "sntpd -n $SNTPD_OPTIONS"
}
+1 -7
View File
@@ -1,8 +1,2 @@
# Start SSH daemon as soon as basic networking comes up # Start SSH daemon as soon as basic networking comes up
service sshd { service [2345] <net/lo/up> env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
description = "OpenSSH daemon"
runlevel = "2345"
conditions = { "net/lo/up" }
envfile = "-/etc/default/ssh"
command = "/usr/sbin/sshd -D $SSHD_OPTS"
}
@@ -1,7 +1,2 @@
# Start sysklogd as soon as possible, should always run if enabled # Start sysklogd as soon as possible, should always run if enabled
service syslogd { service [S123456789] env:-/etc/default/syslogd syslogd -F $SYSLOGD_OPTIONS -- System logging daemon
description = "System logging daemon"
runlevel = "S123456789"
envfile = "-/etc/default/syslogd"
command = "syslogd -F $SYSLOGD_OPTIONS"
}
+1 -5
View File
@@ -1,8 +1,4 @@
# Here we use XDM since it has few dependencies (relatively speaking). # Here we use XDM since it has few dependencies (relatively speaking).
# It is also an example of a service without a -- description. # It is also an example of a service without a -- description.
# service [3] lxdm -- Lightweight Login Manager # service [3] lxdm -- Lightweight Login Manager
service xdm { service [3] xdm -nodaemon -- X Display Manager
description = "X Display Manager"
runlevel = "3"
command = "xdm -nodaemon"
}
+43 -107
View File
@@ -1,149 +1,85 @@
# Sample finit.conf to demonstrate the syntax # Sample finit.conf to demonstrate the syntax
# #
hostname = "testbed" host testbed
# Linux kernel modules to load # Linux kernel modules to load
modules = { "button", "evdev", "loop", "psmouse" } module button
module evdev
module loop
module psmouse
# Top-level cgroups and their default settings. All groups mandatory # Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup # but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are # 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the # as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names. # cgroup v2 controller default names.
cgroup init { cpu.weight = 100 } cgroup init cpu.weight:100
cgroup user { cpu.weight = 100 } cgroup user cpu.weight:100
cgroup system { cpu.weight = 9700 cpu.max = 50000 } cgroup system cpu.weight:9700 cpu.max:50000
# Example extra cgroup # Example extra cgroup
cgroup maint { cpu.weight = 100 } cgroup maint cpu.weight:100
# Runlevel to start after bootstrap, runlevel 'S' # Runlevel to start after bootstrap, runlevel 'S'
runlevel = 2 runlevel 2
# Network bringup script # Network bringup script
network = "service networking start" network service networking start
# Max file size for each log file: 100 kiB, rotate max 4 copies: # Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz # log => log.1 => log.2.gz => log.3.gz => log.4.gz
log { log size:100k count:4
size = 100k
count = 4
}
# Virtual consoles to start getty on # Virtual consoles to start getty on
tty tty1 { device = "/dev/tty1" } tty /dev/tty1
tty tty2 { device = "/dev/tty2" } tty /dev/tty2
tty tty3 { device = "/dev/tty3" } tty /dev/tty3
# Task to run if ctrl-alt-del is pressed. This condition is asserted by # Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel). # Finit upon receiving SIGINT (from the kernel).
task reboot { task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
description = "Rebooting system"
runlevel = "12345789"
conditions = { "sys/key/ctrlaltdel" }
command = "initctl reboot"
}
# Task to run if the kernel gets a power fail condition is pressed. # Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel). # Asserted by Finit upon receiving SIGPWR (from the kernel).
task poweroff { task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
description = "Shutting down system"
runlevel = "12345789"
conditions = { "sys/pwr/fail" }
command = "initctl poweroff"
}
# Alternative method instead of runparts # Alternative method instead of runparts
#task keyboard-setup { #task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
# description = "Setting up preliminary keymap" #task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
# runlevel = "S" #task [S] /etc/init.d/kbd start -- Preparing console
# command = "/etc/init.d/keyboard-setup start" #run [2] /etc/init.d/networking start -- Start networking
#}
#task acpid {
# description = "Starting ACPI Daemon"
# runlevel = "S"
# command = "/etc/init.d/acpid start"
#}
#task kbd {
# description = "Preparing console"
# runlevel = "S"
# command = "/etc/init.d/kbd start"
#}
#run networking {
# description = "Start networking"
# runlevel = "2"
# command = "/etc/init.d/networking start"
#}
# Services to be monitored and respawned as needed # Services to be monitored and respawned as needed
# klogd and syslogd are placed in the maint cgroup. There is no # klgod and syslogd are placed in the maint cgroup
# "current cgroup" to switch in this format, every block names its own. cgroup.maint
service klogd { service [2345] <pid/syslogd> klogd -n -- Kernel logging server
description = "Kernel logging server" service [2345] syslogd -n -- Syslog server
runlevel = "2345"
conditions = { "pid/syslogd" }
cgroup maint {}
command = "klogd -n"
}
service syslogd {
description = "Syslog server"
runlevel = "2345"
cgroup maint {}
command = "syslogd -n"
}
# gdm gets its own limits inside the system group, which is where # gdm in the system cgroup (default when read from it's own .conf file)
# services end up when no cgroup block is given. cgroup.system
service gdm { service [3] cgroup:cpu.weight:250,mem.max:10M gdm -- GNOME Display Manager
description = "GNOME Display Manager"
runlevel = "3"
cgroup system {
cpu.weight = 250
memory.max = 10M
}
command = "gdm"
}
# Start SSH daemon with opts from /etc/default/ssh (if available), move # Start SSH daemon with opts from /etc/default/ssh (if available), move
# to user cgroup with cpu.weight 250. Default weight in the user group # to user cgroup with cpu.weight:250. Default for services is system
# is 100 (above). # group. Default weight in user group is 100 (above). Max length of
service sshd { # cgroup argument is (currently) 63 chars.
description = "OpenSSH daemon" #
runlevel = "2345" # Notice alternative syntax where current cgroup is only set for this
envfile = "-/etc/default/ssh" # particular service.
cgroup user { service [2345] cgroup.user:cpu.weight:250,mem.max:10M env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
cpu.weight = 250
memory.max = 10M
}
command = "/usr/sbin/sshd -D $SSHD_OPTS"
}
# The BusyBox ntpd does not use syslog when running in the foreground # The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The # So we use this trick to redirect stdout/stderr to a log file. The
# log file is rotated with the above settings. The condition declares # log file is rotated with the above settings. The condition declares
# a dependency on a system default route (gateway) to be set. ntpd # a dependency on a system default route (gateway) to be set. A single
# does not respect SIGHUP, so Finit restarts it on reload instead. # <!> at the beginning means ntpd does not respect SIGHUP for restart.
service ntpd { service [2345] <!net/route/default> log:/var/log/ntpd.log ntpd -n -l -I eth0 -- NTP daemon
description = "NTP daemon"
runlevel = "2345"
conditions = { "net/route/default" }
reload-signal = "none"
log { file = "/var/log/ntpd.log" }
command = "ntpd -n -l -I eth0"
}
# Run start scripts from this directory # Run start scripts from this directory
#runparts = "/etc/start.d" # runparts /etc/start.d
# For multiple instances of the same service, add :ID to the block title. # For multiple instances of the same service, add :ID somewhere between
service httpd:80 { # the service/run/task keyword and the command.
description = "Web server" service :80 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
runlevel = "2345" service :8080 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
command = "/sbin/httpd -f -h /http -p 80"
}
service httpd:8080 {
description = "Old web server"
runlevel = "2345"
command = "/sbin/httpd -f -h /http -p 8080"
}
+6 -16
View File
@@ -3,34 +3,24 @@
## Runlevel to start after bootstrap, runlevel 'S' ## Runlevel to start after bootstrap, runlevel 'S'
# Default is 2 # Default is 2
#runlevel = 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory # Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup # but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are # 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the # as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names. # cgroup v2 controller default names.
#cgroup init { cpu.weight = 100 } #cgroup init cpu.weight:100
#cgroup user { cpu.weight = 100 } #cgroup user cpu.weight:100
#cgroup system { cpu.weight = 9700 } #cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by # Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel). # Finit upon receiving SIGINT (from the kernel).
#task reboot { #task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# description = "Rebooting system"
# runlevel = "12345789"
# conditions = { "sys/key/ctrlaltdel" }
# command = "initctl reboot"
#}
# Task to run if the kernel gets a power fail condition is pressed. # Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel). # Asserted by Finit upon receiving SIGPWR (from the kernel).
#task poweroff { #task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
# description = "Shutting down system"
# runlevel = "12345789"
# conditions = { "sys/pwr/fail" }
# command = "initctl poweroff"
#}
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+1 -5
View File
@@ -1,7 +1,3 @@
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager) # Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
# Depends on syslogd having started. Redirect any output to log. # Depends on syslogd having started. Redirect any output to log.
service acpid { service [2345] acpid -f -- ACPI daemon
description = "ACPI daemon"
runlevel = "2345"
command = "acpid -f"
}
+1 -6
View File
@@ -1,6 +1 @@
service crond { service [2345] <pid/syslogd> crond -f -S -c /etc/crontabs -- Cron daemon
description = "Cron daemon"
runlevel = "2345"
conditions = { "pid/syslogd" }
command = "crond -f -S -c /etc/crontabs"
}
+1 -5
View File
@@ -1,5 +1 @@
service dhcpcd { service [2345] dhcpcd -B -M -- DHCP client
description = "DHCP client"
runlevel = "2345"
command = "dhcpcd -B -M"
}
+1 -5
View File
@@ -1,5 +1 @@
service dmeventd { service [S12345] dmeventd -f -- Device mapper event daemon
description = "Device mapper event daemon"
runlevel = "S12345"
command = "dmeventd -f"
}
+3 -18
View File
@@ -1,19 +1,4 @@
# Consoles to start Finit built-in getty on when system is up # Consoles to start Finit built-in getty on when system is up
tty tty1 { tty [12345] /dev/tty1 noclear nowait
runlevel = "12345" tty [2345] /dev/tty2 noclear nowait
device = "/dev/tty1" tty [2345] /dev/tty3 noclear nowait
noclear = true
nowait = true
}
tty tty2 {
runlevel = "2345"
device = "/dev/tty2"
noclear = true
nowait = true
}
tty tty3 {
runlevel = "2345"
device = "/dev/tty3"
noclear = true
nowait = true
}
+1 -5
View File
@@ -1,6 +1,2 @@
# Oneshot task to run once at bootstrap # Oneshot task to run once at bootstrap
task keymap { task [S] zcat /etc/keymap/se.bmap.gz | loadkmap -- Loading keymap
description = "Loading keymap"
runlevel = "S"
command = "zcat /etc/keymap/se.bmap.gz | loadkmap"
}
+1 -5
View File
@@ -1,5 +1 @@
service lxdm { service [2345] lxdm -- Lightweight Login Manager
description = "Lightweight Login Manager"
runlevel = "2345"
command = "lxdm"
}
+6 -2
View File
@@ -1,3 +1,7 @@
## Linux kernel modules to load ## Linux kernel modules to load
# Other candidates: fbcon, button, evdev, mousedev, 8139cp #module fbcon
modules = { "softdog" } #module button
#module evdev
#module mousedev
#module 8139cp
module softdog
+1 -7
View File
@@ -1,8 +1,2 @@
# Silly Busybox ntpd has no option to use syslog when running in foreground # Silly Busybox ntpd has no option to use syslog when running in foreground
service ntpd { service [2345] <pid/syslogd> log ntpd -n -N -p pool.ntp.org -- NTP daemon
description = "NTP daemon"
runlevel = "2345"
conditions = { "pid/syslogd" }
log { }
command = "ntpd -n -N -p pool.ntp.org"
}
+1 -5
View File
@@ -1,6 +1,2 @@
# Start SSH daemon as soon as basic networking comes up # Start SSH daemon as soon as basic networking comes up
service sshd { service [2345] /usr/sbin/sshd -D -- OpenSSH daemon
description = "OpenSSH daemon"
runlevel = "2345"
command = "/usr/sbin/sshd -D"
}
+2 -11
View File
@@ -1,13 +1,4 @@
# Start syslogd as soon as possible, should always run # Start syslogd as soon as possible, should always run
# Start klogd as soon as possible, but after syslogd # Start klogd as soon as possible, but after syslogd
service syslogd { service [S12345] syslogd -n -- Syslog daemon
description = "Syslog daemon" service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
runlevel = "S12345"
command = "syslogd -n"
}
service klogd {
description = "Kernel log daemon"
runlevel = "S12345"
conditions = { "pid/syslogd" }
command = "klogd -n"
}
+1 -7
View File
@@ -1,7 +1 @@
service uuidd { service @uuidd:uuidd [2345] uuidd -F -- UUID generation daemon
description = "UUID generation daemon"
runlevel = "2345"
user = "uuidd"
group = "uuidd"
command = "uuidd -F"
}
-2
View File
@@ -1,2 +0,0 @@
/Makefile
/Makefile.in
-6
View File
@@ -1,6 +0,0 @@
EXTRA_DIST = org.finit.conf
if DBUS
dbuspolicydir = $(datadir)/dbus-1/system.d
dist_dbuspolicy_DATA = org.finit.conf
endif
-63
View File
@@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!--
System-bus policy for finit.
finit (PID 1, root) owns the `org.finit` well-known name. The
policy below lets unprivileged callers introspect, read
properties, and query service / condition state, but disallows
every state-changing method at the broker. finit *also* enforces
per-method authorisation itself (LINK_METHOD_PRIVILEGED), so
defense-in-depth: even if a permissive policy is installed by
mistake, finit rejects unprivileged state changes. It reads the
caller from SO_PEERCRED on the local bus, and on the system bus it
asks the broker who sent each privileged call, via
GetConnectionUnixUser.
-->
<busconfig>
<!-- Only finit (root) may own the org.finit bus name. -->
<policy user="root">
<allow own="org.finit"/>
<allow send_destination="org.finit"/>
<allow receive_sender="org.finit"/>
</policy>
<!-- Everyone may introspect, read properties, and call the
read-only Manager1 / Cond1 methods. Signals fan out
unconditionally. -->
<policy context="default">
<allow send_destination="org.finit"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="org.finit"
send_interface="org.freedesktop.DBus.Peer"/>
<allow send_destination="org.finit"
send_interface="org.freedesktop.DBus.Properties"
send_member="Get"/>
<allow send_destination="org.finit"
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
<allow send_destination="org.finit"
send_interface="org.finit.Manager1"
send_member="ListServices"/>
<allow send_destination="org.finit"
send_interface="org.finit.Manager1"
send_member="GetService"/>
<allow send_destination="org.finit"
send_interface="org.finit.Cond1"
send_member="Get"/>
<allow send_destination="org.finit"
send_interface="org.finit.Cond1"
send_member="List"/>
<allow send_destination="org.finit"
send_interface="org.finit.Cond1"
send_member="Dump"/>
<allow receive_sender="org.finit"/>
</policy>
</busconfig>
+1 -336
View File
@@ -3,337 +3,6 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[5.0][UNRELEASED]
--------------------
### Changes
- keventd is now a full device manager, promoted from the power supply
monitor it was in v4, and the device management defaults have swapped:
keventd is enabled by default (`--without-keventd` opts out, libblkid is
required to build it), while the hotplug plugin, which runs an external
`udevd`, `mdevd`, or `mdev`, is now opt-in. With the plugin enabled,
keventd steps back to passive mode and monitors only power supply
events, as before. As the system device manager it replaces mdev,
mdevd, and udevd, and handles:
* udev rules: an engine covering most of the udev(7) grammar --
parent-chain matching, `PROGRAM`/`RESULT`, `IMPORT`, `RUN`,
`GOTO`/`LABEL`, and the standard substitutions -- reading the usual
`udev/rules.d` directories under `/lib`, `/run`, and `/etc` with
udev's precedence and masking. A curated ruleset derived from eudev
3.2.14 is installed by default, `--without-udev-rules` skips it.
See [Device Manager](keventd.md) for the supported subset and the
few deliberate divergences
* builtins used by the ruleset: `blkid`, `hwdb`, `input_id`, `kmod`,
`net_id`, `path_id`, and `usb_id`
* device nodes with per-subsystem default permissions, persistent
symlinks (`/dev/disk/by-uuid`, `by-label`, `by-id`, `by-path`, plus
`by-id`/`by-path` for input, serial, sound, v4l, and tape), kernel
firmware load requests, and module loading from `MODALIAS`
* predictable network interface names via the eudev ruleset,
`net.ifnames=0` on the kernel command line keeps classic names.
**Note:** switching a system over to keventd can rename its NICs
* coldplug: as device manager keventd starts with `-c`, replaying add
events for hardware present at boot. The `pid/keventd` condition
is deferred until the coldplug queue has drained, so services
gating on it can assume `/dev` is populated and symlinks are live
* `keventd -S`, the `udevadm settle` equivalent, asks the running
daemon over D-Bus and falls back to polling the kernel's uevent
sequence number
* libudev compatibility: per-device properties in `/run/udev/data`
and processed uevents rebroadcast to netlink group 0x4, so
libudev-zero consumers, e.g. compositors and libinput, see hotplug
events
* new conditions for the service engine: `dev/<node>` for device
nodes, `class/<subsystem>/<name>` for sysfs class devices without a
node (LEDs, IIO, DSA ports), and `driver/<name>`, asserted while a
driver is bound to at least one device
- Generated `/run/finit/system/*.conf` files for bundled services (keventd,
watchdogd, runparts) are now read before `/lib/finit/system/*.conf` instead
of after. Built-ins can now be referenced in `if:` statements from system
.conf files and overridden by name from both `/lib/finit/system` and
`/etc/finit.d`, the latter is still always read last
- The `dbus.so` plugin, which starts an external `dbus-daemon`, is now enabled
by default. It does nothing on systems without a `dbus-daemon` installed,
and `--disable-dbus-plugin` opts out. The daemon it used to register from C
now lives in `20-dbus.conf`, and the directories it needs in
`tmpfiles.d/dbus.conf`, so both can be overridden from `/etc` like any other
system file. Those directories are no longer chowned to `messagebus`,
matching how Finit ships directories for other daemons
- The bundled `sulogin` and `watchdogd` are now built and installed by
default, like keventd and the D-Bus support. `--without-sulogin` and
`--without-watchdog` opt out. Note, Finit starts `watchdogd` by itself
when the WDT device node exists
- The `tty` block takes `passenv`, which the line-based format has had since
v4.4 (issue #286) and the block format was missing
- New `provides` setting for run/task/service/sysv blocks, naming conditions
the service asserts in addition to its own `pid/<ident>`. Lets variants of
one service, qualified with `if`, share the barrier condition downstream
services wait for, which a block title alone could not express. Any
namespace is allowed, and a second claim on the same condition is refused
with a warning naming the owner
- The `command` setting in a block takes a list of candidates for the same
service, `command = { "/lib/systemd/systemd-udevd", "-udevd" }`, and Finit
starts the first one it finds. The line-based format could only express
this by repeating the whole stanza per candidate
- New `pam = "NAME"` setting for run/task/service/sysv blocks, runs the
service inside a PAM session set up from `/etc/pam.d/NAME`, so the `session`
stack applies to the process that becomes the daemon, e.g. limits from
`pam_limits`, which override a per-service `rlimit`. Issue #420
- Finit now ships with a built-in brokerless D-Bus implementation, **libink**,
exposing the running init system as a peer on its own private bus at
`/run/finit/bus`, and -- opportunistically -- registering `org.finit` on the
standard system bus when a `dbus-daemon` is reachable. No external
`libdbus`/`sd-bus`/`GIO` dependency. The bus implements the stock
`org.freedesktop.DBus`, `Peer`, `Introspectable`, and `Properties`
interfaces. See [D-Bus Integration](dbus.md) for the full surface, build
flag, and `dbus-send`/`dbus-monitor` examples
- New `org.finit.Manager1` interface at `/org/finit/manager`: `ListServices`,
`GetService`, `Start`/`Stop`/`Restart`/`Reload`, `SetRunlevel`, `SetDebug`,
`Signal`, `Suspend`, and the `Reboot`/`Halt`/`Poweroff` triplet, which take
a timeout argument arming the emergency shutdown bypass like `initctl -t`.
Read-only properties `Runlevel`, `PrevRunlevel`, `Version`; signals
`ServiceStateChanged (sss)` and `RunlevelChanged (ss)`
- New `org.finit.Service1` interface at `/org/finit/service/<encoded>`, one
object per loaded service, with `Start`/`Stop`/`Restart`/`Reload` for
working off an object handle rather than passing the identity string around
- New `org.finit.Cond1` interface at `/org/finit/cond`: `Get`, `Set`, `Clear`,
`List`, `Dump` for [user-defined conditions](conditions.md), with a
`ConditionChanged (ss)` signal
- keventd serves `org.finit.Device1` on its own bus at `/run/keventd/bus`:
`Settle`, `Trigger`, `Info`, `RulesReload`, queue-state properties, and a
`DeviceProcessed (ss)` signal. The `udevadm` settle/trigger/info
equivalents are thereby bus methods and `keventd -S` asks the running daemon
first. Finit also gained a `Manager1.ConfigReloaded` signal, fired when
`initctl reload` completes, for external condition providers and monitoring
- Privileged D-Bus methods accept root and members of the `--with-group`
group; everyone else is refused. On the local bus the caller's uid and
groups come straight from the kernel (`SO_PEERCRED` + `SO_PEERGROUPS`), so
the check never blocks PID 1 on an NSS lookup. Over the system bus the uid
is resolved from the broker (`GetConnectionUnixUser`) and privileged methods
are root-only. Read-only methods are open
- `initctl` now transparently routes through D-Bus when the bus is reachable,
with the legacy `INIT_SOCKET` transport as a fallback: `start`, `stop`,
`restart`, `reload`, `reload <svc>`, `reboot`, `halt`, `poweroff`,
`suspend`, `debug`, `signal`, `runlevel`, and `cond {get,set,clr}` all use
the new path. Two new subcommands show up that have no legacy equivalent:
* `initctl monitor` -- streams every signal on the bus to the terminal,
one line per delivery (`HH:MM:SS iface.member(args)`), until
interrupted. Same idea as `dbus-monitor`, but scoped to Finit and with
no address plumbing required.
* Issuing `initctl cond set/clr` over D-Bus also fires the
`Cond1.ConditionChanged` signal, so observers see user-driven state
changes the same way they see service-driven ones.
- Restart log now spells out the signal name and flags core dumps,
e.g. `killed by SIGKILL` or `killed by SIGSEGV, core dumped`, in place of
the bare numeric `by signal: N`. Gives operators a much stronger breadcrumb
when a daemon dies unexpectedly
- Service `user:` and `group:` names, including supplementary groups, can
now be up to 63 characters, up from 15. Longer names were silently
truncated before the lookup, so the service never started. By Anees Rehman
- `stop:` and `reload:` scripts take a timeout of their own, `stop:5,/path`,
defaulting to the kill delay as before. Such a line used to crash Finit at
config load
- Shutdown now stops after the first successful remount of `/` read only.
The plain `mount -n -o remount,ro /`, which reads `/etc/fstab`, is only
used when both device-agnostic forms fail, the same order as Debian's
`umountroot`. By Anees Rehman
### Fixes
- Reject a .conf file that declares the same block title twice. The title is
the service identity and libConfuse merges two sections sharing one, so the
file loaded as a single service holding a mix of both declarations,
silently. This hit `system/10-hotplug.conf`, where the two `udevd` blocks
left only the second command, dropping the service on systems that only have
`systemd-udevd`. The same title in another file is still how a system .conf
is overridden
- Remove stale daemon-owned (`pid:!`) pidfiles after unclean exits. When a
daemon dies via SIGKILL/OOM/segfault, or exits early during startup, its
pidfile lingers and can prevent the next instance from starting --
`dbus-daemon` for example refuses to start when its pidfile already exists,
so Finit's restart loop gives up. Finit now drops the file when it still
names the just-reaped PID and that PID is no longer alive (the liveness
check guards against PID reuse). This is a controlled exception to the
long-standing rule that Finit does not touch service-owned pidfiles
- Fix misspelled `SIGUNKOWN` returned by `sig_name()` for unknown signal
numbers, now spelled correctly as `SIGUNKNOWN`. Surfaced by the new restart
log above
- Fix mode of existing `d`/`D` directories in tmpfiles, the chmod branch
never ran because the directory creation swallowed `EEXIST`. Mode and owner
of `d`/`D`, `e`, and `f`/`F` entries, and of the service directories
`mksubsysd()` creates, are now set through an open fd and only when they
differ, so an immutable directory like `/var/empty` no longer warns on every
boot. By Sam Brkopac
- Fix a lost reload for a service paused mid-reload by a condition in
flux. A second reload arriving meanwhile re-parsed the unchanged .conf
and cleared the pending mark, so the service resumed without ever being
reloaded. Seen on Infix, where sshd kept its old listen addresses after
a change that landed as two reloads in a row
[4.17][] - 2026-04-28
---------------------
### Changes
- Add Plymouth boot splash plugin to manage the `plymouthd` lifecycle across boot,
`switch_root`, and shutdown. Activated by the `splash` kernel command line
argument. By Aaron Andersen
- Add `tty:` option for run/task/service stanzas to acquire a controlling
terminal. The device is opened as stdin, set as the session's controlling TTY
via `TIOCSCTTY`, and `TERM` is set automatically — `linux` for virtual terminals
and `vt102` for serial lines. By Ollie Gutierrez
- Improve console output atomicity at boot and shutdown: description and status
(`[ OK ]` / `[FAIL]`) are emitted in a single write so kernel messages cannot
split them. If output has been scrolled away, the description is re-printed
before the final status line. Console buffer is drained with `tcdrain(2)` and
ANSI attributes are reset before handing control back to the kernel at
shutdown/reboot
### Fixes
- Fix reverse-dependency condition handling on reload: when a service without
SIGHUP support is reloaded, its condition is now cleared immediately so
dependents transition to `STOPPING` before `SIGTERM` is sent to the parent,
preventing them from crashing with an incremented restart counter
- Fix `/var/tmp` mode in `tmpfiles.d/var.conf` from `0777` to `1777` (sticky bit)
to match `/tmp`, by Aaron Andersen
- Fix potential memory corruption in config parser: stale pointer captures of
`PATH` and `SHELL` at startup could dangle after environment modification;
`conf_reset_env()` now reads from the live environment instead, by Vasily Zubko
- Fix plugin name registration: replace `__FILE__` macro with explicit string
literals in all plugin structs and debug messages, preventing subtle build-
system–dependent macro expansion issues, by Vasily Zubko
[4.16][] - 2026-02-27
---------------------
### Changes
- Add `initctl switch-root` command and plugin hook point. Useful for
systems requiring early boot tasks like LUKS unlock, LVM activation,
or network boot before mounting the real root, by Aaron Andersen
- Set USER and LOGNAME environment variables when dropping privileges.
Fixes issues with software like rootless Podman that determines user
identity from environment variables, by Aaron Andersen
- Add `remain:yes` option for run/task oneshot commands, similar to the
systemd `RemainAfterExit=yes`, by Aaron Andersen
- Clear service conditions on `initctl reload NAME` to ensure dependent
services are properly updated
- Run service `stop:` and `reload:` scripts as non-blocking processes,
preventing Finit from stalling on long-running helper scripts
- Guard shutdown with timer watchdog to detect and debug shutdown hangs
- Add `~` condition prefix to propagate reload from a dependency to the
dependent service. E.g., `<!~pid/netd>` means not only a regular
condition, but when `netd` reloads, restart this service too. Similar
to systemd's directive `PropagatesReloadTo=`, but declared on the
consumer side. Issue #416
- `tmpfiles`: add support for `-E` and `--exclude-prefix` flags
contributed by Aaron Andersen
### Fixes
- Fix #464: invalid user:group examples in cgroups.md
- Fix #466: elogind path for Debian-based distros, by Jackie Liu
- Fix #467: TTY services stuck in restart state after non-zero exit.
Throttling logic introduced in v4.15 had duplicate checks causing
infinite timer loop, and TTYs lacked default restart timeout
- Fix #475: clear pid condition on service collection to fix stale
deps. When a service crashes (SIGKILL), the RUNNING → HALTED path
bypasses STOPPING where `cond_clear()` is normally called, leaving
dependents stuck
- Fix #476: dependents not restarted after SIGHUP reload of service in
dependency chain. Add `service_step_all()` at end of reload cycle to
guarantee convergence after conditions are reasserted. See also the
new `~` condition prefix (above) to propagate reload to dependents
- Fix #478: fix `<dev/foo>` conditions at boot, conditions that exist
before inotify was set up. Found and fixed by Mattias Walström
- Fix reload of SIGHUP-capable services incorrectly disrupting their
dependents. E.g., `initctl reload syslogd` would stop dbus, dnsmasq,
etc. even though syslogd handles SIGHUP gracefully and its PID persists
- Only remove managed pidfiles in service cleanup. For SysV services
with `pid:!/path`, the pidfile belongs to the service itself and Finit
should not delete it
- Silence spurious cgroup warnings for short-lived processes where the
kernel reaps the child before cgroup assignment completes
- Fix handling of already-mounted cgroups in `cgroup_init()`, can occur
after switch_root or in container environments
- Improve cgroups documentation clarity, grammar, and examples
[4.15][] - 2026-01-01
---------------------
The Finit project now has a new home page: <https://finit-project.github.io>
### Changes
- Change default reboot to SOC reset from WDT reset, issue #460
- Bump required versions of libite (v2.6.2) and libuev (v2.4.1)
- Allow longer service identifiers (`:id`), up to 64 characters
- Allow skipping bootstrap wait (180 sec) with Ctrl-C
- Unlock `initctl` commands start/stop/restart/reload service during bootstrap,
shutdown, and reboot. Also, allow `initctl reload` in bootstrap. This makes
it possible to start manual:yes type services at botostrap, for example, which
has been a common feature request
- Add support for `clone3()` syscall with atomic cgroup assignment, from Linux
kernel 5.3+. Eliminates race conditions where forked processes (e.g., logger
processes) ended up in wrong cgroups. Falls back to `fork()` on older kernels
- Add support for cgroup delegation, useful for, e.g., container runtimes
creating sub-groups for the container instance's processes
- Add cgroup leaf name override: `cgroup.system,name:udevd` allows using
descriptive names like `/system/udevd/` instead of filename derived names
like `/system/10-hotplug/`. Syntax supports combining with other options:
`cgroup.system,name:foo,delegate,cpu.max:10000`
- Add support for Linux capabilities, allowing services to run with minimal
required privileges instead of running as root. Uses the modern IAB API
(Inheritable, Ambient, Bounding) from libcap. By Aaron Andersen
- Add support for supplementary groups for services. Uses the syntax
`@user:group,sup1,sup2` to explicitly specify supplementary groups, in
addition to reading group membership from `/etc/group`. By Aaron Andersen
- Add CPU throttled information to `initctl` output, showing when a service
has been throttled due to reaching its CPU quota
- Add memory field to `initctl --json` status output for cgroup-enabled
services
- Default to user and group `root` (UID 0) for services
- Always check that service user and group resolve to valid UID/GID pairs,
otherwise log error and refuse to start service
- Hide cursor at boot and shutdown for cleaner visual experience
- Rename `tty` services early from `init` -> `getty` even though no getty
process has been started yet. This to aid in boot timing detection using
tools like `bootchartd` (BusyBox version or bootchart2 project) when Finit
only shows *"Please press Enter to activate this console."*
- Services with `respawn` option (including TTYs) now delay retry on non-zero
exit codes instead of immediate respawn. Prevents busy-loop when service
fails repeatedly, which on some systems may have a crippling effect
### Fixes
- Fix #453: RTC plugin fixes by Ming Liu, Atlas Copco
- Fix #455: ANSI escape sequences logged by tools using glib logging,
caused by use of PTY instead of pipe for stdout/stderr redirection
- Fix #462: `/dev/pts` mounted with wrong mode (400 instead of 620), causing
permission issues with pseudo-terminals
- getty: fix terminal scrollback issues after login on console TTY. Mouse
scroll wheel and Shift+PgUp/PgDn sometimes would not work properly
- Fix flickering and artifacts in `initctl top`
- Fix file descriptor leak in `initctl top`
- Fix incorrect "max" limit reporting in `initctl [top|cgroup|--json]` output
for child cgroups. Resolves effective limits by walking the cgroup hierarchy
- Fix issue with running Finit under `bootchartd` (bootchart2 project), always
set `PATH` and `SHELL` envs early, before attempting to mount `/proc` et al
- Always reset ownership and permissions on TTY device nodes before launching getty
- Ensure mount/unmount skips `noauto` entries
[4.14][] - 2025-08-29 [4.14][] - 2025-08-29
@@ -2183,11 +1852,7 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.17...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.12...HEAD
[4.17]: https://github.com/finit-project/finit/compare/4.16...4.17
[4.16]: https://github.com/finit-project/finit/compare/4.15...4.16
[4.15]: https://github.com/finit-project/finit/compare/4.14...4.15
[4.14]: https://github.com/finit-project/finit/compare/4.13...4.14
[4.13]: https://github.com/troglobit/finit/compare/4.12...4.13 [4.13]: https://github.com/troglobit/finit/compare/4.12...4.13
[4.12]: https://github.com/troglobit/finit/compare/4.11...4.12 [4.12]: https://github.com/troglobit/finit/compare/4.11...4.12
[4.11]: https://github.com/troglobit/finit/compare/4.10...4.11 [4.11]: https://github.com/troglobit/finit/compare/4.10...4.11
-1
View File
@@ -3,7 +3,6 @@ EXTRA_DIST = AUTHORS ChangeLog.md
docsdir := @docdir@/doc docsdir := @docdir@/doc
dist_docs_DATA = build.md cmdline.md commands.md conditions.md \ dist_docs_DATA = build.md cmdline.md commands.md conditions.md \
dbus.md \
distro.md example.md features.md index.md initctl.md \ distro.md example.md features.md index.md initctl.md \
keventd.md plugins.md requirements.md runlevels.md \ keventd.md plugins.md requirements.md runlevels.md \
runparts.md service.md signals.md state-machine.md \ runparts.md service.md signals.md state-machine.md \
+10 -119
View File
@@ -2,23 +2,10 @@ Building Finit
============== ==============
Finit comes with a traditional configure script to control features and Finit comes with a traditional configure script to control features and
optional plugins to enable. It depends on three external libraries: optional plugins to enable. It depends on two external libraries:
- [libuEv][], the event loop - [libuEv][], the event loop
- [libite][] (-lite), much needed frog DNA - [libite][] (-lite), much needed frog DNA
- [libConfuse][], the parser behind the .conf format
The bundled device manager, keventd, needs a fourth: libblkid from
[util-linux][]. It reads the filesystem UUID and label off block
devices, which is what the curated rules name the `/dev/disk/by-uuid/`
and `/dev/disk/by-label/` symlinks after. keventd is enabled by
default, so this is a hard requirement unless you build with
`--without-keventd`.
PAM session support for services is built by default. It needs
[Linux-PAM][] (-lpam); the build falls back to no PAM support when that
library is missing. A static build, `--enable-static`, disables PAM
too, since libpam loads its modules with `dlopen()`.
> [!IMPORTANT] > [!IMPORTANT]
> Most free/open source software packages that use `configure` default > Most free/open source software packages that use `configure` default
@@ -35,8 +22,6 @@ version of the above mentioned libraries. Currently required versions:
- libite v2.2.0, or later - libite v2.2.0, or later
- libuEv v2.2.0, or later - libuEv v2.2.0, or later
- libConfuse v3.3, or later
- libblkid, any version, when keventd is enabled
Configure Configure
@@ -59,56 +44,21 @@ Below are a few of the main switches to configure:
built-ins (.o files) and all external libraries, except the C library built-ins (.o files) and all external libraries, except the C library
will be linked statically. will be linked statically.
* `--disable-pam`: Opt out of Finit's built-in PAM session support,
enabled by default, which lets a service declare `pam = "name"` and
run inside a session set up from `/etc/pam.d/name`. Needs libpam,
falls back to disabled when it is missing, and is also disabled for
`--enable-static` builds. Asking for `--enable-pam` and
`--enable-static` together is an error rather than a fallback. See
[PAM Sessions](config/pam.md)
* `--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
* `--disable-dbus`: Opt out of Finit's built-in D-Bus support, enabled by
default. See [D-Bus Integration](dbus.md) for what it provides. Not to
be confused with `--disable-dbus-plugin` below, which only governs
starting an external `dbus-daemon`
* `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin. * `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin.
* `--disable-dbus-plugin`: Drop the `dbus.so` plugin, which launches * `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
`dbus-daemon` at boot. Enabled by default; the plugin does nothing on
a system that has no `dbus-daemon` installed, so opting out is only
needed to keep init from starting a bus on a system that has one.
Unrelated to the built-in bus, see `--disable-dbus` above.
* `--enable-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin. * `--enable-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin.
* `--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.
* `--without-keventd`: Drop the bundled device manager, for systems that * `--with-sulogin`: Enable bundled `sulogin` program. Default is to use the
run mdev, mdevd, or udevd instead. Enabled by default, and the only system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
thing that pulls in libblkid. See [Bundled Device Manager](keventd.md). login if the `root` user is disabled or has no password at all.
* `--without-sulogin`: Drop the bundled `sulogin`, enabled by default,
and use the system `sulogin(8)` instead. The one shipped with Finit
*allows password-less* login if the `root` user is disabled or has no
password at all. `--with-sulogin=USER` prompts for another user's
password than `root`'s.
* `--without-watchdog`: Drop the bundled `watchdogd`, enabled by default
on `/dev/watchdog`, `--with-watchdog=DEV` selects another device.
Finit starts it when the device node exists, see [Watchdog](watchdog.md).
* `--with-libsystemd`: Build the replacement `libsystemd`, which lets
programs that use `sd_notify()` talk to Finit without systemd. Off by
default because it installs `libsystemd.so.0` in `$libdir`: on a host
that has the real one, ours shadows it in the loader cache and every
program linking it, `dbus-daemon` and `udevd` included, loses the
symbols it needs. Safe on a systemd-free system, which is the point
of it.
For more configure flags, see <kbd>./configure --help</kbd> For more configure flags, see <kbd>./configure --help</kbd>
@@ -135,7 +85,8 @@ 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
$ make $ make
. .
. .
@@ -163,60 +114,6 @@ Linux config to:
> `/etc/fstab`. > `/etc/fstab`.
Testing
-------
`make check` runs the test suite in a private namespace, so it is safe
on a running system. It needs `unshare` and, on Ubuntu, unprivileged
user namespaces enabled:
```shell
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
make check
```
The D-Bus message parser is the only place in Finit where bytes off a
socket become pointers, so it also has a fuzz target. `make check`
runs it as a fixed sweep, which takes milliseconds and needs nothing
beyond the normal build. To fuzz it properly, build it with clang and
libFuzzer:
```shell
clang -fsanitize=fuzzer,address -DLINK_FUZZ_LIBFUZZER -D_GNU_SOURCE \
-I libink -I . -o fuzz-msg-parse \
test/src/fuzz-msg-parse.c libink/*.c
mkdir -p .fuzz-corpus
./fuzz-msg-parse .fuzz-corpus -max_total_time=300
```
Give it a corpus directory as above and it saves what it learns there,
so the next run picks up where this one left off instead of starting
cold. Nothing writes to it unless you name it: without the argument
libFuzzer keeps everything in memory and the run leaves only crashes
behind. `make distclean` clears the corpus and the target.
Once a corpus has grown, most of it reaches code some earlier input
already reached. Minimise it:
```shell
./fuzz-msg-parse -merge=1 .fuzz-corpus-min .fuzz-corpus
rm -rf .fuzz-corpus && mv .fuzz-corpus-min .fuzz-corpus
```
Run `./configure` first, the target needs the generated `config.h`.
If the link fails with `cannot find -lstdc++`, install the `libstdc++`
headers matching the newest GCC on the system, not the default one:
clang picks the newest tree it finds, and that is the one that needs
them. CI runs this target on every pull request, carrying its
corpus between runs so it reaches deeper over time.
Feed a file back to the target to reproduce a find:
```shell
./fuzz-msg-parse crash-3f2a...
```
Running Running
------- -------
@@ -274,11 +171,7 @@ 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:
tty board { tty [12345789] notty noclear
runlevel = "12345789"
notty = true
noclear = true
}
The `notty` option ensures reusing the stdin/stdout set up by the The `notty` option ensures reusing the stdin/stdout set up by the
kernel. Remember, this is only for debugging and would leave your kernel. Remember, this is only for debugging and would leave your
@@ -293,9 +186,7 @@ it only for debugging start up issues when Finit crashes.
> None of these options should be enabled on production systems since > None of these options should be enabled on production systems since
> they can potentially give a user root access. > they can potentially give a user root access.
[1]: https://github.com/finit-project/finit/releases/download/4.3/finit-4.3.tar.gz
[1]: ftp://troglobit.com/finit/finit-4.3.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
[libConfuse]: https://github.com/libconfuse/libconfuse
[util-linux]: https://github.com/util-linux/util-linux
[Linux-PAM]: https://github.com/linux-pam/linux-pam
+3 -4
View File
@@ -41,7 +41,7 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
Useful when starting up in various [rescue mode][rescue], factory, or Useful when starting up in various [rescue mode][rescue], factory, or
production test setups. Use the top-level configuration file production test setups. Use the top-level configuration file
setting `rcsd = "/path/to/finit.d"` to override the default rcS.d directive `rcsd /path/to/finit.d` to override the default rcS.d
directory. directory.
* `finit.debug[=bool]`: Enable finit debug. This is operated * `finit.debug[=bool]`: Enable finit debug. This is operated
@@ -104,9 +104,8 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
* `single`, or `S`: Overrides the configured runlevel (default: 2) to go * `single`, or `S`: Overrides the configured runlevel (default: 2) to go
to after bootstrap by forcing it to runlevel 1, this is also known to after bootstrap by forcing it to runlevel 1, this is also known
as single user mode. Useful to debug startup problems. All services as single user mode. Useful to debug startup problems. All services
and TTYs in runlevel 1 will be started, so a `tty` block with and TTYs in `[1]` will be started, so a `tty [1] @console nologin`
`runlevel = "1"`, `device = "@console"`, and `nologin = true` configuration presents you with a root console without login.
presents you with a root console without login.
* `1-9`, except `6`: override the configured `runlevel`. Like the `S` * `1-9`, except `6`: override the configured `runlevel`. Like the `S`
and `rescue`, giving a single number on the kernel command line tells and `rescue`, giving a single number on the kernel command line tells
+37 -124
View File
@@ -7,26 +7,18 @@ mechanism for common synchronization problems. 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"*
A condition is named in the `conditions` list of a `service`, `task`, or Conditions are similar in syntax to declaring runlevels per service.
`run` block. The list may hold several, and they are logically AND'ed They are specified within angle brackets `<>` and can be applied to any
of the `service`, `task`, or `run` stanza. Multiple conditions may be
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. In running text, and in `initctl` output, a condition service to run.
is written inside angle brackets, `<pid/syslogd>`.
One prefix can be used on a condition: A special syntax, using a leading `!` in run/task/service conditions,
denote if a:
- `~` -- propagate reload from this dependency, see below - service does not support `SIGHUP`
- run/task should not block runlevel changes (i.e., bootstrap)
The line-based format also accepts a leading `!` on the list, which is
not a condition and not a negation. It is a flag on the block, and it
means two unrelated things depending on where it sits: a service does
not support `SIGHUP` (noreload), or a run/task should not block
runlevel changes, i.e. bootstrap. Each of those is its own key here:
| Line-based | Block format |
|---|---|
| `<!...>` on a service or sysv | `reload-signal = "none"` |
| `<!...>` on a run or task | `required = false` |
Finit guarantees by default that all run/tasks run (at least) once Finit guarantees by default that all run/tasks run (at least) once
per runlevel. For most tasks this is a good default, for example per runlevel. For most tasks this is a good default, for example
@@ -35,12 +27,7 @@ that are unlikely to happen it is not. (See example below.)
### Example ### Example
service netd { service [2345] <pid/setupd,pid/zebra> /sbin/netd -- Network monitor
description = "Network monitor"
runlevel = "2345"
conditions = { "pid/setupd", "pid/zebra" }
command = "/sbin/netd"
}
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
@@ -55,62 +42,15 @@ being created, i.e., the service's default readiness notification.
Another example is `dropbear`, it does not support `SIGHUP`, but we can Another example is `dropbear`, it does not support `SIGHUP`, but we can
also see optional sourcing of arguments from an environment file: also see optional sourcing of arguments from an environment file:
service dropbear { service [2345789] <!> env:-/etc/default/dropbear dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
description = "Dropbear SSH daemon"
runlevel = "2345789"
reload-signal = "none"
envfile = "-/etc/default/dropbear"
command = "dropbear -F -R $DROPBEAR_ARGS"
}
Finally, the weird "block runlevel changes" example. Here we see what Finally, the weird "block runlevel changes" example. Here we see what
happens when Finit receives `SIGPWR`, sent from a power daemon like happens when Finit receives `SIGPWR`, sent from a power daemon like
[powstatd(8)][]. A condition is asserted and a user can set up their [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 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 moving to the next runlevel after bootstrap, so we set `<!>`:
`required = false`:
task pwrfail { task [S0123456789] <!sys/pwr/fail> name:pwrfail initctl poweroff -- Power failure, shutting down
description = "Power failure, shutting down"
runlevel = "S0123456789"
conditions = { "sys/pwr/fail" }
required = false
command = "initctl poweroff"
}
Propagating Reload in Dependencies
-----------------------------------
By default, when a service reloads during `initctl reload`, dependent
services are paused (`SIGSTOP`) and simply resumed (`SIGCONT`) when the
condition is reasserted. This is correct for barrier-style dependencies
like `<pid/syslogd>`, where dependents just need syslogd running and do
not care if it reloads its config.
For services that need to react when their upstream reloads, the `~`
prefix propagates the reload from the dependency:
service svc_b {
description = "Needs A (barrier)"
conditions = { "pid/svc_a" }
command = "/sbin/svc_b"
}
service svc_c {
description = "Propagate reload from B"
conditions = { "~pid/svc_b" }
reload-signal = "none"
command = "/sbin/svc_c"
}
Here, `<~pid/svc_b>` means: propagate a reload of `svc_b` to `svc_c`.
When `svc_b` reloads, `svc_c` will be restarted, because of
`reload-signal = "none"`, instead of merely resumed. If `svc_c`
supported `SIGHUP`, it would be sent `SIGHUP` instead.
This is similar to systemd's `PropagatesReloadTo=` directive, but
declared on the consumer side rather than the provider side.
Triggering Triggering
@@ -139,12 +79,7 @@ services, or run/task jobs, on external site-dependent stimuli.
**Example:** **Example:**
service alarm { service [2345] <usr/foo> alarm --arg foo -- Foo alarm
description = "Foo alarm"
runlevel = "2345"
conditions = { "usr/foo" }
command = "alarm --arg foo"
}
For convenience, prefixing with `usr/` is allowed, but any other slashes For convenience, prefixing with `usr/` is allowed, but any other slashes
or period characters are disallowed. E.g., to trigger the `Foo alarm`, or period characters are disallowed. E.g., to trigger the `Foo alarm`,
@@ -167,9 +102,8 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
> "touch" or recreate, their PID file on `SIGHUP`. This can be done by > "touch" or recreate, their PID file on `SIGHUP`. This can be done by
> calling `utimensat()` on the PID file. Provided, of course, that the > calling `utimensat()` on the PID file. Provided, of course, that the
> service supports reloading on `SIGHUP`, otherwise it will be restarted > service supports reloading on `SIGHUP`, otherwise it will be restarted
> by Finit when they instead exit on the signal. For such services, > by Finit when they instead exit on the signal. For such services, use
> set `reload-signal = "none"` to tell Finit the service does not > `<!>` to tell Finit the service does not support `SIGHUP`.
> support `SIGHUP`.
Built-in Conditions Built-in Conditions
@@ -177,35 +111,25 @@ Built-in Conditions
Finit comes with a set of plugins for conditions: Finit comes with a set of plugins for conditions:
- `keventd`: provides `<dev/...>`, `<class/...>`, `<driver/...>`, and - `devmon` (built-in)
`<sys/pwr/...>` - `netlink`
- `devmon` (built-in fallback for `<dev/...>` without keventd) - `pidfile`
- `netlink`: provides `<net/...>`
- `pidfile`: provides `<pid/...>`
- `sys` - `sys`
- `usr` - `usr`
The `dev/` conditions are provided by `keventd`, the built-in device The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device
manager. When keventd creates a device node in `/dev`, it also asserts nodes being created and removed. It is active only when a run, task, or
the corresponding `dev/` condition. When a device is removed, the service has declared a `<dev/foo>` or `<dev/dir/bar>` condition.
condition is cleared. If keventd is not in use (an external device
manager like udevd is used instead), the `devmon` built-in provides the
same conditions by monitoring `/dev` and `/dev/dir` with inotify.
keventd also asserts `class/<subsystem>/<name>` for devices that have The `pidfile` plugin (recursively) watches `/run/` (recursively) for PID
no `/dev` node, e.g. LEDs and DSA switch ports, and `driver/<name>` files created by the monitored services, and sets a corresponding
while a driver is bound to at least one device. See condition in the `pid/` namespace.
[keventd](keventd.md) for details.
The `pidfile` plugin recursively watches `/run/` 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 Similarly, the `netlink` plugin provides basic conditions for when an
interface is brought up/down and when a default route (gateway) is set, interface is brought up/down and when a default route (gateway) is set,
in the `net/` namespace. in the `net/` namespace.
The `sys` and `usr` plugins are passive condition monitors where The `sys` and `usr` plugins monitor are passive condition monitors where
the action is provided by `keventd`, signal handlers, and in the case of the action is provided by `keventd`, signal handlers, and in the case of
`usr`, the end-user via the `initctl` tool. `usr`, the end-user via the `initctl` tool.
@@ -240,8 +164,6 @@ Built-in conditions:
- `usr/foo` - `usr/foo`
- `boot/arg` - `boot/arg`
- `dev/node` and `dev/dir/node` - `dev/node` and `dev/dir/node`
- `class/<subsystem>/<name>`
- `driver/<name>`
> [!NOTE] > [!NOTE]
> Here, `up` means administratively up, the interface flag `IFF_UP`. > Here, `up` means administratively up, the interface flag `IFF_UP`.
@@ -253,16 +175,16 @@ Composition
----------- -----------
The `pid/` conditions are generated by the Finit `pidfile.so` plugin and The `pid/` conditions are generated by the Finit `pidfile.so` plugin and
composed from a service's block title and its `:id`. By default the composed from a service's `name:` and `:id`. By default the basename of
basename of the daemon and the empty string. the daemon and the empty string.
| **service** | **condition** | | **service** | **condition** |
|-------------------------------------------------------------------|------------------| |----------------------------------------------------|------------------|
| `service { command = "/sbin/foo" }` | pid/foo | | /sbin/foo | pid/foo |
| `service { command = "/sbin/bar -p /run/baz.pid" }` | pid/bar | | /sbin/bar -p /run/baz.pid | pid/bar |
| `service lxc:foo { command = "lxc-start -n foo -p /run/lxc/foo.pid" }` | pid/lxc:foo | | name:lxc :foo lxc-start -n foo -p /run/lxc/foo.pid | pid/lxc:foo |
| `service { command = "/usr/bin/dbus-daemon" }` | pid/dbus-daemon | | /usr/bin/dbus-daemon | pid/dbus-daemon |
| `service dropbear:222 { command = "dropbear -p 222" }` | pid/dropbear:222 | | :222 dropbear -p 222 | pid/dropbear:222 |
The condition is asserted when `pidfile.so` receives an inotify event The condition is asserted when `pidfile.so` receives an inotify event
for a file matching `/run/*.pid`, `/run/**/*.pid`, or `/run/**/pid`, for a file matching `/run/*.pid`, `/run/**/*.pid`, or `/run/**/pid`,
@@ -272,13 +194,9 @@ When Finit configuration files are changed and the `initctl reload`
command is called, it is expected of services to touch their PID files command is called, it is expected of services to touch their PID files
for Finit to reassert their conditions. for Finit to reassert their conditions.
Similarly, when a single service is reloaded with `initctl reload NAME`,
its conditions are cleared and reasserted, ensuring dependent services
are properly updated.
Daemons that don't create PID files, or fail to touch them on reload, Daemons that don't create PID files, or fail to touch them on reload,
can be worked around by setting `pidfile` and `pidfile-create` in the can be worked around by using the `pid:/path/to/file.pid` syntax in
service block for the daemon. It is far from optimal since any the service stanza for the daemon. It is far from optimal since any
synchronization of depending services may fail due to the daemon not synchronization of depending services may fail due to the daemon not
having reinitialized/created their IPC sockets, or similar. having reinitialized/created their IPC sockets, or similar.
@@ -359,11 +277,6 @@ This STOP/CONT handling minimizes the number of unnecessary service
restarts that would otherwise occur because a depending service was sent restarts that would otherwise occur because a depending service was sent
`SIGHUP` for example. `SIGHUP` for example.
Services with the `~` prefix are an exception to this rule: when their
conditions return to `on` after being in `flux`, the reload is propagated
-- the service is reloaded (SIGHUP), or restarted if it has
`reload-signal = "none"`, instead of simply being resumed.
Therefore, any plugin that supplies Finit with conditions must ensure 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
-204
View File
@@ -1,204 +0,0 @@
Linux Capabilities
==================
Finit supports Linux capabilities, allowing services to run with minimal
required privileges instead of running as root. This significantly improves
system security by following the principle of least privilege.
## Overview
Linux capabilities divide the traditional root privileges into distinct units
that can be independently granted to processes. For example, a web server only
needs the capability to bind to privileged ports (< 1024), not full root access.
Finit uses the modern IAB (Inheritable, Ambient, Bounding) API from libcap,
which is the same approach used by other modern service managers like dinit.
## Basic Usage
Capabilities are specified with the `capabilities` key, alias `caps`:
```conf
service nginx {
description = "Web server"
runlevel = "2345"
user = "www-data"
group = "www-data"
capabilities = { "^cap_net_bind_service" }
command = "/usr/sbin/nginx -g 'daemon off;'"
}
```
This example allows nginx to bind to privileged ports (like 80 and 443) while
running as the unprivileged `www-data` user.
## IAB Format
The capability string uses the IAB (Inheritable, Ambient, Bounding) format
with the following prefixes:
- `^` **Ambient** (and Inheritable) - **Recommended for most use cases**
- Capabilities survive across `exec()` calls
- Automatically raised to effective after exec
- Example: `^cap_net_bind_service`
- `%` **Inheritable** only
- Requires the executed binary to have matching file capabilities
- Less common, more complex setup
- Example: `%cap_net_admin`
- `!` **Bounding** - Block capability from bounding set
- Prevents the service from ever acquiring this capability
- Useful for security hardening
- Example: `!cap_sys_admin`
Multiple capabilities can be specified as a comma-separated list:
```conf
capabilities = { "^cap_net_raw", "^cap_net_admin", "^cap_net_bind_service" }
```
## Common Use Cases
### Web Server (Privileged Ports)
Allow a web server to bind to ports 80 and 443 without running as root:
```conf
service webserver {
runlevel = "2345"
user = "www-data"
group = "www-data"
capabilities = { "^cap_net_bind_service" }
command = "/usr/sbin/nginx -g 'daemon off;'"
}
```
### Network Monitoring (Raw Sockets)
Allow packet capture without root privileges:
```conf
service tcpdump {
runlevel = "2345"
user = "tcpdump"
capabilities = { "^cap_net_raw", "^cap_net_admin" }
command = "/usr/sbin/tcpdump -i eth0 -w /var/log/capture.pcap"
}
```
### NTP Daemon (System Time)
Allow time synchronization without full root:
```conf
service ntpd {
runlevel = "2345"
user = "ntp"
capabilities = { "^cap_sys_time", "^cap_sys_nice" }
command = "/usr/sbin/ntpd -n"
}
```
## Available Capabilities
Common capabilities include (see `man 7 capabilities` for the complete list):
- `cap_chown` - Make arbitrary changes to file UIDs and GIDs
- `cap_dac_override` - Bypass file read, write, and execute permission checks
- `cap_dac_read_search` - Bypass file read permission checks
- `cap_fowner` - Bypass permission checks on operations that normally require filesystem UID
- `cap_kill` - Bypass permission checks for sending signals
- `cap_net_admin` - Perform various network-related operations
- `cap_net_bind_service` - Bind to privileged ports (< 1024)
- `cap_net_raw` - Use RAW and PACKET sockets
- `cap_setgid` - Make arbitrary manipulations of process GIDs
- `cap_setuid` - Make arbitrary manipulations of process UIDs
- `cap_sys_admin` - Perform system administration operations (very powerful!)
- `cap_sys_module` - Load and unload kernel modules
- `cap_sys_nice` - Raise process nice value and change scheduling
- `cap_sys_time` - Set system clock
## Security Best Practices
1. **Use the minimum required capabilities**
- Only grant what the service actually needs
- Don't grant `cap_sys_admin` unless absolutely necessary
2. **Specify a user (preferably non-root)**
- The `user` setting is **required** for `capabilities` to take effect
- For ambient capabilities (`^`), use a non-root user (not `"root"`)
- Example: `user = "www-data"`, `user = "nginx"`, `user = "tcpdump"`
3. **Use ambient capabilities (`^`)**
- The `^` prefix ensures capabilities survive exec()
- Simpler than setting file capabilities on binaries
4. **Block dangerous capabilities**
- Use `!` to explicitly block capabilities you don't want
- Example: `!cap_sys_admin,!cap_sys_module`
5. **Test with `getpcaps`**
- After starting a service, verify its capabilities:
```bash
getpcaps $(pidof nginx)
```
- Should show only the capabilities you granted
## Verification
After configuring a service with capabilities, verify it works correctly:
```bash
# Start the service
initctl start webserver
# Check the process capabilities
getpcaps $(pidof nginx)
# Should show something like:
# 12345: cap_net_bind_service=eip
# Verify the user
ps -o user,pid,cmd -p $(pidof nginx)
# Should show the service running as the specified user
```
## Requirements
- Linux kernel 4.3+ (for ambient capabilities support)
- libcap library installed
- Finit built with `--enable-libcap`, otherwise a `capabilities` list is
ignored, with a warning
## Limitations
- `capabilities` requires `user` to be set for it to take effect
- Without `user`, the service runs as root with full capabilities and
the `capabilities` list is silently ignored
- You can use `user = "root"`, but see below about ambient capabilities
- For ambient capabilities (`^`, recommended), the user **must be non-root**
- Using `user = "root"` with `^` capabilities will not work effectively, as ambient
capabilities are only added to the effective set when euid ≠ 0
- Use inheritable (`%`) or bounding (`!`) capabilities with `user = "root"` if needed
- Finit warns about this when reading the .conf file:
nginx: ambient capabilities ('^') have no effect as root, use a
non-root user, or '%' and '!' entries
- Services without `capabilities` use standard privilege dropping:
- Services with a non-root `user` have no special capabilities
- Services without `user` run as root with full capabilities
- A capability granted by `pam_cap.so` in a [PAM session](pam.md) is
merged into this set, but only when `capabilities` is also set.
Without it the grant is lost when privileges drop
- Some very old binaries may not work correctly with ambient capabilities
- File system capabilities are not managed by Finit (use `setcap` for that)
## See Also
- [capabilities(7)](https://man7.org/linux/man-pages/man7/capabilities.7.html) - Linux capabilities overview
- [cap_iab(3)](https://man7.org/linux/man-pages/man3/cap_iab.3.html) - IAB capability API documentation
- [setcap(8)](https://man7.org/linux/man-pages/man8/setcap.8.html) - Set file capabilities
- [getcap(8)](https://man7.org/linux/man-pages/man8/getcap.8.html) - Query file capabilities
- [capsh(1)](https://man7.org/linux/man-pages/man1/capsh.1.html) - Capability shell wrapper
+33 -212
View File
@@ -1,244 +1,65 @@
Finit has two `cgroup` blocks for controlling resource allocation: There are three major cgroup configuration directives:
1. **Top-level definition**, at file scope: declares a group such as 1. Global top-level group: `init`, `system`, `user`, or a custom group
`init`, `system`, or `user`, and its default settings. 2. Selecting a top-level group for a set of run/task/services
3. Per run/task/service limits
cgroup system { cpu.weight = 9700 }
2. **Joining a group**, inside a service block: names the group this
service runs in, and may override settings for itself alone.
service foo {
cgroup maint { memory.max = 1G }
command = "/path/to/cmd"
}
> [!NOTE] > [!NOTE]
> Linux cgroups and details surrounding values are not explained in the > Linux cgroups and details surrounding values are not explained in the
> Finit documentation. The Linux admin-guide covers this well: > Finit documentation. The Linux admin-guide cover this well:
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html> > <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
Top-level Cgroup Definition Top-level Group Configuration
---------------------------- -----------------------------
**Syntax:** `cgroup NAME { settings }`
# Top-level cgroups and their default settings. All groups mandatory # Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup # but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are # 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the # as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names. # cgroup v2 controller default names.
cgroup init { cpu.weight = 100 } cgroup init cpu.weight:100
cgroup user { cpu.weight = 100 } cgroup user cpu.weight:100
cgroup system { cpu.weight = 9800 } cgroup system cpu.weight:9800
Adding an extra cgroup `maint/` will require you to adjust the weight of Adding an extra cgroup `maint/` will require you to adjust the weight of
the above three. We leave `init/` and `user/` as-is reducing weight of the above three. We leave `init/` and `user/` as-is reducing weight of
`system/` to 9700. `system/` to 9700.
cgroup system { cpu.weight = 9700 } cgroup system cpu.weight:9700
# Example extra cgroup 'maint' # Example extra cgroup 'maint'
cgroup maint { cpu.weight = 100 } cgroup maint cpu.weight:100
By default, the `system/` cgroup is selected for almost everything. The By default, the `system/` cgroup is selected for almost everything. The
`init/` cgroup is reserved for the early boot helpers Finit drives `init/` cgroup is reserved for PID 1 itself and its closest relatives.
itself, like udev and mdev. PID 1 runs in the root cgroup, since a
cgroup holding processes cannot delegate controllers to its children.
The `user/` cgroup is for local TTY logins spawned by getty. The `user/` cgroup is for local TTY logins spawned by getty.
Joining a Cgroup Selecting Cgroups
---------------- ------------------
**Syntax:** `cgroup NAME { settings }` (inside a service block) To select a different top-level cgroup, e.g. `maint/`, one can either
define it for a group of run/task/service directives in a `.conf` or per
each stanza:
Every block says which group it joins, by name. An empty block joins cgroup.maint
without changing anything: service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
service foo { or
cgroup maint {}
command = "/path/to/foo args"
}
service bar { service [...] <...> cgroup.maint /path/to/foo args -- description
cgroup maint {}
command = "/path/to/bar args"
}
Both services run in the `maint/` cgroup. Settings inside the block The latter form also allows per-stanza limits on the form:
apply to that service alone:
service foo { service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
cgroup maint {
cpu.max = 10000
memory.max = 655360
}
command = "/path/to/foo args"
}
> [!NOTE] Notice the comma separation and the `mem.` exception to the rule: every
> The legacy format has a standalone `cgroup.NAME` line that selects a cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
> group for every stanza following it in the file, and a `cgroup:options` to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no filtering,
> form that applies settings to whichever group is current without except for expanding the shorthand `mem.` to `memory.`, if the file is
> naming it. Neither has an equivalent here, by design: a block that not available, either the cgroup controller is not available in your
> joins a group says so itself, so the group cannot depend on what came Linux kernel, or the name is misspelled.
> earlier in the file.
Every cgroup setting maps directly to cgroup v2 syntax, so `cpu.max` A daemon using `SCHED_RR` currently need to run outside the default cgroups.
maps to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no
filtering, the one exception being the shorthand `mem.`, which expands
to `memory.`. If the file is not available, either the controller is
missing from your Linux kernel, or the name is misspelled.
### Overriding Cgroup Leaf Names service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process
By default, the cgroup leaf directory name is derived from the service
configuration filename (without the `.conf` extension). For example, a
service defined in `system/10-hotplug.conf` would create a cgroup at
`/sys/fs/cgroup/system/10-hotplug/` by default.
To use a more descriptive name (recommended for clarity), set `name`
inside the cgroup block:
service udevd {
description = "Device event daemon"
cgroup system { name = "udevd" }
command = "/lib/systemd/systemd-udevd"
}
This creates the cgroup at `/sys/fs/cgroup/system/udevd/` instead.
`name` combines with any other setting:
service udevd {
description = "Device event daemon"
cgroup system {
name = "udevd"
cpu.max = 10000
}
command = "/lib/systemd/systemd-udevd"
}
Or with delegation:
service podman {
description = "Podman API"
runlevel = "2345"
user = "podman"
group = "podman"
cgroup containers {
name = "podman"
delegate = true
memory.max = 4G
}
command = "/usr/bin/podman system service"
}
A daemon using `SCHED_RR` currently needs to run outside the default
cgroups.
service rt {
description = "Real-Time process"
cgroup root {}
command = "/path/to/daemon arg"
}
Cgroup Delegation
-----------------
For services that need to create their own child cgroups (container runtimes
like Docker, Podman, systemd-nspawn, LXC), set `delegate`:
service dockerd {
description = "Docker daemon"
runlevel = "2345"
user = "dockerd"
group = "dockerd"
cgroup system { delegate = true }
command = "/usr/bin/dockerd"
}
This allows the container runtime to:
- Create child cgroups for containers
- Manage controller settings for containers
- Move processes between cgroups
When delegation is enabled, Finit:
1. Creates the service cgroup as a **domain group** (not a leaf)
2. Enables all available controllers in `cgroup.subtree_control`
3. Changes ownership of delegation files to the service user
4. Moves the service process to the cgroup root
5. Lets the container runtime manage its own subdirectories
**Requirements:**
- The service should specify `user` and `group` for proper ownership
- Controllers are delegated from the parent cgroup
**Example with additional config:**
service podman {
description = "Podman API"
runlevel = "2345"
user = "podman"
group = "podman"
cgroup containers {
delegate = true
memory.max = 4G
}
command = "/usr/bin/podman system service"
}
This delegates the cgroup while also setting a 4GB memory limit.
**Container template example:**
Here's a real-world example from [Infix OS](https://github.com/kernelkit/infix)
for running rootful podman container instances using delegation:
sysv container:%i {
description = "container %i"
runlevel = "2345"
reload-signal = "none"
pidfile = "/run/container:%i.pid"
stop-timeout = 30
log { priority = "local1" identity = "%i" }
exec-start-pre = "/usr/sbin/container"
exec-start-pre-timeout = 0
exec-cleanup = "/usr/sbin/container"
exec-cleanup-timeout = 0
cgroup system { delegate = true }
command = "container -n %i"
}
This template uses `sysv` type with delegation, demonstrating that cgroup
delegation works with different service types, not just `service`.
**Cgroup structure with delegation:**
Initially, the service process runs directly in the cgroup root:
/sys/fs/cgroup/system/container@web/
├── cgroup.procs (service PID - owned by service user)
├── cgroup.subtree_control (+cpu +memory +io - owned by service user)
└── (container children will be created here)
Once the container runtime creates child cgroups (e.g., `libpod-*/`), cgroups v2
enforces the "no internal processes" rule. When Finit detects this (`EBUSY` error),
it automatically creates a `supervisor/` subdirectory and moves service-related
processes there:
/sys/fs/cgroup/system/container@web/
├── cgroup.procs (empty)
├── cgroup.subtree_control (+cpu +memory +io)
├── supervisor/ (service processes)
│ └── cgroup.procs (conmon PIDs, etc.)
└── libpod-$HASH/ (container processes)
└── cgroup.procs (container PIDs)
This happens automatically - no configuration needed. Without delegation, the
cgroup would be a leaf and the container runtime could not create child cgroups.
+6 -7
View File
@@ -6,16 +6,15 @@ and any `*.conf`, was added. It is worth noting that these are global
and *shared with all* services -- the only way to have a service-local and *shared with all* services -- the only way to have a service-local
environment is detailed in [Services Environment](service-env.md). environment is detailed in [Services Environment](service-env.md).
Global environment variables go in an `environment` block, `env` for The syntax for global environment variables is straight forward. In
short: Finit v4.4 the `set` keyword was added for completeness, but the old
syntax (without the `set ` prefix) is still honored:
environment { set foo=bar
foo = "bar" set baz="qux"
baz = "qux"
}
On reload of .conf files, all tracked environment variables are cleared On reload of .conf files, all tracked environment variables are cleared
so if `foo` is removed from `finit.conf`, or any `finit.d/*.conf` so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf`
file, it will no longer be used by Finit or any new (!) started file, it will no longer be used by Finit or any new (!) started
run/tasks or services. The environment of already started processes can run/tasks or services. The environment of already started processes can
not be changed. not be changed.
+23 -27
View File
@@ -24,32 +24,29 @@ the following layout is recommended:
| `- ... | `- ...
`- run/ `- run/
`- finit/ `- finit/
|- bus # Finit's own D-Bus socket, see doc/dbus.md
`- system/ `- system/
|- dbus.conf
|- keventd.conf |- keventd.conf
|- runparts.conf |- runparts.conf
|- watchdogd.conf |- watchdogd.conf
`- ... `- ...
Configuration files in `/etc` are provided by the user, or projects like Configuration files in `/etc` are provided by the user, or projects like
[finit-skel](https://github.com/finit-project/finit-skel) and extended by [finit-skel](https://github.com/troglobit/finit-skel) and extended by
the user. the user.
The files in `/run/finit/system/*.conf` are generated by Finit for its
built-in services: runparts, [watchdog](../watchdog.md), and
[`keventd`](../keventd.md), if they are enabled. They are read before
the other .conf directories, so any later file can reference them in
`if:` statements and override them by name.
The files in `/lib/finit/system/*.conf` are system critical services and The files in `/lib/finit/system/*.conf` are system critical services and
setup provided by Finit, e.g. udev/mdev that must run very early at system setup provided by Finit, e.g. udev/mdev that must run very early at system
bootstrap. This system directory was introduced in Finit v4.4 to replace bootstrap. This system directory was introduced in Finit v4.4 to replace
the hard-coded services provided by plugins before. the hard-coded services provided by plugins before. All .conf files in this
directory be either replaced by a system administrator or overridden by a
file with the same name in `/etc/finit.d/`.
All .conf files in both directories can be either replaced by a system The files in `/run/finit/system/*.conf` are created by plugins and Finit
administrator or overridden by a file with the same name in bundled services like runparts, [watchdog](../watchdog.md), and
`/etc/finit.d/`, which is always read last -- copy a .conf there and [`keventd`](../keventd.md) if they are enabled. Like
modify it, or symlink it to `/dev/null` to disable it outright. `/lib/finit/system/*.conf`, these files can be overridden by file with
the same name in `/etc/finit.d/`.
Services in the `available/` and `enabled/` sub-directories are called Services in the `available/` and `enabled/` sub-directories are called
dynamic services, in contrast to static services -- the only difference dynamic services, in contrast to static services -- the only difference
@@ -58,28 +55,27 @@ with the `enable` and `disable` commands. An administrator can always
create files and symlinks manually. create files and symlinks manually.
At bootstrap, and `initctl reload`, all .conf files are read, starting At bootstrap, and `initctl reload`, all .conf files are read, starting
with `finit.conf`, then `/run/finit/system/*.conf`, with `finit.conf`, then `/lib/finit/system/*.conf`, `finit.d/*.conf`,
`/lib/finit/system/*.conf`, `finit.d/*.conf`, and finally all and finally all `finit.d/enabled/*.conf` files. Each directory is a
`finit.d/enabled/*.conf` files. Each directory is a unique group, unique group, where files within each group are sorted alphabetically.
where files within each group are sorted alphabetically.
**Example:** **Example:**
/run/finit/system/keventd.conf
/run/finit/system/runparts.conf
/lib/finit/system/10-hotplug.conf /lib/finit/system/10-hotplug.conf
/lib/finit/system/90-testserv.conf /lib/finit/system/90-testserv.conf
/run/finit/system/dbus.conf
/run/finit/system/runparts.conf
/etc/finit.d/10-abc.conf /etc/finit.d/10-abc.conf
/etc/finit.d/20-abc.conf /etc/finit.d/20-abc.conf
/etc/finit.d/enabled/1-aaa.conf /etc/finit.d/enabled/1-aaa.conf
/etc/finit.d/enabled/1-abc.conf /etc/finit.d/enabled/1-abc.conf
The resulting combined configuration is read in order, each `run`, The resulting combined configuration is read line by line, each `run`,
`task`, and `service` added to an ordered list that ensures they are `task`, and `service` added to an ordered list that ensures they are
started in the same order. This is important because of the blocking started in the same order. This is important because of the blocking
properties of `run`. For an example on the relation of `service` and properties of the `run` statement. For an example on the relation of
`run`, and dependency handling between them, see `service` and `run` statements, and dependency handling between them,
[Conditional Loading](services.md#conditional-loading), below. see [Conditional Loading](services.md#conditional-loading), below.
> [!NOTE] > [!NOTE]
> The names `finit.conf` and `finit.d/` are only defaults. They can be > The names `finit.conf` and `finit.d/` are only defaults. They can be
@@ -88,7 +84,7 @@ properties of `run`. For an example on the relation of `service` and
> >
> They can also be overridden from the [kernel command line](../cmdline.md) > They can also be overridden from the [kernel command line](../cmdline.md)
> using: `-- finit.config=/etc/bar.conf` and in that file use the > using: `-- finit.config=/etc/bar.conf` and in that file use the
> top-level setting `rcsd = "/path/to/finit.d"`. > top-level configuration directive `rcsd /path/to/finit.d`.
Filesystem Layout Filesystem Layout
----------------- -----------------
@@ -184,7 +180,7 @@ document [Finit Services](../service.md).
Alternate finit.d/ Alternate finit.d/
------------------ ------------------
**Syntax:** `rcsd = "/path/to/finit.d"` **Syntax:** `rcsd /path/to/finit.d`
The Finit rcS.d directory is set at compile time with: The Finit rcS.d directory is set at compile time with:
@@ -198,7 +194,7 @@ configurations, starting with the kernel command line option:
This file in turn can use the `rcsd` directive to tell Finit to use This file in turn can use the `rcsd` directive to tell Finit to use
another set of .conf files, e.g.: another set of .conf files, e.g.:
rcsd = "/etc/factory.d" rcsd /etc/factory.d
> [!NOTE] > [!NOTE]
> This directive is only available from the top-level bootstrap .conf > This directive is only available from the top-level bootstrap .conf
@@ -207,6 +203,6 @@ another set of .conf files, e.g.:
Including Finit Configs Including Finit Configs
------------------------ ------------------------
**Syntax:** `include("CONF")` **Syntax:** `include <CONF>`
Include another configuration file. Absolute path required. Include another configuration file. Absolute path required.
+13 -99
View File
@@ -6,110 +6,24 @@ the navigation menu.
Configuration File Syntax Configuration File Syntax
-------------------------- --------------------------
A `.conf` file is a series of blocks. Every setting is a key inside The file format is line based, empty lines and comments, lines starting
one, so nothing has to be remembered by position: with `#`, are ignored. A configuration directive starts with a keyword
followed by a space and the rest of the line is treated as the value.
As of Finit v4.4, configuration directives can be broken up in multiple
lines using the continuation character `\`, and trailing comments are
also allowed. Example:
```aconf ```aconf
service sysklogd { # Escape \# chars if you want them literal in, e.g., descriptions
description = "System log daemon" service name:sysklogd [S123456789] \
runlevel = "S123456789" env:-/etc/default/sysklogd \
envfile = "-/etc/default/sysklogd" syslogd -F $SYSLOGD_ARGS \
command = "syslogd -F $SYSLOGD_ARGS" -- System log daemon \# 1 # Comments allowed
}
```
Values are quoted strings, bare words, or integers. Lists use braces,
and a block may carry a title, which becomes the service identity:
```aconf
# shell comment
// C++ comment
/* C comment */
key = "value" # string
number = 20 # integer
flag = true # boolean
list = { "one", "two" } # string list
block title { key = "value" } # titled section
path = "${HOME}/thing" # environment expansion
include("/etc/finit.d/extra.conf") # include another file
```
Bare words work wherever a string is expected, so `memory.max = 65M`
and `restart = always` need no quotes.
### Two conventions
Keys are kebab-case, never `snake_case` or `CamelCase`: `restart-sec`,
`stop-timeout`, `reboot-watchdog`.
Keys that take a list are plural: `conditions`, `conflicts`,
`capabilities`, `modules`, `extra-groups`. Two imperatives keep their
singular form because they are verbs rather than nouns: `mknod` and
`include`.
### Short forms
Nine keys have an alias. An alias may abbreviate the canonical name,
or preserve the spelling the line-based format used; it never renames.
| Canonical | Alias | Canonical | Alias |
|---|---|---|---|
| `description` | `desc` | `manual-start` | `manual` |
| `conditions` | `cond` | `remain-after-exit` | `remain` |
| `capabilities` | `caps` | `stop-signal` | `halt` |
| `modules` | `mod` | `stop-timeout` | `kill` |
| `envfile` | `env` | | |
### Optional paths
A leading `-` on a path means carry on if it is not there:
```aconf
service foo {
envfile = "-/etc/default/foo" # skip the file if missing
command = "-/usr/sbin/foo" # skip the whole block if missing
}
```
That is the only mark meaning "optional". Two others mean something
else: `!` on `runlevel` inverts the set, e.g. `runlevel = "!12345"`,
and `~` on a condition propagates a reload from the service it names.
Both are covered where they apply.
Both Formats Are Read
---------------------
Finit also reads the line-based format it has always used, where a
stanza is a keyword followed by values whose meaning comes from their
position:
```aconf
service [S123456789] env:-/etc/default/sysklogd syslogd -F $SYSLOGD_ARGS -- System log daemon
```
That format still works and is not going away. It is frozen at the
Finit 4.x feature set, so new settings appear only in the block format,
and the documentation is written in blocks throughout. To convert a
file, see the [Syntax Migration Guide](migration.md).
There is no new file extension and no marker line. Every file is still
`*.conf`, and Finit decides which format a file is in by reading it: if
it parses as blocks it is a block file, otherwise it goes to the
line-based parser. A file is one format or the other, never a mix.
Because the two are told apart before either parser commits, a typo
reports its own file and line instead of being mistaken for the other
format:
```
parse error: /etc/finit.d/foo.conf:3: no such option 'commnad'
``` ```
The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support many The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support many
settings. Some are restricted, e.g., only available at bootstrap, directives. Some are restricted, e.g., only available at bootstrap,
runlevel `S`. Read on in [Files & Layout](files.md) for more on how runlevel `S`. Read on in [Files & Layout](files.md) for more on how
to structure your .conf files. to structure your .conf files.
+17 -36
View File
@@ -1,13 +1,13 @@
General Logging General Logging
=============== ===============
**Syntax:** `log { size = 200k count = 5 }` **Syntax:** `log size:200k count:5`
Log rotation for run/task/services that redirect output to a log file Log rotation for run/task/services using the `log` sub-option with
with their own `log` block. Global setting, applies to all services. redirection to a log file. Global setting, applies to all services.
The size can be given as bytes, without a specifier, or in `k`, `M`, The size can be given as bytes, without a specifier, or in `k`, `M`,
or `G`, e.g. `size = 10M`, or `size = 3G`. A value of `size = 0` disables or `G`, e.g. `size:10M`, or `size:3G`. A value of `size:0` disables
log rotation. The default is `200k`. log rotation. The default is `200k`.
The count value is recommended to be between 1-5, with a default 5. The count value is recommended to be between 1-5, with a default 5.
@@ -17,48 +17,33 @@ size limit is reached.
Redirecting Output Redirecting Output
------------------ ------------------
The `run`, `task`, and `service` blocks take a `log` block of their own, The `run`, `task`, and `service` stanzas also allow the keyword `log` to
redirecting `stderr` and `stdout` of the application to a file or syslog redirect `stderr` and `stdout` of the application to a file or syslog
using the native `logit` tool. This is useful for programs that do not using the native `logit` tool. This is useful for programs that do not
support syslog on their own, which is sometimes the case when running support syslog on their own, which is sometimes the case when running
in the foreground. in the foreground.
An empty block means syslog with the defaults, and three keys adjust it: The full syntax is:
| Setting | Description | log:/path/to/file
|---|---| log:prio:facility.level,tag:ident
| `file` | Write to this path instead of syslog | log:console
| `priority` | Syslog `facility.level`, default `daemon.info` | log:null
| `identity` | Syslog tag, default the basename of the command | log
`/dev/console` and `/dev/null` are spelled as the paths they are: Default `prio` is `daemon.info` and default `tag` is the basename of the
service or run/task command.
service foo { log { } command = "foo" } # syslog
service foo { log { file = "/var/log/foo" } command = "foo" } # a file
service foo { log { file = "/dev/console" } command = "foo" } # console
service foo { log { file = "/dev/null" } command = "foo" } # discard
> [!NOTE]
> A `log` block at file scope is a different setting -- that one is the
> global rotation above, and it takes only `size` and `count`.
Log rotation is controlled using the global `log` setting. Log rotation is controlled using the global `log` setting.
**Example:** **Example:**
service ntpd { service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
description = "NTP daemon"
log {
priority = "user.warn"
identity = "ntpd"
}
command = "/sbin/ntpd pool.ntp.org"
}
Output Buffering Output Buffering
---------------- ----------------
When using the `log` block, Finit redirects the service's stdout and When using the `log` directive, Finit redirects the service's stdout and
stderr to a pipe connected to a logger process. Programs detect this as stderr to a pipe connected to a logger process. Programs detect this as
non-interactive output (i.e., `isatty()` returns false) and typically non-interactive output (i.e., `isatty()` returns false) and typically
switch from line-buffered to fully-buffered mode. switch from line-buffered to fully-buffered mode.
@@ -68,11 +53,7 @@ directly, so this is rarely an issue. However, if a service's log
messages appear delayed or batched, you can force line-buffered output messages appear delayed or batched, you can force line-buffered output
by wrapping the command with `stdbuf`: by wrapping the command with `stdbuf`:
service myservice { service log /usr/bin/stdbuf -oL /path/to/command -- My service
description = "My service"
log { }
command = "/usr/bin/stdbuf -oL /path/to/command"
}
The `-oL` option forces line-buffered output, and `-o0` forces unbuffered The `-oL` option forces line-buffered output, and `-o0` forces unbuffered
output. See `stdbuf(1)` for details. output. See `stdbuf(1)` for details.
-267
View File
@@ -1,267 +0,0 @@
Syntax Migration Guide
======================
Finit reads both configuration formats. Every file is still `*.conf`,
and the format is detected per file, so a system can be migrated one
file at a time -- a file is one format or the other, never a mix.
This guide maps each part of a line-based stanza to its block key.
The shape of the change:
service [S12345] <pid/syslogd> env:-/etc/default/klogd name:klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
becomes
service klogd {
description = "Kernel log daemon"
runlevel = "S12345"
conditions = { "pid/syslogd" }
envfile = "-/etc/default/klogd"
command = "klogd -n $KLOGD_OPTS"
}
The block title is the service identity, shown by `initctl`. Multiple
instances spell the ID in the title: `service sshd:1 { ... }`. The
legacy bare-ID form, `service :80 ...`, has no equivalent -- the title
carries both name and ID.
Positional parts
----------------
| Line-based | Block key |
|----------------------------------|-------------------------------------------------------|
| `service`, `task`, `run`, `sysv` | same, as block type |
| `[S12345]` | `runlevel = "S12345"` |
| `<pid/a,net/b>` | `conditions = { "pid/a", "net/b" }` |
| `<!pid/a>` on service/sysv | `conditions = { "pid/a" }` + `reload-signal = "none"` |
| `<!pid/a>` on run/task | `conditions = { "pid/a" }` + `required = false` |
| `@user:group,extra` | `user`, `group`, `extra-groups = { "extra" }` |
| `name:foo :1` | block title `foo:1` |
| the command and arguments | `command = "..."` |
| `-- Description text` | `description = "..."` |
The `!` was a flag on the stanza, not a negation, and meant different
things for daemons and one-shots; each meaning is now its own key. A
`~` prefix on a condition is unchanged: `conditions = { "~pid/a" }`.
Service options
---------------
| Line-based | Block key |
|----------------------------------|--------------------------------------------------|
| `env:[-]/path` | `envfile = "[-]/path"` |
| `pid:/path` | `pidfile = "/path"` + `pidfile-create = true` |
| `pid:!/path` | `pidfile = "/path"` |
| `pid` | `pidfile = true` + `pidfile-create = true` |
| `log` | `log { }` |
| `log:/path` | `log { file = "/path" }` |
| `log:null`, `log:console` | `log { file = "/dev/null" }`, `"/dev/console"` |
| `log:prio:p,tag:t` | `log { priority = "p" identity = "t" }` |
| `notify:systemd` | `notify = "systemd"` |
| `type:forking` | `type = "forking"` |
| `manual:yes` | `manual-start = true` |
| `remain:yes` | `remain-after-exit = true` |
| `restart:always` / `restart:NUM` | `restart = "always"` / `restart-max = NUM` |
| `restart_sec:SEC` | `restart-sec = SEC` |
| `norestart` | `restart = "never"` |
| `respawn` | `respawn = true` |
| `oncrash:reboot` | `oncrash = "reboot"` |
| `halt:SIG` | `stop-signal = "SIG"` |
| `kill:SEC` | `stop-timeout = SEC` |
| `pre:[TMO,]/script` | `exec-start-pre`, `exec-start-pre-timeout` |
| `ready:[TMO,]/script` | `exec-start-ready`, and its `-timeout` |
| `stop:[TMO,]/script` | `exec-stop`, and its `-timeout` |
| `post:[TMO,]/script` | `exec-stop-post`, and its `-timeout` |
| `reload:[TMO,]/script` | `exec-reload`, and its `-timeout` |
| `cleanup:[TMO,]/script` | `exec-cleanup`, and its `-timeout` |
| `caps:^cap_a,%cap_b` | `capabilities = { "^cap_a", "%cap_b" }` |
| `conflict:a,b` | `conflicts = { "a", "b" }` |
| `if:svc` / `if:<cond>` | `if = "svc"` / `if = "cond"`, no angle brackets |
| `tty:/dev/x` | `tty = "/dev/x"` |
| `nowarn` | leading `-` on `command` or `envfile` |
| `restarttmo:` | dropped, was already an alias for `restart_sec:` |
Repeated stanzas
----------------
The line-based format has no titles, so the same service could be
declared more than once and Finit would sort out which line applied.
A block title *is* the identity, and two blocks sharing one in the
same file are rejected. The two shapes that relied on the repetition
convert differently.
**Several candidate binaries for one service.** The daemon is known
under more than one name and the stanzas were repeated once per name,
each with `nowarn` so the ones that were not installed were skipped:
service nowarn pid:!/run/udevd.pid [S12345789] /lib/systemd/systemd-udevd -- Device event daemon
service nowarn pid:!/run/udevd.pid [S12345789] udevd -- Device event daemon
The candidates now go in one block, and Finit starts the first one it
finds:
service udevd {
description = "Device event daemon"
runlevel = "S12345789"
pidfile = "/run/udevd.pid"
command = { "/lib/systemd/systemd-udevd", "-udevd" }
}
**One service gated differently per platform.** The command is the
same every time, only `if:` and the conditions differ, e.g. a syslog
daemon that has to wait for whichever hotplug daemon the system has:
service if:udevd nowarn env:-/etc/default/sysklogd <run/udevadm:5/success> \
[S0123456789] syslogd -F $SYSLOGD_ARGS -- System log daemon
service if:mdev nowarn env:-/etc/default/sysklogd <run/coldplug/success> \
[S0123456789] syslogd -F $SYSLOGD_ARGS -- System log daemon
All of these are one service, `syslogd`, and everything downstream
waits for the one `pid/syslogd` barrier it provides. That barrier is
the reason the repetition existed: the title alone spells it, so
separate titles would rename it.
Give each variant its own title and name the shared barrier with
`provides`:
service syslogd:udev {
description = "System log daemon"
runlevel = "S0123456789"
if = "udevd"
conditions = { "run/udevadm:5/success" }
provides = "pid/syslogd"
envfile = "-/etc/default/sysklogd"
command = "-syslogd -F $SYSLOGD_ARGS"
}
service syslogd:mdev {
description = "System log daemon"
runlevel = "S0123456789"
if = "mdev"
conditions = { "run/coldplug/success" }
provides = "pid/syslogd"
envfile = "-/etc/default/sysklogd"
command = "-syslogd -F $SYSLOGD_ARGS"
}
Whichever variant `if` qualifies asserts `pid/syslogd` on top of its
own `pid/syslogd:udev`, so downstream blocks need no change. The `if`
statements are meant to be mutually exclusive; if two of them do
qualify, the second claim is refused with a warning and only the first
variant supplies the barrier. See [Provided
Conditions](service-opts.md#provided-conditions).
`initctl` now knows the variants apart, `initctl status syslogd:udev`,
and a bare `initctl status syslogd` still lists them all.
Splitting the variants across files also works, since the ban on
duplicate titles is per file, as does leaving that one file in the
line-based format. Neither is needed for this shape any more.
Cgroups
-------
The standalone `cgroup.NAME` line selected a group for every stanza
after it in the file, and `cgroup:opts` applied settings to whichever
group was current. Neither survives: every block names its own group,
so nothing depends on what came earlier in the file.
cgroup.maint
service [2345] cgroup:cpu.weight:250 foo -- Foo daemon
becomes
service foo {
description = "Foo daemon"
runlevel = "2345"
cgroup maint { cpu.weight = 250 }
command = "foo"
}
Top-level group declarations keep their name and settings:
cgroup system cpu.weight:9700 mem.max:10M
becomes
cgroup system {
cpu.weight = 9700
memory.max = 10M
}
TTYs
----
The three positional variants become three key choices:
tty [12345] /dev/ttyS0 115200 noclear vt220
tty [12345] /sbin/getty -L ttyS0 115200 vt100
tty [12345] notty
become
tty ttyS0 {
runlevel = "12345"
device = "/dev/ttyS0"
baud = 115200
term = "vt220"
noclear = true
}
tty getty {
runlevel = "12345"
command = "/sbin/getty -L ttyS0 115200 vt100"
}
tty shell {
runlevel = "12345"
notty = true
}
The bare flags keep their names as booleans: `noclear`, `nowait`,
`nologin`, `passenv`, `rescue`, e.g. `passenv` becomes
`passenv = true`.
A `tty` block is named by the device, not the title, so all three above
show up as `tty:ttyS0` and the like in `initctl`. The title still has
to be unique within the file.
Top-level directives
--------------------
| Line-based | Block key |
|----------------------------------|----------------------------------------------------------|
| `host NAME`, `hostname NAME` | `hostname = "NAME"` |
| `module foo args` (repeated) | `modules = { "foo args", ... }` |
| `network script args` | `network = "script args"` |
| `runlevel N` | `runlevel = N` |
| `rcsd /path` | `rcsd = "/path"` |
| `runparts [progress] [sysv] DIR` | `runparts = "DIR"`, `runparts-progress`, `runparts-sysv` |
| `set KEY=VAL` (repeated) | `environment { KEY = "VAL" }` |
| `log size:100k count:4` | `log { size = 100k count = 4 }` |
| `rlimit [hard\|soft] RES LIM` | `rlimit { hard.res = LIM }` |
| `readiness none` | `readiness = "none"` |
| `reboot-delay N` | `reboot-delay = N` |
| `reboot-watchdog on` | `reboot-watchdog = true` |
| `service-interval SEC` | `service-interval = SEC` |
| `shutdown script` | `shutdown = "script"` |
| `mknod /dev/x c 1 2` | `mknod = { "/dev/x c 1 2" }` |
| `include /path` | `include("/path")` |
Worth knowing
-------------
* Templates work unchanged: `%i` is replaced before the file is
parsed, so `service serv:%i { ... }` in a `serv@.conf` behaves
exactly like its legacy counterpart.
* A list cannot hold comments; the lexer reads entries after a `#`
regardless. Put commented-out candidates above the list.
* `${VAR}` in a value is expanded when the file is read, against
Finit's environment, and `${VAR:-default}` works too. An unset
variable expands to nothing. A plain `$VAR` is left alone and
reaches the service, which is what keeps `command = "syslogd -F
$SYSLOGD_ARGS"` working with an `envfile`.
* New settings only appear in the block format. The first are the
[per-service directories](service-opts.md#service-directories),
`runtime-dir` and friends, and [`pam`](pam.md).
For the full description of every key, see the rest of the
[Configuration](index.md) section.
-151
View File
@@ -1,151 +0,0 @@
PAM Sessions
============
`pam = "NAME"` runs a service inside a PAM session set up from
`/etc/pam.d/NAME`. The `session` stack in that file runs for the
process that goes on to become the daemon, so modules like `pam_limits`,
`pam_env`, or `pam_keyinit` see the service the way they see a login.
## Basic Usage
A display server that needs the session a login would have arranged for
it:
```conf
service weston {
user = "weston"
pam = "weston-autologin"
command = "/usr/bin/weston --continue-without-input"
}
```
With `/etc/pam.d/weston-autologin`:
```
auth required pam_permit.so
account required pam_unix.so
session required pam_unix.so
session required pam_limits.so
```
The `auth` line is needed even though nothing is ever authenticated.
Opening the session goes through `pam_setcred()`, which consults the
`auth` stack, and an empty stack comes back as a permission denial, so a
pam.d file with only `account` and `session` lines keeps the service
from starting.
The value names a file in `/etc/pam.d`, it is not a path. A value
holding `/` or `..`, or one too long to fit, is refused, with an error
in the log when the .conf file is read. The service does not start
either, `initctl status` reports it as missing. Like the per-service
directory keys, `pam` exists in the block format only.
## No Authentication
Finit runs the account and session stacks, `pam_acct_mgmt()`,
`pam_setcred()`, and `pam_open_session()`, and never
`pam_authenticate()`. A module that asks a question gets a
conversation error back and its entry in the stack fails.
A service whose account is denied, an expired account say, does not
start, and neither does one naming a pam.d file that is not installed.
The child exits 71 (`EX_OSERR`), `initctl status` shows the service as
crashed, and PAM's own reason is in the log.
## Requirements
PAM support is built by default, see [Building Finit](../build.md).
Without it, e.g. after `--disable-pam`, a service declaring `pam` is
refused rather than started with the stacks skipped:
weston: pam weston-autologin requires Finit built with --enable-pam
## Which Blocks Take It
`service`, `task`, `run`, and `sysv`. Not `tty`: `login` opens a
session of its own there.
Every fork gets its own session, so the `pre:`, `post:`, `ready:`, and
`cleanup:` scripts each open and close one too, as do the stop and
reload scripts and the `stop` call on a `sysv` script.
A refused value stops those too. A `pre:` script forks before the
start-time check runs, so it exits 71 without running instead of the
service being reported missing.
## The User
`user` decides who the session is for. Without it the session is for
root.
systemd documents the opposite for its equivalent. `systemd.exec(5)`
says `PAMName=` is "only useful in conjunction with the `User=`
setting, and is otherwise ignored". That was true of systemd up to and
including v256, v257 changed it to open a session for the manager's own
user, and the man page was never updated. Finit does what v257 does.
A service with a [controlling tty](tty.md#controlling-tty-for-services)
has it passed to PAM as `PAM_TTY`, for the modules that care which
terminal a session is on.
## Precedence
Three places where PAM and a Finit setting cover the same ground:
- `pam_limits` overrides a per-service `rlimit`. A block asking for
`nofile = 4096` under a `limits.conf` that says 512 gets 512.
- `pam_env` overrides Finit's own environment defaults, `PATH`, `USER`,
`LOGNAME`, and `HOME`, and `envfile` in turn overrides `pam_env`. A
`HOME` from the session stack also moves the working directory, which
otherwise is the home directory from `/etc/passwd`.
- The groups from `/etc/group` and `extra-groups` override `pam_group`.
## The Session Keeper
A helper is forked next to the service to close the session when the
service exits, `(finit-pam)`:
```
CGroup : /system/weston cpu 0 [100, max] mem [--.--, max]
|- 312 /usr/bin/weston --continue-without-input
`- 313 (finit-pam)
```
There is one per fork. It runs as the service's user, in the service's
cgroup, and stopping the service takes it along.
A daemon that reaps children in its own `wait()` loop will see a child
it never forked. systemd has the same property, with `(sd-pam)`.
## Limitations
- `type = "forking"` closes the session early. The initial process
exits by design, the keeper's parent-death signal fires with it, and
the session is closed while the real daemon runs on. Finit warns
about the combination when it reads the .conf file, and starts the
service anyway:
/etc/finit.d/foo.conf: foo: pam with type = forking closes the
session when the initial process exits
- A daemon whose initial thread exits while the process lives closes the
session the same way. The parent-death signal follows the thread that
forked the keeper, not the process.
- A capability granted by `pam_cap.so` is only kept when the service
also sets [`capabilities`](capabilities.md).
- The keeper shares the service's cgroup, so an empty cgroup directory
can outlive a stop until the next start reuses it. Cosmetic.
- A module that blocks has no time bound. `pam_open_session()` waits
for as long as the module does, `pam_ldap` against an unreachable
server, say, or `pam_mount` on a hung network mount. The fork has already succeeded by then, so the service
reaches the running state and stays there with no daemon behind it.
Nothing crashes and nothing restarts.
## See Also
- [Service Options](service-opts.md) - the other run/task/service keys
- [Linux Capabilities](capabilities.md) - the key `pam_cap.so` needs
- [Building Finit](../build.md) - `--disable-pam` and its dependency
- [pam(8)](https://man7.org/linux/man-pages/man8/pam.8.html) - the PAM library
- [pam.d(5)](https://man7.org/linux/man-pages/man5/pam.d.5.html) - the file format
- [pam_limits(8)](https://man7.org/linux/man-pages/man8/pam_limits.8.html) - limits from `limits.conf`
+6 -13
View File
@@ -47,23 +47,16 @@ system administrator.
The bundled default `rescue.conf` contains nothing more than: The bundled default `rescue.conf` contains nothing more than:
runlevel = 1 runlevel 1
tty rescue { tty [12345] rescue
runlevel = "12345"
rescue = true
}
The `tty` block has `rescue` set, which works similar to the board The `tty` has the `rescue` option set, which works similar to the board
bring-up setting `notty`. The major difference being that `sulogin` bring-up tty option `notty`. The major difference being that `sulogin`
is started to query for root/admin password. If `sulogin` is not found, is started to query for root/admin password. If `sulogin` is not found,
`rescue` behaves like `notty` and gives a plain root shell prompt. `rescue` behaves like `notty` and gives a plain root shell prompt.
If Finit cannot find `/lib/finit/rescue.conf` it falls back to a If Finit cannot find `/lib/finit/rescue.conf` it defaults to:
built-in equivalent, which runs in every runlevel it can:
tty rescue { tty [12345] rescue
runlevel = "12345789"
rescue = true
}
There is no way to exit the *fallback* rescue mode. There is no way to exit the *fallback* rescue mode.
+31 -48
View File
@@ -26,20 +26,12 @@ the lifetime of your system.
Example: Example:
task console-setup { task [S] /lib/console-setup/console-setup.sh
runlevel = "S" service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
command = "/lib/console-setup/console-setup.sh"
}
service rsyslogd {
runlevel = "S12345"
envfile = "-/etc/default/rsyslog"
command = "rsyslogd -n $RSYSLOGD_ARGS"
}
When bootstrap has completed, Finit moves to runlevel 2. This can be When bootstrap has completed, Finit moves to runlevel 2. This can be
changed in `/etc/finit.conf` with `runlevel = N`, or by a script running changed in `/etc/finit.conf` using the `runlevel N` directive, or by a
in runlevel S that calls, e.g., `initctl runlevel 9`. script running in runlevel S that calls, e.g., `initctl runlevel 9`.
The latter is useful if startup scripts detect problems outside of The latter is useful if startup scripts detect problems outside of
Finit's control, e.g., critical services/devices missing or hardware Finit's control, e.g., critical services/devices missing or hardware
problems. problems.
@@ -52,7 +44,7 @@ complete before proceeding to 2.
Finit first stops everything that is not allowed to run in 2, and then Finit first stops everything that is not allowed to run in 2, and then
brings up networking. Networking is expected to be available in all brings up networking. Networking is expected to be available in all
runlevels except: S, 1 (single user level), 6, and 0. Networking is runlevels except: S, 1 (single user level), 6, and 0. Networking is
enabled either by `network = "script"`, or if you have an enabled either by the `network script` directive, or if you have an
`/etc/network/interfaces` file, Finit calls `ifup -a` -- at the very `/etc/network/interfaces` file, Finit calls `ifup -a` -- at the very
least the loopback interface is brought up. least the loopback interface is brought up.
@@ -66,7 +58,7 @@ least the loopback interface is brought up.
Runlevel Configuration Runlevel Configuration
---------------------- ----------------------
**Syntax:** `runlevel = N` **Syntax:** `runlevel <N>`
The system runlevel to go to after bootstrap (S) has completed. `N` is The system runlevel to go to after bootstrap (S) has completed. `N` is
the runlevel number 0-9, where 6 is reserved for reboot and 0 for halt. the runlevel number 0-9, where 6 is reserved for reboot and 0 for halt.
@@ -84,11 +76,11 @@ Finit disables networking in this mode.
Networking Networking
---------- ----------
**Syntax:** `network = "PATH"` **Syntax:** `network <PATH>`
Script or program to bring up networking, with optional arguments. Script or program to bring up networking, with optional arguments.
Deprecated. We recommend using dedicated task/run blocks per runlevel, Deprecated. We recommend using dedicated task/run stanzas per runlevel,
or `/etc/network/interfaces` if you have a system with `ifupdown`, like or `/etc/network/interfaces` if you have a system with `ifupdown`, like
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system. Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
@@ -98,7 +90,7 @@ Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
System Hostname System Hostname
--------------- ---------------
**Syntax:** `hostname = "NAME"` **Syntax:** `host <NAME>`, or `hostname <NAME>`
Set system hostname to NAME, unless `/etc/hostname` exists in which case Set system hostname to NAME, unless `/etc/hostname` exists in which case
the contents of that file is used. the contents of that file is used.
@@ -111,24 +103,16 @@ Deprecated. We recommend using `/etc/hostname` instead.
Kernel Modules Kernel Modules
-------------- --------------
**Syntax:** `modules = { "MODULE [ARGS]", ... }`, alias `mod` **Syntax:** `module <MODULE> [ARGS]`
Load kernel modules, each with optional arguments. Similar to the Load a kernel module, with optional arguments. Similar to `insmod`
`insmod` command line tool. command line tool.
modules = { "button", "evdev", "softdog" }
> [!NOTE]
> A list cannot hold comments; the lexer reads the entries after a `#`
> regardless. Put commented-out candidates above the list.
Deprecated, there is both a `modules-load.so` and a `modprobe.so` plugin Deprecated, there is both a `modules-load.so` and a `modprobe.so` plugin
that can handle module loading better. The former supports loading from that can handle module loading better. The former supports loading from
`/etc/modules-load.d/`, the latter uses kernel modinfo to automatically `/etc/modules-load.d/`, the latter uses kernel modinfo to automatically
load (or coldplug) every required module. Hotplug module loading is load (or coldplug) every required module. For hotplug we recommend the
handled by [keventd](../keventd.md), the built-in device manager. On BusyBox mdev tool, add to `/etc/mdev.conf`:
systems using the hotplug plugin with BusyBox mdev instead, add to
`/etc/mdev.conf`:
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS" $MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
@@ -138,35 +122,34 @@ systems using the hotplug plugin with BusyBox mdev instead, add to
Resource Limits Resource Limits
--------------- ---------------
**Syntax:** `rlimit { RESOURCE = LIMIT }`, with `soft.` or `hard.` prefix **Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>`
Set the hard or soft limit for a resource, or both if the prefix is Set the hard or soft limit for a resource, or both if that argument is
omitted. `RESOURCE` is the lower-case `RLIMIT_` string constants from omitted. `RESOURCE` is the lower-case `RLIMIT_` string constants from
`setrlimit(2)`, without prefix. E.g. to set `RLIMIT_CPU`, use `cpu`. `setrlimit(2)`, without prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
LIMIT is an integer that depends on the resource being modified, see LIMIT is an integer that depends on the resource being modified, see
[setrlimit(2)](https://man7.org/linux/man-pages/man2/setrlimit.2.html), the man page, or the kernel `/proc/PID/limits` file, for details.
or the kernel `/proc/PID/limits` file, for details. Finit versions Finit versions before v3.1 used `infinity` for `unlimited`, which is
before v3.1 used `infinity` for `unlimited`, which is still supported, still supported, albeit deprecated.
albeit deprecated.
rlimit { # No process is allowed more than 8MB of address space
hard.as = 8388608 # no more than 8MB of address space rlimit hard as 8388608
soft.core = unlimited # core dumps may be arbitrarily large
cpu = 10 # soft & hard = 10 sec # Core dumps may be arbitrarily large
} rlimit soft core infinity
# CPU limit for all services, soft & hard = 10 sec
rlimit cpu 10
`rlimit` can be set globally, in `/etc/finit.conf`, or locally per `rlimit` can be set globally, in `/etc/finit.conf`, or locally per
each `/etc/finit.d/*.conf` read. I.e., a set of task/run/service each `/etc/finit.d/*.conf` read. I.e., a set of task/run/service
blocks can share the same rlimits if they are in the same .conf. stanzas can share the same rlimits if they are in the same .conf.
For a service with [`pam`](pam.md), `pam_limits` runs after these
limits are applied, so `limits.conf` has the last word.
Miscellaneous Settings Miscellaneous Settings
---------------------- ----------------------
**Syntax:** `reboot-delay = 0-60` **Syntax:** `reboot-delay <0-60>`
Optional delay at reboot (or shutdown or halt) to allow kernel Optional delay at reboot (or shutdown or halt) to allow kernel
filesystem threads to complete after calling `sync(2)` before filesystem threads to complete after calling `sync(2)` before
@@ -184,7 +167,7 @@ sync(2) has been called, twice.
> writing; it can actually take a short time before all the blocks are > writing; it can actually take a short time before all the blocks are
> finally written. > finally written.
**Syntax:** `reboot-watchdog = true|false` **Syntax:** `reboot-watchdog <on|off|true|false|1|0>`
Controls whether the system should reboot via the watchdog timer (WDT) Controls whether the system should reboot via the watchdog timer (WDT)
or directly via the SoC/kernel. When enabled, Finit will: or directly via the SoC/kernel. When enabled, Finit will:
+7 -8
View File
@@ -1,7 +1,7 @@
Run-parts Scripts Run-parts Scripts
----------------- -----------------
**Syntax:** `runparts = "DIR"` **Syntax:** `runparts [progress] [sysv] <DIR>`
Call [run-parts(8)][] on `DIR` to run start scripts. All executable Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files in the directory are called, in alphabetic order. The scripts in files in the directory are called, in alphabetic order. The scripts in
@@ -14,10 +14,9 @@ whatever the next runlevel is set to be (default 2). E.g., generate a
**Options:** **Options:**
- `runparts-progress = true`: display the progress of each script being - `progress`: display the progress of each script being executed
executed - `sysv`: run only SysV style scripts, i.e., `SNNfoo`, or `KNNbar`,
- `runparts-sysv = true`: run only SysV style scripts, i.e., `SNNfoo`, where `NN` is a number (0-99).
or `KNNbar`, where `NN` is a number (0-99).
If global debug mode is enabled, the `runparts` program is also called If global debug mode is enabled, the `runparts` program is also called
with the debug flag. with the debug flag.
@@ -25,7 +24,7 @@ with the debug flag.
**Limitations:** **Limitations:**
Scripts called from `runparts`, or hook scripts (see below), are limited Scripts called from `runparts`, or hook scripts (see below), are limited
in their interaction with Finit. Like a standalone `run` block and in their interaction with Finit. Like the standalone `run` stanza and
the `/etc/rc.local` shell script, Finit waits for their completion the `/etc/rc.local` shell script, Finit waits for their completion
before continuing. None of them can issue commands to start, stop, or before continuing. None of them can issue commands to start, stop, or
restart other services. Also, ensure all your services and programs restart other services. Also, ensure all your services and programs
@@ -40,13 +39,13 @@ either terminate or start in the background or you will block Finit.
It can be beneficial to use `01-name`, `02-othername`, etc., to ensure It can be beneficial to use `01-name`, `02-othername`, etc., to ensure
the scripts are started in that order, e.g., if there is a dependency the scripts are started in that order, e.g., if there is a dependency
order between scripts. Symlinks to existing daemons can also be used, order between scripts. Symlinks to existing daemons can talso be used,
but make sure they daemonize (background) themselves properly, otherwise but make sure they daemonize (background) themselves properly, otherwise
Finit will lock up. Finit will lock up.
If `S[0-9]foo` and `K[0-9]bar` style naming is used, the executable will If `S[0-9]foo` and `K[0-9]bar` style naming is used, the executable will
be called with an extra argument, `start` and `stop`, respectively. be called with an extra argument, `start` and `stop`, respectively.
E.g., `S01foo` will be called as `S01foo start`. Of course, `S01foo` E.g., `S01foo` will be called as `S01foo start`. Of course, `S01foo`
and `K01foo` may be a symlink to `another/directory/foo`. and `K01foo` may be a symlink to to `another/directory/foo`.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
+4 -14
View File
@@ -13,12 +13,7 @@ file `/etc/default/foo`. Like this:
* `/etc/finit.conf`: * `/etc/finit.conf`:
service foo { service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon
description = "Example foo daemon"
runlevel = "2345"
envfile = "-/etc/default/foo"
command = "foo -n $FOO_OPTIONS"
}
Here the service `foo` is started with `-n`, to make sure it runs in the Here the service `foo` is started with `-n`, to make sure it runs in the
foreground, and the with the options found in the environment file. With foreground, and the with the options found in the environment file. With
@@ -27,11 +22,6 @@ the `ps` command we can see that the process is started with:
foo -n --extra-arg=bar -s -x foo -n --extra-arg=bar -s -x
> [!NOTE] > [!NOTE]
For a service with [`pam`](pam.md), the session environment is applied > The leading `-` in `env:` determines if Finit should treat a missing
before `envfile`, so the file overrides anything `pam_env` set. > environment file as blocking the start of the service or not. When
> `-` is used, a missing environment file does *not* block the start.
> The leading `-` on `envfile` determines if Finit should treat a
> missing environment file as blocking the start of the service or not.
> When `-` is used, a missing environment file does *not* block the
> start. The same mark works on `command`, where it means carry on if
> the binary is missing.
+89 -287
View File
@@ -1,22 +1,21 @@
Service Options Service Options
=============== ===============
The `run`, `task`, `tty`, `service` and `sysv` blocks take settings as The run/task/tty/service/sysv directives take modifiers, or options, to
keys. This section lists them with their limitations. control their behavior. This section lists them with their limitations.
All modifiers must be placed between the directive and its command.
The name of a service, shown by the `initctl` tool, is the block title. The name of a service, shown by the `initctl` tool, defaults to the
It defaults to the basename of the executable if the title is omitted: basename of the service executable. It can be changed with the `name:`
option:
service sshd { name:<service-name>
command = "/usr/sbin/sshd -D"
}
For multiple instances of a service, with the same name, add an For multiple instances of a service, with the same `name`, set the
identifier after a colon to prevent Finit from replacing previous identifier `:ID` to prevent Finit from replacing previous instances:
instances:
service ssdp:eth1 { command = "ssdpd eth1" } service name:ssdp :eth1 ssdpd eth1 -- Windows discovery on eth1
service ssdp:eth2 { command = "ssdpd eth2" } service name:ssdp :eth2 ssdpd eth2 -- Windows discovery on eth2
The [`initctl`](../initctl.md) tool will list these two services as: The [`initctl`](../initctl.md) tool will list these two services as:
@@ -24,295 +23,100 @@ The [`initctl`](../initctl.md) tool will list these two services as:
- ssdp:eth2 - ssdp:eth2
Conflicting services that must be prevented from starting, use the Conflicting services that must be prevented from starting, use the
`conflicts` key: `conflict:` option:
service udevd { command = "udevd" } service [S12345789] udevd -- Device event management daemon
run mdev { run [S] conflict:udevd mdev -s -- Populating device tree
runlevel = "S"
conflicts = { "udevd" }
command = "mdev -s"
}
The list may name several, and an instance is named the same way it is Multiple conflicting services can be separated using `,`:
declared:
service abc:1 { command = "abc 1" } service :1 abc
service abc:2 { command = "abc 2" } service :2 abc
service cde { conflicts = { "abc:1", "abc:2" } command = "cde" } service conflict:abc:1,abc:2 cde
If a service should not be automatically started, set `manual-start`. If a service should not be automatically started, it can be configured
It can then be started at any time with `initctl start <service>`: as manual with the optional `manual` argument. The service can then be
started at any time by running `initctl start <service>`.
service lldpd { manual:yes
manual-start = true
command = "/usr/sbin/lldpd -d"
}
Other run/task/service settings are: Other run/task/service options are:
* `capabilities` -- see the [Linux Capabilities](capabilities.md) section * `cgroups:...` -- see the [Cgroups](cgroups.md) section
* `cgroup NAME {}` -- see the [Cgroups](cgroups.md) section * `env:[-]/path/to/env` -- see the [Service Environment](service-env.md) section
* `envfile` -- see the [Service Environment](service-env.md) section * `log:...` -- see [Redirecting Output](logging.md#redirecting-output)
* `log {}` -- see [Redirecting Output](logging.md#redirecting-output) * `nowarn` -- see [Conditional Loading](services.md#conditional-loading)
* `tty` -- see [Controlling TTY](tty.md#controlling-tty-for-services) * `notify:...` -- see [Service Synchronization](service-sync.md)
* `notify` -- see [Service Synchronization](service-sync.md) * `if:...` -- see [Conditional Execution](services.md#conditional-execution)
* `pam` -- see the [PAM Sessions](pam.md) section * `type:forking` -- see description of the [service](services.md) directive
* `if` -- see [Conditional Execution](services.md#conditional-execution)
* `type = "forking"` -- see description of the [service](services.md) block
* a leading `-` on `command` -- see
[Conditional Loading](services.md#conditional-loading)
Command Candidates As mentioned previously, services are automatically started, restarted,
------------------ and stopped, depending on the configuration and conditions. Within the
confines of that the following options are available:
The `command` setting may list several candidates for the same * `restart:NUM` -- number of times Finit tries to restart a crashing
service, in which case Finit starts the first one it finds: service, default: 10, max: 255. When this limit is reached the
service udevd {
command = { "/lib/systemd/systemd-udevd", "-udevd" }
}
A candidate that is not installed is skipped without a warning, that
is the point of listing more than one. The leading `-` is read from
the candidate that wins, or from the last one when none are found, so
a block with a single command behaves as it always has.
The candidates are alternative spellings of one service, not a
fallback for a service that fails to start. Finit chooses when the
.conf file is read and does not revisit the choice.
A [`tty`](tty.md) block takes the same list, where the candidates are
the getty to run:
tty ttyAMA0 {
command = { "/sbin/agetty -L ttyAMA0 115200 vt100",
"/sbin/getty -L 115200 /dev/ttyAMA0 vt100" }
}
Provided Conditions
-------------------
A service always asserts `pid/<ident>` for itself, named after the
block title. `provides` names conditions it asserts in addition to
that one:
service syslogd:udev {
if = "udevd"
provides = "pid/syslogd"
command = "syslogd -F"
}
This is what lets variants of one service, picked apart by
[`if`](services.md#conditional-execution), share the barrier that
everything downstream waits for. Each variant has its own title, so
each is its own service to `initctl`, while the condition they publish
stays the same.
Any namespace works, `pid/`, `usr/`, `service/`, since the point is
publishing a name existing configuration already waits on. A value
without a namespace separator is not a condition and is rejected. The
list holds at most four.
A provided condition is asserted and cleared with the service, exactly
like its own, so stopping the provider clears the barrier.
Two services cannot supply the same condition. The second claim is
refused, with the owner named:
finit.conf: second: provides condition <usr/barrier> already registered by service first, ignoring
The service itself still registers and runs, only the claim is
dropped. Which one is "first" is .conf load order, so it follows the
file names, see [Files & Layout](files.md).
A real identity outranks a claim on it. `provides = "pid/sshd"` in a
system that also runs a service titled `sshd` loses to that service,
because `pid/<ident>` is how Finit tracks the service itself.
`initctl cond dump` names the owner of every condition, which for a
provided one is the service that claimed it:
$ initctl cond dump
PID IDENT STATUS CONDITION
1234 syslogd:udev on <pid/syslogd>
1234 syslogd:udev on <pid/syslogd:udev>
Duplicate Titles
----------------
The title is the service identity, so two blocks sharing a title in
one file are two declarations of one service. The parser underneath
merges them without a word, leaving a single service holding a mix of
both, so Finit rejects the file and names the title:
/etc/finit.d/hotplug.conf:12: found duplicate title 'udevd'
Nothing from a rejected file is loaded and the rest of the
configuration is unaffected.
The same title in *another* file is fine. That is how a system .conf
is overridden, the later file wins, see [Files & Layout](files.md).
Two shapes need a single identity and used to be written as repeated
declarations. One is a service with several candidate binaries, which
is now a [command list](#command-candidates). The other is a service
gated differently per platform, e.g. a syslog daemon waiting for
whichever hotplug daemon the system happens to have. For that one,
give each variant its own file, or keep them as one-liners in the
line-based format, which has no titles. The [migration
guide](migration.md) works through it.
Restarting
----------
Services are automatically started, restarted, and stopped, depending
on the configuration and conditions. Within the confines of that the
following settings are available:
* `restart-max = NUM` -- number of times Finit tries to restart a
crashing service, default: 10. When this limit is reached the
service is marked *crashed* and must be restarted manually with service is marked *crashed* and must be restarted manually with
`initctl restart NAME` `initctl restart NAME`
* `restart-sec = SEC` -- number of seconds before Finit tries to * `restart_sec:SEC` -- number of seconds before Finit tries to restart
restart a crashing service. The default is 2 seconds for the first a crashing service, default: 2 seconds for the first five retries,
half of `restart-max` attempts, then a back-off to 5 seconds -- with then back-off to 5 seconds. The maximum of this configured value
the default `restart-max` that is the first five retries. The and the above (2 and 5) will be used
greater of this configured value and the back-off is used * `restart:always` -- no upper limit on the number of times Finit
* `restart = "always"` -- no upper limit on the number of times Finit tries to restart a crashing service. Same as `restart:-1`
tries to restart a crashing service * `norestart` -- dont restart on failures, same as `restart:0`
* `restart = "never"` -- do not restart on failures. `false` is * `respawn` -- bypasses the `restart` mechanism completely, allows
accepted as a synonym, and `true` selects the default policy endless restarts. Useful in many use-cases, but not what `service`
* `respawn = true` -- bypasses the `restart` mechanism completely, was originally designed for so not the default behavior
allowing endless restarts. Exiting is treated as normal work * `oncrash:reboot` -- when all retries have failed, and the service
rather than failure, so the service is restarted at once instead of has *crashed*, if this option is set the system is rebooted
being counted and delayed. This is how a `tty` behaves; it is not * `oncrash:script` -- similarly, but instead of rebooting, call the
what `service` was designed for, so it is not the default `post:script` action with exit code `crashed`, see below
* `remain-after-exit = true` -- for `run` and `task` only. Prevents * `reload:'script [args]'` -- some services do not support `SIGHUP` but
the task from re-running on runlevel re-entry and ensures the may have other ways to update the configuration of a running daemon.
`exec-stop-post` script runs when the task is explicitly stopped or When `reload:script` is defined it is preferred over `SIGHUP`. Like
leaves its valid runlevels. This is systemd's `RemainAfterExit=`. systemd, Finit sets `$MAINPID` as a convenience to scripts, which in
See [Task and Run](task-and-run.md) for more details effect also allow `reload:'kill -HUP $MAINPID'`
* `oncrash = "reboot"` -- when all retries have failed, and the * `stop:'script [args]'` -- some services may require alternate methods
service has *crashed*, the system is rebooted to be stopped. If a `stop:script` is defined it is preferred over
* `oncrash = "script"` -- similarly, but instead of rebooting, call `SIGTERM` and `stop`, for `service` and `sysv`, respectively.
the `exec-stop-post` script with exit code `crashed`, see below Similar to `reload:script`, Finit sets `$MAINPID`
Service directories > [!CAUTION]
------------------- > Both `reload:script` and `stop:script` are called as PID 1, without
> any timeout! Meaning, it is up to you to ensure the script is not
> blocking for seconds at a time or never terminates.
Five settings ask Finit to create a directory for the service before it When stopping a service (run/task/sysv/service), either manually or when
starts, owned by its `user` and `group`, mode 0755. The value is a moving to another runlevel, Finit starts by sending `SIGTERM`, to allow
directory name, resolved under a fixed base -- absolute paths and `..` the process to shut down gracefully (unless a `stop:'script'` is used).
are refused: However, if the process has not been collected within 3 seconds, Finit
will send `SIGKILL`. To stop the process using a different signal than
`SIGTERM`, use `halt:SIGNAL`, e.g., `halt:SIGPWR`. To change the delay
between the stop signal and KILL, use the option `kill:<1-60>`, e.g.,
`kill:10` to wait 10 seconds before sending `SIGKILL`.
| Setting | Base | Environment variable | Services, including the `sysv` variant, support pre/post/ready and
|---|---|---| cleanup scripts:
| `runtime-dir` | `/run` | `RUNTIME_DIRECTORY` |
| `state-dir` | `/var/lib` | `STATE_DIRECTORY` |
| `cache-dir` | `/var/cache` | `CACHE_DIRECTORY` |
| `logs-dir` | `/var/log` | `LOGS_DIRECTORY` |
| `config-dir` | `/etc` | `CONFIGURATION_DIRECTORY` |
Each resolved path is exported to the process environment under the * `pre:[0-3600,]script` -- called before the sysv/service is stated
listed name, the same names systemd uses for `RuntimeDirectory=` and * `post:[0-3600,]script` -- called after the sysv/service has stopped
friends. As in systemd, `config-dir` is the odd one out: it is created * `ready:[0-3600,]script` -- called when the sysv/service is ready
but never chowned. * `cleanup:[0-3600,]script` -- called when run/task/sysv/service is removed
Each takes a matching `-mode`, e.g. `runtime-dir-mode = 0700`, default The optional number (0-3600) is the timeout before Finit kills the
0755. Modes are octal, with the leading zero. script, it defaults to the kill delay value and can be disabled by
setting it to zero. These scripts run as the same `@USER:GROUP` as the
The mode of the named directory is locked down again on every start. service itself, with any `env:file` sourced. The scripts are executed
Its contents are left alone as long as the owner is right; if the owner from the `$HOME` of the given user. The scripts are not called with any
has drifted, everything under it is chowned back. argument, but get a set of environment variables:
The runtime directory is removed again when the service stops, after
any `exec-stop-post` script has run; `/run` is a tmpfs so it would not
survive a reboot anyway. The other four persist. A completed `run` or
`task` counts as stopped, unless `remain-after-exit` keeps it alive
until stopped for real. `runtime-dir-preserve` adjusts this, same
values as systemd's `RuntimeDirectoryPreserve=`:
* `"no"` -- removed when the service stops, the default
* `"restart"` -- kept across restarts, removed on a real stop
* `"yes"` -- never removed
This is what lets a service drop privileges and still create, and
later touch, its own PID file:
service ntpd {
user = "ntp"
group = "ntp"
runtime-dir = "ntpd"
pidfile = "/run/ntpd/ntpd.pid"
command = "/usr/sbin/ntpd -n -p /run/ntpd/ntpd.pid"
}
These settings exist only in the block format.
Stopping and reloading
----------------------
When stopping a service, either manually or when moving to another
runlevel, Finit starts by sending `SIGTERM` to let the process shut
down gracefully. If it has not been collected within 3 seconds,
`SIGKILL` follows. Both ends of that are configurable:
* `stop-signal = "SIGPWR"` -- send this instead of `SIGTERM`
* `stop-timeout = 10` -- seconds to wait before `SIGKILL`, 1-300
Some services need more than a signal:
* `exec-stop = "script [args]"` -- run instead of sending the stop
signal, and instead of `stop` for `sysv`
* `exec-reload = "script [args]"` -- run instead of sending `SIGHUP`.
Like systemd, Finit sets `$MAINPID` as a convenience to scripts,
which in effect also allows `exec-reload = "kill -HUP $MAINPID"`
If a daemon cannot be reloaded with a signal at all, say so and Finit
restarts it instead:
service dropbear {
reload-signal = "none"
command = "/usr/sbin/dropbear -R -F"
}
`SIGHUP` is the default and the only other accepted value, with or
without the `SIG` prefix and in any case.
Scripts
-------
Services, including the `sysv` variant, support six lifecycle scripts:
* `exec-start-pre` -- called before the sysv/service is started
* `exec-start-ready` -- called when the sysv/service is ready
* `exec-stop` -- called instead of the stop signal
* `exec-stop-post` -- called after the sysv/service has stopped
* `exec-reload` -- called instead of `SIGHUP`
* `exec-cleanup` -- called when run/task/sysv/service is removed
Each takes a matching `-timeout`, in seconds, 0-3600, after which Finit
kills the script. It defaults to the `stop-timeout` value and can be
disabled by setting it to zero:
service foo {
exec-start-pre = "/etc/foo/pre.sh"
exec-start-pre-timeout = 10
command = "/usr/sbin/foo"
}
These scripts run as the same user and group as the service itself,
with any `envfile` sourced. They are executed from the `$HOME` of the
given user. The scripts are not called with any argument, but get a
set of environment variables:
* `SERVICE_IDENT=foo:1` * `SERVICE_IDENT=foo:1`
* `SERVICE_NAME=foo` * `SERVICE_NAME=foo`
* `SERVICE_ID=1` * `SERVICE_ID=1`
The `exec-stop-post` script is called with an additional set of The `post:script` is called with an additional set of environment
environment variables. Yes, the text is correct, the naming was an variables. Yes, the text is correct, the naming was an accident:
accident:
- `EXIT_CODE=[exited,signal,crashed]`: normal exit, signaled, or - `EXIT_CODE=[exited,signal,crashed]`: normal exit, signaled, or
crashed crashed
@@ -320,13 +124,11 @@ accident:
the program, if it exited normally, or the signal name (`HUP`, the program, if it exited normally, or the signal name (`HUP`,
`TERM`, etc.) if it exited due to signal `TERM`, etc.) if it exited due to signal
When a run/task/sysv/service is removed (disable + reload) it is first When a run/task/sys/service is removed (disable + reload) it is first
stopped and then removed from the runlevel. The `exec-stop-post` stopped and then removed from the runlevel. The `post:script` always
script always runs when the process has stopped, and `exec-cleanup` runs when the process has stopped, and the `cleanup:script` runs when
runs when the block has been removed from the runlevel. the the stanza has been removed from the runlevel.
> [!IMPORTANT] > [!IMPORTANT]
> These script actions are intended for setup, cleanup, and readiness > These script actions are intended for setup, cleanup, and readiness
> notification. A script that outlives its timeout is killed, so pick > notification. It is up to the user to ensure the scripts terminate.
> one that suits the work, or set it to zero to opt out and take
> responsibility for the script terminating.
+13 -20
View File
@@ -32,35 +32,28 @@ notification is available, and the native PID file mode of operation is,
as of Finit v4.6 optional, by default it is still enabled, but this can as of Finit v4.6 optional, by default it is still enabled, but this can
be changed in `finit.conf`: be changed in `finit.conf`:
readiness = "none" readiness none
This will be made the default in Finit 5.0. In this mode of operation, This will be made the default in Finit 5.0. In this mode of operation,
every service needs to explicitly declare their readiness notification, every service needs to explicitly declare their readiness notification,
like this: like this:
service watchdogd { notify = "pid" command = "watchdogd" } service notify:pid watchdogd
service foo { notify = "systemd" command = "foo" } service notify:systemd foo
service bar { notify = "s6" command = "bar" } service notify:s6 bar
service qux { notify = "none" command = "qux" } service notify:none qux
The `notify = "none"` setting is for completeness in systems which run The `notify:none` syntax is for completeness in systems which run in
in `readiness = "pid"` mode (default). Services declared with `readiness pid` mode (default). Services declared with `notify:none`
`notify = "none"` will transition to ready as soon as Finit has started will transition to ready as soon as Finit has started them, e.g.,
them, e.g., `service/qux/ready`. `service/qux/ready`.
To synchronize two services the following condition can be used: To synchronize two services the following condition can be used:
service watchdogd { service notify:pid watchdogd
notify = "pid" service <service/watchdogd/ready> stress-ng --cpu 8
command = "watchdogd"
}
service stress-ng { For details on the syntax and options, see below.
conditions = { "service/watchdogd/ready" }
command = "stress-ng --cpu 8"
}
For the full list of conditions, see [Finit Conditions](../conditions.md).
> [!NOTE] > [!NOTE]
> On `initctl reload` conditions are set in "flux", while figuring out > On `initctl reload` conditions are set in "flux", while figuring out
@@ -73,7 +66,7 @@ For the full list of conditions, see [Finit Conditions](../conditions.md).
> However, the s6 notify mode does not support this because in s6 you > However, the s6 notify mode does not support this because in s6 you
> are expected to close your notify descriptor after having written > are expected to close your notify descriptor after having written
> `\n`. This means s6 style daemons currently must be stop-started. > `\n`. This means s6 style daemons currently must be stop-started.
> (Declare the service with `reload-signal = "none"`.) > (Declare the service with `<!>` in its condition statement.)
> >
> For default, PID file style readiness notification, daemons are > For default, PID file style readiness notification, daemons are
> expected to either create their PID files, or touch it using > expected to either create their PID files, or touch it using
+5 -10
View File
@@ -7,19 +7,14 @@ use a wrapper shell script to start your service.
The Finit service `.conf` file can be put into `/etc/finit.d/available`, The Finit service `.conf` file can be put into `/etc/finit.d/available`,
so you can control the service using `initctl`. Then use the path to so you can control the service using `initctl`. Then use the path to
the wrapper script in the Finit `.conf` service block. The following the wrapper script in the Finit `.conf` service stanza. The following
example employs a wrapper script in `/etc/start.d`. example employs a wrapper script in `/etc/start.d`.
**Example:** **Example:**
* `/etc/finit.d/available/program.conf`: * `/etc/finit.d/available/program.conf`:
service program { service [235] <!> /etc/start.d/program -- Example Program
description = "Example Program"
runlevel = "235"
reload-signal = "none"
command = "/etc/start.d/program"
}
* `/etc/start.d/program:` * `/etc/start.d/program:`
@@ -31,6 +26,6 @@ example employs a wrapper script in `/etc/start.d`.
exec /usr/bin/program $OPTIONS exec /usr/bin/program $OPTIONS
> [!NOTE] > [!NOTE]
> The example sets `reload-signal = "none"` to say the program does not > The example sets `<!>` to denote that it doesn't support `SIGHUP`.
> support `SIGHUP`. Finit then stop/starts the service instead of > That way Finit will stop/start the service instead of sending SIGHUP
> signalling it at restart/reload events. > at restart/reload events.
+81 -236
View File
@@ -1,17 +1,16 @@
Services Services
======== ========
**Syntax:** `service NAME { command = "/path/to/daemon ARGS" }` **Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Finit tries to restart services that die, by default exits prematurely. Finit tries to restart services that die, by default
10 times before giving up and marking them as *crashed*. After which 10 times before giving up and marking them as *crashed*. After which
they have to be manually restarted with `initctl restart NAME`. The they have to be manually restarted with `initctl restart NAME`. The
limits controlling this are configurable, see limits controlling this are configurable, see the options below.
[Service Options](service-opts.md).
> [!TIP] > [!TIP]
> To allow endless restarts, see [`respawn`](service-opts.md#restarting) > To allow endless restarts, see the [`respawn` option](service-opts.md)
For daemons that support it, we recommend appending `--foreground`, For daemons that support it, we recommend appending `--foreground`,
`--no-background`, `-n`, `-F`, or similar command line argument to `--no-background`, `-n`, `-F`, or similar command line argument to
@@ -20,105 +19,65 @@ the most reliable way to monitor a service.
However, not all daemons support running in the foreground, or they may However, not all daemons support running in the foreground, or they may
start logging to the foreground as well, these are forking daemons and start logging to the foreground as well, these are forking daemons and
are supported using the same syntax as forking `sysv` services, by are supported using the same syntax as forking `sysv` services, using
naming the file to watch with `pidfile`. There is an alternative that the `pid:!/path/to/pidfile.pid` syntax. There is an alternative syntax
may be more intuitive, where Finit can also guess the PID file based on that may be more intuitive, where Finit can also guess the PID file
the daemon's command name: based on the daemon's command name:
service ntpd { service type:forking ntpd -- NTP daemon
description = "NTP daemon"
type = "forking"
command = "ntpd"
}
This example lets BusyBox `ntpd` daemonize itself. Finit uses the This example lets BusyBox `ntpd` daemonize itself. Finit uses the
basename of the binary to guess the PID file to watch for the PID: basename of the binary to guess the PID file to watch for the PID:
`/var/run/ntpd.pid`. If Finit guesses wrong, name the file yourself `/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the
with `pidfile = "/path/to/file.pid"`. full `pid:!/path/to/file.pid`.
The file belongs to the service: Finit reads it but does not create or
remove it. That is the default, and `pidfile-create = true` is what
asks Finit to write the file instead. The one exception is *stale*
cleanup — if the service dies without removing its own pidfile
(SIGKILL, OOM, segfault), and the file still names the just-reaped
PID, Finit removes it before the next retry. This prevents daemons
that refuse to start on an existing pidfile (e.g. `dbus-daemon`)
from getting stuck in a crash-restart loop.
**Example:** **Example:**
In the case of `ospfd` (below), we omit the `-d` flag (daemonize) to In the case of `ospfd` (below), we omit the `-d` flag (daemonize) to
prevent it from forking to the background: prevent it from forking to the background:
service ospfd { service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
description = "OSPF daemon"
runlevel = "2345"
conditions = { "pid/zebra" }
command = "/sbin/ospfd"
}
`runlevel` denotes the runlevels `ospfd` is allowed to run in, it is `[2345]` denote the runlevels `ospfd` is allowed to run in, they are
optional and defaults to level 2-4 if omitted. optional and default to level 2-5 if omitted.
`conditions` lists what must be asserted before starting `ospfd`. In `<...>` is the condition for starting `ospfd`. In this example Finit
this example Finit waits for another service, `zebra`, to have created waits for another service, `zebra`, to have created its PID file in
its PID file in `/var/run/quagga/zebra.pid`. Finit watches *all* files `/var/run/quagga/zebra.pid` before starting `ospfd`. Finit watches
in `/var/run`, for each file named `*.pid`, or `*/pid`, Finit opens it *all* files in `/var/run`, for each file named `*.pid`, or `*/pid`,
and finds the matching `NAME:ID` using the PID. Finit opens it and find the matching `NAME:ID` using the PID.
A condition may be prefixed with `~` to propagate a reload of the
upstream service to this one, rather than merely pausing and resuming
it:
conditions = { "~pid/zebra" }
If `ospfd` cannot be reloaded with `SIGHUP` at all, that is a property
of `ospfd` and not of the condition, so it is said directly:
reload-signal = "none"
The legacy format spells that second one as a `!` leading the condition
list, which is not accepted here. For details, see the
[Finit Conditions](../conditions.md) document.
Some services do not maintain a PID file and rather than patching each Some services do not maintain a PID file and rather than patching each
application Finit provides a workaround. With `pidfile-create` Finit application Finit provides a workaround. A `pid` keyword can be set
creates the file when starting and removes it when stopping. The path to have Finit automatically create (when starting) and later remove
comes from `pidfile`, which takes three forms: (when stopping) the PID file. The file is created in the `/var/run`
directory using the `basename(1)` of the service. The default can be
modified with an optional `pid:`-argument:
pidfile = true # /var/run/<command basename>.pid pid[:[/path/to/]filename[.pid]]
pidfile = "bar" # a bare name, /var/run/bar.pid
pidfile = "/run/bar.pid" # an explicit path
Such a file is also used by the Finit condition subsystem, so another For example, by adding `pid:/run/bar.pid` to the service `/sbin/bar`,
service, run or task can depend on `pid/bar`. Here foo is not started that PID file will, not only be created and removed automatically, but
until bar has: also be used by the Finit condition subsystem. So a service/run/task
can depend on `<pid/bar>`, like this foo will not be started until bar
has started:
service bar { service pid:/run/bar.pid bar -- Bar Service
description = "Bar Service" service <pid/bar> foo -- Foo Service
pidfile = "/run/bar.pid"
pidfile-create = true
command = "bar"
}
service foo {
description = "Foo Service"
conditions = { "pid/bar" }
command = "foo"
}
Needless to say, it is better if `bar` creates its own PID file when it Needless to say, it is better if `bar` creates its own PID file when it
has completed starting up and is ready for service. has completed starting up and is ready for service.
As an alternative "readiness" notification, Finit supports both systemd As an alternative "readiness" notification, Finit supports both systemd
and s6 style notification. This is enabled with the `notify` key: and s6 style notification. This can be enabled by using the `notify`
option:
* `notify = "systemd"` -- tells Finit the service uses the `sd_notify()` * `notify:systemd` -- tells Finit the service uses the `sd_notify()`
API to signal PID 1 when it has completed its startup and is ready API to signal PID 1 when it has completed its startup and is ready
to service events. The [sd_notify()][] API expects `NOTIFY_SOCKET` to service events. The [sd_notify()][] API expects `NOTIFY_SOCKET`
to be set to the socket where the application can send `"READY=1\n"` to be set to the socket where the application can send `"READY=1\n"`
when it is starting up or has processed a `SIGHUP`. when it is starting up or has processed a `SIGHUP`.
* `notify = "s6"` -- puts Finit in s6 compatibility mode. Compared to the * `notify:s6` -- puts Finit in s6 compatibility mode. Compared to the
systemd notification, [s6 expect][] compliant daemons to send `"\n"` systemd notification, [s6 expect][] compliant daemons to send `"\n"`
and then close their socket. Finit takes care of "hard-wiring" the and then close their socket. Finit takes care of "hard-wiring" the
READY state as long as the application is running, events across any READY state as long as the application is running, events across any
@@ -126,11 +85,7 @@ and s6 style notification. This is enabled with the `notify` key:
(must be >3) on then command line, Finit provides the following (must be >3) on then command line, Finit provides the following
syntax (`%n` is replaced by Finit with then descriptor number): syntax (`%n` is replaced by Finit with then descriptor number):
service mdevd { service [S12345789] notify:s6 mdevd -O 4 -D %n
runlevel = "S12345789"
notify = "s6"
command = "mdevd -O 4 -D %n"
}
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html [sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
[s6 expect]: https://skarnet.org/software/s6/notifywhenup.html [s6 expect]: https://skarnet.org/software/s6/notifywhenup.html
@@ -144,13 +99,7 @@ then service's ready condition which other services can depend on:
This can be used to synchronize the start of another run/task/service: This can be used to synchronize the start of another run/task/service:
task mdevd-coldplug { task [S] <service/mdevd/ready> @root:root mdevd-coldplug
runlevel = "S"
conditions = { "service/mdevd/ready" }
user = "root"
group = "root"
command = "mdevd-coldplug"
}
Finit waits for `mdevd` to notify it, before starting `mdevd-coldplug`. Finit waits for `mdevd` to notify it, before starting `mdevd-coldplug`.
Notice how both start in runlevel S, and the coldplug task only runs in Notice how both start in runlevel S, and the coldplug task only runs in
@@ -166,191 +115,87 @@ Non-privileged Services
----------------------- -----------------------
Every `run`, `task`, or `service` can also list the privileges the Every `run`, `task`, or `service` can also list the privileges the
command should be executed with, using `user`, `group` and `/path/to/cmd` should be executed with. Prefix the command with
`extra-groups`, all optional: `@USR[:GRP]`, group is optional, like this:
run hello { run [2345] @joe:users logger "Hello world"
runlevel = "2345"
user = "joe"
group = "users"
command = "logger \"Hello world\""
}
Finit reads the user's supplementary group membership from `/etc/group`
automatically. Any groups the user belongs to will be inherited by
the service.
To specify additional supplementary groups beyond those in
`/etc/group`, list them in `extra-groups`:
service caddy {
user = "caddy"
group = "caddy"
extra-groups = { "ssl-cert" }
command = "/usr/bin/caddy run"
}
This runs the `caddy` service as user `caddy`, with primary group
`caddy`, inheriting any groups `caddy` is a member of in `/etc/group`,
plus the additional `ssl-cert` group. This is useful when a service
needs access to resources owned by groups not listed in `/etc/group`.
For multiple instances of the same command, e.g. a DHCP client or For multiple instances of the same command, e.g. a DHCP client or
multiple web servers, add `:ID` to the block title, like this: multiple web servers, add `:ID` somewhere between the `run`, `task`,
`service` keyword and the command, like this:
service httpd:80 { service :80 [2345] httpd -f -h /http -p 80 -- Web server
description = "Web server" service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
runlevel = "2345"
command = "httpd -f -h /http -p 80"
}
service httpd:8080 { Without the `:ID` to the service the latter will overwrite the former
description = "Old web server" and only the old web server would be started and supervised.
runlevel = "2345"
command = "httpd -f -h /http -p 8080"
}
Without the `:ID` the latter will overwrite the former and only the old
web server would be started and supervised.
> [!NOTE]
> The line-based format also accepts a bare ID, `service :80 ...`,
> deriving the name from the command. There is no block equivalent:
> the title carries both name and ID.
Conditional Loading Conditional Loading
------------------- -------------------
Finit supports conditional loading of blocks. The following example is Finit support conditional loading of stanzas. The following example is
taken from the `system/10-hotplug.conf` file in the Finit distribution. take from the `system/hotplug.conf` file in the Finit distribution.
Here we only show a simplified subset. Here we only show a simplified subset.
Starting with the udev daemon, which goes by two names depending on Starting with the `nowarn` option.
how it was built. Both are candidates for the same service:
service udevd { service nowarn name:udevd pid:udevd /lib/systemd/systemd-udevd
pidfile = "udevd" service nowarn name:udevd pid:udevd udevd
command = { "/lib/systemd/systemd-udevd", "-udevd" }
}
When loading the .conf file Finit looks for When loading the .conf file Finit looks for `/lib/systemd/systemd-udevd`
`/lib/systemd/systemd-udevd`, and if that is not there it moves on to if that is not found Finit automatically logs a warning. The `nowarn`
`udevd`. A candidate that is not installed is expected, so no warning option disables this warning so that the second line can be evaluated,
is logged for the ones that are skipped. The leading `-` on the last which also provides a service named `udevd`.
one says it is also fine if none of them are found, in which case the
block is dropped quietly and no service named `udevd` exists.
> [!NOTE] run nowarn if:udevd <pid/udevd> :1 udevadm settle -t 0
> This needs to be one block. The title is the service identity, so
> two blocks titled `udevd` in the same file are two declarations of
> one service, which Finit rejects. See [Duplicate
> titles](service-opts.md#duplicate-titles).
run udevadm:1 { This line is only loaded if we know of a service named `udevd`. Again,
runlevel = "S"
if = "udevd"
conditions = { "pid/udevd" }
command = "-udevadm settle -t 0"
}
This block is only loaded if we know of a service named `udevd`. Again,
we do not warn if `udevadm` is not found, execution will also stop here we do not warn if `udevadm` is not found, execution will also stop here
until the PID condition is asserted, i.e., Finit detecting udevd has until the PID condition is asserted, i.e., Finit detecting udevd has
started. started.
run mdev { run nowarn conflict:udevd [S] mdev -s -- Populating device tree
description = "Populating device tree"
runlevel = "S"
conflicts = { "udevd" }
command = "-mdev -s"
}
If `udevd` is not available, we try to run `mdev`, but if that is not If `udevd` is not available, we try to run `mdev`, but if that is not
found, again we do not warn. found, again we do not warn.
Conditional loading can also be negated, so the previous block can be Conditional loading statements can also be negated, so the previous stanza
written as: can also be written as:
run mdev { run nowarn if:!udevd [S] mdev -s -- Populating device tree
description = "Populating device tree"
runlevel = "S"
if = "!udevd"
command = "-mdev -s"
}
The reason for using `conflicts` in this example is that a conflict can The reason for using `conflict` in this example is that a conflict can be
be resolved. Blocks naming a conflict are rechecked at runtime. resolved. Stanzas marked with `conflict:foo` are rechecked at runtime.
Conditional Execution Conditional Execution
--------------------- ---------------------
Similar to conditional loading of blocks there is conditional runtime Similar to conditional loading of stanzas there is conditional runtime
execution. This can be confusing at first, since Finit already has a execution. This can be confusing at first, since Finit already has a
condition subsystem, but this is more akin to the qualification to a condition subsystem, but this is more akin to the qualification to a
runlevel. E.g., a task with `runlevel = "123"` is qualified to run runlevel. E.g., a `task [123]` is qualified to run only in runlevel 1,
only in runlevel 1, 2, and 3. It is not considered for other 2, and 3. It is not considered for other runlevels.
runlevels.
Conditional execution qualify a run/task/service based on a condition. Conditional execution qualify a run/task/service based on a condition.
Consider this (simplified) example from the Infix operating system: Consider this (simplified) example from the Infix operating system:
run startup { run [S] name:startup <pid/sysrepo> confd -b --load startup-config
runlevel = "S" run [S] if:<usr/fail-startup> name:failure <pid/sysrepo> confd --load failure-config
conditions = { "pid/sysrepo" }
command = "confd -b --load startup-config"
}
run failure { The two run statements reside in the same .conf file so Finit runs them
runlevel = "S" in true sequence. If loading the file `startup-config` fails confd sets
if = "usr/fail-startup" the condition `usr/fail-startup`, thus allowing the next run statement
conditions = { "pid/sysrepo" } to load `failure-config`.
command = "confd --load failure-config"
}
The two run blocks reside in the same .conf file so Finit runs them in Notice the critical difference between the `<pid/sysrepo>` condition and
true sequence. If loading the file `startup-config` fails confd sets `if:<usr/fail-startup>`. The former is a condition for starting and the
the condition `usr/fail-startup`, thus allowing the next one to load latter is a condition to check if a run/task/service is qualified to
`failure-config`. even be considered.
Notice the critical difference between the `conditions` list and `if`. Conditional execution statements can also be negated, so provided the
The former is a condition for starting; the latter is a condition to file loaded did the opposite, i.e., set a condition on success, the
check whether a run/task/service is qualified to even be considered. previous stanza can also be written as:
`if` has a negation of its own, `!`, which is unrelated to anything in
the `conditions` list.
What `if` compares against depends on the value. A namespace run [S] if:<!usr/startup-ok> name:failure <pid/sysrepo> confd ...
separator makes it a condition, anything else is a service name:
| `if` | Asks |
|---|---|
| `"udevd"` | is a service by this name known? |
| `"usr/foo"` | was this condition set? |
Both are questions about whether the block belongs in the running
configuration at all, usually answered from what bootstrap established.
A statement is all of one kind or the other, so the block is rejected
if you mix them.
> [!NOTE]
> `if` qualifies, it does not track. A condition asserted or cleared
> later does not start or stop the service by itself -- that is what
> the `conditions` list is for.
Conditional execution can also be negated, so provided the file loaded
did the opposite, i.e., set a condition on success, the previous block
can be written as:
run failure {
runlevel = "S"
if = "!usr/startup-ok"
conditions = { "pid/sysrepo" }
command = "confd ..."
}
Variants of one service are often qualified this way, one per platform,
and they usually have to supply the same barrier to whatever waits for
them. Each variant needs its own title, since a title is an identity,
and the shared barrier is named with `provides`. See [Provided
Conditions](service-opts.md#provided-conditions).
+17 -26
View File
@@ -9,45 +9,42 @@ from a serialized boot process.
SysV Init Scripts SysV Init Scripts
----------------- -----------------
**Syntax:** `sysv NAME { command = "/path/to/init-script" }` **Syntax:** `sysv [LVLS] <COND> /path/to/init-script -- Optional description`
> [!NOTE] > `<COND>` is described in the [Services](services.md) section.
> Conditions, runlevels, and the other settings a `sysv` block takes
> are described in [Service Options](service-opts.md).
A `sysv` block is a supervised daemon, like `service`, but started and Similar to `task` is the `sysv` stanza, which can be used to call SysV
stopped through a SysV style init script instead of a command line. The style scripts. The primary intention for this command is to be able to
intention is to reuse existing setup and init scripts from Linux reuse much of existing setup and init scripts in Linux distributions.
distributions.
When entering an allowed runlevel, Finit calls `init-script start`, when When entering an allowed runlevel, Finit calls `init-script start`, when
entering a disallowed runlevel, Finit calls `init-script stop`, and if entering a disallowed runlevel, Finit calls `init-script stop`, and if
the Finit .conf, where the `sysv` block is declared, is modified, Finit the Finit .conf, where `sysv` stanza is declared, is modified, Finit
calls `init-script restart` on `initctl reload`. Similar to how calls `init-script restart` on `initctl reload`. Similar to how
`service` blocks work. `service` stanzas work.
Forking services started with `sysv` scripts can be monitored by Finit Forking services started with `sysv` scripts can be monitored by Finit
by declaring the PID file to look for: `pidfile = "/path/to/file.pid"`. by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
Finit does not create that file, it watches it for the resulting Notice the leading `!`, it signifies Finit should not try to create the
forked-off PID. That is the default; `pidfile-create = true` asks Finit file, but rather watch that file for the resulting forked-off PID. This
to write it instead. The same applies to forking daemons with no way to syntax also works for forking daemons that do not have a command line
run in the foreground, see [Services](services.md). option to run it in the foreground, more on this below in `service`.
> [!TIP] > [!TIP]
> See also [SysV Init Compatibility](#sysv-init-compatibility). > See also [SysV Init Compatibility](#sysv-init-compatibility).
Run-parts `runparts DIRECTORY`
--------- --------------------
For a directory with traditional start/stop scripts that should run, in For a directory with traditional start/stop scripts that should run, in
order, at bootstrap, Finit provides the `runparts` setting. It runs order, at bootstrap, Finit provides the `runparts` directive. It runs
in runlevel S, at the very end of it (before calling `/etc/rc.local`) in runlevel S, at the very end of it (before calling `/etc/rc.local`)
making it perfect for most scenarios. making it perfect for most scenarios.
For syntax details, see the [Run-parts Scripts](runparts.md) section. For syntax details, see the [Run-parts Scripts](runparts.md) section.
Here is an example take from a Debian installation: Here is an example take from a Debian installation:
runparts = "/etc/rc2.d" runparts /etc/rc2.d
Files in these directories are usually named `SNNfoo` and `KNNfoo`, Files in these directories are usually named `SNNfoo` and `KNNfoo`,
which Finit knows about and automatically appends the correct argument: which Finit knows about and automatically appends the correct argument:
@@ -67,12 +64,7 @@ Start/Stop Scripts
For syntax details, see [SysV Init Scripts](#sysv-init-scripts), above. For syntax details, see [SysV Init Scripts](#sysv-init-scripts), above.
Here follows an example taken from a Debian installation: Here follows an example taken from a Debian installation:
sysv inetd { sysv [2345] <pid/syslogd> /etc/init.d/openbsd-inetd -- OpenBSD inet daemon
description = "OpenBSD inet daemon"
runlevel = "2345"
conditions = { "pid/syslogd" }
command = "/etc/init.d/openbsd-inetd"
}
The init script header could be parsed to extract `Default-Start:` and The init script header could be parsed to extract `Default-Start:` and
other parameters for the `sysv` command to Finit. There is currently no other parameters for the `sysv` command to Finit. There is currently no
@@ -91,7 +83,6 @@ it exists, and is executable. It is called very late in the boot process
when the system has left runlevel S, stopped all old and started all new when the system has left runlevel S, stopped all old and started all new
services in the target runlevel (default 2). services in the target runlevel (default 2).
> [!NOTE]
> In Finit releases before v4.5 this script blocked Finit execution and > In Finit releases before v4.5 this script blocked Finit execution and
> made it as good as impossible to call `initctl` during that time. > made it as good as impossible to call `initctl` during that time.
+5 -60
View File
@@ -1,10 +1,9 @@
run (sequence) run (sequence)
-------------- --------------
**Syntax:** `run NAME { command = "/path/to/cmd ARGS" }` **Syntax:** `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
> Conditions, runlevels, and the other settings a `run` block takes are > `<COND>` is described in the [Services](services.md) section.
> described in [Service Options](service-opts.md).
One-shot command to run in sequence when entering a runlevel, with One-shot command to run in sequence when entering a runlevel, with
optional arguments and description. `run` commands are guaranteed to be optional arguments and description. `run` commands are guaranteed to be
@@ -32,70 +31,16 @@ also the `--quiet` and `--batch` options.
task (parallel) task (parallel)
--------------- ---------------
**Syntax:** `task NAME { command = "/path/to/cmd ARGS" }` **Syntax:** `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
> A `task` block takes the same settings as `run`, see > `<COND>` is described in the [Services](services.md) section.
> [Service Options](service-opts.md).
One-shot like 'run', but starts in parallel with the next command. One-shot like 'run', but starts in parallel with the next command.
Both `run` and `task` commands are run in a shell, so basic pipes and Both `run` and `task` commands are run in a shell, so basic pipes and
redirects can be used: redirects can be used:
task bar { task [s] echo "foo" | cat >/tmp/bar
runlevel = "S"
command = "echo \"foo\" | cat >/tmp/bar"
}
Please note, `;`, `&&`, `||`, and similar are *not supported*. Any Please note, `;`, `&&`, `||`, and similar are *not supported*. Any
non-trivial constructs are better placed in a separate shell script. non-trivial constructs are better placed in a separate shell script.
remain-after-exit
-----------------
By default, a `run` or `task` will re-run each time its runlevel is
entered, and its `exec-stop-post` script does not run on completion.
With `remain-after-exit`, the task runs once and does not re-run on
runlevel re-entry:
task firewall {
description = "Firewall setup"
runlevel = "2345"
remain-after-exit = true
command = "/usr/sbin/setup-firewall"
}
This has the following effects:
* The task does not re-run on runlevel re-entry
* The `exec-stop-post` script runs when:
- The task is explicitly stopped (`initctl stop NAME`)
- The task leaves its valid runlevels (e.g., runlevel change)
This is useful for tasks that set up persistent state where:
* Cleanup should only happen on explicit stop or when leaving valid runlevels
* The setup should not be re-run on every runlevel entry
**Example:** Setting up firewall rules with cleanup on shutdown:
```
task firewall {
description = "Firewall setup"
runlevel = "2345"
remain-after-exit = true
exec-stop-post = "/usr/sbin/teardown-firewall"
command = "/usr/sbin/setup-firewall"
}
```
The firewall rules are created once. The `exec-stop-post` script runs when
entering runlevel 0 (halt) or 6 (reboot), or on explicit stop.
> [!NOTE]
> The `remain-after-exit` setting is not supported for bootstrap-only
> tasks (tasks with only runlevel S). Bootstrap tasks are deleted
> immediately after completion, and their `exec-stop-post` scripts never
> run. A warning is logged if `remain-after-exit` is used on such tasks.
+4 -8
View File
@@ -5,20 +5,16 @@ Finit comes with rudimentary support for templating, similar to that of
systemd. Best illustrated with an example: systemd. Best illustrated with an example:
$ initctl show avahi-autoipd@ $ initctl show avahi-autoipd@
service avahi-autoipd:%i { service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
description = "ZeroConf for %i"
command = "avahi-autoipd --syslog %i"
}
To enable ZeroConf for, e.g., `eth0`, use To enable ZeroConf for, e.g., `eth0`, use
$ initctl enable avahi-autoipd@eth0.conf $ initctl enable avahi-autoipd@eth0.conf
The enabled symlink will be set up to `avahi-autoipd@.conf` and every The enabled symlink will be set up to `avahi-autoipd@.conf` and every
instance of `%i` will be replaced with `eth0` before the file is instance of `%i` will in the instantiated directive be replaced with
parsed, so it works in the block title, in any value, and in the `eth0`. Inspect the resulting instantiated template with `initctl show
command line alike. Inspect the result with `initctl show avahi-autoipd:eth0` and check the status of a running instance with:
avahi-autoipd:eth0`, and check a running instance with:
``` ```
$ initctl status avahi-autoipd:eth0 $ initctl status avahi-autoipd:eth0
+34 -115
View File
@@ -1,99 +1,67 @@
TTYs and Consoles TTYs and Consoles
================= =================
**Syntax:** `tty NAME { device = DEV }` -- built-in getty **Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty NAME { command = "CMD ARGS" }` -- external getty `tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
`tty NAME { notty = true }`, or `{ rescue = true }` -- bare shell, no device `tty [LVLS] <COND> [notty] [rescue]`
The block title NAME is what `initctl` shows. The three variants differ The first variant of this option uses the built-in getty on the given
in what they open: `device` runs the built-in getty on that TTY, and DEV TTY device DEV, in the given runlevels. DEV may be the special keyword
may be the special keyword `@console`, expanded from `@console`, which is expanded from `/sys/class/tty/console/active`,
`/sys/class/tty/console/active`, useful on embedded systems. `command` useful on embedded systems.
hands the TTY to an external getty. `notty` opens nothing at all.
`command` may name several candidates, `{ "agetty ...", "getty ..." }`, The default baud rate is 0, i.e., keep kernel default.
and Finit runs the first one it finds. See [Command
Candidates](service-opts.md#command-candidates).
Settings common to all three: > The `tty` stanza inherits runlevel, condition (and other feature)
> parsing from the `service` stanza. So TTYs can run in one or many
| Setting | Alias | Description |
|---|---|---|
| `runlevel` | | Runlevels to run in, e.g. `"12345"` |
| `conditions` | `cond` | Conditions to wait for |
| `noclear` | | Do not clear the TTY after each session |
| `nowait` | | Do not wait for Enter before the login prompt |
| `nologin` | | Skip login, give a shell straight away |
| `passenv` | | Pass the environment on to `login`, built-in getty only |
The `device` variant takes two more:
| Setting | Description |
|---|---|
| `baud` | Baud rate, default 0, i.e., keep kernel default |
| `term` | `$TERM` value, e.g. `"vt220"` |
> A `tty` block inherits runlevel, condition (and other feature)
> parsing from the `service` block. So TTYs can run in one or many
> runlevels and depend on any condition supported by Finit. This is > runlevels and depend on any condition supported by Finit. This is
> useful e.g. to depend on `<pid/elogind>` before starting a TTY. > useful e.g. to depend on `<pid/elogind>` before starting a TTY.
**Example:** **Example:**
tty ttyAMA0 { tty [12345] /dev/ttyAMA0 115200 noclear vt220
runlevel = "12345"
device = "/dev/ttyAMA0"
baud = 115200
term = "vt220"
noclear = true
}
The second `tty` syntax variant is for using an external getty, like The second `tty` syntax variant is for using an external getty, like
agetty or the BusyBox getty. agetty or the BusyBox getty.
The third variant is for board bringup and the `rescue` boot mode. No The third variant is for board bringup and the `rescue` boot mode. No
device node is required, the same output the kernel uses is reused for device node is required in this variant, the same output that the kernel
stdio. With `notty` a shell is started (`nologin`, `noclear`, and uses is reused for stdio. If the `rescue` option is omitted, a shell is
`nowait` are implied); with `rescue` the bundled started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
`/libexec/finit/sulogin` presents a bare-bones root login prompt. If option is set the bundled `/libexec/finit/sulogin` is started to present
the root (uid:0, gid:0) user does not have a password set, no rescue is a bare-bones root login prompt. If the root (uid:0, gid:0) user does
possible. For more information, see the [Rescue Mode](rescue.md) not have a password set, no rescue is possible. For more information,
section. see the [Rescue Mode](rescue.md) section.
By default, the first two syntax variants *clear* the TTY and *wait* for By default, the first two syntax variants *clear* the TTY and *wait* for
the user to press enter before starting getty. the user to press enter before starting getty.
**Example:** **Example:**
tty getty { tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
runlevel = "12345" tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
command = "/sbin/getty -L 115200 /dev/ttyAMA0 vt100"
}
tty agetty { The `noclear` option disables clearing the TTY after each session.
runlevel = "12345"
command = "/sbin/agetty -L ttyAMA0 115200 vt100"
nowait = true
}
The `noclear` setting disables clearing the TTY after each session.
Clearing the TTY when a user logs out is usually preferable. Clearing the TTY when a user logs out is usually preferable.
The `nowait` setting disables the `press Enter to activate console` The `nowait` option disables the `press Enter to activate console`
message before actually starting the getty program. On small and message before actually starting the getty program. On small and
embedded systems running multiple unused getty wastes both memory embedded systems running multiple unused getty wastes both memory
and CPU cycles, so `wait` is the preferred default. and CPU cycles, so `wait` is the preferred default.
The `nologin` setting disables getty and `/bin/login`, and gives the The `nologin` option disables getty and `/bin/login`, and gives the
user a root (login) shell on the given TTY immediately. user a root (login) shell on the given TTY `<DEV>` immediately.
Needless to say, this is a rather insecure option, but can be very Needless to say, this is a rather insecure option, but can be very
useful for developer builds, during board bringup, or similar. useful for developer builds, during board bringup, or similar.
Notice the ordering, the `TERM` option to the built-in getty must be
the last argument.
Embedded systems may want to enable automatic `DEV` by supplying the Embedded systems may want to enable automatic `DEV` by supplying the
special `@console` device. This works regardless whether the system special `@console` device. This works regardless weather the system
uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
it out by querying sysfs: `/sys/class/tty/console/active`. Leave it out by querying sysfs: `/sys/class/tty/console/active`. The speed
`baud` out to keep the kernel default. can be omitted to keep the kernel default.
> Most systems get by fine by just using `console`, which will evaluate > Most systems get by fine by just using `console`, which will evaluate
> to `/dev/console`. If you have to use `@console` to get any output, > to `/dev/console`. If you have to use `@console` to get any output,
@@ -101,67 +69,18 @@ it out by querying sysfs: `/sys/class/tty/console/active`. Leave
**Example:** **Example:**
tty console { tty [12345] @console noclear vt220
runlevel = "12345"
device = "@console"
term = "vt220"
noclear = true
}
On really bare bones systems, or for board bringup, Finit can give you a On really bare bones systems, or for board bringup, Finit can give you a
shell prompt as soon as bootstrap is done, without opening any device shell prompt as soon as bootstrap is done, without opening any device
node: node:
tty board { tty [12345789] notty
runlevel = "12345789"
notty = true
}
This should of course not be enabled on production systems. Because it This should of course not be enabled on production systems. Because it
may give a user root access without having to log in. However, for may give a user root access without having to log in. However, for
board bringup and system debugging it can come in handy. board bringup and system debugging it can come in handy.
One can also use a `service` block to start a stand-alone shell: One can also use the `service` stanza to start a stand-alone shell:
service shell { service [12345] /bin/sh -l
runlevel = "12345"
command = "/bin/sh -l"
}
Controlling TTY for Services
----------------------------
The `tty` setting gives a `run`, `task`, or `service` a controlling
terminal on the given device. The device is opened, set as the
controlling terminal for the session (after `setsid()`), and connected to
the process's stdin, stdout, and stderr. A default `TERM` environment
variable is set based on the device type: `vt102` for serial lines and
`linux` for virtual terminals.
The value may be a device node like `/dev/ttyS0`, or the special
keyword `@console` (see above). Note that `@console` expands only to
the first console, not all.
When `tty` is combined with a `log` block, stdout and stderr are
redirected to the log sink instead of the TTY, but stdin remains
connected to the TTY device.
> The `tty` setting is for `run`, `task`, and `service` blocks only.
> A `tty` block (for getty/login) is a different thing entirely, see
> above.
**Example:**
service foo {
description = "Foo on serial console"
runlevel = "2345"
tty = "/dev/ttyS0"
command = "/usr/sbin/foo"
}
task setup {
description = "Board bringup on console"
runlevel = "S"
tty = "@console"
command = "my-setup-script"
}
-375
View File
@@ -1,375 +0,0 @@
D-Bus Integration
=================
Finit ships with a built-in, brokerless [D-Bus][] implementation, **libink**
(`-link`), that exposes the running init system as a peer on its own private
bus, and optionally on the system bus when `dbus-daemon` is available.
Everything `initctl` does is also reachable from any generic D-Bus tooling —
`dbus-send`, `dbus-monitor`, `gdbus`, language bindings, dashboards,
monitoring agents, etc.
> [!NOTE]
> D-Bus support is enabled by default, opt out at build time with
> `--disable-dbus`, see [Building](build.md) for details. When disabled,
> `initctl` keeps using the legacy `INIT_SOCKET` transport and Finit
> exposes no bus.
Bus address
-----------
| Bus | Address |
|------------------------|---------------------------------------------|
| Local (always) | `unix:path=/run/finit/bus` |
| System (opportunistic) | `unix:path=/var/run/dbus/system_bus_socket` |
The bundled device manager serves its own `org.finit.Device1` the same
way on `unix:path=/run/keventd/bus`, see [keventd](keventd.md); one
socket per daemon, no forwarding between them.
The **local** bus is brokerless: clients connect straight to Finit over a
Unix-domain socket using the standard D-Bus SASL EXTERNAL handshake. No
`dbus-daemon` is required, which makes it suitable for embedded systems that
don't ship one.
The socket is `0660`, owned by `root` and the group given to
`--with-group` at build time, the same gate as `/run/finit/socket` that
`initctl` falls back on. The bus reaches every operation `initctl`
does, so restricting one and not the other would leave the door open.
Members of that group may use it, see [Authorization](#authorization).
Finit accepts at most 64 concurrent local-bus peers; further
connections are refused until one disconnects. Match rules given to
`AddMatch` support the `type`, `interface`, `member`, and `path` keys;
`arg0` and its variants are not implemented.
For the system bus a standard busconfig policy ships in
`dbus-1/org.finit.conf`, installed to `$datadir/dbus-1/system.d`: only
root may own `org.finit`, unprivileged callers reach introspection,
properties, and the read-only methods.
Finit also enforces per-method authorization itself, so a permissive
policy installed by mistake does not open state-changing methods.
The **system** bus is best-effort: Finit probes for a running `dbus-daemon`
and, when reachable, claims the well-known name `org.finit` so that standard
tooling sees Finit just like any other system service:
```sh
dbus-send --system --print-reply --dest=org.finit /org/finit/manager \
org.finit.Manager1.ListServices
dbus-monitor --system "sender='org.finit'"
```
If no system bus is present (the common case on embedded targets), this step
is silently skipped.
Object tree
-----------
```
/
├── org/
│ └── finit/
│ ├── manager Manager1
│ ├── cond Cond1
│ └── service/
│ ├── keventd Service1 (one per service)
│ ├── sshd
│ └── …
└── org/freedesktop/DBus Standard well-known interfaces
```
Every node implements the usual stock interfaces:
| Interface | Purpose |
|---------------------------------------|-------------------------------------------|
| `org.freedesktop.DBus` | `Hello`, `AddMatch`, `RemoveMatch` — on `/org/freedesktop/DBus` only |
| `org.freedesktop.DBus.Peer` | `Ping`, `GetMachineId` |
| `org.freedesktop.DBus.Introspectable` | `Introspect()` — XML description |
| `org.freedesktop.DBus.Properties` | `Get`, `GetAll`; nothing is writable |
Note: `Hello`, `AddMatch`, and `RemoveMatch` are answered on the canonical
`/org/freedesktop/DBus` object only, as per the D-Bus specification.
`org.finit.Manager1`
--------------------
Lives at **`/org/finit/manager`**. Owns the global init operations
and the service registry.
### Methods
| Method | In sig | Out sig | Priv. | Notes |
|------------------------------|--------|---------|-------|-----------------------------------------------------------|
| `ListServices` | — | `as` | no | Returns identities (`name`, `name:id`) of loaded services |
| `GetService` | `s` | `o` | no | Resolves an identity to its `Service1` object path |
| `Start` | `s` | — | yes | Start the service(s) matching the identity |
| `Stop` | `s` | — | yes | Stop the service(s) matching the identity |
| `Restart` | `s` | — | yes | Restart (stop + start) the service(s) |
| `Reload` | — | — | yes | Re-read all `*.conf` and apply changes |
| `SetRunlevel` | `u` | — | yes | Transition to runlevel `u` (0–9) |
| `SetDebug` | — | — | yes | Toggle Finit's runtime debug flag |
| `Signal` | `su` | — | yes | Send signal `u` (1–31) to services matching identity `s` |
| `Suspend` | — | — | yes | `sync()` + suspend-to-RAM |
| `Reboot`, `Halt`, `Poweroff` | `u` | — | yes | Trigger the corresponding shutdown sequence; the argument arms the emergency fallback timer (seconds, 0 = none) |
Edge cases follow the legacy `initctl` protocol: `Suspend` and the
reboot family reply `WrongRunlevel` during bootstrap and shutdown,
`SetRunlevel` is a warning + no-op in runlevel 0/6 and records the
level for the end of bootstrap in runlevel S, and `Signal` fails for
stopped services. One deliberate divergence: a `SetRunlevel` argument
outside 0–9 returns `InvalidArgs`, where the legacy protocol
acknowledges silently.
### Properties
All read-only strings; observable via `Properties.Get` and
`Properties.GetAll`.
| Property | Type | Returns |
|----------------|------|--------------------------------------------------------|
| `Runlevel` | `s` | Current runlevel as a digit (`"2"`, `"3"`, …) or `"S"` |
| `PrevRunlevel` | `s` | Previous runlevel, same encoding, `"N"` when none |
| `Version` | `s` | Finit's version string (`PACKAGE_VERSION`) |
### Signals
| Signal | Body | Fires when |
|-----------------------|----------------------------------------|------------------------|
| `ServiceStateChanged` | `sss` — identity, old state, new state | Service transitions |
| `RunlevelChanged` | `ss` — old level, new level | System runlevel change |
| `ConfigReloaded` | — | `initctl reload` completed; providers of generation-file conditions re-assert |
State names emitted by `ServiceStateChanged` are stable wire strings:
`halted`, `done`, `dead`, `cleanup`, `teardown`, `stopping`, `setup`,
`paused`, `waiting`, `starting`, `running`. `RunlevelChanged` levels
use the same encoding as the `Runlevel` property: digits, `"S"`, `"N"`.
`org.finit.Service1` (per-service objects)
------------------------------------------
Lives at **`/org/finit/service/<encoded>`**, one object per loaded service.
`<encoded>` is the service identity (name, or `name:id` for templated
services) put through systemd-style `_HH` hex escaping — ASCII alphanumerics
and `_` pass through, anything else becomes `_HH` where `HH` is the hex byte.
Use `Manager1.GetService(identity)` to look up the exact path rather than
constructing it by hand.
| Method | In sig | Out sig | Priv. | Notes |
|-----------|--------|---------|-------|-------------------------------------------------------------|
| `Start` | — | — | yes | Equivalent to `Manager1.Start(<identity>)` for this service |
| `Stop` | — | — | yes | … |
| `Restart` | — | — | yes | … |
| `Reload` | — | — | yes | Send the service's `reload-signal`, default `SIGHUP`; restart when set to `"none"` |
### Properties
All read-only; observable via `Properties.Get` and `Properties.GetAll`.
| Property | Type | Returns |
|-----------------|------|----------------------------------------------------------------|
| `Identity` | `s` | Service identity, `name` or `name:id` |
| `Name` | `s` | Program name (basename of the command) |
| `State` | `s` | Current status, same vocabulary as `initctl status` |
| `Pid` | `u` | Current PID, 0 when not running |
| `RestartCount` | `u` | Restarts since the last stable run |
| `Runlevels` | `u` | Allowed runlevels as a bitmask, bit N = runlevel N, bit 10 = S |
| `Description` | `s` | The service's `description` string |
| `Command` | `s` | Full command line, arguments included |
| `Conditions` | `s` | Declared conditions, raw `.conf` form |
| `Type` | `s` | Unit type: `service`, `task`, `run`, `sysv`, `tty`, `free` |
| `Origin` | `s` | Source `.conf` file, empty for built-ins |
| `Environment` | `s` | The service's `env` setting, raw |
| `PidFile` | `s` | Declared PID file, raw (`!` prefix included) |
| `User` | `s` | User the service runs as |
| `Group` | `s` | Group the service runs as |
| `Uptime` | `u` | Seconds since start, 0 when not running |
| `ExitStatus` | `u` | Raw `waitpid(2)` status from the last exit |
| `RestartsTotal` | `u` | Restarts over the service's lifetime |
| `RestartMax` | `u` | Restart limit before the service is blocked |
| `Starts` | `u` | Times started, for `manual-start` units |
| `ManualStart` | `b` | `manual-start` set in the `.conf` |
| `Forking` | `b` | Daemon forks to background |
| `Started` | `b` | Run/task completed successfully |
On every state transition the object also emits the standard
`org.freedesktop.DBus.Properties.PropertiesChanged` signal: `State`
in the changed dictionary, `Pid` and `RestartCount` invalidated (call
`Get` for fresh values).
The per-service surface lets generic tooling supply an object handle
once and then invoke methods on it, instead of repeatedly passing the
identity string.
`org.finit.Cond1`
-----------------
Lives at **`/org/finit/cond`**. Exposes Finit's
[condition system](conditions.md) to bus clients.
### Methods
| Method | In sig | Out sig | Priv. | Notes |
|---------|--------|---------|-------|--------------------------------------------------------------|
| `Get` | `s` | `s` | no | Returns `"on"`, `"off"`, or `"flux"` for the named condition |
| `Set` | `s` | — | yes | Assert a `usr/<name>` condition |
| `Clear` | `s` | — | yes | Deassert a `usr/<name>` condition |
| `List` | — | `as` | no | Names of all known conditions |
| `Dump` | — | `a(ss)` | no | `(name, state)` pairs for everything `List` returns |
Note: non-`usr/*` paths are rejected with `InvalidArgs` -- system
conditions belong to Finit's state machine.
A service consuming such a condition:
service alarm {
description = "Foo alarm"
runlevel = "2345"
conditions = { "usr/data-ready" }
command = "alarm --arg foo"
}
asserted from any bus client with `Cond1.Set string:"data-ready"`, see
the examples below.
### Signals
| Signal | Body | Fires when |
|--------------------|------------------------|---------------------------------------|
| `ConditionChanged` | `ss` — name, new state | A condition is asserted or deasserted |
Authorization
-------------
Privileged methods accept `root`, and any caller belonging to the group given
to `--with-group` at build time. That is the same set the socket mode already
admits, so the two gates agree instead of the socket letting the group in and
every method turning it away.
On the **local** bus the kernel settles this at `connect()`: `SO_PEERCRED`
gives Finit the caller's uid and `SO_PEERGROUPS` its group set, both straight
from the kernel. Finit matches the group against `--with-group` itself, so
the check never touches NSS -- `getpwuid`/`getgrouplist` can block on a slow
LDAP or SSSD backend, and PID 1 must never block. Because the group set is
the caller's real credentials, privilege escalation through the bus is
impossible.
On the **system** bus one connection carries every caller, so `SO_PEERCRED`
describes `dbus-daemon` rather than whoever asked. Finit asks the bus driver
`GetConnectionUnixUser` about the message sender and holds the call until the
answer arrives -- nothing blocks, the reply comes back through the same event
loop as everything else. The broker does not report the caller's groups in
that reply, so **privileged methods over the system bus are root-only**; group
membership is honoured on the local bus only. Lifting that is future work,
see `libink/README.md`.
Answers are cached per sender. A bus never reuses a unique name while it
runs, so an answer holds for as long as that bus does; Finit empties the cache
when the broker goes away, since a new one numbers its clients from scratch.
A caller Finit cannot identify is refused, so the failure mode is a denial
rather than an escalation.
When a privileged method is rejected the error name is exactly
`org.freedesktop.DBus.Error.AccessDenied`, and the body carries a short reason
string.
`initctl` integration
---------------------
`initctl` transparently routes through D-Bus when the bus socket is present,
and falls back to the legacy `INIT_SOCKET` transport otherwise. Concretely,
the following subcommands use the bus first:
| Subcommand | Method |
|----------------------------|--------------------------------------------------|
| `initctl start` | `Manager1.Start(svc)` |
| `initctl stop` | `Manager1.Stop(svc)` |
| `initctl restart` | `Manager1.Restart(svc)` |
| `initctl reload` | `Manager1.Reload()` |
| `initctl reload S` | `Service1.Reload()` (per-svc) |
| `initctl reboot` | `Manager1.Reboot(timeout)` |
| `initctl halt` | `Manager1.Halt(timeout)` |
| `initctl poweroff` | `Manager1.Poweroff(timeout)` |
| `initctl suspend` | `Manager1.Suspend()` |
| `initctl debug` | `Manager1.SetDebug()` |
| `initctl signal` (or `kill`) | `Manager1.Signal(svc, signo)` |
| `initctl runlevel` | `Properties.Get(Manager1.Runlevel/PrevRunlevel)` |
| `initctl cond set/get/clear` | `Cond1.{Set,Get,Clear}`, `clr` is an alias |
Two `initctl` subcommands are pure D-Bus features without legacy equivalents:
* `initctl monitor` — subscribes to every signal on the local bus and prints
one line per delivery (with timestamp, interface and member). Same idea as
`dbus-monitor`, but scoped to Finit and with no need to pass `--address`.
* `initctl cond` emits the standard `Cond1.ConditionChanged` signal as a side
effect, so subscribers observe user-driven state changes the same way they
observe service-driven ones.
Examples
--------
The examples below use `dbus-send` and `dbus-monitor`, which ship as part of
the [dbus][] reference implementation; they're widely packaged and don't pull
in any extra runtime. Any tool that speaks D-Bus over an AF_UNIX socket works
equally well — `gdbus`, Python's `jeepney`/`dasbus`, etc. — substitute their
syntax for setting the bus address. The wire protocol is the compatibility
surface: Finit's own **libink** is an internal implementation detail, external
clients should use any standard D-Bus library.
When `org.finit` is registered on the system bus you can replace
`--address=unix:path=/run/finit/bus` with `--system` in any example below.
List the running services:
```sh
dbus-send --address=unix:path=/run/finit/bus \
--type=method_call --print-reply --dest=org.finit \
/org/finit/manager \
org.finit.Manager1.ListServices
```
Read the current runlevel via the Properties interface:
```sh
dbus-send --address=unix:path=/run/finit/bus \
--type=method_call --print-reply --dest=org.finit \
/org/finit/manager \
org.freedesktop.DBus.Properties.Get \
string:org.finit.Manager1 string:Runlevel
```
Subscribe to every state change on the manager object:
```sh
dbus-monitor --address=unix:path=/run/finit/bus \
"type='signal',interface='org.finit.Manager1'"
```
Or use `initctl monitor`, which does the same without any address
plumbing.
Restart a service by its object path:
```sh
dbus-send --address=unix:path=/run/finit/bus \
--type=method_call --dest=org.finit \
/org/finit/service/sshd \
org.finit.Service1.Restart
```
Trigger a `usr/`-condition assertion that wakes any dependent service:
```sh
dbus-send --address=unix:path=/run/finit/bus \
--type=method_call --dest=org.finit \
/org/finit/cond \
org.finit.Cond1.Set string:"data-ready"
```
The `--dest=org.finit` argument is informational on the local
brokerless bus — Finit accepts any destination because there's no
broker to route by name — but `dbus-send` requires it syntactically.
[dbus]: https://gitlab.freedesktop.org/dbus/dbus
[D-Bus]: https://dbus.freedesktop.org/doc/dbus-specification.html
+46 -105
View File
@@ -1,139 +1,80 @@
Distro Recommendations Distro Recommendations
====================== ======================
Finit supports two directory layouts for managing service configurations: By default Finit uses the following directories for configuration files:
1. **Simple layout** - all configuration files directly in `/etc/finit.d/`
2. **Distro layout** - separate `available/` and `enabled/` directories
The distro layout is recommended for distributions that need clear
separation between installed and enabled services, and want to use the
`initctl enable/disable` commands for service management.
## Simple Layout
Finit supports using a flat directory structure:
``` ```
/etc/ /etc/
|-- finit.d/ -- All enabled services |-- finit.d/ -- Regular (enabled) services
| |-- lighttpd.conf | |-- lighttpd.conf
| `- *.conf | `- *.conf
|-- finit.conf -- Bootstrap tasks and services |-- finit.conf -- Bootstrap tasks and services
: :
``` ```
In this layout, you enable a service by placing its `.conf` file in To enable a service one simply drops a small configuration file in the
`/etc/finit.d/` and disable it by removing the file. This works well for `/etc/finit.d/` directory. This practice works with systems that
embedded systems or custom setups where services are managed by other tools keep disabled services elsewhere, or generates them as needed from some
or generated dynamically. other tool.
> [!NOTE] No tooling support Distributions, however, may want a clearer separation of enabled and
> The `initctl enable/disable` commands **do not work** with this layout. available (installed but not enabled) services. They may even want to
> You must manually manage files in `/etc/finit.d/`. customize the directories used, for brand labeling or uniformity.
## Distro Layout To that end Finit allows for a sub-directory `/etc/finit.d/available/`
where installed but disabled services can reside. Adding a symlink to a
configuration in this sub-directory enables the service, but it will not
be started.
Distributions typically want clearer separation between available (installed) To change the default configuration directory and configuration file
and enabled services. Finit supports this through the `available/` and names the Finit `configure` script offers the following two options at
`enabled/` subdirectories: build time:
```
/etc/
|-- finit.d/
| |-- available/ -- Installed but disabled services
| | |-- httpd.conf
| | |-- ntpd.conf
| | `-- sshd.conf
| |-- enabled/ -- Enabled services (symlinks)
| | `- sshd.conf -- Symlink to ../available/sshd.conf
|-- finit.conf -- Bootstrap tasks and services
:
```
In this layout:
- **available/** contains all installed service configurations
- **enabled/** contains symlinks to configurations that should start at boot
- Services are enabled/disabled by creating/removing symlinks
> [!IMPORTANT] Recommended
> This is the recommended layout. In fact, the `initctl enable/disable`
> commands **require** this layout with both `available/` and `enabled/`
> directories and will not work without it.
## Managing Services with initctl
When using the distro layout, the `initctl` tool provides convenient commands
for managing service configurations:
```
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf by creating symlink in enabled/
disable <CONF> Disable .conf by removing symlink from enabled/
reload Reload *.conf in /etc/finit.d/ (activate changes)
```
Example usage:
```bash
# Enable sshd (creates enabled/sshd.conf -> ../available/sshd.conf)
initctl enable sshd
# Disable httpd (removes enabled/httpd.conf symlink)
initctl disable httpd
# Apply changes (reload configuration and start/stop services)
initctl reload
```
The `.conf` suffix is optional - `initctl` adds it automatically if missing.
> [!NOTE] Remember `initctl reload`
> Changes made with `enable`/`disable` take effect only after running
> `initctl reload`, unless Finit was built with `--enable-auto-reload`
> which automatically detects and applies configuration changes.
### Service Overrides
The `initctl` tool only operates on symlinks in the `enabled/` directory.
If you place a regular (non-symlink) `.conf` file directly in the parent
directory (e.g., `/etc/finit.d/`), `initctl` will ignore it. This allows
you to create system-level overrides that cannot be accidentally disabled
by package management tools.
## Customizing Directory Paths
Distributions can customize the directory names and locations at build time
using the Finit `configure` script:
```sh ```sh
./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf ./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf
``` ```
This changes the default `/etc/finit.d/` to `/etc/init.d/` and moves the > [!IMPORTANT]
bootstrap configuration file accordingly. > Remember `--prefix` et al as well, the default is likely *not* what
> you want. See the [build docs][1] for details.
> [!IMPORTANT] Remember to set `--prefix` The resulting directory structure is depicted below. Please notice how
> Remember to set `--prefix` and related options appropriately. The default `/etc/finit.conf` now resides in the same sub-directory as a non-symlink
> prefix is `/usr/local`, which is likely not what you want for a system `/etc/init.d/init.conf`:
> init. See the [build documentation][1] for details.
Example with custom paths:
``` ```
/etc/ /etc/
|-- init.d/ |-- init.d/
| |-- available/ -- Installed services | |-- available/ -- Regular (disabled) services
| | |-- httpd.conf | | |-- httpd.conf
| | |-- ntpd.conf | | |-- ntpd.conf
| | `-- sshd.conf | | `-- sshd.conf
| |-- enabled/ -- Enabled services (symlinks) | |-- sshd.conf -- Symlink to available/sshd.conf
| | `- sshd.conf -- Symlink to ../available/sshd.conf
| `- init.conf -- Bootstrap tasks and services | `- init.conf -- Bootstrap tasks and services
: :
``` ```
Notice how both the service directory and bootstrap configuration file now To facilitate the task of managing configurations, be it a service,
use the custom `/etc/init.d/` path specified at build time. task, run, or other stanza, the `initctl` tool has a a few built-in
commands:
```
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
reload Reload *.conf in /etc/finit.d/ (activates changes)
```
To enable a service like `sshd.conf`, above, use
initctl enable sshd
The `.conf` suffix is not needed, `initctl` adds it implicitly if it is
missing. The `disable` command works in a similar fashion.
Note, however, that `initctl` only operates on symlinks and it always
requires the `available/` sub-directory. Any non-symlink in the parent
directory, here `/etc/init.d/`, will be considered a system override by
`initctl`.
[1]: build.md#configure [1]: build.md#configure
+59 -136
View File
@@ -10,194 +10,117 @@ See the [contrib/][contrib] directory on GitHub for examples, or take a
peek at systems using Finit, like [Infix OS][infix] and [myLinux][]. peek at systems using Finit, like [Infix OS][infix] and [myLinux][].
> [!TIP] > [!TIP]
> A block spans as many lines as it needs, so no continuation character is > As of Finit v4.4, `.conf` lines can be broken up using the standard UNIX
> called for. For the full syntax, see the [finit.conf(5)][] manual or the > continuation character (`\`), trailing comments are also supported. The
> [Configuration](config/index.md) section. > latter means you must escape any hashes used in directives and descriptions
> (`\#`). For more on this and examples, see the [finit.conf(5)][] manual or
> the [Configuration](config/index.md) section.
```ApacheConf ```ApacheConf
# Fallback if /etc/hostname is missing # Fallback if /etc/hostname is missing
hostname = "default" host default
# Runlevel to start after bootstrap, 'S', default: 2 # Runlevel to start after bootstrap, 'S', default: 2
#runlevel = 2 #runlevel 2
# Global environment variables, be careful though with variables like # Support for setting global environment variables, using foo=bar syntax
# PATH, SHELL, LOGNAME, etc. # be careful though with variables like PATH, SHELL, LOGNAME, etc.
#environment { #PATH=/usr/bin:/bin:/usr/sbin:/sbin
# 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
log { log size=100k count=4
size = 100k
count = 4
}
# Services to be monitored and respawned as needed # Services to be monitored and respawned as needed
service watchdog { service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon
description = "System watchdog daemon" service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon
runlevel = "S12345" service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
envfile = "-/etc/conf.d/watchdog" service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab)
command = "watchdog $WATCHDOG_OPTS $WATCHDOG_DEV"
}
service syslogd {
description = "System log daemon"
runlevel = "S12345"
envfile = "-/etc/conf.d/syslog"
command = "syslogd -n $SYSLOGD_OPTS"
}
service klogd {
description = "Kernel log daemon"
runlevel = "S12345"
conditions = { "pid/syslogd" }
envfile = "-/etc/conf.d/klogd"
command = "klogd -n $KLOGD_OPTS"
}
service lldpd {
description = "LLDP daemon (IEEE 802.1ab)"
runlevel = "2345"
envfile = "-/etc/conf.d/lldpd"
command = "lldpd -d $LLDPD_OPTS"
}
# The BusyBox ntpd does not use syslog when running in the foreground # The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The # So we use this trick to redirect stdout/stderr to a log file. The
# log file is rotated with the above settings. The condition declares # log file is rotated with the above settings. The condition declares
# a dependency on a system default route (gateway) to be set. ntpd # a dependency on a system default route (gateway) to be set. A single
# does not respect SIGHUP, so Finit restarts it on reload instead. # <!> at the beginning means ntpd does not respect SIGHUP for restart.
service ntpd { service [2345] log:/var/log/ntpd.log <!net/route/default> ntpd -n -l -I eth0 -- NTP daemon
description = "NTP daemon"
runlevel = "2345"
conditions = { "net/route/default" }
reload-signal = "none"
log { file = "/var/log/ntpd.log" }
command = "ntpd -n -l -I eth0"
}
# For multiple instances of the same service, add :ID to the block title. # For multiple instances of the same service, add :ID somewhere between
service merecat:80 { # the service/run/task keyword and the command.
description = "Web server" service :80 [2345] merecat -n -p 80 /var/www -- Web server
runlevel = "2345" service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
command = "merecat -n -p 80 /var/www"
}
service merecat:8080 {
description = "Old web server"
runlevel = "2345"
command = "merecat -n -p 8080 /var/www"
}
# Alternative method instead of below runparts, can also use /etc/rc.local # Alternative method instead of below runparts, can also use /etc/rc.local
#sysv keyboard-setup { #sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap
# description = "Setting up preliminary keymap" #sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
# runlevel = "S" #task [S] /etc/init.d/kbd -- Preparing console
# command = "/etc/init.d/keyboard-setup"
#}
# Hidden from boot progress, using an empty description # Hidden from boot progress, using empty `--` description
#sysv acpid { #sysv [S] /etc/init.d/keyboard-setup --
# description = "" #sysv [S] /etc/init.d/acpid --
# runlevel = "S" #task [S] /etc/init.d/kbd --
# command = "/etc/init.d/acpid"
#}
# Run start scripts from this directory # Run start scripts from this directory
#runparts = "/etc/start.d" # runparts /etc/start.d
# Virtual consoles run BusyBox getty, keep kernel default speed # Virtual consoles run BusyBox getty, keep kernel default speed
tty tty1 { tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear
runlevel = "12345" tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear
command = "/sbin/getty -L 0 /dev/tty1 linux" tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear
nowait = true
noclear = true
}
tty tty2 {
runlevel = "2345"
command = "/sbin/getty -L 0 /dev/tty2 linux"
nowait = true
noclear = true
}
tty tty3 {
runlevel = "2345"
command = "/sbin/getty -L 0 /dev/tty3 linux"
nowait = true
noclear = true
}
# Use built-in getty for serial port and USB serial # Use built-in getty for serial port and USB serial
#tty ttyAMA0 { runlevel = "12345" device = "/dev/ttyAMA0" noclear = true nowait = true } #tty [12345] /dev/ttyAMA0 noclear nowait
#tty ttyUSB0 { runlevel = "12345" device = "/dev/ttyUSB0" noclear = true } #tty [12345] /dev/ttyUSB0 noclear
# Just give me a shell, I need to debug this embedded system! # Just give me a shell, I need to debug this embedded system!
#tty console { runlevel = "12345" device = "@console" noclear = true nologin = true } #tty [12345] console noclear nologin
``` ```
The `service` block, as well as `task`, `run` and others are described in The `service` stanza, as well as `task`, `run` and others are described in
full in the [Services Syntax](config/services.md) section. full in the [Services Syntax](config/services.md) section.
Here's a quick overview of some of the most common components needed to start Here's a quick overview of some of the most common components needed to start
a UNIX daemon: a UNIX daemon:
``` ```
service NAME { <-- Supervised program (daemon) service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Example daemon
description = "Example daemon" <-- Optional description ^ ^ ^ ^ ^ ^ ^ ^
runlevel = "2345" <-- Optional runlevels | | | | | | | `---------- Optional description
conditions = { "net/route/default" } <-- Optional conditions | | | | | | `------------------ Daemon arguments
envfile = "-/etc/default/daemon" <-- Optional env. file | | | | | `------------------------- Path to daemon
log { } <-- Redirect output to log | | | | `---------------------------------------------------- Optional env. file
command = "daemon ARGS" <-- Path to daemon, and its arguments | | | `-------------------------------------------------------- Redirect output to log
} | | `--------------------------------------------------------------- Optional conditions
| `---------------------------------------------------------------------- Optional Runlevels
`------------------------------------------------------------------------------ Supervised program (daemon)
``` ```
Only `command` is required, which makes simple cases short while leaving Some components are optional: runlevel(s), condition(s) and description,
room for more advanced uses: making it easy to create simple start scripts and still possible for more
advanced uses as well:
service sshd { service /usr/sbin/sshd -D
command = "/usr/sbin/sshd -D"
}
Dependencies are handled using [conditions](conditions.md). One of Dependencies are handled using [conditions](conditions.md). One of
the most common conditions is to wait for basic networking to become the most common conditions is to wait for basic networking to become
available: available:
service nginx { service <net/route/default> nginx -- High performance HTTP server
description = "High performance HTTP server"
conditions = { "net/route/default" }
command = "nginx"
}
Here is another example where we instruct Finit to not start BusyBox Here is another example where we instruct Finit to not start BusyBox
`ntpd` until `syslogd` has started properly. Finit waits for `syslogd` `ntpd` until `syslogd` has started properly. Finit waits for `syslogd`
to create its PID file, by default `/var/run/syslogd.pid`. to create its PID file, by default `/var/run/syslogd.pid`.
service ntpd { service [2345] log <!pid/syslogd> ntpd -n -N -p pool.ntp.org
runlevel = "2345" service [S12345] syslogd -n -- Syslog daemon
conditions = { "pid/syslogd" }
reload-signal = "none"
log { }
command = "ntpd -n -N -p pool.ntp.org"
}
service syslogd { Notice the `log` keyword, BusyBox `ntpd` uses `stderr` for logging when
description = "Syslog daemon" run in the foreground. With `log` Finit redirects `stdout` + `stderr`
runlevel = "S12345" to the system log daemon using the command line `logger(1)` tool.
command = "syslogd -n"
}
Notice the empty `log` block, BusyBox `ntpd` uses `stderr` for logging
when run in the foreground. With it Finit redirects `stdout` +
`stderr` to the system log daemon using the command line `logger(1)`
tool.
A service, or task, can have multiple dependencies listed. Here we wait A service, or task, can have multiple dependencies listed. Here we wait
for *both* `syslogd` to have started and basic networking to be up: for *both* `syslogd` to have started and basic networking to be up:
service ntpd { service [2345] log <pid/syslogd,net/route/default> ntpd -n -N -p pool.ntp.org
runlevel = "2345"
conditions = { "pid/syslogd", "net/route/default" }
log { }
command = "ntpd -n -N -p pool.ntp.org"
}
If either condition fails, e.g. loss of networking, `ntpd` is stopped If either condition fails, e.g. loss of networking, `ntpd` is stopped
and as soon as it comes back up again `ntpd` is restarted automatically. and as soon as it comes back up again `ntpd` is restarted automatically.
@@ -211,4 +134,4 @@ and as soon as it comes back up again `ntpd` is restarted automatically.
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html [finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
[infix]: https://kernelkit.github.io [infix]: https://kernelkit.github.io
[myLinux]: https://github.com/troglobit/myLinux/ [myLinux]: https://github.com/troglobit/myLinux/
[contrib]: https://github.com/finit-project/finit/tree/master/contrib [contrib]: https://github.com/troglobit/finit/tree/master/contrib
+20 -196
View File
@@ -1,10 +1,6 @@
Finit Features Finit Features
============== ==============
This page highlights some of Finit's key features with examples and usage
scenarios. For complete documentation, configuration syntax, and advanced
options, see the [Configuration](config/index.md) section.
**Process Supervision** **Process Supervision**
Start, monitor and restart services should they fail. Start, monitor and restart services should they fail.
@@ -18,9 +14,9 @@ waits for user input before handing over to `/bin/login`, which is
responsible for handling the actual authentication. responsible for handling the actual authentication.
```conf ```conf
tty tty1 { runlevel = "12345" device = "/dev/tty1" term = "linux" nowait = true } tty [12345] /dev/tty1 nowait linux
tty ttyAMA0 { runlevel = "12345" device = "/dev/ttyAMA0" term = "vt100" noclear = true } tty [12345] /dev/ttyAMA0 noclear vt100
tty getty { runlevel = "12345" command = "/sbin/getty -L /dev/ttyAMA0 vt100" } tty [12345] /sbin/getty -L /dev/ttyAMA0 vt100
``` ```
Users of embedded systems may want to enable automatic serial console Users of embedded systems may want to enable automatic serial console
@@ -29,7 +25,7 @@ system uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit
figures it out by querying sysfs: `/sys/class/tty/console/active`. figures it out by querying sysfs: `/sys/class/tty/console/active`.
```conf ```conf
tty console { runlevel = "12345" device = "@console" term = "linux" noclear = true } tty [12345] @console linux noclear
``` ```
Notice the optional `noclear`, `nowait`, and `nologin` flags. The Notice the optional `noclear`, `nowait`, and `nologin` flags. The
@@ -40,21 +36,21 @@ see the [TTY and Consoles](config/tty.md) section.
**Runlevels** **Runlevels**
Support for SysV init-style [runlevels][5] is available, in the same Support for SysV init-style [runlevels][5] is available, in the same
minimal style as everything else in Finit. The `runlevel` setting minimal style as everything else in Finit. The `[2345]` syntax can be
applies to service, task, run, and tty blocks alike. applied to service, task, run, and TTY stanzas.
Reserved runlevels are 0 and 6, halt and reboot, respectively just like Reserved runlevels are 0 and 6, halt and reboot, respectively just like
SysV init. Runlevel 1 can be configured freely, but is recommended to SysV init. Runlevel 1 can be configured freely, but is recommended to
be kept as the system single-user runlevel since Finit will not start be kept as the system single-user runlevel since Finit will not start
networking here. The configured `runlevel` from `/etc/finit.conf` 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) 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 a `run` block with `runlevel = "S"`. Changing runlevels at by using `run [S] cmd`. Changing runlevels at runtime is done like any
runtime is done like any other init, e.g. <kbd>init 4</kbd>, but also other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
using the more advanced [`initctl`](initctl.md) tool. [`initctl`](initctl.md) tool.
**Conditions** **Conditions**
@@ -86,17 +82,8 @@ the condition `<hw/model/foo>` when starting other scripts. Here is an
example: example:
``` ```
run ident { run [S] /path/to/ident --
description = "" task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board
runlevel = "S"
command = "/path/to/ident"
}
task foo-init {
description = "Initializing Foo board"
runlevel = "2"
conditions = { "hw/model/foo" }
command = "/path/to/foo-init"
}
``` ```
> [!TIP] > [!TIP]
@@ -126,7 +113,7 @@ hook into the boot process or respond to various I/O.
For more information, see the [Plugins](plugins.md) section. For more information, see the [Plugins](plugins.md) section.
**Automatic Reload**{ #automatic-reload } **Automatic Reload**
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/` By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
registering any changes to `.conf` files. To activate a change the user registering any changes to `.conf` files. To activate a change the user
@@ -143,83 +130,6 @@ unnecessary overhead, which can be removed at build-time using:
configure --enable-auto-reload configure --enable-auto-reload
**Linux Capabilities**
Finit supports Linux capabilities, allowing services to run with minimal
required privileges instead of running as root. This improves security by
following the principle of least privilege.
```conf
service nginx {
runlevel = "2345"
user = "www-data"
group = "www-data"
capabilities = { "^cap_net_bind_service" }
command = "/usr/sbin/nginx -g 'daemon off;'"
}
```
In this example, nginx runs as the unprivileged `www-data` user but retains
the ability to bind to privileged ports (80, 443) through the
`cap_net_bind_service` capability.
The `capabilities` list uses the IAB (Inheritable, Ambient, Bounding) format:
- `^` = Ambient (recommended) - capabilities survive exec()
- `%` = Inheritable only - requires file capabilities
- `!` = Bounding - block from acquiring capability
Multiple capabilities can be specified as comma-separated:
```conf
capabilities = { "^cap_net_raw", "^cap_net_admin", "!cap_sys_admin" }
```
See the [Linux Capabilities](config/capabilities.md) section for detailed
information, examples, and security best practices.
**PAM Sessions**
A service can run inside a PAM session, so the `session` stack in
`/etc/pam.d` applies to it, e.g. limits from `pam_limits`:
```conf
service weston {
user = "weston"
pam = "weston-autologin"
command = "/usr/bin/weston --continue-without-input"
}
```
Without a `user` the session is for root. Requires a build with PAM
support, which is the default; see `--disable-pam`.
See the [PAM Sessions](config/pam.md) section for the pam.d file the
example needs, and for how PAM and Finit settings interact.
**Supplementary Groups**
Finit supports supplementary groups for services, allowing them to access
resources owned by multiple groups without running as root. This complements
capabilities for fine-grained privilege control.
```conf
service @caddy:caddy,ssl-cert /usr/bin/caddy run
```
In this example, the Caddy web server runs as user `caddy` with primary group
`caddy`, but also has access to resources owned by the `ssl-cert` group (such
as TLS certificates).
Finit automatically reads the user's supplementary group membership from
`/etc/group`. Additional groups can be specified explicitly using the syntax
`@user:group,sup1,sup2,...`.
See the [Non-privileged Services](config/services.md#non-privileged-services)
section for more information.
**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 in
@@ -242,19 +152,15 @@ The name of each sub-group is taken from the username.
A fourth group also exists, the `root` group. It is also _reserved_ and A fourth group also exists, the `root` group. It is also _reserved_ and
primarily intended for RT tasks. If you have RT tasks they need to be primarily intended for RT tasks. If you have RT tasks they need to be
declared as such in their service block like this: declared as such in their service stanza like this:
service foo { service [...] <...> cgroup.root /path/to/foo args -- description
cgroup root {}
command = "/path/to/foo args"
}
Every block names the group it joins, so a second RT task says so too: or
service bar { cgroup.root
cgroup root {} service [...] <...> /path/to/foo args -- description
command = "/path/to/bar args" service [...] <...> /path/to/bar args -- description
}
See the [Cgroups](config/cgroups.md) section for more information, e.g., See the [Cgroups](config/cgroups.md) section for more information, e.g.,
how to configure per-group limits. how to configure per-group limits.
@@ -269,86 +175,4 @@ commands for details.
> disabled early at boot. > disabled early at boot.
**Service Management**
Finit includes the `initctl` tool for managing services and system state at
runtime. Key capabilities include:
- **Enable/Disable services**: Manage which services start at boot by moving
configuration files between `/etc/finit.d/available` and
`/etc/finit.d/enabled`
- **Start/Stop/Restart**: Control individual services without requiring a
full system reboot
- **Status monitoring**: View service state, PID, uptime, and resource usage
- **Condition management**: Set and clear user-defined conditions to control
service dependencies
- **Cgroup monitoring**: Real-time process and resource monitoring with
`initctl top`, similar to the traditional `top` command but cgroup-aware
Example commands:
```bash
initctl enable myservice # Enable service for next boot
initctl start myservice # Start service now
initctl status # Show all services
initctl top # Interactive resource monitor
initctl cond set usr/custom # Set custom condition
```
`initctl` rides Finit's built-in [D-Bus API](dbus.md) when available,
which is also open to any other bus client, including `dbus-send` and
language bindings.
See the [Commands & Status](initctl.md) section for complete documentation.
**Rescue Mode**
Finit provides a built-in rescue mode for system recovery and maintenance.
When booting with the `rescue` kernel parameter, the system enters a
protected maintenance shell.
If the bundled `sulogin` program is available (from Finit, util-linux, or
BusyBox), you'll be prompted for the root password before accessing the
maintenance shell. This provides secure access for system recovery.
If `sulogin` is not available, Finit falls back to reading
`/lib/finit/rescue.conf` and boots the system in a limited maintenance mode.
```
# Kernel command line
linux /vmlinuz root=/dev/sda1 rescue
```
In rescue mode, `initctl` will not work. After fixing the problem, use
`reboot -f` to force reboot.
Rescue mode can be disabled at build time with `configure --without-rescue`.
See the [Rescue Mode](config/rescue.md) section for more information.
**Switch Root**
Finit supports switching from an initramfs to a real root filesystem using
the built-in `initctl switch-root` command. This allows Finit to serve as
the init system in an initramfs for early boot tasks (LUKS unlock, LVM
activation, network boot) before transitioning to the real root.
```bash
# In initramfs, after mounting the real root:
initctl switch-root /mnt/root
```
The switch-root operation:
1. Runs the `HOOK_SWITCH_ROOT` hook for cleanup
2. Stops all services gracefully
3. Moves virtual filesystems (`/dev`, `/proc`, `/sys`, `/run`) to the new root
4. Deletes initramfs contents to free memory
5. Pivots to the new root and execs the new init
See the [Switch Root](switchroot.md) section for complete documentation.
[5]: https://en.wikipedia.org/wiki/Runlevel [5]: https://en.wikipedia.org/wiki/Runlevel
+15 -22
View File
@@ -1,7 +1,7 @@
Introduction Introduction
============ ============
![Alpine screenshot](img/alpine-screenshot2.png){ align=right width=40% } ![Alpine screenshot](img/alpine-screenshot2.png){ align=right }
> Reverse engineered from the [EeePC fastinit][] > Reverse engineered from the [EeePC fastinit][]
> "gaps filled with frog DNA …" > "gaps filled with frog DNA …"
@@ -17,32 +17,26 @@ PID file monitoring, or [conditions](conditions.md).
Features Features
-------- --------
* [Runlevels](config/runlevels.md), defined per service * [Runlevels][5], defined per service
* [One-shot tasks](config/task-and-run.md), [services](config/services.md) (daemons), or [SysV init][4] start/stop scripts * One-shot tasks, services (daemons), or [SysV init][4] start/stop scripts
* [Runparts](config/runparts.md) and `/etc/rc.local` support * Runparts and `/etc/rc.local` support
* Process supervision similar to [systemd][] * Process supervision similar to [systemd][]
* Fine-grained privilege control: * Sourcing environment files
- [Linux capabilities](config/capabilities.md) for minimal required privileges * Conditions for network/process/custom dependencies
- [Supplementary groups](config/services.md#non-privileged-services) for multi-group resource access * Readiness notification; PID files (native) for synchronizing system
* [Sourcing environment files](config/service-env.md)
* [Conditions](conditions.md) for network/process/custom dependencies
* [Readiness notification](config/service-sync.md); PID files (native) for synchronizing system
startup, support for systemd [sd_notify()][], or [s6 style][] too startup, support for systemd [sd_notify()][], or [s6 style][] too
* Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes) * Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes)
* [Pre/Post script actions](config/service-wrappers.md) * Pre/Post script actions
* Rudimentary [templating support](config/templating.md) * Rudimentary [templating support](config/templating.md)
* [Tooling](initctl.md) to enable/disable services * Tooling to enable/disable services
* [D-Bus API](dbus.md), `org.finit` control and signals for bus clients * Built-in getty
* [Automatic reload](features.md#automatic-reload) of modified configuration files (optional)
* [Built-in getty](config/tty.md)
* Built-in watchdog, with support for hand-over to [watchdogd][] * Built-in watchdog, with support for hand-over to [watchdogd][]
* Built-in support for Debian/BusyBox [`/etc/network/interfaces`][3] * Built-in support for Debian/BusyBox [`/etc/network/interfaces`][3]
* [Cgroups v2](config/cgroups.md), both configuration and monitoring in [`initctl top`](initctl.md) * Cgroups v2, both configuration and monitoring in `initctl top`
* [Plugin support](plugins.md) for customization * Plugin support for customization
* Proper [rescue mode](config/rescue.md) with bundled `sulogin` for protected maintenance shell * Proper rescue mode with bundled `sulogin` for protected maintenance shell
* [Switch root](switchroot.md) support for initramfs-to-real-root transitions
* Integration with [watchdogd][] for full system supervision * Integration with [watchdogd][] for full system supervision
* [Logging](config/logging.md) to kernel ring buffer before `syslogd` has started, see the * Logging to kernel ring buffer before `syslogd` has started, see the
recommended [sysklogd][] project for complete logging integration recommended [sysklogd][] project for complete logging integration
and how to log to the kernel ring buffer from scripts using `logger` and how to log to the kernel ring buffer from scripts using `logger`
@@ -69,8 +63,7 @@ and proposed extensions.
[3]: https://manpages.debian.org/unstable/ifupdown2/interfaces.5.en.html [3]: https://manpages.debian.org/unstable/ifupdown2/interfaces.5.en.html
[4]: https://en.wikipedia.org/wiki/Init [4]: https://en.wikipedia.org/wiki/Init
[5]: https://en.wikipedia.org/wiki/Runlevel [5]: https://en.wikipedia.org/wiki/Runlevel
[6]: https://github.com/finit-project/finit [6]: https://github.com/troglobit/finit
[dinit]: https://davmac.org/projects/dinit/
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/ [systemd]: https://www.freedesktop.org/wiki/Software/systemd/
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html [sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
[s6 style]: https://skarnet.org/software/s6/notifywhenup.html [s6 style]: https://skarnet.org/software/s6/notifywhenup.html
+9 -32
View File
@@ -5,11 +5,6 @@ Finit also implements a modern API to query status, and start/stop
services, called `initctl`. Unlike `telinit` the `initctl` tool does services, called `initctl`. Unlike `telinit` the `initctl` tool does
not return until the given command has fully completed. not return until the given command has fully completed.
Since Finit v5, `initctl` talks to Finit over the built-in
[D-Bus API](dbus.md) when the bus socket is available, falling back
to the legacy `INIT_SOCKET` transport. The `monitor` command streams
the `org.finit` signals until interrupted.
``` ```
Usage: initctl [OPTIONS] [COMMAND] Usage: initctl [OPTIONS] [COMMAND]
@@ -19,7 +14,6 @@ Options:
-f, --force Ignore missing files and arguments, never prompt -f, --force Ignore missing files and arguments, never prompt
-h, --help This help text -h, --help This help text
-j, --json JSON output in 'status' and 'cond' commands -j, --json JSON output in 'status' and 'cond' commands
-n, --noerr Ignore error, e.g., already started/enabled/...
-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
@@ -32,8 +26,15 @@ Commands:
help This help text help This help text
version Show program version version Show program version
show Show /etc/finit.conf ls | list List all .conf in /etc/finit.d
reload Reload /etc/finit.conf (activate changes) create <CONF> Create .conf in /etc/finit.d/available
delete <CONF> Delete .conf in /etc/finit.d/available
show <CONF> Show .conf in /etc/finit.d/available
edit <CONF> Edit .conf in /etc/finit.d/available
touch <CONF> Change .conf in /etc/finit.d/available
enable <CONF> Enable .conf in /etc/finit.d/available
disable <CONF> Disable .conf in /etc/finit.d/enabled
reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined conditions +usr/COND cond set <COND> Set (assert) user-defined conditions +usr/COND
cond get <COND> Get status of user-defined condition, see $? and -v cond get <COND> Get status of user-defined condition, see $? and -v
@@ -49,7 +50,6 @@ Commands:
Note: Finit .conf file(s) are *not* reloaded! 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
kill <NAME>[:ID] <S> Send signal S to service by name, with optional ID kill <NAME>[:ID] <S> Send signal S to service by name, with optional ID
monitor Stream D-Bus signals (service state, conditions) until ^C
ident [NAME] Show matching identities for NAME, or all 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
@@ -65,10 +65,8 @@ Commands:
halt Halt system halt Halt system
poweroff Halt and power off system poweroff Halt and power off system
suspend Suspend system suspend Suspend system
switch-root ROOT [INIT] Switch to new root filesystem (initramfs)
utmp show Raw dump of UTMP/WTMP db 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
@@ -145,24 +143,3 @@ Apr 8 12:37:46 alpine authpriv.info dropbear[2300]: Exit (root) from <192.168.1
Apr 8 15:02:11 alpine authpriv.info dropbear[2634]: Child connection from 192.168.121.1:48576 Apr 8 15:02:11 alpine authpriv.info dropbear[2634]: Child connection from 192.168.121.1:48576
Apr 8 15:02:12 alpine authpriv.notice dropbear[2634]: Password auth succeeded for 'root' from 192.168.121.1:48576 Apr 8 15:02:12 alpine authpriv.notice dropbear[2634]: Password auth succeeded for 'root' from 192.168.121.1:48576
``` ```
Switch Root
-----------
The `switch-root` command is used when running Finit in an initramfs to
transition to the real root filesystem. This is similar to the standalone
`switch_root(8)` utility but integrated into Finit.
```
initctl switch-root /mnt/root [/sbin/init]
```
Requirements:
- Must be run during runlevel S (bootstrap) or runlevel 1
- The new root must be a mount point (different device than /)
- Can only be used when Finit is running as PID 1 in an initramfs
For complete documentation and usage examples, see the dedicated
[Switch Root](switchroot.md) section.
+11 -534
View File
@@ -1,537 +1,14 @@
Bundled Device Manager keventd
====================== =======
The kernel event daemon `keventd` is a built-in device manager bundled The kernel event daemon bundled with Finit is a simple uevent monitor
with Finit. It replaces the need for external device managers like for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition,
mdev, mdevd, or udevd on systems where a lighter-weight solution is which can be useful to prevent power hungry services like anacron to run
preferred, particularly on embedded systems. when a laptop is only running on battery, for instance.
It is enabled by default since Finit v5, and needs libblkid from Since keventd is not an integral part of Finit yet it is not enabled by
util-linux (the `libblkid-dev` package, or whatever your distribution default. Enable it using `./configure --with-keventd`. The bundled
calls it) to read the filesystem UUID and label behind the `contrib/` build scripts for Debian, Alpine, and Void have this enabled.
`/dev/disk/by-uuid/` and `/dev/disk/by-label/` symlinks. To disable it
and use an external device manager instead:
./configure --without-keventd This daemon is planned to be extended with monitoring of other uevents,
patches and ideas are welcome in the issue tracker.
Features
--------
When started, keventd listens on a `NETLINK_KOBJECT_UEVENT` socket for
kernel events and handles:
- **Device node creation**: creates and removes `/dev` nodes with
correct permissions on device add/remove events
- **udev rules**: matches events against rules read from
`/lib/udev/rules.d/`, `/run/udev/rules.d/`, and `/etc/udev/rules.d/`,
with a curated ruleset derived from eudev installed by default
- **Persistent symlinks**: creates `by-id` and `by-path` symlinks under
`/dev/disk/`, `/dev/input/`, and elsewhere, for stable device naming
- **Firmware loading**: responds to kernel firmware requests by searching
`/lib/firmware/` and writing firmware data to sysfs
- **Module loading**: parses `MODALIAS` from uevents and spawns `modprobe`
to load the appropriate kernel module
- **Coldplug**: with the `-c` flag, walks `/sys/devices` and triggers
add events for all devices present at boot
- **Power supply monitoring**: tracks AC power status and provides the
`sys/pwr/ac` condition
- **Device conditions**: sets `dev/*` conditions in the Finit condition
system when device nodes appear or disappear
- **Netlink rebroadcast**: rebroadcasts processed uevents to netlink
group 0x4 for [libudev-zero][] consumers (enabled by default)
- **D-Bus interface**: `org.finit.Device1` -- settle, trigger, device
info, rules reload, and queue state over its own bus socket
Device Nodes
------------
On receiving an `add` event with `MAJOR`, `MINOR`, and `DEVNAME`
fields, keventd creates the corresponding device node in `/dev` using
`mknod()`. Parent directories are created automatically (e.g.,
`/dev/input/` for `/dev/input/event0`).
On `remove` events, the device node and its associated symlinks and
conditions are cleaned up.
### Default Permissions
keventd applies permissions based on built-in rules that match on
device subsystem and name:
| **Subsystem** | **Pattern** | **Mode** | **Owner:Group** |
|---------------|--------------------------------------------|----------|-----------------|
| block | sd*, vd*, nvme*, mmcblk*, loop*, dm-*, md* | 0660 | root:disk |
| tty | tty[0-9]* | 0620 | root:tty |
| tty | ttyS*, ttyUSB*, ttyACM* | 0660 | root:dialout |
| input | event*, mouse*, mice | 0660 | root:root |
| sound | * | 0660 | root:audio |
| video4linux | * | 0660 | root:video |
| drm | card*, render* | 0660 | root:video |
| (any) | null, zero, full, random, urandom | 0666 | root:root |
| (any) | console | 0600 | root:root |
| (default) | | 0660 | root:root |
udev Rules
----------
Rules are read from `/lib/udev/rules.d/`, `/run/udev/rules.d/`, and
`/etc/udev/rules.d/`, plus any directory passed with `-r`. Files need a
`.rules` suffix. Like udev, all files sort together by filename,
whichever directory they live in, and a file masks one of the same name
in an earlier directory: `/etc/` overrides `/run/`, which overrides
`/lib/`, and the `-r` directory overrides them all. A symlink to
`/dev/null` in `/etc/udev/rules.d/` disables the `/lib/` file of the
same name.
The syntax is the one described in [udev(7)][]. What follows is the
subset keventd understands, not a second copy of the grammar. To pick up
edited rules, call `RulesReload` on
[`org.finit.Device1`](#d-bus-interface-orgfinitdevice1). `initctl reload`
does not touch them.
### Match Keys
| **Key** | **Matches** |
|---------------------------------------------------|--------------------------------------------------------------------------|
| `ACTION` | `add`, `remove`, `change`, `move`, `bind`, `unbind`, `online`, `offline` |
| `DEVPATH` | kernel device path, without the `/sys` prefix |
| `KERNEL` | sysfs name of the device |
| `NAME` | device node name |
| `SUBSYSTEM`, `DRIVER` | subsystem and bound driver |
| `ATTR{file}` | sysfs attribute of the device |
| `SYSCTL{param}` | value under `/proc/sys/` |
| `ENV{key}` | uevent property, including ones set by earlier rules |
| `CONST{key}` | `arch`; `virt` reports `container` or nothing |
| `TAG` | tag set by an earlier `TAG+=` |
| `TEST{octal}` | file exists, optionally with the given permission bits |
| `PROGRAM` | exit status of a program, output kept for `RESULT` and `%c` |
| `RESULT` | output of the last `PROGRAM` |
| `KERNELS`, `SUBSYSTEMS`, `DRIVERS`, `ATTRS{file}` | the device or any of its parents, see below |
Values match literally, as an `fnmatch()` glob, or as `a|b|c` alternatives.
Both `==` and `!=` work everywhere.
`SECLABEL`, `OPTIONS`, and `TAGS` parse but do nothing. Rules using them load
without complaint and the key is skipped, so `OPTIONS+="static_node=..."`,
`link_priority=`, `string_escape=`, and `watch` have no effect, and a `TAGS==`
match never succeeds.
### The Parent Chain
`ATTR{}` reads an attribute of the device the event fired for. `ATTRS{}` also
looks at the parents, walking up the sysfs tree until one matches. For a
keyboard behind a hub:
/sys/devices/.../usb2/ <- root hub
usb2/2-1/ <- hub
usb2/2-1/2-1.4/ <- keyboard, event fires here
`ATTRS{authorized_default}=="1"` finds no such attribute on the keyboard or
the hub, reaches the root hub, and matches there. `KERNELS`, `SUBSYSTEMS`,
and `DRIVERS` do the same for a parent's name, subsystem, and driver.
Each of these keys walks the chain on its own. udev requires all the parent
keys in one rule to match on the *same* parent, so a rule pairing
`SUBSYSTEMS=="usb"` with `ATTRS{idVendor}=="1d6b"` is looser here: the two may
land on different ancestors. Where that matters, match on one attribute
specific enough to identify the device by itself.
### Assignments
| **Key** | **Effect** |
|-----------------------------|--------------------------------------------------------------------------|
| `NAME=` | name of the device node |
| `SYMLINK=`, `+=` | symlinks to create, space separated, relative to `/dev` |
| `OWNER=`, `GROUP=`, `MODE=` | ownership and permissions of the node |
| `ENV{key}=`, `+=`, `-=` | set, append to, or clear a property |
| `TAG+=`, `-=` | tags for later `TAG==` matching |
| `RUN+=` | program to run once the event is handled, `RUN{builtin}+=` for a builtin |
| `IMPORT{type}` | `program`, `file`, `db`, `builtin`, `parent`, `cmdline` |
| `ATTR{file}=` | write to a sysfs attribute |
| `SYSCTL{param}=` | write to `/proc/sys/` |
| `LABEL=`, `GOTO=` | skip ahead to a label |
`:=` locks `MODE`, `OWNER`, and `GROUP` against later rules the way udev
does. On `NAME` and `ENV{}` it behaves like plain `=` and locks nothing,
and `SYMLINK:=` is dropped without a word.
`IMPORT{builtin}` and `RUN{builtin}` can call `blkid`, `hwdb`,
`input_id`, `kmod`, `net_id`, `path_id`, and `usb_id`.
### Substitutions
| **Long** | **Short** | **Expands to** |
|--------------------|---------------|------------------------------------|
| `$kernel` | `%k` | sysfs name of the device |
| `$devpath` | `%p` | kernel device path |
| `$name` | `%N`, `%D` | device node name |
| `$major`, `$minor` | `%M`, `%m` | device numbers |
| `$driver` | `%d` | bound driver |
| `$attr{file}` | `%s{file}` | sysfs attribute of the device |
| `$env{key}` | | uevent property |
| `$result` | `%c`, `%c{N}` | `PROGRAM` output, or its Nth field |
| `$root` | | `/dev` |
| `$sys` | | `/sys` |
| | `%n` | trailing digits of the sysfs name |
| | `%b` | `major:minor` |
udev's `$id`, `$parent`, `$links`, and `%E{}` are not implemented, and `%b`
here is the device number pair rather than udev's parent bus id. An unknown
specifier is left in the string as written.
Persistent Symlinks
-------------------
Symlinks come from two places. A handful are built into keventd and are
created for every device in the subsystem, whatever rules are loaded.
For block devices, under `/dev/disk/`:
- **by-id**: based on the device serial number and model, read from
sysfs attributes (`/sys/.../device/vendor`, `model`, `serial`)
- **by-path**: based on the device topology path
For input devices, under `/dev/input/`:
- **by-id**: based on the device name from sysfs
- **by-path**: based on the physical device path
The rest come from `SYMLINK+=` in the udev rules, so what you get
depends on the ruleset installed. The curated rules Finit ships add,
among others:
| **Directory** | **Links** |
|----------------|--------------------------------------------------------|
| `/dev/disk/` | `by-uuid`, `by-label`, `by-partuuid`, `by-partlabel`, `by-diskseq`, and further `by-id` and `by-path` names for NVMe, virtio, MMC, WWN, and FireWire |
| `/dev/input/` | longer `by-id` and `by-path` names than the built-in ones, down to the USB interface number |
| `/dev/serial/` | `by-id`, `by-path` |
| `/dev/snd/` | `by-id`, `by-path` |
| `/dev/v4l/` | `by-id`, `by-path` |
| `/dev/tape/` | `by-id`, `by-path` |
| `/dev/dri/` | `by-path` |
They also set up a few fixed names: `/dev/rtc`, `/dev/cdrom`, and
`/dev/virtio-ports/<name>`.
The `by-uuid` and `by-label` links are why keventd needs libblkid. The
rules read the filesystem metadata off the device with
`IMPORT{builtin}="blkid"`, and libblkid is what that builtin calls.
Built-in and rule-provided symlinks alike are tracked internally and
removed when the corresponding device is unplugged.
Firmware Loading
----------------
When a kernel driver requests firmware (via `request_firmware()`), the
kernel sends a uevent with a `FIRMWARE=` field. keventd handles this
by:
1. Searching for the firmware file in order:
- `/lib/firmware/updates/<kernel-version>/<name>`
- `/lib/firmware/updates/<name>`
- `/lib/firmware/<kernel-version>/<name>`
- `/lib/firmware/<name>`
2. Writing `1` to `/sys/<devpath>/loading` to signal start
3. Copying the firmware data to `/sys/<devpath>/data`
4. Writing `0` to `/sys/<devpath>/loading` on success (or `-1` on failure)
This is particularly important early in boot when drivers for graphics
cards, network adapters, and other hardware need firmware before they
can operate.
Module Loading
--------------
When a device add event includes a `MODALIAS` field, keventd spawns
`modprobe -bq <modalias>` to load the matching kernel module. Module
loading is done asynchronously (keventd does not wait for modprobe to
complete) to avoid blocking other event processing.
Coldplug
--------
To handle devices that were present before keventd started, it supports
a coldplug mode activated with the `-c` flag. This walks the entire
`/sys/devices` tree and writes `add` to each `uevent` file, causing the
kernel to re-emit add events for all existing devices.
This replaces the separate `coldplug` script previously used with mdev.
When `-c` is used, keventd defers its pidfile (and the `pid/keventd`
condition Finit asserts from it) until the coldplug event queue has
been fully drained. Services that depend on `<pid/keventd>` can
therefore assume `/dev` is populated and persistent symlinks are live,
without needing a separate `settle` step.
Netlink Rebroadcast
-------------------
The Linux kernel sends uevents to netlink multicast group 1 (bit 0) of
`NETLINK_KOBJECT_UEVENT`. Only the device manager listens on this raw
kernel group. Userspace consumers — applications using libudev —
expect to receive processed events on a separate netlink group.
systemd/udevd established the convention of rebroadcasting processed
events to a separate group, and [libudev-zero][], a daemonless drop-in
replacement for libudev, listens on group `0x4` for these events.
Without a device manager rebroadcasting, graphical applications,
Wayland/X11 compositors, libinput, and anything else using libudev to
monitor device hotplug will never see any events.
keventd rebroadcasts by default to netlink group 4 (`0x4`). A second
netlink socket is created at startup, and after each uevent has been
fully processed (device nodes created, symlinks set up, modules loaded),
the original raw event is sent to the configured group(s).
Rebroadcasting after processing ensures that device nodes and symlinks
already exist by the time consumers receive the notification.
Use `-g GROUP` to override the default group mask, or `-G` to disable
rebroadcast entirely. Bit 0 of the group mask is always forced off to
prevent a feedback loop with the kernel's own multicast group.
### Background
The netlink uevent architecture uses separate multicast groups to
isolate the kernel-to-device-manager channel from the device-manager-to-
application channel:
| **Group** | **Bit** | **Purpose** |
|-----------|---------|---------------------------------------------|
| 1 | 0 | Kernel events (device manager listens here) |
| 4 | 2 | Processed events (libudev consumers listen) |
This two-group design was established by systemd/udevd and is the de
facto standard. [mdevd][] implements the same mechanism via its `-O`
flag, and keventd follows the same convention.
For more details, see:
- [libudev-zero][] — daemonless replacement for libudev
- [mdevd][] — mdev-compatible device manager with rebroadcast support
Conditions
----------
keventd provides conditions in two namespaces:
### Device Conditions (`dev/`)
When a device node is created, keventd asserts a corresponding condition
in `/run/finit/cond/dev/`. This allows services to wait for specific
devices:
service mdadm {
description = "RAID monitor"
runlevel = "2345"
conditions = { "dev/sda" }
command = "/usr/sbin/mdadm --monitor /dev/md0"
}
service gps-daemon {
description = "GPS daemon"
runlevel = "2345"
conditions = { "dev/ttyUSB0" }
command = "/usr/sbin/gps-daemon"
}
Network interfaces are not device nodes and do not live in the `dev/`
namespace -- a `/dev/wan` node created by a user must not be confused
with a `wan` interface. To wait for an interface, use the `netlink`
plugin's `net/<iface>/exist` condition, plus `net/<iface>/up` (admin
up) and `net/<iface>/running` (carrier present) to gate on link state:
service dhcpcd {
description = "DHCP client"
runlevel = "2345"
conditions = { "net/wan/exist" }
command = "/usr/sbin/dhcpcd"
}
keventd provides the parallel `class/net/<iface>` condition, like for
any other sysfs class device.
When the device is removed, the condition is cleared and Finit stops
the dependent services.
### Class Conditions (`class/`)
Many devices live in sysfs without a `/dev/` node -- DSA switch ports,
IIO sensors, LEDs, backlight, PHYs, regulators. For those, keventd
asserts `class/<subsystem>/<sysname>` on every `add` event so services
can still wait for them:
service blink-blue {
description = "LED driver"
runlevel = "2345"
conditions = { "class/leds/blue" }
command = "/usr/sbin/blink-blue"
}
The condition is cleared on `remove`.
### Driver Conditions (`driver/`)
`driver/<name>` is asserted while the driver `<name>` is bound to at
least one device, from the kernel's `bind`/`unbind` uevents. Use this
to gate on slow-probing hardware whose readiness isn't marked by a
class device or `/dev` node, such as a switch core or complex PHY:
service dsa-probe {
description = "DSA topology probe"
runlevel = "2345"
conditions = { "driver/mt7530" }
command = "/usr/sbin/dsa-probe"
}
A driver bound to several devices keeps the condition asserted until
the last device is unbound. To wait for one specific device instance,
gate on what its probe creates instead: the `class/` condition or
`/dev` node of the child device.
### Power Supply Conditions (`sys/pwr/`)
keventd monitors the `power_supply` subsystem and provides:
- `sys/pwr/ac` — asserted when AC power is connected
This is useful for preventing power-hungry services from running on
battery:
service cron {
description = "Cron daemon"
runlevel = "2345"
conditions = { "sys/pwr/ac", "pid/syslogd" }
command = "cron -f"
}
Usage
-----
keventd [-cdGhnpSv] [-g GROUP] [-r DIR] [-t SECONDS]
Options:
-c Run coldplug at startup
-d Enable debug mode (foreground, verbose)
-g GROUP Override netlink rebroadcast group (default: 4)
-G Disable netlink rebroadcast entirely
-h Show help text
-n Run in foreground (no daemon)
-p Passive mode: power supply events only
-r DIR Extra rules directory, overrides the standard udev paths
-S Settle mode: wait for kernel uevent queue to quiet, then exit
-t SEC Settle timeout (default 30s, used with -S)
-v Show version
In normal operation, Finit starts keventd automatically via its system
configuration. The `-d` flag is useful for debugging device issues --
it runs keventd in the foreground and logs all received uevents.
`keventd -S` is a one-shot command, not a flag to the running daemon.
It is the `udevadm settle` equivalent for migration scenarios:
keventd -S -t 10 && start-graphical-session
With D-Bus support it asks the running keventd over
[`Device1.Settle`](#d-bus-interface-orgfinitdevice1), which tracks the
event queue where the events actually flow. When no bus answers --
another device manager, or a build without D-Bus -- it falls back to
polling `/sys/kernel/uevent_seqnum` until the kernel's sequence
counter has been stable for 200ms, then exits zero. After the
`-t SECONDS` timeout (default 30) it exits non-zero instead. The
fallback works regardless of which device manager is active, or even
if none is.
Prefer the condition-based model (`<dev/X>`, `<class/...>`,
`<driver/...>`) over settle when you control the service definition --
settle is racy with slow probes that fire after the queue appears
quiet. It is provided for legacy boot scripts and init transitions
where condition wiring isn't feasible.
Debug logging can also be toggled at runtime by sending `SIGUSR1`:
kill -USR1 $(pidof keventd)
D-Bus Interface (`org.finit.Device1`)
-------------------------------------
With D-Bus support (default, `--disable-dbus` opts out) keventd serves
`org.finit.Device1` at `/org/finit/device` on its own brokerless bus,
`unix:path=/run/keventd/bus`, the same way Finit serves
[`org.finit`](dbus.md) on `/run/finit/bus`. There is no forwarding
between the two -- clients that want both connect to both.
| Method | In sig | Out sig | Priv. | Notes |
|---------------|--------|---------|-------|----------------------------------------------|
| `Settle` | `u` | `b` | no | Wait until the event queue drains, timeout in seconds; `false` on timeout |
| `Trigger` | `ss` | — | yes | Replay events: action, subsystem glob (empty = all) |
| `Info` | `s` | `a{ss}` | no | `/run/udev/data` properties for a devpath |
| `RulesReload` | — | `u` | yes | Re-read the rules directories, returns rule count |
| Property | Sig | Notes |
|-------------------|-----|-------------------------------------------|
| `QueueEmpty` | `b` | No device events in flight |
| `SeqnumProcessed` | `t` | Highest kernel seqnum keventd has handled |
| Signal | Body | Fires when |
|-------------------|-------------------------|---------------------------------|
| `DeviceProcessed` | `ss` — devpath, action | An event has been fully handled: node, symlinks, database |
Example, wait up to ten seconds for the queue to drain:
```sh
dbus-send --address=unix:path=/run/keventd/bus \
--type=method_call --print-reply --dest=org.finit \
/org/finit/device org.finit.Device1.Settle uint32:10
```
In passive mode (`-p`) `Trigger` and `RulesReload` refuse -- device
events are not handled here -- while `Settle` and the queue-state
properties remain meaningful.
keventd's conditions are symlinks to Finit's `reconf` generation
marker, so they read the current generation by construction and never
enter `flux` -- like user-defined conditions, they need no reassert
after `initctl reload`. Device state does not change because Finit
re-read its configuration.
Integration with Finit
----------------------
keventd is a standalone daemon started by Finit as an internal service.
It communicates with Finit exclusively through the filesystem-based
condition system — creating and removing symlinks in `/run/finit/cond/`.
This means keventd can also be tested independently:
# Run in debug mode to see all kernel events
keventd -d
# Run with coldplug to populate /dev from scratch
keventd -c -n
# Run without rebroadcast (e.g., headless embedded system)
keventd -c -G
Only one device manager should be active at a time. This is settled
at build time: with the hotplug plugin enabled, Finit starts keventd
in passive mode (`-p`), leaving device management to the udevd, mdevd,
or mdev service from `system/10-hotplug.conf` and monitoring only
power supply events. Without the hotplug plugin, keventd runs as the
system device manager (`keventd -c`).
[libudev-zero]: https://github.com/illiliti/libudev-zero
[mdevd]: https://skarnet.org/software/mdevd/
[udev(7)]: https://man7.org/linux/man-pages/man7/udev.7.html
+3 -21
View File
@@ -35,15 +35,7 @@ For your convenience a set of *optional* plugins are available:
> the `/etc/fstab` file and in the Linux kernel config. > 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.
Enabled by default, and a no-op on systems without a `dbus-daemon`. _Optional plugin._
The daemon is declared in `20-dbus.conf` and its directories in
`tmpfiles.d/dbus.conf`, both overridable from `/etc`; the plugin
itself only handles what has to inspect the running system.
Not to be confused with Finit's built-in [D-Bus API](dbus.md): the
plugin (`--disable-dbus-plugin`) starts the external system bus
daemon, while the built-in support (`--disable-dbus`) is the
`org.finit` interface served by Finit itself.
* *hook-scripts.so*: Trigger the execution of scripts from plugin hook * *hook-scripts.so*: Trigger the execution of scripts from plugin hook
points (see [Hooks](#hooks)). _Optional plugin._ points (see [Hooks](#hooks)). _Optional plugin._
@@ -78,8 +70,8 @@ For your convenience a set of *optional* plugins are available:
name of the module to load. Any line starting with the standard UNIX name of the module to load. Any line starting with the standard UNIX
comment character, `#`, or `;`, is skipped. comment character, `#`, or `;`, is skipped.
Modules are by default loaded in runlevel `S` using a `task` block. Modules are by default loaded in runlevel `S` using the `task` stanza.
Each module is named `modprobe.foo`, with an optional `:ID`. The Each module gets a unique `name:modprobe.foo`, and optional`:ID`. The
runlevel can be changed per file using: runlevel can be changed per file using:
set runlevel 2345 set runlevel 2345
@@ -213,16 +205,6 @@ hook points:
new runlevel have been been stopped. When the hook has completed, new runlevel have been been stopped. When the hook has completed,
Finit continues to start all services in the new runlevel. Finit continues to start all services in the new runlevel.
### Switch Root Hooks
* `HOOK_SWITCH_ROOT`, `hook/sys/switchroot`: Called when
`initctl switch-root` is issued, before the transition begins. Use
this hook to save state, unmount initramfs-only filesystems, or perform
cleanup before switching to the new root. Only runs when Finit is
operating as PID 1 in an initramfs.
See the [Switch Root](switchroot.md) section for more information.
### Shutdown Hooks ### Shutdown Hooks
* `HOOK_NETWORK_DN`, `hook/net/down`: Called right after having changed * `HOOK_NETWORK_DN`, `hook/net/down`: Called right after having changed
+1 -5
View File
@@ -15,11 +15,7 @@ done slightly differently and on systems with udev you might want to add
the following one-shot task early in your `/etc/finit.conf`: the following one-shot task early in your `/etc/finit.conf`:
```conf ```conf
run udevadm { run [S] udevadm settle --timeout=120 -- Waiting for udev
description = "Waiting for udev"
runlevel = "S"
command = "udevadm settle --timeout=120"
}
``` ```
Finit has a built-in Getty for TTYs, but requires a working `/bin/login` Finit has a built-in Getty for TTYs, but requires a working `/bin/login`
+13 -36
View File
@@ -13,37 +13,20 @@ more of a policy for the user to define. Normally only runlevels 1-6
are used, and even more commonly, only the default runlevel is used. are used, and even more commonly, only the default runlevel is used.
To specify an allowed set of runlevels for a `service`, `run` command, To specify an allowed set of runlevels for a `service`, `run` command,
`task`, or `tty`, set `runlevel` in your `/etc/finit.conf`, like this: `task`, or `tty`, add `[NNN]` to your `/etc/finit.conf`, like this:
``` ```
service syslogd { service [S12345] syslogd -n -x -- System log daemon
description = "System log daemon" run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
runlevel = "S12345" task [S] /etc/init.d/kbd start -- Preparing console
command = "syslogd -n -x" service [S12345] <pid/syslogd> klogd -n -x -- Kernel log daemon
}
run acpid {
description = "Starting ACPI Daemon"
runlevel = "S"
command = "/etc/init.d/acpid start"
}
task kbd {
description = "Preparing console"
runlevel = "S"
command = "/etc/init.d/kbd start"
}
service klogd {
description = "Kernel log daemon"
runlevel = "S12345"
conditions = { "pid/syslogd" }
command = "klogd -n -x"
}
tty tty1 { runlevel = "12345" device = "/dev/tty1" } tty [12345] /dev/tty1
tty tty2 { runlevel = "2" device = "/dev/tty2" } tty [2] /dev/tty2
tty tty3 { runlevel = "2" device = "/dev/tty3" } tty [2] /dev/tty3
tty tty4 { runlevel = "2" device = "/dev/tty4" } tty [2] /dev/tty4
tty tty5 { runlevel = "2" device = "/dev/tty5" } tty [2] /dev/tty5
tty tty6 { runlevel = "2" device = "/dev/tty6" } tty [2] /dev/tty6
``` ```
In this example syslogd is first started, in parallel, and then acpid is In this example syslogd is first started, in parallel, and then acpid is
@@ -63,14 +46,8 @@ are also removed when they have completed, `initctl show` will not list
them. them.
``` ```
task foo { task [S] echo "foo" | cat >/tmp/bar
runlevel = "S" run [S] echo "$HOME" >/tmp/secret
command = "echo \"foo\" | cat >/tmp/bar"
}
run secret {
runlevel = "S"
command = "echo \"$HOME\" >/tmp/secret"
}
``` ```
Switching between runlevels can be done by calling init with a single Switching between runlevels can be done by calling init with a single
+4 -4
View File
@@ -3,18 +3,18 @@ Runparts & `/etc/rc.local`
At the end of the boot, when all bootstrap (`S`) tasks and services have At the end of the boot, when all bootstrap (`S`) tasks and services have
started, but not networking, Finit calls its built-in [run-parts(8)][] started, but not networking, Finit calls its built-in [run-parts(8)][]
command on any configured `runparts = "DIR"` directory. This happens just command on any configured `runparts <DIR>` directory. This happens just
before changing to the configured runlevel (default 2). (Networking is before changing to the configured runlevel (default 2). (Networking is
enabled just prior to changing from single user mode.) enabled just prior to changing from single user mode.)
```aconf ```shell
runparts = "/etc/rc.d/" runparts /etc/rc.d/
``` ```
Right after the runlevel change when all services have started properly, Right after the runlevel change when all services have started properly,
`/etc/rc.local` is called. `/etc/rc.local` is called.
No setting 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 in the end of the boot, before calling the `HOOK_SYSTEM_UP`. See more in the
[Hook Scripts](plugins.md#hooks) section. [Hook Scripts](plugins.md#hooks) section.
+9 -30
View File
@@ -27,22 +27,13 @@ Finit can *not* start and monitor a daemon that:
### Forks to bg w/ PID file ### Forks to bg w/ PID file
There are two variants. The former names the pidfile to watch, as for There are two syntax variants, type 1 and type 2. The former is the
`sysv` start/stop scripts, and the latter is inspired by systemd, with a traditional one used also for `sysv` start/stop scripts, and the latter
twist -- it lets Finit guess the pidfile based on the standard path and is inspired by systemd, with a twist -- it lets Finit guess the pifdile
the basename of the command. to look for based on the standard path and the basename of the command.
service serv { service pid:!/run/serv.pid serv -- Forking service, type 1
description = "Forking service, type 1" service type:forking serv -- Forking service, type 2
pidfile = "/run/serv.pid"
command = "serv"
}
service serv {
description = "Forking service, type 2"
type = "forking"
command = "serv"
}
In this example the resulting files to watch for are `/run/serv.pid` and 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 `/var/run/serv.pid`, respectively. On most modern Linux systems this is
@@ -50,29 +41,17 @@ the same directory (`/var/run` is a symlink to `../run`).
### Runs in fg w/ PID file ### Runs in fg w/ PID file
service serv { service serv -n -p -- Foreground service w/ PID file
description = "Foreground service w/ PID file"
command = "serv -n -p"
}
### Runs in fg w/o PID file ### Runs in fg w/o PID file
Same as previous, but we tell Finit to create the PID file, because we Same as previous, but we tell Finit to create the PID file, because we
need it to synchronize start/stop of a dependent service. need it to synchronize start/stop of a dependent service.
service serv { service pid:/run/serv.pid serv -n -- Foreground service w/o PID file
description = "Foreground service w/o PID file"
pidfile = "/run/serv.pid"
pidfile-create = true
command = "serv -n"
}
### Runs in fg w/ custom PID file ### Runs in fg w/ custom PID file
service serv { service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file
description = "Foreground service w/ custom PID file"
pidfile = "/run/servy.pid"
command = "serv -n -p -P /run/servy.pid"
}
[1]: config/service-sync.md [1]: config/service-sync.md
-193
View File
@@ -1,193 +0,0 @@
Switch Root
===========
Finit supports switching from an initramfs to a real root filesystem
using the `initctl switch-root` command. This is useful for systems
that use an initramfs for early boot (LUKS, LVM, network boot, etc.)
and need to transition to the real root before starting services.
Usage
-----
```sh
initctl switch-root NEWROOT [INIT]
```
- `NEWROOT`: Path to the mounted new root filesystem (e.g., `/mnt/root`)
- `INIT`: Optional path to init on the new root (default: `/sbin/init`)
Requirements
------------
1. Must be run during runlevel S (bootstrap) or runlevel 1
2. `NEWROOT` must be a mount point (different device than /)
3. `INIT` must exist and be executable on the new root
4. Finit must be running as PID 1 (in initramfs)
How It Works
------------
1. Runs `HOOK_SWITCH_ROOT` for any cleanup scripts/plugins
2. Runs `HOOK_SHUTDOWN` to notify plugins
3. Stops all services and kills remaining processes
4. Exits all plugins gracefully
5. Moves `/dev`, `/proc`, `/sys`, `/run` to new root
6. Deletes initramfs contents (if on tmpfs/ramfs) to free memory
7. Moves new root mount to `/`
8. Chroots to new root
9. Reopens `/dev/console` for stdin/stdout/stderr
10. Execs new init as PID 1
Example: Initramfs finit.conf
-----------------------------
Configuration file `/etc/finit.conf` in the initramfs:
```
# /etc/finit.conf in initramfs
# Mount the real root filesystem
run mount-root {
description = "Mounting root filesystem"
runlevel = "S"
command = "/bin/mount /dev/sda1 /mnt/root"
}
# Switch to real root after mount completes
run switch-root {
description = "Switching to real root"
runlevel = "S"
command = "/sbin/initctl switch-root /mnt/root"
}
```
For more complex setups (LUKS, LVM, etc.):
```
# Unlock LUKS volume
# The tty setting is required so cryptsetup can prompt for a passphrase
run cryptsetup {
description = "Unlocking encrypted root"
runlevel = "S"
tty = "@console"
command = "/sbin/cryptsetup open /dev/sda2 cryptroot"
}
# Activate LVM
run lvm {
description = "Activating LVM volumes"
runlevel = "S"
command = "/sbin/lvm vgchange -ay"
}
# Mount root
run mount-root {
description = "Mounting root"
runlevel = "S"
command = "/bin/mount /dev/vg0/root /mnt/root"
}
# Switch root
run switch-root {
description = "Switching to real root"
runlevel = "S"
command = "/sbin/initctl switch-root /mnt/root"
}
```
Example: Using Runlevel 1 for Switch Root
-----------------------------------------
For more complex initramfs setups where ordering of tasks becomes
difficult in runlevel S, you can perform the switch-root in runlevel 1:
```
# /etc/finit.conf in initramfs
# Start mdevd for device handling
service mdevd {
description = "Device event daemon"
runlevel = "S"
notify = "s6"
command = "/sbin/mdevd -D %n"
}
run coldplug {
description = "Coldplug devices"
runlevel = "S"
conditions = { "service/mdevd/ready" }
command = "/sbin/mdevd-coldplug"
}
# Mount the real root filesystem (after devices are ready)
run mount-root {
description = "Mounting root"
runlevel = "S"
conditions = { "run/coldplug/success" }
command = "/bin/mount /dev/sda1 /mnt/root"
}
# Transition to runlevel 1 after all S tasks complete
# The switch-root runs cleanly in runlevel 1
run switch-root {
description = "Switching to real root"
runlevel = "1"
command = "/sbin/initctl switch-root /mnt/root"
}
```
This approach separates the initramfs setup (runlevel S) from the
switch-root operation (runlevel 1), making task ordering simpler.
Hooks
-----
The `HOOK_SWITCH_ROOT` hook runs before the switch begins. Use it for:
- Saving state to the new root
- Unmounting initramfs-only mounts
- Cleanup tasks
Plugins can register for `HOOK_SWITCH_ROOT` just like other hooks:
```c
static void my_switch_root_hook(void *arg)
{
/* Cleanup before switch_root */
}
static plugin_t plugin = {
.name = "my-plugin",
.hook[HOOK_SWITCH_ROOT] = {
.cb = my_switch_root_hook
}
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
```
Conditions
----------
After switch_root, the new finit instance starts fresh. No conditions
or state are preserved across the switch. The new finit will:
1. Re-read `/etc/finit.conf` from the new root
2. Re-initialize all conditions
3. Start services according to the new configuration
See Also
--------
- [switch_root(8)](https://man7.org/linux/man-pages/man8/switch_root.8.html) - util-linux switch_root utility
- [Kernel initramfs documentation](https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html)
+4 -4
View File
@@ -1,10 +1,10 @@
Bundled Watchdog Daemon Bundled Watchdog Daemon
======================= =======================
A separate service is built and installed in `/libexec/finit/watchdogd`, When built `--with-watchdog` a separate service is built and installed
unless disabled with `--without-watchdog`. If this exists at runtime, in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
and the WDT device node exists, Finit will start it and treat it as the device node exists, Finit will start it and treat it as the elected
elected watchdog service to delegate its reboot to. This delegation is to watchdog service to delegate its reboot to. This delegation is to
ensure that the system is rebooted by a hardware watchdog timer -- on ensure that the system is rebooted by a hardware watchdog timer -- on
many embedded systems this is crucial to ensure all circuits on the many embedded systems this is crucial to ensure all circuits on the
board are properly reset for the next boot, in effect ensuring the board are properly reset for the next boot, in effect ensuring the
-4
View File
@@ -1,4 +0,0 @@
.deps/
keventd
Makefile
Makefile.in
-67
View File
@@ -1,67 +0,0 @@
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
AM_CPPFLAGS += -I$(top_srcdir)/src
pkglibexec_PROGRAMS = keventd
keventd_SOURCES = keventd.c keventd.h uevent.c \
udevdb.c udevdb.h \
sysfs.c sysfs.h \
rules.c rules.h \
builtin.c builtin.h \
$(top_srcdir)/src/util.c $(top_srcdir)/src/util.h
# Per-target flags exist even when their conditional is false, so
# assign the base unconditionally and append under DBUS
keventd_CPPFLAGS = $(AM_CPPFLAGS)
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
# Silence noisy false positives from PATH_MAX-into-PATH_MAX sysfs path
# concatenations; real concerns (e.g. USB strings into small buffers) are
# handled via explicit checks where present. Matches systemd-udev policy.
keventd_CFLAGS += -Wno-format-truncation
keventd_CFLAGS += $(lite_CFLAGS)
keventd_CFLAGS += $(uev_CFLAGS)
keventd_CFLAGS += $(blkid_CFLAGS)
keventd_LDADD = $(lite_LIBS)
keventd_LDADD += $(uev_LIBS)
keventd_LDADD += $(blkid_LIBS)
if DBUS
keventd_SOURCES += dbus.c
keventd_CPPFLAGS += -I$(top_srcdir)/libink
keventd_LDADD += $(top_builddir)/libink/libink.la
endif
# Curated udev rules derived from eudev 3.2.14 (GPL-2.0-or-later).
# Installed to /lib/udev/rules.d/ so existing third-party rules and
# local overrides under /etc/udev/rules.d/ coexist with standard udev
# precedence. See .notes/curated-rules-manifest.md.
if UDEV_RULES
udevrulesdir = $(prefix)/lib/udev/rules.d
dist_udevrules_DATA = \
rules.d/50-udev-default.rules \
rules.d/60-autosuspend.rules \
rules.d/60-block.rules \
rules.d/60-cdrom_id.rules \
rules.d/60-drm.rules \
rules.d/60-evdev.rules \
rules.d/60-fido-id.rules \
rules.d/60-input-id.rules \
rules.d/60-persistent-alsa.rules \
rules.d/60-persistent-input.rules \
rules.d/60-persistent-storage.rules \
rules.d/60-persistent-storage-tape.rules \
rules.d/60-persistent-v4l.rules \
rules.d/60-sensor.rules \
rules.d/60-serial.rules \
rules.d/64-btrfs.rules \
rules.d/70-camera.rules \
rules.d/70-joystick.rules \
rules.d/70-memory.rules \
rules.d/70-mouse.rules \
rules.d/70-touchpad.rules \
rules.d/75-net-description.rules \
rules.d/75-probe_mtd.rules \
rules.d/78-sound-card.rules \
rules.d/80-drivers.rules \
rules.d/80-net-name-slot.rules \
rules.d/81-net-dhcp.rules
endif

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