mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f32e4d4b30 | ||
|
|
eff87b3dc5 | ||
|
|
ea4821fd18 | ||
|
|
6285668dcd | ||
|
|
449bc7d12b | ||
|
|
ee867e7f3b | ||
|
|
b4c7ae9c8c | ||
|
|
95995a45cf | ||
|
|
823bb82b8c | ||
|
|
9a5bb46f78 | ||
|
|
950ca953f5 | ||
|
|
495c8c9eac | ||
|
|
2887795586 | ||
|
|
6a893f50ae | ||
|
|
5d08353284 | ||
|
|
a37037ded4 | ||
|
|
d331b72dfd | ||
|
|
3b21d35f23 | ||
|
|
62a694b8b7 | ||
|
|
eeba2960f9 | ||
|
|
91550c926b | ||
|
|
184525687c | ||
|
|
cdede817fd | ||
|
|
df42c2f6fe | ||
|
|
b53f2f4a60 | ||
|
|
5ff27c4fe2 | ||
|
|
e5fff5117d | ||
|
|
615cd5aac3 | ||
|
|
621d95a89e | ||
|
|
cd383167f9 | ||
|
|
cb2969002c | ||
|
|
1cf204e23d | ||
|
|
393834b253 | ||
|
|
4ca8256e18 | ||
|
|
280d91b9bf | ||
|
|
980152fd53 | ||
|
|
d994420d4d | ||
|
|
38f3ecf01f | ||
|
|
5cd0d05b31 | ||
|
|
62519c3638 | ||
|
|
27bb2deb1a | ||
|
|
a2c8e71f14 | ||
|
|
d1f56e7c2e | ||
|
|
a9fd0cbf02 | ||
|
|
be8c89ec00 | ||
|
|
4f53752693 | ||
|
|
393673222d | ||
|
|
a88f4524d5 | ||
|
|
3a02066a66 | ||
|
|
7e3981d1a3 | ||
|
|
ec2900e4dd | ||
|
|
d718a27abf | ||
|
|
db8027911c | ||
|
|
aef3b4117d | ||
|
|
6728b77aca | ||
|
|
a7c0b42965 | ||
|
|
667b6ecbbd | ||
|
|
b852878a4a | ||
|
|
940f1f7486 | ||
|
|
49c705b5ad | ||
|
|
5163e8a433 | ||
|
|
71e9603e4e | ||
|
|
c71102f44e | ||
|
|
9b9158a765 | ||
|
|
595e2b4e81 | ||
|
|
ae8e205343 | ||
|
|
00b33fbd42 | ||
|
|
841b06df37 | ||
|
|
522a06f864 | ||
|
|
fa14ed1649 | ||
|
|
64e41e066b | ||
|
|
2a71f35005 | ||
|
|
961a337a5e | ||
|
|
f0622a692e | ||
|
|
ef7042dd9d | ||
|
|
046ecfa329 | ||
|
|
47fbe49861 | ||
|
|
2f34315634 | ||
|
|
c8d6ad6546 | ||
|
|
3dfae3074e | ||
|
|
97bebe52c9 | ||
|
|
8e1845f57b | ||
|
|
6cd6769997 | ||
|
|
03884d3566 | ||
|
|
899925e249 | ||
|
|
efefa398ce | ||
|
|
1ee24c69bc | ||
|
|
b16a36980c | ||
|
|
c1b52bbc04 | ||
|
|
9461ad4d4d | ||
|
|
e2ef6e9c34 | ||
|
|
cdc8873f5b | ||
|
|
b82b205d92 | ||
|
|
39d879f240 | ||
|
|
9c1db9cab3 | ||
|
|
2f5c00f2bd | ||
|
|
dc36b313ff | ||
|
|
093fa0107a | ||
|
|
e8f5ec2579 | ||
|
|
62241668df | ||
|
|
ee67c08b64 | ||
|
|
41a14391a2 | ||
|
|
6eccca31b9 | ||
|
|
d497942a9f | ||
|
|
68c7352108 | ||
|
|
0d40e43c4b | ||
|
|
9867fd82df | ||
|
|
1c941fdfbb | ||
|
|
0ba67a72f8 | ||
|
|
09b9e26b2d | ||
|
|
d25c1d5564 | ||
|
|
d4358ed3f7 | ||
|
|
cc6ffa35fc | ||
|
|
55e9cb2f58 | ||
|
|
2641256254 | ||
|
|
473116f603 | ||
|
|
e18d63df4a | ||
|
|
4b1b3f6913 | ||
|
|
f6da5e4136 | ||
|
|
d1d5d39144 | ||
|
|
754358113f | ||
|
|
c24207516d | ||
|
|
2a23bce78d | ||
|
|
a19b82ea5f | ||
|
|
ed9590aef1 | ||
|
|
25683221cc | ||
|
|
9cc9e3ddbb | ||
|
|
654f8aeb19 | ||
|
|
e349abb897 | ||
|
|
3027a6755f | ||
|
|
5404aa6037 | ||
|
|
a319e32438 | ||
|
|
2eaf57fc67 | ||
|
|
88ec4a55cb | ||
|
|
0acbc60ec1 | ||
|
|
4362971c6a | ||
|
|
94c0d1b833 | ||
|
|
1e795d4e16 | ||
|
|
93a027ca23 | ||
|
|
5f3958fab4 | ||
|
|
1351b708fe | ||
|
|
e867e039bb | ||
|
|
1d1087a3b3 | ||
|
|
e91f1ea1b2 | ||
|
|
55d16edadc | ||
|
|
b79245ddb3 | ||
|
|
e706300b6d | ||
|
|
d08cbd77aa | ||
|
|
b070ae73a4 | ||
|
|
2c30eef9a1 | ||
|
|
31cdc113fc | ||
|
|
f6f813498b | ||
|
|
4c8baaf7c0 | ||
|
|
57ca937f27 | ||
|
|
d13e39e345 | ||
|
|
f52c141062 | ||
|
|
40042da8ff | ||
|
|
c251304146 | ||
|
|
c047e37a79 | ||
|
|
d2c57447f0 | ||
|
|
79e9dabc11 | ||
|
|
b04dc4d457 | ||
|
|
f86e7810be | ||
|
|
2018c82ea1 | ||
|
|
c27f3603e6 | ||
|
|
6f82b806ad | ||
|
|
cbb8935660 | ||
|
|
21ebbb942f | ||
|
|
988ecd95e3 | ||
|
|
3f3e70c686 | ||
|
|
073b040981 | ||
|
|
b1a058ccdd | ||
|
|
cbc7b8c3c7 | ||
|
|
13340e4200 | ||
|
|
0076c1f218 | ||
|
|
6fc71b7528 | ||
|
|
b42f1e99e2 | ||
|
|
afd6ad06a6 | ||
|
|
ea0bfaa2f3 | ||
|
|
5c3ec176c2 | ||
|
|
40425ee9ec | ||
|
|
424db825a3 | ||
|
|
b9cc4cd629 | ||
|
|
243b8f025b | ||
|
|
6c60b67588 | ||
|
|
910bb13711 | ||
|
|
add55cfc2a | ||
|
|
565820325b | ||
|
|
6bb4d67d92 | ||
|
|
534d50c03a | ||
|
|
9a42883a96 | ||
|
|
7c77e2c2b6 | ||
|
|
ea8c46cd30 | ||
|
|
fead3b0671 | ||
|
|
081990b49e | ||
|
|
6b89f8c6db | ||
|
|
373097040a | ||
|
|
a4ab75172c | ||
|
|
5cf9958957 | ||
|
|
b137e08e84 | ||
|
|
a1af8e8687 | ||
|
|
53c7ddcb22 | ||
|
|
3872077ff1 | ||
|
|
9e85c4f3d1 | ||
|
|
2531102623 | ||
|
|
33deada6f0 | ||
|
|
9d1506bb7e | ||
|
|
c3f115c9ec | ||
|
|
430d402b14 | ||
|
|
e011dac98b | ||
|
|
42b84d1ccb | ||
|
|
288105eac7 | ||
|
|
842b522e68 | ||
|
|
e6f36974c8 | ||
|
|
679b4df881 | ||
|
|
666cae6f49 | ||
|
|
be2ac1d1b1 | ||
|
|
266e5b30eb | ||
|
|
4e5d06b580 | ||
|
|
fe334135ce | ||
|
|
aef10fb5e7 | ||
|
|
86707c1f0b | ||
|
|
a28d5b3c09 | ||
|
|
550ae9c784 | ||
|
|
cd5b91680a | ||
|
|
221ab20898 | ||
|
|
96534789db | ||
|
|
26128dd788 | ||
|
|
799e992542 | ||
|
|
fa524eec60 | ||
|
|
6dced29b49 | ||
|
|
2ea613a42b | ||
|
|
985df20519 | ||
|
|
f6161b9be9 | ||
|
|
25c5014d3c | ||
|
|
b8b7b53d96 | ||
|
|
7707c4b8e6 | ||
|
|
437f95f00f | ||
|
|
099672fabb | ||
|
|
860515a716 | ||
|
|
c85551a42e | ||
|
|
79dc6e7472 | ||
|
|
06e456384c | ||
|
|
38f7d24a80 | ||
|
|
145c78ef3e | ||
|
|
dd437bf0a5 | ||
|
|
d82d119729 | ||
|
|
7f76202865 | ||
|
|
aee92751ac | ||
|
|
2ea7236d45 | ||
|
|
cedc04e1e3 | ||
|
|
edc6eb1747 | ||
|
|
be5ec94fdf | ||
|
|
23a13fe1b6 | ||
|
|
0168b765c1 | ||
|
|
01358a5a45 | ||
|
|
106516d12d | ||
|
|
ba858743e5 | ||
|
|
9fd860ef3c | ||
|
|
46ccdc3820 | ||
|
|
242fe54afd | ||
|
|
df5d296cd9 | ||
|
|
dc4630f214 | ||
|
|
ac07d3bac7 | ||
|
|
f641eef8f7 | ||
|
|
f17b5b3cd3 | ||
|
|
d945f4960b | ||
|
|
6a5a6ac62f | ||
|
|
a9ba4300ec | ||
|
|
7bb649f11a | ||
|
|
85ada0ac99 | ||
|
|
be343a3b34 | ||
|
|
0ac0e5c7d3 | ||
|
|
7f272768e4 | ||
|
|
7b74c9900f | ||
|
|
36f1a39b31 | ||
|
|
6d380082eb | ||
|
|
8d3917e9fa | ||
|
|
c3cd1a5bfa | ||
|
|
eaeddc36c2 | ||
|
|
4f40b65f0c | ||
|
|
f958aa504e | ||
|
|
3d1b95aa7a | ||
|
|
4bb67bc06f | ||
|
|
0929c93caf | ||
|
|
c0368d2b16 | ||
|
|
85a212ef93 | ||
|
|
12c3cb3dc0 | ||
|
|
16710e7ab5 | ||
|
|
b652aaf0cb | ||
|
|
30020263ba | ||
|
|
3cf5d36972 | ||
|
|
c9ae96bcf7 | ||
|
|
5955110e67 | ||
|
|
4191e15c77 | ||
|
|
d60baa2b80 | ||
|
|
a50bc331db | ||
|
|
eff4453de9 | ||
|
|
4cfc8fec0e | ||
|
|
c9e1c2f80f | ||
|
|
93039e40cc | ||
|
|
015a52a20d | ||
|
|
d53cc8bfe7 | ||
|
|
3c4eca60b7 | ||
|
|
c85e28b971 | ||
|
|
d373068619 | ||
|
|
997f3d7862 | ||
|
|
f12f53692e | ||
|
|
b921b8cf0f | ||
|
|
61781c72fe | ||
|
|
3b5cfc5ecf | ||
|
|
a42a789d29 | ||
|
|
d77c014723 | ||
|
|
23fca9c067 | ||
|
|
37eb1901f9 | ||
|
|
98c4e7764f | ||
|
|
d3e4463612 | ||
|
|
b812045d82 | ||
|
|
b1bcdc88e0 | ||
|
|
62c5a9d06d | ||
|
|
dcdc842336 | ||
|
|
12eaa3073a | ||
|
|
34875f57a5 | ||
|
|
f0bf1343b0 | ||
|
|
4e6b9514d5 | ||
|
|
3b495d7518 | ||
|
|
badf8701bf | ||
|
|
85fa0d4926 | ||
|
|
3de9643893 | ||
|
|
73629944c0 | ||
|
|
c97c83bfb6 | ||
|
|
19b18afac9 | ||
|
|
e3c424e129 | ||
|
|
c0716d7f63 | ||
|
|
d43af8c9c1 | ||
|
|
2825100f77 | ||
|
|
b1bee31d61 | ||
|
|
522276b80d | ||
|
|
7bc1470a1e | ||
|
|
ca32cb0dfc | ||
|
|
d59f55b90c | ||
|
|
02936de993 | ||
|
|
8ed223343d | ||
|
|
90f7ff1c23 | ||
|
|
a56220f158 | ||
|
|
d40b426c1b | ||
|
|
5342881bf4 | ||
|
|
c84c17ea8c | ||
|
|
f976b8261f | ||
|
|
10f4c7fa18 | ||
|
|
665d212bd6 | ||
|
|
9e6e653c57 | ||
|
|
bda4da71a6 | ||
|
|
a24fc941a6 | ||
|
|
f307ad060b | ||
|
|
1845b7be54 | ||
|
|
a260e157eb | ||
|
|
0641b1a73e | ||
|
|
9ba57f0c9d | ||
|
|
9c2f0b2dc8 | ||
|
|
d4bbebeb93 | ||
|
|
1a22b4f0aa | ||
|
|
9c4d5341ae | ||
|
|
b9d498e01c | ||
|
|
a1e38a3e40 | ||
|
|
c0ae7763a6 | ||
|
|
9f98b1d3d1 | ||
|
|
a7e2224f05 | ||
|
|
3bb1e41394 | ||
|
|
e437379939 | ||
|
|
3ad4f6ef4b | ||
|
|
4170a61d0b | ||
|
|
b3c5ab41e7 | ||
|
|
b3fad1077e | ||
|
|
522e1f57d7 | ||
|
|
ff86f29f41 | ||
|
|
3107027132 | ||
|
|
728db73b09 | ||
|
|
fcb8ec9967 | ||
|
|
5b8fe12502 | ||
|
|
6e24f2ff13 | ||
|
|
74d715243c | ||
|
|
d9993860cb | ||
|
|
6a6f3a8291 |
@@ -0,0 +1,68 @@
|
||||
name: Bob the Builder
|
||||
|
||||
# Run on all branches, including all pull requests, except the 'dev'
|
||||
# branch since that's where we run Coverity Scan (limited tokens/day)
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- '!dev'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Verify we can build on latest Ubuntu with both gcc and clang
|
||||
name: ${{ matrix.compiler }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [gcc, clang]
|
||||
fail-fast: false
|
||||
env:
|
||||
MAKEFLAGS: -j3
|
||||
CC: ${{ matrix.compiler }}
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
|
||||
tar xf libuev-2.3.2.tar.xz
|
||||
tar xf libite-2.3.1.tar.gz
|
||||
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
sudo ldconfig
|
||||
- uses: actions/checkout@v2
|
||||
- name: Static Finit
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix= --enable-static
|
||||
make V=1
|
||||
- name: Regular Finit
|
||||
run: |
|
||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
|
||||
make clean
|
||||
make V=1
|
||||
- name: Install to ~/tmp and Inspect
|
||||
run: |
|
||||
DESTDIR=/tmp make install-strip
|
||||
tree /tmp
|
||||
ldd /tmp/sbin/finit
|
||||
size /tmp/sbin/finit
|
||||
ldd /tmp/sbin/initctl
|
||||
size /tmp/sbin/initctl
|
||||
ldd /tmp/sbin/reboot
|
||||
size /tmp/sbin/reboot
|
||||
/tmp/sbin/finit -h
|
||||
/tmp/sbin/initctl -h
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
make -j1 distcheck
|
||||
- name: Upload Test Results
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: finit-test-${{ matrix.compiler }}
|
||||
path: test/*
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Coverity Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'dev'
|
||||
|
||||
env:
|
||||
PROJECT_NAME: finit
|
||||
CONTACT_EMAIL: troglobit@gmail.com
|
||||
COVERITY_NAME: troglobit-finit
|
||||
COVERITY_PROJ: troglobit%2Ffinit
|
||||
|
||||
jobs:
|
||||
coverity:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
id: coverity-toolchain-cache
|
||||
with:
|
||||
path: cov-analysis-linux64
|
||||
key: ${{ runner.os }}-coverity
|
||||
- name: Download Coverity Scan
|
||||
if: steps.coverity-toolchain-cache.outputs.cache-hit != 'true'
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
run: |
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 \
|
||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}" \
|
||||
-O cov-analysis-linux64.tar.gz
|
||||
mkdir cov-analysis-linux64
|
||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
|
||||
tar xf libuev-2.3.2.tar.xz
|
||||
tar xf libite-2.3.1.tar.gz
|
||||
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
sudo ldconfig
|
||||
- name: Build
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
|
||||
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
|
||||
cov-build --dir cov-int make
|
||||
- name: Submit result to Coverity Scan
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
run: |
|
||||
tar czvf ${PROJECT_NAME}.tgz cov-int
|
||||
curl \
|
||||
--form project=${COVERITY_NAME} \
|
||||
--form token=$TOKEN \
|
||||
--form email=${CONTACT_EMAIL} \
|
||||
--form file=@${PROJECT_NAME}.tgz \
|
||||
--form version=trunk \
|
||||
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
|
||||
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
|
||||
@@ -0,0 +1,69 @@
|
||||
name: Release General
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Create GitHub release
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
release_id: ${{ steps.create_release.outputs.id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Extract ChangeLog entry ...
|
||||
# Hack to extract latest entry for body_path below
|
||||
run: |
|
||||
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|
||||
|head -n -1 > release.md
|
||||
cat release.md
|
||||
- name: Create release ...
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Finit v${{ github.ref }}
|
||||
body_path: release.md
|
||||
draft: false
|
||||
prerelease: false
|
||||
tarball:
|
||||
name: Build and upload release tarball
|
||||
needs: release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Installing dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
|
||||
tar xf libuev-2.3.2.tar.xz
|
||||
tar xf libite-2.3.1.tar.gz
|
||||
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
sudo ldconfig
|
||||
- name: Creating Makefiles
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
|
||||
- name: Building release
|
||||
run: |
|
||||
make release
|
||||
ls -lF ../
|
||||
mkdir -p artifacts/
|
||||
mv ../*.tar.* artifacts/
|
||||
- name: Uploading release artifacts
|
||||
uses: skx/github-action-publish-binaries@release-0.15
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
releaseId: ${{ needs.release.outputs.release_id }}
|
||||
args: artifacts/*
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
# Travis CI integration -- https://travis-ci.org/troglobit/finit
|
||||
# Defaults to GNU GCC and autotools: ./configure && make && make test
|
||||
language: c
|
||||
|
||||
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
|
||||
sudo: false
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
# The secure: declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
env:
|
||||
global:
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig
|
||||
- CFLAGS="-W -Wall -Wextra"
|
||||
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- tree
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "troglobit/finit"
|
||||
description: "finit | Fast & Extensible init for Linux"
|
||||
notification_email: troglobit@gmail.com
|
||||
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp --disable-doc --disable-contrib --enable-watchdog --enable-fallback-shell"
|
||||
build_command: "make"
|
||||
branch_pattern: dev
|
||||
|
||||
install:
|
||||
- wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
||||
- wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
|
||||
- tar xf libuev-2.3.2.tar.xz
|
||||
- tar xf libite-2.3.1.tar.gz
|
||||
- (cd libuev-2.3.2 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- (cd libite-2.3.1 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --prefix=/tmp --enable-static
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog
|
||||
- cat config.log
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- make install-strip
|
||||
- tree /tmp
|
||||
- ldd /tmp/sbin/finit
|
||||
- size /tmp/sbin/finit
|
||||
- size /tmp/sbin/initctl
|
||||
- size /tmp/sbin/reboot
|
||||
- LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
|
||||
- unshare -V
|
||||
- nsenter -V
|
||||
- chroot --version
|
||||
- 'LD_LIBRARY_PATH=/tmp/lib make distcheck || (cat finit-4.0-beta1/_build/sub/test/test-suite.log; exit 1)'
|
||||
+214
-27
@@ -4,12 +4,175 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[4.0][UNRELEASED] - 2021-03-xx
|
||||
------------------------------
|
||||
[4.2][] - 2022-01-16
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Support for non-root users to use `initctl`, e.g. group wheel
|
||||
* Support for new libite (-lite) header namespace
|
||||
* RTC plugin now reset an invalid RTC time to the kernel default
|
||||
time, 2000-01-01 00:00, prevents errors and is less crazy than
|
||||
some systems coming with with <= Jan 1, 1970
|
||||
* urandom plugin now use RNDADDENTROPY ioctl to seed kernel rng,
|
||||
incrementing entropy count. Also, 32 kiB instead of 512 bytes are
|
||||
now saved (and restored) on reboot. This should greatly improve
|
||||
reliability of systems with none or poor HWRNG
|
||||
* Kernel logging to console (loglevel >= 7, debug, when quiet mode is
|
||||
not used) is now honored by Finit, regardless of the finit.debug
|
||||
command line option
|
||||
* Reduced default log level from `LOG_NOTICE` to `LOG_INFO`
|
||||
* Wrapped all calls to mount(2) to add logging in case of failure
|
||||
* New configure options to control fastboot (no fsck) and fsck fix
|
||||
options, by Ming Liu, Atlas Copco
|
||||
* Support for overriding default `/etc/nologin` file with an external
|
||||
`#define`, by Ming Liu, Atlas Copco
|
||||
* Support for overriding default `/var/run/dbus/pid` file with an
|
||||
external `#define`, by Ming Liu, Atlas Copco
|
||||
* Support for more service options to control respawn behavior of
|
||||
crashing services, by Robert Andersson and Ming Liu, Atlas Copco
|
||||
* Support for `initctl ident [NAME]` which lists all instances of
|
||||
`NAME`, or all enabled system run/tasks and services
|
||||
* Show number of total restarts and current respawn count for a
|
||||
service in `initctl status foo`
|
||||
* Crashing services no longer have the crash/restart counter reset as
|
||||
soon as they have stabilized. Instead, a background timer will
|
||||
slowly (every 300 sec) age (decrement) the counter. This will still
|
||||
catch services that "rage quit", but also those that crash after a
|
||||
longer period of activity
|
||||
|
||||
### Fixes
|
||||
* Fix #180: user managed (`manual:yes`) services accidentally started
|
||||
by `initctl reload`, regression introduced in Finit v4.0
|
||||
* Fix #181: lots of typos all over the tree, by David Yang, Debian
|
||||
* Fix #187: fix typos, incl. small cleanup, in doc/bootstrap.md
|
||||
* Fix #188: support running on kernels that do not have cgroups v2.
|
||||
When this is detect, all functions related to cgroups support in
|
||||
Finit are disabled, *except* the .conf file parser. Hence, you
|
||||
may get parse error if you have invalid cgroup configuration in
|
||||
your Finit .conf files
|
||||
* Fix #197: `initctl status foo` now shows a focused overview of all
|
||||
matching instances; foo:1, foo:2 -- if only one instance matches the
|
||||
command line argument, or if onle one instance exists, the detailed
|
||||
view is shown, as before
|
||||
* Fix #198: a few typos found by Tim Gates
|
||||
* Fix #199: avoid using C++ reserved keywords
|
||||
* Fix #201: memory leak in usr condition plugin, by Ming Liu, Atlas Copco
|
||||
* Fix #203: ensure all filesystems listed in /proc/mounts are properly
|
||||
unumounted on shutdown/reboot, by Robert Andersson, Atlas Copco
|
||||
* Fix #210: resizing terminal (smaller) after boot causes empty lines
|
||||
to be inserted between boot progress
|
||||
* Fix #211: drop hard-coded 32 character limit in getty, now reads
|
||||
`_SC_LOGIN_NAME_MAX` from `sysconf(3)`
|
||||
* Fix #212: service PID file lost after inictl reload, visible from
|
||||
the output from `initctl status foo`
|
||||
|
||||
|
||||
[4.1][] - 2021-06-06
|
||||
--------------------
|
||||
|
||||
Bug fix release. Also disables handlers for `SIGINT` and `SIGPWR`, a
|
||||
new set of `sys` conditions are instead generated which can be used to
|
||||
trigger external programs.
|
||||
|
||||
### Changes
|
||||
* Change behavior on SIGUSR1 to be compatible with sysvinit and systemd.
|
||||
Previously SIGUSR1 caused Finit to halt, like BusyBox init. This had
|
||||
"interesting" side effects on Debian systems when coexisting with
|
||||
sysvinit (upgrading/reinstalling causes scripts to `kill -USR1 1`)
|
||||
* Change how `contrib/debian/install.sh` sets up a Grub boot entry for
|
||||
finit. We now modify the $SUPPORTED_INITS variable in `10_linux`
|
||||
* Disable default kernel ctrl-alt-delete handler and let Finit instead
|
||||
catch `SIGINT` from kernel to be able to perform a proper reboot.
|
||||
There is no default command for this, you need to set up a task that
|
||||
triggers on `<sys/key/ctrlaltdel>` to issue `initctl reboot`
|
||||
* Added keventd to provide `<sys/pwr/ac>` condition to Finit. keventd
|
||||
is currently only responsible for monitoring `/sys/class/power_supply`
|
||||
for changes to active AC mains power online status. Enable keventd
|
||||
with `configure --with-keventd`
|
||||
* For handling power fail events (from UPS and similar) a process may
|
||||
send `SIGPWR` to PID 1. Finit no longer redirects this to `SIGUSR1`
|
||||
(poweroff). There is no default command for this, you need to set up
|
||||
a task that triggers on `<sys/pwr/fail>` to issue `initctl poweroff`
|
||||
* Built-in Finit getty is now a standalone program
|
||||
* Default termios for TTYs now enable `IUTF8` on input
|
||||
* If `/bin/login` is not found, Finit now tries `sulogin` before it
|
||||
falls back to an unauthenticated `/bin/sh`
|
||||
* Dropped (broken) support for multiple consoles. Finit now follows
|
||||
the default console selected by the kernel, `/dev/console`
|
||||
* Dropped signal handlers for SIGSTOP/TSTP and SIGCONT
|
||||
* Added support for `\n`, in addition to `\r`, in "Please press Enter"
|
||||
prompt before starting getty
|
||||
* Finit no longer parses `/proc/cmdline` for its options. Instead all
|
||||
options are by default now read from `argv[]`, like a normal program,
|
||||
this is also what the kernel does by default. Please note, this may
|
||||
not work if your systems boots with an initramfs (ymmv), for such
|
||||
cases, see `configure --enable-kernel-cmdline`
|
||||
* The following plugins are now possible to disable (for containers):
|
||||
`rtc.so`, `urandom.so`, you may also want to disable `hotplug.so`.
|
||||
They are all enabled by default, as in Finit 4.0, but may be moved
|
||||
to external tools or entries in `finit.conf` in Finit 5.0
|
||||
* Added support for reading `PRETTY_NAME` from `/etc/os-release` to use
|
||||
as heading in progress output, unless `--with-heading=GREET` is used.
|
||||
* Added manual pages for finit(8), initctl(8), and finit.conf(5)
|
||||
|
||||
### Fixes
|
||||
* Stricter interface name validation in netlink plugin, modeled after
|
||||
the kernel. Suggested by Coverity Scan
|
||||
* Fix problem of re-registering a service as a task. Previously, if a
|
||||
fundamental change, like type, was made to an active service/run/task
|
||||
it did not take. Only possible workaround was to remove from config
|
||||
* initctl: drop warning when removing a non-existing usr condition
|
||||
* initctl: drop confusing `errno 0` when timing out waiting for reply
|
||||
* Ensure services in plugins and from Finit main belong to a cgroup
|
||||
* Ensure init top-level cgroup remains a leaf group
|
||||
* Fix tty parse error for detecting use of external getty
|
||||
* Fix default `name:` and `:ID` for tty's, e.g. `ttyS0` now gives
|
||||
`tty:S0` as expected. This was default for built-in getty already
|
||||
* Fix max username (32 chars) in bundled Finit getty
|
||||
* The `contrib/*/install.sh` scripts failed to run from tarball
|
||||
* Finit no longer forcibly mounts; `/dev`, `/proc`, or `/sys`, instead
|
||||
it checks first if they are already mounted (devtmpfs or container)
|
||||
* Fix `/etc/fstab` parser to properly check for 'ro' to not remount the
|
||||
root filesystem at boot. The wrong field was read, so a root mounted
|
||||
by an initramfs, or by lxc for a container, had their root remounted
|
||||
* Fix SIGCHLD handler, `waitpd()` may be interrupted by a signal
|
||||
* Reset `starting` flag of services being stopped. When a service
|
||||
is started and then stopped before it has created its pid file,
|
||||
it could be left forever in the stopping state, unless we reset
|
||||
the starting flag.
|
||||
* Fix #170: detect loss of default route when interfaces go down. This
|
||||
emulates the missing kernel netlink message to remove the condition
|
||||
net/default/route to allow stopping dependent services
|
||||
* Fix #171: restore automatic mount of `/dev/shm`, `/dev/pts`, `/run`
|
||||
and `/tmp`, unless mounted already by `/etc/fstab`. This is what most
|
||||
desktop systems expect PID 1 to do. Here we also make sure to mount
|
||||
`/run/lock` as a tmpfs as well, with write perms for regular users,
|
||||
this prevents regular users from filling up `/run` and causing DoS.
|
||||
* Fix #173: netlink plugin runs out of socket buffer space;
|
||||
|
||||
finit[1]: nl_callback():recv(): No buffer space available
|
||||
|
||||
Fixed by adding support for resync with kernel on `ENOBUFS`. See
|
||||
netlink(7) for details. As a spin-off the plugin now supports any
|
||||
number of interfaces and routes on a system. On resync, the following
|
||||
message is now logged, as a warning:
|
||||
|
||||
finit[1]: nl_callback():busy system, resynchronizing with kernel.
|
||||
|
||||
* Fix #174: loss of log messages using combo of prio and facility, e.g.,
|
||||
`logit(LOG_CONSOLE | LOG_NOTICE, ...)`, by Jacques de Laval, Westermo
|
||||
* Fix #175: ensure Finit does not acquire a controlling TTY when checking
|
||||
if a device is a TTY before starting a getty. This fixes an old bug
|
||||
where Ctrl-C after logout from a shell could cause PID 1 to get SIGINT,
|
||||
which in turn could lead to a system reboot
|
||||
|
||||
|
||||
[4.0][] - 2021-04-26
|
||||
--------------------
|
||||
|
||||
This release became v4.0, and not v3.2, because of incompatible changes
|
||||
to service conditions. There are other significant changes as well, so
|
||||
make sure to read the whole changelog when upgrading.
|
||||
make sure to read the whole change log when upgrading.
|
||||
|
||||
### Changes
|
||||
* The stand-alone `reboot` tool has been replaced with a symlink to
|
||||
@@ -24,28 +187,47 @@ make sure to read the whole changelog when upgrading.
|
||||
* Removed built-in inetd super server. If you need this functionality,
|
||||
use an external inetd, like xinetd, instead. A pull request for a
|
||||
stand-alone inetd, like watchdogd and getty, is most welcome!
|
||||
* Incompatible `configure` script changes, i.e., you must give proper
|
||||
path arguments to the script, no more guessing just GNU defaults.
|
||||
There are examples in the documentation and the `contrib/` section
|
||||
* Change service conditions from the non-obvious `<svc/path/to/foo>` to
|
||||
`<pid/foo:id>`. This to hopefully make it clear that one service's
|
||||
'pid:!foo' pidfile is another service's `<pid/foo>` condition
|
||||
* Initial support for cgroups, every service now runs in its own cgroup
|
||||
https://twitter.com/b0rk/status/1214341831049252870?s=20
|
||||
* Incompatible `configure` script changes, i.e., no guessing `--prefix`
|
||||
and other paths. Also, many options have been changed, renamed, or
|
||||
flipped defaults, or even dropped altogether. There are examples in
|
||||
the documentation and the `contrib/` section
|
||||
* Service conditions change from the non-obvious `<svc/path/to/foo>` to
|
||||
`<pid/foo:id>`. Not only does this give simpler internal semantics,
|
||||
it hopefully also makes it clear that one service's `pid:!foo` pidfile
|
||||
is another service's `<pid/foo>` condition, issue #143
|
||||
* Initial support for cgroups v2:
|
||||
- services runs in a cgroup named after their respective *.conf file
|
||||
- top-level groups are; init, user, and system
|
||||
- all top-level groups can be configured from finit *.conf files
|
||||
- each service can tweak the cgroup settings
|
||||
- Use `initctl [top|ps|cgroup]` commands to inspect runtime state
|
||||
- https://twitter.com/b0rk/status/1214341831049252870?s=20
|
||||
* Major refactor of Finit's `main()` function to be able to start the
|
||||
event loop earlier. This also facilitated factoring out functionality
|
||||
previously hard-coded in Finit, e.g., starting the bundled watchdogd,
|
||||
various distro packed udevd and other hotplugging tools
|
||||
* A proper rescue mode has been added. It is started extremely early
|
||||
and is protected with a bundled `suslogin`. Exiting rescue mode now
|
||||
brings up the system as a normal boot, as one expects
|
||||
* Support for `sysv` start/stop scripts as well as monitoring forking
|
||||
services, stared using `sysv` or `service` stanza
|
||||
* Support for custom `kill:DELAY`, default 3 sec.
|
||||
* Support for custom `halt:SIGNAL`, default SIGTERM
|
||||
* Support for `pre:script` and `post:script`, allows for setup and
|
||||
teardown/cleanup before and after a service runs, issue #129
|
||||
* Support for `env:file` in `/etc/default/foo` or `/etc/conf.d/foo`, see
|
||||
the contrib section for examples that utilize this feature. Variables
|
||||
expanded from env files, and the env files themselves, are tracked for
|
||||
changes to see if a service .conf file is "dirty" and needs restart on
|
||||
`initctl reload`
|
||||
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
|
||||
useful with new `sysv` or `service` which fork to background
|
||||
* Support starting run/task/services without absolute path, trust `$PATH`
|
||||
* Add support for `--disable-doc` and `--disable-contrib` to speed up
|
||||
builds and work around issue with massively parallel builds
|
||||
* Add support for `@console` also for external getty
|
||||
* Support for `@console` also for external getty
|
||||
* Support for `notty` option to built-in getty, for board bring-up
|
||||
* Support for `rescue` option to built-in getty, for rescue shells
|
||||
* Add `-b`, batch mode, for non-interactive use to `initctl`
|
||||
* Prefer udev to handle `/dev/` if mdev is also available
|
||||
* Redirect dbus daemon output to syslog
|
||||
@@ -65,7 +247,7 @@ make sure to read the whole changelog when upgrading.
|
||||
- As long as a stepped service changes state we queue another step all
|
||||
event, because services may depend on each other
|
||||
* Require new libuEv API: `uev_init1()` to reduce event cache so that
|
||||
the kernel can invalidate deleted events before enqueing to userspace
|
||||
the kernel can invalidate deleted events before enqueuing to userspace
|
||||
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
|
||||
from the `hwclock` tool. Note: the kernel can also be set to load
|
||||
and store RTC to/from system clock at boot/halt as well, issue #110
|
||||
@@ -95,6 +277,8 @@ make sure to read the whole changelog when upgrading.
|
||||
* Always append `:ID` qualifier to conditions if set for a service
|
||||
* The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket`
|
||||
officially only supported for use by the `initctl` tool
|
||||
* The IPC socket now uses `SOCK_SEQPACKET` instead of `SOCK_STREAM`.
|
||||
Recommend using watchdogd v3.4, or later, which support this
|
||||
* Improved support for modern `/etc/network/interfaces`, which has
|
||||
include statements. No more native `ifup` of individual interfaces,
|
||||
Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to
|
||||
@@ -112,16 +296,21 @@ make sure to read the whole changelog when upgrading.
|
||||
that do not have a watchdog
|
||||
* Fix #100: Early condition handling may not work if `/var/run` does
|
||||
not yet exist (symlink to `/run`). Added compat layer for access
|
||||
* Fix #101: Built-in inetd removed
|
||||
* Fix #102: Start built-in watchdogd as a regular service
|
||||
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY,
|
||||
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
|
||||
Fixed by Jonas Johansson, Westermo
|
||||
* Fix #109: Support for PID files in sub-directories to `/var/run`
|
||||
* Handle rename of PID files, by Robert Andersson, Atlas Copco
|
||||
* Fix #110: automatic modprobe of RTC devices, built-in hwclock
|
||||
* Fix #120: Redirect `stdin` to `/dev/null` for services by default
|
||||
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
|
||||
fixed by Jacques de Laval, Westermo
|
||||
* Fix #124: Lingering processes in process group when session leader
|
||||
exits. E.g., lingering `logit` processes when parent dies
|
||||
* Fix #127: Show all runparts scripts as they start, like rc.local, fixed
|
||||
by Jacques de Laval, Westermo
|
||||
* Fix service name matching, e.g. for condition handling, may match with
|
||||
wrong service, by Jonas Holmberg, Westermo
|
||||
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard
|
||||
@@ -140,6 +329,8 @@ make sure to read the whole changelog when upgrading.
|
||||
* Provide service description for built-in watchdog daemon
|
||||
* Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system
|
||||
* Drop the '%m' GNUism, for compat with older musl libc
|
||||
* Fix #139: call `tzset()` on `initctl reload` to activate system
|
||||
timezone changes (for logging)
|
||||
|
||||
|
||||
[3.1][] - 2018-01-23
|
||||
@@ -378,7 +569,7 @@ Bug fix release.
|
||||
|
||||
* Add support for status/show service by `name:id`
|
||||
* Enforce terse mode after boot, if verbose mode is disabled
|
||||
* Reenable verbose mode at reboot, if disabled at boot
|
||||
* Re-enable verbose mode at reboot, if disabled at boot
|
||||
* Update section mentioning BusyBox getty
|
||||
* Update debugging documentation
|
||||
* Allow debug to override terse mode
|
||||
@@ -433,7 +624,7 @@ handling and reconfiguration at runtime.
|
||||
* Clarify how to select different plugins with the configure script
|
||||
* Improve urandom plugin for embedded systems w/o random seed
|
||||
* Add `--debug` flag to `initctl`
|
||||
* The runlevels listed for services in `initctl show` now hightlight the
|
||||
* The runlevels listed for services in `initctl show` now highlight the
|
||||
active runlevel.
|
||||
* Clarify in the README and in `initctl help` that the GW event to
|
||||
listen for in service declarations is `GW:UP`
|
||||
@@ -465,7 +656,7 @@ handling and reconfiguration at runtime.
|
||||
* Do not use `-Os` use `-O2` as default optimization level. Many cross
|
||||
compiler toolchains are known to have problems with `-Os`
|
||||
* Do not allow build VERSION to be overloaded by an environment variable
|
||||
* Fix too small MAX arguments and too few argments in `svc_t` for
|
||||
* Fix too small MAX arguments and too few arguments in `svc_t` for
|
||||
reading currently running services with `initctl show`
|
||||
* Unblock blocked signals after forking off a child
|
||||
|
||||
@@ -583,7 +774,7 @@ The inetd release.
|
||||
turned out to not work so well after all. For instance, launching
|
||||
TTYs in a background process completely blocked inetd services from
|
||||
even starting up listening sockets ... proper fork seems to work fine
|
||||
though. This is the casue for *yanking* the [1.11] release, below.
|
||||
though. This is the cause for *yanking* the [1.11] release, below.
|
||||
* Trap segfaults caused by external plugins/callbacks in a sub-process.
|
||||
This prevents a single programming mistake in by a 3rd party developer
|
||||
from taking down the entire system.
|
||||
@@ -679,7 +870,7 @@ Major bug fix release.
|
||||
|
||||
### Changes
|
||||
* Show `__FILE__` in `_d()` debug messages, useful for plugins with
|
||||
similarily named callbacks. Also, only in debug mode anyway
|
||||
similarly named callbacks. Also, only in debug mode anyway
|
||||
* Make sure to cleanup recorded PID when a service is lost. Required by
|
||||
service plugins for their callbacks to work.
|
||||
* Only clear screen when in verbose mode. Maybe this should be removed
|
||||
@@ -748,14 +939,14 @@ Major bug fix release.
|
||||
* Makefile fixes for installation, paths encoded wrong
|
||||
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip
|
||||
* Default install is now to `/sbin/finit` and `/usr/`
|
||||
* Note change in `$PLUGIN_DIR` environemnt variable to `$plugindir`
|
||||
* Note change in `$PLUGIN_DIR` environment variable to `$plugindir`
|
||||
|
||||
|
||||
[1.2][] - 2012-09-27
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Update README with section on building and enviroment variables
|
||||
* Update README with section on building and environment variables
|
||||
|
||||
### Fixes
|
||||
* Fix installation paths encoded in finit binary
|
||||
@@ -847,7 +1038,9 @@ Major bug fix release.
|
||||
|
||||
* Initial release
|
||||
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD
|
||||
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
|
||||
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
|
||||
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
|
||||
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
|
||||
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
|
||||
@@ -879,9 +1072,3 @@ Major bug fix release.
|
||||
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
|
||||
[libuEv]: https://github.com/troglobit/libuev
|
||||
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
SUBDIRS = plugins src test
|
||||
SUBDIRS = man plugins src test
|
||||
doc_DATA = README.md LICENSE contrib/finit.conf
|
||||
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
|
||||
[![License Badge][]][License] [![GitHub Status]][GitHub] [![Coverity Status]][Coverity Scan]
|
||||
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -22,13 +22,30 @@
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
|
||||
was reverse engineered from the [EeePC fastinit][] over ten years ago
|
||||
by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
|
||||
Fast init for Linux systems. Reverse engineered from the [EeePC
|
||||
fastinit][] over ten years ago by [Claudio Matsuoka][] — "gaps filled
|
||||
with frog DNA …"
|
||||
|
||||

|
||||
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
|
||||
|
||||
Features include:
|
||||
|
||||
* Runlevels, defined per service
|
||||
* One-shot tasks, services (daemons), or [SysV init][4] start/stop scripts
|
||||
* Runparts and `/etc/rc.local` support
|
||||
* Process supervision similar to [systemd][]
|
||||
* Sourcing environment files
|
||||
* Conditions for network/process/custom dependencies
|
||||
* Pre/Post script actions
|
||||
* Tooling to enable/disable services
|
||||
* Built-in getty
|
||||
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
|
||||
* Built-in support for Debian/BusyBox `/etc/network/interfaces`
|
||||
* Cgroups v2, both configuration and monitoring in `initctl top`
|
||||
* Plugin support for customization
|
||||
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
|
||||
|
||||
Focus is on small and embedded systems, although Finit is fully usable
|
||||
on server and desktop systems as well. For working examples, see the
|
||||
[contrib/](contrib/) section with tutorials for:
|
||||
@@ -58,10 +75,10 @@ runlevel 2
|
||||
log size=100k count=4
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
service [S12345] watchdogd -L -f -- System watchdog daemon
|
||||
service [S12345] syslogd -n -b 3 -D -- System log daemon
|
||||
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
||||
service [2345] lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
||||
service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon
|
||||
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon
|
||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||
service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab)
|
||||
|
||||
# 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
|
||||
@@ -76,24 +93,24 @@ service :80 [2345] merecat -n -p 80 /var/www -- Web server
|
||||
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
|
||||
|
||||
# Alternative method instead of below runparts, can also use /etc/rc.local
|
||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd start -- Preparing console
|
||||
#sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap
|
||||
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd -- Preparing console
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
|
||||
# Virtual consoles run BusyBox getty, keep kernel default speed
|
||||
tty [12345] /sbin/getty -L 0 /dev/tty1 linux
|
||||
tty [12345] /sbin/getty -L 0 /dev/tty2 linux
|
||||
tty [12345] /sbin/getty -L 0 /dev/tty3 linux
|
||||
tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear
|
||||
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear
|
||||
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear
|
||||
|
||||
# Use built-in getty for serial port and USB serial
|
||||
tty [12345] /dev/ttyAMA0 noclear nowait
|
||||
tty [12345] /dev/ttyUSB0 noclear
|
||||
#tty [12345] /dev/ttyAMA0 noclear nowait
|
||||
#tty [12345] /dev/ttyUSB0 noclear
|
||||
|
||||
# Just give me a shell, I need to debug this embedded system!
|
||||
tty [12345] @console noclear nologin
|
||||
#tty [12345] console noclear nologin
|
||||
```
|
||||
|
||||
The `service` stanza, as well as `task`, `run` and others are described
|
||||
@@ -102,10 +119,10 @@ some of the most common components needed to start a UNIX daemon:
|
||||
|
||||
```
|
||||
service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon
|
||||
^ ^ ^ ^ ^ ^ ^ ^
|
||||
| | | | | | | `-- Optional description
|
||||
| | | | | | `----------- Daemon arguments
|
||||
| | | | | `-------------------------- Path to daemon
|
||||
^ ^ ^ ^ ^ ^ ^ ^
|
||||
| | | | | | | `---------- Optional description
|
||||
| | | | | | `------------------ Daemon arguments
|
||||
| | | | | `------------------------- Path to daemon
|
||||
| | | | `---------------------------------------------------- Optional env. file
|
||||
| | | `-------------------------------------------------------- Redirect output to log
|
||||
| | `--------------------------------------------------------------- Optional conditions
|
||||
@@ -117,13 +134,13 @@ Some components are optional: runlevel(s), condition(s) and description,
|
||||
making it easy to create simple start scripts and still possible for more
|
||||
advanced uses as well:
|
||||
|
||||
service sshd -D
|
||||
service /usr/sbin/sshd -D
|
||||
|
||||
Dependencies are handled using [conditions](doc/conditions.md). One of
|
||||
the most common conditions is to wait for basic networking to become
|
||||
available:
|
||||
|
||||
service <net/route/default> nginx -- High performace HTTP server
|
||||
service <net/route/default> nginx -- High performance HTTP server
|
||||
|
||||
Here is another example where we instruct Finit to not start BusyBox
|
||||
`ntpd` until `syslogd` has started properly. Finit waits for `syslogd`
|
||||
@@ -232,8 +249,11 @@ For more information, see [doc/plugins.md](doc/plugins.md).
|
||||
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
|
||||
registering any changes to `.conf` files. To activate a change the user
|
||||
must call `initctl reload`, which reloads all modified files, stops any
|
||||
removed services, starts new ones, and restarts any modified ones, with
|
||||
SIGHUP if the process supports it.
|
||||
removed services, starts new ones, and restarts any modified ones. If the
|
||||
command line arguments of a service have changed, the process will be
|
||||
terminated and then started again with the updated arguments. If the arguments
|
||||
have not been modified and the process supports SIGHUP, the process will
|
||||
receive a SIGHUP rather than being terminated and started.
|
||||
|
||||
For some use-cases the extra step of calling `initctl reload` creates an
|
||||
unnecessary overhead, which can be removed at build-time using:
|
||||
@@ -241,6 +261,46 @@ unnecessary overhead, which can be removed at build-time using:
|
||||
configure --enable-auto-reload
|
||||
|
||||
|
||||
**Cgroups**
|
||||
|
||||
Finit supports cgroups v2 and comes with the following default groups
|
||||
in which services and user sessions are placed in:
|
||||
|
||||
/sys/fs/cgroup
|
||||
|-- init/ # cpu.weight:100
|
||||
|-- system/ # cpu.weight:9800
|
||||
`-- user/ # cpu.weight:100
|
||||
|
||||
Finit itself and its helper scripts and services are placed in the
|
||||
top-level leaf-node group `init/`, which also is _reserved_.
|
||||
|
||||
All run/task/service/sysv processes are placed in their own sub-group
|
||||
in `system/`. The name of each sub-group is taken from the respective
|
||||
`.conf` file from `/etc/finit.d`.
|
||||
|
||||
All getty/tty processes are placed in their own sub-group in `user/`.
|
||||
The name of each sub-group is taken from the username.
|
||||
|
||||
A fourth group also exists, the `root` group. It is also _reserved_ and
|
||||
primarily intended for RT tasks. If you have RT tasks they need to be
|
||||
declared as such in their service stanza like this:
|
||||
|
||||
service [...] <...> cgroup.root /path/to/foo args -- description
|
||||
|
||||
or
|
||||
|
||||
cgroup.root
|
||||
service [...] <...> /path/to/foo args -- description
|
||||
service [...] <...> /path/to/bar args -- description
|
||||
|
||||
See [doc/config.md](doc/config.md#cgroups) for more information, e.g.,
|
||||
how to configure per-group limits.
|
||||
|
||||
The `initctl` tool has three commands to help debug and optimize the
|
||||
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
|
||||
commands for details.
|
||||
|
||||
|
||||
Runparts & /etc/rc.local
|
||||
------------------------
|
||||
|
||||
@@ -356,7 +416,9 @@ Options:
|
||||
-b, --batch Batch mode, no screen size probing
|
||||
-c, --create Create missing paths (and files) as needed
|
||||
-f, --force Ignore missing files and arguments, never prompt
|
||||
-1, --once Only one lap in commands like 'top'
|
||||
-p, --plain Use plain table headings, no ctrl chars
|
||||
-q, --quiet Silent, only return status of command
|
||||
-t, --no-heading Skip table headings
|
||||
-v, --verbose Verbose output
|
||||
-h, --help This help text
|
||||
@@ -378,17 +440,20 @@ Commands:
|
||||
|
||||
cond set <COND> Set (assert) user-defined condition +usr/COND
|
||||
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
|
||||
cond show Show condition status
|
||||
cond status Show condition status, default cond command
|
||||
cond dump Dump all conditions and their status
|
||||
|
||||
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||
start <NAME>[:ID] Start service by name, with optional ID
|
||||
stop <NAME>[:ID] Stop/Pause a running service by name
|
||||
reload <NAME>[:ID] Reload service by name (SIGHUP or restart)
|
||||
restart <NAME>[:ID] Restart (stop/start) service by name
|
||||
status <NAME>[:ID] Show service status, by name
|
||||
status Show status of services, default command
|
||||
|
||||
cgroup List cgroup config overview
|
||||
ps List processes based on cgroups
|
||||
top Show top-like listing based on cgroups
|
||||
|
||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||
reboot Reboot system
|
||||
@@ -402,13 +467,17 @@ file must be used to tell Finit to stop and start it on `reload` and
|
||||
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
|
||||
these will also affect how a service is maintained.
|
||||
|
||||
**Note:** even though it is possible to start services not belonging in
|
||||
the current runlevel these services will not be respawned automatically
|
||||
by Finit if they exit (crash). Hence, if the runlevel is 2, the below
|
||||
Dropbear SSH service will not be restarted if it is killed or exits.
|
||||
> **Note:** even though it is possible to start services not belonging in
|
||||
> the current runlevel these services will not be respawned automatically
|
||||
> by Finit if they exit (crash). Hence, if the runlevel is 2, the below
|
||||
> Dropbear SSH service will not be restarted if it is killed or exits.
|
||||
|
||||
The `status` command is the default, it displays a quick overview of all
|
||||
monitored run/task/services. Here we call `initctl -p`, suitable for
|
||||
scripting and documentation:
|
||||
|
||||
```
|
||||
alpine:~# initctl
|
||||
alpine:~# initctl -p
|
||||
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] ACPI daemon
|
||||
@@ -418,7 +487,7 @@ PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||
1512 ntpd running [--2345----] NTP daemon
|
||||
1473 syslogd running [S12345----] Syslog daemon
|
||||
|
||||
alpine:~# initctl -v
|
||||
alpine:~# initctl -pv
|
||||
PID IDENT STATUS RUNLEVELS COMMAND
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] acpid -f
|
||||
@@ -433,6 +502,40 @@ The environment variables to each of the services above are read from,
|
||||
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
|
||||
have other directories, e.g., Debian use `/etc/default/`.
|
||||
|
||||
The `status` command takes an optional `NAME:ID` argument. Here we
|
||||
check the status of `dropbear`, which only has one instance in this
|
||||
system:
|
||||
|
||||
```
|
||||
alpine:~# initctl -p status dropbear
|
||||
Status : running
|
||||
Identity : dropbear
|
||||
Description : Dropbear SSH daemon
|
||||
Origin : /etc/finit.d/enabled/dropbear.conf
|
||||
Environment : -/etc/conf.d/dropbear
|
||||
Condition(s):
|
||||
Command : dropbear -R -F $DROPBEAR_OPTS
|
||||
PID file : !/run/dropbear.pid
|
||||
PID : 1485
|
||||
User : root
|
||||
Group : root
|
||||
Uptime : 2 hour 46 min 56 sec
|
||||
Runlevels : [--2345----]
|
||||
Memory : 1.2M
|
||||
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|
||||
|- 1485 dropbear -R -F
|
||||
|- 2634 dropbear -R -F
|
||||
|- 2635 ash
|
||||
`- 2652 initctl -p status dropbear
|
||||
|
||||
Apr 8 12:19:49 alpine authpriv.info dropbear[1485]: Not backgrounding
|
||||
Apr 8 12:37:45 alpine authpriv.info dropbear[2300]: Child connection from 192.168.121.1:47834
|
||||
Apr 8 12:37:46 alpine authpriv.notice dropbear[2300]: Password auth succeeded for 'root' from 192.168.121.1:47834
|
||||
Apr 8 12:37:46 alpine authpriv.info dropbear[2300]: Exit (root) from <192.168.121.1:47834>: Disconnect received
|
||||
Apr 8 15:02:11 alpine authpriv.info dropbear[2634]: Child connection from 192.168.121.1:48576
|
||||
Apr 8 15:02:12 alpine authpriv.notice dropbear[2634]: Password auth succeeded for 'root' from 192.168.121.1:48576
|
||||
```
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
@@ -468,36 +571,34 @@ This project is based on the [original finit][] by [Claudio Matsuoka][]
|
||||
which was reverse engineered from syscalls of the [EeePC fastinit][] —
|
||||
"gaps filled with frog DNA …"
|
||||
|
||||
Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][].
|
||||
Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][6].
|
||||
Please file bug reports, clone it, or send pull requests for bug fixes
|
||||
and proposed extensions.
|
||||
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Process_supervision
|
||||
[2]: http://cr.yp.to/daemontools.html
|
||||
[3]: http://smarden.org/runit/
|
||||
[4]: http://en.wikipedia.org/wiki/Init
|
||||
[5]: http://en.wikipedia.org/wiki/Runlevel
|
||||
[6]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||
[9]: https://github.com/troglobit/troglos
|
||||
[1]: https://en.wikipedia.org/wiki/Process_supervision
|
||||
[2]: https://cr.yp.to/daemontools.html
|
||||
[3]: https://smarden.org/runit/
|
||||
[4]: https://en.wikipedia.org/wiki/Init
|
||||
[5]: https://en.wikipedia.org/wiki/Runlevel
|
||||
[6]: https://github.com/troglobit/finit
|
||||
[RFC862]: https://tools.ietf.org/html/rfc862
|
||||
[RFC863]: https://tools.ietf.org/html/rfc863
|
||||
[RFC864]: https://tools.ietf.org/html/rfc864
|
||||
[RFC867]: https://tools.ietf.org/html/rfc867
|
||||
[RFC868]: https://tools.ietf.org/html/rfc868
|
||||
[init]: http://en.wikipedia.org/wiki/Init
|
||||
[upstart]: http://upstart.ubuntu.com/
|
||||
[systemd]: http://www.freedesktop.org/wiki/Software/systemd/
|
||||
[openrc]: http://www.gentoo.org/proj/en/base/openrc/
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
[init]: https://en.wikipedia.org/wiki/Init
|
||||
[upstart]: https://upstart.ubuntu.com/
|
||||
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||
[openrc]: https://www.gentoo.org/proj/en/base/openrc/
|
||||
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
[original finit]: http://helllabs.org/finit/
|
||||
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
||||
[Joachim Wiberg]: http://troglobit.com
|
||||
[GitHub]: https://github.com/troglobit/finit
|
||||
[Joachim Wiberg]: https://troglobit.com
|
||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||
[Travis]: https://travis-ci.org/troglobit/finit
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
|
||||
[GitHub]: https://travis-ci.org/troglobit/finit
|
||||
[GitHub Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
|
||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
|
||||
|
||||
+66
-42
@@ -1,4 +1,4 @@
|
||||
AC_INIT(Finit, 4.0-rc3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 4.2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
@@ -8,9 +8,10 @@ AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile
|
||||
contrib/Makefile
|
||||
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
|
||||
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/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
|
||||
doc/Makefile
|
||||
man/Makefile
|
||||
plugins/Makefile
|
||||
src/Makefile
|
||||
test/test.env
|
||||
@@ -48,30 +49,25 @@ PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
|
||||
PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
|
||||
|
||||
# Check for configured Finit features
|
||||
AC_ARG_ENABLE(emergency_shell,
|
||||
AS_HELP_STRING([--enable-emergency-shell], [Experimental emergency fallback at boot.
|
||||
Use only for debugging very early boot problems with Finit]),,[
|
||||
enable_emergency_shell=no])
|
||||
|
||||
AC_ARG_ENABLE(fallback_shell,
|
||||
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
|
||||
enable_fallback_shell=no])
|
||||
|
||||
AC_ARG_ENABLE(auto_reload,
|
||||
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
|
||||
enable_auto_reload=no])
|
||||
|
||||
AC_ARG_ENABLE(watchdog,
|
||||
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
||||
enable_watchdog=no])
|
||||
AC_ARG_ENABLE(kernel_cmdline,
|
||||
AS_HELP_STRING([--enable-kernel-cmdline], [Parse init args from /proc/cmdline (don't use!)]),,[
|
||||
enable_kernel_cmdline=no])
|
||||
|
||||
AC_ARG_ENABLE(fastboot,
|
||||
AS_HELP_STRING([--enable-fastboot], [Skip fsck check on filesystems listed in /etc/fstab]),,[
|
||||
enable_fastboot=no])
|
||||
|
||||
AC_ARG_ENABLE(fsckfix,
|
||||
AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[
|
||||
enable_fsckfix=no])
|
||||
|
||||
AC_ARG_ENABLE(redirect,
|
||||
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
|
||||
enable_redirect_output=no])
|
||||
|
||||
AC_ARG_ENABLE(logit,
|
||||
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
|
||||
enable_logit=yes])
|
||||
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
|
||||
enable_redirect=yes])
|
||||
|
||||
AC_ARG_ENABLE(logrotate,
|
||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
||||
@@ -94,10 +90,12 @@ AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
|
||||
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([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
|
||||
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
|
||||
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
|
||||
|
||||
# Check for extra arguments or packages
|
||||
AC_ARG_WITH(heading,
|
||||
AS_HELP_STRING([--with-heading=GREET], [Display GREET instead of Finit version at boot]),
|
||||
AS_HELP_STRING([--with-heading=GREET], [Heading for boot progress. Default: PRETTY_NAME from /etc/os-release, fallback to "Finit vX.YY"]),
|
||||
[heading=$withval], [heading=yes])
|
||||
|
||||
AC_ARG_WITH(config,
|
||||
@@ -116,6 +114,10 @@ AC_ARG_WITH(sysconfig,
|
||||
AS_HELP_STRING([--with-sysconfig=DIR], [System environment directory, sourced by .conf files, default /etc/default & /etc/conf.d]),
|
||||
[sysconfig=$withval], [sysconfig=default])
|
||||
|
||||
AC_ARG_WITH(group,
|
||||
AS_HELP_STRING([--with-group=NAME], [Group for /run/finit/socket (initctl), default: root]),
|
||||
[group=$withval], [group=root])
|
||||
|
||||
AC_ARG_WITH(hostname,
|
||||
AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]),
|
||||
[hostname=$withval], [hostname=yes])
|
||||
@@ -128,28 +130,32 @@ AC_ARG_WITH(random-seed,
|
||||
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
|
||||
[random_seed=$withval], [random_seed=yes])
|
||||
|
||||
AC_ARG_WITH(keventd,
|
||||
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
|
||||
|
||||
AC_ARG_WITH(watchdog,
|
||||
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
|
||||
[watchdog=$withval], [with_watchdog=no watchdog=])
|
||||
|
||||
### Enable features ###########################################################################
|
||||
|
||||
# Create config.h from selected features and fallback defautls
|
||||
AS_IF([test "x$enable_emergency_shell" = "xyes"], [
|
||||
AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
|
||||
|
||||
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
|
||||
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
|
||||
|
||||
# Create config.h from selected features and fallback defaults
|
||||
AS_IF([test "x$enable_auto_reload" = "xyes"], [
|
||||
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
|
||||
|
||||
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
||||
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
||||
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [
|
||||
AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])])
|
||||
|
||||
AS_IF([test "x$enable_fastboot" = "xyes"], [
|
||||
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
|
||||
|
||||
AS_IF([test "x$enable_fsckfix" = "xyes"], [
|
||||
AC_DEFINE(FSCK_FIX, 1, [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab])])
|
||||
|
||||
AS_IF([test "x$enable_redirect" = "xyes"], [
|
||||
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
||||
|
||||
### Disable features ###########################################################################
|
||||
AS_IF([test "x$enable_logit" != "xno"], [
|
||||
AC_DEFINE(LOGIT_ENABLED, 1, [Enable logit tool, used for log redirection by Finit])])
|
||||
|
||||
AS_IF([test "x$enable_logrotate" != "xno"], [
|
||||
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
||||
|
||||
@@ -183,9 +189,13 @@ AS_IF([test "x$with_sysconfig" != "xno"], [
|
||||
[System environment, default /etc/default & /etc/conf.d])])])
|
||||
|
||||
AS_IF([test "x$with_heading" != "xno"], [
|
||||
AS_IF([test "x$heading" = "xyes"], [
|
||||
heading="$PACKAGE_NAME v$PACKAGE_VERSION"])])
|
||||
AC_DEFINE_UNQUOTED(INIT_HEADING, "$heading", [Displayed by Finit at boot])
|
||||
AS_IF([test "x$heading" = "xyes"], [heading=""])
|
||||
AC_DEFINE_UNQUOTED(INIT_OSHEADING, "$heading", [Displayed by Finit at boot, defaults to /etc/os-release PRETTY_NAME])])
|
||||
|
||||
AS_IF([test "x$with_group" != "xno"], [
|
||||
AS_IF([test "x$group" = "xyes"], [
|
||||
group=root])])
|
||||
AC_DEFINE_UNQUOTED(DEFGROUP, "$group", [For /run/finit/socket])
|
||||
|
||||
AS_IF([test "x$with_hostname" != "xno"], [
|
||||
AS_IF([test "x$hostname" = "xyes"], [
|
||||
@@ -204,10 +214,19 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
||||
AC_EXPAND_DIR(random_path, "$random_seed")
|
||||
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
|
||||
|
||||
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
|
||||
|
||||
AS_IF([test "x$with_watchdog" != "xno"], [
|
||||
AS_IF([test "x$watchdog" = "xyes"], [
|
||||
watchdog=/dev/watchdog])
|
||||
with_watchdog=yes
|
||||
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
|
||||
watchdog=])
|
||||
|
||||
# Control build with automake flags
|
||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
||||
AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
|
||||
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
|
||||
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
|
||||
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
|
||||
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||
|
||||
@@ -226,6 +245,9 @@ AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
|
||||
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf")
|
||||
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
|
||||
|
||||
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
|
||||
AS_IF([test "$heading" = "no"], [heading="<NONE>"])
|
||||
|
||||
# Generate all files
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -253,12 +275,14 @@ Behavior:
|
||||
Optional features:
|
||||
Install doc/..........: $enable_doc
|
||||
Install contrib/......: $enable_contrib
|
||||
Built-in watchdogd....: $enable_watchdog
|
||||
Built-in keventd......: $with_keventd
|
||||
Built-in watchdogd....: $with_watchdog $watchdog
|
||||
Built-in logrotate....: $enable_logrotate
|
||||
Scripting tool logit..: $enable_logit
|
||||
Emergency shell.......: $enable_emergency_shell
|
||||
Fallback shell........: $enable_fallback_shell
|
||||
Skip fsck check.......: $enable_fastboot
|
||||
Run fsck fix mode.....: $enable_fsckfix
|
||||
Redirect output.......: $enable_redirect
|
||||
Default hostname......: $hostname
|
||||
Default group.........: $group
|
||||
Default runlevel......: $runlevel
|
||||
|
||||
------------- Compiler version --------------
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/alpine
|
||||
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
|
||||
docs_DATA = README.md finit.conf build.sh install.sh rc.local
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
3.6.2
|
||||
@@ -17,8 +17,8 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
||||
--prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin --enable-watchdog \
|
||||
--with-heading="Alpine Linux 3.13"
|
||||
--enable-alsa-utils-plugin --with-watchdog \
|
||||
--with-keventd
|
||||
|
||||
echo
|
||||
echo "*** Building ..."
|
||||
|
||||
@@ -5,6 +5,22 @@
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
#cgroup init cpu.weight:100
|
||||
#cgroup user cpu.weight:100
|
||||
#cgroup system cpu.weight:9700
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
docsdir := @docdir@/contrib/alpine/finit.d/available
|
||||
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
||||
lxdm.conf modules.conf ntpd.conf klogd.conf syslog.conf \
|
||||
watchdog.conf
|
||||
lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Consoles to start getty on when system is up
|
||||
tty [12345] /dev/tty1 noclear nowait
|
||||
tty [2345] /dev/tty2 noclear nowait
|
||||
tty [2345] /dev/tty3 noclear nowait
|
||||
tty [12345] /dev/ttyS0 noclear
|
||||
tty [12345] /dev/tty1 noclear nowait
|
||||
tty [2345] /dev/tty2 noclear nowait
|
||||
tty [2345] /dev/tty3 noclear nowait
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Start klogd as soon as possible, after syslogd, should always run
|
||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||
@@ -4,3 +4,4 @@
|
||||
#module evdev
|
||||
#module mousedev
|
||||
#module 8139cp
|
||||
module softdog
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# Start syslogd as soon as possible, should always run
|
||||
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
|
||||
# Start klogd as soon as possible, but after syslogd
|
||||
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
|
||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||
|
||||
@@ -8,9 +8,10 @@ if [ "x`id -u`" != "x0" ]; then
|
||||
fi
|
||||
|
||||
# Adjust base directory
|
||||
if [ -e alpine-release ]; then
|
||||
grep -q alpine Makefile 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
cd ../..
|
||||
elif [ ! -e autogen.sh ]; then
|
||||
elif [ ! -e configure.ac ]; then
|
||||
echo "*** Please run this script from the Finit base directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
#
|
||||
# Each line is run in sequence.
|
||||
#
|
||||
# NOTE: Remeber to set the executable bit!
|
||||
# NOTE: Remember to set the executable bit!
|
||||
#
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = finit.d grub.d
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/debian
|
||||
docs_DATA = README.md finit.conf os-release build.sh install.sh
|
||||
docs_DATA = README.md finit.conf build.sh install.sh
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -20,29 +20,42 @@ tools pre-systemd work as intended.
|
||||
|
||||
root@debian:~# apt install initscripts console-setup
|
||||
|
||||
> **Note:** as of Debian 11 (Bullseye), libuev and libite are part of
|
||||
> the main section of Debian. So just install the -dev packages :)
|
||||
|
||||
The following script can then be used to configure, build, install and
|
||||
set up your system to run Finit:
|
||||
|
||||
user@debian:~/finit$ contrib/debian/build.sh
|
||||
|
||||
However, since `/sbin/init` already exists on your system, the script
|
||||
creates another entry in your GRUB config, in `/etc/grub.d/40_custom`,
|
||||
where `init=/sbin/finit` is added to your kernel line. It is of course
|
||||
also possible to change the default init to Finit, if you do you can
|
||||
remove the custom Grub entry:
|
||||
creates another entry in your GRUB config by updating `$SUPPORTED_INITS`
|
||||
in `/etc/grub.d/10_inux` and run `update-grub`. On reboot you will find
|
||||
a (finit) entry in the "Advanced options for Debian GNU/Linux" section.
|
||||
It is of course also possible to change the default init to Finit:
|
||||
|
||||
user@debian:~/finit# cd /sbin
|
||||
user@debian:/sbin# sudo rm init
|
||||
user@debian:/sbin# sudo mv init oldinit
|
||||
user@debian:/sbin# sudo ln -s finit init
|
||||
|
||||
Before rebooting, check the default [/etc/finit.conf](finit.conf) and
|
||||
`/etc/finit.d/*.conf` files. The build + install script above provides
|
||||
a few sample `.conf` files. See `initctl list` after boot for a list of
|
||||
enabled and available services.
|
||||
a few sample `.conf` files. See `initctl ls` after boot for a list of
|
||||
enabled and available services, you can then use `enable` and `disable`
|
||||
comands to `initctl`, followed by `reload` to activate your changes.
|
||||
|
||||
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
|
||||
and initialization like keyboard language etc.
|
||||
|
||||
> **NOTE:** X Window system, you may need to `sudo apt install elogind`
|
||||
> (Bullseye and later), followed by `initctl reload` to activate it (it
|
||||
> is enabled by default), and logout/login again. The elogind daemon
|
||||
> ensures a regular non-root user can start and interact with an X
|
||||
> session, otherwise keyboard and mouse won't work. When you're happy,
|
||||
> you can enable the lightdm.conf, change the default runlevel to 3, and
|
||||
> presto! you have a desktop again.
|
||||
|
||||
|
||||
Have fun!
|
||||
/Joachim ツ
|
||||
|
||||
|
||||
@@ -25,9 +25,8 @@ echo
|
||||
--prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin \
|
||||
--with-random-seed=/var/lib/urandom/random-seed \
|
||||
--with-heading="Debian GNU/Linux"
|
||||
--enable-alsa-utils-plugin --with-keventd \
|
||||
--with-random-seed=/var/lib/urandom/random-seed
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo
|
||||
|
||||
@@ -5,6 +5,22 @@
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
#cgroup init cpu.weight:100
|
||||
#cgroup user cpu.weight:100
|
||||
#cgroup system cpu.weight:9700
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
SUBDIRS = available
|
||||
docsdir := @docdir@/contrib/debian/finit.d
|
||||
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
|
||||
modules.conf rsyslogd.conf sshd.conf
|
||||
d_LINKS = acpid.conf apparmor.conf avahi-daemon.conf \
|
||||
bluetooth.conf console-setup.conf elogind.conf \
|
||||
getty.conf keyboard-setup.conf modules.conf \
|
||||
plymouth-quit.conf rsyslogd.conf sshd.conf
|
||||
|
||||
install-exec-hook:
|
||||
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
docsdir := @docdir@/contrib/debian/finit.d/available
|
||||
docs_DATA = acpid.conf console-setup.conf getty.conf keyboard-setup.conf \
|
||||
lightdm.conf modules.conf rsyslogd.conf sntpd.conf \
|
||||
sshd.conf syslogd.conf xdm.conf
|
||||
docs_DATA = acpid.conf apparmor.conf avahi-daemon.conf bluetooth.conf \
|
||||
console-setup.conf elogind.conf getty.conf \
|
||||
keyboard-setup.conf lightdm.conf modules.conf sntpd.conf \
|
||||
plymouth-quit.conf rsyslogd.conf sshd.conf syslogd.conf \
|
||||
xdm.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
|
||||
@@ -0,0 +1,2 @@
|
||||
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
|
||||
@@ -0,0 +1,3 @@
|
||||
# To ignore errors like "org.freedesktop.hostname1", update the env file with
|
||||
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
|
||||
service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
|
||||
@@ -0,0 +1 @@
|
||||
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
|
||||
@@ -0,0 +1,4 @@
|
||||
# On some systems, bootstrap can be really quick and cause elogind
|
||||
# to not start before a user has managed to log in. To prevent
|
||||
# this, add <pid/elogind> as condition to your TTYs in getty.conf
|
||||
service [2345] <pid/syslogd> /lib/elogind/elogind -- Login manager
|
||||
@@ -1,4 +1,7 @@
|
||||
# Consoles to start getty on when system is up
|
||||
tty [12345] /dev/tty1 linux noclear nowait
|
||||
tty [2345] /dev/tty2 linux noclear nowait
|
||||
tty [2345] /dev/tty3 linux noclear nowait
|
||||
tty [2345] /dev/tty2 linux nowait
|
||||
tty [2345] /dev/tty3 linux nowait
|
||||
tty [2345] /dev/tty4 linux nowait
|
||||
tty [2345] /dev/tty5 linux nowait
|
||||
tty [2345] /dev/tty6 linux nowait
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Need to stop plymouth, or it'll block stdin on tty1
|
||||
task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
exec tail -n +3 $0
|
||||
# This file provides an easy way to add custom menu entries. Simply type the
|
||||
# menu entries you want to add after this comment. Be careful not to change
|
||||
# the 'exec tail' line above.
|
||||
|
||||
menuentry '$MENU with Finit' --class $TYPE --class gnu-linux --class gnu --class os --user {
|
||||
insmod gzio
|
||||
insmod part_msdos
|
||||
insmod ext2
|
||||
search --no-floppy --fs-uuid --set=root $BOOT
|
||||
linux /vmlinuz root=UUID=$ROOT ro quiet init=/sbin/finit
|
||||
initrd /initrd.img
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
docsdir := @docdir@/contrib/debian/grub.d
|
||||
docs_DATA = 40_custom
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -8,10 +8,10 @@ if [ "x`id -u`" != "x0" ]; then
|
||||
fi
|
||||
|
||||
# Adjust base directory
|
||||
grep -q Debian os-release 2>/dev/null
|
||||
grep -q debian Makefile 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
cd ../..
|
||||
elif [ ! -e autogen.sh ]; then
|
||||
elif [ ! -e configure.ac ]; then
|
||||
echo "*** Please run this script from the Finit base directory."
|
||||
exit 1
|
||||
fi
|
||||
@@ -23,7 +23,7 @@ echo "/sbin/finit - PID 1"
|
||||
echo "/lib/finit/plugins/* - All enabled Finit plugins"
|
||||
echo "/etc/finit.conf - Finit configuration file"
|
||||
echo "/etc/finit.d/ - Finit services"
|
||||
echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader"
|
||||
echo "/etc/grub.d/10_linux - Add Finit to Grub's SUPPORTED_INITS"
|
||||
echo
|
||||
read -p "Do you want to continue (y/N)? " yorn
|
||||
echo
|
||||
@@ -38,12 +38,18 @@ if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
|
||||
cp -va finit.d /etc/
|
||||
|
||||
echo "*** Setting up a GRUB boot entry ..."
|
||||
MENU=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/menuentry '\([^']*\).*/\1/"`
|
||||
TYPE=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/.*' --class \([a-z]*\).*/\1/"`
|
||||
BOOT=`grep set=root /boot/grub/grub.cfg |head -1 |sed 's/.* \([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
|
||||
ROOT=`grep root=UUID /boot/grub/grub.cfg |head -1 |sed 's/.*UUID=\([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
|
||||
cat grub.d/40_custom | sed "s,\$BOOT,$BOOT,;s,\$ROOT,$ROOT,;s,\$TYPE,$TYPE,;s,\$MENU,$MENU," >/etc/grub.d/40_custom
|
||||
update-grub
|
||||
fn=/etc/grub.d/10_linux
|
||||
if [ -e $fn ]; then
|
||||
if `grep SUPPORTED_INITS $fn |head -1 |grep -q finit`; then
|
||||
echo "Already installed, done."
|
||||
else
|
||||
echo "Adding Finit to list of SUPPORTED_INITS ..."
|
||||
sed -i 's/SUPPORTED_INITS="[^"]*/& finit:\/sbin\/finit/' $fn
|
||||
update-grub
|
||||
fi
|
||||
else
|
||||
echo "Cannot find $fn, you'll have to set up your bootloader on your own."
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "*** Done"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
|
||||
NAME="Debian GNU/Linux"
|
||||
VERSION_ID="8"
|
||||
VERSION="8 (jessie)"
|
||||
ID=debian
|
||||
HOME_URL="http://www.debian.org/"
|
||||
SUPPORT_URL="http://www.debian.org/support"
|
||||
BUG_REPORT_URL="https://bugs.debian.org/"
|
||||
+37
-2
@@ -8,6 +8,18 @@ module evdev
|
||||
module loop
|
||||
module psmouse
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
cgroup init cpu.weight:100
|
||||
cgroup user cpu.weight:100
|
||||
cgroup system cpu.weight:9700 cpu.max:50000
|
||||
|
||||
# Example extra cgroup
|
||||
cgroup maint cpu.weight:100
|
||||
|
||||
# Runlevel to start after bootstrap, runlevel 'S'
|
||||
runlevel 2
|
||||
|
||||
@@ -16,13 +28,21 @@ network service networking start
|
||||
|
||||
# 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 size=100k count=4
|
||||
log size:100k count:4
|
||||
|
||||
# Virtual consoles to start getty on
|
||||
tty /dev/tty1
|
||||
tty /dev/tty2
|
||||
tty /dev/tty3
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
# Alternative method instead of runparts
|
||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
@@ -30,9 +50,24 @@ tty /dev/tty3
|
||||
#run [2] /etc/init.d/networking start -- Start networking
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
|
||||
# klgod and syslogd are placed in the maint cgroup
|
||||
cgroup.maint
|
||||
service [2345] <pid/syslogd> klogd -n -- Kernel logging server
|
||||
service [2345] syslogd -n -- Syslog server
|
||||
service [3] gdm -- GNOME Display Manager
|
||||
|
||||
# gdm in the system cgroup (default when read from it's own .conf file)
|
||||
cgroup.system
|
||||
service [3] cgroup:cpu.weight:250,mem.max:10M gdm -- GNOME Display Manager
|
||||
|
||||
# Start SSH daemon with opts from /etc/default/ssh (if available), move
|
||||
# to user cgroup with cpu.weight:250. Default for services is system
|
||||
# group. Default weight in user group is 100 (above). Max length of
|
||||
# cgroup argument is (currently) 63 chars.
|
||||
#
|
||||
# Notice alternative syntax where current cgroup is only set for this
|
||||
# particular service.
|
||||
service [2345] cgroup.user:cpu.weight:250,mem.max:10M env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
|
||||
|
||||
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/void
|
||||
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
|
||||
docs_DATA = README.md finit.conf build.sh install.sh rc.local
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -17,8 +17,8 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
||||
--prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin --enable-watchdog \
|
||||
--with-heading="Void Linux"
|
||||
--enable-alsa-utils-plugin --with-watchdog \
|
||||
--with-keventd
|
||||
|
||||
echo
|
||||
echo "*** Building ..."
|
||||
|
||||
@@ -5,5 +5,22 @@
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
#cgroup init cpu.weight:100
|
||||
#cgroup user cpu.weight:100
|
||||
#cgroup system cpu.weight:9700
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
docsdir := @docdir@/contrib/void/finit.d/available
|
||||
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
|
||||
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
|
||||
sshd.conf syslogd.conf uuidd.conf
|
||||
keymap.conf lxdm.conf modules.conf ntpd.conf \
|
||||
sshd.conf syslog.conf uuidd.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Start klogd as soon as possible, should always run
|
||||
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
||||
@@ -4,3 +4,4 @@
|
||||
#module evdev
|
||||
#module mousedev
|
||||
#module 8139cp
|
||||
module softdog
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Start syslogd as soon as possible, should always run
|
||||
# Start klogd as soon as possible, but after syslogd
|
||||
service [S12345] syslogd -n -- Syslog daemon
|
||||
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
||||
@@ -1,2 +0,0 @@
|
||||
# Start syslogd as soon as possible, should always run
|
||||
service [S12345] syslogd -n -- Syslog daemon
|
||||
@@ -8,9 +8,10 @@ if [ "x`id -u`" != "x0" ]; then
|
||||
fi
|
||||
|
||||
# Adjust base directory
|
||||
if [ -e xbps ]; then
|
||||
grep -q void Makefile 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
cd ../..
|
||||
elif [ ! -e autogen.sh ]; then
|
||||
elif [ ! -e configure.ac ]; then
|
||||
echo "*** Please run this script from the Finit base directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
#
|
||||
# Each line is run in sequence.
|
||||
#
|
||||
# NOTE: Remeber to set the executable bit!
|
||||
# NOTE: Remember to set the executable bit!
|
||||
|
||||
+18
-6
@@ -19,19 +19,31 @@ Some of these are also registered in the issue tracker on GitHub.
|
||||
* Add new (complimentary) .svc file format to slowly migrate away
|
||||
from the very terse one-liner format currently used
|
||||
* Allow running as non-pid1 => read .conf and RCSD form cmdline
|
||||
* When a process dies, and Finit does not restart it, we should collect
|
||||
a `siginfo_t` from the SIGCHLD signal and supply to the user in case
|
||||
`initctl status <process>` is called to debug the issue.
|
||||
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
|
||||
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
|
||||
|
||||
As of Finit v4.1 SIGPWR generates <sys/power/fail> condition and it
|
||||
is up to a task to check why and take appropriate action.
|
||||
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
|
||||
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
|
||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||
`pimd` man pages ...
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown
|
||||
|
||||
As of Finit v4.1 SIGINT generates <sys/key/ctrlaltdel>, but there is
|
||||
no check if the signal actually originates from the kernel. To get
|
||||
this verification, a new libuEv release is needed. As of June 6 -21
|
||||
that functionality is not yet released.
|
||||
|
||||
|
||||
Initctl
|
||||
-------
|
||||
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown:
|
||||
|
||||
shutdown -h 03:35 "foobar was here"
|
||||
|
||||
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
||||
`initctl show --json`
|
||||
* Add support for `-s` to redirect all output to syslog, for scripting
|
||||
|
||||
|
||||
Init
|
||||
|
||||
+15
-14
@@ -9,12 +9,16 @@ Bootstrap
|
||||
6. Load all `.so` plugins
|
||||
7. Call `fsck` on file systems listed in `/etc/fstab`
|
||||
8. Populate `/dev` using either udev or mdev, depending on system type
|
||||
9. Parse `/etc/finit.conf` and all `/etc/finit.d/*.conf` files
|
||||
10. Start built-in watchdog, if enabled
|
||||
11. Set hostname
|
||||
9. Parse `/etc/finit.conf`
|
||||
10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This
|
||||
means any WDT that requires a kernel module need to be either
|
||||
compiled into the kernel, or insmod'ed in `/etc/finit.conf`
|
||||
11. Load all `/etc/finit.d/*.conf` files and set hostname
|
||||
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
|
||||
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
|
||||
14. Mount all file systems listed in `/etc/fstab` and swap, if available
|
||||
14. Mount all file systems listed in `/etc/fstab` and swap, if available.
|
||||
On mount error `HOOK_MOUNT_ERROR` is called. After mount, regardless
|
||||
of error, `HOOK_MOUNT_POST` is called
|
||||
15. Enable SysV init signals
|
||||
16. Call 2nd level hooks, `HOOK_BASEFS_UP`
|
||||
17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin
|
||||
@@ -34,18 +38,15 @@ Bootstrap
|
||||
24. Call 4th level hooks, `HOOK_SVC_UP`
|
||||
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
||||
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||
27. Start all configured TTYs, or a fallback shell on `/dev/console`
|
||||
27. Start all configured TTYs
|
||||
|
||||
In (19) and (22) tasks and services defined in `/etc/finit.conf` and
|
||||
`/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task`
|
||||
stanzas are started in parallel and `run` in sequence, and in the order
|
||||
listed. Hence, to emulate a SysV `/etc/init.d/rcS` one could write a
|
||||
long `finit.conf` with only `run` statements.
|
||||
In (19) and (23) tasks and services defined in `/etc/finit.conf` and
|
||||
`/etc/finit.d/*.conf` are started.
|
||||
|
||||
Notice the five hook points that are called at various point in the
|
||||
bootstrap process. This is where plugins can extend the boot in any way
|
||||
they please. There are other hook points, e.g. `HOOK_MOUNT_ERROR`, for
|
||||
more on this see [plugins.md](plugins.md).
|
||||
Notice the seven hook points that are called at various point in the
|
||||
bootstrap process. This is where plugins can extend the boot in any
|
||||
way they please. There are other hook points available, for more on
|
||||
this, see [plugins.md](plugins.md).
|
||||
|
||||
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
|
||||
USB stick, convert/copy its contents to the system's `/etc/` directory,
|
||||
|
||||
+23
-16
@@ -27,7 +27,7 @@ optional plugins to enable. It depends on two external libraries:
|
||||
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
|
||||
|
||||
The configure script checks for all dependencies, including the correct
|
||||
version of the above mentioned libraries. Currently requried versions:
|
||||
version of the above mentioned libraries. Currently required versions:
|
||||
|
||||
- libite v2.2.0
|
||||
- libuEv v2.2.0
|
||||
@@ -53,6 +53,10 @@ Below are a few of the main switches to configure:
|
||||
built-ins (.o files) and all external libraries, except the C library
|
||||
will be linked statically.
|
||||
|
||||
* `--enable-kernel-cmdline`: Enable Finit pre-4.1 parsing of init args from
|
||||
`/proc/cmdline`, this is not recommended since Finit may be running as the
|
||||
init for container apps that can see the host's `/proc` filesystem
|
||||
|
||||
* `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin.
|
||||
|
||||
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
|
||||
@@ -70,16 +74,16 @@ Example
|
||||
First, unpack the archive:
|
||||
|
||||
```shell
|
||||
$ tar xf finit-3.0.tar.xz
|
||||
$ cd finit-3.0/
|
||||
$ tar xf finit-4.1.tar.gz
|
||||
$ cd finit-4.1/
|
||||
```
|
||||
|
||||
Then configure, build and install:
|
||||
|
||||
```shell
|
||||
$ ./configure --prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--with-heading="Alpine Linux 3.13" --with-hostname=alpine
|
||||
$ ./configure --prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--with-keventd --with-watchdog
|
||||
$ make
|
||||
.
|
||||
.
|
||||
@@ -87,13 +91,13 @@ $ make
|
||||
$ DESTDIR=/tmp/finit make install
|
||||
```
|
||||
|
||||
In this example the [finit-3.0.tar.xz][1] archive is unpacked to the
|
||||
In this example the [finit-4.1.tar.gz][1] archive is unpacked to the
|
||||
user's home directory, configured, built and installed to a temporary
|
||||
staging directory. The environment variable `DESTDIR` controls the
|
||||
destination directory when installing, very useful for building binary
|
||||
standalone packages.
|
||||
|
||||
Finit 3.0 and later can detect if it runs on an embedded system, or a
|
||||
Finit 4.1 and later can detect if it runs on an embedded system, or a
|
||||
system that use BusyBox tools instead of udev & C:o. On such systems
|
||||
`mdev` instead of `udev` is used. However, remember to also change the
|
||||
Linux config to:
|
||||
@@ -153,21 +157,24 @@ kernel messages and add `debug` to enable Finit debug messages.
|
||||
append="init=/sbin/finit debug"
|
||||
```
|
||||
|
||||
To debug startup issues, in particular issues with getty/login, try
|
||||
`configure --enable-fallback-shell`. When no TTYs are detected, and
|
||||
Finit is configured with this option, Finit will try to start a bare
|
||||
`/bin/sh` on the boot console. Remember, this is only for debugging
|
||||
and would leave your production system potentially wide open.
|
||||
To debug startup issues, in particular issues with getty/login, add
|
||||
the following to your Finit .conf file:
|
||||
|
||||
tty [12345789] notty noclear
|
||||
|
||||
The `notty` option ensures reusing the stdin/stdout set up by the
|
||||
kernel. Remember, this is only for debugging and would leave your
|
||||
production system potentially wide open.
|
||||
|
||||
There is also a rescue shell available, in case Finit crashes and the
|
||||
kernel usually reboots: `configure --enable-emergency-shell`. However,
|
||||
the behavior of Finit is severely limited when this is enabled, so use
|
||||
it only for debugging start up issues when Finit crashes.
|
||||
|
||||
**NOTE:** Neither of these two configure options should be enabled on
|
||||
production systems since they can potentially give a user root access.
|
||||
**NOTE:** Neither of these options should be enabled on production
|
||||
systems since they can potentially give a user root access.
|
||||
|
||||
|
||||
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
|
||||
[1]: ftp://troglobit.com/finit/finit-4.1.tar.gz
|
||||
[libuEv]: https://github.com/troglobit/libuev
|
||||
[libite]: https://github.com/troglobit/libite
|
||||
|
||||
+10
-6
@@ -6,6 +6,8 @@ passed on the Linux kernel command line. Not limited to Finit.
|
||||
|
||||
The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
|
||||
> **NOTE:** remember to use `--` to separate kernel parameters from
|
||||
> parameters to init. E.g., `init=/sbin/finit -- finit.debug rescue`
|
||||
|
||||
* `debug`: Enable kernel debug. Debug messages are printed to the
|
||||
console until Finit starts up.
|
||||
@@ -30,17 +32,19 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
* `quiet`: Suppress kernel logging to console, except for warnings and
|
||||
errors.
|
||||
|
||||
* `rescue`: Start Finit rescue mode; no network, no `fsck` of file
|
||||
systems in `/etc/fstab`, no `/etc/rc.local`, no `runparts`. The
|
||||
configuration is read from `/lib/finit/rescue.conf`, and can be
|
||||
modified by the operator.
|
||||
* `rescue`: Start rescue/maintenance mode. If your system comes with
|
||||
the bundled `sulogin` program (Finit, or from util-linux/Busybox),
|
||||
you will be given a root login to a maintenance shell. However, if
|
||||
`sulogin` is missing, the file `/lib/finit/rescue.conf` is read and
|
||||
the system booted in a limited fallback mode. See [config.md][]
|
||||
for more information.
|
||||
|
||||
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
|
||||
debug problems with the regular runlevel. All services and TTYs in
|
||||
`[1]` will be started, so a `tty [1] @console nologin` configuration
|
||||
presents you with a root console without login.
|
||||
|
||||
For more on kernel boot paramaters, see the man page [bootparam(7)][].
|
||||
|
||||
For more on kernel boot parameters, see the man page [bootparam(7)][].
|
||||
|
||||
[config.md][]: config.md#rescue-mode
|
||||
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
|
||||
|
||||
+14
-7
@@ -87,12 +87,15 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||
Built-in Conditions
|
||||
-------------------
|
||||
|
||||
Finit comes with a `pidfile` and `netlink` plugin. When enabled, the
|
||||
`pidfile` plugin watches `/var/run/` (recursively) for PID files created
|
||||
by the monitored services, and sets a corresponding condition in the
|
||||
`pid/` namespace. Similarily, the `netlink` plugin provides basic
|
||||
conditions for when an interface is brought up/down and when a default
|
||||
route (gateway) is set, in the `net/` namespace.
|
||||
Finit comes with a set of plugins for conditions: `pidfile`, `netlink`,
|
||||
`sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/`
|
||||
(recursively) for PID files created by the monitored services, and sets
|
||||
a corresponding condition in the `pid/` namespace. Similarly, the
|
||||
`netlink` plugin provides basic conditions for when an interface is
|
||||
brought up/down and when a default route (gateway) is set, in the `net/`
|
||||
namespace. The `sys` and `usr` plugins monitor are passive condition
|
||||
monitors where the action is provided by `keventd`, signal handlers,
|
||||
and in the case of `usr`, the end-user via the `initctl` tool
|
||||
|
||||
With the example listed above, finit does not start the `/sbin/netd`
|
||||
daemon until `setupd` and `zebra` has started *and* created their PID
|
||||
@@ -111,6 +114,10 @@ Built-in conditions:
|
||||
- `net/<IFNAME>/exist`
|
||||
- `net/<IFNAME>/up`
|
||||
- `net/<IFNAME>/running`
|
||||
- `sys/pwr/ac`
|
||||
- `sys/pwr/fail`
|
||||
- `sys/key/ctrlaltdel`
|
||||
- `usr/foo`
|
||||
|
||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
||||
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
||||
@@ -173,7 +180,7 @@ PID IDENT STATUS CONDITION (+ ON, ~ FLUX, - OFF)
|
||||
|
||||
Here we can see that `netd` is allowed to run since both its conditions
|
||||
are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however
|
||||
is not allowed to run since `net/vlan1/exist` condition is not satsifed.
|
||||
is not allowed to run since `net/vlan1/exist` condition is not satisfied.
|
||||
As indicated by the `-`-prefix.
|
||||
|
||||
To fake interface `vlan1` suddenly appearing, and test what happens to
|
||||
|
||||
+278
-42
@@ -2,7 +2,9 @@ Configuration
|
||||
=============
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service Environment](#service-environment)
|
||||
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||
* [Cgroups](#cgroups)
|
||||
* [Configuration File Syntax](#configuration-file-syntax)
|
||||
* [Hostname](#hostname)
|
||||
* [Kernel Modules](#kernel-modules)
|
||||
@@ -19,9 +21,12 @@ Configuration
|
||||
* [TTYs and Consoles](#ttys-and-consoles)
|
||||
* [Non-privileged Services](#non-privileged-services)
|
||||
* [Redirecting Output](#redirecting-output)
|
||||
* [Rescue Mode](#rescue-mode)
|
||||
* [Limitations](#limitations)
|
||||
* [/etc/finit.conf](#etcfinitconf)
|
||||
* [/etc/finit.d](#etcfinitd)
|
||||
* [Watchdog](#watchdog)
|
||||
* [keventd](#keventd)
|
||||
|
||||
|
||||
Introduction
|
||||
@@ -35,16 +40,16 @@ Linux distributions -- each package can provide its own "script" file.
|
||||
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
||||
|
||||
Not all configuration directives are available in `/etc/finit.d/*.conf`
|
||||
and some directives are only available at bootstrap, runlevel `S`, see
|
||||
the section [Limitations](#limitations) below for details.
|
||||
and some directives are only available at [bootstrap][], runlevel `S`,
|
||||
see the section [Limitations](#limitations) below for details.
|
||||
|
||||
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
|
||||
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
|
||||
call `finit q`, but that has been deprecated with the `initctl` tool).
|
||||
Finit monitors all known active `.conf` files, so if you want to force
|
||||
a restart of any service you can simply touch its corresponding `.conf`
|
||||
file in `/etc/finit.d` and call `initctl reload`. Finit handle any
|
||||
and all conditions and dependencies between services automatically.
|
||||
To add a new service, drop a `.conf` file in `/etc/finit.d` and run
|
||||
`initctl reload`. (It is also possible to `SIGHUP` to PID 1, or call
|
||||
`finit q`, but that has been deprecated with the `initctl` tool). Finit
|
||||
monitors all known active `.conf` files, so if you want to force a
|
||||
restart of any service you can touch its corresponding `.conf` file in
|
||||
`/etc/finit.d` and call `initctl reload`. Finit handle any and all
|
||||
conditions and dependencies between services automatically.
|
||||
|
||||
It is also possible to drop `.conf` files in `/etc/finit.d/available/`
|
||||
and use `initctl enable` to enable a service `.conf` file. This may be
|
||||
@@ -54,7 +59,7 @@ files for a package and let the user decide when to enable a service.
|
||||
On `initctl reload` the following is checked for all services:
|
||||
|
||||
- If a service's `.conf` file has been removed, or its conditions are no
|
||||
longer satisifed, the service is stopped.
|
||||
longer satisfied, the service is stopped.
|
||||
- If the file is modified, or a service it depends on has been reloaded,
|
||||
the service is reloaded (stopped and started).
|
||||
- If a new service is added it is automatically started — respecting
|
||||
@@ -128,6 +133,67 @@ example employs a wrapper script in `/etc/start.d`.
|
||||
> instead of sending SIGHUP at restart/reload events.
|
||||
|
||||
|
||||
Cgroups
|
||||
-------
|
||||
|
||||
There are three major cgroup configuration directives:
|
||||
|
||||
1. Global top-level group: init, system, user, or a custom group
|
||||
2. Selecting a top-level group for a set of run/task/services
|
||||
3. Per run/task/service limits
|
||||
|
||||
Top-level group configuration.
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
cgroup init cpu.weight:100
|
||||
cgroup user cpu.weight:100
|
||||
cgroup system cpu.weight:9800
|
||||
|
||||
Adding an extra cgroup `maint/` will require you to adjust the weight of
|
||||
the above three. We leave `init/` and `user/` as-is reducing weight of
|
||||
`system/` to 9700.
|
||||
|
||||
cgroup system cpu.weight:9700
|
||||
|
||||
# Example extra cgroup 'maint'
|
||||
cgroup maint cpu.weight:100
|
||||
|
||||
By default, the `system/` cgroup is selected for almost everything. The
|
||||
`init/` cgroup is reserved for PID 1 itself and its closest relatives.
|
||||
The `user/` cgroup is for local TTY logins spawned by getty.
|
||||
|
||||
To select a different top-level cgroup, e.g. `maint/`, one can either
|
||||
define it for a group of run/task/service directives in a `.conf` or per
|
||||
each stanza:
|
||||
|
||||
cgroup.maint
|
||||
service [...] <...> /path/to/foo args -- description
|
||||
service [...] <...> /path/to/bar args -- description
|
||||
|
||||
or
|
||||
|
||||
service [...] <...> cgroup.maint /path/to/foo args -- description
|
||||
|
||||
The latter form also allows per-stanza limits on the form:
|
||||
|
||||
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||
|
||||
Notice the comma separation and the `mem.` exception to the rule: every
|
||||
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
|
||||
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no filtering,
|
||||
except for expanding the shorthand `mem.` to `memory.`, if the file is
|
||||
not available, either the cgroup controller is not available in your
|
||||
Linux kernel, or the name is misspelled.
|
||||
|
||||
> Linux cgroups and details surrounding values are not explained in the
|
||||
> Finit documentation. The Linux admin-guide cover this well:
|
||||
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
|
||||
|
||||
|
||||
Configuration File Syntax
|
||||
-------------------------
|
||||
|
||||
@@ -140,7 +206,7 @@ the contents of that file is used.
|
||||
|
||||
Deprecated. We recommend using `/etc/hostname` instead.
|
||||
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
|
||||
|
||||
### Kernel Modules
|
||||
@@ -158,7 +224,7 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
|
||||
|
||||
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
|
||||
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
|
||||
|
||||
### Networking
|
||||
@@ -171,7 +237,7 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
|
||||
or `/etc/network/interfaces` if you have a system with `ifupdown`, like
|
||||
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
||||
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
|
||||
|
||||
### Resource Limits
|
||||
@@ -205,13 +271,13 @@ stanzas can share the same rlimits if they are in the same .conf.
|
||||
|
||||
**Syntax:** `runlevel <N>`
|
||||
|
||||
Defines 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
|
||||
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.
|
||||
|
||||
Default: 2
|
||||
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
|
||||
|
||||
### One-shot Commands (sequence)
|
||||
@@ -266,8 +332,11 @@ by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
|
||||
**Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
||||
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely. Finit tries to restart services that die 10 times
|
||||
before giving up, then you have to `initctl restart NAME` it manually.
|
||||
exits prematurely. Finit tries to restart services that die, by
|
||||
default 10 times before giving up and marking them as *crashed*.
|
||||
After which they have to be manually restarted with `initctl restart
|
||||
NAME`. The limits controling this are configurable, see the options
|
||||
below.
|
||||
|
||||
For daemons that support it, we recommend appending `--foreground`,
|
||||
`--no-background`, `-n`, `-F`, or similar command line argument to
|
||||
@@ -324,6 +393,21 @@ optional `name` argument:
|
||||
|
||||
name:<service-name>
|
||||
|
||||
As mentioned previously, services are automatically restarted, this is
|
||||
configurable with the following options:
|
||||
|
||||
* `restart:NUM` -- number of times Finit tries to restart a crashing
|
||||
service, default: 10. When this limit is reached the service is
|
||||
marked *crashed* and must be restarted manually with `initctl`
|
||||
* `restart_sec:SEC` -- number of seconds before Finit tries to restart
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
* `norestart` -- dont restart on failures, same as `restart:0`
|
||||
* `oncrash:reboot` -- when all retries have failed, and the service
|
||||
has *crashed*, if this option is set the system is rebooted.
|
||||
Note, future releases may include other `oncrash:` actions
|
||||
|
||||
When stopping a service (run/task/sysv/service), either manually or
|
||||
when moving to another runlevel, Finit starts by sending `SIGTERM`, to
|
||||
allow the process to shut down gracefully. If the process has not
|
||||
@@ -333,6 +417,28 @@ process using a different signal, use the option `halt:SIGNAL`, e.g.,
|
||||
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
|
||||
sending `SIGKILL`.
|
||||
|
||||
Services support `pre:script` and `post:script` actions as well. These
|
||||
run as the same `@USER:GROUP` as the service itself, with any `env:file`
|
||||
sourced. The scripts must use an absolute path, but are executed from
|
||||
the `$HOME` of the given user. The scripts are not called with any
|
||||
argument (currently), but both get the `SERVICE_IDENT=foo` environment
|
||||
variable set. Here `foo` denotes the identity of the service, which if
|
||||
there are multiple services named `foo`, may be `foo:1`, or any unique
|
||||
identifier specified in the .conf file. The `post:script` is called
|
||||
with an additional set of environment variables:
|
||||
|
||||
- `EXIT_CODE=[exited,signal]`: set to one of `exited` or `signal`
|
||||
- `EXIT_STATUS=[num,SIGNAME]`: set to one of exit status code from
|
||||
the program, if it exited normally, or the signal name (`HUP`,
|
||||
`TERM`, etc.) if it exited due to signal
|
||||
|
||||
The scripts have a default execution time of 3 seconds before they are
|
||||
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
|
||||
|
||||
> **Note:** the `pre:script` _must_ be idempotent, because a service
|
||||
> can transition between READY and HALTED states any number
|
||||
> of times before going to RUNNING.
|
||||
|
||||
|
||||
### Run-parts Scripts
|
||||
|
||||
@@ -341,7 +447,7 @@ sending `SIGKILL`.
|
||||
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
||||
files, or scripts, in the directory are called, in alphabetic order.
|
||||
The scripts in this directory are executed at the very end of runlevel
|
||||
`S`, bootstrap.
|
||||
`S`, [bootstrap][].
|
||||
|
||||
It can be beneficial to use `S01name`, `S02othername`, etc. if there
|
||||
is a dependency order between the scripts. Symlinks to existing
|
||||
@@ -351,7 +457,10 @@ Similar to the `/etc/rc.local` shell script, make sure that all your
|
||||
services and programs either terminate or start in the background or
|
||||
you will block Finit.
|
||||
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
|
||||
|
||||
### Including Finit Configs
|
||||
|
||||
@@ -377,24 +486,40 @@ size limit is reached.
|
||||
|
||||
### TTYs and Consoles
|
||||
|
||||
**Syntax:** `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
|
||||
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
|
||||
`tty [LVLS] <COND> [notty] [rescue]`
|
||||
|
||||
The first variant of this option uses the built-in getty on the given
|
||||
TTY device DEV, in the given runlevels. The DEV may be the special
|
||||
keyword `@console`, or `console`, useful on embedded systems.
|
||||
TTY device DEV, in the given runlevels. DEV may be the special keyword
|
||||
`@console`, which is expanded from `/sys/class/tty/console/active`,
|
||||
useful on embedded systems.
|
||||
|
||||
The default baud rate is 0, i.e., keep kernel default.
|
||||
|
||||
> The `tty` stanza inherits runlevel, condition (and other feature)
|
||||
> parsing from the `service` stanza. So TTYs can run in one or many
|
||||
> runlevels and depend on any condition supported by Finit. This is
|
||||
> useful e.g. to depend on `<pid/elogind>` before starting a TTY.
|
||||
|
||||
**Example:**
|
||||
|
||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||
|
||||
The second `tty` syntax variant is for using an external getty, like
|
||||
agetty or the BusyBox getty.
|
||||
|
||||
By default both variants *clear* the TTY and *wait* for the user to
|
||||
press enter before starting getty.
|
||||
|
||||
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
|
||||
uses is reused for stdio. If the `rescue` option is omitted, a shell is
|
||||
started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
|
||||
option is set the bundled `/libexec/finit/sulogin` is started to present
|
||||
a bare-bones root login prompt. If the root (uid:0, gid:0) user does
|
||||
not have a password set, no rescue is possible. For more information,
|
||||
see the [Rescue Mode](#rescue-mode) section.
|
||||
|
||||
By default, the first two syntax variants *clear* the TTY and *wait* for
|
||||
the user to press enter before starting getty.
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -423,16 +548,23 @@ uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
|
||||
it out by querying sysfs: `/sys/class/tty/console/active`. The speed
|
||||
can be omitted to keep the kernel default.
|
||||
|
||||
> Most systems get by fine by just using `console`, which will evaluate
|
||||
> to `/dev/console`. If you have to use `@console` to get any output,
|
||||
> you may have some issue with your kernel config.
|
||||
|
||||
**Example:**
|
||||
|
||||
tty [12345] @console noclear vt220
|
||||
|
||||
On really bare bones systems Finit offers a fallback shell, which
|
||||
should not be enabled on production systems since. This because it
|
||||
may give a user root access without having to log in. However, for
|
||||
bringup and system debugging it can come in handy:
|
||||
On really bare bones systems, or for board bringup, Finit can give you a
|
||||
shell prompt as soon as [bootstrap][] is done, without opening any
|
||||
device node:
|
||||
|
||||
configure --enable-fallback-shell
|
||||
tty [12345789] notty
|
||||
|
||||
This should of course not be enabled on production systems. Because it
|
||||
may give a user root access without having to log in. However, for
|
||||
board bringup and system debugging it can come in handy.
|
||||
|
||||
One can also use the `service` stanza to start a stand-alone shell:
|
||||
|
||||
@@ -442,8 +574,8 @@ One can also use the `service` stanza to start a stand-alone shell:
|
||||
### Non-privileged Services
|
||||
|
||||
Every `run`, `task`, or `service` can also list the privileges the
|
||||
`/path/to/cmd` should be executed with. Simply prefix the path with
|
||||
`[@USR[:GRP]]` like this:
|
||||
`/path/to/cmd` should be executed with. Prefix the command with
|
||||
`@USR[:GRP]`, group is optional, like this:
|
||||
|
||||
run [2345] @joe:users logger "Hello world"
|
||||
|
||||
@@ -484,24 +616,128 @@ Log rotation is controlled using the global `log` setting.
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
|
||||
|
||||
Rescue Mode
|
||||
-----------
|
||||
|
||||
Finit supports a rescue mode which is activated by the `rescue` option
|
||||
on the kernel command line. See [cmdline docs](cmdline.md) for how to
|
||||
activate it.
|
||||
|
||||
The rescue mode comes in two flavors; *traditional* and *fallback*.
|
||||
|
||||
|
||||
### Traditional
|
||||
|
||||
This is what most users expect. A very early maintenance login prompt,
|
||||
served by the bundled `/libexec/finit/sulogin` program, or the standard
|
||||
`sulogin` from util-linux or BusyBox is searched for in the UNIX default
|
||||
`$PATH`. If a successful login is made, or the user exits (Ctrl-D), the
|
||||
rescue mode is ended and the system boots up normally.
|
||||
|
||||
> **Note:** if the user (UID 0 and GID 0) does not have a password, or
|
||||
> __the account is locked__, the user is presented with a password-less
|
||||
> `"Press enter to enter maintenance mode."`, prompt which opens up a
|
||||
> root shell.
|
||||
|
||||
|
||||
### Fallback
|
||||
|
||||
If no `sulogin` program is found, Finit tries to bring up as much of its
|
||||
own functionality as possible, yet limiting many aspects, meaning; no
|
||||
network, no `fsck` of file systems in `/etc/fstab`, no `/etc/rc.local`,
|
||||
no `runparts`, and most plugins are skipped (except those that provide
|
||||
functionality for the condition subsystem).
|
||||
|
||||
Instead of reading `/etc/finit.conf` et al, system configuration is read
|
||||
from `/lib/finit/rescue.conf`, which can be freely modified by the
|
||||
system administrator.
|
||||
|
||||
The bundled default `rescue.conf` contains nothing more than:
|
||||
|
||||
runlevel 1
|
||||
tty [12345] rescue
|
||||
|
||||
The `tty` has the `rescue` option set, which works similar to the board
|
||||
bring-up tty option `notty`. The major difference being that `sulogin`
|
||||
is started to query for root/admin password. If `sulogin` is not found,
|
||||
`rescue` behaves like `notty` and gives a plain root shell prompt.
|
||||
|
||||
If Finit cannot find `/lib/finit/rescue.conf` it defaults to:
|
||||
|
||||
tty [12345] rescue
|
||||
|
||||
There is no way to exit the *fallback* rescue mode.
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
As of Finit v4 there are no limitations to where `.conf` settings can be
|
||||
placed. Except for the system/global `rlimit`, which can only be set
|
||||
from `/etc/finit.conf`, since it is the first `.conf` file Finit reads.
|
||||
placed. Except for the system/global `rlimit` and `cgroup` top-level
|
||||
group declarations, which can only be set from `/etc/finit.conf`, since
|
||||
it is the first `.conf` file Finit reads.
|
||||
|
||||
Originally, `/etc/finit.conf` was the only way to set up a Finit system.
|
||||
Today it is mainly used for bootstrap settings like system hostname,
|
||||
network bringup and system shutdown. These can now also be set in any
|
||||
`.conf` file in `/etc/finit.d`.
|
||||
early module loading for watchdogd, network bringup and system shutdown.
|
||||
These can now also be set in any `.conf` file in `/etc/finit.d`.
|
||||
|
||||
There is, however, nothing preventing you from having all configuration
|
||||
settings in `/etc/finit.conf`.
|
||||
|
||||
> **Note:** The `/etc/finit.d` directory was previously the default
|
||||
> Finit `runparts` directory. Finit no longer has a default
|
||||
> `runparts`, make sure to update your setup, or the finit
|
||||
> configuration, accordingly.
|
||||
> Finit [runparts](#run-parts-scripts) directory. Finit no
|
||||
> longer has a default `runparts`, make sure to update your
|
||||
> setup, or the finit configuration, accordingly.
|
||||
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
|
||||
Watchdog
|
||||
--------
|
||||
|
||||
When built `--with-watchdog` a separate service is built and installed
|
||||
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
|
||||
device node exists, Finit will start it and treat it as the elected
|
||||
watchdog service to delegate its reboot to. See [bootstrap][] for
|
||||
details. This delegation is to ensure that the system is rebooted by a
|
||||
hardware watchdog timer -- on many embedded systems this is crucial to
|
||||
ensure all circuits on the board are properly reset for the next boot,
|
||||
in effect ensuring the system works the same after both a power-on and
|
||||
reboot event.
|
||||
|
||||
The delegation is performed at the very last steps of system shutdown,
|
||||
if reboot has been selected and an elected watchdog is known, first a
|
||||
`SIGPWR` is sent to advise watchdogd of the pending reboot. Then, when
|
||||
the necessary steps of preparing the system for shutdown (umount etc.)
|
||||
are completed, Finit sends `SIGTERM` to watchdogd and puts itself in a
|
||||
10 sec timeout loop waiting for the WDT to reset the board. If a reset
|
||||
is not done before the timeout, Finit falls back to`reboot(RB_AUTOBOOT)`
|
||||
which tells the kernel to do the reboot.
|
||||
|
||||
An external watchdog service can also be used. The more advanced cousin
|
||||
[watchdogd](https://github.com/troglobit/watchdogd/) is the recommended
|
||||
option here. It can register itself with Finit using the same IPC as
|
||||
`initctl`. If the bundled watchdogd is running a hand-over takes place,
|
||||
so it's safe to have both services installed on a system. For the
|
||||
hand-over to work it requires that the WDT driver supports the safe exit
|
||||
functionality where `"V"` is written to the device before closing the
|
||||
device descriptor. If the kernel driver has been built without this,
|
||||
the only option is to remove `/libexec/finit/watchdogd` or build without
|
||||
it at configure time.
|
||||
|
||||
[bootstrap]: bootstrap.md
|
||||
|
||||
|
||||
keventd
|
||||
-------
|
||||
|
||||
The kernel event daemon bundled with Finit is a simple uevent monitor
|
||||
for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition,
|
||||
which can be useful to prevent power hungry services like anacron to run
|
||||
when a laptop is only running on battery, for instance.
|
||||
|
||||
Since keventd is not an integral part of Finit yet it is not enabled by
|
||||
default. Enable it using `./configuure --with-keventd`. The bundled
|
||||
contrib build scripts for Debian, Alpine, and Void have this enabled.
|
||||
|
||||
This daemon is planned to be extended with monitoring of other uevents,
|
||||
patches and ideas are welcome in the issue tracker.
|
||||
|
||||
+17
-9
@@ -12,28 +12,36 @@ have been adopted:
|
||||
Same effect as `finit q`, `init q`, or `initctl reload`, reloads all
|
||||
*.conf files in `/etc/finit.d/`
|
||||
|
||||
This also restarts the API (initctl) socket, like SysV init and systemd
|
||||
does on USR1 with their FIFO/D-Bus.
|
||||
|
||||
|
||||
`SIGUSR1`
|
||||
---------
|
||||
|
||||
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
|
||||
processes, and unmounts all file systems. Then tells kernel to halt.
|
||||
Most people these days want `SIGUSR2` though.
|
||||
Since Finit 4.1 this signal causes Finit to restart its API (initctl)
|
||||
socket, like SysV init and systemd does on USR1 with their FIFO/D-Bus.
|
||||
|
||||
Finit <= 4.0 performed a system halt (like USR2 without power-off), but
|
||||
this caused compatibility problems with systemd and sysvinit on desktop
|
||||
systems. Hence, since Finit 4.1 it is no longer possible to halt a
|
||||
system with a signal.
|
||||
|
||||
SysV init and systemd use this to re-open their FIFO/D-Bus.
|
||||
|
||||
`SIGUSR2`
|
||||
---------
|
||||
|
||||
Like SIGUSR1, but tell kernel to power-off the system, if ACPI or
|
||||
similar exists to actually do this. If the kernel fails power-off,
|
||||
Finit falls back to halt.
|
||||
|
||||
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
|
||||
processes, and unmounts all file systems. Then tells kernel to power
|
||||
off the system, if ACPI or similar exists to actually do this. If the
|
||||
kernel fails power-off, Finit falls back to halt.
|
||||
|
||||
SysV init N/A, systemd dumps its internal state to log.
|
||||
|
||||
`SIGTERM`
|
||||
---------
|
||||
|
||||
Like `SIGUSR1`, but tell kernel to reboot the system when done.
|
||||
Like `SIGUSR2`, but tell kernel to reboot the system when done.
|
||||
|
||||
SysV init N/A, systemd rexecutes itself.
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 138 KiB |
@@ -0,0 +1,2 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
@@ -0,0 +1,2 @@
|
||||
dist_man8_MANS = finit.8 initctl.8
|
||||
dist_man5_MANS = finit.conf.5
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd June 6, 2021
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.Dt FINIT 8 SMM
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
.Nm finit
|
||||
.Nd Fast init for Linux systems
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/finit
|
||||
.Op finit.status_style=classic
|
||||
.Op finit.show_status=[on,off]
|
||||
.Op finit.debug
|
||||
.Op rescue | recover
|
||||
.Op single | S
|
||||
.Nm /sbin/telinit
|
||||
.Op Fl hv
|
||||
.Op q | Q | 0-9
|
||||
.Sh DESCRIPTION
|
||||
Fast init for Linux systems. Reverse engineered from the EeePC
|
||||
fastinit, 10+ years ago by Claudio Matsuoka, "gaps filled with frog
|
||||
DNA ...". Focus is on small and embedded systems, although Finit is
|
||||
fully usable on server and desktop systems as well.
|
||||
.Pp
|
||||
Features include:
|
||||
.Bl -bullet -width 1n -compact
|
||||
.It
|
||||
Runlevels, defined per service
|
||||
.It
|
||||
One-shot tasks, services (daemons), or SysV init start/stop scripts
|
||||
.It
|
||||
Runparts and
|
||||
.Pa /etc/rc.local
|
||||
support
|
||||
.It
|
||||
Process supervision similar to
|
||||
.Xr systemd 8
|
||||
.It
|
||||
Sourcing environment files
|
||||
.It
|
||||
Conditions for network/process/custom dependencies
|
||||
.It
|
||||
Pre/Post script actions
|
||||
.It
|
||||
Tooling to enable/disable services
|
||||
.It
|
||||
Built-in getty
|
||||
.It
|
||||
Built-in watchdog, with support for hand-over to
|
||||
.Lk https://troglobit.com/watchdogd.html watchdogd
|
||||
.It
|
||||
Built-in support for Debian/BusyBox
|
||||
.Pa /etc/network/interfaces
|
||||
.It
|
||||
Cgroups v2, both configuration and monitoring in `initctl top`
|
||||
.It
|
||||
Plugin support for customization
|
||||
.It
|
||||
Proper rescue mode with bundled `sulogin` for protected maintenance shell
|
||||
.El
|
||||
.Sh BOOTING
|
||||
.Pp
|
||||
Before laucnhing services, the system needs to be bootstrapped. This
|
||||
involves mounting all filesystems (not already mounted by an initramfs)
|
||||
in
|
||||
.Pa /etc/fstab ,
|
||||
ensuring that necessary filesystems like:
|
||||
.Pa /dev ,
|
||||
.Pa /proc ,
|
||||
.Pa /sys ,
|
||||
and
|
||||
.Pa /run
|
||||
are available and can provide the system with the services required.
|
||||
.Pp
|
||||
With that out of the way, a set of plugins take over:
|
||||
.Pp
|
||||
.Bl -tag -width bootmisc.so -offset indent
|
||||
.It Cm bootmisc.so
|
||||
Cleans up stale files from previous boots and default directories are
|
||||
created in tmpfs filesystems
|
||||
.It Cm modprobe.so
|
||||
Probes kernel modules required by the system
|
||||
.It Cm procps.so
|
||||
Set kernel variables from
|
||||
.Pa /etc/sysctl.conf
|
||||
et al
|
||||
.El
|
||||
.Pp
|
||||
When the plugins are done, finit reads
|
||||
.Pa /etc/finit.conf
|
||||
and all other enabled services in
|
||||
.Pa /etc/finit.d/* .
|
||||
First all services, run/tasks in runlevel S (bootStrap) are started,
|
||||
When all run/tasks have completed, Finit changes to the configured
|
||||
runlevel (default 2), Processes are started in parallel, in dependency
|
||||
order according to their conditions.
|
||||
.Pp
|
||||
When all services and run/tasks have been started, the console progress
|
||||
is disabled and all configured getty services are started.
|
||||
.Sh CONDITIONS
|
||||
The condition subysystem in Finit was initially created to track PID
|
||||
files of daemons. If service/task B requires service A to have started,
|
||||
Finit waits for the PID file of service A to be created/updated before
|
||||
it starts service B. Conversely, if service A is stopped and its PID
|
||||
file removed, service B is also stopped.
|
||||
.Pp
|
||||
The following condition families are available today:
|
||||
.Pp
|
||||
.Bl -tag -width pid -offset indent
|
||||
.It Cm net
|
||||
Linux netlink events, e.g. net/route/default, net/eth0/up, and
|
||||
net/et0/running
|
||||
.It Cm pid
|
||||
PID files basd on the service declaration
|
||||
.Cm name:id ,
|
||||
gives the condition pid/name:id
|
||||
.It Cm sys
|
||||
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
|
||||
.It Cm usr
|
||||
User conditions, assert and deassert using
|
||||
.Cm initctl cond [set|clr] foo
|
||||
.El
|
||||
.Sh SIGNALS
|
||||
PID 1 is the only process that by default blocks all signals. Finit
|
||||
allows for the following signals to control operation, but recommends
|
||||
instead using the
|
||||
.Xr initctl 8
|
||||
tool.
|
||||
.Pp
|
||||
.Bl -tag -width TERM
|
||||
.It HUP
|
||||
Tell
|
||||
.Nm
|
||||
to reload its configuration file(s), same as
|
||||
.Cm initctl reload
|
||||
.It USR1
|
||||
Restart API
|
||||
.Cm ( initctl )
|
||||
socket.
|
||||
.It USR2
|
||||
Tell
|
||||
.Nm
|
||||
to perform a system shutdown, same as
|
||||
.Cm initctl poweroff
|
||||
.It TERM
|
||||
Like SIGUSR2 but performs a reboot, same as
|
||||
.Cm initctl reboot
|
||||
.It INT
|
||||
Sent from kernel when Ctrl-Alt-Del is pressed (on the console), this
|
||||
asserts the sys/key/ctrlaltdel condition, which can be used to start
|
||||
tasks or services
|
||||
.It PWR
|
||||
Sent from a a power daemon on changes to UPS status, Finit asserts
|
||||
the sys/pwr/fail condition
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width /etc/finit.d/available/*.conf -compact
|
||||
.It Pa /etc/finit.conf
|
||||
Main configuration file
|
||||
.It Pa /etc/finit.d/*.conf
|
||||
Static service definitions
|
||||
.It Pa /etc/finit.d/available/*.conf
|
||||
Available services
|
||||
.It Pa /etc/finit.d/enabled/*.conf
|
||||
Symlinks from available
|
||||
.It Pa /run/finit/
|
||||
Runtime files, including the condition subsystem
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr finit.conf 5
|
||||
.Xr initctl 8
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
maintained by Joachim Wiberg, with contributions by many others.
|
||||
@@ -0,0 +1,847 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd Oct 17, 2020
|
||||
.Dt FINIT.CONF 5 SMM
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
.Nm finit.conf
|
||||
.Nd Finit configuration file format
|
||||
.Sh SYNOPSIS
|
||||
.Nm /etc/finit.conf
|
||||
.Nm /etc/finit.d/*.conf
|
||||
.Nm /etc/finit.d/available/*.conf
|
||||
.Sh DESCRIPTION
|
||||
.Nm finit
|
||||
can be configured using only the original
|
||||
.Pa /etc/finit.conf
|
||||
file or in combination with
|
||||
.Pa /etc/finit.d/*.conf .
|
||||
Useful for package-based Linux distributions \(em each package can
|
||||
provide its own "script" file.
|
||||
.Pp
|
||||
.Bl -tag -width /etc/finit.d/*.conf -offset indent -compact
|
||||
.It Pa /etc/finit.conf
|
||||
Main configuration file
|
||||
.It Pa /etc/finit.d/*.conf
|
||||
Snippets, usually one service per file
|
||||
.El
|
||||
.Pp
|
||||
Not all configuration directives are available in
|
||||
.Pa /etc/finit.d/*.conf
|
||||
and some directives are only available at bootstrap, runlevel
|
||||
.Cm S ,
|
||||
see the section
|
||||
.Sx Limitations
|
||||
below for details.
|
||||
.Sh FILES
|
||||
.Bl -tag -width /etc/finit.d/available/*.conf -compact
|
||||
.It Pa /etc/finit.conf
|
||||
Main configuration file
|
||||
.It Pa /etc/finit.d/*.conf
|
||||
Static service definitions
|
||||
.It Pa /etc/finit.d/available/*.conf
|
||||
Available services
|
||||
.It Pa /etc/finit.d/enabled/*.conf
|
||||
Enabled services, symlinks from available
|
||||
.El
|
||||
.Sh FILE FORMAT
|
||||
Lines starting with `#' are ignored. Note that end-of-line comments are
|
||||
NOT supported, comments must be on a line of their own.
|
||||
.Pp
|
||||
A configuration stanza must be on a single (long) line and
|
||||
.Em cannot
|
||||
be broken up in multiple lines by use of a backslash as the last
|
||||
character.
|
||||
.Sh DIRECTIVES
|
||||
This section lists all supported configuration directives. There also exist
|
||||
deprecated directives, see the Markdown documentation for details on these.
|
||||
.Pp
|
||||
.Bl -tag -width 1n
|
||||
.It Cm rlimit Oo hard|soft Oc Ar RESOURCE Aq LIMIT | unlimited
|
||||
.Pp
|
||||
Set the hard or soft limit for a resource, or both if that argument is
|
||||
omitted.
|
||||
.Ar RESOURCE
|
||||
is the lower-case
|
||||
.Cm RLIMIT_
|
||||
string constants from
|
||||
.Xr setrlimit 2 ,
|
||||
without prefix. E.g. to set
|
||||
.Cm RLIMIT_CPU ,
|
||||
use
|
||||
.Cm cpu .
|
||||
.Pp
|
||||
.Ar LIMIT
|
||||
is an integer that depends on the resource being modified, see
|
||||
the man page, or the kernel
|
||||
.Pa /proc/PID/limits
|
||||
file, for details.
|
||||
Finit versions before v3.1 used
|
||||
.Cm infinity
|
||||
for
|
||||
.Cm unlimited ,
|
||||
which is still supported, albeit deprecated.
|
||||
.Bd -unfilled -offset indent
|
||||
# No process is allowed more than 8MB of address space
|
||||
rlimit hard as 8388608
|
||||
|
||||
# Core dumps may be arbitrarily large
|
||||
rlimit soft core infinity
|
||||
|
||||
# CPU limit for all services, soft & hard = 10 sec
|
||||
rlimit cpu 10
|
||||
.Ed
|
||||
.Pp
|
||||
.Cm rlimit
|
||||
can be set globally, in
|
||||
.Pa /etc/finit.conf ,
|
||||
or locally per each
|
||||
.Pa /etc/finit.d/*.conf
|
||||
read. I.e., a set of task/run/service stanzas can share the same
|
||||
rlimits if they are in the same .conf.
|
||||
.It Cm runlevel Aq N
|
||||
The system runlevel to go to after bootstrap (S) has completed.
|
||||
.Cm N
|
||||
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
||||
halt. All other can be used by operating system administrators.
|
||||
Default: 2
|
||||
.Pp
|
||||
.Sy Note:
|
||||
only read and executed in runlevel S (bootstrap).
|
||||
.It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
|
||||
One-shot command to run in sequence when entering a runlevel, with
|
||||
optional arguments and description.
|
||||
.Pp
|
||||
.Cm run
|
||||
commands are guaranteed to be completed before running the next
|
||||
command. Highly useful if true serialization is needed. Usually
|
||||
only used in the bootstrap (S) runlevel.
|
||||
.Pp
|
||||
.Cm Aq COND
|
||||
conditions are described in
|
||||
.Xr finit 8 ,
|
||||
see also the
|
||||
.Sx Examples
|
||||
section below.
|
||||
.It Cm task Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
|
||||
One-shot like
|
||||
.Cm run ,
|
||||
but starts in parallel with the next command.
|
||||
.Pp
|
||||
Both
|
||||
.Cm run
|
||||
and
|
||||
.Cm task
|
||||
commands are run in a shell, so pipes and redirects can be freely used:
|
||||
.Bd -unfilled -offset indent
|
||||
task [s] echo "foo" | cat >/tmp/bar
|
||||
.Ed
|
||||
.It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description
|
||||
Similar to
|
||||
.Cm task
|
||||
is the
|
||||
.Cm sysv
|
||||
stanza, which can be used to call SysV style start/stop scripts. The
|
||||
primary intention for this command is to be able to re-use much of
|
||||
existing setup and init scripts in Linux distributions.
|
||||
.Pp
|
||||
When entering an allowed runlevel, Finit calls
|
||||
.Cm init-script start ,
|
||||
when entering a disallowed runlevel, Finit calls
|
||||
.Cm init-script stop ,
|
||||
and if the Finit .conf, where the
|
||||
.Cm sysv
|
||||
stanza is declared, is modified, Finit calls
|
||||
.Cm init-script restart
|
||||
on
|
||||
.Cm initctl reload .
|
||||
Similar to how
|
||||
.Cm service
|
||||
stanzas work.
|
||||
.Pp
|
||||
Forking services started with
|
||||
.Cm sysv
|
||||
scripts can be monitored by Finit by declaring the PID file to look for:
|
||||
.Bd -unfilled -offset indent
|
||||
sysv pid:!/path/to/pidfile.pid /path/to/script ...
|
||||
.Ed
|
||||
.Pp
|
||||
The leading '!' is to prevent Finit from managing the PID file, which is
|
||||
the default behavior for the
|
||||
.Cm pid:
|
||||
command modifier.
|
||||
.It Cm service Oo LVLS Oc Ao COND Ac Ar /path/to/daemon ARGS Op -- Optional description
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely. Finit tries to restart services that die, by
|
||||
default 10 times, before giving up and marking them as
|
||||
.Em crashed .
|
||||
After which they have to be restarted manually using
|
||||
.Cm initctl restart NAME .
|
||||
The number of restarts, the delay between each restart, and more is
|
||||
configurable, see the options below.
|
||||
.Pp
|
||||
For daemons that support it, we recommend appending
|
||||
.Cm --foreground , --no-background , -n , -F ,
|
||||
or similar command line argument to prevent them from forking off a
|
||||
sub-process in the background. This is the most reliable way to monitor
|
||||
a service.
|
||||
.Pp
|
||||
However, not all daemons support running in the foreground, or they may
|
||||
start logging to the foreground as well, these are called forking
|
||||
services and are supported using the same syntax as forking
|
||||
.Cm sysv
|
||||
services, using the
|
||||
.Cm pid:!/path/to/pidfile.pid
|
||||
command modifier syntax.
|
||||
.Pp
|
||||
.Sy Example: in the case of
|
||||
.Cm ospfd
|
||||
(below), we omit the
|
||||
.Cm -d
|
||||
flag (daemonize) to prevent it from forking to the background:
|
||||
.Bd -unfilled -offset indent
|
||||
service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
|
||||
.Ed
|
||||
.Pp
|
||||
.Cm [2345]
|
||||
denote the runlevels
|
||||
.Cm ospfd
|
||||
is allowed to run in, they are optional and default to runlevel 2-5 if
|
||||
omitted.
|
||||
.Pp
|
||||
.Cm <pid/zebra>
|
||||
is the condition for starting
|
||||
.Cm ospfd .
|
||||
In this example Finit waits for another service,
|
||||
.Cm zebra ,
|
||||
to have created its PID file in
|
||||
.Pa /var/run/quagga/zebra.pid
|
||||
before starting
|
||||
.Cm ospfd .
|
||||
Finit watches *all* files in
|
||||
.Pa /var/run ,
|
||||
for each file named
|
||||
.Cm *.pid ,
|
||||
.Cm */pid ,
|
||||
Finit opens it and find the matching
|
||||
.Cm NAME:ID
|
||||
using the PID.
|
||||
.Pp
|
||||
Some services do not maintain a PID file and rather than patching each
|
||||
application Finit provides a workaround. A
|
||||
.Cm pid
|
||||
modifier keyword can be set to have Finit automatically create (when
|
||||
starting) and later remove (when stopping) the PID file. The file is
|
||||
created in the
|
||||
.Pa /var/run
|
||||
directory using the
|
||||
.Xr basename 3
|
||||
of the service. The full syntax of the
|
||||
.Cm pid
|
||||
modifier is:
|
||||
.Bd -unfilled -offset indent
|
||||
pid[:[!][/path/to/]filename[.pid]]
|
||||
.Ed
|
||||
.Pp
|
||||
For example, by adding
|
||||
.Cm pid:/run/foo.pid
|
||||
to the service
|
||||
.Cm /sbin/bar ,
|
||||
that PID file will, not only be created and removed automatically, but
|
||||
also be used by the Finit condition subsystem. So a service/run/task
|
||||
can depend on the
|
||||
.Cm <pid/bar>
|
||||
condition.
|
||||
.Pp
|
||||
If a service should not be automatically started, it can be configured
|
||||
as manual with the
|
||||
.Cm manual:yes
|
||||
command modifier. The service can then be started at any time by
|
||||
running
|
||||
.Cm initctl start NAME
|
||||
.Pp
|
||||
The name of a service, shown by the
|
||||
.Cm initctl
|
||||
tool, defaults to the basename of the service executable. It can be
|
||||
changed with the
|
||||
.Cm name:foo
|
||||
command modifier.
|
||||
.Pp
|
||||
As mentioned previously, services are automatically restarted should
|
||||
they crash, this is configurable with the following options:
|
||||
.Bl -tag -width 1n
|
||||
.It Cm restart:NUM
|
||||
number of times Finit tries to restart a crashing
|
||||
service, default: 10. When this limit is reached the service is
|
||||
marked
|
||||
.Em crashed
|
||||
and must be restarted manually with
|
||||
.Xr initctl 8 .
|
||||
.It Cm restart_sec:SEC
|
||||
number of seconds before Finit tries to restart
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
.It Cm norestart
|
||||
dont restart on failures, same as
|
||||
.Cm restart:0
|
||||
.It Cm oncrash:reboot
|
||||
when all retries have failed, and the service
|
||||
has
|
||||
.Em crashed ,
|
||||
if this option is set the system is rebooted. Note, future releases
|
||||
may include other
|
||||
.Cm oncrash:
|
||||
actions.
|
||||
.El
|
||||
.Pp
|
||||
When stopping a service (run/task/sysv/service), either manually or when
|
||||
moving to another runlevel, Finit starts by sending SIGTERM, to allow
|
||||
the process to shut down gracefully. If the process has not been
|
||||
collected within 3 seconds, Finit sends SIGKILL. To halt the process
|
||||
using a different signal, use the command modifier
|
||||
.Cm halt:SIGNAL ,
|
||||
e.g.,
|
||||
.Cm halt:SIGPWR .
|
||||
To change the delay between your halt signal and KILL, use the command
|
||||
modifier
|
||||
.Cm kill:SEC ,
|
||||
e.g.,
|
||||
.Cm kill:10
|
||||
to wait 10 seconds before sending SIGKILL.
|
||||
.Pp
|
||||
Services support the
|
||||
.Cm pre:script
|
||||
and
|
||||
.Cm post:script
|
||||
command actions as well. These run as the same
|
||||
.Cm @USER:GROUP
|
||||
as the service itself, with any
|
||||
.Cm env:file
|
||||
sourced. The scripts must use an absolute path, but are executed from
|
||||
the
|
||||
.Cm $HOME
|
||||
of the given user. The scripts are not called with any argument
|
||||
(currently), but both get the
|
||||
.Cm SERVICE_IDENT=foo
|
||||
environment variable set. Here
|
||||
.Cm foo
|
||||
denotes the identity of the service, which if there are multiple
|
||||
services named
|
||||
.Cm foo ,
|
||||
may be
|
||||
.Cm foo:1 ,
|
||||
or any unique identifier specified in the .conf file. The
|
||||
.Cm post:script
|
||||
is called with an additional set of environment variables:
|
||||
.Bl -tag -offset indent -width 1n
|
||||
.It Cm EXIT_CODE=[exited,signal]
|
||||
set to one of
|
||||
.Cm exited
|
||||
or
|
||||
.Cm signal
|
||||
.It Cm EXIT_STATUS=[num,SIGNAME]
|
||||
set to one of exit status code from the program, if it exited normally,
|
||||
or the signal name (HUP, TERM, etc.) if it exited due to signal
|
||||
.El
|
||||
.Pp
|
||||
The scripts have a default execution time of 3 seconds before they are
|
||||
SIGKILLed, this can be adjusted using the
|
||||
.Cm kill:SEC
|
||||
modifier syntax.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
the
|
||||
.Cm pre:script
|
||||
.Em must
|
||||
be idempotent, because a service can transition between READY and HALTED
|
||||
states any number of times before going to RUNNING.
|
||||
.It Cm runparts Aq DIR
|
||||
Call
|
||||
.Xr run-parts 8
|
||||
on
|
||||
.Cm DIR
|
||||
to run start scripts. All executable files, or scripts, in the
|
||||
directory are called, in alphabetic order. The scripts in this
|
||||
directory are executed at the very end of bootstrap, runlevel S.
|
||||
.Pp
|
||||
It can be beneficial to use
|
||||
.Cm S01name ,
|
||||
.Cm S02othername ,
|
||||
etc. if there is a dependency order between the scripts. Symlinks to
|
||||
existing daemons can talso be used, but make sure they daemonize by
|
||||
default.
|
||||
.Pp
|
||||
Similar to the
|
||||
.Pa /etc/rc.local
|
||||
shell script, make sure that all your services and programs either
|
||||
terminate or start in the background or you will block Finit.
|
||||
.Sy Note:
|
||||
only read and executed in runlevel S (bootstrap).
|
||||
.It Cm include Aq CONF
|
||||
Include another configuration file. Absolute path required.
|
||||
.It Cm log size:BYTES count:NUM
|
||||
Log rotation for run/task/services using the
|
||||
.Cm log
|
||||
command modifier with redirection to a log file. Global setting,
|
||||
applies to all services.
|
||||
.Pp
|
||||
The size can be given as bytes, without a specifier, or in `k`, `M`,
|
||||
or `G`, e.g.
|
||||
.Cm size:10M ,
|
||||
or
|
||||
.Cm size:3G .
|
||||
A value of
|
||||
.Cm size:0
|
||||
disables log rotation. The default is
|
||||
.Cm size:200k .
|
||||
.Pp
|
||||
The count value is recommended to be between 1-5, with a default 5.
|
||||
Setting count to 0 means the logfile will be truncated when the MAX
|
||||
size limit is reached.
|
||||
.It Cm tty Oo LVLS Oc Ao COND Ac Ar DEV Oo BAUD Oc Oo noclear Oc Oo nowait Oc Oo nologin Oc Oo TERM Oc
|
||||
This form of the
|
||||
.Cm tty
|
||||
stanza uses the built-in getty on the given TTY device
|
||||
.Ar DEV ,
|
||||
in the given runlevels.
|
||||
.Ar DEV may be the special keyword
|
||||
.Cm @console ,
|
||||
or `console`, which is expanded from `/sys/class/tty/console/active`,
|
||||
useful on embedded systems.
|
||||
.Pp
|
||||
The default baud rate is 0, i.e., keep kernel default.
|
||||
.Pp
|
||||
The `tty` stanza inherits runlevel, condition (and other feature)
|
||||
parsing from the `service` stanza. So TTYs can run in one or many
|
||||
runlevels and depend on any condition supported by Finit. This is
|
||||
useful e.g. to depend on `<pid/elogind>` before starting a TTY.
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||
.Ed
|
||||
.It Cm tty Oo LVLS Oc Ao COND Ac Ar CMD DEV Oo noclear Oc Oo nowait Oc
|
||||
This form of the
|
||||
.Cm tty
|
||||
stanza is for using an external getty, like agetty or the BusyBox getty.
|
||||
.Pp
|
||||
By default, these first two syntax variants
|
||||
.Em clear
|
||||
the TTY and
|
||||
.Em wait
|
||||
for the user to press enter before starting getty.
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
||||
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm noclear
|
||||
option disables clearing the TTY after each session. Clearing the TTY
|
||||
when a user logs out is usually preferable.
|
||||
.Pp
|
||||
The
|
||||
.Cm nowait
|
||||
option disables the
|
||||
.Cm Please press Enter to activate console
|
||||
message before actually starting the getty program. On small and
|
||||
embedded systems running multiple unused getty wastes both memory and
|
||||
CPU cycles, so `wait` is the preferred default.
|
||||
.Pp
|
||||
The
|
||||
.Cm nologin
|
||||
option disables getty and
|
||||
.Pa /bin/login ,
|
||||
and gives the user a root (login) shell on the given TTY
|
||||
.Cm DEV
|
||||
immediately. Needless to say, this is a rather insecure option, but can
|
||||
be very useful for developer builds, during board bringup, or similar.
|
||||
.Pp
|
||||
Notice the ordering, the
|
||||
.Cm TERM
|
||||
option to the built-in getty must be the last argument.
|
||||
.Pp
|
||||
Embedded systems may want to enable automatic `DEV` by supplying the
|
||||
special
|
||||
.Cm @console
|
||||
device. This works regardless weather the system uses
|
||||
.Cm ttyS0 , ttyAMA0 , ttyMXC0 ,
|
||||
or anything else. Finit figures it out by querying sysfs:
|
||||
.Pa /sys/class/tty/console/active .
|
||||
The speed can be omitted to keep the kernel default.
|
||||
.Pp
|
||||
Most systems get by fine by just using `console`, which will evaluate
|
||||
to
|
||||
.Pa /dev/console .
|
||||
If you have to use
|
||||
.Cm @console
|
||||
to get any output, you may have some issue with your kernel config.
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] @console noclear vt220
|
||||
.Ed
|
||||
.Pp
|
||||
On really bare bones systems, or for board bringup, Finit can give you a
|
||||
shell prompt as soon as bootstrap is done, without opening any device
|
||||
node:
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345789] notty
|
||||
.Ed
|
||||
.Pp
|
||||
This should of course not be enabled on production systems. Because it
|
||||
may give a user root access without having to log in. However, for
|
||||
board bringup and system debugging it can come in handy.
|
||||
.Pp
|
||||
One can also use the
|
||||
.Cm service
|
||||
stanza to start a stand-alone shell:
|
||||
.Bd -unfilled -offset indent
|
||||
service [12345] /bin/sh -l
|
||||
.Ed
|
||||
.Pp
|
||||
.It Cm tty Oo LVLS Oc Ao COND Ac Oo notty Oc Oo rescue Oc
|
||||
The third
|
||||
.Cm tty
|
||||
form is for board bringup and the
|
||||
.Cm rescue
|
||||
boot mode. No device node is required in this variant, the same output
|
||||
that the kernel uses is reused for stdio. If the
|
||||
.Cm rescue
|
||||
option is omitted, a shell is started. The flags
|
||||
.Cm nologin , noclear ,
|
||||
and
|
||||
.Cm nowait
|
||||
are implied. If the
|
||||
.Cm rescue
|
||||
option is set the bundled
|
||||
.Pa /libexec/finit/sulogin
|
||||
is started to present a bare-bones root login prompt. If the root
|
||||
(uid:0, gid:0) user does not have a password set, no rescue is possible.
|
||||
.El
|
||||
.Sh COMMAND MODIFIERS
|
||||
The run/task/tty/service/sysv stanzas take modifiers, or options, to
|
||||
control their behavior. This section lists them with their limitations.
|
||||
All modifiers must be placed between the stanza and its command.
|
||||
.Bl -tag -width 1n
|
||||
.It Cm @user:group
|
||||
Every
|
||||
.Cm run , task ,
|
||||
or
|
||||
.Cm service
|
||||
can also list the privileges the
|
||||
.Cm /path/to/cmd
|
||||
should be executed with. Prefix the command with
|
||||
.Cm @USR[:GRP] ,
|
||||
group is optional, like this:
|
||||
.Bd -unfilled -offset indent
|
||||
run [2345] @joe:users logger "Hello world"
|
||||
.Ed
|
||||
.Pp
|
||||
For multiple instances of the same command, e.g. a DHCP client or
|
||||
multiple web servers, add
|
||||
.Cm :ID
|
||||
somewhere between the
|
||||
.Cm run , task , service
|
||||
keyword and the command, like this:
|
||||
.Bd -unfilled -offset indent
|
||||
service :80 [2345] httpd -f -h /http -p 80 -- Web server
|
||||
service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
|
||||
.Ed
|
||||
.Pp
|
||||
Without the
|
||||
.Cm :ID
|
||||
to the service the latter will overwrite the former and only the old web
|
||||
server would be started and supervised.
|
||||
.It Cm log:/path/to/file
|
||||
Redirect stdout/stderr of a command to the given log file. See the
|
||||
global log directive, above, for details on log rotation.
|
||||
.It Cm log:console
|
||||
Redirect stdout/stderr of a command to
|
||||
.Pa /dev/console ,
|
||||
only use this for debugging or bringup.
|
||||
.It Cm log:null
|
||||
Redirect stdout/stderr of a command to
|
||||
.Pa /dev/null .
|
||||
.It Cm log:prio:facility.level,tag:ident
|
||||
Redirect stdout/stderr of a command to syslog using the given priority
|
||||
and tag identity.
|
||||
.Bd -unfilled -offset indent
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
.Ed
|
||||
.It Cm log
|
||||
Default
|
||||
.Cm prio
|
||||
is
|
||||
.Cm daemon.info
|
||||
and the default
|
||||
.Cm tag
|
||||
identity is the basename of the service or run/task command.
|
||||
.El
|
||||
.Sh RESCUE MODE
|
||||
Finit supports a rescue mode which is activated by the
|
||||
.Cm rescue
|
||||
option on the kernel command line. The rescue mode comes in two
|
||||
flavors:
|
||||
.Em traditional
|
||||
and
|
||||
.Em fallback .
|
||||
.Ss Traditional
|
||||
This is what most users expect. A very early maintenance login prompt,
|
||||
served by the bundled
|
||||
.Pa /libexec/finit/sulogin
|
||||
program, or the standard
|
||||
.Cm sulogin
|
||||
from util-linux or BusyBox is searched for in the UNIX default
|
||||
.Cm $PATH .
|
||||
If a successful login is made, or the user exits (Ctrl-D), the rescue
|
||||
mode is ended and the system boots up normally.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
if the user (UID 0 and GID 0) does not have a password, or
|
||||
.Em the account is locked ,
|
||||
the user is presented with a password-less prompt:
|
||||
.Cm "Press enter to enter maintenance mode." ,
|
||||
which opens up a root shell.
|
||||
.Ss Fallback
|
||||
If no
|
||||
.Cm sulogin
|
||||
program is found, Finit tries to bring up as much of its own
|
||||
functionality as possible, yet limiting many aspects, meaning; no
|
||||
network, no`fsck` of file systems in
|
||||
.Pa /etc/fstab ,
|
||||
no
|
||||
.Pa /etc/rc.local ,
|
||||
no
|
||||
.Cm runparts ,
|
||||
and most plugins are skipped (except those that provide functionality
|
||||
for the condition subsystem).
|
||||
.Pp
|
||||
Instead of reading
|
||||
.Pa /etc/finit.conf
|
||||
et al, system configuration is read from
|
||||
.Pa /lib/finit/rescue.conf ,
|
||||
which can be freely modified by the system administrator.
|
||||
.Pp
|
||||
The bundled default `rescue.conf` contains nothing more than:
|
||||
.Bd -unfilled -offset indent
|
||||
runlevel 1
|
||||
tty [12345] rescue
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm tty
|
||||
has the
|
||||
.Cm rescue
|
||||
option set, which works similar to the board bring-up tty option
|
||||
.Cm notty .
|
||||
The major difference being that `sulogin` is started to query for
|
||||
root/admin password. If
|
||||
.Cm sulogin
|
||||
is not found,
|
||||
.Cm rescue
|
||||
behaves like
|
||||
.Cm notty
|
||||
and gives a plain root shell prompt.
|
||||
.Pp
|
||||
If Finit cannot find
|
||||
.Pa /lib/finit/rescue.conf
|
||||
it defaults to:
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] rescue
|
||||
.Ed
|
||||
.Pp
|
||||
There is no way to exit the
|
||||
.Em fallback
|
||||
rescue mode.
|
||||
.Sh SERVICE ENVIRONMENT
|
||||
Finit supports sourcing environment variables from
|
||||
.Pa /etc/default/* ,
|
||||
or similar. This is a common pattern from SysV init scripts, where the
|
||||
start/stop script is a generic script for the given service,
|
||||
.Cm foo ,
|
||||
and the options for the service are sourced from the file
|
||||
.Pa /etc/default/foo .
|
||||
Like this:
|
||||
.Bd -unfilled -offset indent
|
||||
/etc/default/foo:
|
||||
|
||||
FOO_OPTIONS=--extra-arg="bar" -s -x
|
||||
|
||||
/etc/finit.conf:
|
||||
|
||||
service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon
|
||||
.Ed
|
||||
.Pp
|
||||
Here the service
|
||||
.Cm foo
|
||||
is started with
|
||||
.Op Fl -n ,
|
||||
to make sure it runs in the foreground, and the with the options found
|
||||
in the environment file. With the
|
||||
.Cm ps
|
||||
command we can see that the process is started with:
|
||||
.Bd -unfilled -offset indent
|
||||
foo -n --extra-arg=bar -s -x
|
||||
.Ed
|
||||
.Pp
|
||||
.Sy Note:
|
||||
the leading `-` determines if Finit should treat a missing environment
|
||||
file as blocking the start of the service or not. When `-` is used, a
|
||||
missing environment file does
|
||||
.Em not
|
||||
block the start.
|
||||
.Sh SERVICE WRAPPER SCRIPTS
|
||||
If your service requires to run additional commands, executed before the
|
||||
service is actually started, like the systemd `ExecStartPre`, you can
|
||||
use a wrapper shell script to start your service.
|
||||
.Pp
|
||||
The Finit service
|
||||
.Cm .conf
|
||||
file can be put into
|
||||
.Pa /etc/finit.d/available ,
|
||||
so you can control the service using
|
||||
.Cm initctl .
|
||||
Then use the path to the wrapper script in the Finit
|
||||
.Cm .conf
|
||||
service stanza. The following example employs a wrapper script in
|
||||
.Pa /etc/start.d .
|
||||
.Bd -unfilled
|
||||
/etc/finit.d/available/program.conf:
|
||||
|
||||
service [235] <!> /etc/start.d/program -- Example Program
|
||||
|
||||
/etc/start.d/program:
|
||||
|
||||
#!/bin/sh
|
||||
# Prepare the command line options
|
||||
OPTIONS="-u $(cat /etc/username)"
|
||||
|
||||
# Execute the program
|
||||
exec /usr/bin/program $OPTIONS
|
||||
.Ed
|
||||
.Pp
|
||||
.Sy Note:
|
||||
the example sets
|
||||
.Cm <!>
|
||||
to denote that it doesn't support SIGHUP. That way Finit will
|
||||
stop/start the service instead of sending SIGHUP at restart/reload
|
||||
events.
|
||||
.Sh CGROUPS
|
||||
There are three major cgroup configuration directives:
|
||||
.Pp
|
||||
.Bl -enum -offset indent -compact
|
||||
.It
|
||||
Global top-level group: init, system, user, or a custom group
|
||||
.It
|
||||
Selecting a top-level group for a set of run/task/services
|
||||
.It
|
||||
Per run/task/service limits
|
||||
.El
|
||||
.Pp
|
||||
Top-level group configuration.
|
||||
.Bd -unfilled -offset indent
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
cgroup init cpu.weight:100
|
||||
cgroup user cpu.weight:100
|
||||
cgroup system cpu.weight:9800
|
||||
.Ed
|
||||
.Pp
|
||||
Adding an extra cgroup
|
||||
.Cm maint/
|
||||
will require you to adjust the weight of the above three. We leave
|
||||
.Cm init/
|
||||
and
|
||||
.Cm user/
|
||||
as-is reducing weight of
|
||||
.Cm system/
|
||||
to 9700.
|
||||
.Bd -unfilled -offset indent
|
||||
cgroup system cpu.weight:9700
|
||||
|
||||
# Example extra cgroup 'maint'
|
||||
cgroup maint cpu.weight:100
|
||||
.Ed
|
||||
.Pp
|
||||
By default, the
|
||||
.Cm system/
|
||||
cgroup is selected for almost everything. The
|
||||
.Cm init/
|
||||
cgroup is reserved for PID 1 itself and its closest relatives. The
|
||||
.Cm user/
|
||||
cgroup is for local TTY logins spawned by getty.
|
||||
.Pp
|
||||
To select a different top-level cgroup, e.g.
|
||||
.Cm maint/ ,
|
||||
one can either define it for a group of run/task/service directives in a
|
||||
.Cm .conf
|
||||
or per each stanza:
|
||||
.Bd -unfilled -offset indent
|
||||
cgroup.maint
|
||||
service [...] <...> /path/to/foo args -- description
|
||||
service [...] <...> /path/to/bar args -- description
|
||||
.Ed
|
||||
.Pp
|
||||
or
|
||||
.Bd -unfilled -offset indent
|
||||
service [...] <...> cgroup.maint /path/to/foo args -- description
|
||||
.Ed
|
||||
.Pp
|
||||
The latter form also allows per-stanza limits on the form:
|
||||
.Bd -unfilled -offset indent
|
||||
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||
.Ed
|
||||
.Pp
|
||||
Notice the comma separation and the
|
||||
.Cm mem.
|
||||
exception to the rule: every cgroup setting maps directly to cgroup v2
|
||||
syntax. I.e.,
|
||||
.Cm cpu.max
|
||||
maps to the file
|
||||
.Pm /sys/fs/cgroup/maint/foo/cpu.max .
|
||||
There is no filtering, except for expanding the shorthand
|
||||
.Cm mem.
|
||||
to
|
||||
.Cm memory. ,
|
||||
if the file is not available, either the cgroup controller is not
|
||||
available in your Linux kernel, or the name is misspelled.
|
||||
.Pp
|
||||
Linux cgroups and details surrounding values are not explained in the
|
||||
Finit documentation. The Linux admin-guide cover this well:
|
||||
.Lk https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
|
||||
.Sh LIMITATIONS
|
||||
As of Finit v4 there are no limitations to where
|
||||
.Cm .conf
|
||||
settings can be placed. Except for the system/global
|
||||
.Cm rlimit
|
||||
and
|
||||
.Cm cgroup
|
||||
top-level group declarations, which can only be set from
|
||||
.Pa /etc/finit.conf ,
|
||||
since it is the first
|
||||
.Cm .conf
|
||||
file Finit reads.
|
||||
.Pp
|
||||
Originally,
|
||||
.Pp /etc/finit.conf
|
||||
was the only way to set up a Finit system. Today it is mainly used for
|
||||
bootstrap settings like system hostname, early module loading for
|
||||
watchdogd, network bringup and system shutdown. These can now also be
|
||||
set in any
|
||||
.Cm .conf
|
||||
file in
|
||||
.Pa /etc/finit.d .
|
||||
.Pp
|
||||
There is, however, nothing preventing you from having all configuration
|
||||
settings in
|
||||
.Pa /etc/finit.conf .
|
||||
.Sh SEE ALSO
|
||||
.Xr finit 8 ,
|
||||
.Xr initctl 8
|
||||
.Sh AUTHORS
|
||||
.Nm finit
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
maintained by Joachim Wiberg, with contributions by many others.
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd June 6, 2021
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.Dt INITCTL 8 SMM
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
.Nm initctl
|
||||
.Nd Control tool for Finit
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/initctl
|
||||
.Op Fl bcfhlpqtv
|
||||
.Op COMMAND
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the official tool for interacting with
|
||||
.Xr finit 8 .
|
||||
It comes with functions for showing status, querying health of services,
|
||||
as well as editing, enabling and disabling services.
|
||||
.Sh OPTIONS
|
||||
This program follows the usual UNIX command line syntax, with long
|
||||
options starting with two dashes (`-'). The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl b, -batch
|
||||
Batch mode, no screen size probing.
|
||||
.It Fl c, -create
|
||||
Create missing paths (and files) as needed. Useful with the edit command.
|
||||
.It Fl f, -force
|
||||
Ignore missing files and arguments, never prompt.
|
||||
.It Fl h, -help
|
||||
Show built-in help text.
|
||||
.It Fl 1, -once
|
||||
Only one lap in commands like top.
|
||||
.It Fl p, -plain
|
||||
Use plain table headings, no ANSI control characters.
|
||||
.It Fl q, -quiet
|
||||
Silent, only return status of command.
|
||||
.It Fl t, -no-heading
|
||||
Skip table headings.
|
||||
.It Fl v, -verbose
|
||||
Verbose output, where applicable.
|
||||
.El
|
||||
.Sh COMMANDS
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ar debug
|
||||
Toggle finit (daemon) debug to
|
||||
.Pa /dev/console
|
||||
.It Nm Ar help
|
||||
Show built-in help text
|
||||
.It Nm Ar version
|
||||
Show Finit (daemon) version
|
||||
.It Nm Ar ls | list
|
||||
List all
|
||||
.Cm *.conf
|
||||
files in
|
||||
.Pa /etc/finit.d
|
||||
.It Nm Ar create Ar CONF
|
||||
Create
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.It Nm Ar delete Ar CONF
|
||||
Delete
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.It Nm Ar show Ar CONF
|
||||
Show
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.It Nm Ar edit Ar CONF
|
||||
Edit
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.It Nm Ar touch Ar CONF
|
||||
Change (mark as modified)
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available ,
|
||||
like
|
||||
.Ar edit
|
||||
this tells
|
||||
.Nm finit
|
||||
that a configuration has been changed and is a candidate to be restarted
|
||||
(or SIGHUP'ed) on
|
||||
.Cm initctl reload
|
||||
.It Nm Ar enable Ar CONF
|
||||
Enable
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available ,
|
||||
i.e., add symlink in
|
||||
.Pa /etc/finit.d/enabled
|
||||
.It Nm Ar disable Ar CONF
|
||||
Disable
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/enabled ,
|
||||
i.e., removes symlink
|
||||
.It Nm Ar reload
|
||||
Reload
|
||||
.Cm *.conf in
|
||||
.Pa /etc/finit.d ,
|
||||
i.e., activates changes
|
||||
.It Nm Ar cond set Ar COND
|
||||
Set (assert) user-defined condition,
|
||||
.Cm +usr/COND
|
||||
.It Nm Ar cond clr | clear Ar COND
|
||||
Clear (deassert) user-defined condition,
|
||||
.Cm -usr/COND
|
||||
.It Nm Ar cond status
|
||||
Show condition status, default
|
||||
.Cm cond
|
||||
command.
|
||||
.It Nm Ar cond dump
|
||||
Dump all conditions and their status
|
||||
.It Nm Ar ident Op Cm NAME
|
||||
Display indentities of all run/task/services, or only instances
|
||||
matching
|
||||
.Cm NAME .
|
||||
A partial string, e.g.,
|
||||
.Cm NAM ,
|
||||
will not match anything
|
||||
.It Nm Ar log Op Cm NAME
|
||||
Show ten last Finit, or
|
||||
.Cm NAME ,
|
||||
messages from syslog
|
||||
.It Nm Ar start Cm NAME[:ID]
|
||||
Start service by name, with optional ID, e.g.,
|
||||
.Cm initctl start tty:1
|
||||
.It Nm Ar stop Cm NAME[:ID]
|
||||
Stop/Pause a running service by name
|
||||
.It Nm Ar reload Cm NAME[:ID]
|
||||
Reload service by name (SIGHUP or restart)
|
||||
.It Nm Ar restart Cm NAME[:ID]
|
||||
Restart (stop/start) service by name
|
||||
.It Nm Ar status Cm NAME[:ID]
|
||||
Show service status, by name. If only
|
||||
.Cm NAME
|
||||
is given and multiple instances exits, a summary of all matching
|
||||
instances are shown. Only an exact match displays the detailed status
|
||||
for a particular instance
|
||||
.It Nm Ar status
|
||||
Show status of all services, default command
|
||||
.It Nm Ar cgroup
|
||||
List cgroup config overview
|
||||
.It Nm Ar ps
|
||||
List processes based on cgroups
|
||||
.It Nm Ar top
|
||||
Show top-like listing based on cgroups
|
||||
.It Nm Ar runlevel Op Ar 0-9
|
||||
Show or set runlevel: 0 halt, 6 reboot
|
||||
.It Nm Ar reboot
|
||||
Reboot system, default if
|
||||
.Cm reboot
|
||||
is symlinked to
|
||||
.Nm
|
||||
.It Nm Ar halt
|
||||
Halt system, default if
|
||||
.Cm halt
|
||||
is symlinked to
|
||||
.Nm
|
||||
.It Nm Ar poweroff
|
||||
Power-off system, default if
|
||||
.Cm poweroff
|
||||
is symlinked to
|
||||
.Nm
|
||||
.It Nm Ar suspend
|
||||
Suspend system, default if
|
||||
.Cm suspend
|
||||
is symlinked to
|
||||
.Nm
|
||||
.It Nm Ar utmp show
|
||||
Raw dump of UTMP/WTMP db
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr finit.conf 5
|
||||
.Xr finit 8
|
||||
.Sh AUTHORS
|
||||
.Nm finit
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
maintained by Joachim Wiberg, with contributions by many others.
|
||||
+22
-2
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c pidfile.c procps.c sys.c tty.c usr.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
@@ -33,8 +33,20 @@ if BUILD_RESOLVCONF_PLUGIN
|
||||
libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
if BUILD_RTC_PLUGIN
|
||||
libplug_la_SOURCES += rtc.c
|
||||
endif
|
||||
|
||||
if BUILD_URANDOM_PLUGIN
|
||||
libplug_la_SOURCES += urandom.c
|
||||
endif
|
||||
|
||||
if BUILD_X11_COMMON_PLUGIN
|
||||
libplug_la_SOURCES += x11-common.c
|
||||
endif
|
||||
|
||||
else
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la pidfile.la procps.la sys.la tty.la usr.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
@@ -60,6 +72,14 @@ if BUILD_RESOLVCONF_PLUGIN
|
||||
pkglib_LTLIBRARIES += resolvconf.la
|
||||
endif
|
||||
|
||||
if BUILD_RTC_PLUGIN
|
||||
pkglib_LTLIBRARIES += rtc.la
|
||||
endif
|
||||
|
||||
if BUILD_URANDOM_PLUGIN
|
||||
pkglib_LTLIBRARIES += urandom.la
|
||||
endif
|
||||
|
||||
if BUILD_X11_COMMON_PLUGIN
|
||||
pkglib_LTLIBRARIES += x11-common.la
|
||||
endif
|
||||
|
||||
+16
-1
@@ -21,7 +21,12 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
@@ -30,6 +35,11 @@
|
||||
|
||||
static void save(void *arg)
|
||||
{
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (whichp(ALSACTL)) {
|
||||
_d("Saving sound settings ...");
|
||||
run_interactive(ALSACTL " -g store", "Saving sound settings");
|
||||
@@ -38,6 +48,11 @@ static void save(void *arg)
|
||||
|
||||
static void restore(void *arg)
|
||||
{
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (whichp(ALSACTL)) {
|
||||
_d("Restoring sound settings ...");
|
||||
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
|
||||
|
||||
+37
-18
@@ -24,7 +24,11 @@
|
||||
#include <ftw.h>
|
||||
#include <mntent.h>
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
@@ -65,7 +69,7 @@ static int is_tmpfs(char *path)
|
||||
return tmpfs;
|
||||
}
|
||||
|
||||
static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||
static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||
{
|
||||
if (ftw->level == 0)
|
||||
return 1;
|
||||
@@ -76,8 +80,14 @@ static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We can safely skip tmpfs, nothing to clean from previous boot there */
|
||||
static void bootclean(void)
|
||||
/*
|
||||
* Cleanup stale files from previous boot, if any still linger on.
|
||||
* Some systems, e.g. Alpine Linux, still have a persistent /run and
|
||||
* /tmp, i.e. not tmpfs.
|
||||
*
|
||||
* We can safely skip tmpfs, nothing to clean there.
|
||||
*/
|
||||
static void clean(void *arg)
|
||||
{
|
||||
char *dir[] = {
|
||||
"/tmp/",
|
||||
@@ -90,10 +100,16 @@ static void bootclean(void)
|
||||
if (is_tmpfs(dir[i]))
|
||||
continue;
|
||||
|
||||
nftw(dir[i], do_clean, 20, FTW_DEPTH);
|
||||
nftw(dir[i], bootclean, 20, FTW_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
static void ln(const char *target, const char *linkpath, int ignerr)
|
||||
{
|
||||
if (symlink(target, linkpath) && !ignerr)
|
||||
_pe("Failed creating %s -> %s symlink", target, linkpath);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP
|
||||
*/
|
||||
@@ -101,9 +117,6 @@ static void setup(void *arg)
|
||||
{
|
||||
mode_t prev;
|
||||
|
||||
/* Cleanup stale files, if any still linger on. */
|
||||
bootclean();
|
||||
|
||||
prev = umask(0);
|
||||
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
@@ -116,15 +129,16 @@ static void setup(void *arg)
|
||||
makedir("/var/lib/urandom",0755);
|
||||
if (fisdir("/run")) {
|
||||
_d("System with new /run tmpfs ...");
|
||||
makedir("/run/lock", 1777);
|
||||
symlink("/run/lock", "/var/lock");
|
||||
symlink("/dev/shm", "/run/shm");
|
||||
if (!fisdir("/run/lock"))
|
||||
makedir("/run/lock", 1777);
|
||||
ln("/run/lock", "/var/lock", 0);
|
||||
ln("/dev/shm", "/run/shm", 0);
|
||||
|
||||
/* compat only, should really be set up by OS/dist */
|
||||
symlink("/run", "/var/run");
|
||||
ln("/run", "/var/run", 1);
|
||||
} else {
|
||||
makedir("/var/lock", 1777);
|
||||
makedir("/var/run", 0755);
|
||||
makedir("/var/lock", 1777);
|
||||
makedir("/var/run", 0755);
|
||||
}
|
||||
makedir("/var/log", 0755);
|
||||
makedir("/var/mail", 0755);
|
||||
@@ -175,19 +189,24 @@ static void setup(void *arg)
|
||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||
|
||||
if (!fexist("/etc/mtab"))
|
||||
symlink("../proc/self/mounts", "/etc/mtab");
|
||||
ln("../proc/self/mounts", "/etc/mtab", 1);
|
||||
|
||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
||||
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
|
||||
|
||||
/* Debian has /run/sudo, ensure correct perms and SELinux label */
|
||||
mksubsys("/var/run/sudo", 0711, "root", "root");
|
||||
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
|
||||
if (whichp("restorecon"))
|
||||
run("restorecon /var/run/sudo /var/run/sudo/ts");
|
||||
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.hook[HOOK_MOUNT_POST] = { .cb = clean },
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = setup },
|
||||
.depends = { "pidfile" },
|
||||
};
|
||||
|
||||
|
||||
+20
-5
@@ -22,7 +22,11 @@
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "config.h"
|
||||
@@ -35,7 +39,13 @@
|
||||
#define ARGS "--nofork --system --syslog-only"
|
||||
#define DESC "D-Bus message bus daemon"
|
||||
|
||||
#ifndef DAEMONUSER
|
||||
#define DAEMONUSER "messagebus"
|
||||
#endif
|
||||
|
||||
#ifndef DAEMONPIDFILE
|
||||
#define DAEMONPIDFILE "/var/run/dbus/pid"
|
||||
#endif
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
@@ -43,6 +53,11 @@ static void setup(void *arg)
|
||||
mode_t prev;
|
||||
char *cmd;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = which(DAEMON);
|
||||
if (!cmd) {
|
||||
_d("Skipping plugin, %s is not installed.", DAEMON);
|
||||
@@ -56,11 +71,11 @@ static void setup(void *arg)
|
||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||
|
||||
/* Clean up from any previous pre-bootstrap run */
|
||||
remove("/var/run/dbus/pid");
|
||||
remove(DAEMONPIDFILE);
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345] @%s:%s %s %s -- %s",
|
||||
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
|
||||
DAEMONPIDFILE, DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
@@ -70,7 +85,7 @@ static void setup(void *arg)
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_NETWORK_UP] = {
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
|
||||
+9
-3
@@ -34,6 +34,11 @@ static void setup(void *arg)
|
||||
char cmd[256];
|
||||
char *path;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate /dev and prepare for runtime events from kernel.
|
||||
* Prefer udev if mdev is also available on the system.
|
||||
@@ -43,17 +48,17 @@ static void setup(void *arg)
|
||||
path = which("/lib/systemd/systemd-udevd");
|
||||
if (path) {
|
||||
/* Register udevd as a monitored service */
|
||||
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd name:udevd log %s "
|
||||
snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s "
|
||||
"-- Device event managing daemon", path);
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
||||
_pe("Failed registering %s", path);
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), ":1 [S] <pid/udevd> log "
|
||||
snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log "
|
||||
"udevadm trigger -c add -t devices "
|
||||
"-- Requesting device events");
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
||||
|
||||
snprintf(cmd, sizeof(cmd), ":2 [S] <pid/udevd> log "
|
||||
snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log "
|
||||
"udevadm trigger -c add -t subsystems "
|
||||
"-- Requesting subsystem events");
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
||||
@@ -90,6 +95,7 @@ static plugin_t plugin = {
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc" },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+6
-2
@@ -40,11 +40,15 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h> /* gettimeofday() */
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
#include "plugin.h"
|
||||
|
||||
struct module {
|
||||
|
||||
+25
-16
@@ -21,10 +21,14 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -36,43 +40,48 @@
|
||||
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||
#endif
|
||||
#define SERVICE_LINE \
|
||||
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
||||
"cgroup.init :%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
||||
|
||||
static void load(void *arg)
|
||||
{
|
||||
FILE *fp;
|
||||
DIR *dirp;
|
||||
struct dirent *direntp;
|
||||
struct dirent *d;
|
||||
char line[256];
|
||||
int index = 1;
|
||||
FILE *fp;
|
||||
DIR *dir;
|
||||
|
||||
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
|
||||
|
||||
dirp = opendir(MODULES_LOAD_PATH);
|
||||
if (!dirp) return;
|
||||
dir = opendir(MODULES_LOAD_PATH);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
while ((direntp = readdir(dirp)))
|
||||
{
|
||||
while ((d = readdir(dir))) {
|
||||
char module_path[PATH_MAX];
|
||||
|
||||
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
||||
strlcat(module_path, direntp->d_name, sizeof(module_path));
|
||||
strlcat(module_path, d->d_name, sizeof(module_path));
|
||||
|
||||
fp = fopen(module_path, "r");
|
||||
if (!fp) continue;
|
||||
if (!fp)
|
||||
continue;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
char cmd[CMD_SIZE], *mod, *args;
|
||||
|
||||
mod = strip_line(line);
|
||||
if (!*mod) continue;
|
||||
if (!*mod)
|
||||
continue;
|
||||
|
||||
mod = chomp(mod);
|
||||
if (!mod || !*mod) continue;
|
||||
if (!mod || !*mod)
|
||||
continue;
|
||||
|
||||
mod = strtok_r(mod, " ", &args);
|
||||
if (!mod)
|
||||
continue;
|
||||
|
||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
|
||||
index, mod, mod, args, mod);
|
||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod);
|
||||
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
||||
|
||||
index++;
|
||||
@@ -81,7 +90,7 @@ static void load(void *arg)
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+228
-42
@@ -30,31 +30,35 @@
|
||||
#include "cond.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
|
||||
static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
|
||||
{
|
||||
if (!NLMSG_OK(nh, len))
|
||||
return 1;
|
||||
#define NL_BUFSZ 4096
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_DONE) {
|
||||
_d("Done with netlink messages.");
|
||||
return 1;
|
||||
}
|
||||
struct nl_request {
|
||||
struct nlmsghdr nh;
|
||||
union {
|
||||
struct rtmsg rtm;
|
||||
struct ifinfomsg ifi;
|
||||
};
|
||||
};
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_ERROR) {
|
||||
_d("Netlink reports error.");
|
||||
return 1;
|
||||
}
|
||||
static int nl_defidx;
|
||||
static int nl_ifdown;
|
||||
static char *nl_buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
char daddr[INET_ADDRSTRLEN];
|
||||
char gaddr[INET_ADDRSTRLEN];
|
||||
struct in_addr ind, ing;
|
||||
struct rtmsg *r;
|
||||
struct rtattr *a;
|
||||
int plen = 0;
|
||||
int dst = 0;
|
||||
int idx = 0;
|
||||
int gw = 0;
|
||||
int la;
|
||||
int gw = 0, dst = 0, mask = 0, idx = 0;
|
||||
|
||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
|
||||
_e("Packet too small or truncated!");
|
||||
@@ -80,8 +84,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
|
||||
case RTA_DST:
|
||||
dst = *((int *)data);
|
||||
mask = r->rtm_dst_len;
|
||||
//_d("MASK: 0x%04x", mask);
|
||||
plen = r->rtm_dst_len;
|
||||
//_d("Prefix LEN: 0x%04x", plen);
|
||||
break;
|
||||
|
||||
case RTA_OIF:
|
||||
@@ -93,11 +97,20 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
a = RTA_NEXT(a, la);
|
||||
}
|
||||
|
||||
if ((!dst && !mask) && (gw || idx)) {
|
||||
if (nlmsg->nlmsg_type == RTM_DELROUTE)
|
||||
ind.s_addr = dst;
|
||||
ing.s_addr = gw;
|
||||
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
|
||||
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
|
||||
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
|
||||
|
||||
if ((!dst && !plen) && (gw || idx)) {
|
||||
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
|
||||
cond_clear("net/route/default");
|
||||
else
|
||||
nl_defidx = 0;
|
||||
} else {
|
||||
cond_set("net/route/default");
|
||||
nl_defidx = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,14 +130,14 @@ static int validate_ifname(const char *ifname)
|
||||
if (!ifname || !ifname[0])
|
||||
return 1;
|
||||
|
||||
if (strlen(ifname) >= IFNAMSIZ)
|
||||
if (strnlen(ifname, IFNAMSIZ) == IFNAMSIZ)
|
||||
return 1;
|
||||
|
||||
if (strstr(ifname, ".."))
|
||||
if (!strcmp(ifname, ".") || !strcmp(ifname, ".."))
|
||||
return 1;
|
||||
|
||||
while (*ifname) {
|
||||
if (*ifname == '/' || isspace(*ifname))
|
||||
if (*ifname == '/' || *ifname == ':' || isspace(*ifname))
|
||||
return 1;
|
||||
ifname++;
|
||||
}
|
||||
@@ -132,12 +145,25 @@ static int validate_ifname(const char *ifname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if this interface was associated with the default route
|
||||
* previously, or if it's been removed. If so, trigger a recheck
|
||||
* of the system default route.
|
||||
*/
|
||||
static void nl_check_default(char *ifname)
|
||||
{
|
||||
int idx = (int)if_nametoindex(ifname);
|
||||
|
||||
if ((nl_defidx > 0 && nl_defidx == idx) || (idx == 0 && errno == ENODEV))
|
||||
nl_ifdown = 1;
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
int la;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
struct rtattr *a;
|
||||
struct ifinfomsg *i;
|
||||
struct rtattr *a;
|
||||
int la;
|
||||
|
||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
|
||||
_e("Packet too small or truncated!");
|
||||
@@ -172,6 +198,8 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
net_cond_set(ifname, "exist", 1);
|
||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||
if (!(i->ifi_flags & IFF_UP) || !(i->ifi_flags & IFF_RUNNING))
|
||||
nl_check_default(ifname);
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
@@ -180,6 +208,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
net_cond_set(ifname, "exist", 0);
|
||||
net_cond_set(ifname, "up", 0);
|
||||
net_cond_set(ifname, "running", 0);
|
||||
nl_check_default(ifname);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
@@ -197,26 +226,176 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
}
|
||||
}
|
||||
|
||||
static void nl_callback(void *arg, int sd, int events)
|
||||
static int nl_parse(int sd)
|
||||
{
|
||||
static char buf[4096];
|
||||
struct nlmsghdr *nh;
|
||||
ssize_t len;
|
||||
while (1) {
|
||||
struct nlmsghdr *nh;
|
||||
ssize_t len;
|
||||
size_t l;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
len = recv(sd, buf, sizeof(buf), 0);
|
||||
if (len < 0) {
|
||||
if (errno != EINTR) /* Signal */
|
||||
_pe("recv()");
|
||||
while ((len = recv(sd, nl_buf, NL_BUFSZ, 0)) < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN: /* Nothing more right now. */
|
||||
return 0;
|
||||
|
||||
case EINTR: /* Signal */
|
||||
continue;
|
||||
|
||||
case ENOBUFS: /* netlink(7) */
|
||||
break;
|
||||
|
||||
default:
|
||||
_pe("recv()");
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
_d("recv %lld bytes", len);
|
||||
l = (size_t)len;
|
||||
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
|
||||
struct nlmsgerr *nle;
|
||||
|
||||
switch (nh->nlmsg_type) {
|
||||
case NLMSG_DONE:
|
||||
_d("Done with netlink messages.");
|
||||
return 0;
|
||||
|
||||
case NLMSG_ERROR:
|
||||
_d("Kernel netlink comm. error.");
|
||||
nle = NLMSG_DATA(nh);
|
||||
if (nle) {
|
||||
errno = -nle->error;
|
||||
_pe("Kernel netlink error %d", errno);
|
||||
}
|
||||
return -1;
|
||||
|
||||
case RTM_NEWROUTE:
|
||||
case RTM_DELROUTE:
|
||||
_d("Netlink route ...");
|
||||
nl_route(nh, len);
|
||||
break;
|
||||
|
||||
case RTM_NEWLINK:
|
||||
case RTM_DELLINK:
|
||||
_d("Netlink link ...");
|
||||
nl_link(nh, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
_w("unhandled netlink message, type %d", nh->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int nl_request(int sd, unsigned int seq, int type)
|
||||
{
|
||||
struct nl_request *nlr = (struct nl_request *)nl_buf;
|
||||
|
||||
memset(nlr, 0, sizeof(struct nl_request));
|
||||
nlr->nh.nlmsg_type = type;
|
||||
nlr->nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||||
nlr->nh.nlmsg_seq = seq;
|
||||
nlr->nh.nlmsg_pid = 1;
|
||||
|
||||
switch (type) {
|
||||
case RTM_GETROUTE:
|
||||
_d("RTM_GETROUTE");
|
||||
nlr->rtm.rtm_family = AF_INET;
|
||||
nlr->rtm.rtm_table = RT_TABLE_MAIN;
|
||||
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
break;
|
||||
|
||||
case RTM_GETLINK:
|
||||
_d("RTM_GETLINK");
|
||||
nlr->ifi.ifi_family = AF_UNSPEC;
|
||||
nlr->ifi.ifi_change = 0xFFFFFFFF;
|
||||
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
break;
|
||||
|
||||
default:
|
||||
_w("Cannot resync, unhandled message type %d", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (send(sd, nlr, nlr->nh.nlmsg_len, 0) < 0)
|
||||
return 1;
|
||||
|
||||
return nl_parse(sd);
|
||||
}
|
||||
|
||||
static void nl_resync_routes(int sd, unsigned int seq)
|
||||
{
|
||||
if (nl_request(sd, seq, RTM_GETROUTE))
|
||||
_pe("Failed netlink route request");
|
||||
}
|
||||
|
||||
static void nl_resync_ifaces(int sd, unsigned int seq)
|
||||
{
|
||||
if (nl_request(sd, seq, RTM_GETLINK))
|
||||
_pe("Failed netlink link request");
|
||||
}
|
||||
|
||||
/*
|
||||
* We've potentially lost netlink events, let's resync with kernel.
|
||||
*/
|
||||
static void nl_resync(int all)
|
||||
{
|
||||
unsigned int seq = 0;
|
||||
int sd;
|
||||
|
||||
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||||
if (sd < 0) {
|
||||
_pe("netlink socket");
|
||||
return;
|
||||
}
|
||||
|
||||
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
|
||||
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
|
||||
nl_route(nh, len);
|
||||
else
|
||||
nl_link(nh, len);
|
||||
if (all) {
|
||||
_d("============================ RESYNC =================================");
|
||||
/* this doesn't update conditions, and thus does not stop services */
|
||||
cond_deassert("net/");
|
||||
|
||||
nl_resync_ifaces(sd, seq++);
|
||||
nl_resync_routes(sd, seq++);
|
||||
|
||||
/* delayed update after we've corrected things */
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
_d("=========================== RESYNCED ================================");
|
||||
} else
|
||||
nl_resync_routes(sd, seq++);
|
||||
|
||||
close(sd);
|
||||
}
|
||||
|
||||
static void nl_callback(void *arg, int sd, int events)
|
||||
{
|
||||
if (nl_parse(sd) < 0) {
|
||||
if (errno == ENOBUFS) { /* netlink(7) */
|
||||
_w("busy system, resynchronizing with kernel.");
|
||||
nl_resync(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux doesn't send route changes when interfaces go down, so
|
||||
* we need to check ourselves, e.g. for loss of default route.
|
||||
*/
|
||||
if (nl_ifdown) {
|
||||
_d("interface down, checking default route.");
|
||||
if (nl_defidx > 0) {
|
||||
nl_defidx = 0;
|
||||
nl_resync(0);
|
||||
if (nl_defidx <= 0) {
|
||||
cond_clear("net/route/default");
|
||||
nl_defidx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
nl_ifdown = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,8 +415,8 @@ static plugin_t plugin = {
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
int sd;
|
||||
struct sockaddr_nl sa;
|
||||
int sd;
|
||||
|
||||
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (sd < 0) {
|
||||
@@ -247,7 +426,7 @@ PLUGIN_INIT(plugin_init)
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.nl_family = AF_NETLINK;
|
||||
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; // | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR;
|
||||
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK;
|
||||
sa.nl_pid = getpid();
|
||||
|
||||
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
@@ -256,6 +435,13 @@ PLUGIN_INIT(plugin_init)
|
||||
return;
|
||||
}
|
||||
|
||||
nl_buf = malloc(NL_BUFSZ);
|
||||
if (!nl_buf) {
|
||||
_pe("malloc()");
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.io.fd = sd;
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
+16
-1
@@ -84,13 +84,20 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
if (!svc_has_pidfile(svc))
|
||||
if (!svc_has_pidfile(svc)) {
|
||||
_d("Setting %s PID file to %s", svc->name, fn);
|
||||
pid_file_set(svc, fn, 1);
|
||||
}
|
||||
|
||||
if (svc_is_forking(svc)) {
|
||||
pid_t pid;
|
||||
|
||||
pid = pid_file_read(pid_file(svc));
|
||||
if (pid == -1) {
|
||||
cond_clear(cond);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid != svc->pid) {
|
||||
_d("Forking service %s changed PID from %d to %d",
|
||||
svc->cmd, svc->pid, pid);
|
||||
@@ -231,8 +238,14 @@ static void pidfile_reconf(void *arg)
|
||||
|
||||
static void pidfile_init(void *arg)
|
||||
{
|
||||
char piddir[MAX_ARG_LEN];
|
||||
char *path;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The bootmisc plugin is responsible for setting up /var/run.
|
||||
* and/or /run, with proper symlinks etc. We depend on bootmisc
|
||||
@@ -250,6 +263,8 @@ static void pidfile_init(void *arg)
|
||||
|
||||
if (pidfile_add_path(&iw_pidfile, path))
|
||||
iwatch_exit(&iw_pidfile);
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+13
-1
@@ -22,7 +22,14 @@
|
||||
*/
|
||||
|
||||
#include <glob.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
static void setup(void *arg)
|
||||
@@ -31,6 +38,11 @@ static void setup(void *arg)
|
||||
glob_t gl;
|
||||
char buf[160];
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
glob("/run/sysctl.d/*.conf", 0, NULL, &gl);
|
||||
glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
glob("/usr/local/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
|
||||
@@ -21,7 +21,11 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
|
||||
+26
-2
@@ -24,7 +24,11 @@
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -84,6 +88,11 @@ static void rtc_save(void *arg)
|
||||
int fd, rc = 0;
|
||||
char tz[128];
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = rtc_open();
|
||||
if (fd < 0)
|
||||
return;
|
||||
@@ -121,6 +130,11 @@ static void rtc_restore(void *arg)
|
||||
int fd, rc = 0;
|
||||
char tz[128];
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = rtc_open();
|
||||
if (fd < 0)
|
||||
return;
|
||||
@@ -142,8 +156,18 @@ static void rtc_restore(void *arg)
|
||||
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
||||
else
|
||||
logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno));
|
||||
|
||||
/* Been here already? */
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Attempt to set RTC to a sane value ... */
|
||||
tv.tv_sec = RTC_TIMESTAMP_BEGIN_2000;
|
||||
if (!gmtime_r(&tv.tv_sec, &tm))
|
||||
goto out;
|
||||
|
||||
logit(LOG_NOTICE, "Resetting RTC to kernel default, 2000-01-01 00:00.");
|
||||
rc = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
||||
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/* System condition event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fnmatch.h>
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "iwatch.h"
|
||||
|
||||
static struct iwatch iw_sys;
|
||||
|
||||
|
||||
static int sys_add_path(struct iwatch *iw, char *path)
|
||||
{
|
||||
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* unlink conditions with no active task/service, otherwise they may
|
||||
* trigger inadvertently when calling `initctl reload` to activate
|
||||
* a new configuration.
|
||||
*/
|
||||
static void sys_update_conds(char *dir, char *name, uint32_t mask)
|
||||
{
|
||||
char path[256];
|
||||
char *cond;
|
||||
char *ptr;
|
||||
|
||||
ptr = strrchr(name, '/');
|
||||
if (ptr)
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, ++ptr);
|
||||
else
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
|
||||
cond = strstr(path, COND_BASE);
|
||||
if (!cond)
|
||||
return;
|
||||
|
||||
cond += strlen(COND_BASE) + 1;
|
||||
_d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0);
|
||||
if (!cond_update(cond))
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
/* synthesize events in case of new run dirs */
|
||||
static void sys_scandir(struct iwatch *iw, char *dir, int len)
|
||||
{
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
char path[len + 6];
|
||||
int rc;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/*", dir);
|
||||
rc = glob(path, GLOB_NOSORT, NULL, &gl);
|
||||
if (rc && rc != GLOB_NOMATCH)
|
||||
return;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
_d("scan found %s", gl.gl_pathv[i]);
|
||||
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
|
||||
/*
|
||||
* create/remove sub-directory in monitored directory
|
||||
*/
|
||||
static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||
{
|
||||
char path[strlen(dir) + strlen(name) + 2];
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
_d("path: %s", path);
|
||||
|
||||
iwp = iwatch_find_by_path(iw, path);
|
||||
|
||||
if (mask & IN_CREATE) {
|
||||
if (!iwp) {
|
||||
if (!sys_add_path(iw, path))
|
||||
sys_scandir(iw, path, sizeof(path));
|
||||
}
|
||||
} else if (mask & IN_DELETE) {
|
||||
if (iwp)
|
||||
iwatch_del(&iw_sys, iwp);
|
||||
}
|
||||
}
|
||||
|
||||
static void sys_callback(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
/* Find base path for this event */
|
||||
iwp = iwatch_find_by_wd(&iw_sys, ev->wd);
|
||||
if (!iwp)
|
||||
continue;
|
||||
|
||||
if (ev->mask & IN_ISDIR) {
|
||||
sys_handle_dir(&iw_sys, iwp->path, ev->name, ev->mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & (IN_CREATE | IN_DELETE))
|
||||
sys_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void sys_init(void *arg)
|
||||
{
|
||||
char sysdir[MAX_ARG_LEN];
|
||||
char *path;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
|
||||
return;
|
||||
}
|
||||
|
||||
path = realpath(_PATH_CONDSYS, NULL);
|
||||
if (!path) {
|
||||
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
|
||||
iwatch_exit(&iw_sys);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = iwatch_init(&iw_sys);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
plugin.io.fd = fd;
|
||||
plugin.io.cb = sys_callback;
|
||||
plugin.io.flags = PLUGIN_IO_READ;
|
||||
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
iwatch_exit(&iw_sys);
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+21
-11
@@ -27,18 +27,23 @@
|
||||
#include <unistd.h> /* read() */
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
|
||||
static void watcher(void *arg, int fd, int events);
|
||||
static void tty_watcher(void *arg, int fd, int events);
|
||||
|
||||
static plugin_t plugin = {
|
||||
.io = {
|
||||
.cb = watcher,
|
||||
.cb = tty_watcher,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
@@ -56,19 +61,24 @@ static void setup(void)
|
||||
static void do_tty(char *tty, size_t len, int creat)
|
||||
{
|
||||
char name[len + 6];
|
||||
struct tty *entry;
|
||||
svc_t *svc;
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/%s", tty);
|
||||
entry = tty_find(name);
|
||||
if (entry && tty_enabled(entry)) {
|
||||
if (creat)
|
||||
tty_start(entry);
|
||||
else if (entry->pid)
|
||||
tty_stop(entry);
|
||||
svc = svc_find_by_tty(name);
|
||||
if (svc) {
|
||||
if (svc_is_blocked(svc) && creat) {
|
||||
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
|
||||
svc_start(svc);
|
||||
} else if (svc->pid) {
|
||||
_d("%s: missing ...", svc_ident(svc, NULL, 0));
|
||||
svc_missing(svc);
|
||||
}
|
||||
|
||||
service_step_all(SVC_TYPE_TTY);
|
||||
}
|
||||
}
|
||||
|
||||
static void watcher(void *arg, int fd, int events)
|
||||
static void tty_watcher(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
|
||||
+81
-4
@@ -1,4 +1,4 @@
|
||||
/* Setup random seed
|
||||
/* Setup and save random seed at boot/shutdown
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
@@ -22,19 +22,39 @@
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h> /* gettimeofday() */
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <linux/random.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#ifndef RANDOM_BYTES
|
||||
#define RANDOM_BYTES (32*1024)
|
||||
#endif
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
struct rand_pool_info *rpi;
|
||||
ssize_t len = 0;
|
||||
int rc = -1;
|
||||
int fd, err;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fexist(RANDOMSEED)) {
|
||||
int ret = 1;
|
||||
mode_t prev;
|
||||
@@ -62,7 +82,59 @@ static void setup(void *arg)
|
||||
}
|
||||
|
||||
print_desc("Initializing random number generator", NULL);
|
||||
print_result(512 != copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
|
||||
|
||||
/*
|
||||
* Simply copying our saved entropy to /dev/urandom doesn't
|
||||
* increment the kernel "entropy count", instead we must use
|
||||
* the ioctl below.
|
||||
*/
|
||||
fd = open(RANDOMSEED, O_RDONLY);
|
||||
if (fd < 0)
|
||||
goto fallback;
|
||||
|
||||
rpi = malloc(sizeof(*rpi) + RANDOM_BYTES);
|
||||
if (!rpi) {
|
||||
close(fd);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
do {
|
||||
ssize_t num;
|
||||
|
||||
num = read(fd, &rpi->buf[len], RANDOM_BYTES - len);
|
||||
if (num <= 0) {
|
||||
if (num == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (len > 0)
|
||||
break;
|
||||
|
||||
close(fd);
|
||||
free(rpi);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
len += num;
|
||||
} while (len < RANDOM_BYTES);
|
||||
close(fd);
|
||||
|
||||
fd = open("/dev/urandom", O_WRONLY);
|
||||
if (fd < 0) {
|
||||
free(rpi);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
rpi->buf_size = len;
|
||||
rpi->entropy_count = len * 8;
|
||||
rc = ioctl(fd, RNDADDENTROPY, rpi);
|
||||
err = errno;
|
||||
close(fd);
|
||||
free(rpi);
|
||||
if (rc < 0)
|
||||
logit(LOG_ERR, "Failed adding entropy to kernel random pool: %s", strerror(err));
|
||||
print_result(rc < 0);
|
||||
return;
|
||||
fallback:
|
||||
print_result(512 > copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -71,10 +143,15 @@ static void save(void *arg)
|
||||
#ifdef RANDOMSEED
|
||||
mode_t prev;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
prev = umask(077);
|
||||
|
||||
print_desc("Saving random seed", NULL);
|
||||
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
||||
print_result(RANDOM_BYTES != copyfile("/dev/urandom", RANDOMSEED, RANDOM_BYTES, 0));
|
||||
|
||||
umask(prev);
|
||||
#endif
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
/* User-defined condtion event monitor for the Finit condition engine
|
||||
/* User-defined condition event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
@@ -97,6 +97,8 @@ static void usr_init(void *arg)
|
||||
|
||||
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
|
||||
iwatch_exit(&iw_usr);
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+10
-1
@@ -24,7 +24,11 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -39,6 +43,11 @@ static void setup(void *arg)
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
prev = umask(0);
|
||||
|
||||
makedir("/var/run/console", 01777);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
.deps/*
|
||||
finit
|
||||
getty
|
||||
initctl
|
||||
keventd
|
||||
logit
|
||||
reboot
|
||||
sulogin
|
||||
watchdogd
|
||||
Makefile
|
||||
Makefile.in
|
||||
|
||||
+30
-18
@@ -1,4 +1,4 @@
|
||||
EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
|
||||
EXTRA_DIST = rescue.conf sample.conf
|
||||
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
if STATIC
|
||||
AM_CPPFLAGS += -DENABLE_STATIC
|
||||
@@ -7,29 +7,42 @@ else
|
||||
AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
sbin_PROGRAMS = finit initctl
|
||||
pkglibexec_SCRIPTS = cgreaper.sh
|
||||
sbin_PROGRAMS = finit initctl
|
||||
pkglibexec_PROGRAMS = getty logit sulogin
|
||||
if KEVENTD
|
||||
pkglibexec_PROGRAMS += keventd
|
||||
endif
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS += watchdogd
|
||||
endif
|
||||
|
||||
finit_pkglibdir = $(pkglibdir)
|
||||
finit_pkglib_SCRIPTS = rescue.conf sample.conf
|
||||
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS = watchdogd
|
||||
endif
|
||||
getty_SOURCES = finit.h getty.c helpers.h logrotate.c stty.c utmp-api.c utmp-api.h
|
||||
getty_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
getty_CFLAGS += $(lite_CFLAGS)
|
||||
getty_LDADD = $(lite_LIBS)
|
||||
|
||||
if LOGIT
|
||||
pkglibexec_PROGRAMS = logit
|
||||
logit_SOURCES = logit.c logrotate.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
logit_CFLAGS += $(lite_CFLAGS)
|
||||
logit_LDADD = $(lite_LIBS)
|
||||
endif
|
||||
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h
|
||||
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
keventd_CFLAGS += $(lite_CFLAGS)
|
||||
keventd_LDADD = $(lite_LIBS)
|
||||
|
||||
sulogin_SOURCES = sulogin.c
|
||||
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
sulogin_LDADD = -lcrypt
|
||||
|
||||
logit_SOURCES = logit.c logrotate.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
logit_CFLAGS += $(lite_CFLAGS)
|
||||
logit_LDADD = $(lite_LIBS)
|
||||
|
||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
cond.c cond-w.c cond.h \
|
||||
conf.c conf.h \
|
||||
exec.c finit.c finit.h \
|
||||
getty.c stty.c \
|
||||
stty.c \
|
||||
helpers.c helpers.h \
|
||||
iwatch.c iwatch.h \
|
||||
log.c log.h \
|
||||
@@ -48,7 +61,7 @@ if LOGROTATE
|
||||
finit_SOURCES += logrotate.c
|
||||
endif
|
||||
|
||||
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
|
||||
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
|
||||
|
||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
@@ -63,8 +76,8 @@ initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||
client.c client.h cond.c cond.h reboot.c \
|
||||
serv.c serv.h svc.h util.c util.h
|
||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
initctl_CFLAGS += $(lite_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS)
|
||||
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
|
||||
INIT_LNKS = init telinit
|
||||
REBOOT_LNKS = reboot shutdown halt poweroff suspend
|
||||
@@ -72,7 +85,6 @@ REBOOT_LNKS = reboot shutdown halt poweroff suspend
|
||||
if WATCHDOGD
|
||||
watchdogd_SOURCES = watchdog.c watchdog.h
|
||||
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
#log.c log.h
|
||||
endif
|
||||
|
||||
# Hook in install to add finit and reboot symlink(s)
|
||||
|
||||
@@ -29,7 +29,11 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <uev/uev.h>
|
||||
|
||||
#include "config.h"
|
||||
@@ -74,7 +78,24 @@ static int start(svc_t *svc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: this does not wait for svc to be stopped first, that is the
|
||||
* responsibility of initctl to do. Otherwise we'd block PID 1,
|
||||
* or introduce some nasty race conditions.
|
||||
*/
|
||||
static int restart(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
svc_start(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reload(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
@@ -88,9 +109,10 @@ static int restart(svc_t *svc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
|
||||
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
|
||||
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
|
||||
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
|
||||
static int do_reload (char *buf, size_t len) { return call(reload, buf, len); }
|
||||
|
||||
static char query_buf[368];
|
||||
static int missing(char *job, char *id)
|
||||
@@ -110,7 +132,7 @@ static int missing(char *job, char *id)
|
||||
|
||||
static int do_query(char *buf, size_t len)
|
||||
{
|
||||
memset(query_buf, 0, sizeof(query_buf));
|
||||
query_buf[0] = 0;
|
||||
if (svc_parse_jobstr(buf, len, NULL, missing)) {
|
||||
memcpy(buf, query_buf, len);
|
||||
return 1;
|
||||
@@ -252,10 +274,10 @@ static void send_svc(int sd, svc_t *svc)
|
||||
|
||||
static void api_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
int sd, lvl;
|
||||
svc_t *svc;
|
||||
static svc_t *iter = NULL;
|
||||
struct init_request rq;
|
||||
int sd, lvl;
|
||||
svc_t *svc;
|
||||
|
||||
sd = accept(w->fd, NULL, NULL);
|
||||
if (sd < 0) {
|
||||
@@ -327,16 +349,22 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
result = do_start(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
_d("restart %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_restart(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_STOP_SVC:
|
||||
_d("stop %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_stop(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
_d("restart %s", rq.data);
|
||||
case INIT_CMD_RELOAD_SVC:
|
||||
_d("reload %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_restart(rq.data, sizeof(rq.data));
|
||||
result = do_reload(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_RUNLEVEL:
|
||||
@@ -363,27 +391,33 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
break;
|
||||
}
|
||||
|
||||
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
||||
if (!svc_find_by_pid(rq.runlevel)) {
|
||||
_d("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
||||
svc = svc_find_by_pid(rq.runlevel);
|
||||
if (!svc) {
|
||||
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable and allow Finit to collect bundled watchdog */
|
||||
if (wdog) {
|
||||
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
|
||||
stop(wdog);
|
||||
if (wdog && wdog != svc) {
|
||||
char name[32];
|
||||
|
||||
svc_ident(svc, name, sizeof(name));
|
||||
logit(LOG_NOTICE, "Handing over wdog ctrl from %s[%d] to %s[%d]",
|
||||
svc_ident(wdog, NULL, 0), wdog->pid, name, svc->pid);
|
||||
|
||||
if (wdog->protect) {
|
||||
wdog->protect = 0;
|
||||
wdog->runlevels = 0;
|
||||
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog.");
|
||||
stop(wdog);
|
||||
svc_del(wdog);
|
||||
}
|
||||
}
|
||||
wdog = svc;
|
||||
break;
|
||||
|
||||
case INIT_CMD_SVC_ITER:
|
||||
// _d("svc iter, first: %d", rq.runlevel);
|
||||
/*
|
||||
* XXX: This severly limits the number of
|
||||
* XXX: This severely limits the number of
|
||||
* simultaneous client connections, but will
|
||||
* have to do for now.
|
||||
*/
|
||||
@@ -436,34 +470,41 @@ error:
|
||||
|
||||
int api_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int sd;
|
||||
mode_t oldmask;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
mode_t oldmask;
|
||||
int uid, gid;
|
||||
int sd;
|
||||
|
||||
_d("Setting up external API socket ...");
|
||||
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
||||
if (-1 == sd) {
|
||||
_pe("Failed starting external API socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uid = geteuid();
|
||||
gid = getgroup(DEFGROUP);
|
||||
|
||||
erase(INIT_SOCKET);
|
||||
oldmask = umask(0077);
|
||||
oldmask = umask(0117);
|
||||
if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun)))
|
||||
goto error;
|
||||
|
||||
if (-1 == listen(sd, 10))
|
||||
goto error;
|
||||
|
||||
if (chown(INIT_SOCKET, uid, gid))
|
||||
_pe("Failed setting group %s on %s", DEFGROUP, INIT_SOCKET);
|
||||
|
||||
umask(oldmask);
|
||||
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
|
||||
return 0;
|
||||
|
||||
error:
|
||||
_pe("Failed intializing API socket");
|
||||
_pe("Failed initializing API socket");
|
||||
umask(oldmask);
|
||||
close(sd);
|
||||
return 1;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Called by kernel when last child in cgroup exists
|
||||
|
||||
BASE=/sys/fs/cgroup/finit
|
||||
PROC=$(basename "$1")
|
||||
DIR="$BASE$1"
|
||||
|
||||
#/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
|
||||
rmdir "$DIR"
|
||||
|
||||
exit 0
|
||||
+443
-125
@@ -24,178 +24,496 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
# include <libite/queue.h> /* BSD sys/queue.h API */
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
# include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#endif
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysinfo.h> /* get_nprocs_conf() */
|
||||
|
||||
#include "cgroup.h"
|
||||
#include "finit.h"
|
||||
#include "iwatch.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static char controllers[256] = "none";
|
||||
struct cg {
|
||||
TAILQ_ENTRY(cg) link;
|
||||
|
||||
static void group_init(char *path, char *nproc, int share)
|
||||
char *name; /* top-level group name */
|
||||
char *cfg; /* kernel settings */
|
||||
|
||||
int active; /* for mark & sweep */
|
||||
int is_protected; /* for init/, user/, & system/ */
|
||||
};
|
||||
|
||||
static TAILQ_HEAD(, cg) cgroups = TAILQ_HEAD_INITIALIZER(cgroups);
|
||||
|
||||
static char controllers[256];
|
||||
|
||||
static struct iwatch iw_cgroup;
|
||||
static uev_t cgw;
|
||||
static int avail;
|
||||
|
||||
|
||||
static void cgset(const char *path, char *ctrl, char *prop)
|
||||
{
|
||||
char file[strlen(path) + 64];
|
||||
char *val;
|
||||
|
||||
paste(file, sizeof(file), path, "cgroup.clone_children");
|
||||
echo(file, 0, "1");
|
||||
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
|
||||
if (!path || !ctrl) {
|
||||
_e("Missing path or controller, skipping!");
|
||||
return;
|
||||
}
|
||||
|
||||
paste(file, sizeof(file), path, "cpuset.cpus");
|
||||
echo(file, 0, nproc);
|
||||
if (!prop) {
|
||||
prop = strchr(ctrl, '.');
|
||||
if (!prop) {
|
||||
_e("Invalid cgroup ctrl syntax: %s", ctrl);
|
||||
return;
|
||||
}
|
||||
|
||||
paste(file, sizeof(file), path, "cpuset.mems");
|
||||
echo(file, 0, "0");
|
||||
*prop++ = 0;
|
||||
}
|
||||
|
||||
paste(file, sizeof(file), path, "cpu.shares");
|
||||
echo(file, 0, "%d", share);
|
||||
val = strchr(prop, ':');
|
||||
if (!val) {
|
||||
_e("Missing cgroup ctrl value, prop %s", prop);
|
||||
return;
|
||||
}
|
||||
*val++ = 0;
|
||||
|
||||
paste(file, sizeof(file), path, "memory.use_hierarchy");
|
||||
echo(file, 0, "1");
|
||||
/* disallow sneaky relative paths */
|
||||
if (strstr(ctrl, "..") || strstr(prop, "..")) {
|
||||
_e("Possible security violation; '..' not allowed in cgroup config!");
|
||||
return;
|
||||
}
|
||||
|
||||
_d("%s/%s.%s <= %s", path, ctrl, prop, val);
|
||||
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
|
||||
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Settings for a cgroup are on the form: cpu.weight:1234,mem.max:4321,...
|
||||
* Finit supports the short-form 'mem.', replacing it with 'memory.' when
|
||||
* writing the setting to the file system.
|
||||
*/
|
||||
static void group_init(char *path, int leaf, const char *cfg)
|
||||
{
|
||||
char *ptr, *s;
|
||||
|
||||
_d("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
|
||||
if (!fisdir(path)) {
|
||||
if (mkdir(path, 0755)) {
|
||||
_pe("Failed creating cgroup %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
/* enable detected controllers on domain groups */
|
||||
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
|
||||
_pe("Failed enabling %s for %s", controllers, path);
|
||||
}
|
||||
|
||||
if (!cfg || !cfg[0])
|
||||
return;
|
||||
|
||||
s = strdupa(cfg);
|
||||
if (!s) {
|
||||
_pe("Failed activating cgroup cfg for %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("%s <=> %s", path, s);
|
||||
ptr = strtok(s, ",");
|
||||
while (ptr) {
|
||||
_d("ptr: %s", ptr);
|
||||
if (!strncmp("mem.", ptr, 4))
|
||||
cgset(path, "memory", &ptr[4]);
|
||||
else
|
||||
cgset(path, ptr, NULL);
|
||||
|
||||
ptr = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
|
||||
static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
||||
if (pid < 0 || pid == 1) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create and initialize new group */
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||
group_init(path, 1, cfg);
|
||||
|
||||
/* move process to new group */
|
||||
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
|
||||
_pe("Failed moving pid %d to group %s", pid, path);
|
||||
|
||||
strlcat(path, "/cgroup.events", sizeof(path));
|
||||
|
||||
return iwatch_add(&iw_cgroup, path, 0);
|
||||
}
|
||||
|
||||
int cgroup_user(char *name, int pid)
|
||||
{
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
return cgroup_leaf_init("user", name, pid, NULL);
|
||||
}
|
||||
|
||||
int cgroup_service(char *name, int pid, struct cgroup *cg)
|
||||
{
|
||||
char *group = "system";
|
||||
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
if (cg && cg->name[0]) {
|
||||
char path[256];
|
||||
|
||||
if (!strcmp(cg->name, "root"))
|
||||
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
|
||||
|
||||
if (!strcmp(cg->name, "init"))
|
||||
return fnwrite(str("%d", pid), FINIT_CGPATH "/init/cgroup.procs");
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
|
||||
if (fisdir(path))
|
||||
group = cg->name;
|
||||
}
|
||||
|
||||
return cgroup_leaf_init(group, name, pid, cg ? cg->cfg : NULL);
|
||||
}
|
||||
|
||||
static void append_ctrl(char *ctrl)
|
||||
{
|
||||
char *wanted[] = {
|
||||
"cpu",
|
||||
"cpuacct",
|
||||
"cpuset",
|
||||
"memory"
|
||||
};
|
||||
size_t i;
|
||||
if (controllers[0])
|
||||
strlcat(controllers, " ", sizeof(controllers));
|
||||
|
||||
for (i = 0; i < NELEMS(wanted); i++) {
|
||||
if (strcmp(wanted[i], ctrl))
|
||||
strlcat(controllers, "+", sizeof(controllers));
|
||||
strlcat(controllers, ctrl, sizeof(controllers));
|
||||
}
|
||||
|
||||
static void cgroup_handle_event(char *event, uint32_t mask)
|
||||
{
|
||||
char path[strlen(event) + 1];
|
||||
char buf[80];
|
||||
char *ptr;
|
||||
FILE *fp;
|
||||
|
||||
_d("event: '%s', mask: %08x", event, mask);
|
||||
if (!(mask & IN_MODIFY))
|
||||
return;
|
||||
|
||||
fp = fopen(event, "r");
|
||||
if (!fp) {
|
||||
_d("Failed opening %s, skipping ...", event);
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (strncmp(buf, "populated", 9))
|
||||
continue;
|
||||
|
||||
if (strcmp(controllers, "none"))
|
||||
strlcat(controllers, ",", sizeof(controllers));
|
||||
else
|
||||
strlcpy(controllers, "", sizeof(controllers));
|
||||
chomp(buf);
|
||||
if (atoi(&buf[10]))
|
||||
break;
|
||||
|
||||
strlcat(controllers, ctrl, sizeof(controllers));
|
||||
strlcpy(path, event, sizeof(path));
|
||||
ptr = strrchr(path, '/');
|
||||
if (ptr) {
|
||||
*ptr = 0;
|
||||
if (!cgroup_del(path)) {
|
||||
/*
|
||||
* try with parent, top-level group, we
|
||||
* may get events out-of-order *sigh*
|
||||
*/
|
||||
ptr = strrchr(path, '/');
|
||||
if (!ptr)
|
||||
break;
|
||||
*ptr = 0;
|
||||
cgroup_del(path);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static void cgroup_events_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
/* Find base path for this event */
|
||||
iwp = iwatch_find_by_wd(&iw_cgroup, ev->wd);
|
||||
if (!iwp || !iwp->path)
|
||||
continue;
|
||||
|
||||
cgroup_handle_event(iwp->path, ev->mask);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AUTO_RELOAD
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct cg *cgroup_find(char *name)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||
if (strcmp(cg->name, name))
|
||||
continue;
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Marks all unprotected cgroups for deletion (during reload)
|
||||
*/
|
||||
void cgroup_mark_all(void)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
if (!avail)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||
if (cg->is_protected)
|
||||
continue;
|
||||
|
||||
cg->active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove (try to) all unused cgroups
|
||||
*/
|
||||
void cgroup_cleanup(void)
|
||||
{
|
||||
struct cg *cg, *tmp;
|
||||
char path[256];
|
||||
|
||||
if (!avail)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) {
|
||||
if (cg->active)
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
|
||||
cgroup_del(path);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add, or update, settings for top-level cgroup
|
||||
*/
|
||||
int cgroup_add(char *name, char *cfg, int is_protected)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
if (!name)
|
||||
return -1;
|
||||
if (!cfg)
|
||||
cfg = "";
|
||||
|
||||
cg = cgroup_find(name);
|
||||
if (!cg) {
|
||||
cg = malloc(sizeof(struct cg));
|
||||
if (!cg) {
|
||||
_pe("Failed allocating 'struct cg' for %s", name);
|
||||
return -1;
|
||||
}
|
||||
cg->name = strdup(name);
|
||||
if (!cg->name) {
|
||||
_pe("Failed setting cgroup name %s", name);
|
||||
free(cg);
|
||||
return -1;
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&cgroups, cg, link);
|
||||
} else
|
||||
free(cg->cfg);
|
||||
|
||||
cg->cfg = strdup(cfg);
|
||||
if (!cg->cfg) {
|
||||
_pe("Failed add/update of cgroup %s", name);
|
||||
TAILQ_REMOVE(&cgroups, cg, link);
|
||||
free(cg->name);
|
||||
free(cg);
|
||||
return -1;
|
||||
}
|
||||
cg->is_protected = is_protected;
|
||||
cg->active = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove inactive top-level cgroup
|
||||
*/
|
||||
int cgroup_del(char *dir)
|
||||
{
|
||||
struct cg *cg;
|
||||
char path[256];
|
||||
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
|
||||
if (strcmp(path, dir))
|
||||
continue;
|
||||
|
||||
if (cg->active)
|
||||
return -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (rmdir(dir) && errno != ENOENT) {
|
||||
_d("Failed removing %s: %s", dir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cg) {
|
||||
TAILQ_REMOVE(&cgroups, cg, link);
|
||||
free(cg->name);
|
||||
free(cg->cfg);
|
||||
free(cg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */
|
||||
void cgroup_config(void)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
if (!avail)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||
char path[256];
|
||||
int leaf = 0;
|
||||
|
||||
if (!cg->active)
|
||||
continue;
|
||||
if (!strcmp(cg->name, "init"))
|
||||
leaf = 1; /* reserved */
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, cg->name);
|
||||
group_init(path, leaf, cg->cfg);
|
||||
|
||||
strlcat(path, "/cgroup.events", sizeof(path));
|
||||
iwatch_add(&iw_cgroup, path, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by Finit at early boot to mount initial cgroups
|
||||
*/
|
||||
void cgroup_init(void)
|
||||
void cgroup_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||
char buf[80];
|
||||
FILE *fp;
|
||||
int fd;
|
||||
|
||||
fp = fopen("/proc/cgroups", "r");
|
||||
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
|
||||
if (errno == ENOENT)
|
||||
logit(LOG_NOTICE, "Kernel does not support cgroups v2, disabling.");
|
||||
else
|
||||
_pe("Failed mounting cgroup v2, disabling.");
|
||||
avail = 0;
|
||||
return;
|
||||
}
|
||||
avail = 1;
|
||||
|
||||
/* Find available controllers */
|
||||
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
|
||||
if (!fp) {
|
||||
_d("No cgroup support");
|
||||
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
|
||||
umount(FINIT_CGPATH);
|
||||
avail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
|
||||
_d("Failed mounting cgroups");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Skip first line, header */
|
||||
(void)fgets(buf, sizeof(buf), fp);
|
||||
|
||||
/* Create and mount traditional cgroups v1 hier */
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
char *cgroup;
|
||||
char rc[80];
|
||||
|
||||
cgroup = strtok(buf, "\t ");
|
||||
if (!cgroup)
|
||||
continue;
|
||||
|
||||
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
||||
#if 0
|
||||
if (mkdir(rc, 0755) && EEXIST != errno)
|
||||
continue;
|
||||
|
||||
if (mount("cgroup", rc, "cgroup", opts, cgroup))
|
||||
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
||||
#else
|
||||
symlink("finit", rc);
|
||||
append_ctrl(cgroup);
|
||||
#endif
|
||||
cgroup = strtok(chomp(buf), "\t ");
|
||||
while (cgroup) {
|
||||
append_ctrl(cgroup);
|
||||
cgroup = strtok(NULL, "\t ");
|
||||
}
|
||||
}
|
||||
|
||||
/* Finit default cgroups for process monitoring */
|
||||
if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
|
||||
#if 0
|
||||
strlcpy(controllers, "none,name=finit", sizeof(controllers));
|
||||
#else
|
||||
strlcat(controllers, ",name=finit", sizeof(controllers));
|
||||
#endif
|
||||
if (mount("none", FINIT_CGPATH, "cgroup", opts, controllers)) {
|
||||
_pe("Failed mounting Finit cgroup hierarchy");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set up reaper and enable callbacks */
|
||||
echo(FINIT_CGPATH "/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
||||
echo(FINIT_CGPATH "/notify_on_release", 0, "1");
|
||||
|
||||
/* Default groups, PID 1, services, and user/login processes */
|
||||
if (mkdir(FINIT_CGPATH "/init", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir(FINIT_CGPATH "/system", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Set up basic limits for our groups. Finit optimized defaults
|
||||
* for embedded and server systems include limiting the groups
|
||||
* init and user to a single CPU with 10% share, the system
|
||||
* group gets the rest. Override this from finit.conf
|
||||
*/
|
||||
#if 0
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "0-%d", get_nprocs_conf() - 1);
|
||||
|
||||
group_init(FINIT_CGPATH "/init", "0", 50);
|
||||
group_init(FINIT_CGPATH "/user", "0", 75);
|
||||
group_init(FINIT_CGPATH "/system", buf, 900);
|
||||
#endif
|
||||
|
||||
/* Move ourselves to init */
|
||||
echo(FINIT_CGPATH "/init/cgroup.procs", 0, "1");
|
||||
|
||||
fail:
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int move_pid(char *group, char *name, int pid)
|
||||
{
|
||||
char path[256];
|
||||
/* Enable all controllers */
|
||||
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control"))
|
||||
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||
if (mkdir(path, 0755) && errno != EEXIST)
|
||||
return 1;
|
||||
/* Default (protected) groups, PID 1, services, and user/login processes */
|
||||
cgroup_add("init", "cpu.weight:100", 1);
|
||||
cgroup_add("system", "cpu.weight:9800", 1);
|
||||
cgroup_add("user", "cpu.weight:100", 1);
|
||||
cgroup_config();
|
||||
|
||||
strlcat(path, "/cgroup.procs", sizeof(path));
|
||||
/* Move ourselves to init (best effort, otherwise run in 'root' group */
|
||||
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs"))
|
||||
_pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init");
|
||||
|
||||
return echo(path, 0, "%d", pid);
|
||||
}
|
||||
|
||||
int cgroup_user(char *name, int pid)
|
||||
{
|
||||
return move_pid("finit/user", name, pid);
|
||||
}
|
||||
|
||||
int cgroup_service(char *name, int pid)
|
||||
{
|
||||
if (pid <= 0) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
/* prepare cgroup.events watcher */
|
||||
fd = iwatch_init(&iw_cgroup);
|
||||
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
||||
_pe("Failed setting up cgroup.events watcher");
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return move_pid("finit/system", name, pid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+17
-3
@@ -24,9 +24,23 @@
|
||||
#ifndef FINIT_CGROUP_H_
|
||||
#define FINIT_CGROUP_H_
|
||||
|
||||
void cgroup_init (void);
|
||||
#include <uev/uev.h>
|
||||
|
||||
int cgroup_user (char *name, int pid);
|
||||
int cgroup_service (char *name, int pid);
|
||||
struct cgroup {
|
||||
char name[16];
|
||||
char cfg[128];
|
||||
};
|
||||
|
||||
void cgroup_mark_all(void);
|
||||
void cgroup_cleanup (void);
|
||||
|
||||
int cgroup_add (char *name, char *cfg, int is_protected);
|
||||
int cgroup_del (char *dir);
|
||||
void cgroup_config (void);
|
||||
|
||||
void cgroup_init (uev_ctx_t *ctx);
|
||||
|
||||
int cgroup_user (char *name, int pid);
|
||||
int cgroup_service (char *name, int pid, struct cgroup *cg);
|
||||
|
||||
#endif /* FINIT_CGROUP_H_ */
|
||||
|
||||
+332
-153
@@ -26,12 +26,18 @@
|
||||
#include <err.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <search.h>
|
||||
#include <inttypes.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
|
||||
#include "cgutil.h"
|
||||
#include "initctl.h"
|
||||
|
||||
#define CDIM plain ? "" : "\e[2m"
|
||||
@@ -44,23 +50,15 @@
|
||||
|
||||
uint64_t total_ram; /* From sysinfo() */
|
||||
|
||||
struct cg {
|
||||
struct cg *cg_next;
|
||||
|
||||
char *cg_path; /* path in /sys/fs/cgroup */
|
||||
uint64_t cg_prev; /* cpuacct.usage */
|
||||
uint64_t cg_rss; /* memory.stat */
|
||||
uint64_t cg_vmlib; /* memory.stat */
|
||||
uint64_t cg_vmsize; /* memory.usage_in_bytes */
|
||||
float cg_mem; /* cg_rss / total_ram * 100 */
|
||||
float cg_load; /* curr - prev / 10000000.0 */
|
||||
};
|
||||
|
||||
struct cg dummy; /* empty result "NULL" */
|
||||
struct cg *list;
|
||||
|
||||
int cgroup_avail(void)
|
||||
{
|
||||
return fismnt(FINIT_CGPATH);
|
||||
}
|
||||
|
||||
static char *pid_cmdline(int pid, char *buf, size_t len)
|
||||
char *pid_cmdline(int pid, char *buf, size_t len)
|
||||
{
|
||||
size_t i, sz;
|
||||
char *ptr;
|
||||
@@ -94,40 +92,84 @@ static char *pid_cmdline(int pid, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *pid_comm(int pid, char *buf, size_t len)
|
||||
char *pid_comm(int pid, char *buf, size_t len)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/comm", pid);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
fgets(buf, len, fp);
|
||||
if (fgets(buf, len, fp))
|
||||
ptr = chomp(buf);
|
||||
fclose(fp);
|
||||
|
||||
return chomp(buf);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
char *pid_cgroup(int pid, char *buf, size_t len)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/cgroup", pid);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
if (fgets(buf, len, fp))
|
||||
ptr = chomp(buf);
|
||||
fclose(fp);
|
||||
|
||||
if (ptr)
|
||||
ptr = strchr(buf, '/');
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static char *cgroup_val(char *path, char *file, char *buf, size_t len)
|
||||
{
|
||||
char *val = NULL;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "%s/%s", path, file);
|
||||
if (fp) {
|
||||
if (fgets(buf, len, fp)) {
|
||||
val = chomp(buf);
|
||||
len = strcspn(val, " \t");
|
||||
val[len] = 0;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t cgroup_uint64(char *path, char *file)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
char buf[42];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "%s/%s", path, file);
|
||||
if (!fp)
|
||||
return val;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
if (cgroup_val(path, file, buf, sizeof(buf)))
|
||||
val = strtoull(buf, NULL, 10);
|
||||
fclose(fp);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cgroup_shares(char *path)
|
||||
static char *cgroup_memval(char *path, char *file, char *buf, size_t len)
|
||||
{
|
||||
return (int)cgroup_uint64(path, "cpu.shares");
|
||||
char data[42];
|
||||
|
||||
if (cgroup_val(path, file, data, sizeof(data))) {
|
||||
if (!strcmp(data, "max"))
|
||||
strlcpy(buf, data, len);
|
||||
else
|
||||
memsz(strtoull(data, NULL, 10), buf, len);
|
||||
} else
|
||||
buf[0] = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static uint64_t cgroup_memuse(struct cg *cg)
|
||||
@@ -135,47 +177,62 @@ static uint64_t cgroup_memuse(struct cg *cg)
|
||||
char buf[42];
|
||||
FILE *fp;
|
||||
|
||||
|
||||
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
|
||||
if (fp) {
|
||||
cg->cg_rss = 0;
|
||||
cg->cg_vmlib = 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (!strncmp(buf, "total_rss", 9)) {
|
||||
cg->cg_rss = strtoull(&buf[10], NULL, 10);
|
||||
chomp(buf);
|
||||
|
||||
if (!strncmp(buf, "anon", 4)) {
|
||||
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "total_mapped_file", 17)) {
|
||||
cg->cg_vmlib = strtoull(&buf[18], NULL, 10);
|
||||
break; /* done */
|
||||
if (!strncmp(buf, "slab", 4)) {
|
||||
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "kernel_stack", 12)) {
|
||||
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "pagetables", 10)) {
|
||||
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "percpu", 6)) {
|
||||
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "sock", 4)) {
|
||||
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "file", 4)) {
|
||||
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "file_mapped", 11)) {
|
||||
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
cg->cg_mem = (float)(cg->cg_rss * 100 / total_ram);
|
||||
cg->cg_memshare = (float)(cg->cg_rss * 100 / total_ram);
|
||||
|
||||
/* "fuzz value for efficient access", sort of like total_cache + total_rss */
|
||||
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.usage_in_bytes");
|
||||
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.current");
|
||||
}
|
||||
|
||||
static char *memsz(uint64_t sz, char *buf, size_t len)
|
||||
uint64_t cgroup_memory(char *group)
|
||||
{
|
||||
int gb, mb, kb, b;
|
||||
char path[256];
|
||||
|
||||
gb = sz / (1024 * 1024 * 1024);
|
||||
sz = sz % (1024 * 1024 * 1024);
|
||||
mb = sz / (1024 * 1024);
|
||||
sz = sz % (1024 * 1024);
|
||||
kb = sz / (1024);
|
||||
b = sz % (1024);
|
||||
paste(path, sizeof(path), FINIT_CGPATH, group);
|
||||
|
||||
if (gb)
|
||||
snprintf(buf, len, "%d.%dG", gb, mb / 102);
|
||||
else if (mb)
|
||||
snprintf(buf, len, "%d.%dM", mb, kb / 102);
|
||||
else
|
||||
snprintf(buf, len, "%d.%dk", kb, b / 102);
|
||||
|
||||
return buf;
|
||||
return cgroup_uint64(path, "memory.current");
|
||||
}
|
||||
|
||||
static float cgroup_cpuload(struct cg *cg)
|
||||
@@ -184,23 +241,28 @@ static float cgroup_cpuload(struct cg *cg)
|
||||
char buf[64];
|
||||
FILE *fp;
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", cg->cg_path);
|
||||
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
err(1, "Cannot open %s", fn);
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
uint64_t curr;
|
||||
|
||||
curr = strtoull(buf, NULL, 10);
|
||||
chomp(buf);
|
||||
if (strncmp(buf, "usage_usec", 10))
|
||||
continue;
|
||||
|
||||
curr = strtoull(&buf[11], NULL, 10);
|
||||
if (cg->cg_prev != 0) {
|
||||
uint64_t diff = curr - cg->cg_prev;
|
||||
|
||||
/* this expects 1 sec poll interval */
|
||||
cg->cg_load = (float)(diff / 1000000);
|
||||
cg->cg_load /= 10.0;
|
||||
cg->cg_load *= 100.0;
|
||||
}
|
||||
cg->cg_prev = curr;
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
@@ -214,9 +276,11 @@ static struct cg *append(char *path)
|
||||
char fn[256];
|
||||
ENTRY item;
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", path);
|
||||
snprintf(fn, sizeof(fn), "%s/cpu.stat", path);
|
||||
if (access(fn, F_OK)) {
|
||||
warn("not a cgroup path with cpuacct controller, %s", path);
|
||||
/* older kernels, 4.19, don't have summary cpu.stat in root */
|
||||
if (strcmp(path, FINIT_CGPATH))
|
||||
warn("not a cgroup path with cpu controller, %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -248,7 +312,8 @@ static struct cg *find(char *path)
|
||||
return append(path);
|
||||
}
|
||||
|
||||
static struct cg *cg_update(char *path)
|
||||
/* update stats */
|
||||
struct cg *cg_stats(char *path)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
@@ -262,6 +327,21 @@ static struct cg *cg_update(char *path)
|
||||
return cg;
|
||||
}
|
||||
|
||||
/* query config */
|
||||
struct cg *cg_conf(char *path)
|
||||
{
|
||||
static struct cg cg;
|
||||
|
||||
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
|
||||
cgroup_memval(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
|
||||
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
|
||||
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
||||
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
|
||||
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
|
||||
|
||||
return &cg;
|
||||
}
|
||||
|
||||
static int cgroup_filter(const struct dirent *entry)
|
||||
{
|
||||
/* Skip current dir ".", and prev dir "..", from list of files */
|
||||
@@ -278,11 +358,11 @@ static int cgroup_filter(const struct dirent *entry)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cgroup_tree(char *path, char *pfx, int top)
|
||||
int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
||||
{
|
||||
struct dirent **namelist = NULL;
|
||||
char s[32], r[32], l[32];
|
||||
char row[screen_cols + 9]; /* + control codes */
|
||||
char row[ttcols + 9]; /* + control codes */
|
||||
size_t rlen = sizeof(row) - 1;
|
||||
size_t rplen = rlen - 9;
|
||||
struct stat st;
|
||||
@@ -293,7 +373,7 @@ static int cgroup_tree(char *path, char *pfx, int top)
|
||||
int i, n;
|
||||
int num;
|
||||
|
||||
if (top >= screen_rows)
|
||||
if (pos >= ttrows)
|
||||
return 0;
|
||||
|
||||
if (-1 == lstat(path, &st))
|
||||
@@ -313,60 +393,29 @@ static int cgroup_tree(char *path, char *pfx, int top)
|
||||
|
||||
if (!pfx) {
|
||||
pfx = "";
|
||||
if (top) {
|
||||
cg = cg_update(path);
|
||||
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f [%s]",
|
||||
switch (mode) {
|
||||
case 1:
|
||||
cg = cg_stats(path);
|
||||
snprintf(row, rplen, "\r %6.6s %6.6s %6.6s %5.1f %5.1f %s",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
memsz(cg->cg_rss, r, sizeof(r)),
|
||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||
cg->cg_mem, cg->cg_load, path);
|
||||
} else
|
||||
strlcpy(row, path, rplen);
|
||||
|
||||
puts(row);
|
||||
}
|
||||
|
||||
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
||||
if (n > 0) {
|
||||
for (i = 0; i < n; i++) {
|
||||
char *nm = namelist[i]->d_name;
|
||||
char prefix[80], tmp[42];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
||||
if (top) {
|
||||
cg = cg_update(buf);
|
||||
snprintf(row, rplen,
|
||||
" %6.6s %6.6s %6.6s %5.1f %5.1f ",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
memsz(cg->cg_rss, r, sizeof(r)),
|
||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||
cg->cg_mem, cg->cg_load);
|
||||
} else
|
||||
row[0] = 0;
|
||||
|
||||
strlcat(row, pfx, rplen);
|
||||
if (i + 1 == n) {
|
||||
strlcat(row, END, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s ", pfx);
|
||||
} else {
|
||||
strlcat(row, FORK, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
|
||||
}
|
||||
strlcat(row, " ", rplen);
|
||||
|
||||
strlcat(row, nm, rplen);
|
||||
strlcat(row, "/ ", rplen);
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "[cpu.shares: %d]", cgroup_shares(buf));
|
||||
strlcat(row, tmp, rplen);
|
||||
puts(row);
|
||||
|
||||
rc += cgroup_tree(buf, prefix, top ? top + i : 0);
|
||||
|
||||
free(namelist[i]);
|
||||
cg->cg_memshare, cg->cg_load, path);
|
||||
break;
|
||||
case 2:
|
||||
cg = cg_conf(path);
|
||||
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6s [%-6.6s%6.6s] %s",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
cg->cg_mem.min, cg->cg_mem.max, cg->cg_cpu.set,
|
||||
cg->cg_cpu.weight, cg->cg_cpu.max, path);
|
||||
break;
|
||||
default:
|
||||
strlcpy(row, "\r", rplen);
|
||||
strlcat(row, path, rplen);
|
||||
break;
|
||||
}
|
||||
|
||||
free(namelist);
|
||||
puts(row);
|
||||
}
|
||||
|
||||
if (num > 0) {
|
||||
@@ -384,13 +433,20 @@ static int cgroup_tree(char *path, char *pfx, int top)
|
||||
/* skip kernel threads for now (no cmdline) */
|
||||
pid_comm(pid, comm, sizeof(comm));
|
||||
if (pid_cmdline(pid, buf, sizeof(buf))) {
|
||||
char proc[screen_cols];
|
||||
char proc[ttcols];
|
||||
int len;
|
||||
|
||||
if (top)
|
||||
snprintf(row, rplen, "%37s", " ");
|
||||
else
|
||||
row[0] = 0;
|
||||
switch (mode) {
|
||||
case 1:
|
||||
snprintf(row, rplen, "\r%37s", " ");
|
||||
break;
|
||||
case 2:
|
||||
snprintf(row, rplen, "\r --.-- [ ] [ ] ");
|
||||
break;
|
||||
default:
|
||||
strlcpy(row, "\r", rplen);
|
||||
break;
|
||||
}
|
||||
|
||||
strlcat(row, pfx, rplen);
|
||||
strlcat(row, ++i == num ? END : FORK, rlen);
|
||||
@@ -413,21 +469,173 @@ static int cgroup_tree(char *path, char *pfx, int top)
|
||||
puts(row);
|
||||
}
|
||||
|
||||
if (top) {
|
||||
top += i;
|
||||
if (top >= screen_rows)
|
||||
if (mode == 1) {
|
||||
pos += i;
|
||||
if (pos >= ttrows)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
||||
if (n > 0) {
|
||||
for (i = 0; i < n; i++) {
|
||||
char *nm = namelist[i]->d_name;
|
||||
char prefix[80];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
||||
switch (mode) {
|
||||
case 1:
|
||||
cg = cg_stats(buf);
|
||||
snprintf(row, rplen,
|
||||
"\r %6.6s %6.6s %6.6s %5.1f %5.1f ",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
memsz(cg->cg_rss, r, sizeof(r)),
|
||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||
cg->cg_memshare, cg->cg_load);
|
||||
break;
|
||||
case 2:
|
||||
cg = cg_conf(buf);
|
||||
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6.6s [%-6.6s%6.6s] ",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
cg->cg_mem.min, cg->cg_mem.max,
|
||||
cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max);
|
||||
break;
|
||||
default:
|
||||
strlcpy(row, "\r", rplen);
|
||||
break;
|
||||
}
|
||||
|
||||
strlcat(row, pfx, rplen);
|
||||
if (i + 1 == n) {
|
||||
strlcat(row, END, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s ", pfx);
|
||||
} else {
|
||||
strlcat(row, FORK, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
|
||||
}
|
||||
strlcat(row, " ", rplen);
|
||||
|
||||
strlcat(row, nm, rplen);
|
||||
strlcat(row, "/ ", rplen);
|
||||
|
||||
puts(row);
|
||||
|
||||
rc += cgroup_tree(buf, prefix, mode, pos + i);
|
||||
|
||||
free(namelist[i]);
|
||||
}
|
||||
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int show_cgps(char *arg)
|
||||
{
|
||||
char path[512];
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
else if (arg[0] != '/') {
|
||||
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||
arg = path;
|
||||
}
|
||||
|
||||
return cgroup_tree(arg, NULL, 0, 0);
|
||||
}
|
||||
|
||||
static void cgtop(uev_t *w, void *arg, int events)
|
||||
{
|
||||
(void)w;
|
||||
(void)events;
|
||||
|
||||
fputs("\e[2J\e[1;1H", stdout);
|
||||
if (heading)
|
||||
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
|
||||
cgroup_tree(arg, NULL, 1, 0);
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
ttcooked();
|
||||
showcursor();
|
||||
puts("");
|
||||
}
|
||||
|
||||
static void leave(uev_t *w, void *arg, int events)
|
||||
{
|
||||
(void)arg;
|
||||
(void)events;
|
||||
|
||||
uev_exit(w->ctx);
|
||||
}
|
||||
|
||||
static void key(uev_t *w, void *arg, int events)
|
||||
{
|
||||
char ch;
|
||||
|
||||
(void)arg;
|
||||
(void)events;
|
||||
|
||||
if (read(w->fd, &ch, sizeof(ch)) != -1) {
|
||||
switch (ch) {
|
||||
case 'q':
|
||||
uev_exit(w->ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg("Got char 0x%02x", ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int show_cgtop(char *arg)
|
||||
{
|
||||
struct sysinfo si = { 0 };
|
||||
uev_t timer, input, sigint, sigterm, sigquit;
|
||||
char path[512];
|
||||
uev_ctx_t ctx;
|
||||
int flags;
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
else if (arg[0] != '/') {
|
||||
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||
arg = path;
|
||||
}
|
||||
|
||||
if (!hcreate(ttrows + 25))
|
||||
err(1, "failed creating hash table");
|
||||
|
||||
sysinfo(&si);
|
||||
total_ram = si.totalram * si.mem_unit;
|
||||
|
||||
uev_init(&ctx);
|
||||
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
|
||||
|
||||
if (!ionce && !plain) {
|
||||
atexit(cleanup);
|
||||
ttraw();
|
||||
hidecursor();
|
||||
|
||||
flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||
if (flags != -1)
|
||||
(void)fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
|
||||
(void)uev_io_init(&ctx, &input, key, NULL, STDIN_FILENO, UEV_READ);
|
||||
|
||||
(void)uev_signal_init(&ctx, &sigint, leave, NULL, SIGINT);
|
||||
(void)uev_signal_init(&ctx, &sigterm, leave, NULL, SIGTERM);
|
||||
(void)uev_signal_init(&ctx, &sigquit, leave, NULL, SIGQUIT);
|
||||
}
|
||||
|
||||
return uev_run(&ctx, 0);
|
||||
}
|
||||
|
||||
int show_cgroup(char *arg)
|
||||
{
|
||||
char path[512];
|
||||
@@ -439,40 +647,11 @@ int show_cgroup(char *arg)
|
||||
arg = path;
|
||||
}
|
||||
|
||||
return cgroup_tree(arg, NULL, 0);
|
||||
}
|
||||
/* memory.current memory.min memory.max cpuset.cpus cpu.weight cpu.max */
|
||||
if (heading)
|
||||
print_header(" MEM [MIN MAX] CPU [WEIGHT MAX] GROUP");
|
||||
|
||||
int show_cgtop(char *arg)
|
||||
{
|
||||
struct sysinfo si = { 0 };
|
||||
char path[512];
|
||||
int laps = 0;
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
else if (arg[0] != '/') {
|
||||
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||
arg = path;
|
||||
}
|
||||
|
||||
if (!hcreate(screen_rows + 25))
|
||||
err(1, "failed creating hash table");
|
||||
|
||||
sysinfo(&si);
|
||||
total_ram = si.totalram * si.mem_unit;
|
||||
|
||||
while (1) {
|
||||
fputs("\e[2J\e[1;1H", stdout);
|
||||
if (heading)
|
||||
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
|
||||
cgroup_tree(arg, NULL, 1);
|
||||
|
||||
if (ionce && laps++)
|
||||
break;
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return cgroup_tree(arg, NULL, 2, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+44
-2
@@ -24,8 +24,50 @@
|
||||
#ifndef FINIT_CGUTIL_H_
|
||||
#define FINIT_CGUTIL_H_
|
||||
|
||||
int show_cgroup (char *arg);
|
||||
int show_cgtop (char *arg);
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct cg {
|
||||
struct cg *cg_next;
|
||||
|
||||
/* stats */
|
||||
char *cg_path; /* path in /sys/fs/cgroup */
|
||||
uint64_t cg_prev; /* cpuacct.usage */
|
||||
uint64_t cg_rss; /* memory.stat */
|
||||
uint64_t cg_vmlib; /* memory.stat */
|
||||
uint64_t cg_vmsize; /* memory.current */
|
||||
float cg_memshare; /* cg_rss / total_ram * 100 */
|
||||
float cg_load; /* curr - prev / 10000000.0 */
|
||||
|
||||
/* config */
|
||||
struct {
|
||||
char min[32];
|
||||
char max[32];
|
||||
} cg_mem;
|
||||
|
||||
struct {
|
||||
char set[16];
|
||||
char weight[32];
|
||||
char max[32];
|
||||
} cg_cpu;
|
||||
};
|
||||
|
||||
int cgroup_avail(void);
|
||||
|
||||
char *pid_cmdline (int pid, char *buf, size_t len);
|
||||
char *pid_comm (int pid, char *buf, size_t len);
|
||||
char *pid_cgroup (int pid, char *buf, size_t len);
|
||||
|
||||
uint64_t cgroup_memory(char *group);
|
||||
|
||||
struct cg *cg_stats(char *path);
|
||||
struct cg *cg_conf (char *path);
|
||||
|
||||
int cgroup_tree (char *path, char *pfx, int mode, int pos);
|
||||
|
||||
int show_cgroup (char *arg);
|
||||
int show_cgtop (char *arg);
|
||||
int show_cgps (char *arg);
|
||||
|
||||
#endif /* FINIT_CGUTIL_H_ */
|
||||
|
||||
|
||||
+30
-8
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
@@ -37,7 +38,7 @@ int client_connect(void)
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (-1 == sd)
|
||||
err(1, "Failed creating UNIX domain socket");
|
||||
|
||||
@@ -64,27 +65,48 @@ int client_disconnect(void)
|
||||
|
||||
int client_send(struct init_request *rq, ssize_t len)
|
||||
{
|
||||
struct pollfd pfd = { 0 };
|
||||
int sd, result = 255;
|
||||
int rc;
|
||||
|
||||
sd = client_connect();
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (write(sd, rq, len) != len)
|
||||
goto error;
|
||||
pfd.fd = sd;
|
||||
pfd.events = POLLOUT;
|
||||
if (poll(&pfd, 1, 2000) <= 0) {
|
||||
warn("Timed out waiting for Finit, errno %d", errno);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (read(sd, rq, len) != len)
|
||||
goto error;
|
||||
if (write(sd, rq, len) != len) {
|
||||
warn("Failed communicating with Finit, errno %d", errno);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pfd.fd = sd;
|
||||
pfd.events = POLLIN;
|
||||
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
|
||||
if (rc)
|
||||
warn("poll(), errno %d", errno);
|
||||
else
|
||||
warnx("Timed out waiting for reply from Finit.");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (read(sd, rq, len) != len) {
|
||||
warn("Failed reading reply from Finit, errno %d", errno);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rq->cmd == INIT_CMD_NACK)
|
||||
result = 1;
|
||||
else
|
||||
result = 0;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
exit:
|
||||
client_disconnect();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+90
-29
@@ -23,8 +23,12 @@
|
||||
|
||||
#include <ftw.h>
|
||||
#include <libgen.h>
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
@@ -105,7 +109,8 @@ static void cond_bump_reconf(void)
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
rgen++;
|
||||
|
||||
cond_set_gen(_PATH_RECONF, rgen);
|
||||
if (cond_set_gen(_PATH_RECONF, rgen))
|
||||
_pe("Failed setting %s to gen %d", _PATH_RECONF, rgen);
|
||||
}
|
||||
|
||||
static int cond_checkpath(const char *path)
|
||||
@@ -127,12 +132,12 @@ static int cond_checkpath(const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new)
|
||||
int cond_set_path(const char *path, enum cond_state next)
|
||||
{
|
||||
enum cond_state old;
|
||||
enum cond_state prev;
|
||||
unsigned int rgen;
|
||||
|
||||
_d("%s", path);
|
||||
_d("%s <= %d", path, next);
|
||||
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen) {
|
||||
@@ -140,9 +145,9 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
return -1;
|
||||
}
|
||||
|
||||
old = cond_get_path(path);
|
||||
prev = cond_get_path(path);
|
||||
|
||||
switch (new) {
|
||||
switch (next) {
|
||||
case COND_ON:
|
||||
if (cond_checkpath(path))
|
||||
return 0;
|
||||
@@ -159,61 +164,95 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new != old;
|
||||
return next != prev;
|
||||
}
|
||||
|
||||
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
|
||||
void cond_update(const char *name)
|
||||
/* Should only be used by cond_set*(), cond_clear(), and usr/sys plugins! */
|
||||
int cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
int affects = 0;
|
||||
|
||||
_d("%s", name);
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
||||
continue;
|
||||
|
||||
affects++;
|
||||
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
|
||||
service_step(svc);
|
||||
}
|
||||
|
||||
return affects;
|
||||
}
|
||||
|
||||
int cond_set_noupdate(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return 1;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_ON))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cond_set(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_ON))
|
||||
if (cond_set_noupdate(name))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_set_oneshot(const char *name)
|
||||
int cond_set_oneshot_noupdate(const char *name)
|
||||
{
|
||||
const char *path;
|
||||
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
return 1;
|
||||
|
||||
path = cond_path(name);
|
||||
_d("%s => %s", name, path);
|
||||
|
||||
if (cond_checkpath(path))
|
||||
return 1;
|
||||
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
|
||||
_pe("Failed creating onshot cond %s", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cond_set_oneshot(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (cond_set_oneshot_noupdate(name))
|
||||
return;
|
||||
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||
_pe("Failed creating onshot cond %s", name);
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
int cond_clear_noupdate(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return 1;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cond_clear(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||
if (cond_clear_noupdate(name))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
@@ -226,7 +265,7 @@ void cond_reload(void)
|
||||
cond_bump_reconf();
|
||||
}
|
||||
|
||||
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||
static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw, int set)
|
||||
{
|
||||
char *nm;
|
||||
|
||||
@@ -238,25 +277,47 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
|
||||
nm = strstr((char *)fpath, COND_BASE);
|
||||
if (!nm) {
|
||||
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
||||
_e("Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de");
|
||||
return 1;
|
||||
}
|
||||
|
||||
nm += strlen(COND_BASE);
|
||||
_d("Reasserting %s => %s", fpath, nm);
|
||||
cond_set(nm);
|
||||
_d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
|
||||
if (set)
|
||||
cond_set(nm);
|
||||
else
|
||||
cond_clear_noupdate(nm); /* important, see netlink plugin! */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||
{
|
||||
return do_assert(fpath, sb, tflg, ftw, 1);
|
||||
}
|
||||
|
||||
static int deassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||
{
|
||||
return do_assert(fpath, sb, tflg, ftw, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used only by netlink plugin atm.
|
||||
* type: is a one of pid/, net/, etc.
|
||||
*/
|
||||
void cond_reassert(const char *type)
|
||||
void cond_reassert(const char *pat)
|
||||
{
|
||||
_d("%s", type);
|
||||
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
|
||||
_d("%s", pat);
|
||||
nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used only by netlink plugin atm.
|
||||
*/
|
||||
void cond_deassert(const char *pat)
|
||||
{
|
||||
_d("%s", pat);
|
||||
nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
|
||||
}
|
||||
|
||||
void cond_init(void)
|
||||
|
||||
+6
-2
@@ -21,8 +21,12 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
@@ -52,8 +56,8 @@ const char *cond_path(const char *name)
|
||||
|
||||
unsigned int cond_get_gen(const char *file)
|
||||
{
|
||||
unsigned int gen;
|
||||
char *ptr, path[256];
|
||||
unsigned int gen;
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
|
||||
+14
-2
@@ -2,12 +2,17 @@
|
||||
#define FINIT_COND_H_
|
||||
|
||||
#include <paths.h>
|
||||
#include <svc.h>
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
#define COND_BASE "finit/cond"
|
||||
#define COND_PID "pid/"
|
||||
#define COND_SYS "sys/"
|
||||
#define COND_USR "usr/"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||
#define _PATH_CONDPID _PATH_COND COND_PID
|
||||
#define _PATH_CONDSYS _PATH_COND COND_SYS
|
||||
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||
#define _PATH_RECONF _PATH_COND "reconf"
|
||||
|
||||
@@ -26,13 +31,20 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
void cond_update (const char *name);
|
||||
int cond_update (const char *name);
|
||||
int cond_set_path (const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
void cond_set_oneshot (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
|
||||
int cond_set_noupdate (const char *name);
|
||||
int cond_set_oneshot_noupdate(const char *name);
|
||||
int cond_clear_noupdate(const char *name);
|
||||
|
||||
void cond_reassert (const char *pat);
|
||||
void cond_deassert (const char *pat);
|
||||
|
||||
void cond_init (void);
|
||||
|
||||
#endif /* FINIT_COND_H_ */
|
||||
|
||||
+151
-43
@@ -23,18 +23,25 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/resource.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
# include <libite/queue.h> /* BSD sys/queue.h API */
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
# include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <glob.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "iwatch.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
#include "helpers.h"
|
||||
@@ -50,6 +57,8 @@ int logfile_count_max = 5;
|
||||
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
||||
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
|
||||
|
||||
struct conf_change {
|
||||
TAILQ_ENTRY(conf_change) link;
|
||||
char *name;
|
||||
@@ -63,22 +72,6 @@ static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(c
|
||||
static int parse_conf(char *file, int is_rcsd);
|
||||
static void drop_changes(void);
|
||||
|
||||
static void hide_args(int argc, char *argv[])
|
||||
{
|
||||
/*
|
||||
* Hide command line arguments from ps (in particular for
|
||||
* forked children that don't execv()). This is an ugly
|
||||
* hack that only works on Linux.
|
||||
* https://web.archive.org/web/20110227041321/http://netsplit.com/2007/01/10/hiding-arguments-from-ps/
|
||||
*/
|
||||
if (argc > 1) {
|
||||
char *arg_end;
|
||||
|
||||
arg_end = argv[argc-1] + strlen (argv[argc-1]);
|
||||
*arg_end = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
int get_bool(char *arg, int default_value)
|
||||
{
|
||||
if (!arg)
|
||||
@@ -95,6 +88,7 @@ fallback:
|
||||
/*
|
||||
* finit.debug = [on,off]
|
||||
* finit.show_status = [on,off]
|
||||
* finit.status_style = [old,classic,modern]
|
||||
*/
|
||||
static void parse_finit_opts(char *opt)
|
||||
{
|
||||
@@ -142,24 +136,27 @@ static void parse_arg(char *arg)
|
||||
single = 1;
|
||||
}
|
||||
|
||||
void conf_parse_cmdline(int argc, char *argv[])
|
||||
#ifdef KERNEL_CMDLINE
|
||||
/*
|
||||
* Parse /proc/cmdline to find args for init. Don't use this!
|
||||
*
|
||||
* Instead, rely on the kernel to give Finit its arguments as
|
||||
* regular argc + argv[]. Only use this if the system you run
|
||||
* on has a broken initramfs system that cannot forward args
|
||||
* to Finit properly.
|
||||
*/
|
||||
static void parse_kernel_cmdline(void)
|
||||
{
|
||||
int dbg = 0;
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE], *cmdline, *tok;
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
parse_arg(argv[i]);
|
||||
|
||||
hide_args(argc, argv);
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/cmdline", "r");
|
||||
if (!fp)
|
||||
goto done;
|
||||
return;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
goto done;
|
||||
return;
|
||||
}
|
||||
|
||||
cmdline = chomp(line);
|
||||
@@ -170,9 +167,46 @@ void conf_parse_cmdline(int argc, char *argv[])
|
||||
parse_arg(tok);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
#else
|
||||
#define parse_kernel_cmdline()
|
||||
#endif
|
||||
|
||||
done:
|
||||
log_init(dbg);
|
||||
static void parse_kernel_loglevel(void)
|
||||
{
|
||||
char line[LINE_SIZE], *ptr;
|
||||
FILE *fp;
|
||||
int val;
|
||||
|
||||
fp = fopen("/proc/sys/kernel/printk", "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
ptr = chomp(line);
|
||||
_d("%s", ptr);
|
||||
val = atoi(ptr);
|
||||
if (val >= 7)
|
||||
kerndebug = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kernel gives us all non-kernel options on our cmdline
|
||||
*/
|
||||
void conf_parse_cmdline(int argc, char *argv[])
|
||||
{
|
||||
for (int i = 1; i < argc; i++)
|
||||
parse_arg(argv[i]);
|
||||
|
||||
parse_kernel_cmdline();
|
||||
parse_kernel_loglevel();
|
||||
|
||||
log_init();
|
||||
}
|
||||
|
||||
static int kmod_exists(char *mod)
|
||||
@@ -352,7 +386,7 @@ char *lim2str(struct rlimit *rlim)
|
||||
char tmp[25];
|
||||
static char buf[42];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 0;
|
||||
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
@@ -423,6 +457,29 @@ error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
}
|
||||
|
||||
/* cgroup NAME ctrl.prop:value,ctrl.prop:value ... */
|
||||
static void conf_parse_cgroup(char *line)
|
||||
{
|
||||
char config[strlen(line) + 1];
|
||||
char *ptr, *name;
|
||||
|
||||
name = strtok(line, " \t");
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
if (strstr(name, "..") || strchr(name, '/'))
|
||||
return; /* illegal */
|
||||
|
||||
config[0] = 0;
|
||||
while ((ptr = strtok(NULL, " \t"))) {
|
||||
if (config[0])
|
||||
strlcat(config, ",", sizeof(config));
|
||||
strlcat(config, ptr, sizeof(config));
|
||||
}
|
||||
|
||||
cgroup_add(name, config, 0);
|
||||
}
|
||||
|
||||
static void parse_static(char *line, int is_rcsd)
|
||||
{
|
||||
char cmd[CMD_SIZE];
|
||||
@@ -516,6 +573,26 @@ static void parse_static(char *line, int is_rcsd)
|
||||
cfglevel = 2; /* Fallback */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Periodic check and instability index leveler, seconds
|
||||
*/
|
||||
if (MATCH_CMD(line, "service-interval ", x)) {
|
||||
char *token = strip_line(x);
|
||||
const char *err = NULL;
|
||||
int val;
|
||||
|
||||
/* 0 min to 1 day, should check at least daily */
|
||||
val = strtonum(token, 0, 1440, &err);
|
||||
if (!err) {
|
||||
int disabled = !service_interval;
|
||||
|
||||
service_interval = val * 1000; /* to milliseconds */
|
||||
if (disabled)
|
||||
service_init();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
@@ -552,9 +629,21 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read control group limits */
|
||||
if (MATCH_CMD(line, "cgroup ", x)) {
|
||||
conf_parse_cgroup(x);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set current cgroup for the following services/run/tasks */
|
||||
if (MATCH_CMD(line, "cgroup.", x)) {
|
||||
strlcpy(cgroup_current, x, sizeof(cgroup_current));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Regular or serial TTYs to run getty */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
tty_register(strip_line(x), rlimit, file);
|
||||
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -580,9 +669,11 @@ static int parse_conf(char *file, int is_rcsd)
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
/* Prepare default limits for each service in /etc/finit.d/ */
|
||||
if (is_rcsd)
|
||||
/* Prepare default limits and group for each service in /etc/finit.d/ */
|
||||
if (is_rcsd) {
|
||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||
cgroup_current[0] = 0;
|
||||
}
|
||||
|
||||
_d("*** Parsing %s", file);
|
||||
while (!feof(fp)) {
|
||||
@@ -622,8 +713,8 @@ int conf_reload(void)
|
||||
daylight, timezone, tzname[0], tzname[1]);
|
||||
|
||||
/* Mark and sweep */
|
||||
cgroup_mark_all();
|
||||
svc_mark_dynamic();
|
||||
tty_mark();
|
||||
|
||||
/*
|
||||
* Reset global rlimit to bootstrap values from conf_init().
|
||||
@@ -632,12 +723,12 @@ int conf_reload(void)
|
||||
|
||||
if (rescue) {
|
||||
int rc;
|
||||
char line[80] = "tty [12345] @console noclear nologin";
|
||||
char line[80] = "tty [12345] rescue";
|
||||
|
||||
/* If rescue.conf is missing, fall back to a root shell */
|
||||
rc = parse_conf(RESCUE_CONF, 0);
|
||||
if (rc)
|
||||
tty_register(line, global_rlimit, NULL);
|
||||
service_register(SVC_TYPE_TTY, line, global_rlimit, NULL);
|
||||
|
||||
print(rc, "Entering rescue mode");
|
||||
goto done;
|
||||
@@ -677,7 +768,7 @@ int conf_reload(void)
|
||||
|
||||
/* Check for dangling symlinks */
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
path = rp = realpath(path, NULL);
|
||||
rp = realpath(path, NULL);
|
||||
if (!rp) {
|
||||
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
|
||||
continue;
|
||||
@@ -687,7 +778,7 @@ int conf_reload(void)
|
||||
/* Check that file ends with '.conf' */
|
||||
len = strlen(path);
|
||||
if (len < 6 || strcmp(&path[len - 5], ".conf"))
|
||||
_d("Skipping %s, not a valid .conf ... ", path);
|
||||
_d("Skipping %s, not a Finit .conf file ... ", path);
|
||||
else
|
||||
parse_conf(path, 1);
|
||||
|
||||
@@ -699,7 +790,13 @@ int conf_reload(void)
|
||||
|
||||
/* Mark any reverse deps as chenaged. */
|
||||
service_update_rdeps();
|
||||
|
||||
/* Set up top-level cgroups */
|
||||
cgroup_config();
|
||||
done:
|
||||
/* Remove all unused top-level cgroups */
|
||||
cgroup_cleanup();
|
||||
|
||||
/* Drop record of all .conf changes */
|
||||
drop_changes();
|
||||
|
||||
@@ -792,13 +889,21 @@ int conf_any_change(void)
|
||||
|
||||
int conf_changed(char *file)
|
||||
{
|
||||
int rc = 0;
|
||||
char *rp;
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
if (conf_find(file))
|
||||
return 1;
|
||||
rp = realpath(file, NULL);
|
||||
if (!rp)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
if (conf_find(rp))
|
||||
rc = 1;
|
||||
free(rp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void conf_cb(uev_t *w, void *arg, int events)
|
||||
@@ -880,7 +985,7 @@ int conf_monitor(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare .conf parser and load all .conf files
|
||||
* Prepare .conf parser and load /etc/finit.conf for global settings
|
||||
*/
|
||||
int conf_init(uev_ctx_t *ctx)
|
||||
{
|
||||
@@ -903,6 +1008,9 @@ int conf_init(uev_ctx_t *ctx)
|
||||
/* Initialize global rlimits, e.g. for built-in services */
|
||||
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||
|
||||
/* Read global rlimits and global cgroup setup from /etc/finit.conf */
|
||||
parse_conf(FINIT_CONF, 0);
|
||||
|
||||
/* prepare /etc watcher */
|
||||
fd = iwatch_init(&iw_conf);
|
||||
if (fd < 0)
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
#ifndef FINIT_CONF_H_
|
||||
#define FINIT_CONF_H_
|
||||
|
||||
#include "cgroup.h"
|
||||
#include "svc.h"
|
||||
|
||||
extern int logfile_size_max;
|
||||
extern int logfile_count_max;
|
||||
|
||||
extern struct rlimit global_rlimit[];
|
||||
extern char cgroup_current[];
|
||||
|
||||
int str2rlim(char *str);
|
||||
char *rlim2str(int rlim);
|
||||
|
||||
+68
-106
@@ -26,15 +26,22 @@
|
||||
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <linux/vt.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
@@ -105,8 +112,6 @@ int run(char *cmd)
|
||||
if (0 == pid) {
|
||||
FILE *fp;
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
setsid();
|
||||
|
||||
/* Always redirect stdio for run() */
|
||||
@@ -239,14 +244,8 @@ int exec_runtask(char *cmd, char *args[])
|
||||
|
||||
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct termios term;
|
||||
char name[80];
|
||||
int fd;
|
||||
|
||||
/* Detach from initial controlling TTY and become session leader */
|
||||
vhangup();
|
||||
setsid();
|
||||
int fd, dummy;
|
||||
|
||||
fd = open(tty, O_RDWR);
|
||||
if (fd < 0) {
|
||||
@@ -257,40 +256,18 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
|
||||
/* Set default TERM, run_getty() below allows override */
|
||||
if (ioctl(fd, VT_OPENQRY, &dummy) == -1)
|
||||
setenv("TERM", "vt102", 1); /* likely a serial line */
|
||||
else
|
||||
setenv("TERM", "linux", 1);
|
||||
|
||||
/*
|
||||
* Reset to sane defaults in case of messup from prev. session
|
||||
*/
|
||||
stty(STDIN_FILENO, speed);
|
||||
|
||||
/*
|
||||
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
|
||||
* It is up to the getty process to allow them again.
|
||||
*/
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_lflag &= ~ISIG;
|
||||
term.c_cc[VEOF] = _POSIX_VDISABLE;
|
||||
term.c_cc[VINTR] = _POSIX_VDISABLE;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
|
||||
}
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
|
||||
/*
|
||||
* Ignore a few signals, needed to prevent Ctrl-C at login:
|
||||
* prompt and to prevent QUIT from dumping core.
|
||||
*/
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
|
||||
/* Set configured limits */
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
@@ -308,14 +285,18 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
||||
|
||||
static int activate_console(int noclear, int nowait)
|
||||
{
|
||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||
static const char clr[] = "\r\e[K";
|
||||
static const char cup[] = "\e[A";
|
||||
struct termios orig;
|
||||
int ret = 0;
|
||||
char c;
|
||||
int rc;
|
||||
|
||||
if (nowait || rescue)
|
||||
return 1;
|
||||
|
||||
if (!noclear)
|
||||
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
||||
dprint(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
||||
|
||||
/* Disable ECHO, XON/OFF while waiting for <CR> */
|
||||
if (!tcgetattr(STDIN_FILENO, &orig)) {
|
||||
@@ -328,36 +309,24 @@ static int activate_console(int noclear, int nowait)
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &c);
|
||||
}
|
||||
|
||||
while (!fexist(SYNC_SHUTDOWN)) {
|
||||
char c;
|
||||
static const char clr[] = "\r\e[2K";
|
||||
static const char cup[] = "\e[A";
|
||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, msg, strlen(msg));
|
||||
while ((rc = read(STDIN_FILENO, &c, 1)) > 0 && c != '\r' && c != '\n')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED)) {
|
||||
sleep(5);
|
||||
continue;
|
||||
}
|
||||
/* On any error (likely EINTR), we avoid starting getty */
|
||||
if (rc == -1)
|
||||
rc = 0;
|
||||
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
(void)write(STDERR_FILENO, msg, strlen(msg));
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
(void)write(STDERR_FILENO, cup, strlen(cup));
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
/* Clear msg and move cursor up for next message/login: */
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, cup, strlen(cup));
|
||||
|
||||
/* Restore TTY */
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig) == -1)
|
||||
ret = 0;
|
||||
rc = 0; /* TTY in bad shape, restart */
|
||||
|
||||
return ret;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -379,64 +348,57 @@ static int activate_console(int noclear, int nowait)
|
||||
* since /bin/login usually only disables ECHO until a password line has
|
||||
* been entered. Upon starting the user's $SHELL the ISIG flag is reset
|
||||
*/
|
||||
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
|
||||
pid_t run_getty(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
pid_t pid;
|
||||
int rc = 1;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
speed_t speed;
|
||||
int rc = 1;
|
||||
|
||||
speed = stty_parse_speed(baud);
|
||||
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
|
||||
prepare_tty(tty, speed, "tty", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
rc = getty(tty, speed, term, NULL);
|
||||
|
||||
_exit(rc);
|
||||
/* Dunno speed, tell stty() to not mess with it */
|
||||
prepare_tty(tty, B0, "getty", rlimit);
|
||||
if (activate_console(noclear, nowait)) {
|
||||
logit(LOG_INFO, "Starting getty on %s", tty);
|
||||
rc = execv(cmd, args);
|
||||
}
|
||||
|
||||
return pid;
|
||||
vhangup();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
|
||||
int sh(char *tty)
|
||||
{
|
||||
pid_t pid;
|
||||
char *args[2] = {
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
char *arg0;
|
||||
size_t len;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
int rc = 1;
|
||||
/* The getty process is usually responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
|
||||
/* Dunno speed, tell stty() to not mess with it */
|
||||
logit(LOG_INFO, "Starting external getty on %s, speed %u", tty, B0);
|
||||
prepare_tty(tty, B0, "getty", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
rc = execv(cmd, args);
|
||||
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||
len = strlen(_PATH_BSHELL) + 2;
|
||||
arg0 = malloc(len);
|
||||
if (!arg0)
|
||||
err(1, "Failed allocating memory");
|
||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||
args[0] = arg0;
|
||||
|
||||
vhangup();
|
||||
_exit(rc);
|
||||
}
|
||||
/* Unblock signals inherited from parent */
|
||||
sig_unblock();
|
||||
|
||||
return pid;
|
||||
return execv(_PATH_BSHELL, args);
|
||||
}
|
||||
|
||||
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
pid_t pid;
|
||||
int rc = 1;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
int rc = 1;
|
||||
prepare_tty(tty, B0, "finitsh", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
rc = sh(tty);
|
||||
|
||||
prepare_tty(tty, B0, "finit-sh", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
rc = sh(tty);
|
||||
|
||||
_exit(rc);
|
||||
}
|
||||
|
||||
return pid;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int run_parts(char *dir, char *cmd)
|
||||
|
||||
+260
-106
@@ -36,7 +36,11 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h> /* umask(), mkdir() */
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
@@ -52,6 +56,7 @@
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
#include "schedule.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
int runlevel = 0; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
@@ -59,11 +64,14 @@ int prevlevel = -1;
|
||||
int debug = 0; /* debug mode from kernel cmdline */
|
||||
int rescue = 0; /* rescue mode from kernel cmdline */
|
||||
int single = 0; /* single user mode from kernel cmdline */
|
||||
int bootstrap = 1; /* set while bootrapping (for TTYs) */
|
||||
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *hostname = NULL;
|
||||
char *rcsd = FINIT_RCSD;
|
||||
char *runparts = NULL;
|
||||
char *osheading = NULL;
|
||||
|
||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||
svc_t *wdog = NULL; /* No watchdog by default */
|
||||
@@ -79,7 +87,7 @@ static void banner(void)
|
||||
* similar enabled to start emptying /dev/kmsg, but for
|
||||
* our progress we want to own the console.
|
||||
*/
|
||||
if (!debug)
|
||||
if (!debug && !kerndebug)
|
||||
klogctl(6, NULL, 0);
|
||||
|
||||
/*
|
||||
@@ -88,7 +96,14 @@ static void banner(void)
|
||||
*/
|
||||
plugin_run_hooks(HOOK_BANNER);
|
||||
|
||||
print_banner(INIT_HEADING);
|
||||
#ifdef INIT_OSHEADING
|
||||
osheading = INIT_OSHEADING;
|
||||
if (osheading) {
|
||||
if (!osheading[0])
|
||||
osheading = release_heading();
|
||||
print_banner(osheading);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -107,6 +122,7 @@ static int fsck(int pass)
|
||||
while ((fs = getfsent())) {
|
||||
char cmd[80];
|
||||
struct stat st;
|
||||
int fsck_rc = 0;
|
||||
|
||||
if (fs->fs_passno != pass)
|
||||
continue;
|
||||
@@ -124,8 +140,33 @@ static int fsck(int pass)
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef FSCK_FIX
|
||||
snprintf(cmd, sizeof(cmd), "fsck -yf %s", fs->fs_spec);
|
||||
#else
|
||||
snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec);
|
||||
rc += run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
||||
#endif
|
||||
fsck_rc = run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
||||
/*
|
||||
* "failure" is defined as exiting with a return code of
|
||||
* 2 or larger. A return code of 1 indicates that filesystem
|
||||
* errors were corrected but that the boot may proceed.
|
||||
*/
|
||||
if (fsck_rc > 1) {
|
||||
char *sulogin[] = {
|
||||
_PATH_SULOGIN,
|
||||
"sulogin",
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(sulogin); i++) {
|
||||
if (systemf(sulogin[i]))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
do_shutdown(SHUT_REBOOT);
|
||||
}
|
||||
rc += fsck_rc;
|
||||
}
|
||||
|
||||
endfsent();
|
||||
@@ -135,18 +176,51 @@ static int fsck(int pass)
|
||||
|
||||
static int fsck_all(void)
|
||||
{
|
||||
int pass, rc = 0;
|
||||
int rc = 0;
|
||||
#ifndef FAST_BOOT
|
||||
int pass;
|
||||
|
||||
for (pass = 1; pass < 10; pass++) {
|
||||
rc = fsck(pass);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Wrapper for mount(2), logs any errors to stderr */
|
||||
static void fs_mount(const char *src, const char *tgt, const char *fstype,
|
||||
unsigned long flags, const void *data)
|
||||
{
|
||||
const char *msg = !fstype ? "MS_MOVE" : "mounting";
|
||||
int rc;
|
||||
|
||||
rc = mount(src, tgt, fstype, flags, data);
|
||||
if (rc && errno != EBUSY)
|
||||
_pe("Failed %s %s on %s", msg, src, tgt);
|
||||
}
|
||||
|
||||
#ifndef SYSROOT
|
||||
/* If / is not listed in fstab, or listed as 'ro', leave it alone */
|
||||
static int fs_readonly_root(struct fstab *fs)
|
||||
{
|
||||
char *tok, *str;
|
||||
|
||||
if (!fs)
|
||||
return 1;
|
||||
|
||||
str = fs->fs_mntops;
|
||||
while ((tok = strtok(str, ","))) {
|
||||
if (!strcmp(tok, "ro"))
|
||||
return 1;
|
||||
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fs_remount_root(int fsckerr)
|
||||
{
|
||||
struct fstab *fs;
|
||||
@@ -159,8 +233,7 @@ static void fs_remount_root(int fsckerr)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If / is not listed in fstab, or listed as 'ro', leave it alone */
|
||||
if (!fs || !strcmp(fs->fs_type, "ro"))
|
||||
if (fs_readonly_root(fs))
|
||||
goto out;
|
||||
|
||||
if (fsckerr)
|
||||
@@ -179,12 +252,74 @@ static void fs_remount_root(int fsckerr)
|
||||
* XXX: Untested, in the initramfs age we should
|
||||
* probably use switch_root instead.
|
||||
*/
|
||||
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL))
|
||||
_pe("Failed %s / MS_MOVE");
|
||||
fs_mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
}
|
||||
#endif /* SYSROOT */
|
||||
|
||||
static void fs_mount(void)
|
||||
/*
|
||||
* Opinionated file system setup. Checks for critical mount points and
|
||||
* mounts them as most users expect. All file systems are checked with
|
||||
* /proc/mounts before mounting.
|
||||
*
|
||||
* Embedded systems, and other people who want full control, can set up
|
||||
* their system with /etc/fstab, which is handled before this function
|
||||
* is called. For systems like Debian/Ubuntu, who only have / and swap
|
||||
* in their /etc/fstab, this function does all the magic necessary.
|
||||
*/
|
||||
static void fs_finalize(void)
|
||||
{
|
||||
/*
|
||||
* Some systems rely on us to both create /dev/shm and, to mount
|
||||
* a tmpfs there. Any system with dbus needs shared memory, so
|
||||
* mount it, unless its already mounted, but not if listed in
|
||||
* the /etc/fstab file already.
|
||||
*/
|
||||
if (!fismnt("/dev/shm")) {
|
||||
makedir("/dev/shm", 0777);
|
||||
fs_mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
|
||||
}
|
||||
|
||||
/* Modern systems use /dev/pts */
|
||||
if (!fismnt("/dev/pts")) {
|
||||
char opts[32];
|
||||
int mode;
|
||||
int gid;
|
||||
|
||||
gid = getgroup("tty");
|
||||
if (gid == -1)
|
||||
gid = 0;
|
||||
|
||||
/* 0600 is default on Debian, use 0620 to get mesg y by default */
|
||||
mode = 0620;
|
||||
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
|
||||
|
||||
makedir("/dev/pts", 0755);
|
||||
fs_mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Modern systems use tmpfs for /run. Fallback to /var/run if
|
||||
* /run doesn't exist is handled by the bootmisc plugin. It
|
||||
* also sets up compat symlinks.
|
||||
*
|
||||
* The unconditional mount of /run/lock is for DoS prevention.
|
||||
* To override any of this behavior, add entries to /etc/fstab
|
||||
* for /run (and optionally /run/lock).
|
||||
*/
|
||||
if (fisdir("/run") && !fismnt("/run")) {
|
||||
fs_mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
|
||||
|
||||
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
|
||||
makedir("/run/lock", 1777);
|
||||
fs_mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880");
|
||||
}
|
||||
|
||||
/* Modern systems use tmpfs for /tmp */
|
||||
if (!fismnt("/tmp"))
|
||||
fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
|
||||
}
|
||||
|
||||
static void fs_mount_all(void)
|
||||
{
|
||||
if (!rescue)
|
||||
fs_remount_root(fsck_all());
|
||||
@@ -199,15 +334,18 @@ static void fs_mount(void)
|
||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||
|
||||
run("swapon -ea");
|
||||
|
||||
_d("Finalize, ensure common file systems are available ...");
|
||||
fs_finalize();
|
||||
}
|
||||
|
||||
/*
|
||||
* We need /proc for rs_remount_root() and conf_parse_cmdline(), /dev
|
||||
* for early multi-console, and /sys for the cgroups support. Any
|
||||
* occurrence of these file systems in /etc/fstab will replace these
|
||||
* mounts later in fs_mount()
|
||||
* mounts later in fs_mount_all()
|
||||
*
|
||||
* Ignore any mount errors with EBUSY, kernel likely alread mounted
|
||||
* Ignore any mount errors with EBUSY, kernel likely already mounted
|
||||
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
|
||||
*/
|
||||
static void fs_init(void)
|
||||
@@ -225,57 +363,20 @@ static void fs_init(void)
|
||||
umask(022);
|
||||
|
||||
for (i = 0; i < NELEMS(fs); i++) {
|
||||
int rc;
|
||||
|
||||
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
if (rc && errno != EBUSY)
|
||||
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If everything goes south we can use this to give the operator an
|
||||
* emergency shell to debug the problem -- Finit should not crash!
|
||||
*
|
||||
* Note: Only use this for debugging a new Finit setup, don't use
|
||||
* this in production since it gives a root shell to anyone
|
||||
* if Finit crashes.
|
||||
*
|
||||
* This emergency shell steps in to prevent "Aieee, PID 1 crashed"
|
||||
* messages from the kernel, which usually results in a reboot, so
|
||||
* that the operator instead can debug the problem.
|
||||
*/
|
||||
static void emergency_shell(void)
|
||||
{
|
||||
#ifdef EMERGENCY_SHELL
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid) {
|
||||
while (1) {
|
||||
pid_t id;
|
||||
|
||||
/* Reap 'em (prevents Zombies) */
|
||||
id = waitpid(-1, NULL, WNOHANG);
|
||||
if (id == pid)
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
||||
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
|
||||
|
||||
/*
|
||||
* Become session leader and set controlling TTY
|
||||
* to enable Ctrl-C and job control in shell.
|
||||
* Check if already mounted, we may be running in a
|
||||
* container, or an initramfs ran before us. The
|
||||
* function fismnt() reliles on /proc/mounts being
|
||||
* unique for each chroot/container.
|
||||
*/
|
||||
setsid();
|
||||
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
||||
if (fismnt(fs[i].file))
|
||||
continue;
|
||||
|
||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
||||
fs_mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
}
|
||||
#endif /* EMERGENCY_SHELL */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle bootstrap transition to configured runlevel, start TTYs
|
||||
*
|
||||
@@ -287,20 +388,8 @@ static void emergency_shell(void)
|
||||
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||
* finalize the bootstrap process by calling this function.
|
||||
*/
|
||||
static void finalize(void)
|
||||
static void finalize(void *unused)
|
||||
{
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts) && !rescue)
|
||||
run_parts(runparts, NULL);
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel
|
||||
*/
|
||||
_d("Change to default runlevel, start all services ...");
|
||||
service_runlevel(cfglevel);
|
||||
|
||||
/* Clean up bootstrap-only tasks/services that never started */
|
||||
_d("Clean up all bootstrap-only tasks/services ...");
|
||||
svc_prune_bootstrap();
|
||||
@@ -322,9 +411,9 @@ static void finalize(void)
|
||||
/* Disable progress output at normal runtime */
|
||||
enable_progress(0);
|
||||
|
||||
/* Delayed start of TTYs at bootstrap */
|
||||
_d("Launching all getty services ...");
|
||||
tty_runlevel();
|
||||
/* System bootrapped, launch TTYs et al */
|
||||
bootstrap = 0;
|
||||
service_step_all(SVC_TYPE_RESPAWN);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -333,7 +422,7 @@ static void finalize(void)
|
||||
static void crank_worker(void *unused)
|
||||
{
|
||||
/*
|
||||
* Initalize state machine and start all bootstrap tasks
|
||||
* Initialize state machine and start all bootstrap tasks
|
||||
* NOTE: no network available!
|
||||
*/
|
||||
sm_init(&sm);
|
||||
@@ -342,12 +431,17 @@ static void crank_worker(void *unused)
|
||||
|
||||
/*
|
||||
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||
* allowed to complete their work in [S], or timeout, before we call
|
||||
* finalize(), should not take more than 120 sec.
|
||||
* allowed to complete their work in [S], or timeout, before we switch
|
||||
* to the configured runlevel and call finalize(), should not take more
|
||||
* than 120 sec.
|
||||
*/
|
||||
static void final_worker(void *work)
|
||||
static void bootstrap_worker(void *work)
|
||||
{
|
||||
static int cnt = 120;
|
||||
static struct wq final = {
|
||||
.cb = finalize,
|
||||
.delay = 10
|
||||
};
|
||||
|
||||
_d("Step all services ...");
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
@@ -363,12 +457,36 @@ static void final_worker(void *work)
|
||||
else
|
||||
_d("Timeout, resuming bootstrap.");
|
||||
|
||||
finalize();
|
||||
_d("Starting runlevel change finalize ...");
|
||||
schedule_work(&final);
|
||||
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts) && !rescue)
|
||||
run_parts(runparts, NULL);
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel
|
||||
*/
|
||||
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
|
||||
service_runlevel(cfglevel);
|
||||
}
|
||||
|
||||
static int version(int rc)
|
||||
{
|
||||
puts(PACKAGE_STRING);
|
||||
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
||||
#ifdef PACKAGE_URL
|
||||
printf("Project homepage: %s\n", PACKAGE_URL);
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
printf("Usage: telinit [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
printf("Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
"Options:\n"
|
||||
// " -a Ignored, compat SysV init\n"
|
||||
// " -b Ignored, compat SysV init\n"
|
||||
@@ -386,12 +504,7 @@ static int usage(int rc)
|
||||
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
|
||||
" if modified, same as initctl reload or SIGHUP to PID 1\n"
|
||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||
"\n");
|
||||
|
||||
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
||||
#ifdef PACKAGE_URL
|
||||
printf("Project homepage: %s\n", PACKAGE_URL);
|
||||
#endif
|
||||
"\n", prognm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -404,6 +517,7 @@ static int telinit(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
|
||||
progname(argv[0]);
|
||||
while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) {
|
||||
switch(c) {
|
||||
case 'a': case 'b': case 'e': case 's': case 'z':
|
||||
@@ -413,7 +527,7 @@ static int telinit(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'v': case 'V':
|
||||
return puts("v" VERSION) == EOF;
|
||||
return version(0);
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
@@ -425,13 +539,13 @@ static int telinit(int argc, char *argv[])
|
||||
int req = (int)argv[optind][0];
|
||||
|
||||
if (isdigit(req))
|
||||
return systemf("initctl runlevel %c", req);
|
||||
return systemf("initctl -b runlevel %c", req);
|
||||
|
||||
if (req == 'q' || req == 'Q')
|
||||
return systemf("initctl reload");
|
||||
return systemf("initctl -b reload");
|
||||
|
||||
if (req == 's' || req == 'S')
|
||||
return systemf("initctl runlevel %c", req);
|
||||
return systemf("initctl -b runlevel %c", req);
|
||||
}
|
||||
|
||||
/* XXX: add non-pid1 process monitor here
|
||||
@@ -445,11 +559,12 @@ static int telinit(int argc, char *argv[])
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct wq crank = {
|
||||
.cb = crank_worker
|
||||
struct wq crank_work = {
|
||||
.cb = crank_worker,
|
||||
.delay = 10
|
||||
};
|
||||
struct wq final = {
|
||||
.cb = final_worker,
|
||||
struct wq bootstrap_work = {
|
||||
.cb = bootstrap_worker,
|
||||
.delay = 100
|
||||
};
|
||||
uev_ctx_t loop;
|
||||
@@ -475,7 +590,7 @@ int main(int argc, char *argv[])
|
||||
console_init();
|
||||
|
||||
/*
|
||||
* Initalize event context.
|
||||
* Initialize event context.
|
||||
*/
|
||||
uev_init1(&loop, 1);
|
||||
ctx = &loop;
|
||||
@@ -485,17 +600,30 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
setenv("SHELL", _PATH_BSHELL, 1);
|
||||
setenv("LOGNAME", "root", 1);
|
||||
setenv("USER", "root", 1);
|
||||
|
||||
if (chdir("/"))
|
||||
_pe("Failed cd /");
|
||||
|
||||
/* Allow progress, if enabled */
|
||||
enable_progress(1);
|
||||
|
||||
/*
|
||||
* In case of emergency.
|
||||
*/
|
||||
emergency_shell();
|
||||
if (rescue) {
|
||||
char *sulogin[] = {
|
||||
_PATH_SULOGIN,
|
||||
"sulogin",
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(sulogin); i++) {
|
||||
if (systemf(sulogin[i]))
|
||||
continue;
|
||||
|
||||
rescue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Load plugins early, the first hook is in banner(), so we
|
||||
@@ -506,8 +634,14 @@ int main(int argc, char *argv[])
|
||||
/*
|
||||
* Hello world.
|
||||
*/
|
||||
enable_progress(1); /* Allow progress, if enabled */
|
||||
banner();
|
||||
|
||||
if (osheading)
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel S", osheading);
|
||||
else
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel S");
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
@@ -516,17 +650,18 @@ int main(int argc, char *argv[])
|
||||
/*
|
||||
* Initialize default control groups, if available
|
||||
*/
|
||||
cgroup_init();
|
||||
|
||||
cgroup_init(&loop);
|
||||
|
||||
/* Check and mount filesystems. */
|
||||
fs_mount();
|
||||
fs_mount_all();
|
||||
|
||||
/* Bootstrap conditions, needed for hooks */
|
||||
cond_init();
|
||||
|
||||
/* Emit conditions for early hooks that ran before the
|
||||
* condition system was initialized in case anyone . */
|
||||
/*
|
||||
* Emit conditions for early hooks that ran before the condition
|
||||
* system was initialized in case anyone.
|
||||
*/
|
||||
cond_set_oneshot(plugin_hook_str(HOOK_BANNER));
|
||||
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
|
||||
|
||||
@@ -535,6 +670,20 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
conf_init(&loop);
|
||||
|
||||
/*
|
||||
* Start built-in watchdogd as soon as possible, if enabled
|
||||
*/
|
||||
if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
|
||||
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
|
||||
wdog = svc_find_by_nameid("watchdog", "finit");
|
||||
}
|
||||
|
||||
/*
|
||||
* Start kernel event daemon as soon as possible, if enabled
|
||||
*/
|
||||
if (whichp(FINIT_LIBPATH_ "/keventd"))
|
||||
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init " FINIT_LIBPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
|
||||
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
@@ -551,10 +700,15 @@ int main(int argc, char *argv[])
|
||||
api_init(&loop);
|
||||
|
||||
_d("Starting the big state machine ...");
|
||||
schedule_work(&crank);
|
||||
schedule_work(&crank_work);
|
||||
|
||||
_d("Starting bootstrap finalize timer ...");
|
||||
schedule_work(&final);
|
||||
schedule_work(&bootstrap_work);
|
||||
|
||||
/*
|
||||
* Background service tasks
|
||||
*/
|
||||
service_init();
|
||||
|
||||
/*
|
||||
* Enter main loop to monitor /dev/initctl and services
|
||||
|
||||
+16
-5
@@ -30,10 +30,15 @@
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <uev/uev.h>
|
||||
|
||||
/* just in case */
|
||||
@@ -46,10 +51,13 @@
|
||||
#endif
|
||||
|
||||
#ifndef FINIT_CGPATH
|
||||
#define FINIT_CGPATH "/sys/fs/cgroup/finit"
|
||||
#define FINIT_CGPATH "/sys/fs/cgroup"
|
||||
#endif
|
||||
|
||||
#define LOGIT_PATH FINIT_LIBPATH_ "/logit"
|
||||
/* To use these, include config.h, or define FINIT_LIBPATH_ */
|
||||
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
|
||||
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
|
||||
#define _PATH_GETTY FINIT_LIBPATH_ "/getty"
|
||||
|
||||
#define CMD_SIZE 256
|
||||
#define LINE_SIZE 1024
|
||||
@@ -71,10 +79,10 @@
|
||||
/* Finit extensions over std SysV */
|
||||
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
|
||||
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
|
||||
#define INIT_CMD_START_SVC 10
|
||||
#define INIT_CMD_START_SVC 10 /* START service */
|
||||
#define INIT_CMD_STOP_SVC 11
|
||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
|
||||
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
|
||||
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
|
||||
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
@@ -105,11 +113,14 @@ extern int prevlevel;
|
||||
extern int debug;
|
||||
extern int rescue;
|
||||
extern int single;
|
||||
extern int bootstrap;
|
||||
extern int kerndebug;
|
||||
extern char *rcsd;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
extern char *runparts;
|
||||
extern char *osheading;
|
||||
extern uev_ctx_t *ctx;
|
||||
|
||||
#endif /* FINIT_H_ */
|
||||
|
||||
+90
-78
@@ -21,45 +21,46 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h> /* LOGIN_NAME_MAX */
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <unistd.h> /* sysconf() */
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#ifndef _PATH_LOGIN
|
||||
#define _PATH_LOGIN "/bin/login"
|
||||
#endif
|
||||
|
||||
#define print(s) (void)write(STDOUT_FILENO, s, strlen(s))
|
||||
#ifndef LOGIN_NAME_MIN
|
||||
#define LOGIN_NAME_MIN 64
|
||||
#endif
|
||||
|
||||
static long logname_len = 32; /* useradd(1) limit at 32 chars */
|
||||
|
||||
/*
|
||||
* Read one character from stdin.
|
||||
*/
|
||||
static int readch(char *tty)
|
||||
static int readch(void)
|
||||
{
|
||||
int st;
|
||||
char ch1;
|
||||
int st;
|
||||
|
||||
st = read(STDIN_FILENO, &ch1, 1);
|
||||
if (st == 0) {
|
||||
print("\n");
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (st < 0)
|
||||
errx(1, "getty: %s: read error", tty);
|
||||
if (st <= 0)
|
||||
return -1;
|
||||
|
||||
return ch1 & 0xFF;
|
||||
}
|
||||
@@ -74,31 +75,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
s0 = line;
|
||||
for (s = line; *s != 0; s++) {
|
||||
if (*s == '\\') {
|
||||
(void)write(1, s0, s - s0);
|
||||
if ((s - s0) > 0)
|
||||
dprint(STDOUT_FILENO, s0, s - s0);
|
||||
s0 = s + 2;
|
||||
switch (*++s) {
|
||||
case 'l':
|
||||
print(tty);
|
||||
dprint(STDOUT_FILENO, tty, 0);
|
||||
break;
|
||||
case 'm':
|
||||
print(uts->machine);
|
||||
dprint(STDOUT_FILENO, uts->machine, 0);
|
||||
break;
|
||||
case 'n':
|
||||
print(uts->nodename);
|
||||
dprint(STDOUT_FILENO, uts->nodename, 0);
|
||||
break;
|
||||
#ifdef _GNU_SOURCE
|
||||
case 'o':
|
||||
print(uts->domainname);
|
||||
dprint(STDOUT_FILENO, uts->domainname, 0);
|
||||
break;
|
||||
#endif
|
||||
case 'r':
|
||||
print(uts->release);
|
||||
dprint(STDOUT_FILENO, uts->release, 0);
|
||||
break;
|
||||
case 's':
|
||||
print(uts->sysname);
|
||||
dprint(STDOUT_FILENO, uts->sysname, 0);
|
||||
break;
|
||||
case 'v':
|
||||
print(uts->version);
|
||||
dprint(STDOUT_FILENO, uts->version, 0);
|
||||
break;
|
||||
case 0:
|
||||
goto leave;
|
||||
@@ -109,7 +111,8 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
}
|
||||
|
||||
leave:
|
||||
(void)write(1, s0, s - s0);
|
||||
if ((s - s0) > 0)
|
||||
dprint(STDOUT_FILENO, s0, s - s0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -142,10 +145,10 @@ static void do_issue(char *tty)
|
||||
/*
|
||||
* Handle the process of a GETTY.
|
||||
*/
|
||||
static void do_getty(char *tty, char *name, size_t len)
|
||||
static int get_logname(char *tty, char *name, size_t len)
|
||||
{
|
||||
int ch;
|
||||
char *np;
|
||||
int ch;
|
||||
|
||||
/*
|
||||
* Display prompt.
|
||||
@@ -156,14 +159,9 @@ static void do_getty(char *tty, char *name, size_t len)
|
||||
do_issue(tty);
|
||||
|
||||
np = name;
|
||||
while ((ch = readch(tty)) != '\n') {
|
||||
if (ch == CTRL('U')) {
|
||||
while (np > name) {
|
||||
(void)write(1, "\b \b", 3);
|
||||
np--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
while ((ch = readch()) != '\n') {
|
||||
if (ch < 0)
|
||||
return 1;
|
||||
|
||||
if (np < name + len)
|
||||
*np++ = ch;
|
||||
@@ -175,6 +173,7 @@ static void do_getty(char *tty, char *name, size_t len)
|
||||
}
|
||||
|
||||
name[len - 1] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -186,26 +185,28 @@ static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
cgroup_user(name, getpid());
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
* Failed to exec login, should be impossible. Try a starting a
|
||||
* fallback shell instead.
|
||||
*
|
||||
* Note: Add /etc/securetty handling.
|
||||
* Failed to exec login, should not happen on normal systems.
|
||||
* Try a starting a rescue shell instead.
|
||||
*/
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_BSHELL);
|
||||
if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode))
|
||||
if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode)) {
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_SULOGIN);
|
||||
execl(_PATH_SULOGIN, _PATH_SULOGIN, NULL);
|
||||
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_SULOGIN, _PATH_BSHELL);
|
||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
||||
}
|
||||
|
||||
return 1; /* We shouldn't get here ... */
|
||||
}
|
||||
|
||||
int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
static int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
{
|
||||
const char cln[] = "\r\e[2K\n";
|
||||
char name[30];
|
||||
const char cln[] = "\r\e[K\n";
|
||||
char name[logname_len + 1]; /* +1 for NUL termination */
|
||||
pid_t sid;
|
||||
|
||||
/*
|
||||
* Clean up tty name.
|
||||
@@ -213,59 +214,70 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
if (!strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)))
|
||||
tty += 5;
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(STDIN_FILENO, speed);
|
||||
(void)write(STDERR_FILENO, cln, strlen(cln));
|
||||
|
||||
/* The getty process is responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
if (!user)
|
||||
do_getty(tty, name, sizeof(name));
|
||||
else
|
||||
|
||||
/* Replace "Please press enter ..." with login: */
|
||||
dprint(STDERR_FILENO, cln, strlen(cln));
|
||||
|
||||
restart:
|
||||
stty(STDIN_FILENO, speed);
|
||||
if (!user) {
|
||||
if (get_logname(tty, name, sizeof(name)))
|
||||
goto restart;
|
||||
} else
|
||||
strlcpy(name, user, sizeof(name));
|
||||
|
||||
/* check current session associated with our tty */
|
||||
sid = tcgetsid(0);
|
||||
if (sid < 0 || getpid() != sid) {
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) == -1)
|
||||
err(1, "failed stealing controlling TTY");
|
||||
}
|
||||
|
||||
if (term && term[0])
|
||||
setenv("TERM", term, 1);
|
||||
|
||||
return do_login(name);
|
||||
}
|
||||
|
||||
int sh(char *tty)
|
||||
static int usage(int rc)
|
||||
{
|
||||
struct termios term;
|
||||
char *args[2] = {
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
char *arg0;
|
||||
size_t len;
|
||||
warnx("usage: getty [-h?] tty [speed [term]]");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The getty process is usually responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *tty, *speed = "0", *term = NULL;
|
||||
int c;
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(STDIN_FILENO, B0);
|
||||
|
||||
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||
len = strlen(_PATH_BSHELL) + 2;
|
||||
arg0 = malloc(len);
|
||||
if (!arg0)
|
||||
err(1, "Failed allocating memory");
|
||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||
args[0] = arg0;
|
||||
|
||||
/* Reenable Ctrl-D and Ctrl-C, and ... */
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_lflag |= ISIG;
|
||||
term.c_cc[VEOF] = CEOF;
|
||||
term.c_cc[VINTR] = CINTR;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
|
||||
while ((c = getopt(argc, argv, "h?")) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ... unblock signals in general */
|
||||
sig_unblock();
|
||||
if (optind >= argc)
|
||||
return usage(1);
|
||||
|
||||
return execv(_PATH_BSHELL, args);
|
||||
tty = argv[optind++];
|
||||
if (optind < argc)
|
||||
speed = argv[optind++];
|
||||
if (optind < argc)
|
||||
term = argv[optind++];
|
||||
|
||||
logname_len = sysconf(_SC_LOGIN_NAME_MAX);
|
||||
if (logname_len == -1i)
|
||||
logname_len = LOGIN_NAME_MAX;
|
||||
if (logname_len < LOGIN_NAME_MIN)
|
||||
logname_len = LOGIN_NAME_MIN;
|
||||
|
||||
return getty(tty, atoi(speed), term, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+66
-145
@@ -31,7 +31,11 @@
|
||||
#include <netinet/in.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -68,93 +72,14 @@ static const char *status2[] = STATUS_CLASS;
|
||||
#define CHOOSE(x,y,z) z
|
||||
static const char *color[] = STATUS_CLASS;
|
||||
|
||||
/*
|
||||
* System consoles, can be given multiple times on the kernel cmdline.
|
||||
* We support only three currently. If none are given on the cmdline
|
||||
* we probe /sys/class/tty/console/active and use the first (default).
|
||||
*/
|
||||
#define MAX_CONS 3
|
||||
|
||||
static char *consoles[MAX_CONS];
|
||||
static int fds[MAX_CONS];
|
||||
static int num_cons = 0;
|
||||
|
||||
/*
|
||||
* System default console is always the first
|
||||
*/
|
||||
char *console(void)
|
||||
{
|
||||
return consoles[0];
|
||||
}
|
||||
|
||||
static void add_console(char *cons)
|
||||
{
|
||||
char path[80];
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (num_cons == NELEMS(fds)) {
|
||||
_e("too many consoles, max %d supported", NELEMS(fds));
|
||||
return;
|
||||
}
|
||||
|
||||
/* drop any options */
|
||||
ptr = strchr(cons, ',');
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
if (!strncmp(cons, "/dev/", 5))
|
||||
cons += 5;
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/%s", cons);
|
||||
|
||||
/* check for and skip duplicates */
|
||||
for (i = 0; i < num_cons; i++) {
|
||||
if (string_compare(path, consoles[i]))
|
||||
return;
|
||||
}
|
||||
|
||||
/* this requires we have /dev mounted && a device there */
|
||||
fds[num_cons] = open(path, O_WRONLY);
|
||||
if (fds[num_cons] < 0) {
|
||||
_pe("Failed opening console %s", path);
|
||||
return;
|
||||
}
|
||||
consoles[num_cons] = strdup(path);
|
||||
num_cons++;
|
||||
}
|
||||
|
||||
static void find_active_consoles(void)
|
||||
{
|
||||
char *cons, *ptr, *tok;
|
||||
char buf[512];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/sys/class/tty/console/active", "r");
|
||||
if (!fp) {
|
||||
fallback:
|
||||
_d("Using fallback /dev/console");
|
||||
add_console("console");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = fgets(buf, sizeof(buf), fp);
|
||||
fclose(fp);
|
||||
if (!ptr)
|
||||
goto fallback;
|
||||
|
||||
cons = chomp(ptr);
|
||||
_d("Using these system consoles: %s", cons);
|
||||
while ((tok = strtok(cons, " \t"))) {
|
||||
cons = NULL;
|
||||
add_console(tok);
|
||||
}
|
||||
return _PATH_CONSOLE;
|
||||
}
|
||||
|
||||
void console_init(void)
|
||||
{
|
||||
find_active_consoles();
|
||||
screen_init();
|
||||
ttinit();
|
||||
}
|
||||
|
||||
ssize_t cprintf(const char *fmt, ...)
|
||||
@@ -163,14 +88,12 @@ ssize_t cprintf(const char *fmt, ...)
|
||||
char buf[len < 256 ? 256 : len];
|
||||
size_t size;
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
va_start(ap, fmt);
|
||||
size = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
for (i = 0; i < num_cons; i++)
|
||||
(void)write(fds[i], buf, size);
|
||||
dprint(STDERR_FILENO, buf, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -205,6 +128,49 @@ void show_progress(pstyle_t style)
|
||||
progress_onoff = style;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find PRETTY_NAME in /etc/os-release to use as heading at boot
|
||||
* and on runlevel changes. Fallback: "Finit vX.YY"
|
||||
*/
|
||||
char *release_heading(void)
|
||||
{
|
||||
char *name = NULL;
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/etc/os-release", "r");
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *ptr = &buf[12];
|
||||
|
||||
if (strncmp(buf, "PRETTY_NAME=", 12))
|
||||
continue;
|
||||
|
||||
if (*ptr == '"' || *ptr == '\'') {
|
||||
char *end = &ptr[strlen(ptr) - 1];
|
||||
char q = *ptr;
|
||||
|
||||
while (end > ptr && isspace(*end))
|
||||
*end-- = 0;
|
||||
|
||||
if (*end == q) {
|
||||
*end = 0;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
name = strdup(ptr);
|
||||
break;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!name)
|
||||
name = strdup(PACKAGE_NAME " v" PACKAGE_VERSION);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return screen length of string, not counting escape chars, and
|
||||
* accounting for unicode characters as only one screen byte wide
|
||||
@@ -255,20 +221,20 @@ static char *pad(char *buf, size_t len, char *ch, size_t width)
|
||||
|
||||
void print_banner(const char *heading)
|
||||
{
|
||||
char buf[4 * SCREEN_WIDTH];
|
||||
char buf[4 * ttcols];
|
||||
|
||||
if (progress_style == PROGRESS_SILENT)
|
||||
return;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strlcat(buf, "\r\e[2K", sizeof(buf));
|
||||
buf[0] = 0;
|
||||
strlcat(buf, "\r\e[K", sizeof(buf));
|
||||
|
||||
if (progress_style == PROGRESS_CLASSIC) {
|
||||
strlcat(buf, "\e[1m", sizeof(buf));
|
||||
strlcat(buf, heading, sizeof(buf));
|
||||
pad(buf, sizeof(buf), "=", SCREEN_WIDTH + 10);
|
||||
pad(buf, sizeof(buf), "=", ttcols + 10);
|
||||
} else {
|
||||
size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH;
|
||||
size_t wmax = 80 <= ttcols ? 80 : ttcols;
|
||||
|
||||
/* • • • Foo System ═══════════════════════════════════════════════ */
|
||||
/* ⬤ ⬤ ⬤ Foo System ═══════════════════════════════════════════════ */
|
||||
@@ -331,7 +297,7 @@ static char *status(int rc)
|
||||
|
||||
void printv(const char *fmt, va_list ap)
|
||||
{
|
||||
char buf[SCREEN_WIDTH];
|
||||
char buf[ttcols];
|
||||
size_t len;
|
||||
|
||||
if (!fmt || progress_style == PROGRESS_SILENT)
|
||||
@@ -339,14 +305,14 @@ void printv(const char *fmt, va_list ap)
|
||||
|
||||
delline();
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 0;
|
||||
len = print_timestamp(buf, sizeof(buf));
|
||||
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
|
||||
|
||||
if (progress_style == PROGRESS_CLASSIC)
|
||||
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
|
||||
else
|
||||
cprintf("\r\e[2K%s%s", status(3), buf);
|
||||
cprintf("\r\e[K%s%s", status(3), buf);
|
||||
}
|
||||
|
||||
void print(int rc, const char *fmt, ...)
|
||||
@@ -462,7 +428,8 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
gid = 0;
|
||||
|
||||
rc = makedir(dir, mode);
|
||||
chown(dir, uid, gid);
|
||||
if (chown(dir, uid, gid))
|
||||
_pe("Failed chown(%s, %d, %d)", dir, uid, gid);
|
||||
}
|
||||
|
||||
umask(omask);
|
||||
@@ -498,8 +465,10 @@ void set_hostname(char **hostname)
|
||||
}
|
||||
|
||||
done:
|
||||
if (*hostname)
|
||||
(void)sethostname(*hostname, strlen(*hostname));
|
||||
if (*hostname) {
|
||||
if (sethostname(*hostname, strlen(*hostname)))
|
||||
_pe("Failed sethostnaem(%s)", *hostname);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -537,6 +506,7 @@ void networking(int updown)
|
||||
run("ifdown -a");
|
||||
_exit(0);
|
||||
}
|
||||
cgroup_service("network", pid, NULL);
|
||||
|
||||
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
|
||||
}
|
||||
@@ -594,55 +564,6 @@ void endfsent(void)
|
||||
}
|
||||
#endif /* HAVE_GETFSENT */
|
||||
|
||||
static int hasopt(char *opts, char *opt)
|
||||
{
|
||||
char buf[strlen(opts) + 1];
|
||||
char *ptr;
|
||||
|
||||
memcpy(buf, opts, sizeof(buf));
|
||||
ptr = strtok(buf, ",");
|
||||
while (ptr) {
|
||||
if (!strcmp(ptr, opt))
|
||||
return 1;
|
||||
|
||||
ptr = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ismnt(char *file, char *dir, char *mode)
|
||||
{
|
||||
struct mntent *mnt;
|
||||
int found = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = setmntent(file, "r");
|
||||
if (!fp)
|
||||
return 0; /* Dunno, maybe not */
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(mnt->mnt_dir, dir)) {
|
||||
if (mode) {
|
||||
if (hasopt(mnt->mnt_opts, mode))
|
||||
found = 1;
|
||||
} else
|
||||
found = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Requires /proc to be mounted */
|
||||
int fismnt(char *dir)
|
||||
{
|
||||
return ismnt("/proc/mounts", dir, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
+26
-8
@@ -26,12 +26,17 @@
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <mntent.h>
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#include <stdarg.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include "log.h"
|
||||
|
||||
#define PROGRESS_DEFAULT PROGRESS_MODERN
|
||||
@@ -67,11 +72,10 @@ char *strip_line (char *line);
|
||||
void enable_progress (int onoff);
|
||||
void show_progress (pstyle_t style);
|
||||
|
||||
int getty (char *tty, speed_t speed, char *term, char *user);
|
||||
int sh (char *tty);
|
||||
char *release_heading (void);
|
||||
|
||||
int stty (int fd, speed_t speed);
|
||||
speed_t stty_parse_speed(char *baud);
|
||||
void stty (int fd, speed_t speed);
|
||||
|
||||
void print_banner (const char *heading);
|
||||
void printv (const char *fmt, va_list ap);
|
||||
@@ -94,8 +98,7 @@ int complete (char *cmd, int pid);
|
||||
int run (char *cmd);
|
||||
int run_interactive (char *cmd, char *fmt, ...);
|
||||
int exec_runtask (char *cmd, char *args[]);
|
||||
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
|
||||
@@ -109,8 +112,23 @@ static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ismnt (char *file, char *dir, char *mode);
|
||||
int fismnt (char *dir);
|
||||
static inline int dprint(int fd, const char *s, size_t len)
|
||||
{
|
||||
size_t loop = 3;
|
||||
int rc = -1;
|
||||
|
||||
if (!len)
|
||||
len = strlen(s);
|
||||
|
||||
while (loop--) {
|
||||
rc = write(fd, s, len);
|
||||
if (rc == -1 && errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* FINIT_HELPERS_H_ */
|
||||
|
||||
|
||||
+306
-120
@@ -33,7 +33,11 @@
|
||||
#include <time.h>
|
||||
#include <utmp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "client.h"
|
||||
#include "cond.h"
|
||||
@@ -41,20 +45,27 @@
|
||||
#include "service.h"
|
||||
#include "cgutil.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
struct cmd {
|
||||
char *cmd;
|
||||
struct cmd *ctx;
|
||||
int (*cb)(char *arg);
|
||||
int *cond;
|
||||
};
|
||||
|
||||
int icreate = 0;
|
||||
int iforce = 0;
|
||||
int ionce = 0;
|
||||
int debug = 0;
|
||||
int heading = 1;
|
||||
int verbose = 0;
|
||||
int plain = 0;
|
||||
int quiet = 0;
|
||||
int runlevel = 0;
|
||||
int cgrp = 0;
|
||||
int utmp = 0;
|
||||
|
||||
int iw, pw;
|
||||
|
||||
extern int reboot_main(int argc, char *argv[]);
|
||||
@@ -101,11 +112,11 @@ void print_header(const char *fmt, ...)
|
||||
|
||||
vfprintf(stdout, fmt, ap);
|
||||
fputs("\n", stdout);
|
||||
for (len = 0; len < screen_cols; len++)
|
||||
for (len = 0; len < ttcols; len++)
|
||||
fputc('=', stdout);
|
||||
fputs("\n", stdout);
|
||||
} else {
|
||||
char buf[screen_cols];
|
||||
char buf[ttcols];
|
||||
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
printheader(stdout, buf, 0);
|
||||
@@ -116,21 +127,20 @@ void print_header(const char *fmt, ...)
|
||||
|
||||
static int runlevel_get(int *prevlevel)
|
||||
{
|
||||
int result;
|
||||
struct init_request rq;
|
||||
int rc;
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
rq.cmd = INIT_CMD_GET_RUNLEVEL;
|
||||
rq.magic = INIT_MAGIC;
|
||||
|
||||
result = client_send(&rq, sizeof(rq));
|
||||
if (!result) {
|
||||
result = rq.runlevel;
|
||||
rc = client_send(&rq, sizeof(rq));
|
||||
if (!rc) {
|
||||
rc = rq.runlevel;
|
||||
if (prevlevel)
|
||||
*prevlevel = rq.sleeptime;
|
||||
}
|
||||
|
||||
return result;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int toggle_debug(char *arg)
|
||||
@@ -165,11 +175,11 @@ static int do_runlevel(char *arg)
|
||||
|
||||
if (!arg) {
|
||||
int prevlevel = 0;
|
||||
int runlevel;
|
||||
int currlevel;
|
||||
char prev;
|
||||
|
||||
runlevel = runlevel_get(&prevlevel);
|
||||
if (255 == runlevel) {
|
||||
currlevel = runlevel_get(&prevlevel);
|
||||
if (255 == currlevel) {
|
||||
printf("unknown\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -178,7 +188,7 @@ static int do_runlevel(char *arg)
|
||||
if (prev <= '0' || prev > '9')
|
||||
prev = 'N';
|
||||
|
||||
printf("%c %d\n", prev , runlevel);
|
||||
printf("%c %d\n", prev , currlevel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -192,7 +202,8 @@ static int do_svc(int cmd, char *arg)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = cmd,
|
||||
.cmd = cmd,
|
||||
.data = "",
|
||||
};
|
||||
|
||||
if (arg)
|
||||
@@ -201,8 +212,6 @@ static int do_svc(int cmd, char *arg)
|
||||
return client_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
|
||||
|
||||
/*
|
||||
* This is a wrapper for do_svc() that adds a simple sanity check of
|
||||
* the service(s) provided as argument. If a service does not exist
|
||||
@@ -236,7 +245,35 @@ static int do_startstop(int cmd, char *arg)
|
||||
|
||||
static int do_start (char *arg) { return do_startstop(INIT_CMD_START_SVC, arg); }
|
||||
static int do_stop (char *arg) { return do_startstop(INIT_CMD_STOP_SVC, arg); }
|
||||
static int do_restart(char *arg) { return do_startstop(INIT_CMD_RESTART_SVC, arg); }
|
||||
|
||||
static int do_reload (char *arg)
|
||||
{
|
||||
if (!arg || !arg[0])
|
||||
return do_svc(INIT_CMD_RELOAD, NULL);
|
||||
|
||||
return do_startstop(INIT_CMD_RELOAD_SVC, arg);
|
||||
}
|
||||
|
||||
static int do_restart(char *arg)
|
||||
{
|
||||
size_t retries = 3;
|
||||
svc_t *svc;
|
||||
|
||||
if (do_startstop(INIT_CMD_STOP_SVC, arg))
|
||||
return 1;
|
||||
|
||||
while (retries-- > 0 && (svc = client_svc_find(arg))) {
|
||||
if (!svc_is_running(svc))
|
||||
break;
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (retries == 0)
|
||||
errx(1, "Failed stopping %s (restart)", arg);
|
||||
|
||||
return do_startstop(INIT_CMD_RESTART_SVC, arg);
|
||||
}
|
||||
|
||||
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||
{
|
||||
@@ -317,7 +354,7 @@ static int do_cond_act(char *arg, int creat)
|
||||
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
|
||||
err(1, "Failed asserting condition <%s>", &oneshot[off]);
|
||||
} else {
|
||||
if (erase(oneshot))
|
||||
if (erase(oneshot) && errno != ENOENT)
|
||||
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
|
||||
}
|
||||
|
||||
@@ -331,12 +368,12 @@ static char *svc_cond(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
char *cond, *conds;
|
||||
|
||||
memset(buf, 0, len);
|
||||
buf[0] = 0;
|
||||
|
||||
if (!svc->cond[0])
|
||||
return buf;
|
||||
|
||||
conds = strdup(svc->cond);
|
||||
conds = strdupa(svc->cond);
|
||||
if (!conds)
|
||||
return buf;
|
||||
|
||||
@@ -373,7 +410,6 @@ static char *svc_cond(svc_t *svc, char *buf, size_t len)
|
||||
}
|
||||
|
||||
strlcat(buf, ">", len);
|
||||
free(conds);
|
||||
|
||||
return buf;
|
||||
}
|
||||
@@ -440,15 +476,12 @@ int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
|
||||
|
||||
int utmp_show(char *file)
|
||||
{
|
||||
static int once = 0;
|
||||
struct tm *sectm;
|
||||
struct utmp *ut;
|
||||
time_t sec;
|
||||
|
||||
int pid;
|
||||
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
|
||||
|
||||
if (heading) {
|
||||
static int once = 0;
|
||||
|
||||
print_header("%s%s ", once ? "\n" : "", file);
|
||||
once++;
|
||||
}
|
||||
@@ -456,12 +489,14 @@ int utmp_show(char *file)
|
||||
|
||||
setutent();
|
||||
while ((ut = getutent())) {
|
||||
char user[sizeof(ut->ut_user) + 1];
|
||||
char id[sizeof(ut->ut_id) + 1];
|
||||
struct tm *sectm;
|
||||
char when[80];
|
||||
char addr[64];
|
||||
int pid;
|
||||
|
||||
memset(id, 0, sizeof(id));
|
||||
strlcpy(id, ut->ut_id, sizeof(id));
|
||||
|
||||
memset(user, 0, sizeof(user));
|
||||
strlcpy(user, ut->ut_user, sizeof(user));
|
||||
|
||||
sec = ut->ut_tv.tv_sec;
|
||||
@@ -484,7 +519,7 @@ int utmp_show(char *file)
|
||||
|
||||
static int do_utmp(char *file)
|
||||
{
|
||||
if (!has_utmp())
|
||||
if (!utmp)
|
||||
return 1;
|
||||
|
||||
if (fexist(file))
|
||||
@@ -496,7 +531,12 @@ static int do_utmp(char *file)
|
||||
|
||||
static int show_version(char *arg)
|
||||
{
|
||||
puts("v" PACKAGE_VERSION);
|
||||
puts(PACKAGE_STRING);
|
||||
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
||||
#ifdef PACKAGE_URL
|
||||
printf("Project homepage: %s\n", PACKAGE_URL);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -508,7 +548,7 @@ static int show_version(char *arg)
|
||||
* Pointer to string on the form [2345], may be up to 12 chars long,
|
||||
* plus escape sequences for highlighting current runlevel.
|
||||
*/
|
||||
char *runlevel_string(int runlevel, int levels)
|
||||
char *runlevel_string(int currlevel, int levels)
|
||||
{
|
||||
int i, pos = 1;
|
||||
static char lvl[21];
|
||||
@@ -518,7 +558,7 @@ char *runlevel_string(int runlevel, int levels)
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ISSET(levels, i)) {
|
||||
if (!plain && runlevel == i)
|
||||
if (!plain && currlevel == i)
|
||||
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
|
||||
|
||||
if (i == 0)
|
||||
@@ -526,7 +566,7 @@ char *runlevel_string(int runlevel, int levels)
|
||||
else
|
||||
lvl[pos++] = '0' + i;
|
||||
|
||||
if (!plain && runlevel == i)
|
||||
if (!plain && currlevel == i)
|
||||
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
|
||||
} else {
|
||||
lvl[pos++] = '-';
|
||||
@@ -584,51 +624,156 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *status(svc_t *svc)
|
||||
static char *exit_status(int status, char *buf, size_t len)
|
||||
{
|
||||
static char buf[25];
|
||||
int bold = !plain;
|
||||
int rc, sig;
|
||||
|
||||
rc = WEXITSTATUS(status);
|
||||
sig = WTERMSIG(status);
|
||||
|
||||
if (WIFEXITED(status))
|
||||
snprintf(buf, len, " (code=exited, status=%d%s)", rc, code2str(rc));
|
||||
else if (WIFSIGNALED(status))
|
||||
snprintf(buf, len, " (code=signal, status=%d%s)", sig, sig2str(sig));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *status(svc_t *svc, int full)
|
||||
{
|
||||
static char buf[96];
|
||||
const char *color;
|
||||
char ok[48] = {0};
|
||||
char *s;
|
||||
|
||||
s = svc_status(svc);
|
||||
if (svc->state != SVC_HALTED_STATE)
|
||||
bold = 0;
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
exit_status(svc->status, ok, sizeof(ok));
|
||||
color = "\e[1m";
|
||||
break;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%-8.8s%s",
|
||||
bold ? "\e[1m" : "", s, bold ? "\e[0m" : "");
|
||||
case SVC_RUNNING_STATE:
|
||||
color = "\e[1;32m";
|
||||
break;
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
exit_status(svc->status, ok, sizeof(ok));
|
||||
if (WIFEXITED(svc->status)) {
|
||||
if (WEXITSTATUS(svc->status))
|
||||
color = "\e[1;31m";
|
||||
else
|
||||
color = "\e[1;32m";
|
||||
} else {
|
||||
if (full && WIFSIGNALED(svc->status))
|
||||
color = "\e[1;31m";
|
||||
else
|
||||
color = "\e[1;33m";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
exit_status(svc->status, ok, sizeof(ok));
|
||||
color = "\e[1;33m";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!full || plain)
|
||||
color = NULL;
|
||||
|
||||
if (!full)
|
||||
snprintf(buf, sizeof(buf), "%-8.8s", s);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "%s%s%s%s",
|
||||
color ? color : "", s, ok, color ? "\e[0m" : "");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void show_cgroup_tree(char *group, char *pfx)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
if (!group) {
|
||||
puts("");
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(path, FINIT_CGPATH, sizeof(path));
|
||||
strlcat(path, group, sizeof(path));
|
||||
|
||||
cgroup_tree(path, pfx, 0, 0);
|
||||
}
|
||||
|
||||
static int show_status(char *arg)
|
||||
{
|
||||
char ident[MAX_IDENT_LEN];
|
||||
char buf[512];
|
||||
int num = 0;
|
||||
svc_t *svc;
|
||||
|
||||
runlevel = runlevel_get(NULL);
|
||||
|
||||
if (arg && arg[0]) {
|
||||
while (arg && arg[0]) {
|
||||
long now = jiffies();
|
||||
char uptm[42] = "N/A";
|
||||
char *pidfn = NULL;
|
||||
int exact = 0;
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
if (string_match(ident, arg))
|
||||
num++;
|
||||
if (string_case_compare(ident, arg))
|
||||
exact++;
|
||||
}
|
||||
|
||||
if (num > 1 && !exact)
|
||||
break;
|
||||
|
||||
svc = client_svc_find(arg);
|
||||
if (!svc)
|
||||
return 1;
|
||||
return 255;
|
||||
|
||||
printf("Status : %s\n", status(svc));
|
||||
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
||||
if (quiet)
|
||||
return svc->state != SVC_RUNNING_STATE;
|
||||
|
||||
pidfn = svc->pidfile;
|
||||
if (pidfn[0] == '!')
|
||||
pidfn++;
|
||||
else if (pidfn[0] == 0)
|
||||
pidfn = "none";
|
||||
|
||||
printf(" Status : %s\n", status(svc, 1));
|
||||
printf(" Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
||||
printf("Description : %s\n", svc->desc);
|
||||
printf("Origin : %s\n", svc->file[0] ? svc->file : "built-in");
|
||||
printf(" Origin : %s\n", svc->file[0] ? svc->file : "built-in");
|
||||
printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf)));
|
||||
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
|
||||
printf("Command : %s\n", svc_command(svc, buf, sizeof(buf)));
|
||||
printf("PID file : %s\n", svc->pidfile);
|
||||
printf("PID : %d\n", svc->pid);
|
||||
printf("User : %s\n", svc->username);
|
||||
printf("Group : %s\n", svc->group);
|
||||
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
|
||||
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||
printf(" Command : %s\n", svc_command(svc, buf, sizeof(buf)));
|
||||
printf(" PID file : %s\n", pidfn);
|
||||
printf(" PID : %d\n", svc->pid);
|
||||
printf(" User : %s\n", svc->username);
|
||||
printf(" Group : %s\n", svc->group);
|
||||
printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
|
||||
printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
|
||||
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||
if (cgrp && svc->pid > 1) {
|
||||
char grbuf[128];
|
||||
char path[256];
|
||||
struct cg *cg;
|
||||
char *group;
|
||||
|
||||
group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf));
|
||||
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group);
|
||||
cg = cg_conf(path);
|
||||
|
||||
printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm)));
|
||||
printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n",
|
||||
group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max,
|
||||
cg->cg_mem.min, cg->cg_mem.max);
|
||||
show_cgroup_tree(group, " ");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return do_log(svc->cmd);
|
||||
@@ -651,10 +796,12 @@ static int show_status(char *arg)
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
char *lvls;
|
||||
|
||||
printf("%-*d ", pw, svc->pid);
|
||||
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
printf("%-*s %s ", iw, ident, status(svc));
|
||||
if (num && !string_match(ident, arg))
|
||||
continue;
|
||||
|
||||
printf("%-*d ", pw, svc->pid);
|
||||
printf("%-*s %s ", iw, ident, status(svc, 0));
|
||||
|
||||
lvls = runlevel_string(runlevel, svc->runlevels);
|
||||
if (strchr(lvls, '\e'))
|
||||
@@ -671,6 +818,30 @@ static int show_status(char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_ident(char *arg)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
char ident[MAX_IDENT_LEN];
|
||||
size_t len;
|
||||
char *pos;
|
||||
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
pos = strchr(ident, ':');
|
||||
if (pos)
|
||||
len = pos - ident;
|
||||
else
|
||||
len = strlen(ident);
|
||||
if (arg && arg[0] && strncasecmp(ident, arg, len))
|
||||
continue;
|
||||
|
||||
puts(ident);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transform(char *nm)
|
||||
{
|
||||
char *names[] = {
|
||||
@@ -717,6 +888,7 @@ static int usage(int rc)
|
||||
" -f, --force Ignore missing files and arguments, never prompt\n"
|
||||
" -1, --once Only one lap in commands like 'top'\n"
|
||||
" -p, --plain Use plain table headings, no ctrl chars\n"
|
||||
" -q, --quiet Silent, only return status of command\n"
|
||||
" -t, --no-heading Skip table headings\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
" -h, --help This help text\n"
|
||||
@@ -764,12 +936,18 @@ static int usage(int rc)
|
||||
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
|
||||
" start <NAME>[:ID] Start service by name, with optional ID\n"
|
||||
" stop <NAME>[:ID] Stop/Pause a running service by name\n"
|
||||
" reload <NAME>[:ID] Reload service by name (SIGHUP or restart)\n"
|
||||
" restart <NAME>[:ID] Restart (stop/start) service by name\n"
|
||||
" ident [NAME] Show matching identities for NAME, or all\n"
|
||||
" status <NAME>[:ID] Show service status, by name\n"
|
||||
" status Show status of services, default command\n"
|
||||
"\n"
|
||||
" ps List processes based on cgroups\n"
|
||||
" top Show top-like listing based on cgroups\n"
|
||||
" status Show status of services, default command\n");
|
||||
if (cgrp)
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
" cgroup List cgroup config overview\n"
|
||||
" ps List processes based on cgroups\n"
|
||||
" top Show top-like listing based on cgroups\n");
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" reboot Reboot system\n"
|
||||
@@ -777,18 +955,11 @@ static int usage(int rc)
|
||||
" poweroff Halt and power off system\n"
|
||||
" suspend Suspend system\n");
|
||||
|
||||
if (has_utmp())
|
||||
if (utmp)
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
" utmp show Raw dump of UTMP/WTMP db\n"
|
||||
"\n");
|
||||
else
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
||||
#ifdef PACKAGE_URL
|
||||
printf("Project homepage: %s\n", PACKAGE_URL);
|
||||
#endif
|
||||
" utmp show Raw dump of UTMP/WTMP db\n");
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -797,7 +968,7 @@ static int do_devel(char *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
printf("Screen %dx%d\n", screen_cols, screen_rows);
|
||||
printf("Screen %dx%d\n", ttcols, ttrows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -807,11 +978,22 @@ static int do_help(char *arg)
|
||||
return usage(0);
|
||||
}
|
||||
|
||||
static int cmd_cond(struct cmd *cmd)
|
||||
{
|
||||
if (!cmd || !cmd->cond)
|
||||
return 1;
|
||||
|
||||
return *cmd->cond;
|
||||
}
|
||||
|
||||
static int cmd_parse(int argc, char *argv[], struct cmd *command)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; argc > 0 && command[i].cmd; i++) {
|
||||
if (!cmd_cond(&command[i]))
|
||||
continue;
|
||||
|
||||
if (!string_match(command[i].cmd, argv[0]))
|
||||
continue;
|
||||
|
||||
@@ -836,72 +1018,79 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
|
||||
|
||||
return command[0].cb(NULL); /* default cmd */
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct option long_options[] = {
|
||||
{ "batch", 0, NULL, 'b' },
|
||||
{ "create", 0, NULL, 'c' },
|
||||
{ "debug", 0, NULL, 'd' },
|
||||
{ "force", 0, NULL, 'f' },
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ "once", 0, NULL, '1' },
|
||||
{ "plain", 0, NULL, 'p' },
|
||||
{ "quiet", 0, NULL, 'q' },
|
||||
{ "no-heading", 0, NULL, 't' },
|
||||
{ "verbose", 0, NULL, 'v' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
struct cmd cond[] = {
|
||||
{ "status", NULL, do_cond_show }, /* default cmd */
|
||||
{ "dump", NULL, do_cond_dump },
|
||||
{ "set", NULL, do_cond_set },
|
||||
{ "clr", NULL, do_cond_clr },
|
||||
{ "clear", NULL, do_cond_clr },
|
||||
{ NULL, NULL, NULL }
|
||||
{ "status", NULL, do_cond_show, NULL }, /* default cmd */
|
||||
{ "dump", NULL, do_cond_dump, NULL },
|
||||
{ "set", NULL, do_cond_set, NULL },
|
||||
{ "clr", NULL, do_cond_clr, NULL },
|
||||
{ "clear", NULL, do_cond_clr, NULL },
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
struct cmd command[] = {
|
||||
{ "status", NULL, show_status }, /* default cmd */
|
||||
{ "status", NULL, show_status, NULL }, /* default cmd */
|
||||
{ "ident", NULL, show_ident, NULL },
|
||||
|
||||
{ "debug", NULL, toggle_debug },
|
||||
{ "devel", NULL, do_devel },
|
||||
{ "help", NULL, do_help },
|
||||
{ "version", NULL, show_version },
|
||||
{ "debug", NULL, toggle_debug, NULL },
|
||||
{ "devel", NULL, do_devel, NULL },
|
||||
{ "help", NULL, do_help, NULL },
|
||||
{ "version", NULL, show_version, NULL },
|
||||
|
||||
{ "list", NULL, serv_list },
|
||||
{ "ls", NULL, serv_list },
|
||||
{ "enable", NULL, serv_enable },
|
||||
{ "disable", NULL, serv_disable },
|
||||
{ "touch", NULL, serv_touch },
|
||||
{ "show", NULL, serv_show },
|
||||
{ "edit", NULL, serv_edit },
|
||||
{ "create", NULL, serv_creat },
|
||||
{ "delete", NULL, serv_delete },
|
||||
{ "reload", NULL, do_reload },
|
||||
{ "list", NULL, serv_list, NULL },
|
||||
{ "ls", NULL, serv_list, NULL },
|
||||
{ "enable", NULL, serv_enable, NULL },
|
||||
{ "disable", NULL, serv_disable, NULL },
|
||||
{ "touch", NULL, serv_touch, NULL },
|
||||
{ "show", NULL, serv_show, NULL },
|
||||
{ "edit", NULL, serv_edit, NULL },
|
||||
{ "create", NULL, serv_creat, NULL },
|
||||
{ "delete", NULL, serv_delete, NULL },
|
||||
{ "reload", NULL, do_reload, NULL },
|
||||
|
||||
{ "cond", cond, NULL },
|
||||
{ "cond", cond, NULL, NULL },
|
||||
|
||||
{ "log", NULL, do_log },
|
||||
{ "start", NULL, do_start },
|
||||
{ "stop", NULL, do_stop },
|
||||
{ "restart", NULL, do_restart },
|
||||
{ "log", NULL, do_log, NULL },
|
||||
{ "start", NULL, do_start, NULL },
|
||||
{ "stop", NULL, do_stop, NULL },
|
||||
{ "restart", NULL, do_restart, NULL },
|
||||
|
||||
{ "ps", NULL, show_cgroup },
|
||||
{ "top", NULL, show_cgtop },
|
||||
{ "cgroup", NULL, show_cgroup, &cgrp },
|
||||
{ "ps", NULL, show_cgps, &cgrp },
|
||||
{ "top", NULL, show_cgtop, &cgrp },
|
||||
|
||||
{ "runlevel", NULL, do_runlevel },
|
||||
{ "reboot", NULL, do_reboot },
|
||||
{ "halt", NULL, do_halt },
|
||||
{ "poweroff", NULL, do_poweroff },
|
||||
{ "suspend", NULL, do_suspend },
|
||||
{ "runlevel", NULL, do_runlevel, NULL },
|
||||
{ "reboot", NULL, do_reboot, NULL },
|
||||
{ "halt", NULL, do_halt, NULL },
|
||||
{ "poweroff", NULL, do_poweroff, NULL },
|
||||
{ "suspend", NULL, do_suspend, NULL },
|
||||
|
||||
{ "utmp", NULL, do_utmp },
|
||||
{ NULL, NULL, NULL }
|
||||
{ "utmp", NULL, do_utmp, &utmp },
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
int interactive = 1, c;
|
||||
|
||||
if (transform(progname(argv[0])))
|
||||
return reboot_main(argc, argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "1bcfh?ptv", long_options, NULL)) != EOF) {
|
||||
/* Enable functionality depending on system capabilities */
|
||||
cgrp = cgroup_avail();
|
||||
utmp = has_utmp();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "1bcdfh?pqtv", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case '1':
|
||||
ionce = 1;
|
||||
@@ -915,6 +1104,10 @@ int main(int argc, char *argv[])
|
||||
icreate = 1;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
iforce = 1;
|
||||
break;
|
||||
@@ -927,6 +1120,10 @@ int main(int argc, char *argv[])
|
||||
plain = 1;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
heading = 0;
|
||||
break;
|
||||
@@ -938,18 +1135,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (interactive)
|
||||
screen_init();
|
||||
|
||||
if (!has_utmp()) {
|
||||
/* system w/o utmp support, disable 'utmp' command */
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!string_compare(command[c].cmd, "utmp"))
|
||||
continue;
|
||||
|
||||
command[c].cb = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ttinit();
|
||||
|
||||
return cmd_parse(argc - optind, &argv[optind], command);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user