mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
175
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
530d6703f1 | ||
|
|
cec76f18a3 | ||
|
|
ee6d4a1dae | ||
|
|
0548485be1 | ||
|
|
1b10c3746e | ||
|
|
24911ebbc2 | ||
|
|
88a9e6575b | ||
|
|
9bfbdc6bac | ||
|
|
c016965371 | ||
|
|
263ea929fd | ||
|
|
cc5e0c5175 | ||
|
|
417abf9000 | ||
|
|
61b0e0f3e6 | ||
|
|
bad7c5c99a | ||
|
|
4c16190831 | ||
|
|
a274677f05 | ||
|
|
f51aee1ec5 | ||
|
|
cdf2337346 | ||
|
|
847f4db974 | ||
|
|
969f9a112c | ||
|
|
2b812101be | ||
|
|
7644874b42 | ||
|
|
c512d57df4 | ||
|
|
59945b87f7 | ||
|
|
d23a9b8a16 | ||
|
|
e5040385f5 | ||
|
|
e9efec8694 | ||
|
|
7110ae4427 | ||
|
|
a7ee24662f | ||
|
|
48e85efb88 | ||
|
|
e8391864d7 | ||
|
|
2a2f7cda63 | ||
|
|
ad02b343f2 | ||
|
|
7263641953 | ||
|
|
42840f0f36 | ||
|
|
1a2cc950f0 | ||
|
|
7eb32bc543 | ||
|
|
558c0f8c3b | ||
|
|
230c65a080 | ||
|
|
292e87c0d1 | ||
|
|
6e60b8183a | ||
|
|
289c1e0de6 | ||
|
|
1bb7d508a4 | ||
|
|
2b861ea0af | ||
|
|
a814e85356 | ||
|
|
39d21ca0bd | ||
|
|
480f9c6a9d | ||
|
|
8f1cd35f1c | ||
|
|
b743d15e35 | ||
|
|
9d4cc8e933 | ||
|
|
3efa9d6f41 | ||
|
|
62b55d5b43 | ||
|
|
8b61aaea1a | ||
|
|
2af83ea632 | ||
|
|
b53c7e6879 | ||
|
|
8114508bbb | ||
|
|
8138b402a0 | ||
|
|
864a13460c | ||
|
|
b3a206a28c | ||
|
|
81cb90804e | ||
|
|
5a3972714a | ||
|
|
c1c68054ea | ||
|
|
f3461ce2ba | ||
|
|
e4d9193f06 | ||
|
|
9d5e646e46 | ||
|
|
0111dc2178 | ||
|
|
153a1de043 | ||
|
|
4e914dd8a9 | ||
|
|
67d61a8282 | ||
|
|
dda0d5c763 | ||
|
|
48f677c3a0 | ||
|
|
db5567b5d1 | ||
|
|
8e91bb4297 | ||
|
|
71628471bc | ||
|
|
425256ff3d | ||
|
|
155d21cee7 | ||
|
|
120c8e6002 | ||
|
|
8cf98a7bfd | ||
|
|
8ea299f39d | ||
|
|
f3b013bc0a | ||
|
|
8e2492fc87 | ||
|
|
3231ade38a | ||
|
|
e62b463852 | ||
|
|
c28acf21a1 | ||
|
|
5cc41b3ba4 | ||
|
|
f3c73f1192 | ||
|
|
bb43b5670c | ||
|
|
853e226812 | ||
|
|
a1969efac1 | ||
|
|
44e7da6d56 | ||
|
|
127049d925 | ||
|
|
2e0b1d6f8b | ||
|
|
9875e76419 | ||
|
|
1e508f9168 | ||
|
|
63aabaa6df | ||
|
|
d710a23513 | ||
|
|
c71ccce742 | ||
|
|
abab0e0fd4 | ||
|
|
3499a5eb76 | ||
|
|
b55dada80b | ||
|
|
6310d9e760 | ||
|
|
ebc0ef62e6 | ||
|
|
dd1390a6c2 | ||
|
|
0a269f3298 | ||
|
|
ae9a24f44f | ||
|
|
1843c24cda | ||
|
|
a35ed8f72e | ||
|
|
d2781ef655 | ||
|
|
287a385e63 | ||
|
|
6b77a16f8b | ||
|
|
96c5b4b031 | ||
|
|
ffb1db7d2d | ||
|
|
22fbb408eb | ||
|
|
4299ce8839 | ||
|
|
cfae4a0e10 | ||
|
|
516ee41494 | ||
|
|
c3c41f113e | ||
|
|
1099d663ab | ||
|
|
2c6547635a | ||
|
|
9360c8e9b9 | ||
|
|
5aa7daf708 | ||
|
|
057ded451f | ||
|
|
3062f49370 | ||
|
|
301992b8d8 | ||
|
|
cdf8ec932a | ||
|
|
c6802b5ab3 | ||
|
|
652479d8fa | ||
|
|
153f719e33 | ||
|
|
6b03a1ec8f | ||
|
|
f0d7257374 | ||
|
|
7f8a64dd39 | ||
|
|
6615a248ee | ||
|
|
be28436c2d | ||
|
|
c584795202 | ||
|
|
ddda905487 | ||
|
|
00794d41bc | ||
|
|
b44a5ff2f8 | ||
|
|
b9ad9bcb21 | ||
|
|
ecac1e58e3 | ||
|
|
ddaef0600f | ||
|
|
13d58ace9f | ||
|
|
f48f8aacec | ||
|
|
8a541acd36 | ||
|
|
8a3d55b416 | ||
|
|
53cb2b7bdb | ||
|
|
2a5190ecff | ||
|
|
0721b0fec2 | ||
|
|
f1393ff8a1 | ||
|
|
47a18140c1 | ||
|
|
c20d64587c | ||
|
|
3b866c95e0 | ||
|
|
fc74f87986 | ||
|
|
ee5038e7af | ||
|
|
ad750a13c3 | ||
|
|
b56ffc155e | ||
|
|
4596e27eb0 | ||
|
|
a3eac3a85e | ||
|
|
7d09e34b80 | ||
|
|
3febc7d513 | ||
|
|
309ad1bea7 | ||
|
|
30f2ca3b2e | ||
|
|
4a53f610cd | ||
|
|
e74dff96ba | ||
|
|
9ed4407b20 | ||
|
|
eb913e8bf2 | ||
|
|
17d04779fb | ||
|
|
ab661423ba | ||
|
|
8e77ea68b2 | ||
|
|
b0e672bc9a | ||
|
|
ad76e15949 | ||
|
|
b8735cc893 | ||
|
|
c058b3f9ce | ||
|
|
8f6c61606a | ||
|
|
a81530754b | ||
|
|
1518eb9466 |
+30
-26
@@ -20,7 +20,6 @@ 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
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@@ -31,8 +30,7 @@ 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 [KNF][].
|
The coding style itself is strictly [Linux coding style][KNF].
|
||||||
|
|
||||||
|
|
||||||
Commit Messages
|
Commit Messages
|
||||||
---------------
|
---------------
|
||||||
@@ -41,31 +39,38 @@ 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:
|
||||||
|
|
||||||
git config --global user.name "Jane Doe"
|
```bash
|
||||||
git config --global user.email jane.doe@example.com
|
$ git config --global user.name "Jane Doe"
|
||||||
|
$ 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
|
||||||
---------------
|
---------------
|
||||||
@@ -73,7 +78,6 @@ 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
|
||||||
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
|
[gitbook]: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines
|
||||||
[gitbook]: https://git-scm.com/book/ch5-2.html
|
|
||||||
[conduct]: CODE-OF-CONDUCT.md
|
[conduct]: CODE-OF-CONDUCT.md
|
||||||
|
|||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/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() }
|
||||||
|
'
|
||||||
+108
-6
@@ -18,6 +18,71 @@ 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 }}
|
||||||
@@ -34,8 +99,7 @@ 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
|
sudo apt-get -y install pkg-config tree jq libcap-dev libconfuse-dev libblkid-dev libpam0g-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.1/libuev-2.4.1.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.6.2/libite-2.6.2.tar.gz
|
||||||
tar xf libuev-2.4.1.tar.xz
|
tar xf libuev-2.4.1.tar.xz
|
||||||
@@ -43,7 +107,7 @@ jobs:
|
|||||||
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
||||||
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Static Finit
|
- name: Static Finit
|
||||||
run: |
|
run: |
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
@@ -52,8 +116,7 @@ jobs:
|
|||||||
- 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 --with-watchdog \
|
--enable-x11-common-plugin --enable-testserv-plugin \
|
||||||
--with-keventd --with-libsystemd \
|
|
||||||
CFLAGS="-fsanitize=address -ggdb"
|
CFLAGS="-fsanitize=address -ggdb"
|
||||||
make -j9 clean
|
make -j9 clean
|
||||||
make -j9 V=1
|
make -j9 V=1
|
||||||
@@ -81,7 +144,46 @@ jobs:
|
|||||||
make -j1 check || (cat test/test-suite.log; false)
|
make -j1 check || (cat test/test-suite.log; false)
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
if: always()
|
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)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
coverity:
|
coverity:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- 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@v4
|
- uses: actions/cache@v6
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
path: coverity-latest.tar.gz
|
path: coverity-latest.tar.gz
|
||||||
@@ -55,7 +55,7 @@ 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
|
sudo apt-get -y install pkg-config 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/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
|
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 libuev-2.4.1.tar.xz
|
||||||
@@ -66,8 +66,7 @@ jobs:
|
|||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog \
|
./configure --prefix= --enable-x11-common-plugin
|
||||||
--with-keventd --with-libsystemd
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
export PATH=`pwd`/coverity/bin:$PATH
|
export PATH=`pwd`/coverity/bin:$PATH
|
||||||
@@ -86,7 +85,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@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: coverity-build.log
|
name: coverity-build.log
|
||||||
path: cov-int/build-log.txt
|
path: cov-int/build-log.txt
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ 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]
|
||||||
@@ -15,6 +17,7 @@ on:
|
|||||||
- 'doc/**'
|
- 'doc/**'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
- 'mkdocs.yml'
|
- 'mkdocs.yml'
|
||||||
|
- 'configure.ac'
|
||||||
- '.github/workflows/docs.yml'
|
- '.github/workflows/docs.yml'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -23,14 +26,23 @@ permissions:
|
|||||||
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@v4
|
uses: actions/checkout@v7
|
||||||
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@v4
|
uses: actions/setup-python@v7
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
@@ -46,25 +58,27 @@ jobs:
|
|||||||
run: mkdocs build --clean
|
run: mkdocs build --clean
|
||||||
|
|
||||||
- name: Upload site artifact
|
- name: Upload site artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: site
|
name: site
|
||||||
path: site/
|
path: site/
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.build.outputs.version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download site artifact
|
- name: Download site artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: site
|
name: site
|
||||||
path: site/
|
path: site/
|
||||||
|
|
||||||
- name: Checkout pages repo
|
- name: Checkout pages repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
repository: finit-project/finit-project.github.io
|
repository: finit-project/finit-project.github.io
|
||||||
path: pages
|
path: pages
|
||||||
@@ -79,7 +93,56 @@ jobs:
|
|||||||
|
|
||||||
- name: Sync site to pages repo
|
- name: Sync site to pages repo
|
||||||
run: |
|
run: |
|
||||||
rsync -a --delete --exclude .git site/ pages/
|
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 — 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
|
- name: Commit and push
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ jobs:
|
|||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- 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
|
sudo apt-get -y install pkg-config 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/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
|
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 libuev-2.4.1.tar.xz
|
||||||
@@ -46,8 +46,7 @@ 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 --with-watchdog \
|
--enable-testserv-plugin
|
||||||
--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
|
||||||
@@ -61,17 +60,16 @@ jobs:
|
|||||||
make -j1 release || (cat $dir/test/test-suite.log; false)
|
make -j1 release || (cat $dir/test/test-suite.log; false)
|
||||||
ls -lF ../
|
ls -lF ../
|
||||||
mkdir -p artifacts/
|
mkdir -p artifacts/
|
||||||
mv ../*.tar.* artifacts/
|
mv ../*.tar.gz artifacts/
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: finit-release-test-logs
|
name: finit-release-test-logs
|
||||||
path: finit-*/_build/sub/test/*.log
|
path: finit-*/_build/sub/test/*.log
|
||||||
- name: Extract ChangeLog entry ...
|
- name: Extract ChangeLog entry ...
|
||||||
run: |
|
run: |
|
||||||
awk '/-----*/{if (x == 1) exit; x=1;next}x' doc/ChangeLog.md \
|
.github/scripts/extract-changelog.sh < doc/ChangeLog.md > release.md
|
||||||
|head -n -1 > release.md
|
|
||||||
cat release.md
|
cat release.md
|
||||||
- uses: ncipollo/release-action@v1
|
- uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
run: ${{ steps.schedule.outputs.run }}
|
run: ${{ steps.schedule.outputs.run }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Check for any commits since last week
|
- name: Check for any commits since last week
|
||||||
id: schedule
|
id: schedule
|
||||||
run: |
|
run: |
|
||||||
@@ -34,7 +34,7 @@ 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
|
sudo apt-get -y install pkg-config 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/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
|
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 libuev-2.4.1.tar.xz
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
||||||
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Base run
|
- name: Base run
|
||||||
run: |
|
run: |
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: finit-weekly-test-logs
|
name: finit-weekly-test-logs
|
||||||
path: finit-*/_build/sub/test/*.log
|
path: finit-*/_build/sub/test/*.log
|
||||||
|
|||||||
@@ -52,3 +52,8 @@ 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/
|
||||||
|
|||||||
+29
-16
@@ -1,5 +1,21 @@
|
|||||||
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
|
||||||
@@ -24,23 +40,20 @@ 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 files:"
|
@echo "Resulting release tarball(s): $(DIST_ARCHIVES)"
|
||||||
@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-watchdog --with-keventd --with-fstab=/etc/fstab \
|
--with-fstab=/etc/fstab --with-libsystemd \
|
||||||
--with-libsystemd --with-bash-completion-dir=no
|
--with-bash-completion-dir=no
|
||||||
|
|||||||
+68
-13
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([Finit], [4.16], [https://github.com/finit-project/finit/issues],
|
AC_INIT([Finit], [5.0-rc1], [https://github.com/finit-project/finit/issues],
|
||||||
[finit], [https://http://finit-project.github.io/])
|
[finit], [https://http://finit-project.github.io/])
|
||||||
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])
|
||||||
@@ -12,10 +12,13 @@ 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
|
||||||
@@ -37,7 +40,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])
|
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r close_range])
|
||||||
|
|
||||||
# Check for uint[8,16,32]_t
|
# Check for uint[8,16,32]_t
|
||||||
AC_TYPE_UINT8_T
|
AC_TYPE_UINT8_T
|
||||||
@@ -50,6 +53,12 @@ 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.4.1])
|
||||||
PKG_CHECK_MODULES([lite], [libite >= 2.6.1])
|
PKG_CHECK_MODULES([lite], [libite >= 2.6.1])
|
||||||
|
# 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,
|
||||||
@@ -76,6 +85,11 @@ AC_ARG_ENABLE(libcap,
|
|||||||
AS_HELP_STRING([--disable-libcap], [Disable Linux capabilities support]),,[
|
AS_HELP_STRING([--disable-libcap], [Disable Linux capabilities support]),,[
|
||||||
enable_libcap=yes])
|
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])
|
||||||
@@ -88,6 +102,10 @@ 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])
|
||||||
@@ -105,17 +123,18 @@ 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], [no], [Setup and start system message bus, D-Bus])
|
AC_PLUGIN([dbus], [yes], [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], [yes], [Start udevd or mdev kernel event datamon])
|
AC_PLUGIN([hotplug], [no], [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
|
||||||
@@ -148,7 +167,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 (initctl), default: root]),
|
AS_HELP_STRING([--with-group=NAME], [Group for /run/finit/socket and /run/finit/bus, default: root]),
|
||||||
[group=$withval], [group=root])
|
[group=$withval], [group=root])
|
||||||
|
|
||||||
AC_ARG_WITH(hostname,
|
AC_ARG_WITH(hostname,
|
||||||
@@ -164,17 +183,20 @@ 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, default: no]),, [with_keventd=no])
|
AS_HELP_STRING([--with-keventd], [Enable built-in keventd device manager, default: yes]),, [with_keventd=yes])
|
||||||
|
|
||||||
|
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@:>@], [Enable built-in sulogin, optional USER to request password for (default root), default: no.]),[sulogin=$withval],[with_sulogin=no])
|
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])
|
||||||
|
|
||||||
AC_ARG_WITH(watchdog,
|
AC_ARG_WITH(watchdog,
|
||||||
AS_HELP_STRING([--with-watchdog@<:@=DEV@:>@], [Enable built-in watchdog, default: /dev/watchdog]),
|
AS_HELP_STRING([--with-watchdog@<:@=DEV@:>@], [Built-in watchdog, default: yes (/dev/watchdog)]),
|
||||||
[watchdog=$withval], [with_watchdog=no watchdog=])
|
[watchdog=$withval], [with_watchdog=yes watchdog=yes])
|
||||||
|
|
||||||
AC_ARG_WITH(libsystemd,
|
AC_ARG_WITH(libsystemd,
|
||||||
AS_HELP_STRING([--with-libsystemd], [Build replacement libsystemd library, default: yes]),
|
AS_HELP_STRING([--with-libsystemd], [Build replacement libsystemd library, default: no]),
|
||||||
[with_libsystemd=$withval], [with_libsystemd=no])
|
[with_libsystemd=$withval], [with_libsystemd=no])
|
||||||
|
|
||||||
AC_ARG_WITH(hook-scripts-path,
|
AC_ARG_WITH(hook-scripts-path,
|
||||||
@@ -218,6 +240,26 @@ AS_IF([test "x$enable_libcap" = "xyes"], [
|
|||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|
||||||
|
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])])
|
||||||
|
|
||||||
@@ -236,6 +278,12 @@ 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],
|
||||||
@@ -281,7 +329,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])
|
AC_DEFINE_UNQUOTED(DEFGROUP, "$group", [For /run/finit/socket and /run/finit/bus])
|
||||||
|
|
||||||
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"], [
|
||||||
@@ -311,7 +359,11 @@ 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"], [with_keventd=yes])
|
AS_IF([test "x$with_keventd" != "xno"], [
|
||||||
|
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"], [
|
||||||
@@ -344,6 +396,7 @@ 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"])
|
||||||
@@ -427,13 +480,15 @@ 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
|
Built-in keventd......: $with_keventd (blkid: $blkid_LIBS)
|
||||||
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 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
|
||||||
|
|||||||
@@ -3,24 +3,34 @@
|
|||||||
|
|
||||||
## 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 <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
#task reboot {
|
||||||
|
# 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 <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
#task poweroff {
|
||||||
|
# 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,3 +1,9 @@
|
|||||||
# 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 [2345] <pid/syslogd> log acpid -f -- ACPI daemon
|
service acpid {
|
||||||
|
description = "ACPI daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
log { }
|
||||||
|
command = "acpid -f"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
service [2345] <pid/syslogd> env:-/etc/conf.d/crond crond -f -S $CRON_OPTS -- Cron daemon
|
service crond {
|
||||||
|
description = "Cron daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
envfile = "-/etc/conf.d/crond"
|
||||||
|
command = "crond -f -S $CRON_OPTS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,7 @@
|
|||||||
# Start SSH daemon as soon as basic networking comes up
|
# Start SSH daemon as soon as basic networking comes up
|
||||||
service [2345] env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
|
service dropbear {
|
||||||
|
description = "Dropbear SSH daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
envfile = "-/etc/conf.d/dropbear"
|
||||||
|
command = "dropbear -R -F $DROPBEAR_OPTS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,24 @@
|
|||||||
# Consoles to start getty on when system is up
|
# Consoles to start getty on when system is up
|
||||||
tty [12345] /dev/ttyS0 noclear
|
tty ttyS0 {
|
||||||
tty [12345] /dev/tty1 noclear nowait
|
runlevel = "12345"
|
||||||
tty [2345] /dev/tty2 noclear nowait
|
device = "/dev/ttyS0"
|
||||||
tty [2345] /dev/tty3 noclear nowait
|
noclear = true
|
||||||
|
}
|
||||||
|
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,2 +1,7 @@
|
|||||||
# Oneshot task to run once at bootstrap
|
# Oneshot task to run once at bootstrap
|
||||||
task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
|
task keymap {
|
||||||
|
description = "Loading keymap"
|
||||||
|
runlevel = "S"
|
||||||
|
envfile = "/etc/conf.d/loadkmap"
|
||||||
|
command = "zcat $KEYMAP | loadkmap"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
service [2345] lxdm -- Lightweight Login Manager
|
service lxdm {
|
||||||
|
description = "Lightweight Login Manager"
|
||||||
|
runlevel = "2345"
|
||||||
|
command = "lxdm"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,2 @@
|
|||||||
## Linux kernel modules to load
|
## Linux kernel modules to load
|
||||||
#module fbcon
|
#modules = { "fbcon", "button", "evdev", "mousedev", "8139cp", "softdog" }
|
||||||
#module button
|
|
||||||
#module evdev
|
|
||||||
#module mousedev
|
|
||||||
#module 8139cp
|
|
||||||
#module softdog
|
|
||||||
|
|||||||
@@ -1,2 +1,9 @@
|
|||||||
# 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 [2345] <pid/syslogd> env:-/etc/conf.d/ntpd log ntpd -n $NTPD_OPTS -- NTP daemon
|
service ntpd {
|
||||||
|
description = "NTP daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
envfile = "-/etc/conf.d/ntpd"
|
||||||
|
log { }
|
||||||
|
command = "ntpd -n $NTPD_OPTS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
# 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 [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
|
service syslogd {
|
||||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
description = "Syslog 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 +1,6 @@
|
|||||||
service [S123456789] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog
|
service watchdog {
|
||||||
|
description = "System watchdog"
|
||||||
|
runlevel = "S123456789"
|
||||||
|
envfile = "-/etc/conf.d/watchdog"
|
||||||
|
command = "watchdog $WATCHDOG_OPTS $WATCHDOG_DEV"
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,24 +3,34 @@
|
|||||||
|
|
||||||
## 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 <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
#task reboot {
|
||||||
|
# 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 <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
#task poweroff {
|
||||||
|
# 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,3 +1,10 @@
|
|||||||
# 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 [2345] <pid/syslogd> env:-/etc/default/acpid log acpid -f $OPTIONS -- ACPI daemon
|
service acpid {
|
||||||
|
description = "ACPI daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
envfile = "-/etc/default/acpid"
|
||||||
|
log { }
|
||||||
|
command = "acpid -f $OPTIONS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,8 @@
|
|||||||
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
|
task anacron {
|
||||||
|
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,2 +1,6 @@
|
|||||||
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
|
sysv apparmor {
|
||||||
|
description = "AppArmor initialization"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/etc/init.d/apparmor"
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,6 @@
|
|||||||
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
|
service avahi-daemon {
|
||||||
|
description = "Avahi mDNS/DNS-SD Stack"
|
||||||
|
runlevel = "2345"
|
||||||
|
envfile = "-/etc/default/avahi-daemon"
|
||||||
|
command = "avahi-daemon -s $AVAHI_DAEMON_OPTS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
# 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 [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
|
service bluetoothd {
|
||||||
|
description = "Bluetooth service"
|
||||||
|
runlevel = "2345"
|
||||||
|
envfile = "-/etc/default/bluetooth"
|
||||||
|
log { file = "/dev/null" }
|
||||||
|
command = "/usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
# Oneshot task to run once at bootstrap
|
# Oneshot task to run once at bootstrap
|
||||||
task [S] /lib/console-setup/console-setup.sh -- Setting up console font and keymap
|
task console-setup {
|
||||||
|
description = "Setting up console font and keymap"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/lib/console-setup/console-setup.sh"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
|
service cron {
|
||||||
|
description = "Background job processing daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "sys/pwr/ac", "pid/syslogd" }
|
||||||
|
envfile = "-/etc/default/cron"
|
||||||
|
command = "cron -f $EXTRA_OPTS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
# 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 [2345] <pid/syslogd> /usr/libexec/elogind -- Login manager
|
service elogind {
|
||||||
|
description = "Login manager"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
command = "/usr/libexec/elogind"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,38 @@
|
|||||||
# Consoles to start getty on when system is up
|
# Consoles to start getty on when system is up
|
||||||
tty [12345] /dev/tty1 linux noclear nowait
|
tty tty1 {
|
||||||
tty [2345] /dev/tty2 linux nowait
|
runlevel = "12345"
|
||||||
tty [2345] /dev/tty3 linux nowait
|
device = "/dev/tty1"
|
||||||
tty [2345] /dev/tty4 linux nowait
|
term = "linux"
|
||||||
tty [2345] /dev/tty5 linux nowait
|
noclear = true
|
||||||
tty [2345] /dev/tty6 linux nowait
|
nowait = true
|
||||||
|
}
|
||||||
|
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,2 +1,6 @@
|
|||||||
# Oneshot task to run once at bootstrap
|
# Oneshot task to run once at bootstrap
|
||||||
task [S] /lib/console-setup/keyboard-setup.sh -- Setting up console keyboard layout
|
task keyboard-setup {
|
||||||
|
description = "Setting up console keyboard layout"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/lib/console-setup/keyboard-setup.sh"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
service [3] lightdm -- Light Display Manager
|
service lightdm {
|
||||||
|
description = "Light Display Manager"
|
||||||
|
runlevel = "3"
|
||||||
|
command = "lightdm"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,2 @@
|
|||||||
## Linux kernel modules to load
|
## Linux kernel modules to load
|
||||||
#module fbcon
|
#modules = { "fbcon", "button", "evdev", "loop", "psmouse", "mousedev", "8139cp" }
|
||||||
#module button
|
|
||||||
#module evdev
|
|
||||||
#module loop
|
|
||||||
#module psmouse
|
|
||||||
#module mousedev
|
|
||||||
#module 8139cp
|
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
# Need to stop plymouth, or it'll block stdin on tty1
|
# Need to stop plymouth, or it'll block stdin on tty1
|
||||||
task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen
|
task plymouth {
|
||||||
|
description = "Stopping Plymouth Boot Screen"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/usr/bin/plymouth quit"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
# 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 [S12345] name:syslogd env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_OPTIONS -- Reliable syslog daemon
|
service syslogd {
|
||||||
|
description = "Reliable syslog daemon"
|
||||||
|
runlevel = "S12345"
|
||||||
|
envfile = "-/etc/default/rsyslog"
|
||||||
|
command = "rsyslogd -n $RSYSLOGD_OPTIONS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
# Simple NTP client (SNTP)
|
# Simple NTP client (SNTP)
|
||||||
service [2345] <net/route/default> env:-/etc/default/sntpd sntpd -n $SNTPD_OPTIONS -- SNTP client
|
service sntpd {
|
||||||
|
description = "SNTP client"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "net/route/default" }
|
||||||
|
envfile = "-/etc/default/sntpd"
|
||||||
|
command = "sntpd -n $SNTPD_OPTIONS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
# Start SSH daemon as soon as basic networking comes up
|
# Start SSH daemon as soon as basic networking comes up
|
||||||
service [2345] <net/lo/up> env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
|
service sshd {
|
||||||
|
description = "OpenSSH daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "net/lo/up" }
|
||||||
|
envfile = "-/etc/default/ssh"
|
||||||
|
command = "/usr/sbin/sshd -D $SSHD_OPTS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,7 @@
|
|||||||
# Start sysklogd as soon as possible, should always run if enabled
|
# Start sysklogd as soon as possible, should always run if enabled
|
||||||
service [S123456789] env:-/etc/default/syslogd syslogd -F $SYSLOGD_OPTIONS -- System logging daemon
|
service syslogd {
|
||||||
|
description = "System logging daemon"
|
||||||
|
runlevel = "S123456789"
|
||||||
|
envfile = "-/etc/default/syslogd"
|
||||||
|
command = "syslogd -F $SYSLOGD_OPTIONS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
# 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 [3] xdm -nodaemon -- X Display Manager
|
service xdm {
|
||||||
|
description = "X Display Manager"
|
||||||
|
runlevel = "3"
|
||||||
|
command = "xdm -nodaemon"
|
||||||
|
}
|
||||||
|
|||||||
+107
-43
@@ -1,85 +1,149 @@
|
|||||||
# Sample finit.conf to demonstrate the syntax
|
# Sample finit.conf to demonstrate the syntax
|
||||||
#
|
#
|
||||||
host testbed
|
hostname = "testbed"
|
||||||
|
|
||||||
# Linux kernel modules to load
|
# Linux kernel modules to load
|
||||||
module button
|
modules = { "button", "evdev", "loop", "psmouse" }
|
||||||
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 size:100k count:4
|
log {
|
||||||
|
size = 100k
|
||||||
|
count = 4
|
||||||
|
}
|
||||||
|
|
||||||
# Virtual consoles to start getty on
|
# Virtual consoles to start getty on
|
||||||
tty /dev/tty1
|
tty tty1 { device = "/dev/tty1" }
|
||||||
tty /dev/tty2
|
tty tty2 { device = "/dev/tty2" }
|
||||||
tty /dev/tty3
|
tty tty3 { device = "/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 <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
task reboot {
|
||||||
|
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 <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
task poweroff {
|
||||||
|
description = "Shutting down system"
|
||||||
|
runlevel = "12345789"
|
||||||
|
conditions = { "sys/pwr/fail" }
|
||||||
|
command = "initctl poweroff"
|
||||||
|
}
|
||||||
|
|
||||||
# Alternative method instead of runparts
|
# Alternative method instead of runparts
|
||||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
#task keyboard-setup {
|
||||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
# description = "Setting up preliminary keymap"
|
||||||
#task [S] /etc/init.d/kbd start -- Preparing console
|
# runlevel = "S"
|
||||||
#run [2] /etc/init.d/networking start -- Start networking
|
# command = "/etc/init.d/keyboard-setup start"
|
||||||
|
#}
|
||||||
|
#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
|
||||||
|
|
||||||
# klgod and syslogd are placed in the maint cgroup
|
# klogd and syslogd are placed in the maint cgroup. There is no
|
||||||
cgroup.maint
|
# "current cgroup" to switch in this format, every block names its own.
|
||||||
service [2345] <pid/syslogd> klogd -n -- Kernel logging server
|
service klogd {
|
||||||
service [2345] syslogd -n -- Syslog server
|
description = "Kernel logging server"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
cgroup maint {}
|
||||||
|
command = "klogd -n"
|
||||||
|
}
|
||||||
|
service syslogd {
|
||||||
|
description = "Syslog server"
|
||||||
|
runlevel = "2345"
|
||||||
|
cgroup maint {}
|
||||||
|
command = "syslogd -n"
|
||||||
|
}
|
||||||
|
|
||||||
# gdm in the system cgroup (default when read from it's own .conf file)
|
# gdm gets its own limits inside the system group, which is where
|
||||||
cgroup.system
|
# services end up when no cgroup block is given.
|
||||||
service [3] cgroup:cpu.weight:250,mem.max:10M gdm -- GNOME Display Manager
|
service gdm {
|
||||||
|
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 for services is system
|
# to user cgroup with cpu.weight 250. Default weight in the user group
|
||||||
# group. Default weight in user group is 100 (above). Max length of
|
# is 100 (above).
|
||||||
# cgroup argument is (currently) 63 chars.
|
service sshd {
|
||||||
#
|
description = "OpenSSH daemon"
|
||||||
# Notice alternative syntax where current cgroup is only set for this
|
runlevel = "2345"
|
||||||
# particular service.
|
envfile = "-/etc/default/ssh"
|
||||||
service [2345] cgroup.user:cpu.weight:250,mem.max:10M env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
|
cgroup user {
|
||||||
|
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. A single
|
# a dependency on a system default route (gateway) to be set. ntpd
|
||||||
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
# does not respect SIGHUP, so Finit restarts it on reload instead.
|
||||||
service [2345] <!net/route/default> log:/var/log/ntpd.log ntpd -n -l -I eth0 -- NTP daemon
|
service ntpd {
|
||||||
|
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 somewhere between
|
# For multiple instances of the same service, add :ID to the block title.
|
||||||
# the service/run/task keyword and the command.
|
service httpd:80 {
|
||||||
service :80 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
|
description = "Web server"
|
||||||
service :8080 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
|
runlevel = "2345"
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|||||||
+16
-6
@@ -3,24 +3,34 @@
|
|||||||
|
|
||||||
## 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 <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
#task reboot {
|
||||||
|
# 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 <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
#task poweroff {
|
||||||
|
# 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,3 +1,7 @@
|
|||||||
# 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 [2345] acpid -f -- ACPI daemon
|
service acpid {
|
||||||
|
description = "ACPI daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
command = "acpid -f"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,6 @@
|
|||||||
service [2345] <pid/syslogd> crond -f -S -c /etc/crontabs -- Cron daemon
|
service crond {
|
||||||
|
description = "Cron daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
command = "crond -f -S -c /etc/crontabs"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
service [2345] dhcpcd -B -M -- DHCP client
|
service dhcpcd {
|
||||||
|
description = "DHCP client"
|
||||||
|
runlevel = "2345"
|
||||||
|
command = "dhcpcd -B -M"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
service [S12345] dmeventd -f -- Device mapper event daemon
|
service dmeventd {
|
||||||
|
description = "Device mapper event daemon"
|
||||||
|
runlevel = "S12345"
|
||||||
|
command = "dmeventd -f"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,19 @@
|
|||||||
# 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 [12345] /dev/tty1 noclear nowait
|
tty tty1 {
|
||||||
tty [2345] /dev/tty2 noclear nowait
|
runlevel = "12345"
|
||||||
tty [2345] /dev/tty3 noclear nowait
|
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,2 +1,6 @@
|
|||||||
# Oneshot task to run once at bootstrap
|
# Oneshot task to run once at bootstrap
|
||||||
task [S] zcat /etc/keymap/se.bmap.gz | loadkmap -- Loading keymap
|
task keymap {
|
||||||
|
description = "Loading keymap"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "zcat /etc/keymap/se.bmap.gz | loadkmap"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
service [2345] lxdm -- Lightweight Login Manager
|
service lxdm {
|
||||||
|
description = "Lightweight Login Manager"
|
||||||
|
runlevel = "2345"
|
||||||
|
command = "lxdm"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
## Linux kernel modules to load
|
## Linux kernel modules to load
|
||||||
#module fbcon
|
# Other candidates: fbcon, button, evdev, mousedev, 8139cp
|
||||||
#module button
|
modules = { "softdog" }
|
||||||
#module evdev
|
|
||||||
#module mousedev
|
|
||||||
#module 8139cp
|
|
||||||
module softdog
|
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
# 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 [2345] <pid/syslogd> log ntpd -n -N -p pool.ntp.org -- NTP daemon
|
service ntpd {
|
||||||
|
description = "NTP daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
log { }
|
||||||
|
command = "ntpd -n -N -p pool.ntp.org"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
# Start SSH daemon as soon as basic networking comes up
|
# Start SSH daemon as soon as basic networking comes up
|
||||||
service [2345] /usr/sbin/sshd -D -- OpenSSH daemon
|
service sshd {
|
||||||
|
description = "OpenSSH daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
command = "/usr/sbin/sshd -D"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
# 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 [S12345] syslogd -n -- Syslog daemon
|
service syslogd {
|
||||||
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
description = "Syslog daemon"
|
||||||
|
runlevel = "S12345"
|
||||||
|
command = "syslogd -n"
|
||||||
|
}
|
||||||
|
service klogd {
|
||||||
|
description = "Kernel log daemon"
|
||||||
|
runlevel = "S12345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
command = "klogd -n"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
service @uuidd:uuidd [2345] uuidd -F -- UUID generation daemon
|
service uuidd {
|
||||||
|
description = "UUID generation daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
user = "uuidd"
|
||||||
|
group = "uuidd"
|
||||||
|
command = "uuidd -F"
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/Makefile
|
||||||
|
/Makefile.in
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
EXTRA_DIST = org.finit.conf
|
||||||
|
|
||||||
|
if DBUS
|
||||||
|
dbuspolicydir = $(datadir)/dbus-1/system.d
|
||||||
|
dist_dbuspolicy_DATA = org.finit.conf
|
||||||
|
endif
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?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>
|
||||||
+217
-1
@@ -3,6 +3,221 @@ 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
|
[4.16][] - 2026-02-27
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
@@ -1968,7 +2183,8 @@ Major bug fix release.
|
|||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.15...HEAD
|
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.17...HEAD
|
||||||
|
[4.17]: https://github.com/finit-project/finit/compare/4.16...4.17
|
||||||
[4.16]: https://github.com/finit-project/finit/compare/4.15...4.16
|
[4.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.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.14]: https://github.com/finit-project/finit/compare/4.13...4.14
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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 \
|
||||||
|
|||||||
+118
-8
@@ -2,10 +2,23 @@ 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 two external libraries:
|
optional plugins to enable. It depends on three 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
|
||||||
@@ -22,6 +35,8 @@ 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
|
||||||
@@ -44,21 +59,56 @@ 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.
|
||||||
|
|
||||||
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
|
* `--disable-dbus-plugin`: Drop the `dbus.so` plugin, which launches
|
||||||
|
`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.
|
||||||
|
|
||||||
* `--with-sulogin`: Enable bundled `sulogin` program. Default is to use the
|
* `--without-keventd`: Drop the bundled device manager, for systems that
|
||||||
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
|
run mdev, mdevd, or udevd instead. Enabled by default, and the only
|
||||||
login if the `root` user is disabled or has no password at all.
|
thing that pulls in libblkid. See [Bundled Device Manager](keventd.md).
|
||||||
|
|
||||||
|
* `--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>
|
||||||
|
|
||||||
@@ -85,8 +135,7 @@ Then configure, build and install:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ./configure --prefix=/usr --exec-prefix= \
|
$ ./configure --prefix=/usr --exec-prefix= \
|
||||||
--sysconfdir=/etc --localstatedir=/var \
|
--sysconfdir=/etc --localstatedir=/var
|
||||||
--with-keventd --with-watchdog
|
|
||||||
$ make
|
$ make
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
@@ -114,6 +163,60 @@ 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
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -171,7 +274,11 @@ 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 [12345789] notty noclear
|
tty board {
|
||||||
|
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
|
||||||
@@ -189,3 +296,6 @@ it only for debugging start up issues when Finit crashes.
|
|||||||
[1]: https://github.com/finit-project/finit/releases/download/4.3/finit-4.3.tar.gz
|
[1]: https://github.com/finit-project/finit/releases/download/4.3/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
|
||||||
|
|||||||
+4
-3
@@ -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
|
||||||
directive `rcsd /path/to/finit.d` to override the default rcS.d
|
setting `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,8 +104,9 @@ 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 `[1]` will be started, so a `tty [1] @console nologin`
|
and TTYs in runlevel 1 will be started, so a `tty` block with
|
||||||
configuration presents you with a root console without login.
|
`runlevel = "1"`, `device = "@console"`, and `nologin = true`
|
||||||
|
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
|
||||||
|
|||||||
+97
-43
@@ -7,19 +7,27 @@ 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"*
|
||||||
|
|
||||||
Conditions are similar in syntax to declaring runlevels per service.
|
A condition is named in the `conditions` list of a `service`, `task`, or
|
||||||
They are specified within angle brackets `<>` and can be applied to any
|
`run` block. The list may hold several, and they are logically AND'ed
|
||||||
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.
|
service to run. In running text, and in `initctl` output, a condition
|
||||||
|
is written inside angle brackets, `<pid/syslogd>`.
|
||||||
|
|
||||||
Two special prefixes can be used inside the angle brackets:
|
One prefix can be used on a condition:
|
||||||
|
|
||||||
- `!` -- service does not support `SIGHUP` (noreload), or run/task
|
|
||||||
should not block runlevel changes (i.e., bootstrap)
|
|
||||||
- `~` -- propagate reload from this dependency, see below
|
- `~` -- propagate reload from this dependency, see below
|
||||||
|
|
||||||
|
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
|
||||||
checking SSH host keys or loading keymap. However, for conditions
|
checking SSH host keys or loading keymap. However, for conditions
|
||||||
@@ -27,7 +35,12 @@ that are unlikely to happen it is not. (See example below.)
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
service [2345] <pid/setupd,pid/zebra> /sbin/netd -- Network monitor
|
service netd {
|
||||||
|
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
|
||||||
@@ -42,15 +55,28 @@ 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 [2345789] <!> env:-/etc/default/dropbear dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
|
service dropbear {
|
||||||
|
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 [S0123456789] <!sys/pwr/fail> name:pwrfail initctl poweroff -- Power failure, shutting down
|
task pwrfail {
|
||||||
|
description = "Power failure, shutting down"
|
||||||
|
runlevel = "S0123456789"
|
||||||
|
conditions = { "sys/pwr/fail" }
|
||||||
|
required = false
|
||||||
|
command = "initctl poweroff"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Propagating Reload in Dependencies
|
Propagating Reload in Dependencies
|
||||||
@@ -65,13 +91,23 @@ not care if it reloads its config.
|
|||||||
For services that need to react when their upstream reloads, the `~`
|
For services that need to react when their upstream reloads, the `~`
|
||||||
prefix propagates the reload from the dependency:
|
prefix propagates the reload from the dependency:
|
||||||
|
|
||||||
service <pid/svc_a> name:svc_b /sbin/svc_b -- Needs A (barrier)
|
service svc_b {
|
||||||
service <!~pid/svc_b> name:svc_c /sbin/svc_c -- Propagate reload from 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`.
|
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 `!`,
|
When `svc_b` reloads, `svc_c` will be restarted, because of
|
||||||
noreload) instead of merely resumed. If `svc_c` supported `SIGHUP`
|
`reload-signal = "none"`, instead of merely resumed. If `svc_c`
|
||||||
(no `!` prefix), it would be sent `SIGHUP` instead.
|
supported `SIGHUP`, it would be sent `SIGHUP` instead.
|
||||||
|
|
||||||
This is similar to systemd's `PropagatesReloadTo=` directive, but
|
This is similar to systemd's `PropagatesReloadTo=` directive, but
|
||||||
declared on the consumer side rather than the provider side.
|
declared on the consumer side rather than the provider side.
|
||||||
@@ -103,7 +139,12 @@ services, or run/task jobs, on external site-dependent stimuli.
|
|||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
|
service 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`,
|
||||||
@@ -126,8 +167,9 @@ 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, use
|
> by Finit when they instead exit on the signal. For such services,
|
||||||
> `<!>` to tell Finit the service does not support `SIGHUP`.
|
> set `reload-signal = "none"` to tell Finit the service does not
|
||||||
|
> support `SIGHUP`.
|
||||||
|
|
||||||
|
|
||||||
Built-in Conditions
|
Built-in Conditions
|
||||||
@@ -135,25 +177,35 @@ Built-in Conditions
|
|||||||
|
|
||||||
Finit comes with a set of plugins for conditions:
|
Finit comes with a set of plugins for conditions:
|
||||||
|
|
||||||
- `devmon` (built-in)
|
- `keventd`: provides `<dev/...>`, `<class/...>`, `<driver/...>`, and
|
||||||
- `netlink`
|
`<sys/pwr/...>`
|
||||||
- `pidfile`
|
- `devmon` (built-in fallback for `<dev/...>` without keventd)
|
||||||
|
- `netlink`: provides `<net/...>`
|
||||||
|
- `pidfile`: provides `<pid/...>`
|
||||||
- `sys`
|
- `sys`
|
||||||
- `usr`
|
- `usr`
|
||||||
|
|
||||||
The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device
|
The `dev/` conditions are provided by `keventd`, the built-in device
|
||||||
nodes being created and removed. It is active only when a run, task, or
|
manager. When keventd creates a device node in `/dev`, it also asserts
|
||||||
service has declared a `<dev/foo>` or `<dev/dir/bar>` condition.
|
the corresponding `dev/` condition. When a device is removed, the
|
||||||
|
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.
|
||||||
|
|
||||||
The `pidfile` plugin (recursively) watches `/run/` (recursively) for PID
|
keventd also asserts `class/<subsystem>/<name>` for devices that have
|
||||||
files created by the monitored services, and sets a corresponding
|
no `/dev` node, e.g. LEDs and DSA switch ports, and `driver/<name>`
|
||||||
condition in the `pid/` namespace.
|
while a driver is bound to at least one device. See
|
||||||
|
[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 monitor are passive condition monitors where
|
The `sys` and `usr` plugins 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.
|
||||||
|
|
||||||
@@ -188,6 +240,8 @@ 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`.
|
||||||
@@ -199,16 +253,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 `name:` and `:id`. By default the basename of
|
composed from a service's block title and its `:id`. By default the
|
||||||
the daemon and the empty string.
|
basename of the daemon and the empty string.
|
||||||
|
|
||||||
| **service** | **condition** |
|
| **service** | **condition** |
|
||||||
|----------------------------------------------------|------------------|
|
|-------------------------------------------------------------------|------------------|
|
||||||
| /sbin/foo | pid/foo |
|
| `service { command = "/sbin/foo" }` | pid/foo |
|
||||||
| /sbin/bar -p /run/baz.pid | pid/bar |
|
| `service { command = "/sbin/bar -p /run/baz.pid" }` | pid/bar |
|
||||||
| name:lxc :foo lxc-start -n foo -p /run/lxc/foo.pid | pid/lxc:foo |
|
| `service lxc:foo { command = "lxc-start -n foo -p /run/lxc/foo.pid" }` | pid/lxc:foo |
|
||||||
| /usr/bin/dbus-daemon | pid/dbus-daemon |
|
| `service { command = "/usr/bin/dbus-daemon" }` | pid/dbus-daemon |
|
||||||
| :222 dropbear -p 222 | pid/dropbear:222 |
|
| `service dropbear:222 { command = "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`,
|
||||||
@@ -223,8 +277,8 @@ its conditions are cleared and reasserted, ensuring dependent services
|
|||||||
are properly updated.
|
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 using the `pid:/path/to/file.pid` syntax in
|
can be worked around by setting `pidfile` and `pidfile-create` in the
|
||||||
the service stanza for the daemon. It is far from optimal since any
|
service block 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.
|
||||||
|
|
||||||
@@ -307,8 +361,8 @@ restarts that would otherwise occur because a depending service was sent
|
|||||||
|
|
||||||
Services with the `~` prefix are an exception to this rule: when their
|
Services with the `~` prefix are an exception to this rule: when their
|
||||||
conditions return to `on` after being in `flux`, the reload is propagated
|
conditions return to `on` after being in `flux`, the reload is propagated
|
||||||
-- the service is reloaded (SIGHUP) or restarted (noreload `!`) instead
|
-- the service is reloaded (SIGHUP), or restarted if it has
|
||||||
of simply being resumed.
|
`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
|
||||||
|
|||||||
+50
-32
@@ -16,14 +16,17 @@ which is the same approach used by other modern service managers like dinit.
|
|||||||
|
|
||||||
## Basic Usage
|
## Basic Usage
|
||||||
|
|
||||||
Capabilities are specified using the `caps:` directive in service configuration:
|
Capabilities are specified with the `capabilities` key, alias `caps`:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
service [2345] name:nginx \
|
service nginx {
|
||||||
@www-data:www-data \
|
description = "Web server"
|
||||||
caps:^cap_net_bind_service \
|
runlevel = "2345"
|
||||||
/usr/sbin/nginx -g 'daemon off;' \
|
user = "www-data"
|
||||||
-- Web server
|
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
|
This example allows nginx to bind to privileged ports (like 80 and 443) while
|
||||||
@@ -52,7 +55,7 @@ with the following prefixes:
|
|||||||
Multiple capabilities can be specified as a comma-separated list:
|
Multiple capabilities can be specified as a comma-separated list:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
caps:^cap_net_raw,^cap_net_admin,^cap_net_bind_service
|
capabilities = { "^cap_net_raw", "^cap_net_admin", "^cap_net_bind_service" }
|
||||||
```
|
```
|
||||||
|
|
||||||
## Common Use Cases
|
## Common Use Cases
|
||||||
@@ -62,10 +65,13 @@ caps:^cap_net_raw,^cap_net_admin,^cap_net_bind_service
|
|||||||
Allow a web server to bind to ports 80 and 443 without running as root:
|
Allow a web server to bind to ports 80 and 443 without running as root:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
service [2345] name:webserver \
|
service webserver {
|
||||||
@www-data:www-data \
|
runlevel = "2345"
|
||||||
caps:^cap_net_bind_service \
|
user = "www-data"
|
||||||
/usr/sbin/nginx -g 'daemon off;'
|
group = "www-data"
|
||||||
|
capabilities = { "^cap_net_bind_service" }
|
||||||
|
command = "/usr/sbin/nginx -g 'daemon off;'"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Network Monitoring (Raw Sockets)
|
### Network Monitoring (Raw Sockets)
|
||||||
@@ -73,10 +79,12 @@ service [2345] name:webserver \
|
|||||||
Allow packet capture without root privileges:
|
Allow packet capture without root privileges:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
service [2345] name:tcpdump \
|
service tcpdump {
|
||||||
@tcpdump \
|
runlevel = "2345"
|
||||||
caps:^cap_net_raw,^cap_net_admin \
|
user = "tcpdump"
|
||||||
/usr/sbin/tcpdump -i eth0 -w /var/log/capture.pcap
|
capabilities = { "^cap_net_raw", "^cap_net_admin" }
|
||||||
|
command = "/usr/sbin/tcpdump -i eth0 -w /var/log/capture.pcap"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### NTP Daemon (System Time)
|
### NTP Daemon (System Time)
|
||||||
@@ -84,10 +92,12 @@ service [2345] name:tcpdump \
|
|||||||
Allow time synchronization without full root:
|
Allow time synchronization without full root:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
service [2345] name:ntpd \
|
service ntpd {
|
||||||
@ntp \
|
runlevel = "2345"
|
||||||
caps:^cap_sys_time,^cap_sys_nice \
|
user = "ntp"
|
||||||
/usr/sbin/ntpd -n
|
capabilities = { "^cap_sys_time", "^cap_sys_nice" }
|
||||||
|
command = "/usr/sbin/ntpd -n"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Available Capabilities
|
## Available Capabilities
|
||||||
@@ -116,9 +126,9 @@ Common capabilities include (see `man 7 capabilities` for the complete list):
|
|||||||
- Don't grant `cap_sys_admin` unless absolutely necessary
|
- Don't grant `cap_sys_admin` unless absolutely necessary
|
||||||
|
|
||||||
2. **Specify a user (preferably non-root)**
|
2. **Specify a user (preferably non-root)**
|
||||||
- The `@user` directive is **required** for `caps:` to take effect
|
- The `user` setting is **required** for `capabilities` to take effect
|
||||||
- For ambient capabilities (`^`), use a non-root user (not `@root`)
|
- For ambient capabilities (`^`), use a non-root user (not `"root"`)
|
||||||
- Example: `@www-data`, `@nginx`, `@tcpdump`
|
- Example: `user = "www-data"`, `user = "nginx"`, `user = "tcpdump"`
|
||||||
|
|
||||||
3. **Use ambient capabilities (`^`)**
|
3. **Use ambient capabilities (`^`)**
|
||||||
- The `^` prefix ensures capabilities survive exec()
|
- The `^` prefix ensures capabilities survive exec()
|
||||||
@@ -159,21 +169,29 @@ ps -o user,pid,cmd -p $(pidof nginx)
|
|||||||
|
|
||||||
- Linux kernel 4.3+ (for ambient capabilities support)
|
- Linux kernel 4.3+ (for ambient capabilities support)
|
||||||
- libcap library installed
|
- libcap library installed
|
||||||
- Finit built with `--enable-libcap`
|
- Finit built with `--enable-libcap`, otherwise a `capabilities` list is
|
||||||
|
ignored, with a warning
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
- The `caps:` directive requires `@user` to be specified for it to take effect
|
- `capabilities` requires `user` to be set for it to take effect
|
||||||
- Without `@user`, the service runs as root with full capabilities and
|
- Without `user`, the service runs as root with full capabilities and
|
||||||
the `caps:` configuration is silently ignored
|
the `capabilities` list is silently ignored
|
||||||
- You can use `@root` with `caps:`, but see below about ambient capabilities
|
- You can use `user = "root"`, but see below about ambient capabilities
|
||||||
- For ambient capabilities (`^`, recommended), the user **must be non-root**
|
- For ambient capabilities (`^`, recommended), the user **must be non-root**
|
||||||
- Using `@root` with `caps:^...` will not work effectively, as ambient
|
- Using `user = "root"` with `^` capabilities will not work effectively, as ambient
|
||||||
capabilities are only added to the effective set when euid ≠ 0
|
capabilities are only added to the effective set when euid ≠ 0
|
||||||
- Use inheritable (`%`) or bounding (`!`) capabilities with `@root` if needed
|
- Use inheritable (`%`) or bounding (`!`) capabilities with `user = "root"` if needed
|
||||||
- Services without `caps:` use standard privilege dropping:
|
- Finit warns about this when reading the .conf file:
|
||||||
- Services with `@user` (non-root) have no special capabilities
|
|
||||||
- Services without `@user` run as root with full capabilities
|
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
|
- Some very old binaries may not work correctly with ambient capabilities
|
||||||
- File system capabilities are not managed by Finit (use `setcap` for that)
|
- File system capabilities are not managed by Finit (use `setcap` for that)
|
||||||
|
|
||||||
|
|||||||
+129
-100
@@ -1,19 +1,17 @@
|
|||||||
Finit provides three different cgroup directives for controlling resource allocation:
|
Finit has two `cgroup` blocks for controlling resource allocation:
|
||||||
|
|
||||||
1. **Top-level cgroup definition**: `cgroup NAME settings`
|
1. **Top-level definition**, at file scope: declares a group such as
|
||||||
- Defines a top-level cgroup (e.g., `init`, `system`, `user`) with default settings
|
`init`, `system`, or `user`, and its default settings.
|
||||||
- Space-separated syntax
|
|
||||||
- Example: `cgroup system cpu.weight:9700`
|
|
||||||
|
|
||||||
2. **Global cgroup selector**: `cgroup.NAME[,options]` (standalone directive)
|
cgroup system { cpu.weight = 9700 }
|
||||||
- Sets the default cgroup for subsequent services in a `.conf` file
|
|
||||||
- Dot-separated with optional comma-separated options
|
|
||||||
- Example: `cgroup.maint` or `cgroup.system,delegate`
|
|
||||||
|
|
||||||
3. **Per-service cgroup option**: `cgroup.NAME[,options]` or `cgroup:options`
|
2. **Joining a group**, inside a service block: names the group this
|
||||||
- Overrides the cgroup for a specific service
|
service runs in, and may override settings for itself alone.
|
||||||
- Part of the service directive line
|
|
||||||
- Example: `service [...] cgroup.maint,mem.max:1G /path/to/cmd`
|
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
|
||||||
@@ -23,83 +21,74 @@ Finit provides three different cgroup directives for controlling resource alloca
|
|||||||
Top-level Cgroup Definition
|
Top-level Cgroup Definition
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
**Syntax:** `cgroup NAME settings`
|
**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 PID 1 itself and its closest relatives.
|
`init/` cgroup is reserved for the early boot helpers Finit drives
|
||||||
|
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.
|
||||||
|
|
||||||
Global Cgroup Selector
|
Joining a Cgroup
|
||||||
----------------------
|
----------------
|
||||||
|
|
||||||
**Syntax:** `cgroup.NAME[,options]` (standalone directive)
|
**Syntax:** `cgroup NAME { settings }` (inside a service block)
|
||||||
|
|
||||||
To select a different top-level cgroup, e.g. `maint/`, for a group of
|
Every block says which group it joins, by name. An empty block joins
|
||||||
run/task/service directives in a `.conf` file, use the `cgroup.NAME`
|
without changing anything:
|
||||||
directive as a standalone line:
|
|
||||||
|
|
||||||
cgroup.maint
|
service foo {
|
||||||
service [...] <...> /path/to/foo args -- description
|
cgroup maint {}
|
||||||
service [...] <...> /path/to/bar args -- description
|
command = "/path/to/foo args"
|
||||||
|
}
|
||||||
|
|
||||||
Both services will run in the `maint/` cgroup.
|
service bar {
|
||||||
|
cgroup maint {}
|
||||||
|
command = "/path/to/bar args"
|
||||||
|
}
|
||||||
|
|
||||||
You can also include options with the global selector:
|
Both services run in the `maint/` cgroup. Settings inside the block
|
||||||
|
apply to that service alone:
|
||||||
|
|
||||||
cgroup.system,delegate
|
service foo {
|
||||||
service [...] <...> /path/to/foo args -- description
|
cgroup maint {
|
||||||
|
cpu.max = 10000
|
||||||
|
memory.max = 655360
|
||||||
|
}
|
||||||
|
command = "/path/to/foo args"
|
||||||
|
}
|
||||||
|
|
||||||
Per-Service Cgroup Option
|
> [!NOTE]
|
||||||
--------------------------
|
> The legacy format has a standalone `cgroup.NAME` line that selects a
|
||||||
|
> group for every stanza following it in the file, and a `cgroup:options`
|
||||||
|
> form that applies settings to whichever group is current without
|
||||||
|
> naming it. Neither has an equivalent here, by design: a block that
|
||||||
|
> joins a group says so itself, so the group cannot depend on what came
|
||||||
|
> earlier in the file.
|
||||||
|
|
||||||
**Syntax:** `cgroup.NAME[,options]` or `cgroup:options` (service option)
|
Every cgroup setting maps directly to cgroup v2 syntax, so `cpu.max`
|
||||||
|
maps to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no
|
||||||
To override the cgroup for a specific service, use the `cgroup.NAME`
|
filtering, the one exception being the shorthand `mem.`, which expands
|
||||||
option within the service directive:
|
to `memory.`. If the file is not available, either the controller is
|
||||||
|
missing from your Linux kernel, or the name is misspelled.
|
||||||
service [...] <...> cgroup.maint /path/to/foo args -- description
|
|
||||||
|
|
||||||
This form also allows per-service limits. Two syntaxes are supported:
|
|
||||||
|
|
||||||
**New comma-separated syntax (recommended):**
|
|
||||||
|
|
||||||
service [...] <...> cgroup.maint,cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
|
||||||
|
|
||||||
**Old colon-separated syntax (legacy):**
|
|
||||||
|
|
||||||
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
|
||||||
|
|
||||||
You can also apply options to the current default cgroup (without changing it)
|
|
||||||
using the `cgroup:options` syntax:
|
|
||||||
|
|
||||||
service [...] <...> cgroup:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
|
||||||
|
|
||||||
Both syntaxes work identically. The new comma-separated syntax is recommended
|
|
||||||
as it's more consistent with other option parsing in Finit.
|
|
||||||
|
|
||||||
Note the `mem.` exception to the rule: every cgroup setting maps directly to
|
|
||||||
cgroup v2 syntax. I.e., `cpu.max` maps to the file `/sys/fs/cgroup/maint/foo/cpu.max`.
|
|
||||||
There is no filtering, except for expanding the shorthand `mem.` to `memory.`.
|
|
||||||
If the file is not available, either the cgroup controller is not available
|
|
||||||
in your Linux kernel, or the name is misspelled.
|
|
||||||
|
|
||||||
### Overriding Cgroup Leaf Names
|
### Overriding Cgroup Leaf Names
|
||||||
|
|
||||||
@@ -108,40 +97,66 @@ configuration filename (without the `.conf` extension). For example, a
|
|||||||
service defined in `system/10-hotplug.conf` would create a cgroup at
|
service defined in `system/10-hotplug.conf` would create a cgroup at
|
||||||
`/sys/fs/cgroup/system/10-hotplug/` by default.
|
`/sys/fs/cgroup/system/10-hotplug/` by default.
|
||||||
|
|
||||||
To use a more descriptive name (recommended for clarity), you can specify
|
To use a more descriptive name (recommended for clarity), set `name`
|
||||||
`name:` in the cgroup directive:
|
inside the cgroup block:
|
||||||
|
|
||||||
service [...] <...> cgroup.system,name:udevd /lib/systemd/systemd-udevd -- Device event daemon
|
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.
|
This creates the cgroup at `/sys/fs/cgroup/system/udevd/` instead.
|
||||||
|
|
||||||
The syntax supports combining the name override with other options:
|
`name` combines with any other setting:
|
||||||
|
|
||||||
service [...] <...> cgroup.system,name:udevd,cpu.max:10000 /lib/systemd/systemd-udevd -- Device event daemon
|
service udevd {
|
||||||
|
description = "Device event daemon"
|
||||||
|
cgroup system {
|
||||||
|
name = "udevd"
|
||||||
|
cpu.max = 10000
|
||||||
|
}
|
||||||
|
command = "/lib/systemd/systemd-udevd"
|
||||||
|
}
|
||||||
|
|
||||||
Or with delegation:
|
Or with delegation:
|
||||||
|
|
||||||
service [2345] @podman:podman \
|
service podman {
|
||||||
cgroup.containers,name:podman,delegate,mem.max:4G \
|
description = "Podman API"
|
||||||
/usr/bin/podman system service -- 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.
|
A daemon using `SCHED_RR` currently needs to run outside the default
|
||||||
|
cgroups.
|
||||||
|
|
||||||
service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process
|
service rt {
|
||||||
|
description = "Real-Time process"
|
||||||
|
cgroup root {}
|
||||||
|
command = "/path/to/daemon arg"
|
||||||
|
}
|
||||||
|
|
||||||
Cgroup Delegation
|
Cgroup Delegation
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
For services that need to create their own child cgroups (container runtimes
|
For services that need to create their own child cgroups (container runtimes
|
||||||
like Docker, Podman, systemd-nspawn, LXC), use the `delegate` option:
|
like Docker, Podman, systemd-nspawn, LXC), set `delegate`:
|
||||||
|
|
||||||
service [2345] @dockerd:dockerd \
|
service dockerd {
|
||||||
cgroup.system,delegate /usr/bin/dockerd -- Docker daemon
|
description = "Docker daemon"
|
||||||
|
runlevel = "2345"
|
||||||
Or with the old colon syntax:
|
user = "dockerd"
|
||||||
|
group = "dockerd"
|
||||||
service [2345] @dockerd:dockerd \
|
cgroup system { delegate = true }
|
||||||
cgroup.system:delegate /usr/bin/dockerd -- Docker daemon
|
command = "/usr/bin/dockerd"
|
||||||
|
}
|
||||||
|
|
||||||
This allows the container runtime to:
|
This allows the container runtime to:
|
||||||
|
|
||||||
@@ -159,32 +174,46 @@ When delegation is enabled, Finit:
|
|||||||
|
|
||||||
**Requirements:**
|
**Requirements:**
|
||||||
|
|
||||||
- The service should specify `@user:group` for proper ownership
|
- The service should specify `user` and `group` for proper ownership
|
||||||
- Controllers are delegated from the parent cgroup
|
- Controllers are delegated from the parent cgroup
|
||||||
|
|
||||||
**Example with additional config (new syntax):**
|
**Example with additional config:**
|
||||||
|
|
||||||
service [2345] @podman:podman \
|
service podman {
|
||||||
cgroup.containers,delegate,mem.max:4G \
|
description = "Podman API"
|
||||||
/usr/bin/podman system service -- Podman API
|
runlevel = "2345"
|
||||||
|
user = "podman"
|
||||||
|
group = "podman"
|
||||||
|
cgroup containers {
|
||||||
|
delegate = true
|
||||||
|
memory.max = 4G
|
||||||
|
}
|
||||||
|
command = "/usr/bin/podman system service"
|
||||||
|
}
|
||||||
|
|
||||||
**Or with old syntax:**
|
This delegates the cgroup while also setting a 4GB memory limit.
|
||||||
|
|
||||||
service [2345] @podman:podman \
|
|
||||||
cgroup.containers:delegate,mem.max:4G \
|
|
||||||
/usr/bin/podman system service -- Podman API
|
|
||||||
|
|
||||||
Both examples delegate the cgroup while also setting a 4GB memory limit.
|
|
||||||
|
|
||||||
**Container template example:**
|
**Container template example:**
|
||||||
|
|
||||||
Here's a real-world example from [Infix OS](https://github.com/kernelkit/infix)
|
Here's a real-world example from [Infix OS](https://github.com/kernelkit/infix)
|
||||||
for running rootful podman container instances using delegation:
|
for running rootful podman container instances using delegation:
|
||||||
|
|
||||||
sysv log:prio:local1,tag:%i kill:30 pid:!/run/container:%i.pid \
|
sysv container:%i {
|
||||||
pre:0,/usr/sbin/container cleanup:0,/usr/sbin/container \
|
description = "container %i"
|
||||||
cgroup.system,delegate \
|
runlevel = "2345"
|
||||||
[2345] <!> :%i container -n %i -- container %i
|
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
|
This template uses `sysv` type with delegation, demonstrating that cgroup
|
||||||
delegation works with different service types, not just `service`.
|
delegation works with different service types, not just `service`.
|
||||||
@@ -200,7 +229,7 @@ Initially, the service process runs directly in the cgroup root:
|
|||||||
|
|
||||||
Once the container runtime creates child cgroups (e.g., `libpod-*/`), cgroups v2
|
Once the container runtime creates child cgroups (e.g., `libpod-*/`), cgroups v2
|
||||||
enforces the "no internal processes" rule. When Finit detects this (`EBUSY` error),
|
enforces the "no internal processes" rule. When Finit detects this (`EBUSY` error),
|
||||||
it automatically creates an `supervisor/` subdirectory and moves service-related
|
it automatically creates a `supervisor/` subdirectory and moves service-related
|
||||||
processes there:
|
processes there:
|
||||||
|
|
||||||
/sys/fs/cgroup/system/container@web/
|
/sys/fs/cgroup/system/container@web/
|
||||||
|
|||||||
+7
-6
@@ -6,15 +6,16 @@ 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).
|
||||||
|
|
||||||
The syntax for global environment variables is straight forward. In
|
Global environment variables go in an `environment` block, `env` for
|
||||||
Finit v4.4 the `set` keyword was added for completeness, but the old
|
short:
|
||||||
syntax (without the `set ` prefix) is still honored:
|
|
||||||
|
|
||||||
set foo=bar
|
environment {
|
||||||
set baz="qux"
|
foo = "bar"
|
||||||
|
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=bar` is removed from `finit.conf`, or any `finit.d/*.conf`
|
so if `foo` 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.
|
||||||
|
|||||||
+26
-22
@@ -24,8 +24,8 @@ 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
|
||||||
@@ -35,18 +35,21 @@ 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/finit-project/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. All .conf files in this
|
the hard-coded services provided by plugins before.
|
||||||
directory be either replaced by a system administrator or overridden by a
|
|
||||||
file with the same name in `/etc/finit.d/`.
|
|
||||||
|
|
||||||
The files in `/run/finit/system/*.conf` are created by plugins and Finit
|
All .conf files in both directories can be either replaced by a system
|
||||||
bundled services like runparts, [watchdog](../watchdog.md), and
|
administrator or overridden by a file with the same name in
|
||||||
[`keventd`](../keventd.md) if they are enabled. Like
|
`/etc/finit.d/`, which is always read last -- copy a .conf there and
|
||||||
`/lib/finit/system/*.conf`, these files can be overridden by file with
|
modify it, or symlink it to `/dev/null` to disable it outright.
|
||||||
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
|
||||||
@@ -55,27 +58,28 @@ 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 `/lib/finit/system/*.conf`, `finit.d/*.conf`,
|
with `finit.conf`, then `/run/finit/system/*.conf`,
|
||||||
and finally all `finit.d/enabled/*.conf` files. Each directory is a
|
`/lib/finit/system/*.conf`, `finit.d/*.conf`, and finally all
|
||||||
unique group, where files within each group are sorted alphabetically.
|
`finit.d/enabled/*.conf` files. Each directory is a unique group,
|
||||||
|
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 line by line, each `run`,
|
The resulting combined configuration is read in order, 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 the `run` statement. For an example on the relation of
|
properties of `run`. For an example on the relation of `service` and
|
||||||
`service` and `run` statements, and dependency handling between them,
|
`run`, and dependency handling between them, see
|
||||||
see [Conditional Loading](services.md#conditional-loading), below.
|
[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
|
||||||
@@ -84,7 +88,7 @@ see [Conditional Loading](services.md#conditional-loading), below.
|
|||||||
>
|
>
|
||||||
> 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 configuration directive `rcsd /path/to/finit.d`.
|
> top-level setting `rcsd = "/path/to/finit.d"`.
|
||||||
|
|
||||||
Filesystem Layout
|
Filesystem Layout
|
||||||
-----------------
|
-----------------
|
||||||
@@ -180,7 +184,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:
|
||||||
|
|
||||||
@@ -194,7 +198,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
|
||||||
@@ -203,6 +207,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.
|
||||||
|
|||||||
+99
-13
@@ -6,24 +6,110 @@ the navigation menu.
|
|||||||
Configuration File Syntax
|
Configuration File Syntax
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
The file format is line based, empty lines and comments, lines starting
|
A `.conf` file is a series of blocks. Every setting is a key inside
|
||||||
with `#`, are ignored. A configuration directive starts with a keyword
|
one, so nothing has to be remembered by position:
|
||||||
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
|
||||||
# Escape \# chars if you want them literal in, e.g., descriptions
|
service sysklogd {
|
||||||
service name:sysklogd [S123456789] \
|
description = "System log daemon"
|
||||||
env:-/etc/default/sysklogd \
|
runlevel = "S123456789"
|
||||||
syslogd -F $SYSLOGD_ARGS \
|
envfile = "-/etc/default/sysklogd"
|
||||||
-- System log daemon \# 1 # Comments allowed
|
command = "syslogd -F $SYSLOGD_ARGS"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
directives. Some are restricted, e.g., only available at bootstrap,
|
settings. 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.
|
||||||
|
|
||||||
|
|||||||
+36
-17
@@ -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 using the `log` sub-option with
|
Log rotation for run/task/services that redirect output to a log file
|
||||||
redirection to a log file. Global setting, applies to all services.
|
with their own `log` block. 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,33 +17,48 @@ size limit is reached.
|
|||||||
Redirecting Output
|
Redirecting Output
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The `run`, `task`, and `service` stanzas also allow the keyword `log` to
|
The `run`, `task`, and `service` blocks take a `log` block of their own,
|
||||||
redirect `stderr` and `stdout` of the application to a file or syslog
|
redirecting `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.
|
||||||
|
|
||||||
The full syntax is:
|
An empty block means syslog with the defaults, and three keys adjust it:
|
||||||
|
|
||||||
log:/path/to/file
|
| Setting | Description |
|
||||||
log:prio:facility.level,tag:ident
|
|---|---|
|
||||||
log:console
|
| `file` | Write to this path instead of syslog |
|
||||||
log:null
|
| `priority` | Syslog `facility.level`, default `daemon.info` |
|
||||||
log
|
| `identity` | Syslog tag, default the basename of the command |
|
||||||
|
|
||||||
Default `prio` is `daemon.info` and default `tag` is the basename of the
|
`/dev/console` and `/dev/null` are spelled as the paths they are:
|
||||||
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 log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
service ntpd {
|
||||||
|
description = "NTP daemon"
|
||||||
|
log {
|
||||||
|
priority = "user.warn"
|
||||||
|
identity = "ntpd"
|
||||||
|
}
|
||||||
|
command = "/sbin/ntpd pool.ntp.org"
|
||||||
|
}
|
||||||
|
|
||||||
Output Buffering
|
Output Buffering
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
When using the `log` directive, Finit redirects the service's stdout and
|
When using the `log` block, 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.
|
||||||
@@ -53,7 +68,11 @@ 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 log /usr/bin/stdbuf -oL /path/to/command -- My service
|
service myservice {
|
||||||
|
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.
|
||||||
|
|||||||
@@ -0,0 +1,267 @@
|
|||||||
|
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.
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
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`
|
||||||
+13
-6
@@ -47,16 +47,23 @@ 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 [12345] rescue
|
tty rescue {
|
||||||
|
runlevel = "12345"
|
||||||
|
rescue = true
|
||||||
|
}
|
||||||
|
|
||||||
The `tty` has the `rescue` option set, which works similar to the board
|
The `tty` block has `rescue` set, which works similar to the board
|
||||||
bring-up tty option `notty`. The major difference being that `sulogin`
|
bring-up setting `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 defaults to:
|
If Finit cannot find `/lib/finit/rescue.conf` it falls back to a
|
||||||
|
built-in equivalent, which runs in every runlevel it can:
|
||||||
|
|
||||||
tty [12345] rescue
|
tty 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.
|
||||||
+43
-27
@@ -26,12 +26,20 @@ the lifetime of your system.
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
task [S] /lib/console-setup/console-setup.sh
|
task console-setup {
|
||||||
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
|
runlevel = "S"
|
||||||
|
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` using the `runlevel N` directive, or by a
|
changed in `/etc/finit.conf` with `runlevel = N`, or by a script running
|
||||||
script running in runlevel S that calls, e.g., `initctl runlevel 9`.
|
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.
|
||||||
@@ -44,7 +52,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 the `network script` directive, or if you have an
|
enabled either by `network = "script"`, 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.
|
||||||
|
|
||||||
@@ -58,7 +66,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.
|
||||||
@@ -76,11 +84,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 stanzas per runlevel,
|
Deprecated. We recommend using dedicated task/run blocks 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.
|
||||||
|
|
||||||
@@ -90,7 +98,7 @@ Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
|||||||
System Hostname
|
System Hostname
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
**Syntax:** `host <NAME>`, or `hostname <NAME>`
|
**Syntax:** `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.
|
||||||
@@ -103,16 +111,24 @@ Deprecated. We recommend using `/etc/hostname` instead.
|
|||||||
Kernel Modules
|
Kernel Modules
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
**Syntax:** `module <MODULE> [ARGS]`
|
**Syntax:** `modules = { "MODULE [ARGS]", ... }`, alias `mod`
|
||||||
|
|
||||||
Load a kernel module, with optional arguments. Similar to `insmod`
|
Load kernel modules, each with optional arguments. Similar to the
|
||||||
command line tool.
|
`insmod` 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. For hotplug we recommend the
|
load (or coldplug) every required module. Hotplug module loading is
|
||||||
BusyBox mdev tool, add to `/etc/mdev.conf`:
|
handled by [keventd](../keventd.md), the built-in device manager. On
|
||||||
|
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"
|
||||||
|
|
||||||
@@ -122,9 +138,9 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
|
|||||||
Resource Limits
|
Resource Limits
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
**Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>`
|
**Syntax:** `rlimit { RESOURCE = LIMIT }`, with `soft.` or `hard.` prefix
|
||||||
|
|
||||||
Set the hard or soft limit for a resource, or both if that argument is
|
Set the hard or soft limit for a resource, or both if the prefix 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`.
|
||||||
|
|
||||||
@@ -134,23 +150,23 @@ or the kernel `/proc/PID/limits` file, for details. Finit versions
|
|||||||
before v3.1 used `infinity` for `unlimited`, which is still supported,
|
before v3.1 used `infinity` for `unlimited`, which is still supported,
|
||||||
albeit deprecated.
|
albeit deprecated.
|
||||||
|
|
||||||
# No process is allowed more than 8MB of address space
|
rlimit {
|
||||||
rlimit hard as 8388608
|
hard.as = 8388608 # no more than 8MB of address space
|
||||||
|
soft.core = unlimited # core dumps may be arbitrarily large
|
||||||
# Core dumps may be arbitrarily large
|
cpu = 10 # soft & hard = 10 sec
|
||||||
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
|
||||||
stanzas can share the same rlimits if they are in the same .conf.
|
blocks 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
|
||||||
@@ -168,7 +184,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 <on|off|true|false|1|0>`
|
**Syntax:** `reboot-watchdog = true|false`
|
||||||
|
|
||||||
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:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Run-parts Scripts
|
Run-parts Scripts
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
**Syntax:** `runparts [progress] [sysv] <DIR>`
|
**Syntax:** `runparts = "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,9 +14,10 @@ whatever the next runlevel is set to be (default 2). E.g., generate a
|
|||||||
|
|
||||||
**Options:**
|
**Options:**
|
||||||
|
|
||||||
- `progress`: display the progress of each script being executed
|
- `runparts-progress = true`: display the progress of each script being
|
||||||
- `sysv`: run only SysV style scripts, i.e., `SNNfoo`, or `KNNbar`,
|
executed
|
||||||
where `NN` is a number (0-99).
|
- `runparts-sysv = true`: run only SysV style scripts, i.e., `SNNfoo`,
|
||||||
|
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.
|
||||||
@@ -24,7 +25,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 the standalone `run` stanza and
|
in their interaction with Finit. Like a standalone `run` block 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
|
||||||
@@ -39,13 +40,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 talso be used,
|
order between scripts. Symlinks to existing daemons can also 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 to `another/directory/foo`.
|
and `K01foo` may be a symlink 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
|
||||||
|
|||||||
@@ -13,7 +13,12 @@ file `/etc/default/foo`. Like this:
|
|||||||
|
|
||||||
* `/etc/finit.conf`:
|
* `/etc/finit.conf`:
|
||||||
|
|
||||||
service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon
|
service foo {
|
||||||
|
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
|
||||||
@@ -22,6 +27,11 @@ 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]
|
||||||
> The leading `-` in `env:` determines if Finit should treat a missing
|
For a service with [`pam`](pam.md), the session environment is applied
|
||||||
> environment file as blocking the start of the service or not. When
|
before `envfile`, so the file overrides anything `pam_env` set.
|
||||||
> `-` 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.
|
||||||
|
|||||||
+287
-95
@@ -1,21 +1,22 @@
|
|||||||
Service Options
|
Service Options
|
||||||
===============
|
===============
|
||||||
|
|
||||||
The run/task/tty/service/sysv directives take modifiers, or options, to
|
The `run`, `task`, `tty`, `service` and `sysv` blocks take settings as
|
||||||
control their behavior. This section lists them with their limitations.
|
keys. 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, defaults to the
|
The name of a service, shown by the `initctl` tool, is the block title.
|
||||||
basename of the service executable. It can be changed with the `name:`
|
It defaults to the basename of the executable if the title is omitted:
|
||||||
option:
|
|
||||||
|
|
||||||
name:<service-name>
|
service sshd {
|
||||||
|
command = "/usr/sbin/sshd -D"
|
||||||
|
}
|
||||||
|
|
||||||
For multiple instances of a service, with the same `name`, set the
|
For multiple instances of a service, with the same name, add an
|
||||||
identifier `:ID` to prevent Finit from replacing previous instances:
|
identifier after a colon to prevent Finit from replacing previous
|
||||||
|
instances:
|
||||||
|
|
||||||
service name:ssdp :eth1 ssdpd eth1 -- Windows discovery on eth1
|
service ssdp:eth1 { command = "ssdpd eth1" }
|
||||||
service name:ssdp :eth2 ssdpd eth2 -- Windows discovery on eth2
|
service ssdp:eth2 { command = "ssdpd eth2" }
|
||||||
|
|
||||||
The [`initctl`](../initctl.md) tool will list these two services as:
|
The [`initctl`](../initctl.md) tool will list these two services as:
|
||||||
|
|
||||||
@@ -23,106 +24,295 @@ 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
|
||||||
`conflict:` option:
|
`conflicts` key:
|
||||||
|
|
||||||
service [S12345789] udevd -- Device event management daemon
|
service udevd { command = "udevd" }
|
||||||
run [S] conflict:udevd mdev -s -- Populating device tree
|
run mdev {
|
||||||
|
runlevel = "S"
|
||||||
|
conflicts = { "udevd" }
|
||||||
|
command = "mdev -s"
|
||||||
|
}
|
||||||
|
|
||||||
Multiple conflicting services can be separated using `,`:
|
The list may name several, and an instance is named the same way it is
|
||||||
|
declared:
|
||||||
|
|
||||||
service :1 abc
|
service abc:1 { command = "abc 1" }
|
||||||
service :2 abc
|
service abc:2 { command = "abc 2" }
|
||||||
service conflict:abc:1,abc:2 cde
|
service cde { conflicts = { "abc:1", "abc:2" } command = "cde" }
|
||||||
|
|
||||||
If a service should not be automatically started, it can be configured
|
If a service should not be automatically started, set `manual-start`.
|
||||||
as manual with the optional `manual` argument. The service can then be
|
It can then be started at any time with `initctl start <service>`:
|
||||||
started at any time by running `initctl start <service>`.
|
|
||||||
|
|
||||||
manual:yes
|
service lldpd {
|
||||||
|
manual-start = true
|
||||||
|
command = "/usr/sbin/lldpd -d"
|
||||||
|
}
|
||||||
|
|
||||||
Other run/task/service options are:
|
Other run/task/service settings are:
|
||||||
|
|
||||||
* `caps:...` -- see the [Linux Capabilities](capabilities.md) section
|
* `capabilities` -- see the [Linux Capabilities](capabilities.md) section
|
||||||
* `cgroup.NAME[,opts]` or `cgroup:opts` -- 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)
|
||||||
|
|
||||||
As mentioned previously, services are automatically started, restarted,
|
Command Candidates
|
||||||
and stopped, depending on the configuration and conditions. Within the
|
------------------
|
||||||
confines of that the following options are available:
|
|
||||||
|
|
||||||
* `restart:NUM` -- number of times Finit tries to restart a crashing
|
The `command` setting may list several candidates for the same
|
||||||
service, default: 10, max: 255. When this limit is reached the
|
service, in which case Finit starts the first one it finds:
|
||||||
|
|
||||||
|
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
|
* `restart-sec = SEC` -- number of seconds before Finit tries to
|
||||||
a crashing service, default: 2 seconds for the first five retries,
|
restart a crashing service. The default is 2 seconds for the first
|
||||||
then back-off to 5 seconds. The maximum of this configured value
|
half of `restart-max` attempts, then a back-off to 5 seconds -- with
|
||||||
and the above (2 and 5) will be used
|
the default `restart-max` that is the first five retries. The
|
||||||
* `restart:always` -- no upper limit on the number of times Finit
|
greater of this configured value and the back-off is used
|
||||||
tries to restart a crashing service. Same as `restart:-1`
|
* `restart = "always"` -- no upper limit on the number of times Finit
|
||||||
* `norestart` -- dont restart on failures, same as `restart:0`
|
tries to restart a crashing service
|
||||||
* `respawn` -- bypasses the `restart` mechanism completely, allows
|
* `restart = "never"` -- do not restart on failures. `false` is
|
||||||
endless restarts. Useful in many use-cases, but not what `service`
|
accepted as a synonym, and `true` selects the default policy
|
||||||
was originally designed for so not the default behavior
|
* `respawn = true` -- bypasses the `restart` mechanism completely,
|
||||||
* `remain:yes` -- for `run` and `task` only. Prevents the task from
|
allowing endless restarts. Exiting is treated as normal work
|
||||||
re-running on runlevel re-entry and ensures the `post:` script runs
|
rather than failure, so the service is restarted at once instead of
|
||||||
when the task is explicitly stopped or leaves its valid runlevels.
|
being counted and delayed. This is how a `tty` behaves; it is not
|
||||||
Similar to systemd's `RemainAfterExit=yes`. See [Task and Run](task-and-run.md)
|
what `service` was designed for, so it is not the default
|
||||||
for more details
|
* `remain-after-exit = true` -- for `run` and `task` only. Prevents
|
||||||
* `oncrash:reboot` -- when all retries have failed, and the service
|
the task from re-running on runlevel re-entry and ensures the
|
||||||
has *crashed*, if this option is set the system is rebooted
|
`exec-stop-post` script runs when the task is explicitly stopped or
|
||||||
* `oncrash:script` -- similarly, but instead of rebooting, call the
|
leaves its valid runlevels. This is systemd's `RemainAfterExit=`.
|
||||||
`post:script` action with exit code `crashed`, see below
|
See [Task and Run](task-and-run.md) for more details
|
||||||
* `reload:'script [args]'` -- some services do not support `SIGHUP` but
|
* `oncrash = "reboot"` -- when all retries have failed, and the
|
||||||
may have other ways to update the configuration of a running daemon.
|
service has *crashed*, the system is rebooted
|
||||||
When `reload:script` is defined it is preferred over `SIGHUP`. Like
|
* `oncrash = "script"` -- similarly, but instead of rebooting, call
|
||||||
systemd, Finit sets `$MAINPID` as a convenience to scripts, which in
|
the `exec-stop-post` script with exit code `crashed`, see below
|
||||||
effect also allow `reload:'kill -HUP $MAINPID'`
|
|
||||||
* `stop:'script [args]'` -- some services may require alternate methods
|
|
||||||
to be stopped. If a `stop:script` is defined it is preferred over
|
|
||||||
`SIGTERM` and `stop`, for `service` and `sysv`, respectively.
|
|
||||||
Similar to `reload:script`, Finit sets `$MAINPID`
|
|
||||||
|
|
||||||
> [!CAUTION]
|
Service directories
|
||||||
> 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.
|
|
||||||
|
|
||||||
When stopping a service (run/task/sysv/service), either manually or when
|
Five settings ask Finit to create a directory for the service before it
|
||||||
moving to another runlevel, Finit starts by sending `SIGTERM`, to allow
|
starts, owned by its `user` and `group`, mode 0755. The value is a
|
||||||
the process to shut down gracefully (unless a `stop:'script'` is used).
|
directory name, resolved under a fixed base -- absolute paths and `..`
|
||||||
However, if the process has not been collected within 3 seconds, Finit
|
are refused:
|
||||||
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`.
|
|
||||||
|
|
||||||
Services, including the `sysv` variant, support pre/post/ready and
|
| Setting | Base | Environment variable |
|
||||||
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` |
|
||||||
|
|
||||||
* `pre:[0-3600,]script` -- called before the sysv/service is stated
|
Each resolved path is exported to the process environment under the
|
||||||
* `post:[0-3600,]script` -- called after the sysv/service has stopped
|
listed name, the same names systemd uses for `RuntimeDirectory=` and
|
||||||
* `ready:[0-3600,]script` -- called when the sysv/service is ready
|
friends. As in systemd, `config-dir` is the odd one out: it is created
|
||||||
* `cleanup:[0-3600,]script` -- called when run/task/sysv/service is removed
|
but never chowned.
|
||||||
|
|
||||||
The optional number (0-3600) is the timeout before Finit kills the
|
Each takes a matching `-mode`, e.g. `runtime-dir-mode = 0700`, default
|
||||||
script, it defaults to the kill delay value and can be disabled by
|
0755. Modes are octal, with the leading zero.
|
||||||
setting it to zero. These scripts run as the same `@USER:GROUP` as the
|
|
||||||
service itself, with any `env:file` sourced. The scripts are executed
|
The mode of the named directory is locked down again on every start.
|
||||||
from the `$HOME` of the given user. The scripts are not called with any
|
Its contents are left alone as long as the owner is right; if the owner
|
||||||
argument, but get a set of environment variables:
|
has drifted, everything under it is chowned back.
|
||||||
|
|
||||||
|
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 `post:script` is called with an additional set of environment
|
The `exec-stop-post` script is called with an additional set of
|
||||||
variables. Yes, the text is correct, the naming was an accident:
|
environment variables. Yes, the text is correct, the naming was an
|
||||||
|
accident:
|
||||||
|
|
||||||
- `EXIT_CODE=[exited,signal,crashed]`: normal exit, signaled, or
|
- `EXIT_CODE=[exited,signal,crashed]`: normal exit, signaled, or
|
||||||
crashed
|
crashed
|
||||||
@@ -130,11 +320,13 @@ variables. Yes, the text is correct, the naming was an 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/sys/service is removed (disable + reload) it is first
|
When a run/task/sysv/service is removed (disable + reload) it is first
|
||||||
stopped and then removed from the runlevel. The `post:script` always
|
stopped and then removed from the runlevel. The `exec-stop-post`
|
||||||
runs when the process has stopped, and the `cleanup:script` runs when
|
script always runs when the process has stopped, and `exec-cleanup`
|
||||||
the the stanza has been removed from the runlevel.
|
runs when the block 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. It is up to the user to ensure the scripts terminate.
|
> notification. A script that outlives its timeout is killed, so pick
|
||||||
|
> one that suits the work, or set it to zero to opt out and take
|
||||||
|
> responsibility for the script terminating.
|
||||||
|
|||||||
+20
-13
@@ -32,28 +32,35 @@ 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 notify:pid watchdogd
|
service watchdogd { notify = "pid" command = "watchdogd" }
|
||||||
service notify:systemd foo
|
service foo { notify = "systemd" command = "foo" }
|
||||||
service notify:s6 bar
|
service bar { notify = "s6" command = "bar" }
|
||||||
service notify:none qux
|
service qux { notify = "none" command = "qux" }
|
||||||
|
|
||||||
The `notify:none` syntax is for completeness in systems which run in
|
The `notify = "none"` setting is for completeness in systems which run
|
||||||
`readiness pid` mode (default). Services declared with `notify:none`
|
in `readiness = "pid"` mode (default). Services declared with
|
||||||
will transition to ready as soon as Finit has started them, e.g.,
|
`notify = "none"` will transition to ready as soon as Finit has started
|
||||||
`service/qux/ready`.
|
them, e.g., `service/qux/ready`.
|
||||||
|
|
||||||
To synchronize two services the following condition can be used:
|
To synchronize two services the following condition can be used:
|
||||||
|
|
||||||
service notify:pid watchdogd
|
service watchdogd {
|
||||||
service <service/watchdogd/ready> stress-ng --cpu 8
|
notify = "pid"
|
||||||
|
command = "watchdogd"
|
||||||
|
}
|
||||||
|
|
||||||
For details on the syntax and options, see below.
|
service stress-ng {
|
||||||
|
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
|
||||||
@@ -66,7 +73,7 @@ For details on the syntax and options, see below.
|
|||||||
> 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 `<!>` in its condition statement.)
|
> (Declare the service with `reload-signal = "none"`.)
|
||||||
>
|
>
|
||||||
> 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
|
||||||
|
|||||||
@@ -7,14 +7,19 @@ 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 stanza. The following
|
the wrapper script in the Finit `.conf` service block. 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 [235] <!> /etc/start.d/program -- Example Program
|
service program {
|
||||||
|
description = "Example Program"
|
||||||
|
runlevel = "235"
|
||||||
|
reload-signal = "none"
|
||||||
|
command = "/etc/start.d/program"
|
||||||
|
}
|
||||||
|
|
||||||
* `/etc/start.d/program:`
|
* `/etc/start.d/program:`
|
||||||
|
|
||||||
@@ -26,6 +31,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 `<!>` to denote that it doesn't support `SIGHUP`.
|
> The example sets `reload-signal = "none"` to say the program does not
|
||||||
> That way Finit will stop/start the service instead of sending SIGHUP
|
> support `SIGHUP`. Finit then stop/starts the service instead of
|
||||||
> at restart/reload events.
|
> signalling it at restart/reload events.
|
||||||
|
|||||||
+222
-90
@@ -1,16 +1,17 @@
|
|||||||
Services
|
Services
|
||||||
========
|
========
|
||||||
|
|
||||||
**Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
**Syntax:** `service NAME { command = "/path/to/daemon ARGS" }`
|
||||||
|
|
||||||
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 the options below.
|
limits controlling this are configurable, see
|
||||||
|
[Service Options](service-opts.md).
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> To allow endless restarts, see the [`respawn` option](service-opts.md)
|
> To allow endless restarts, see [`respawn`](service-opts.md#restarting)
|
||||||
|
|
||||||
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
|
||||||
@@ -19,73 +20,105 @@ 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, using
|
are supported using the same syntax as forking `sysv` services, by
|
||||||
the `pid:!/path/to/pidfile.pid` syntax. There is an alternative syntax
|
naming the file to watch with `pidfile`. There is an alternative that
|
||||||
that may be more intuitive, where Finit can also guess the PID file
|
may be more intuitive, where Finit can also guess the PID file based on
|
||||||
based on the daemon's command name:
|
the daemon's command name:
|
||||||
|
|
||||||
service type:forking ntpd -- NTP daemon
|
service ntpd {
|
||||||
|
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, you have to submit the
|
`/var/run/ntpd.pid`. If Finit guesses wrong, name the file yourself
|
||||||
full `pid:!/path/to/file.pid`.
|
with `pidfile = "/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 [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
|
service ospfd {
|
||||||
|
description = "OSPF daemon"
|
||||||
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/zebra" }
|
||||||
|
command = "/sbin/ospfd"
|
||||||
|
}
|
||||||
|
|
||||||
`[2345]` denote the runlevels `ospfd` is allowed to run in, they are
|
`runlevel` denotes the runlevels `ospfd` is allowed to run in, it is
|
||||||
optional and default to level 2-5 if omitted.
|
optional and defaults to level 2-4 if omitted.
|
||||||
|
|
||||||
`<...>` is the condition for starting `ospfd`. In this example Finit
|
|
||||||
waits for another service, `zebra`, to have created its PID file in
|
|
||||||
`/var/run/quagga/zebra.pid` before starting `ospfd`. Finit watches
|
|
||||||
*all* files in `/var/run`, for each file named `*.pid`, or `*/pid`,
|
|
||||||
Finit opens it and find the matching `NAME:ID` using the PID.
|
|
||||||
|
|
||||||
The condition can be prefixed with `!` and/or `~`:
|
`conditions` lists what must be asserted before starting `ospfd`. In
|
||||||
|
this example Finit waits for another service, `zebra`, to have created
|
||||||
|
its PID file in `/var/run/quagga/zebra.pid`. Finit watches *all* files
|
||||||
|
in `/var/run`, for each file named `*.pid`, or `*/pid`, Finit opens it
|
||||||
|
and finds the matching `NAME:ID` using the PID.
|
||||||
|
|
||||||
- `<!pid/zebra>` -- `ospfd` does not support `SIGHUP` (noreload)
|
A condition may be prefixed with `~` to propagate a reload of the
|
||||||
- `<~pid/zebra>` -- propagate reload from `zebra` to `ospfd`
|
upstream service to this one, rather than merely pausing and resuming
|
||||||
- `<!~pid/zebra>` -- both: noreload and propagate reload
|
it:
|
||||||
|
|
||||||
For details, see the [Finit Conditions](../conditions.md) document.
|
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. A `pid` keyword can be set
|
application Finit provides a workaround. With `pidfile-create` Finit
|
||||||
to have Finit automatically create (when starting) and later remove
|
creates the file when starting and removes it when stopping. The path
|
||||||
(when stopping) the PID file. The file is created in the `/var/run`
|
comes from `pidfile`, which takes three forms:
|
||||||
directory using the `basename(1)` of the service. The default can be
|
|
||||||
modified with an optional `pid:`-argument:
|
|
||||||
|
|
||||||
pid[:[/path/to/]filename[.pid]]
|
pidfile = true # /var/run/<command basename>.pid
|
||||||
|
pidfile = "bar" # a bare name, /var/run/bar.pid
|
||||||
|
pidfile = "/run/bar.pid" # an explicit path
|
||||||
|
|
||||||
For example, by adding `pid:/run/bar.pid` to the service `/sbin/bar`,
|
Such a file is also used by the Finit condition subsystem, so another
|
||||||
that PID file will, not only be created and removed automatically, but
|
service, run or task can depend on `pid/bar`. Here foo is not started
|
||||||
also be used by the Finit condition subsystem. So a service/run/task
|
until bar has:
|
||||||
can depend on `<pid/bar>`, like this foo will not be started until bar
|
|
||||||
has started:
|
|
||||||
|
|
||||||
service pid:/run/bar.pid bar -- Bar Service
|
service bar {
|
||||||
service <pid/bar> foo -- Foo Service
|
description = "Bar 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 can be enabled by using the `notify`
|
and s6 style notification. This is enabled with the `notify` key:
|
||||||
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
|
||||||
@@ -93,7 +126,11 @@ option:
|
|||||||
(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 [S12345789] notify:s6 mdevd -O 4 -D %n
|
service mdevd {
|
||||||
|
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
|
||||||
@@ -107,7 +144,13 @@ 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 [S] <service/mdevd/ready> @root:root mdevd-coldplug
|
task 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
|
||||||
@@ -123,20 +166,29 @@ Non-privileged Services
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Every `run`, `task`, or `service` can also list the privileges the
|
Every `run`, `task`, or `service` can also list the privileges the
|
||||||
`/path/to/cmd` should be executed with. Prefix the command with
|
command should be executed with, using `user`, `group` and
|
||||||
`@USR[:GRP[,SUPP,...]]`, where group and supplementary groups are
|
`extra-groups`, all optional:
|
||||||
optional, like this:
|
|
||||||
|
|
||||||
run [2345] @joe:users logger "Hello world"
|
run hello {
|
||||||
|
runlevel = "2345"
|
||||||
|
user = "joe"
|
||||||
|
group = "users"
|
||||||
|
command = "logger \"Hello world\""
|
||||||
|
}
|
||||||
|
|
||||||
Finit reads the user's supplementary group membership from `/etc/group`
|
Finit reads the user's supplementary group membership from `/etc/group`
|
||||||
automatically. Any groups the user belongs to will be inherited by
|
automatically. Any groups the user belongs to will be inherited by
|
||||||
the service.
|
the service.
|
||||||
|
|
||||||
To specify additional supplementary groups beyond those in `/etc/group`,
|
To specify additional supplementary groups beyond those in
|
||||||
append them after the primary group, separated by commas:
|
`/etc/group`, list them in `extra-groups`:
|
||||||
|
|
||||||
service @caddy:caddy,ssl-cert /usr/bin/caddy run
|
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
|
This runs the `caddy` service as user `caddy`, with primary group
|
||||||
`caddy`, inheriting any groups `caddy` is a member of in `/etc/group`,
|
`caddy`, inheriting any groups `caddy` is a member of in `/etc/group`,
|
||||||
@@ -144,81 +196,161 @@ plus the additional `ssl-cert` group. This is useful when a service
|
|||||||
needs access to resources owned by groups not listed in `/etc/group`.
|
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` somewhere between the `run`, `task`,
|
multiple web servers, add `:ID` to the block title, like this:
|
||||||
`service` keyword and the command, like this:
|
|
||||||
|
|
||||||
service :80 [2345] httpd -f -h /http -p 80 -- Web server
|
service httpd:80 {
|
||||||
service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
|
description = "Web server"
|
||||||
|
runlevel = "2345"
|
||||||
|
command = "httpd -f -h /http -p 80"
|
||||||
|
}
|
||||||
|
|
||||||
Without the `:ID` to the service the latter will overwrite the former
|
service httpd:8080 {
|
||||||
and only the old web server would be started and supervised.
|
description = "Old web server"
|
||||||
|
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 support conditional loading of stanzas. The following example is
|
Finit supports conditional loading of blocks. The following example is
|
||||||
take from the `system/hotplug.conf` file in the Finit distribution.
|
taken from the `system/10-hotplug.conf` file in the Finit distribution.
|
||||||
Here we only show a simplified subset.
|
Here we only show a simplified subset.
|
||||||
|
|
||||||
Starting with the `nowarn` option.
|
Starting with the udev daemon, which goes by two names depending on
|
||||||
|
how it was built. Both are candidates for the same service:
|
||||||
|
|
||||||
service nowarn name:udevd pid:udevd /lib/systemd/systemd-udevd
|
service udevd {
|
||||||
service nowarn name:udevd pid:udevd udevd
|
pidfile = "udevd"
|
||||||
|
command = { "/lib/systemd/systemd-udevd", "-udevd" }
|
||||||
|
}
|
||||||
|
|
||||||
When loading the .conf file Finit looks for `/lib/systemd/systemd-udevd`
|
When loading the .conf file Finit looks for
|
||||||
if that is not found Finit automatically logs a warning. The `nowarn`
|
`/lib/systemd/systemd-udevd`, and if that is not there it moves on to
|
||||||
option disables this warning so that the second line can be evaluated,
|
`udevd`. A candidate that is not installed is expected, so no warning
|
||||||
which also provides a service named `udevd`.
|
is logged for the ones that are skipped. The leading `-` on the last
|
||||||
|
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.
|
||||||
|
|
||||||
run nowarn if:udevd <pid/udevd> :1 udevadm settle -t 0
|
> [!NOTE]
|
||||||
|
> 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).
|
||||||
|
|
||||||
This line is only loaded if we know of a service named `udevd`. Again,
|
run udevadm:1 {
|
||||||
|
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 nowarn conflict:udevd [S] mdev -s -- Populating device tree
|
run mdev {
|
||||||
|
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 statements can also be negated, so the previous stanza
|
Conditional loading can also be negated, so the previous block can be
|
||||||
can also be written as:
|
written as:
|
||||||
|
|
||||||
run nowarn if:!udevd [S] mdev -s -- Populating device tree
|
run mdev {
|
||||||
|
description = "Populating device tree"
|
||||||
|
runlevel = "S"
|
||||||
|
if = "!udevd"
|
||||||
|
command = "-mdev -s"
|
||||||
|
}
|
||||||
|
|
||||||
The reason for using `conflict` in this example is that a conflict can be
|
The reason for using `conflicts` in this example is that a conflict can
|
||||||
resolved. Stanzas marked with `conflict:foo` are rechecked at runtime.
|
be resolved. Blocks naming a conflict are rechecked at runtime.
|
||||||
|
|
||||||
|
|
||||||
Conditional Execution
|
Conditional Execution
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Similar to conditional loading of stanzas there is conditional runtime
|
Similar to conditional loading of blocks 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 [123]` is qualified to run only in runlevel 1,
|
runlevel. E.g., a task with `runlevel = "123"` is qualified to run
|
||||||
2, and 3. It is not considered for other runlevels.
|
only in runlevel 1, 2, and 3. It is not considered for other
|
||||||
|
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 [S] name:startup <pid/sysrepo> confd -b --load startup-config
|
run startup {
|
||||||
run [S] if:<usr/fail-startup> name:failure <pid/sysrepo> confd --load failure-config
|
runlevel = "S"
|
||||||
|
conditions = { "pid/sysrepo" }
|
||||||
|
command = "confd -b --load startup-config"
|
||||||
|
}
|
||||||
|
|
||||||
The two run statements reside in the same .conf file so Finit runs them
|
run failure {
|
||||||
in true sequence. If loading the file `startup-config` fails confd sets
|
runlevel = "S"
|
||||||
the condition `usr/fail-startup`, thus allowing the next run statement
|
if = "usr/fail-startup"
|
||||||
to load `failure-config`.
|
conditions = { "pid/sysrepo" }
|
||||||
|
command = "confd --load failure-config"
|
||||||
|
}
|
||||||
|
|
||||||
Notice the critical difference between the `<pid/sysrepo>` condition and
|
The two run blocks reside in the same .conf file so Finit runs them in
|
||||||
`if:<usr/fail-startup>`. The former is a condition for starting and the
|
true sequence. If loading the file `startup-config` fails confd sets
|
||||||
latter is a condition to check if a run/task/service is qualified to
|
the condition `usr/fail-startup`, thus allowing the next one to load
|
||||||
even be considered.
|
`failure-config`.
|
||||||
|
|
||||||
Conditional execution statements can also be negated, so provided the
|
Notice the critical difference between the `conditions` list and `if`.
|
||||||
file loaded did the opposite, i.e., set a condition on success, the
|
The former is a condition for starting; the latter is a condition to
|
||||||
previous stanza can also be written as:
|
check whether a run/task/service is qualified to even be considered.
|
||||||
|
`if` has a negation of its own, `!`, which is unrelated to anything in
|
||||||
|
the `conditions` list.
|
||||||
|
|
||||||
run [S] if:<!usr/startup-ok> name:failure <pid/sysrepo> confd ...
|
What `if` compares against depends on the value. A namespace
|
||||||
|
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).
|
||||||
|
|||||||
+26
-17
@@ -9,42 +9,45 @@ from a serialized boot process.
|
|||||||
SysV Init Scripts
|
SysV Init Scripts
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
**Syntax:** `sysv [LVLS] <COND> /path/to/init-script -- Optional description`
|
**Syntax:** `sysv NAME { command = "/path/to/init-script" }`
|
||||||
|
|
||||||
> `<COND>` is described in the [Services](services.md) section.
|
> [!NOTE]
|
||||||
|
> Conditions, runlevels, and the other settings a `sysv` block takes
|
||||||
|
> are described in [Service Options](service-opts.md).
|
||||||
|
|
||||||
Similar to `task` is the `sysv` stanza, which can be used to call SysV
|
A `sysv` block is a supervised daemon, like `service`, but started and
|
||||||
style scripts. The primary intention for this command is to be able to
|
stopped through a SysV style init script instead of a command line. The
|
||||||
reuse much of existing setup and init scripts in Linux distributions.
|
intention is to reuse existing setup and init scripts from Linux
|
||||||
|
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 `sysv` stanza is declared, is modified, Finit
|
the Finit .conf, where the `sysv` block 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` stanzas work.
|
`service` blocks 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: `pid:!/path/to/pidfile.pid`.
|
by declaring the PID file to look for: `pidfile = "/path/to/file.pid"`.
|
||||||
Notice the leading `!`, it signifies Finit should not try to create the
|
Finit does not create that file, it watches it for the resulting
|
||||||
file, but rather watch that file for the resulting forked-off PID. This
|
forked-off PID. That is the default; `pidfile-create = true` asks Finit
|
||||||
syntax also works for forking daemons that do not have a command line
|
to write it instead. The same applies to forking daemons with no way to
|
||||||
option to run it in the foreground, more on this below in `service`.
|
run in the foreground, see [Services](services.md).
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> See also [SysV Init Compatibility](#sysv-init-compatibility).
|
> See also [SysV Init Compatibility](#sysv-init-compatibility).
|
||||||
|
|
||||||
`runparts DIRECTORY`
|
Run-parts
|
||||||
--------------------
|
---------
|
||||||
|
|
||||||
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` directive. It runs
|
order, at bootstrap, Finit provides the `runparts` setting. 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:
|
||||||
@@ -64,7 +67,12 @@ 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 [2345] <pid/syslogd> /etc/init.d/openbsd-inetd -- OpenBSD inet daemon
|
sysv inetd {
|
||||||
|
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
|
||||||
@@ -83,6 +91,7 @@ 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.
|
||||||
|
|
||||||
|
|||||||
+34
-20
@@ -1,9 +1,10 @@
|
|||||||
run (sequence)
|
run (sequence)
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
**Syntax:** `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
**Syntax:** `run NAME { command = "/path/to/cmd ARGS" }`
|
||||||
|
|
||||||
> `<COND>` is described in the [Services](services.md) section.
|
> Conditions, runlevels, and the other settings a `run` block takes are
|
||||||
|
> 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
|
||||||
@@ -31,36 +32,45 @@ also the `--quiet` and `--batch` options.
|
|||||||
task (parallel)
|
task (parallel)
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
**Syntax:** `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
**Syntax:** `task NAME { command = "/path/to/cmd ARGS" }`
|
||||||
|
|
||||||
> `<COND>` is described in the [Services](services.md) section.
|
> A `task` block takes the same settings as `run`, see
|
||||||
|
> [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 [s] echo "foo" | cat >/tmp/bar
|
task 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:yes
|
remain-after-exit
|
||||||
----------
|
-----------------
|
||||||
|
|
||||||
By default, a `run` or `task` will re-run each time its runlevel is
|
By default, a `run` or `task` will re-run each time its runlevel is
|
||||||
entered, and its `post:` script does not run on completion.
|
entered, and its `exec-stop-post` script does not run on completion.
|
||||||
|
|
||||||
With `remain:yes`, the task runs once and does not re-run on runlevel
|
With `remain-after-exit`, the task runs once and does not re-run on
|
||||||
re-entry:
|
runlevel re-entry:
|
||||||
|
|
||||||
task [2345] remain:yes /usr/sbin/setup-firewall -- Firewall setup
|
task firewall {
|
||||||
|
description = "Firewall setup"
|
||||||
|
runlevel = "2345"
|
||||||
|
remain-after-exit = true
|
||||||
|
command = "/usr/sbin/setup-firewall"
|
||||||
|
}
|
||||||
|
|
||||||
This has the following effects:
|
This has the following effects:
|
||||||
|
|
||||||
* The task does not re-run on runlevel re-entry
|
* The task does not re-run on runlevel re-entry
|
||||||
* The `post:` script runs when:
|
* The `exec-stop-post` script runs when:
|
||||||
- The task is explicitly stopped (`initctl stop NAME`)
|
- The task is explicitly stopped (`initctl stop NAME`)
|
||||||
- The task leaves its valid runlevels (e.g., runlevel change)
|
- The task leaves its valid runlevels (e.g., runlevel change)
|
||||||
|
|
||||||
@@ -72,16 +82,20 @@ This is useful for tasks that set up persistent state where:
|
|||||||
**Example:** Setting up firewall rules with cleanup on shutdown:
|
**Example:** Setting up firewall rules with cleanup on shutdown:
|
||||||
|
|
||||||
```
|
```
|
||||||
task [2345] remain:yes \
|
task firewall {
|
||||||
post:/usr/sbin/teardown-firewall \
|
description = "Firewall setup"
|
||||||
/usr/sbin/setup-firewall -- 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 `post:` script runs when
|
The firewall rules are created once. The `exec-stop-post` script runs when
|
||||||
entering runlevel 0 (halt) or 6 (reboot), or on explicit stop.
|
entering runlevel 0 (halt) or 6 (reboot), or on explicit stop.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> The `remain:yes` option is not supported for bootstrap-only tasks
|
> The `remain-after-exit` setting is not supported for bootstrap-only
|
||||||
> (tasks with only runlevel S). Bootstrap tasks are deleted immediately
|
> tasks (tasks with only runlevel S). Bootstrap tasks are deleted
|
||||||
> after completion, and their `post:` scripts never run. A warning is
|
> immediately after completion, and their `exec-stop-post` scripts never
|
||||||
> logged if `remain:yes` is used on such tasks.
|
> run. A warning is logged if `remain-after-exit` is used on such tasks.
|
||||||
|
|||||||
@@ -5,16 +5,20 @@ 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 :%i avahi-autoipd --syslog %i -- ZeroConf for %i
|
service avahi-autoipd:%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 in the instantiated directive be replaced with
|
instance of `%i` will be replaced with `eth0` before the file is
|
||||||
`eth0`. Inspect the resulting instantiated template with `initctl show
|
parsed, so it works in the block title, in any value, and in the
|
||||||
avahi-autoipd:eth0` and check the status of a running instance with:
|
command line alike. Inspect the result with `initctl show
|
||||||
|
avahi-autoipd:eth0`, and check a running instance with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ initctl status avahi-autoipd:eth0
|
$ initctl status avahi-autoipd:eth0
|
||||||
|
|||||||
+115
-34
@@ -1,67 +1,99 @@
|
|||||||
TTYs and Consoles
|
TTYs and Consoles
|
||||||
=================
|
=================
|
||||||
|
|
||||||
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
**Syntax:** `tty NAME { device = DEV }` -- built-in getty
|
||||||
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
|
`tty NAME { command = "CMD ARGS" }` -- external getty
|
||||||
`tty [LVLS] <COND> [notty] [rescue]`
|
`tty NAME { notty = true }`, or `{ rescue = true }` -- bare shell, no device
|
||||||
|
|
||||||
The first variant of this option uses the built-in getty on the given
|
The block title NAME is what `initctl` shows. The three variants differ
|
||||||
TTY device DEV, in the given runlevels. DEV may be the special keyword
|
in what they open: `device` runs the built-in getty on that TTY, and DEV
|
||||||
`@console`, which is expanded from `/sys/class/tty/console/active`,
|
may be the special keyword `@console`, expanded from
|
||||||
useful on embedded systems.
|
`/sys/class/tty/console/active`, useful on embedded systems. `command`
|
||||||
|
hands the TTY to an external getty. `notty` opens nothing at all.
|
||||||
|
|
||||||
The default baud rate is 0, i.e., keep kernel default.
|
`command` may name several candidates, `{ "agetty ...", "getty ..." }`,
|
||||||
|
and Finit runs the first one it finds. See [Command
|
||||||
|
Candidates](service-opts.md#command-candidates).
|
||||||
|
|
||||||
> The `tty` stanza inherits runlevel, condition (and other feature)
|
Settings common to all three:
|
||||||
> 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 [12345] /dev/ttyAMA0 115200 noclear vt220
|
tty ttyAMA0 {
|
||||||
|
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 in this variant, the same output that the kernel
|
device node is required, the same output the kernel uses is reused for
|
||||||
uses is reused for stdio. If the `rescue` option is omitted, a shell is
|
stdio. With `notty` a shell is started (`nologin`, `noclear`, and
|
||||||
started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
|
`nowait` are implied); with `rescue` the bundled
|
||||||
option is set the bundled `/libexec/finit/sulogin` is started to present
|
`/libexec/finit/sulogin` presents a bare-bones root login prompt. If
|
||||||
a bare-bones root login prompt. If the root (uid:0, gid:0) user does
|
the root (uid:0, gid:0) user does not have a password set, no rescue is
|
||||||
not have a password set, no rescue is possible. For more information,
|
possible. For more information, see the [Rescue Mode](rescue.md)
|
||||||
see the [Rescue Mode](rescue.md) section.
|
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 [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
tty getty {
|
||||||
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
|
runlevel = "12345"
|
||||||
|
command = "/sbin/getty -L 115200 /dev/ttyAMA0 vt100"
|
||||||
|
}
|
||||||
|
|
||||||
The `noclear` option disables clearing the TTY after each session.
|
tty agetty {
|
||||||
|
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` option disables the `press Enter to activate console`
|
The `nowait` setting 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` option disables getty and `/bin/login`, and gives the
|
The `nologin` setting disables getty and `/bin/login`, and gives the
|
||||||
user a root (login) shell on the given TTY `<DEV>` immediately.
|
user a root (login) shell on the given TTY 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 weather the system
|
special `@console` device. This works regardless whether 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`. The speed
|
it out by querying sysfs: `/sys/class/tty/console/active`. Leave
|
||||||
can be omitted to keep the kernel default.
|
`baud` out 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,
|
||||||
@@ -69,18 +101,67 @@ can be omitted to keep the kernel default.
|
|||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
tty [12345] @console noclear vt220
|
tty console {
|
||||||
|
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 [12345789] notty
|
tty board {
|
||||||
|
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 the `service` stanza to start a stand-alone shell:
|
One can also use a `service` block to start a stand-alone shell:
|
||||||
|
|
||||||
service [12345] /bin/sh -l
|
service shell {
|
||||||
|
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
@@ -0,0 +1,375 @@
|
|||||||
|
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
|
||||||
+135
-58
@@ -10,117 +10,194 @@ 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]
|
||||||
> As of Finit v4.4, `.conf` lines can be broken up using the standard UNIX
|
> A block spans as many lines as it needs, so no continuation character is
|
||||||
> continuation character (`\`), trailing comments are also supported. The
|
> called for. For the full syntax, see the [finit.conf(5)][] manual or the
|
||||||
> latter means you must escape any hashes used in directives and descriptions
|
> [Configuration](config/index.md) section.
|
||||||
> (`\#`). 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
|
||||||
host default
|
hostname = "default"
|
||||||
|
|
||||||
# Runlevel to start after bootstrap, 'S', default: 2
|
# Runlevel to start after bootstrap, 'S', default: 2
|
||||||
#runlevel 2
|
#runlevel = 2
|
||||||
|
|
||||||
# Support for setting global environment variables, using foo=bar syntax
|
# Global environment variables, be careful though with variables like
|
||||||
# be careful though with variables like PATH, SHELL, LOGNAME, etc.
|
# PATH, SHELL, LOGNAME, etc.
|
||||||
#PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
#environment {
|
||||||
|
# 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 size=100k count=4
|
log {
|
||||||
|
size = 100k
|
||||||
|
count = 4
|
||||||
|
}
|
||||||
|
|
||||||
# Services to be monitored and respawned as needed
|
# Services to be monitored and respawned as needed
|
||||||
service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon
|
service watchdog {
|
||||||
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon
|
description = "System watchdog daemon"
|
||||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
runlevel = "S12345"
|
||||||
service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab)
|
envfile = "-/etc/conf.d/watchdog"
|
||||||
|
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. A single
|
# a dependency on a system default route (gateway) to be set. ntpd
|
||||||
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
# does not respect SIGHUP, so Finit restarts it on reload instead.
|
||||||
service [2345] log:/var/log/ntpd.log <!net/route/default> ntpd -n -l -I eth0 -- NTP daemon
|
service ntpd {
|
||||||
|
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 somewhere between
|
# For multiple instances of the same service, add :ID to the block title.
|
||||||
# the service/run/task keyword and the command.
|
service merecat:80 {
|
||||||
service :80 [2345] merecat -n -p 80 /var/www -- Web server
|
description = "Web server"
|
||||||
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
|
runlevel = "2345"
|
||||||
|
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 [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap
|
#sysv keyboard-setup {
|
||||||
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
|
# description = "Setting up preliminary keymap"
|
||||||
#task [S] /etc/init.d/kbd -- Preparing console
|
# runlevel = "S"
|
||||||
|
# command = "/etc/init.d/keyboard-setup"
|
||||||
|
#}
|
||||||
|
|
||||||
# Hidden from boot progress, using empty `--` description
|
# Hidden from boot progress, using an empty description
|
||||||
#sysv [S] /etc/init.d/keyboard-setup --
|
#sysv acpid {
|
||||||
#sysv [S] /etc/init.d/acpid --
|
# description = ""
|
||||||
#task [S] /etc/init.d/kbd --
|
# runlevel = "S"
|
||||||
|
# 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 [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear
|
tty tty1 {
|
||||||
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear
|
runlevel = "12345"
|
||||||
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear
|
command = "/sbin/getty -L 0 /dev/tty1 linux"
|
||||||
|
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 [12345] /dev/ttyAMA0 noclear nowait
|
#tty ttyAMA0 { runlevel = "12345" device = "/dev/ttyAMA0" noclear = true nowait = true }
|
||||||
#tty [12345] /dev/ttyUSB0 noclear
|
#tty ttyUSB0 { runlevel = "12345" device = "/dev/ttyUSB0" noclear = true }
|
||||||
|
|
||||||
# Just give me a shell, I need to debug this embedded system!
|
# Just give me a shell, I need to debug this embedded system!
|
||||||
#tty [12345] console noclear nologin
|
#tty console { runlevel = "12345" device = "@console" noclear = true nologin = true }
|
||||||
```
|
```
|
||||||
|
|
||||||
The `service` stanza, as well as `task`, `run` and others are described in
|
The `service` block, 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 [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Example daemon
|
service NAME { <-- Supervised program (daemon)
|
||||||
^ ^ ^ ^ ^ ^ ^ ^
|
description = "Example daemon" <-- Optional description
|
||||||
| | | | | | | `---------- Optional description
|
runlevel = "2345" <-- Optional runlevels
|
||||||
| | | | | | `------------------ Daemon arguments
|
conditions = { "net/route/default" } <-- Optional conditions
|
||||||
| | | | | `------------------------- Path to daemon
|
envfile = "-/etc/default/daemon" <-- Optional env. file
|
||||||
| | | | `---------------------------------------------------- Optional env. file
|
log { } <-- Redirect output to log
|
||||||
| | | `-------------------------------------------------------- Redirect output to log
|
command = "daemon ARGS" <-- Path to daemon, and its arguments
|
||||||
| | `--------------------------------------------------------------- Optional conditions
|
}
|
||||||
| `---------------------------------------------------------------------- Optional Runlevels
|
|
||||||
`------------------------------------------------------------------------------ Supervised program (daemon)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Some components are optional: runlevel(s), condition(s) and description,
|
Only `command` is required, which makes simple cases short while leaving
|
||||||
making it easy to create simple start scripts and still possible for more
|
room for more advanced uses:
|
||||||
advanced uses as well:
|
|
||||||
|
|
||||||
service /usr/sbin/sshd -D
|
service sshd {
|
||||||
|
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 <net/route/default> nginx -- High performance HTTP server
|
service nginx {
|
||||||
|
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 [2345] log <!pid/syslogd> ntpd -n -N -p pool.ntp.org
|
service ntpd {
|
||||||
service [S12345] syslogd -n -- Syslog daemon
|
runlevel = "2345"
|
||||||
|
conditions = { "pid/syslogd" }
|
||||||
|
reload-signal = "none"
|
||||||
|
log { }
|
||||||
|
command = "ntpd -n -N -p pool.ntp.org"
|
||||||
|
}
|
||||||
|
|
||||||
Notice the `log` keyword, BusyBox `ntpd` uses `stderr` for logging when
|
service syslogd {
|
||||||
run in the foreground. With `log` Finit redirects `stdout` + `stderr`
|
description = "Syslog daemon"
|
||||||
to the system log daemon using the command line `logger(1)` tool.
|
runlevel = "S12345"
|
||||||
|
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 [2345] log <pid/syslogd,net/route/default> ntpd -n -N -p pool.ntp.org
|
service ntpd {
|
||||||
|
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.
|
||||||
|
|||||||
+66
-26
@@ -18,9 +18,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 [12345] /dev/tty1 nowait linux
|
tty tty1 { runlevel = "12345" device = "/dev/tty1" term = "linux" nowait = true }
|
||||||
tty [12345] /dev/ttyAMA0 noclear vt100
|
tty ttyAMA0 { runlevel = "12345" device = "/dev/ttyAMA0" term = "vt100" noclear = true }
|
||||||
tty [12345] /sbin/getty -L /dev/ttyAMA0 vt100
|
tty getty { runlevel = "12345" command = "/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 +29,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 [12345] @console linux noclear
|
tty console { runlevel = "12345" device = "@console" term = "linux" noclear = true }
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
|
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
|
||||||
@@ -40,21 +40,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 `[2345]` syntax can be
|
minimal style as everything else in Finit. The `runlevel` setting
|
||||||
applied to service, task, run, and TTY stanzas.
|
applies to service, task, run, and tty blocks alike.
|
||||||
|
|
||||||
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 NUM` from `/etc/finit.conf`
|
networking here. The configured `runlevel` 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 `run [S] cmd`. Changing runlevels at runtime is done like any
|
by using a `run` block with `runlevel = "S"`. Changing runlevels at
|
||||||
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
|
runtime is done like any other init, e.g. <kbd>init 4</kbd>, but also
|
||||||
[`initctl`](initctl.md) tool.
|
using the more advanced [`initctl`](initctl.md) tool.
|
||||||
|
|
||||||
|
|
||||||
**Conditions**
|
**Conditions**
|
||||||
@@ -86,8 +86,17 @@ the condition `<hw/model/foo>` when starting other scripts. Here is an
|
|||||||
example:
|
example:
|
||||||
|
|
||||||
```
|
```
|
||||||
run [S] /path/to/ident --
|
run ident {
|
||||||
task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board
|
description = ""
|
||||||
|
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]
|
||||||
@@ -117,7 +126,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
|
||||||
@@ -141,17 +150,20 @@ required privileges instead of running as root. This improves security by
|
|||||||
following the principle of least privilege.
|
following the principle of least privilege.
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
service [2345] name:nginx \
|
service nginx {
|
||||||
www-data:www-data \
|
runlevel = "2345"
|
||||||
caps:^cap_net_bind_service \
|
user = "www-data"
|
||||||
/usr/sbin/nginx -g 'daemon off;'
|
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
|
In this example, nginx runs as the unprivileged `www-data` user but retains
|
||||||
the ability to bind to privileged ports (80, 443) through the
|
the ability to bind to privileged ports (80, 443) through the
|
||||||
`cap_net_bind_service` capability.
|
`cap_net_bind_service` capability.
|
||||||
|
|
||||||
The `caps:` directive uses the IAB (Inheritable, Ambient, Bounding) format:
|
The `capabilities` list uses the IAB (Inheritable, Ambient, Bounding) format:
|
||||||
- `^` = Ambient (recommended) - capabilities survive exec()
|
- `^` = Ambient (recommended) - capabilities survive exec()
|
||||||
- `%` = Inheritable only - requires file capabilities
|
- `%` = Inheritable only - requires file capabilities
|
||||||
- `!` = Bounding - block from acquiring capability
|
- `!` = Bounding - block from acquiring capability
|
||||||
@@ -159,13 +171,33 @@ The `caps:` directive uses the IAB (Inheritable, Ambient, Bounding) format:
|
|||||||
Multiple capabilities can be specified as comma-separated:
|
Multiple capabilities can be specified as comma-separated:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
caps:^cap_net_raw,^cap_net_admin,!cap_sys_admin
|
capabilities = { "^cap_net_raw", "^cap_net_admin", "!cap_sys_admin" }
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [Linux Capabilities](config/capabilities.md) section for detailed
|
See the [Linux Capabilities](config/capabilities.md) section for detailed
|
||||||
information, examples, and security best practices.
|
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**
|
**Supplementary Groups**
|
||||||
|
|
||||||
Finit supports supplementary groups for services, allowing them to access
|
Finit supports supplementary groups for services, allowing them to access
|
||||||
@@ -210,15 +242,19 @@ 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 stanza like this:
|
declared as such in their service block like this:
|
||||||
|
|
||||||
service [...] <...> cgroup.root /path/to/foo args -- description
|
service foo {
|
||||||
|
cgroup root {}
|
||||||
|
command = "/path/to/foo args"
|
||||||
|
}
|
||||||
|
|
||||||
or
|
Every block names the group it joins, so a second RT task says so too:
|
||||||
|
|
||||||
cgroup.root
|
service bar {
|
||||||
service [...] <...> /path/to/foo args -- description
|
cgroup root {}
|
||||||
service [...] <...> /path/to/bar args -- description
|
command = "/path/to/bar args"
|
||||||
|
}
|
||||||
|
|
||||||
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.
|
||||||
@@ -259,6 +295,10 @@ initctl top # Interactive resource monitor
|
|||||||
initctl cond set usr/custom # Set custom condition
|
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.
|
See the [Commands & Status](initctl.md) section for complete documentation.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ Features
|
|||||||
* [Pre/Post script actions](config/service-wrappers.md)
|
* [Pre/Post script actions](config/service-wrappers.md)
|
||||||
* Rudimentary [templating support](config/templating.md)
|
* Rudimentary [templating support](config/templating.md)
|
||||||
* [Tooling](initctl.md) to enable/disable services
|
* [Tooling](initctl.md) to enable/disable services
|
||||||
|
* [D-Bus API](dbus.md), `org.finit` control and signals for bus clients
|
||||||
* [Automatic reload](features.md#automatic-reload) of modified configuration files (optional)
|
* [Automatic reload](features.md#automatic-reload) of modified configuration files (optional)
|
||||||
* [Built-in getty](config/tty.md)
|
* [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][]
|
||||||
|
|||||||
+11
-10
@@ -5,6 +5,11 @@ Finit also implements a modern API to query status, and start/stop
|
|||||||
services, called `initctl`. Unlike `telinit` the `initctl` tool does
|
services, called `initctl`. Unlike `telinit` the `initctl` tool does
|
||||||
not return until the given command has fully completed.
|
not return until the given command has fully completed.
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
@@ -14,6 +19,7 @@ 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
|
||||||
@@ -26,15 +32,8 @@ Commands:
|
|||||||
help This help text
|
help This help text
|
||||||
version Show program version
|
version Show program version
|
||||||
|
|
||||||
ls | list List all .conf in /etc/finit.d
|
show Show /etc/finit.conf
|
||||||
create <CONF> Create .conf in /etc/finit.d/available
|
reload Reload /etc/finit.conf (activate changes)
|
||||||
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
|
||||||
@@ -50,6 +49,7 @@ 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,9 +65,10 @@ 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 NEWROOT [INIT] Switch to new root filesystem (initramfs only)
|
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
|
||||||
|
|||||||
+534
-11
@@ -1,14 +1,537 @@
|
|||||||
keventd
|
Bundled Device Manager
|
||||||
=======
|
======================
|
||||||
|
|
||||||
The kernel event daemon bundled with Finit is a simple uevent monitor
|
The kernel event daemon `keventd` is a built-in device manager bundled
|
||||||
for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition,
|
with Finit. It replaces the need for external device managers like
|
||||||
which can be useful to prevent power hungry services like anacron to run
|
mdev, mdevd, or udevd on systems where a lighter-weight solution is
|
||||||
when a laptop is only running on battery, for instance.
|
preferred, particularly on embedded systems.
|
||||||
|
|
||||||
Since keventd is not an integral part of Finit yet it is not enabled by
|
It is enabled by default since Finit v5, and needs libblkid from
|
||||||
default. Enable it using `./configure --with-keventd`. The bundled
|
util-linux (the `libblkid-dev` package, or whatever your distribution
|
||||||
`contrib/` build scripts for Debian, Alpine, and Void have this enabled.
|
calls it) to read the filesystem UUID and label behind the
|
||||||
|
`/dev/disk/by-uuid/` and `/dev/disk/by-label/` symlinks. To disable it
|
||||||
|
and use an external device manager instead:
|
||||||
|
|
||||||
This daemon is planned to be extended with monitoring of other uevents,
|
./configure --without-keventd
|
||||||
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
|
||||||
|
|||||||
+11
-3
@@ -35,7 +35,15 @@ 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.
|
||||||
_Optional plugin._
|
Enabled by default, and a no-op on systems without a `dbus-daemon`.
|
||||||
|
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._
|
||||||
@@ -70,8 +78,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 the `task` stanza.
|
Modules are by default loaded in runlevel `S` using a `task` block.
|
||||||
Each module gets a unique `name:modprobe.foo`, and optional`:ID`. The
|
Each module is named `modprobe.foo`, with an optional `:ID`. The
|
||||||
runlevel can be changed per file using:
|
runlevel can be changed per file using:
|
||||||
|
|
||||||
set runlevel 2345
|
set runlevel 2345
|
||||||
|
|||||||
+5
-1
@@ -15,7 +15,11 @@ 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 [S] udevadm settle --timeout=120 -- Waiting for udev
|
run udevadm {
|
||||||
|
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`
|
||||||
|
|||||||
+36
-13
@@ -13,20 +13,37 @@ 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`, add `[NNN]` to your `/etc/finit.conf`, like this:
|
`task`, or `tty`, set `runlevel` in your `/etc/finit.conf`, like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
service [S12345] syslogd -n -x -- System log daemon
|
service syslogd {
|
||||||
run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
description = "System log daemon"
|
||||||
task [S] /etc/init.d/kbd start -- Preparing console
|
runlevel = "S12345"
|
||||||
service [S12345] <pid/syslogd> klogd -n -x -- Kernel log daemon
|
command = "syslogd -n -x"
|
||||||
|
}
|
||||||
|
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 [12345] /dev/tty1
|
tty tty1 { runlevel = "12345" device = "/dev/tty1" }
|
||||||
tty [2] /dev/tty2
|
tty tty2 { runlevel = "2" device = "/dev/tty2" }
|
||||||
tty [2] /dev/tty3
|
tty tty3 { runlevel = "2" device = "/dev/tty3" }
|
||||||
tty [2] /dev/tty4
|
tty tty4 { runlevel = "2" device = "/dev/tty4" }
|
||||||
tty [2] /dev/tty5
|
tty tty5 { runlevel = "2" device = "/dev/tty5" }
|
||||||
tty [2] /dev/tty6
|
tty tty6 { runlevel = "2" device = "/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
|
||||||
@@ -46,8 +63,14 @@ are also removed when they have completed, `initctl show` will not list
|
|||||||
them.
|
them.
|
||||||
|
|
||||||
```
|
```
|
||||||
task [S] echo "foo" | cat >/tmp/bar
|
task foo {
|
||||||
run [S] echo "$HOME" >/tmp/secret
|
runlevel = "S"
|
||||||
|
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
@@ -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.)
|
||||||
|
|
||||||
```shell
|
```aconf
|
||||||
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 configuration stanza in `/etc/finit.conf` is required for `rc.local`.
|
No setting 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.
|
||||||
|
|||||||
+30
-9
@@ -27,13 +27,22 @@ Finit can *not* start and monitor a daemon that:
|
|||||||
|
|
||||||
### Forks to bg w/ PID file
|
### Forks to bg w/ PID file
|
||||||
|
|
||||||
There are two syntax variants, type 1 and type 2. The former is the
|
There are two variants. The former names the pidfile to watch, as for
|
||||||
traditional one used also for `sysv` start/stop scripts, and the latter
|
`sysv` start/stop scripts, and the latter is inspired by systemd, with a
|
||||||
is inspired by systemd, with a twist -- it lets Finit guess the pifdile
|
twist -- it lets Finit guess the pidfile based on the standard path and
|
||||||
to look for based on the standard path and the basename of the command.
|
the basename of the command.
|
||||||
|
|
||||||
service pid:!/run/serv.pid serv -- Forking service, type 1
|
service serv {
|
||||||
service type:forking serv -- Forking service, type 2
|
description = "Forking service, type 1"
|
||||||
|
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
|
||||||
@@ -41,17 +50,29 @@ the same directory (`/var/run` is a symlink to `../run`).
|
|||||||
|
|
||||||
### Runs in fg w/ PID file
|
### Runs in fg w/ PID file
|
||||||
|
|
||||||
service serv -n -p -- Foreground service w/ PID file
|
service serv {
|
||||||
|
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 pid:/run/serv.pid serv -n -- Foreground service w/o PID file
|
service serv {
|
||||||
|
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 pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file
|
service serv {
|
||||||
|
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
|
||||||
|
|||||||
+55
-10
@@ -51,26 +51,52 @@ Configuration file `/etc/finit.conf` in the initramfs:
|
|||||||
# /etc/finit.conf in initramfs
|
# /etc/finit.conf in initramfs
|
||||||
|
|
||||||
# Mount the real root filesystem
|
# Mount the real root filesystem
|
||||||
run [S] name:mount-root /bin/mount /dev/sda1 /mnt/root -- Mounting 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
|
# Switch to real root after mount completes
|
||||||
run [S] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root
|
run switch-root {
|
||||||
|
description = "Switching to real root"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/sbin/initctl switch-root /mnt/root"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For more complex setups (LUKS, LVM, etc.):
|
For more complex setups (LUKS, LVM, etc.):
|
||||||
|
|
||||||
```
|
```
|
||||||
# Unlock LUKS volume
|
# Unlock LUKS volume
|
||||||
run [S] name:cryptsetup /sbin/cryptsetup open /dev/sda2 cryptroot -- Unlocking encrypted root
|
# 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
|
# Activate LVM
|
||||||
run [S] name:lvm /sbin/lvm vgchange -ay -- Activating LVM volumes
|
run lvm {
|
||||||
|
description = "Activating LVM volumes"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/sbin/lvm vgchange -ay"
|
||||||
|
}
|
||||||
|
|
||||||
# Mount root
|
# Mount root
|
||||||
run [S] name:mount-root /bin/mount /dev/vg0/root /mnt/root -- Mounting root
|
run mount-root {
|
||||||
|
description = "Mounting root"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/bin/mount /dev/vg0/root /mnt/root"
|
||||||
|
}
|
||||||
|
|
||||||
# Switch root
|
# Switch root
|
||||||
run [S] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root
|
run switch-root {
|
||||||
|
description = "Switching to real root"
|
||||||
|
runlevel = "S"
|
||||||
|
command = "/sbin/initctl switch-root /mnt/root"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -84,15 +110,34 @@ difficult in runlevel S, you can perform the switch-root in runlevel 1:
|
|||||||
# /etc/finit.conf in initramfs
|
# /etc/finit.conf in initramfs
|
||||||
|
|
||||||
# Start mdevd for device handling
|
# Start mdevd for device handling
|
||||||
service [S] name:mdevd notify:s6 /sbin/mdevd -D %n -- Device event daemon
|
service mdevd {
|
||||||
run [S] name:coldplug <service/mdevd/ready> /sbin/mdevd-coldplug -- Coldplug devices
|
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)
|
# Mount the real root filesystem (after devices are ready)
|
||||||
run [S] name:mount-root <run/coldplug/success> /bin/mount /dev/sda1 /mnt/root -- Mounting root
|
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
|
# Transition to runlevel 1 after all S tasks complete
|
||||||
# The switch-root runs cleanly in runlevel 1
|
# The switch-root runs cleanly in runlevel 1
|
||||||
run [1] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root
|
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
|
This approach separates the initramfs setup (runlevel S) from the
|
||||||
|
|||||||
+4
-4
@@ -1,10 +1,10 @@
|
|||||||
Bundled Watchdog Daemon
|
Bundled Watchdog Daemon
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
When built `--with-watchdog` a separate service is built and installed
|
A separate service is built and installed in `/libexec/finit/watchdogd`,
|
||||||
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
|
unless disabled with `--without-watchdog`. If this exists at runtime,
|
||||||
device node exists, Finit will start it and treat it as the elected
|
and the WDT device node exists, Finit will start it and treat it as the
|
||||||
watchdog service to delegate its reboot to. This delegation is to
|
elected 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
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.deps/
|
||||||
|
keventd
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
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
|
||||||
+1529
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
/* Builtin command dispatch for keventd rules engine
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Joachim Wiberg <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEVENTD_BUILTIN_H_
|
||||||
|
#define KEVENTD_BUILTIN_H_
|
||||||
|
|
||||||
|
#include "keventd.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute a builtin command from a udev rule.
|
||||||
|
* cmd is the full command string, e.g. "hwdb --subsystem=input" or "path_id".
|
||||||
|
* Returns 0 on success, -1 if the builtin is unknown or fails.
|
||||||
|
*/
|
||||||
|
int builtin_run(struct uevent *ev, const char *cmd);
|
||||||
|
|
||||||
|
/* True if NAME is a registered builtin (e.g. "path_id", "usb_id"). */
|
||||||
|
int builtin_has(const char *name);
|
||||||
|
|
||||||
|
#endif /* KEVENTD_BUILTIN_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+577
@@ -0,0 +1,577 @@
|
|||||||
|
/* keventd D-Bus interface: org.finit.Device1 on its own local bus
|
||||||
|
*
|
||||||
|
* keventd serves /run/keventd/bus the same way Finit serves
|
||||||
|
* /run/finit/bus: brokerless, libink, one socket per daemon. No
|
||||||
|
* message forwarding exists between the two, by design -- clients
|
||||||
|
* that want both connect to both.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Joachim Wiberg <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <uev/uev.h>
|
||||||
|
|
||||||
|
#ifdef _LIBITE_LITE
|
||||||
|
# include <libite/lite.h>
|
||||||
|
#else
|
||||||
|
# include <lite/lite.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "link.h"
|
||||||
|
|
||||||
|
#include "keventd.h"
|
||||||
|
#include "udevdb.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define DEVBUS_DIR "/run/keventd"
|
||||||
|
#define DEVBUS_SOCKET DEVBUS_DIR "/bus"
|
||||||
|
#define DEVBUS_PATH_OBJECT "/org/finit/device"
|
||||||
|
#define DEVBUS_INTERFACE "org.finit.Device1"
|
||||||
|
#define DEVBUS_MAX_PEERS 16
|
||||||
|
#define DEVBUS_MAX_SETTLES 8
|
||||||
|
|
||||||
|
struct peer {
|
||||||
|
uev_t watcher;
|
||||||
|
link_connection_t *conn;
|
||||||
|
int dead;
|
||||||
|
TAILQ_ENTRY(peer) link;
|
||||||
|
};
|
||||||
|
|
||||||
|
static TAILQ_HEAD(, peer) peers = TAILQ_HEAD_INITIALIZER(peers);
|
||||||
|
static TAILQ_HEAD(, peer) reapq = TAILQ_HEAD_INITIALIZER(reapq);
|
||||||
|
static link_server_t *server;
|
||||||
|
static uev_t accept_watcher;
|
||||||
|
static uev_t reap_timer;
|
||||||
|
static uev_ctx_t *devbus_ctx;
|
||||||
|
static size_t peer_count;
|
||||||
|
static int bus_passive;
|
||||||
|
|
||||||
|
/* Parked Settle() calls with their own deadlines */
|
||||||
|
struct settle {
|
||||||
|
link_connection_t *conn;
|
||||||
|
link_authz_t tok;
|
||||||
|
uint64_t deadline; /* CLOCK_MONOTONIC ms */
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct settle settles[DEVBUS_MAX_SETTLES];
|
||||||
|
static uev_t settle_timer;
|
||||||
|
static int settle_armed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same policy as /run/finit/bus: root and members of the --with-group
|
||||||
|
* group. The group set comes from the kernel (SO_PEERGROUPS), the
|
||||||
|
* owning gid is resolved once at init so no call ever hits NSS.
|
||||||
|
*/
|
||||||
|
static gid_t privileged_gid = (gid_t)-1; /* DEFGROUP, resolved at init */
|
||||||
|
|
||||||
|
static int caller_is_privileged(uid_t uid, const gid_t *groups, int ngroups,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
if (uid == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (privileged_gid == (gid_t)-1)
|
||||||
|
return 0; /* no owning group to match against */
|
||||||
|
|
||||||
|
for (int i = 0; i < ngroups; i++) {
|
||||||
|
if (groups[i] == privileged_gid)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void link_log_cb(void *userdata, const char *func, const char *msg)
|
||||||
|
{
|
||||||
|
(void)userdata;
|
||||||
|
logit(LOG_DEBUG, "%s():%s", func, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- peer lifecycle, same shape as src/dbus.c ---------- */
|
||||||
|
|
||||||
|
static void peer_reap(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
struct peer *p;
|
||||||
|
|
||||||
|
(void)w; (void)arg; (void)events;
|
||||||
|
while ((p = TAILQ_FIRST(&reapq))) {
|
||||||
|
TAILQ_REMOVE(&reapq, p, link);
|
||||||
|
link_connection_close(p->conn);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void peer_drop(struct peer *p)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (p->dead)
|
||||||
|
return;
|
||||||
|
p->dead = 1;
|
||||||
|
|
||||||
|
uev_io_stop(&p->watcher);
|
||||||
|
TAILQ_REMOVE(&peers, p, link);
|
||||||
|
peer_count--;
|
||||||
|
|
||||||
|
/* Settles parked on this connection die with it */
|
||||||
|
for (i = 0; i < DEVBUS_MAX_SETTLES; i++) {
|
||||||
|
if (settles[i].conn == p->conn)
|
||||||
|
settles[i].conn = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_INSERT_TAIL(&reapq, p, link);
|
||||||
|
uev_timer_set(&reap_timer, 10, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void peer_cb(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
struct peer *p = arg;
|
||||||
|
|
||||||
|
(void)w;
|
||||||
|
|
||||||
|
if (UEV_ERROR == events || link_connection_process(p->conn) < 0)
|
||||||
|
peer_drop(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void accept_cb(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
logit(LOG_ERR, "D-Bus accept watcher error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
link_connection_t *conn = NULL;
|
||||||
|
struct peer *p;
|
||||||
|
|
||||||
|
if (link_server_accept(server, &conn) < 0) {
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||||
|
logit(LOG_ERR, "Failed accepting D-Bus client: %s",
|
||||||
|
strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peer_count >= DEVBUS_MAX_PEERS) {
|
||||||
|
logit(LOG_WARNING, "D-Bus peer cap reached (%zu), dropping",
|
||||||
|
peer_count);
|
||||||
|
link_connection_close(conn);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = calloc(1, sizeof(*p));
|
||||||
|
if (!p) {
|
||||||
|
link_connection_close(conn);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->conn = conn;
|
||||||
|
TAILQ_INSERT_TAIL(&peers, p, link);
|
||||||
|
peer_count++;
|
||||||
|
|
||||||
|
if (uev_io_init(w->ctx, &p->watcher, peer_cb, p,
|
||||||
|
link_connection_get_fd(conn), UEV_READ))
|
||||||
|
peer_drop(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- parked Settle() bookkeeping ---------- */
|
||||||
|
|
||||||
|
static void settle_sweep(uev_t *w, void *arg, int events);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The queue can only drain when an event is processed, and
|
||||||
|
* devbus_notify() sweeps on exactly that; the timer exists solely to
|
||||||
|
* enforce deadlines, so arm it one-shot for the nearest one.
|
||||||
|
*/
|
||||||
|
static void settle_arm(uint64_t deadline)
|
||||||
|
{
|
||||||
|
uint64_t now = kev_now_ms();
|
||||||
|
int ms = deadline > now ? (int)(deadline - now) : 1;
|
||||||
|
|
||||||
|
if (!settle_armed) {
|
||||||
|
uev_timer_init(devbus_ctx, &settle_timer, settle_sweep,
|
||||||
|
NULL, ms, 0);
|
||||||
|
settle_armed = 1;
|
||||||
|
} else {
|
||||||
|
uev_timer_set(&settle_timer, ms, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resume parked settles: all of them when the queue drained (the
|
||||||
|
* handler re-runs, sees the empty queue, replies true), expired ones
|
||||||
|
* otherwise (the handler re-runs, sees link_call_resumed(), replies
|
||||||
|
* false).
|
||||||
|
*/
|
||||||
|
static void settle_sweep(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
uint64_t next = 0;
|
||||||
|
uint64_t now = kev_now_ms();
|
||||||
|
int empty = kev_queue_empty();
|
||||||
|
int i;
|
||||||
|
|
||||||
|
(void)w; (void)arg; (void)events;
|
||||||
|
|
||||||
|
for (i = 0; i < DEVBUS_MAX_SETTLES; i++) {
|
||||||
|
struct settle *s = &settles[i];
|
||||||
|
|
||||||
|
if (!s->conn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (empty || now >= s->deadline) {
|
||||||
|
link_connection_t *conn = s->conn;
|
||||||
|
link_authz_t tok = s->tok;
|
||||||
|
|
||||||
|
s->conn = NULL;
|
||||||
|
link_call_resume(conn, tok);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!next || s->deadline < next)
|
||||||
|
next = s->deadline;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
settle_arm(next);
|
||||||
|
} else if (settle_armed) {
|
||||||
|
uev_timer_stop(&settle_timer);
|
||||||
|
settle_armed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- org.finit.Device1 ---------- */
|
||||||
|
|
||||||
|
static int device1_settle(link_call_t *call, void *u)
|
||||||
|
{
|
||||||
|
link_writer_t *w;
|
||||||
|
uint32_t timeout;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
int empty;
|
||||||
|
|
||||||
|
(void)u;
|
||||||
|
if (link_call_read_u32(call, &timeout) < 0)
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
|
"expected (u)");
|
||||||
|
|
||||||
|
empty = kev_queue_empty();
|
||||||
|
if (!empty && !link_call_resumed(call)) {
|
||||||
|
for (i = 0; i < DEVBUS_MAX_SETTLES; i++) {
|
||||||
|
if (settles[i].conn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (link_call_park(call, &settles[i].tok))
|
||||||
|
break;
|
||||||
|
|
||||||
|
settles[i].conn = link_call_connection(call);
|
||||||
|
settles[i].deadline = kev_now_ms() + (uint64_t)timeout * 1000;
|
||||||
|
settle_arm(settles[i].deadline);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* no room to wait: answer with the state as it stands */
|
||||||
|
}
|
||||||
|
|
||||||
|
w = link_call_reply(call);
|
||||||
|
link_w_bool(w, empty);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device1_trigger(link_call_t *call, void *u)
|
||||||
|
{
|
||||||
|
const char *action, *glob;
|
||||||
|
|
||||||
|
(void)u;
|
||||||
|
if (link_call_read_string(call, &action) < 0 ||
|
||||||
|
link_call_read_string(call, &glob) < 0)
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
|
"expected (s, s)");
|
||||||
|
|
||||||
|
if (bus_passive)
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.finit.Error.Failed",
|
||||||
|
"keventd is passive, device events are not handled here");
|
||||||
|
|
||||||
|
if (*action && !uevent_action_valid(action))
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
|
"unknown uevent action");
|
||||||
|
|
||||||
|
if (coldplug_trigger(action, glob))
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.finit.Error.Failed", "trigger failed");
|
||||||
|
|
||||||
|
(void)link_call_reply(call);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device1_info(link_call_t *call, void *u)
|
||||||
|
{
|
||||||
|
struct uevent ev;
|
||||||
|
const char *devpath;
|
||||||
|
link_writer_t *w;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
(void)u;
|
||||||
|
if (link_call_read_string(call, &devpath) < 0 || *devpath != '/')
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
|
"expected (s), an absolute devpath");
|
||||||
|
|
||||||
|
memset(&ev, 0, sizeof(ev));
|
||||||
|
if (udevdb_read_devpath(devpath, &ev)) {
|
||||||
|
uevent_env_free(&ev);
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.finit.Error.NoSuchDevice",
|
||||||
|
"no database record for devpath");
|
||||||
|
}
|
||||||
|
|
||||||
|
w = link_call_reply(call);
|
||||||
|
link_w_array_begin(w, '{');
|
||||||
|
for (i = 0; i < ev.nenv; i++) {
|
||||||
|
link_w_struct_begin(w);
|
||||||
|
link_w_string(w, ev.env_key[i]);
|
||||||
|
link_w_string(w, ev.env_val[i]);
|
||||||
|
link_w_struct_end(w);
|
||||||
|
}
|
||||||
|
link_w_array_end(w);
|
||||||
|
|
||||||
|
uevent_env_free(&ev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device1_rules_reload(link_call_t *call, void *u)
|
||||||
|
{
|
||||||
|
link_writer_t *w;
|
||||||
|
|
||||||
|
(void)u;
|
||||||
|
if (bus_passive)
|
||||||
|
return link_call_reply_error(call,
|
||||||
|
"org.finit.Error.Failed",
|
||||||
|
"keventd is passive, rules are not applied here");
|
||||||
|
|
||||||
|
w = link_call_reply(call);
|
||||||
|
link_w_u32(w, (uint32_t)kev_rules_reload());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prop_queue_empty(link_writer_t *w, void *u)
|
||||||
|
{
|
||||||
|
(void)u;
|
||||||
|
link_w_bool(w, kev_queue_empty());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prop_seqnum(link_writer_t *w, void *u)
|
||||||
|
{
|
||||||
|
(void)u;
|
||||||
|
link_w_u64(w, kev_seq_processed());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const link_method_t device_methods[] = {
|
||||||
|
{ .name = "Settle", .in_sig = "u", .out_sig = "b",
|
||||||
|
.handler = device1_settle },
|
||||||
|
{ .name = "Trigger", .in_sig = "ss", .out_sig = "",
|
||||||
|
.flags = LINK_METHOD_PRIVILEGED, .handler = device1_trigger },
|
||||||
|
{ .name = "Info", .in_sig = "s", .out_sig = "a{ss}",
|
||||||
|
.handler = device1_info },
|
||||||
|
{ .name = "RulesReload", .in_sig = "", .out_sig = "u",
|
||||||
|
.flags = LINK_METHOD_PRIVILEGED, .handler = device1_rules_reload },
|
||||||
|
{ NULL, NULL, NULL, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const link_property_t device_properties[] = {
|
||||||
|
{ .name = "QueueEmpty", .sig = "b", .getter = prop_queue_empty },
|
||||||
|
{ .name = "SeqnumProcessed", .sig = "t", .getter = prop_seqnum },
|
||||||
|
{ NULL, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const link_signal_t device_signals[] = {
|
||||||
|
{ .name = "DeviceProcessed", .sig = "ss" },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const link_vtable_t device_vtable = {
|
||||||
|
.interface = DEVBUS_INTERFACE,
|
||||||
|
.methods = device_methods,
|
||||||
|
.properties = device_properties,
|
||||||
|
.signals = device_signals,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------- signal emission + settle nudge ---------- */
|
||||||
|
|
||||||
|
static void devbus_emit_signal(const char *member, const char *sig,
|
||||||
|
const uint8_t *body, size_t len)
|
||||||
|
{
|
||||||
|
struct peer *p, *tmp;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(p, &peers, link, tmp) {
|
||||||
|
if (link_connection_emit_signal(p->conn, DEVBUS_PATH_OBJECT,
|
||||||
|
DEVBUS_INTERFACE, member,
|
||||||
|
sig, body, len) >= 0)
|
||||||
|
continue;
|
||||||
|
if (errno == EMSGSIZE || errno == EINVAL)
|
||||||
|
break; /* nothing on the wire, same for all */
|
||||||
|
peer_drop(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void devbus_notify(const char *devpath, const char *action)
|
||||||
|
{
|
||||||
|
uint8_t body[512];
|
||||||
|
link_writer_t w;
|
||||||
|
ssize_t blen;
|
||||||
|
|
||||||
|
if (!server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* A processed event may have been the last one in flight */
|
||||||
|
if (settle_armed)
|
||||||
|
settle_sweep(NULL, NULL, 0);
|
||||||
|
|
||||||
|
if (TAILQ_EMPTY(&peers) || !devpath)
|
||||||
|
return;
|
||||||
|
|
||||||
|
link_writer_init(&w, body, sizeof(body));
|
||||||
|
link_w_string(&w, devpath);
|
||||||
|
link_w_string(&w, action ?: "");
|
||||||
|
blen = link_writer_finish(&w);
|
||||||
|
if (blen < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
devbus_emit_signal("DeviceProcessed", "ss", body, (size_t)blen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- init / exit / settle client ---------- */
|
||||||
|
|
||||||
|
int devbus_init(uev_ctx_t *ctx, int passive)
|
||||||
|
{
|
||||||
|
devbus_ctx = ctx;
|
||||||
|
bus_passive = passive;
|
||||||
|
|
||||||
|
link_set_logger(link_log_cb, NULL);
|
||||||
|
|
||||||
|
if (mkpath(DEVBUS_DIR, 0755) && errno != EEXIST) {
|
||||||
|
logit(LOG_ERR, "Failed creating %s: %s", DEVBUS_DIR,
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link_server_new(&server, DEVBUS_SOCKET, 0660) < 0) {
|
||||||
|
logit(LOG_ERR, "Failed creating D-Bus socket %s: %s",
|
||||||
|
DEVBUS_SOCKET, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resolve the owning group once, see caller_is_privileged() */
|
||||||
|
privileged_gid = getgroup(DEFGROUP);
|
||||||
|
if (chown(DEVBUS_SOCKET, geteuid(), privileged_gid))
|
||||||
|
logit(LOG_WARNING, "Failed setting group %s on %s: %s",
|
||||||
|
DEFGROUP, DEVBUS_SOCKET, strerror(errno));
|
||||||
|
|
||||||
|
link_server_set_authorizer(server, caller_is_privileged, NULL);
|
||||||
|
|
||||||
|
if (link_server_add_object(server, DEVBUS_PATH_OBJECT,
|
||||||
|
&device_vtable, NULL) < 0) {
|
||||||
|
logit(LOG_ERR, "Failed registering %s", DEVBUS_INTERFACE);
|
||||||
|
link_server_free(server);
|
||||||
|
server = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uev_timer_init(ctx, &reap_timer, peer_reap, NULL, 0, 0);
|
||||||
|
|
||||||
|
if (uev_io_init(ctx, &accept_watcher, accept_cb, NULL,
|
||||||
|
link_server_get_fd(server), UEV_READ)) {
|
||||||
|
logit(LOG_ERR, "Failed watching D-Bus socket");
|
||||||
|
link_server_free(server);
|
||||||
|
server = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
logit(LOG_NOTICE, "Serving %s on %s", DEVBUS_INTERFACE, DEVBUS_SOCKET);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void devbus_exit(void)
|
||||||
|
{
|
||||||
|
struct peer *p;
|
||||||
|
|
||||||
|
if (!server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((p = TAILQ_FIRST(&peers)))
|
||||||
|
peer_drop(p);
|
||||||
|
peer_reap(NULL, NULL, 0);
|
||||||
|
|
||||||
|
uev_io_stop(&accept_watcher);
|
||||||
|
link_server_free(server);
|
||||||
|
server = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Client side of Settle, for `keventd -S`: ask the running keventd.
|
||||||
|
* Returns 0 settled, 1 timeout, -1 when no bus answers (fall back to
|
||||||
|
* polling the kernel seqnum).
|
||||||
|
*/
|
||||||
|
int devbus_client_settle(int timeout_s)
|
||||||
|
{
|
||||||
|
link_client_t *c;
|
||||||
|
link_reader_t r;
|
||||||
|
const link_reply_t *reply;
|
||||||
|
int rc, settled = 0;
|
||||||
|
|
||||||
|
c = link_client_open(DEVBUS_SOCKET);
|
||||||
|
if (!c)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rc = link_client_call_v(c, DEVBUS_PATH_OBJECT, DEVBUS_INTERFACE,
|
||||||
|
"Settle", "u", (uint32_t)timeout_s);
|
||||||
|
if (rc != LINK_CALL_OK) {
|
||||||
|
link_client_close(c);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = link_client_reply(c);
|
||||||
|
if (reply && reply->body) {
|
||||||
|
link_reader_init(&r, reply->body, reply->body_len);
|
||||||
|
link_r_bool(&r, &settled);
|
||||||
|
}
|
||||||
|
link_client_close(c);
|
||||||
|
|
||||||
|
return settled ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_DBUS */
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user