mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cb91854b0 | ||
|
|
939ae02a6a | ||
|
|
480d29175c | ||
|
|
ea68413512 | ||
|
|
4edfa9859e | ||
|
|
eb6291c943 | ||
|
|
756b7c3fb6 | ||
|
|
263aec292a | ||
|
|
5a946a4ea7 | ||
|
|
c90b4d797b | ||
|
|
6e7a2fb6d5 | ||
|
|
ddedcf77e8 | ||
|
|
924ec2ef80 | ||
|
|
a49f27c191 | ||
|
|
b41b4ce989 | ||
|
|
3d74e93fa2 | ||
|
|
443700a88e | ||
|
|
4701edef4c | ||
|
|
e6da0a22e7 | ||
|
|
7be2bbbc30 | ||
|
|
3e269c632f | ||
|
|
f2507d9fd7 | ||
|
|
e9f403bceb | ||
|
|
c03176c42a | ||
|
|
9707bfb103 | ||
|
|
11430f83c8 | ||
|
|
a22a794f55 | ||
|
|
5556501287 | ||
|
|
ab554568b2 | ||
|
|
b39a4498a3 | ||
|
|
dead2444e2 | ||
|
|
a58002588e | ||
|
|
28e101aaae | ||
|
|
6da8af8136 | ||
|
|
3069e753e1 | ||
|
|
aafd5c1010 | ||
|
|
2f51c8ea0d | ||
|
|
d1130473fc | ||
|
|
b682a5e4fe | ||
|
|
1fc0f36c6c | ||
|
|
5df5b22327 | ||
|
|
4153c7966d | ||
|
|
458d68533f | ||
|
|
63b5db38df | ||
|
|
2135639508 | ||
|
|
9c8cd6c024 | ||
|
|
450f498061 | ||
|
|
5769500afe | ||
|
|
dc61969164 | ||
|
|
6f0d448765 | ||
|
|
0c9b7a67e0 | ||
|
|
5db2fb2c32 | ||
|
|
f4d3ec1a9f | ||
|
|
51befb492c | ||
|
|
7850378e15 | ||
|
|
438d6b4e63 | ||
|
|
712f68f9da | ||
|
|
8a32ec5c92 | ||
|
|
081622a085 | ||
|
|
2526431d8e | ||
|
|
47d8a83668 | ||
|
|
f72f3febf9 | ||
|
|
138546d008 | ||
|
|
cee244d151 | ||
|
|
7af4ad5c24 | ||
|
|
c0d3705ed5 | ||
|
|
5cf2b9a283 | ||
|
|
0e898da6e5 | ||
|
|
d9f13a1e50 | ||
|
|
4fbcd1bbad | ||
|
|
b6c5498f43 | ||
|
|
9a6c68c952 | ||
|
|
e8170e1216 | ||
|
|
059a4c8a86 | ||
|
|
0de9db68a3 | ||
|
|
aeff68b598 | ||
|
|
700c32fd73 | ||
|
|
334d4a708a | ||
|
|
4d472273e1 | ||
|
|
13f1359b3a | ||
|
|
2e5b97c8b2 | ||
|
|
9a962c86c8 | ||
|
|
f954a4cb92 | ||
|
|
feb97bc77c | ||
|
|
e00fda5dc3 | ||
|
|
80191f2fd5 | ||
|
|
42ef3d3cf7 | ||
|
|
1f4acb322a | ||
|
|
c3ba15045b | ||
|
|
8f1d1717f8 | ||
|
|
7703d00e82 | ||
|
|
3bb826b22b | ||
|
|
9ee2be121f | ||
|
|
36c6aa73aa | ||
|
|
13dddb6912 | ||
|
|
bdaf3370d4 | ||
|
|
a24077c4af | ||
|
|
414a49d733 | ||
|
|
be9d54561d | ||
|
|
3ce5a2a965 | ||
|
|
1d03e9aa8b | ||
|
|
5340e47e81 | ||
|
|
98bbf4a1e5 | ||
|
|
d70e2f1ed0 | ||
|
|
ecb5949679 | ||
|
|
e55fd19091 | ||
|
|
19c64e323c | ||
|
|
9354221bdd | ||
|
|
1d7048d3ae | ||
|
|
8251d713e0 | ||
|
|
44d0d87ad3 | ||
|
|
571250a9b9 | ||
|
|
91540fd793 | ||
|
|
2566e00ef0 | ||
|
|
1e5ce38928 | ||
|
|
c679b0a93d | ||
|
|
c4dcf568c8 | ||
|
|
b65773d508 | ||
|
|
e75c1792aa | ||
|
|
0be4838e01 | ||
|
|
b71605c819 | ||
|
|
3bb2eb2a8d | ||
|
|
8ce35771c3 | ||
|
|
67e80ea989 | ||
|
|
f4d8ecfb5b | ||
|
|
2eb3ebb404 | ||
|
|
1e1b3564dd | ||
|
|
7af257cccf | ||
|
|
84ddac65e5 | ||
|
|
62d6b56fd3 | ||
|
|
fa71f97dcf | ||
|
|
ed383eaaa6 | ||
|
|
9cb6a54493 | ||
|
|
f796d59f77 | ||
|
|
97ca450966 | ||
|
|
e0c1c08e68 | ||
|
|
c81b722691 | ||
|
|
11da93b759 | ||
|
|
28a90a793d | ||
|
|
56a89db889 | ||
|
|
a613ee908f | ||
|
|
d3f58b5df0 | ||
|
|
af88b10322 | ||
|
|
966ae9fb0d | ||
|
|
a69cef0a11 | ||
|
|
b396a018c5 | ||
|
|
a4af9ba248 | ||
|
|
6942e38413 | ||
|
|
e03bd093b5 | ||
|
|
d6070a0d10 | ||
|
|
bd4efb59ca | ||
|
|
a90cfd4a75 | ||
|
|
ab5a983bf7 | ||
|
|
f7bacf8b1b | ||
|
|
d363501e21 | ||
|
|
4150fdfe59 | ||
|
|
4d2b97e4cc | ||
|
|
21eda9dd1c | ||
|
|
4f7e726a3e | ||
|
|
74fc16b832 | ||
|
|
7393072120 | ||
|
|
96bb3f5c6c | ||
|
|
c23a867485 | ||
|
|
4f8a5a6531 | ||
|
|
48e1a0b17f | ||
|
|
9c727ed08d | ||
|
|
49cd3a441d | ||
|
|
8b9258aa73 | ||
|
|
82c6e4680f | ||
|
|
a67b2ba9a7 | ||
|
|
a96edf0291 | ||
|
|
c26dded49c | ||
|
|
3bd15a53d6 | ||
|
|
f0a888c0d1 | ||
|
|
b6769356b2 | ||
|
|
5a9cafc2fc | ||
|
|
2e36590bbd | ||
|
|
33e504b928 | ||
|
|
6a4f197cc0 | ||
|
|
4894580e8b | ||
|
|
70997fb4c5 | ||
|
|
e9515971ea | ||
|
|
82dfa002ff | ||
|
|
a664fd7192 | ||
|
|
b62ee0ef6d | ||
|
|
0cfd867816 | ||
|
|
934a4db7d5 | ||
|
|
c9fe9afa61 | ||
|
|
320e91653b | ||
|
|
859d215ca4 | ||
|
|
0a94f3f303 | ||
|
|
e83a26b256 | ||
|
|
2508495b8c | ||
|
|
3ef52d9324 | ||
|
|
05b72281e6 | ||
|
|
a47abfdc16 | ||
|
|
2c9265f8c8 | ||
|
|
56cb6f8613 | ||
|
|
3dd56ed8af | ||
|
|
fddeeb683c | ||
|
|
25835b0edf | ||
|
|
27fcfada66 | ||
|
|
2dd5fb0f77 | ||
|
|
63044a4b81 | ||
|
|
4e74f9783a | ||
|
|
c3c662fe64 | ||
|
|
7c2764790c | ||
|
|
732d500cf5 | ||
|
|
4e1ac726ac | ||
|
|
fba58085c6 | ||
|
|
d1ebb255c6 | ||
|
|
a39ee0b44d | ||
|
|
947d91acd5 | ||
|
|
8c206d52e5 | ||
|
|
0f445dc9f4 | ||
|
|
c71b35e61a | ||
|
|
d20308b7d7 | ||
|
|
e4e54a88d9 | ||
|
|
308b7c5197 | ||
|
|
89772bf6c0 | ||
|
|
077002e647 | ||
|
|
988cdf9878 | ||
|
|
97db2078f5 | ||
|
|
02a110e6c2 | ||
|
|
681ef8de6f | ||
|
|
0147e0e64f | ||
|
|
311c6be9aa | ||
|
|
c22d123ab0 | ||
|
|
96b5991355 | ||
|
|
06bd352863 | ||
|
|
a918f7497a | ||
|
|
8bbae3d414 | ||
|
|
bfd1f36fdd | ||
|
|
6fc702a560 | ||
|
|
40dcc5541b | ||
|
|
5b41c6e327 | ||
|
|
4bd81b4d4c | ||
|
|
c29589c2ca | ||
|
|
73b89430e4 | ||
|
|
2a62fa3d85 | ||
|
|
4542fdb5bb | ||
|
|
525aaf1f66 | ||
|
|
910dac5f3a | ||
|
|
2a5c319084 | ||
|
|
612f4fe288 | ||
|
|
c058c05651 | ||
|
|
0de4578e50 | ||
|
|
b9e94e4695 | ||
|
|
9887003b25 | ||
|
|
f6bfbc86e9 | ||
|
|
4c90b51d80 | ||
|
|
48cfbfe5ed | ||
|
|
f98be8540f | ||
|
|
17c69fef3d | ||
|
|
c586204fc1 | ||
|
|
ff5e3219be | ||
|
|
07c52f1566 | ||
|
|
0e04ffd68f | ||
|
|
8b5435908c | ||
|
|
3a74310aa6 | ||
|
|
180859a429 | ||
|
|
1d92819d92 | ||
|
|
cfa871c548 | ||
|
|
685e0a80a4 | ||
|
|
19847d2997 | ||
|
|
7108b0e789 | ||
|
|
7eac0b0341 | ||
|
|
33ec4e7d19 | ||
|
|
1b78f2b364 | ||
|
|
119363ff75 | ||
|
|
f89b508c01 | ||
|
|
dda8c7c3ac | ||
|
|
e2d9aab70d | ||
|
|
3df154b798 | ||
|
|
3d9cbaf040 | ||
|
|
70c2939596 | ||
|
|
d4ee44e27f | ||
|
|
0498326962 | ||
|
|
c84ababbd4 | ||
|
|
67ea42c87b | ||
|
|
a2ab45cad7 | ||
|
|
f3fcca6150 | ||
|
|
2b65d8f1e9 | ||
|
|
7c2abf69c4 | ||
|
|
457c0a246f | ||
|
|
1a439baf68 | ||
|
|
68ce78b3fe | ||
|
|
72e27e99e5 | ||
|
|
a436382886 | ||
|
|
6bbfc7b1f1 | ||
|
|
006dd12b09 | ||
|
|
d3a0631390 | ||
|
|
9046b6fe2b | ||
|
|
f217794773 | ||
|
|
11e02fabb3 | ||
|
|
8b9bb1c7ce | ||
|
|
2a7abf1db0 | ||
|
|
34b8c5a3ba | ||
|
|
8e056422fc | ||
|
|
94e8b185b4 | ||
|
|
996d3960e1 | ||
|
|
3b19734f63 | ||
|
|
64e7c0cd9e | ||
|
|
4d0c88bd7a | ||
|
|
9e8c601507 | ||
|
|
b26104466a | ||
|
|
bd25bf65ce | ||
|
|
edd3b41adf | ||
|
|
28ca3fcc5c | ||
|
|
3b601d0115 | ||
|
|
1ef9a9f128 | ||
|
|
d612a86c3b | ||
|
|
5d3cd05202 | ||
|
|
726f7ba229 | ||
|
|
61dab8bd75 | ||
|
|
a7e81f45c6 | ||
|
|
be4062f019 | ||
|
|
035f06e137 | ||
|
|
2f91ab5eac | ||
|
|
f6b5b0260b | ||
|
|
bb9bd7e0b0 | ||
|
|
287abd5c7c | ||
|
|
d27f114742 | ||
|
|
505c370898 | ||
|
|
f26009ec3e | ||
|
|
8425bc313b | ||
|
|
f4d3cf2835 | ||
|
|
bd5cf7c9eb | ||
|
|
4709394ae6 | ||
|
|
729002ef82 | ||
|
|
58b075dbf2 | ||
|
|
d679840fa3 | ||
|
|
0e70bdbf6a | ||
|
|
d810ac00bc | ||
|
|
5f74adad06 | ||
|
|
585ac51f45 | ||
|
|
ce36797506 | ||
|
|
a61c2ceb79 | ||
|
|
383339dc4a | ||
|
|
36b46ba134 | ||
|
|
a2d0859bf1 | ||
|
|
d5f542dcca | ||
|
|
fb86042cb3 | ||
|
|
359fb8530e | ||
|
|
5dd1a676ac | ||
|
|
278994781b | ||
|
|
c9f1bff419 | ||
|
|
1f9621cf4b | ||
|
|
9458e4e66b | ||
|
|
ff08038bdc | ||
|
|
ce330e8bf1 | ||
|
|
47d837bf6a | ||
|
|
912a281ee2 | ||
|
|
6e863a7872 | ||
|
|
488e6c2adf | ||
|
|
e177b866a9 | ||
|
|
15dcebd0f1 | ||
|
|
af60d8d3ec | ||
|
|
5a32fdb309 | ||
|
|
19f5cb1d01 | ||
|
|
c5bca5f80a | ||
|
|
d22dea74e3 | ||
|
|
dc547317c9 | ||
|
|
6bf5d98569 | ||
|
|
29dea38f50 | ||
|
|
34567dd244 | ||
|
|
a3584c6ca3 | ||
|
|
165d0633f6 | ||
|
|
9f7ba1ba79 | ||
|
|
03c08d3970 | ||
|
|
37e3be9a0d | ||
|
|
10437887fc | ||
|
|
faf1c6233d | ||
|
|
c36960350c | ||
|
|
51ade57723 | ||
|
|
9d79673cea | ||
|
|
762bf5f031 | ||
|
|
abcb3ceb67 | ||
|
|
0ae5071f94 | ||
|
|
e589c5b269 | ||
|
|
12e341fe73 | ||
|
|
9be3c4cb9a | ||
|
|
433f3ce17e | ||
|
|
2c29ede0da | ||
|
|
7cfbca6d74 | ||
|
|
b30621bb3b | ||
|
|
d6f35903d6 |
@@ -9,9 +9,9 @@ For email questions/requests/alerts there is always the risk of memory
|
|||||||
exhaustion on the part of the maintainer(s).
|
exhaustion on the part of the maintainer(s).
|
||||||
|
|
||||||
If you are unsure of what to do, or how to implement an idea or bugfix,
|
If you are unsure of what to do, or how to implement an idea or bugfix,
|
||||||
open an issue with `"[RFC: Unsure if this is a bug ... ?"`, or similar,
|
open an issue with `"[RFC: Unsure if this is a bug ... ?"`, or use the
|
||||||
so we can discuss it. Talking about the code first is the best way to
|
GitHub discussions forum, so we can discuss it. Talking about the code
|
||||||
get started before submitting a pull request.
|
first is the best way to get started before submitting a pull request.
|
||||||
|
|
||||||
Either way, when sending an email, patch, or pull request, start by
|
Either way, when sending an email, patch, or pull request, start by
|
||||||
stating the version the change is made against, what it does, and why.
|
stating the version the change is made against, what it does, and why.
|
||||||
@@ -73,7 +73,6 @@ It is expected of everyone engaging in the project to, in the words of
|
|||||||
Bill & Ted; [be excellent to each other][conduct].
|
Bill & Ted; [be excellent to each other][conduct].
|
||||||
|
|
||||||
|
|
||||||
[github]: https://github.com/troglobit/finit/
|
|
||||||
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
|
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
|
||||||
[gitbook]: https://git-scm.com/book/ch5-2.html
|
[gitbook]: https://git-scm.com/book/ch5-2.html
|
||||||
[conduct]: CODE-OF-CONDUCT.md
|
[conduct]: CODE-OF-CONDUCT.md
|
||||||
|
|||||||
+17
-20
@@ -15,7 +15,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
# Verify we can build on latest Ubuntu with both gcc and clang
|
# Verify we can build on latest Ubuntu with both gcc and clang
|
||||||
name: ${{ matrix.compiler }}
|
name: ${{ matrix.compiler }}
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
compiler: [gcc, clang]
|
compiler: [gcc, clang]
|
||||||
@@ -27,15 +27,8 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install pkg-config
|
sudo apt-get -y install pkg-config tree jq libuev-dev libite-dev
|
||||||
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
- uses: actions/checkout@v3
|
||||||
wget https://github.com/troglobit/libite/releases/download/v2.5.2/libite-2.5.2.tar.gz
|
|
||||||
tar xf libuev-2.3.2.tar.xz
|
|
||||||
tar xf libite-2.5.2.tar.gz
|
|
||||||
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
|
||||||
(cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
|
||||||
sudo ldconfig
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Static Finit
|
- name: Static Finit
|
||||||
run: |
|
run: |
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
@@ -43,28 +36,32 @@ jobs:
|
|||||||
make V=1
|
make V=1
|
||||||
- name: Regular Finit
|
- name: Regular Finit
|
||||||
run: |
|
run: |
|
||||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
|
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
|
||||||
|
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
|
||||||
|
--with-keventd
|
||||||
make clean
|
make clean
|
||||||
make V=1
|
make V=1
|
||||||
- name: Install to ~/tmp and Inspect
|
- name: Install to /tmp
|
||||||
run: |
|
run: |
|
||||||
DESTDIR=/tmp make install-strip
|
DESTDIR=/tmp make install-strip
|
||||||
tree /tmp
|
tree /tmp || true
|
||||||
|
- name: Check dependencies
|
||||||
|
run: |
|
||||||
ldd /tmp/sbin/finit
|
ldd /tmp/sbin/finit
|
||||||
size /tmp/sbin/finit
|
size /tmp/sbin/finit
|
||||||
ldd /tmp/sbin/initctl
|
ldd /tmp/sbin/initctl
|
||||||
size /tmp/sbin/initctl
|
size /tmp/sbin/initctl
|
||||||
ldd /tmp/sbin/reboot
|
ldd /tmp/sbin/reboot
|
||||||
size /tmp/sbin/reboot
|
size /tmp/sbin/reboot
|
||||||
/tmp/sbin/finit -h
|
- name: Verify starting and showing usage text
|
||||||
/tmp/sbin/initctl -h
|
run: |
|
||||||
|
sudo /tmp/sbin/finit -h
|
||||||
|
sudo /tmp/sbin/initctl -h
|
||||||
- name: Run Unit Tests
|
- name: Run Unit Tests
|
||||||
run: |
|
run: |
|
||||||
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
|
make check || (cat test/test-suite.log; false)
|
||||||
dir=finit-$ver/_build/sub
|
|
||||||
make -j1 distcheck || (cat $dir/test/setup-root.log; cat $dir/test/test-suite.log; false)
|
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: finit-test-${{ matrix.compiler }}
|
name: finit-test-${{ matrix.compiler }}
|
||||||
path: test/*
|
path: test/*.log
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ jobs:
|
|||||||
coverity:
|
coverity:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v3
|
||||||
id: coverity-toolchain-cache
|
id: coverity-toolchain-cache
|
||||||
with:
|
with:
|
||||||
path: cov-analysis-linux64
|
path: cov-analysis-linux64
|
||||||
@@ -35,12 +35,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install pkg-config
|
sudo apt-get -y install pkg-config
|
||||||
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
wget https://github.com/troglobit/libuev/releases/download/v2.4.0/libuev-2.4.0.tar.xz
|
||||||
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
|
wget https://github.com/troglobit/libite/releases/download/v2.5.2/libite-2.5.2.tar.gz
|
||||||
tar xf libuev-2.3.2.tar.xz
|
tar xf libuev-2.4.0.tar.xz
|
||||||
tar xf libite-2.3.1.tar.gz
|
tar xf libite-2.5.2.tar.gz
|
||||||
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
(cd libuev-2.4.0 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||||
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
|
(cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -7,63 +7,34 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Create GitHub release
|
name: Build and upload release tarball
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
outputs:
|
runs-on: ubuntu-latest
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
release_id: ${{ steps.create_release.outputs.id }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
- name: Installing dependencies ...
|
||||||
|
run: |
|
||||||
|
sudo apt-get -y update
|
||||||
|
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
|
||||||
|
- name: Creating Makefiles ...
|
||||||
|
run: |
|
||||||
|
./autogen.sh
|
||||||
|
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
|
||||||
|
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
|
||||||
|
--with-keventd
|
||||||
|
- name: Build release ...
|
||||||
|
run: |
|
||||||
|
make release || (cat test/test-suite.log; false)
|
||||||
|
ls -lF ../
|
||||||
|
mkdir -p artifacts/
|
||||||
|
mv ../*.tar.* artifacts/
|
||||||
- name: Extract ChangeLog entry ...
|
- name: Extract ChangeLog entry ...
|
||||||
# Hack to extract latest entry for body_path below
|
|
||||||
run: |
|
run: |
|
||||||
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|
||||||
|head -n -1 > release.md
|
|head -n -1 > release.md
|
||||||
cat release.md
|
cat release.md
|
||||||
- name: Create release ...
|
- uses: ncipollo/release-action@v1
|
||||||
id: create_release
|
|
||||||
uses: actions/create-release@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
name: Finit v${{ github.ref_name }}
|
||||||
release_name: Finit v${{ github.ref }}
|
bodyFile: "release.md"
|
||||||
body_path: release.md
|
artifacts: "artifacts/*"
|
||||||
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/*
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
name: Weekly Distcheck
|
||||||
|
|
||||||
|
# Verify that `make distcheck` works as intended: all tests pass, and
|
||||||
|
# any new files are distributed in the tarballs
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 0'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
schedule:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
run: ${{ steps.check.outputs.run }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Check for any commits since last week
|
||||||
|
id: schedule
|
||||||
|
run: |
|
||||||
|
commits="$(git log --format=%H --since='1 week ago')"
|
||||||
|
if [ -n "$commits" ]; then
|
||||||
|
echo "run=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "run=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
distcheck:
|
||||||
|
needs: schedule
|
||||||
|
if: ${{ needs.schedule.outputs.run }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get -y update
|
||||||
|
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Base run
|
||||||
|
run: |
|
||||||
|
./autogen.sh
|
||||||
|
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
|
||||||
|
make -j9 V=1
|
||||||
|
- name: Install to /tmp
|
||||||
|
run: |
|
||||||
|
DESTDIR=/tmp make install-strip
|
||||||
|
- name: Check dist tarball
|
||||||
|
run: |
|
||||||
|
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
|
||||||
|
dir=finit-$ver/_build/sub
|
||||||
|
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# GNU Global .conf file, see /etc/gtags/gtags.conf for details.
|
||||||
|
default:\
|
||||||
|
:tc=default@/etc/gtags/gtags.conf:\
|
||||||
|
:tc=common:
|
||||||
|
|
||||||
|
common:\
|
||||||
|
:skip=tags,TAGS,ID,y.tab.c,y.tab.h,gtags.files,cscope.files,cscope.out,cscope.po.out,cscope.in.out,,{arch}/,autom4te.cache/,*.orig,*.rej,*.bak,*~,#*#,*.swp,*.tmp,*_flymake.*,*_flymake,*.o,*.a,*.so,*.lo,*.zip,*.gz,*.bz2,*.xz,*.lzh,*.Z,*.tgz,*.min.js,*min.css,sysroot/:
|
||||||
+401
-1
@@ -4,6 +4,404 @@ Change Log
|
|||||||
All relevant changes are documented in this file.
|
All relevant changes are documented in this file.
|
||||||
|
|
||||||
|
|
||||||
|
[4.5][] - 2023-09-15
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- Refactor `runparts` and `/etc/rc.local` to no longer block the main
|
||||||
|
loop, allowing these scripts to call `initctl` to interact with Finit.
|
||||||
|
Still in a limited way due to them being used at bootstrap, issue #356
|
||||||
|
- Refactor the `run` stanza to no longer block the main loop, issue #362
|
||||||
|
- Allow `sulogin` with a user different from `root`, issue #357
|
||||||
|
- Allow disabling invocation of rescue mode from kernel command line
|
||||||
|
- Add `initctl -f` to force-skip asking Finit for existing services
|
||||||
|
when creating new services during bootstrap, e.g. `/etc/rc.local`
|
||||||
|
- `initctl runlevel` now returns `N S` instead of `N 10` in bootstrap
|
||||||
|
- Improved logging on failure to `execvp()` a run/task/service, now
|
||||||
|
with `errno`, e.g., "No such file or directory" when the command
|
||||||
|
is missing from `$PATH`
|
||||||
|
- Add support for Bash completion to `initctl`, issue #360
|
||||||
|
- Handle absolute path to `initctl [enable|disable]`, not supported
|
||||||
|
- Update `finit.conf(5)` man page with the recommended directory
|
||||||
|
hierarchy in `/etc/finit.d/`
|
||||||
|
- The `runparts` code has been split into `/libexec/finit/runparts`
|
||||||
|
- Add SysV Init Compatibility section to documentation
|
||||||
|
- Increased MAX path for commands, and arguments: 64 -> 256
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fix #227: believed to have been fixed in [4.3][], the root cause was
|
||||||
|
actually that Finit was waiting for a process that was no longer in
|
||||||
|
the system. The fix is to ask the kernel on process-stop-timeout and
|
||||||
|
replay the lost PID so that Finit can continue with reboot/shutdown
|
||||||
|
- Fix #358: fix inotify events for `/etc/finit.conf`, improved log
|
||||||
|
messages and error handling
|
||||||
|
- Fix #361: cgroup move fail if run/task/services start as non-root.
|
||||||
|
Regression in the [4.4][] release cycle while adding support for the
|
||||||
|
pre:/post:/ready: scripts. Now the latter scripts also properly run
|
||||||
|
in their correct cgroup
|
||||||
|
- The `runparts` executor now skips backup files (`foo~`)
|
||||||
|
- The `runparts` stanza now properly appends ` start` to scripts that
|
||||||
|
start with `S[0-9]+`. This has been broken for a very long time.
|
||||||
|
|
||||||
|
|
||||||
|
[4.4][] - 2023-05-15
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
> **Note 1:** this release contains changes to the `.conf` parser. If you
|
||||||
|
> have .conf file statements with comment character (`#`) in the command
|
||||||
|
> options or description, you must now escape them (`\#`). Issue #186
|
||||||
|
>
|
||||||
|
> **Note 2:** prior to this release, runlevel `S` and `0` were after
|
||||||
|
> boot treated as the same runlevel. This caused `task [06] ...` to
|
||||||
|
> also run at bootstrap instead of just at shutdown and reboot. The
|
||||||
|
> changes made to Finit to separate `S` from `0` require you to update
|
||||||
|
> the allowed runlevels for services that are allowed to continue
|
||||||
|
> running at shutdown. I.e., change `[S123456789]` to `[S0123456789]`
|
||||||
|
> for, e.g., `sysklogd`. Issue #352
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
* Add limited tmpfiles.d(5) support
|
||||||
|
|
||||||
|
This change adds very basic `tmpfiles.d/` support to Finit. Most of
|
||||||
|
the basic types are supported, but not all, so for now, please check
|
||||||
|
the code and examples for details on what is working.
|
||||||
|
|
||||||
|
* If a run/task/service command does not exist, skip registering it
|
||||||
|
|
||||||
|
This changes the semantics of Finit a bit by checking for the command
|
||||||
|
to run when registering it, skipping commands that cannot be found in
|
||||||
|
the absolute path provided in the command, or in `$PATH`
|
||||||
|
|
||||||
|
This change includes a new `nowarn` flag that can be used to prevent
|
||||||
|
Finit from warning for missing commands. See below for an example.
|
||||||
|
|
||||||
|
* Add run/task/service support for `conflict:foo` handling
|
||||||
|
* Add run/task/service support for `if:[!]ident` and `if:<[!]cond>`
|
||||||
|
|
||||||
|
Conditional loading of stanza depending on ident is already loaded (or
|
||||||
|
not), or condition satisfied (or not). E.g., do not run `mdev` if we
|
||||||
|
found and registered `udevd`, or load service only if `<boot/testing>`
|
||||||
|
condition is set.
|
||||||
|
|
||||||
|
The optional leading `!` negates the comparison, if NOT foo then ...
|
||||||
|
|
||||||
|
* Add support for static services in `/lib/finit/system/*.conf`
|
||||||
|
|
||||||
|
Slowly migrating away from hard-coded services in plugins. This way
|
||||||
|
it's possible for the user to both inspect and override as needed.
|
||||||
|
|
||||||
|
* Migrate hotplug plugin to a conditional `/lib/finit/system/hotplug.conf`
|
||||||
|
|
||||||
|
This is the first example of the just minted advanced stanza syntax with
|
||||||
|
`if:`-statements, `conflict:` handling, and `nowarn` flags.
|
||||||
|
|
||||||
|
* Initial support for template services, `foo@.conf`, similar to systemd
|
||||||
|
|
||||||
|
$ initctl show avahi-autoipd@
|
||||||
|
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
|
||||||
|
|
||||||
|
To enable ZeroConf for, e.g., `eth0`, use
|
||||||
|
|
||||||
|
$ initctl enable avahi-autoipd@eth0.conf
|
||||||
|
|
||||||
|
The enabled symlink will be set up to `avahi-autoipd@.conf` and every
|
||||||
|
instance of `%i` will in the instantiated directive be replaced with
|
||||||
|
`eth0`. Inspect the result with:
|
||||||
|
|
||||||
|
$ initctl status avahi-autoipd:eth0
|
||||||
|
|
||||||
|
* Add `devmon`, a `<dev/foo>` condition provider, issue #185
|
||||||
|
* Support for line continuation character `\` in .conf files, issue #186
|
||||||
|
|
||||||
|
service name:sysklogd [S123456789] \
|
||||||
|
env:-/etc/default/sysklogd \
|
||||||
|
syslogd -F $SYSLOGD_ARGS \
|
||||||
|
-- System log daemon
|
||||||
|
|
||||||
|
* `HOOK_BASEFS_UP` has been moved! External plugins that need to call
|
||||||
|
`service_register()`, please use `HOOK_SVC_PLUGIN` from now on.
|
||||||
|
Apologies for any inconveniences this might cause!
|
||||||
|
* getty: add support for `/etc/os-release` to replace `uname` output
|
||||||
|
This change, which has a fallback to `/usr/lib/os-release`, overrides
|
||||||
|
traditional modifiers with the os-release variant. These values were
|
||||||
|
taken from `uname`, which on Linux systems are pretty useless since
|
||||||
|
they always return the kernel name and version instead of the
|
||||||
|
distro/OS values.
|
||||||
|
|
||||||
|
E.g., \s becomes `PRETTY_NAME` instead of 'Linux' and \v becomes the
|
||||||
|
pretty `VERSION`, while \r becomes `VERSION_ID`.
|
||||||
|
* Support for overriding `/etc/finit.conf` and `/etc/finit.d` issue #235
|
||||||
|
New (kernel) command line option `finit.config=PATH` which can be used
|
||||||
|
to redirect Finit to start up with, e.g., `/etc/factory.conf` instead of
|
||||||
|
`/etc/finit.conf`.
|
||||||
|
|
||||||
|
For the complete experience a new top-level configuration file directive
|
||||||
|
`rcsd PATH` has aslo been added. It in turn can be used by `factory.conf`
|
||||||
|
as follows to override `/etc/finit.d`:
|
||||||
|
|
||||||
|
rcsd /etc/factory.d
|
||||||
|
|
||||||
|
* Support for overriding `/etc/finit.d` from the alternate `finit.conf`
|
||||||
|
with a new `rcsd /path/to/dot.d/` .conf file directive
|
||||||
|
* Support for `fsck_mode=[auto,skip,force]` + `fsck_repair=[preen,no,yes]`
|
||||||
|
* Add `set` keyword for environment variables set in `/etc/finit.conf`
|
||||||
|
* Support `finit.cond=foo` cmdline `<boot/foo>` conditions, issue #250
|
||||||
|
* `initctl` JSON output support for status and conditions, issue #273
|
||||||
|
Example:
|
||||||
|
|
||||||
|
root@infix:~$ initctl status -j resolvconf
|
||||||
|
{
|
||||||
|
"identity": "resolvconf",
|
||||||
|
"description": "Update DNS configuration",
|
||||||
|
"type": "task",
|
||||||
|
"forking": false,
|
||||||
|
"status": "done",
|
||||||
|
"exit": { "code": 0 },
|
||||||
|
"origin": "/etc/finit.d/enabled/sysrepo.conf",
|
||||||
|
"command": "resolvconf -u",
|
||||||
|
"restarts": 0,
|
||||||
|
"pidfile": "none",
|
||||||
|
"pid": 0,
|
||||||
|
"user": "root",
|
||||||
|
"group": "root",
|
||||||
|
"uptime": 0,
|
||||||
|
"runlevels": [ 1, 2, 3, 4, 5, 7, 8, 9 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
The excellent tool `jq` can be used to extract parts of the output for
|
||||||
|
further scripting. E.g. `initctl status -j foo | jq .exit.status`
|
||||||
|
|
||||||
|
* Add JSON support to `initctl ls` command
|
||||||
|
|
||||||
|
This allows for easy access to the *disabled* services:
|
||||||
|
|
||||||
|
root@anarchy:~# initctl ls --json |jq '.available - .enabled'
|
||||||
|
[
|
||||||
|
"chronyd.conf",
|
||||||
|
"dnsmasq.conf",
|
||||||
|
"gdbserver.conf",
|
||||||
|
"inadyn.conf",
|
||||||
|
"inetd.conf",
|
||||||
|
"isisd.conf",
|
||||||
|
"lldpd.conf",
|
||||||
|
"mstpd.conf",
|
||||||
|
"ntpd.conf",
|
||||||
|
"ospf6d.conf",
|
||||||
|
"ospfd.conf",
|
||||||
|
"querierd.conf",
|
||||||
|
"ripd.conf",
|
||||||
|
"ripng.conf",
|
||||||
|
"sshd.conf",
|
||||||
|
"syslogd.conf",
|
||||||
|
"telnetd.conf",
|
||||||
|
"uftpd.conf",
|
||||||
|
"wpa_supplicant.conf",
|
||||||
|
"zebra.conf"
|
||||||
|
]
|
||||||
|
|
||||||
|
* Allow `manual:yes` on sysv/service/run/task stanzas, issue #274
|
||||||
|
* Add support for `oncrash:script` to call the `post:script` action, if
|
||||||
|
defined, for a crashing service. The `EXIT_CODE` variable sent to the
|
||||||
|
script is set to `crashed`. Issue #282
|
||||||
|
* Search for plugins in `/usr` and `/usr/local` as well, issue #284
|
||||||
|
* tty: add support for `passenv` flag to `/bin/login`, issue #286
|
||||||
|
* Add reboot/shutdown/poweroff timeout `-t SEC` to initctl, issue #295
|
||||||
|
* Add support for s6 and systemd readiness notification, issue #299.
|
||||||
|
Service readiness notification to support daemons employing systemd
|
||||||
|
and s6 notification. Complementing the native Finit readiness support
|
||||||
|
using PID files that exist already.
|
||||||
|
|
||||||
|
The two have slightly different ways of implementing readiness:
|
||||||
|
|
||||||
|
- https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||||
|
- https://skarnet.org/software/s6/notifywhenup.html
|
||||||
|
|
||||||
|
Finit now provides both a `NOTIFY_SOCKET` environment variable, for
|
||||||
|
systemd, and a way to start s6 daemons with a descriptor argument.
|
||||||
|
|
||||||
|
For details on the syntax, see the `service` documentation.
|
||||||
|
|
||||||
|
This change also renames internal states for run/task/services to
|
||||||
|
avoid any confusion with the introduction of `ready:scripts`:
|
||||||
|
|
||||||
|
- `WAITING -> PAUSED`
|
||||||
|
- `READY -> WAITING`
|
||||||
|
|
||||||
|
A service condition that used, e.g., `<service/foo/ready>` should now
|
||||||
|
instead use `<service/foo/waiting>`
|
||||||
|
|
||||||
|
* Add `ready:script` for services, called when daemon is ready, issue #300
|
||||||
|
* Add support for running scripts at shutdown at two new hook points
|
||||||
|
during the shutdown process, issue #302. See doc/plugins.md for details:
|
||||||
|
|
||||||
|
- `HOOK_SVC_DN`: after all services and non-reserved processes have been
|
||||||
|
killed (and collected)
|
||||||
|
- `HOOK_SYS_DN`: after all file systems have been unmounted, *just prior*
|
||||||
|
to Finit calling `reboot()` to shut down or reboot the system
|
||||||
|
|
||||||
|
* The `modules-load` plugin now default to runlevel `[S]`, in previous
|
||||||
|
releases it was `[2345]`. This breaking change is to align it more
|
||||||
|
with what users mostly want (modules loaded before services start) and
|
||||||
|
can be changed back to the old behavior with a per-file setting:
|
||||||
|
|
||||||
|
set runlevel 2345
|
||||||
|
|
||||||
|
* The `modules-load` plugin now adds silent tasks for modprobe. This to
|
||||||
|
prevent confusing `[ OK ]` boot messages when in fact modprobe failed.
|
||||||
|
* The `modules-load` plugin now support `set modprobe /path/to/modprobe`
|
||||||
|
* The header files `finit/conf.h` and `finit/service.h` are now exported
|
||||||
|
for external plugins
|
||||||
|
* Add support for multiple args to `initctl cond set/clr`, issue #329
|
||||||
|
* Silence confusing `[ OK ]` progress from modules-load plugin, issue #332
|
||||||
|
|
||||||
|
This change drops the confusing status progress output, which was always
|
||||||
|
OK since the actual modprobe operation runs in the background. No need
|
||||||
|
to show status of the "added a task to finit, found modprobe" command.
|
||||||
|
|
||||||
|
* dbus plugin: adapt to other operating systems
|
||||||
|
|
||||||
|
Not all Linux systems are based on Debian, and even if they are inspired
|
||||||
|
by Debian (Buildroot), they do not necessarily use the same defaults.
|
||||||
|
Probes the system at runtime for:
|
||||||
|
|
||||||
|
- dbus user and group
|
||||||
|
- dbus PID file
|
||||||
|
|
||||||
|
If the user/group cannot be found we fall back to `root`, if the PID
|
||||||
|
file cannot be determined we ignore PID file readiness.
|
||||||
|
|
||||||
|
* Improve documentation for runparts and hook scripts. Issue #315, #320
|
||||||
|
* Add `HOOK_NETWORK_DN`, called after change to runlevel 6 or 0, issue #319
|
||||||
|
* Use sysklogd `logger` tool instead of legacy `logit` tool, issue #344
|
||||||
|
|
||||||
|
For log redirection Finit has the legacy `logit` tool. This change
|
||||||
|
allows Finit to use the `sysklogd` project's extended `logger` tool
|
||||||
|
instead, when available. Allowing logging with the process' PID.
|
||||||
|
|
||||||
|
* Add `initctl` aliases: `cat -> show`, `kill -> signal`
|
||||||
|
* Add `initctl -n,--noerr` to return OK(0) if services do not exist, for
|
||||||
|
integration with openresolv and scripts with similar requirements
|
||||||
|
* Add `initctl plugins`, list installed plugins
|
||||||
|
* Add timestamp to log messages in fallback and logging to `stderr`.
|
||||||
|
When there is no log daemon, and we are running in a container, or we
|
||||||
|
cannot log to the kernel ring buffer, then we log to `stderr`. This
|
||||||
|
change improves log output by prefixing each message with a timestamp.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* Fix #254: document limitations in `rc.local` and `runparts` scripts
|
||||||
|
* Fix #269: add compulsory kernel symlinks in `/dev`
|
||||||
|
* Fix #275: `initctl status foo` should list all instances, regression
|
||||||
|
introduced in v4.3
|
||||||
|
* Fix #278: enforce conditions also for running `pre:` scripts
|
||||||
|
* Fix #279: allow `restart:always`, of crashing services. Similar to
|
||||||
|
`respawn` but honors `restart_sec`
|
||||||
|
* Fix #280: allow calling `initctl restart foo` from within foo
|
||||||
|
* Fix #283: too quick timeout at bootstrap of lingering tasks
|
||||||
|
* Fix #285: `initctl restart` should start crashed service
|
||||||
|
* Fix #288: enable built-in `sulogin` in Alpine and Void Linux builds
|
||||||
|
* Fix #293: modprobe plugin: support for coldplugging more devices. It
|
||||||
|
turns out, not all buses in Linux add `modalias` attributes to their
|
||||||
|
devices in sysfs. One notable exception are MDIO buses. The plugin's
|
||||||
|
scan routine would thus not pick them up.
|
||||||
|
* Fix #294: `shutdown --help` mistakenly shuts down system
|
||||||
|
* Fix #310: Always use configured restart delay for crashing services.
|
||||||
|
If no delay is configured, default to an initial 2000 msec for forking
|
||||||
|
daemons and start-stop scripts, and 1 msec for non-forking daemons
|
||||||
|
* Fix #311: document how to combine device tree with conditions
|
||||||
|
* Fix #312: restart services with respawn set, e.g. ttys, immediately
|
||||||
|
* Fix #313: Cancel pending restart timer on stop/start/restart/reload
|
||||||
|
* Fix #314: skip run/task/service restart if conditions are lost
|
||||||
|
* Fix #315: add environment variables to hook scripts
|
||||||
|
|
||||||
|
All hook scripts are called with at least one environment variable
|
||||||
|
set, `FINIT_HOOK_NAME`, useful when reusing the same hook script for
|
||||||
|
multiple hook points. It is set to the string name, also used by the
|
||||||
|
path, e.g., `hook/net/up`
|
||||||
|
|
||||||
|
For all hook points from hook/sys/shutdown and later, `FINIT_SHUTDOWN`
|
||||||
|
is also set, to one of: `halt`, `poweroff`, `reboot`
|
||||||
|
|
||||||
|
* Fix #317: make sure hook scripts don't run twice, also fixes #316
|
||||||
|
* Fix #318: only show `[ OK ] Calling foo` progress for `runparts ...`
|
||||||
|
* Fix #320: the API/IPC socket is closed immediately at shutdown/reboot
|
||||||
|
to protect hook scripts or services calling initctl. There is no way
|
||||||
|
to service these requests safely at that time
|
||||||
|
* Fix #333: consider a service dirty if command line args have changed
|
||||||
|
|
||||||
|
This fixes `initctl reload` correctly restarting all daemons that have
|
||||||
|
new command line arguments.
|
||||||
|
|
||||||
|
Previously command line arguments changes were only acted upon if the
|
||||||
|
service was explicitly reloaded `initctl reload myservice`.
|
||||||
|
|
||||||
|
Found and fixed by Jack Newman
|
||||||
|
|
||||||
|
* Fix #338: ensure shutdown hooks are called properly; `hook/sys/down`
|
||||||
|
and `hook/svc/down` hook scripts, found and fixed by Jack Newman
|
||||||
|
* Fix #339: use absolute path in `/etc/finit.d/enabled/` symlinks, for
|
||||||
|
use-cases when `/etc` is read-only and `/etc/finit.d/enabled ->
|
||||||
|
/mnt/finit.d/enabled`, reported by Jack Newman
|
||||||
|
* Fix #340: Finit ignores deleted/moved `.conf` file sin `/etc/finit.d`
|
||||||
|
* Fix #342: in runlevel S (bootstrap), not all `initctl` commands can be
|
||||||
|
supported, block the following: runlevel, reload, start/stop, restart,
|
||||||
|
reload, halt, poweroff, suspend. Also, prevent `SIGHUP` and `SIGUSR1`
|
||||||
|
signals when in shutdown or reboot
|
||||||
|
* Fix #352: separate runlevel S from runlevel 0
|
||||||
|
* Fix #355: regression in v3.2 stopping a process and its group
|
||||||
|
|
||||||
|
In Finit v3.2 a regression was introduced that affects the way Finit
|
||||||
|
stops a supervised process and its process group.
|
||||||
|
|
||||||
|
Instead of sending SIGTERM to the process, and thus delegating the
|
||||||
|
responsibility to that process to inform any children it may have, as
|
||||||
|
of commit 91a9c83 Finit sends SIGTERM to the entire process group.
|
||||||
|
For SIGKILL this is fine, SIGKILL only runs as cleanup and as a last
|
||||||
|
ditch effort if the process doesn't respond to SIGTERM.
|
||||||
|
|
||||||
|
This regression, introduced in v3.2, directly affects services like
|
||||||
|
`avahi-autoipd` that have forked off children that it needs to tell to
|
||||||
|
exit cleanly before it returns. With the patch in question these
|
||||||
|
children are never allowed to complete, which in turn causes lingering
|
||||||
|
169.254 link-local addresses on interfaces.
|
||||||
|
|
||||||
|
* Fix bootmisc plugin: octal permission on `/run/lock` and `/var/lock`
|
||||||
|
|
||||||
|
* Ensure `initctl cond get` support the flux state (exit code 255)
|
||||||
|
* Fix potential socket leak at bootstrap and shutdown
|
||||||
|
* Fix potential NULL pointer deref in kernel command line parser
|
||||||
|
* Fix lingering condition in service after reload of service with new
|
||||||
|
config that has no condition
|
||||||
|
* Fix wrong path to command in service after reload, may have changed
|
||||||
|
* `logit`: fall back to package name if `$LOGNAME` and `$USER` are
|
||||||
|
unset. **Note:** you should probably not use `logit` in your own
|
||||||
|
scripts, it is only meant for internal use by Finit. We recommend
|
||||||
|
using `logger` from the `bsdutils` or `sysklogd` packages instead
|
||||||
|
* Fix issue where `env:`/`pre:`/`post:`/etc. is removed from a service
|
||||||
|
|
||||||
|
The trick is when reloading a service like this:
|
||||||
|
|
||||||
|
service env:/etc/env serv -np -e foo:bar
|
||||||
|
|
||||||
|
into this:
|
||||||
|
|
||||||
|
service pre:/bin/pre.sh serv -np
|
||||||
|
|
||||||
|
In the second the `env:` has been removed and `pre:` added. Prior to
|
||||||
|
this patch, `env:` was kept leading to unintended behavior.
|
||||||
|
* Fix parse/diff of command line args, e.g., `nginx -g 'daemon off;'`
|
||||||
|
|
||||||
|
Starting a service like this works fine:
|
||||||
|
|
||||||
|
service [2345789] env:-/etc/default/nginx nginx -g 'daemon off;'
|
||||||
|
|
||||||
|
However, on `initctl reload` (and no change to .conf files) the arg
|
||||||
|
list was lost while parsing the .conf files. Leading to a false
|
||||||
|
positive 'diff' in args causing nginx to be unnecessarily restarted.
|
||||||
|
* Fix issue with disabled "linewrap" on the console TTY after login.
|
||||||
|
The root cause is `qemu-system-x86_64 -nographics` disabling it when
|
||||||
|
starting up. The correct `\e[?7h` escape code is now used.
|
||||||
|
|
||||||
|
|
||||||
[4.3][] - 2022-05-15
|
[4.3][] - 2022-05-15
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@@ -1152,7 +1550,9 @@ Major bug fix release.
|
|||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD
|
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.5...HEAD
|
||||||
|
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
|
||||||
|
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
|
||||||
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
|
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
|
||||||
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
|
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
|
||||||
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
|
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,5 @@
|
|||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
SUBDIRS = man plugins src test
|
SUBDIRS = man plugins src system test tmpfiles.d
|
||||||
doc_DATA = README.md LICENSE contrib/finit.conf
|
doc_DATA = README.md LICENSE contrib/finit.conf
|
||||||
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
||||||
|
|
||||||
@@ -30,4 +30,6 @@ release: distcheck
|
|||||||
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
|
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
|
||||||
done
|
done
|
||||||
|
|
||||||
DISTCHECK_CONFIGURE_FLAGS = --with-fstab=/etc/fstab
|
DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
|
||||||
|
--enable-testserv-plugin --enable-x11-common-plugin \
|
||||||
|
--with-watchdog --with-keventd --with-fstab=/etc/fstab
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ Features include:
|
|||||||
* Process supervision similar to [systemd][]
|
* Process supervision similar to [systemd][]
|
||||||
* Sourcing environment files
|
* Sourcing environment files
|
||||||
* Conditions for network/process/custom dependencies
|
* Conditions for network/process/custom dependencies
|
||||||
|
* Readiness notification; PID files (native) for synchronizing system
|
||||||
|
startup, support for systemd [sd_notify()][], or [s6 style][] too
|
||||||
|
* Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes)
|
||||||
* Pre/Post script actions
|
* Pre/Post script actions
|
||||||
|
* Rudimentary [templating support](doc/config.md#templating)
|
||||||
* Tooling to enable/disable services
|
* Tooling to enable/disable services
|
||||||
* Built-in getty
|
* Built-in getty
|
||||||
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
|
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
|
||||||
@@ -52,7 +56,8 @@ Features include:
|
|||||||
|
|
||||||
Focus is on small and embedded systems, although Finit is fully usable
|
Focus is on small and embedded systems, although Finit is fully usable
|
||||||
on server and desktop systems as well. For working examples, see the
|
on server and desktop systems as well. For working examples, see the
|
||||||
[contrib/](contrib/) section with tutorials for:
|
[contrib/](contrib/) section with tutorials for the following Linux
|
||||||
|
distributions:
|
||||||
|
|
||||||
* [Void Linux](contrib/void/),
|
* [Void Linux](contrib/void/),
|
||||||
* [Alpine Linux](contrib/alpine/), and
|
* [Alpine Linux](contrib/alpine/), and
|
||||||
@@ -70,7 +75,14 @@ Example
|
|||||||
This example `/etc/finit.conf` can also be split up in multiple `.conf`
|
This example `/etc/finit.conf` can also be split up in multiple `.conf`
|
||||||
files in `/etc/finit.d`. Available, but not yet enabled, services can
|
files in `/etc/finit.d`. Available, but not yet enabled, services can
|
||||||
be placed in `/etc/finit.d/available` and enabled by an operator using
|
be placed in `/etc/finit.d/available` and enabled by an operator using
|
||||||
the [initctl](#commands--status) tool.
|
the [initctl](#commands--status) tool. See the above mentioned Linux
|
||||||
|
distributions, or [myLinux][].
|
||||||
|
|
||||||
|
> **Note:** as of Finit v4.4, .conf lines can be broken up using the
|
||||||
|
> standard UNIX continuation character (`\`), also trailing comments are
|
||||||
|
> now supported. The latter means you need to escape any hashes used in
|
||||||
|
> directives and descriptions (`\#`). For more on this and examples,
|
||||||
|
> see the [finit.conf(5)][] manual or [doc/config.md](doc/config.md).
|
||||||
|
|
||||||
```ApacheConf
|
```ApacheConf
|
||||||
# Fallback if /etc/hostname is missing
|
# Fallback if /etc/hostname is missing
|
||||||
@@ -233,13 +245,50 @@ networking here. The configured `runlevel NUM` from `/etc/finit.conf`
|
|||||||
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
|
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
|
||||||
given on the kernel cmdline, in which case runlevel 1 is started.
|
given on the kernel cmdline, in which case runlevel 1 is started.
|
||||||
|
|
||||||
All services in runlevel S(0) are started first, followed by the desired
|
All services in runlevel S) are started first, followed by the desired
|
||||||
run-time runlevel. Run tasks in runlevel S can be started in sequence
|
run-time runlevel. Run tasks in runlevel S can be started in sequence
|
||||||
by using `run [S] cmd`. Changing runlevels at runtime is done like any
|
by using `run [S] cmd`. Changing runlevels at runtime is done like any
|
||||||
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
|
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
|
||||||
`intictl` tool.
|
`intictl` tool.
|
||||||
|
|
||||||
|
|
||||||
|
**Conditions**
|
||||||
|
|
||||||
|
As mentioned previously, Finit has an advanced dependency system to
|
||||||
|
handle synchronization, called [conditions](doc/conditions.md). It can
|
||||||
|
be used in many ways; depend on another service, network availability,
|
||||||
|
etc.
|
||||||
|
|
||||||
|
One *really cool* example useful for embedded systems is to run certain
|
||||||
|
scripts if a board has a certain feature encoded in its device tree. At
|
||||||
|
bootstrap we run the following `ident` script:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/sh
|
||||||
|
conddir=/var/run/finit/cond/hw/model
|
||||||
|
dtmodel=/sys/firmware/devicetree/base/model
|
||||||
|
|
||||||
|
if ! test -e $dtmodel; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
model=$(cat $dtmodel | tr "[A-Z] " "[a-z]-")
|
||||||
|
mkdir -p $conddir && ln -s ../../reconf $conddir/$model
|
||||||
|
```
|
||||||
|
|
||||||
|
Provided the device tree node exists, and is a string, we can then use
|
||||||
|
the condition `<hw/model/foo>` when starting other scripts. Here is an
|
||||||
|
example:
|
||||||
|
|
||||||
|
```
|
||||||
|
run [S] /path/to/ident --
|
||||||
|
task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board
|
||||||
|
```
|
||||||
|
|
||||||
|
> Notice the trick with an empty description to hide the call to `ident`
|
||||||
|
> in the Finit progress output.
|
||||||
|
|
||||||
|
|
||||||
**Plugins**
|
**Plugins**
|
||||||
|
|
||||||
Plugins can *extend* the functionality of Finit and *hook into* the
|
Plugins can *extend* the functionality of Finit and *hook into* the
|
||||||
@@ -346,6 +395,33 @@ in [doc/plugins.md](doc/plugins.md#hooks), and about the system bootstrap
|
|||||||
in [doc/bootstrap.md](doc/bootstrap.md).
|
in [doc/bootstrap.md](doc/bootstrap.md).
|
||||||
|
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
It is not possible to call Finit via signals or use `initctl` in any
|
||||||
|
runparts or `/etc/rc.local` script. This because Finit is single
|
||||||
|
threaded and is calling these scripts in a blocking fashion at the end
|
||||||
|
of runlevel S, at which point the event loop has not yet been started.
|
||||||
|
|
||||||
|
The event loop is the whole thing which Finit is built around, except
|
||||||
|
for runlevel S, which remains a slow procession through a lot of set up,
|
||||||
|
with a few hooks and blocking call outs to external scripts.
|
||||||
|
|
||||||
|
However, not all `initctl` commands are prohibited, supported commands:
|
||||||
|
are:
|
||||||
|
|
||||||
|
- `inictl cond`: only operate of files in `/run/finit/cond`
|
||||||
|
- `initctl enable/disable`: enabled run/task/service is activated on
|
||||||
|
the runlevel change from S to 2
|
||||||
|
- `initctl touch/show/create/delete/list`: `create`, provided the
|
||||||
|
non-interactive mode is used, again changes take effect in the
|
||||||
|
runlevel change directly after bootstrap
|
||||||
|
- `initctl -f reboot/poweroff/halt`: provided the `-f` flag is used to
|
||||||
|
force direct kernel commands
|
||||||
|
|
||||||
|
**Example:** you can set a `usr/` condition in `/etc/rc.local` and have
|
||||||
|
a service/task in runlevel 2 depend on it to execute.
|
||||||
|
|
||||||
|
|
||||||
Runlevels
|
Runlevels
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -438,6 +514,7 @@ Options:
|
|||||||
-c, --create Create missing paths (and files) as needed
|
-c, --create Create missing paths (and files) as needed
|
||||||
-f, --force Ignore missing files and arguments, never prompt
|
-f, --force Ignore missing files and arguments, never prompt
|
||||||
-h, --help This help text
|
-h, --help This help text
|
||||||
|
-j, --json JSON output in 'status' and 'cond' commands
|
||||||
-1, --once Only one lap in commands like 'top'
|
-1, --once Only one lap in commands like 'top'
|
||||||
-p, --plain Use plain table headings, no ctrl chars
|
-p, --plain Use plain table headings, no ctrl chars
|
||||||
-q, --quiet Silent, only return status of command
|
-q, --quiet Silent, only return status of command
|
||||||
@@ -460,9 +537,9 @@ Commands:
|
|||||||
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
||||||
reload Reload *.conf in /etc/finit.d (activate changes)
|
reload Reload *.conf in /etc/finit.d (activate changes)
|
||||||
|
|
||||||
cond set <COND> Set (assert) user-defined condition +usr/COND
|
cond set <COND> Set (assert) user-defined conditions +usr/COND
|
||||||
cond get <COND> Get status of user-defined condition, see $? and -v
|
cond get <COND> Get status of user-defined condition, see $? and -v
|
||||||
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
|
cond clear <COND> Clear (deassert) user-defined conditions -usr/COND
|
||||||
cond status Show condition status, default cond command
|
cond status Show condition status, default cond command
|
||||||
cond dump [TYPE] Dump all, or a type of, conditions and their status
|
cond dump [TYPE] Dump all, or a type of, conditions and their status
|
||||||
|
|
||||||
@@ -482,6 +559,8 @@ Commands:
|
|||||||
ps List processes based on cgroups
|
ps List processes based on cgroups
|
||||||
top Show top-like listing based on cgroups
|
top Show top-like listing based on cgroups
|
||||||
|
|
||||||
|
plugins List installed plugins
|
||||||
|
|
||||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||||
reboot Reboot system
|
reboot Reboot system
|
||||||
halt Halt system
|
halt Halt system
|
||||||
@@ -509,22 +588,22 @@ scripting and documentation:
|
|||||||
alpine:~# initctl -p
|
alpine:~# initctl -p
|
||||||
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||||
======================================================================
|
======================================================================
|
||||||
1506 acpid running [--2345----] ACPI daemon
|
1506 acpid running [---2345----] ACPI daemon
|
||||||
1509 crond running [--2345----] Cron daemon
|
1509 crond running [---2345----] Cron daemon
|
||||||
1489 dropbear running [--2345----] Dropbear SSH daemon
|
1489 dropbear running [---2345----] Dropbear SSH daemon
|
||||||
1511 klogd running [S12345----] Kernel log daemon
|
1511 klogd running [S-12345----] Kernel log daemon
|
||||||
1512 ntpd running [--2345----] NTP daemon
|
1512 ntpd running [---2345----] NTP daemon
|
||||||
1473 syslogd running [S12345----] Syslog daemon
|
1473 syslogd running [S-12345----] Syslog daemon
|
||||||
|
|
||||||
alpine:~# initctl -pv
|
alpine:~# initctl -pv
|
||||||
PID IDENT STATUS RUNLEVELS COMMAND
|
PID IDENT STATUS RUNLEVELS COMMAND
|
||||||
======================================================================
|
======================================================================
|
||||||
1506 acpid running [--2345----] acpid -f
|
1506 acpid running [---2345----] acpid -f
|
||||||
1509 crond running [--2345----] crond -f -S $CRON_OPTS
|
1509 crond running [---2345----] crond -f -S $CRON_OPTS
|
||||||
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
|
1489 dropbear running [---2345----] dropbear -R -F $DROPBEAR_OPTS
|
||||||
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
|
1511 klogd running [S-12345----] klogd -n $KLOGD_OPTS
|
||||||
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
|
1512 ntpd running [---2345----] ntpd -n $NTPD_OPTS
|
||||||
1473 syslogd running [S12345----] syslogd -n
|
1473 syslogd running [S-12345----] syslogd -n
|
||||||
```
|
```
|
||||||
|
|
||||||
The environment variables to each of the services above are read from,
|
The environment variables to each of the services above are read from,
|
||||||
@@ -549,7 +628,7 @@ Condition(s):
|
|||||||
User : root
|
User : root
|
||||||
Group : root
|
Group : root
|
||||||
Uptime : 2 hour 46 min 56 sec
|
Uptime : 2 hour 46 min 56 sec
|
||||||
Runlevels : [--2345----]
|
Runlevels : [---2345----]
|
||||||
Memory : 1.2M
|
Memory : 1.2M
|
||||||
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|
||||||
|- 1485 dropbear -R -F
|
|- 1485 dropbear -R -F
|
||||||
@@ -620,11 +699,15 @@ and proposed extensions.
|
|||||||
[upstart]: https://upstart.ubuntu.com/
|
[upstart]: https://upstart.ubuntu.com/
|
||||||
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
|
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||||
[openrc]: https://www.gentoo.org/proj/en/base/openrc/
|
[openrc]: https://www.gentoo.org/proj/en/base/openrc/
|
||||||
|
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||||
|
[s6 style]: https://skarnet.org/software/s6/notifywhenup.html
|
||||||
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||||
|
[tmpfiles.d(5)]: https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
|
||||||
[original finit]: http://helllabs.org/finit/
|
[original finit]: http://helllabs.org/finit/
|
||||||
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
|
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||||
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
||||||
[Joachim Wiberg]: https://troglobit.com
|
[Joachim Wiberg]: https://troglobit.com
|
||||||
|
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
|
||||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||||
[myLinux]: https://github.com/troglobit/myLinux/
|
[myLinux]: https://github.com/troglobit/myLinux/
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Run test suite with the correct configure params
|
||||||
|
|
||||||
|
make distclean
|
||||||
|
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
|
||||||
|
--enable-x11-common-plugin --with-watchdog --with-keventd
|
||||||
|
make -j17 clean check
|
||||||
+58
-15
@@ -1,10 +1,10 @@
|
|||||||
AC_INIT(Finit, 4.3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/projects/finit/)
|
AC_INIT([Finit],[4.5-rc2],[https://github.com/troglobit/finit/issues],[finit],[https://troglobit.com/projects/finit/])
|
||||||
AC_CONFIG_AUX_DIR(aux)
|
AC_CONFIG_AUX_DIR(aux)
|
||||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||||
AM_SILENT_RULES([yes])
|
AM_SILENT_RULES([yes])
|
||||||
|
|
||||||
AC_CONFIG_SRCDIR([src/finit.c])
|
AC_CONFIG_SRCDIR([src/finit.c])
|
||||||
AC_CONFIG_HEADER([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
AC_CONFIG_FILES([Makefile
|
AC_CONFIG_FILES([Makefile
|
||||||
contrib/Makefile
|
contrib/Makefile
|
||||||
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
|
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
|
||||||
@@ -14,8 +14,10 @@ AC_CONFIG_FILES([Makefile
|
|||||||
man/Makefile
|
man/Makefile
|
||||||
plugins/Makefile
|
plugins/Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
|
system/Makefile
|
||||||
|
tmpfiles.d/Makefile
|
||||||
test/test.env test/Makefile
|
test/test.env test/Makefile
|
||||||
test/tenv/Makefile test/common/Makefile])
|
test/lib/Makefile test/src/Makefile])
|
||||||
|
|
||||||
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
||||||
#m4_include([m4/plugin.m4])
|
#m4_include([m4/plugin.m4])
|
||||||
@@ -32,7 +34,6 @@ AC_PROG_INSTALL
|
|||||||
AC_PROG_MKDIR_P
|
AC_PROG_MKDIR_P
|
||||||
|
|
||||||
# Configuration.
|
# Configuration.
|
||||||
AC_HEADER_STDC
|
|
||||||
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h])
|
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h])
|
||||||
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r])
|
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r])
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ AC_ARG_ENABLE(redirect,
|
|||||||
enable_redirect=yes])
|
enable_redirect=yes])
|
||||||
|
|
||||||
AC_ARG_ENABLE(logrotate,
|
AC_ARG_ENABLE(logrotate,
|
||||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp]),,[
|
||||||
enable_logrotate=yes])
|
enable_logrotate=yes])
|
||||||
|
|
||||||
AC_ARG_ENABLE(doc,
|
AC_ARG_ENABLE(doc,
|
||||||
@@ -81,8 +82,14 @@ AC_ARG_ENABLE(contrib,
|
|||||||
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
|
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
|
||||||
enable_contrib=yes])
|
enable_contrib=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([rescue],
|
||||||
|
AS_HELP_STRING([--disable-rescue], [Disable potentially unsafe rescue mode]),,
|
||||||
|
[enable_rescue=yes])
|
||||||
|
|
||||||
# Check for extra plugins to enable
|
# Check for extra plugins to enable
|
||||||
enable_all_plugins=auto
|
AC_ARG_ENABLE(all_plugins,
|
||||||
|
AS_HELP_STRING([--enable-all-plugins], [Enable all plugins, default: auto]),
|
||||||
|
enable_all_plugins=$enableval, enable_all_plugins=auto)
|
||||||
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
|
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
|
||||||
AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus])
|
AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus])
|
||||||
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
|
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
|
||||||
@@ -95,6 +102,7 @@ AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
|
|||||||
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
|
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
|
||||||
AC_PLUGIN([tty], [yes], [Automatically activate new TTYs, e.g. USB-to-serial])
|
AC_PLUGIN([tty], [yes], [Automatically activate new TTYs, e.g. USB-to-serial])
|
||||||
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
|
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
|
||||||
|
AC_PLUGIN([testserv], [no], [Test plugin to start test serv daemon])
|
||||||
|
|
||||||
# Check for extra arguments or packages
|
# Check for extra arguments or packages
|
||||||
AC_ARG_WITH(fstab,
|
AC_ARG_WITH(fstab,
|
||||||
@@ -141,10 +149,10 @@ AC_ARG_WITH(keventd,
|
|||||||
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
|
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
|
||||||
|
|
||||||
AC_ARG_WITH(sulogin,
|
AC_ARG_WITH(sulogin,
|
||||||
AS_HELP_STRING([--with-sulogin], [Enable built-in sulogin, default: no.]),, [with_sulogin=no])
|
AS_HELP_STRING([--with-sulogin@<:@=USER@:>@], [Enable built-in sulogin, optional USER to request password for (default root), default: no.]),[sulogin=$withval],[with_sulogin=no])
|
||||||
|
|
||||||
AC_ARG_WITH(watchdog,
|
AC_ARG_WITH(watchdog,
|
||||||
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
|
AS_HELP_STRING([--with-watchdog@<:@=DEV@:>@], [Enable built-in watchdog, default: /dev/watchdog]),
|
||||||
[watchdog=$withval], [with_watchdog=no watchdog=])
|
[watchdog=$withval], [with_watchdog=no watchdog=])
|
||||||
|
|
||||||
AC_ARG_WITH(hook-scripts-path,
|
AC_ARG_WITH(hook-scripts-path,
|
||||||
@@ -155,6 +163,10 @@ AC_ARG_WITH(plugin-path,
|
|||||||
AS_HELP_STRING([--with-plugin-path=DIR], [Search path for external plugins, default /usr/lib/finit/plugins:/usr/local/lib/finit/plugins]),
|
AS_HELP_STRING([--with-plugin-path=DIR], [Search path for external plugins, default /usr/lib/finit/plugins:/usr/local/lib/finit/plugins]),
|
||||||
[plugin_path=$withval], [plugin_path=yes])
|
[plugin_path=$withval], [plugin_path=yes])
|
||||||
|
|
||||||
|
AC_ARG_WITH(rtc-date,
|
||||||
|
AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM", default "2000-01-01 00:00"]),
|
||||||
|
[rtc_date=$withval], [rtc_date=no])
|
||||||
|
|
||||||
### Enable features ###########################################################################
|
### Enable features ###########################################################################
|
||||||
|
|
||||||
# Create config.h from selected features and fallback defaults
|
# Create config.h from selected features and fallback defaults
|
||||||
@@ -177,6 +189,9 @@ AS_IF([test "x$enable_redirect" = "xyes"], [
|
|||||||
AS_IF([test "x$enable_logrotate" != "xno"], [
|
AS_IF([test "x$enable_logrotate" != "xno"], [
|
||||||
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
||||||
|
|
||||||
|
AS_IF([test "x$enable_rescue" != "xno"], [
|
||||||
|
AC_DEFINE([RESCUE_MODE], 1, [Define to enable support for rescue mode.])])
|
||||||
|
|
||||||
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
|
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
|
||||||
|
|
||||||
### With features ##############################################################################
|
### With features ##############################################################################
|
||||||
@@ -238,9 +253,18 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
|||||||
AC_EXPAND_DIR(random_path, "$random_seed")
|
AC_EXPAND_DIR(random_path, "$random_seed")
|
||||||
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
|
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
|
||||||
|
|
||||||
|
AS_IF([test "x$rtc_date" != "xno"], [
|
||||||
|
AC_DEFINE(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [
|
||||||
|
rtc_date=""])
|
||||||
|
|
||||||
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
|
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
|
||||||
|
|
||||||
AS_IF([test "x$with_sulogin" != "xno"], [with_sulogin=yes])
|
AS_IF([test "x$with_sulogin" != "xno"], [
|
||||||
|
AS_IF([test "x$sulogin" = "xyes"], [
|
||||||
|
sulogin=root])
|
||||||
|
with_sulogin=yes
|
||||||
|
AC_DEFINE_UNQUOTED(SULOGIN_USER, "$sulogin", [Built-in sulogin user, default: root])], [
|
||||||
|
sulogin=])
|
||||||
|
|
||||||
AS_IF([test "x$with_watchdog" != "xno"], [
|
AS_IF([test "x$with_watchdog" != "xno"], [
|
||||||
AS_IF([test "x$watchdog" = "xyes"], [
|
AS_IF([test "x$watchdog" = "xyes"], [
|
||||||
@@ -249,7 +273,7 @@ AS_IF([test "x$with_watchdog" != "xno"], [
|
|||||||
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
|
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
|
||||||
watchdog=])
|
watchdog=])
|
||||||
|
|
||||||
AS_IF([test "x$enable_hook_scripts_plugin" = "xyes" -a "x$with_hook_scripts_path" != "xno"], [
|
AS_IF([test "x$enable_hook_scripts_plugin" != "xno"], [
|
||||||
AS_IF([test "x$hook_scripts_path" = "xyes"], [hook_scripts_path=$libexecdir/finit/hook])
|
AS_IF([test "x$hook_scripts_path" = "xyes"], [hook_scripts_path=$libexecdir/finit/hook])
|
||||||
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
|
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
|
||||||
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
|
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
|
||||||
@@ -266,25 +290,42 @@ AM_CONDITIONAL(SULOGIN, [test "x$with_sulogin" != "xno"])
|
|||||||
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
|
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
|
||||||
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
|
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
|
||||||
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||||
|
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"])
|
||||||
|
|
||||||
# Override default libdir, used for plugins and rescue.conf
|
# Override default libdir, used for plugins and rescue.conf
|
||||||
pkglibdir=$libdir/finit
|
pkglibdir=$libdir/finit
|
||||||
AC_SUBST(pkglibexecdir)
|
AC_SUBST(pkglibexecdir)
|
||||||
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
|
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
|
||||||
AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path])
|
AC_DEFINE_UNQUOTED(FINIT_EXECPATH_, "$pkglibexecdir", [Finit libexec path])
|
||||||
|
|
||||||
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
|
AC_EXPAND_DIR(plugin_path, "$pkglibdir/plugins")
|
||||||
AC_SUBST(plugin_path)
|
AC_SUBST(plugin_path)
|
||||||
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
|
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
|
||||||
|
|
||||||
AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf")
|
AC_EXPAND_DIR(tmpfiles_path, "$libdir/tmpfiles.d")
|
||||||
|
AC_SUBST(tmpfiles_path)
|
||||||
|
AC_DEFINE_UNQUOTED(TMPFILES_PATH, "$tmpfiles_path", [Main tmpfiles.d/, override in /etc])
|
||||||
|
|
||||||
|
AC_EXPAND_DIR(finit_tmpfiles, "$pkglibdir/tmpfiles.d")
|
||||||
|
AC_SUBST(finit_tmpfiles)
|
||||||
|
AC_DEFINE_UNQUOTED(FINIT_TMPFILES, "$finit_tmpfiles", [Finit tmpfiles.d/ .conf path])
|
||||||
|
|
||||||
|
AC_EXPAND_DIR(system_path, "$pkglibdir/system")
|
||||||
|
AC_SUBST(system_path)
|
||||||
|
AC_DEFINE_UNQUOTED(FINIT_SYSPATH, "$system_path", [Static services, previously generated at runtime by plugins])
|
||||||
|
|
||||||
|
AC_EXPAND_DIR(rescue_conf, "$pkglibdir/rescue.conf")
|
||||||
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
|
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
|
||||||
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf")
|
AC_EXPAND_DIR(sample_conf, "$pkglibdir/sample.conf")
|
||||||
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
|
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
|
||||||
|
|
||||||
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
|
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
|
||||||
AS_IF([test "$heading" = "no"], [heading="<NONE>"])
|
AS_IF([test "$heading" = "no"], [heading="<NONE>"])
|
||||||
|
|
||||||
|
RTC_DATE=$rtc_date
|
||||||
|
AS_IF([test "$rtc_date" = ""], [RTC_DATE="<DEFAULT>"])
|
||||||
|
AS_IF([test "$rtc_date" = "no"], [RTC_DATE="<DEFAULT>"])
|
||||||
|
|
||||||
# Generate all files
|
# Generate all files
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
||||||
@@ -309,17 +350,19 @@ cat <<EOF
|
|||||||
Behavior:
|
Behavior:
|
||||||
Boot heading..........: $heading
|
Boot heading..........: $heading
|
||||||
Plugins...............: $plugins
|
Plugins...............: $plugins
|
||||||
|
RTC restore date......: $RTC_DATE
|
||||||
|
|
||||||
Optional features:
|
Optional features:
|
||||||
Install doc/..........: $enable_doc
|
Install doc/..........: $enable_doc
|
||||||
Install contrib/......: $enable_contrib
|
Install contrib/......: $enable_contrib
|
||||||
Built-in keventd......: $with_keventd
|
Built-in keventd......: $with_keventd
|
||||||
Built-in sulogin......: $with_sulogin
|
Built-in sulogin......: $with_sulogin $sulogin
|
||||||
Built-in watchdogd....: $with_watchdog $watchdog
|
Built-in watchdogd....: $with_watchdog $watchdog
|
||||||
Built-in logrotate....: $enable_logrotate
|
Built-in logrotate....: $enable_logrotate
|
||||||
Skip fsck check.......: $enable_fastboot
|
Skip fsck check.......: $enable_fastboot
|
||||||
Run fsck fix mode.....: $enable_fsckfix
|
Run fsck fix mode.....: $enable_fsckfix
|
||||||
Redirect output.......: $enable_redirect
|
Redirect output.......: $enable_redirect
|
||||||
|
Rescue mode...........: $enable_rescue
|
||||||
Default hostname......: $hostname
|
Default hostname......: $hostname
|
||||||
Default group.........: $group
|
Default group.........: $group
|
||||||
Default runlevel......: $runlevel
|
Default runlevel......: $runlevel
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
|||||||
--sysconfdir=/etc --localstatedir=/var \
|
--sysconfdir=/etc --localstatedir=/var \
|
||||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||||
--enable-alsa-utils-plugin --with-watchdog \
|
--enable-alsa-utils-plugin --with-watchdog \
|
||||||
--with-keventd
|
--with-keventd --with-sulogin
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "*** Building ..."
|
echo "*** Building ..."
|
||||||
@@ -35,4 +35,3 @@ fi
|
|||||||
echo
|
echo
|
||||||
echo "*** Done"
|
echo "*** Done"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,3 @@ else
|
|||||||
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
|
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
|||||||
--sysconfdir=/etc --localstatedir=/var \
|
--sysconfdir=/etc --localstatedir=/var \
|
||||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||||
--enable-alsa-utils-plugin --with-watchdog \
|
--enable-alsa-utils-plugin --with-watchdog \
|
||||||
--with-keventd
|
--with-keventd --with-sulogin
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "*** Building ..."
|
echo "*** Building ..."
|
||||||
@@ -35,4 +35,3 @@ fi
|
|||||||
echo
|
echo
|
||||||
echo "*** Done"
|
echo "*** Done"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
|||||||
+10
@@ -9,6 +9,16 @@ value in the project turned out to be:
|
|||||||
> systems, container, and dedicated server applications.
|
> systems, container, and dedicated server applications.
|
||||||
|
|
||||||
|
|
||||||
|
Before Next Release
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* When did runparts +arg break?
|
||||||
|
* Should we update the =runparts DIR= syntax to =runparts [progress] [debug] DIR=?
|
||||||
|
* Add SNN-only option top runparts, for Buildroot
|
||||||
|
* Calling `initctl runlevel` at bootstrap => N 10, what should it be?
|
||||||
|
* Reindent all shell scripts in true emacs shell-script-mode (4 chars)
|
||||||
|
|
||||||
|
|
||||||
Misc TODOs
|
Misc TODOs
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
+17
-4
@@ -23,6 +23,16 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
|||||||
|
|
||||||
> Note: `<boot/...>` conditions cannot be cleared with `initctl`!
|
> Note: `<boot/...>` conditions cannot be cleared with `initctl`!
|
||||||
|
|
||||||
|
* `finit.config=/path/to/alternative/finit.conf`: override the
|
||||||
|
compile-time bootstrap config file, default:
|
||||||
|
|
||||||
|
./configure --with-config=/etc/finit.conf
|
||||||
|
|
||||||
|
Useful when starting up in various rescue mode, factory, or
|
||||||
|
production test setups. Use the top-level configuration file
|
||||||
|
directive `rcsd /path/to/finit.d` to override the default
|
||||||
|
rcS.d directory.
|
||||||
|
|
||||||
* `finit.debug[=bool]`: Enable finit debug. This is operated
|
* `finit.debug[=bool]`: Enable finit debug. This is operated
|
||||||
independently of the kernel `debug` setting. New as of Finit v4.
|
independently of the kernel `debug` setting. New as of Finit v4.
|
||||||
|
|
||||||
@@ -76,13 +86,16 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
|||||||
the system booted in a limited fallback mode. See [config.md][]
|
the system booted in a limited fallback mode. See [config.md][]
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
|
This option can be disabled with `configure --without-rescue`
|
||||||
|
|
||||||
**Note:** in this mode `initctl` will not work. Correct the problem
|
**Note:** in this mode `initctl` will not work. Correct the problem
|
||||||
and use `reboot -f` to force reboot.
|
and use `reboot -f` to force reboot.
|
||||||
|
|
||||||
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
|
* `single`, or `S`: Overrides the configured runlevel (default: 2) to go
|
||||||
debug problems with the regular runlevel. All services and TTYs in
|
to after bootstrap by forcing it to runlevel 1, this is also known
|
||||||
`[1]` will be started, so a `tty [1] @console nologin` configuration
|
as single user mode. Useful to debug startup problems. All services
|
||||||
presents you with a root console without login.
|
and TTYs in `[1]` will be started, so a `tty [1] @console nologin`
|
||||||
|
configuration presents you with a root console without login.
|
||||||
|
|
||||||
* `1-9`, except `6`: override the configured `runlevel`. Like the `S`
|
* `1-9`, except `6`: override the configured `runlevel`. Like the `S`
|
||||||
and `rescue`, giving a single number on the kernel command line tells
|
and `rescue`, giving a single number on the kernel command line tells
|
||||||
|
|||||||
+39
-16
@@ -13,9 +13,9 @@ Finit Conditions
|
|||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Conditions are a new addition to Finit, introduced in v3, with the
|
Conditions are a relatively new addition to Finit, introduced in v3,
|
||||||
intention of providing a mechanism for common synchronization problems.
|
with the intention of providing a mechanism for common synchronization
|
||||||
For example:
|
problems. For example:
|
||||||
|
|
||||||
- *"wait for service A to start before starting service B"*, or
|
- *"wait for service A to start before starting service B"*, or
|
||||||
- *"wait for basic network access to be available"*
|
- *"wait for basic network access to be available"*
|
||||||
@@ -35,7 +35,7 @@ service to run.
|
|||||||
In this example the Network monitor daemon `netd` is not started until
|
In this example the Network monitor daemon `netd` is not started until
|
||||||
both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A
|
both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A
|
||||||
`pid/` condition is satisfied by the corresponding service's PID file
|
`pid/` condition is satisfied by the corresponding service's PID file
|
||||||
being created.
|
being created, i.e., the service's default readiness notification.
|
||||||
|
|
||||||
**NOTE:** Conditions also stop services when a condition is no longer
|
**NOTE:** Conditions also stop services when a condition is no longer
|
||||||
asserted. I.e., if the Zebra process above stops or restarts, netd
|
asserted. I.e., if the Zebra process above stops or restarts, netd
|
||||||
@@ -49,11 +49,11 @@ Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
|
|||||||
|
|
||||||
- `netlink.so`: provides `<net/...>`
|
- `netlink.so`: provides `<net/...>`
|
||||||
- `pidfile.so`: provides `<pid/...>`
|
- `pidfile.so`: provides `<pid/...>`
|
||||||
- Cmdline `finit.cond=arg`: provides `<boot/arg`
|
- Cmdline `finit.cond=arg`: provides `<boot/arg>`
|
||||||
|
|
||||||
See below for built-in conditions. Finit also supports user-defined
|
See below for built-in conditions. Finit also supports user-defined
|
||||||
conditions, sometimes referred to as static or one-shot conditions.
|
conditions, sometimes referred to as static or one-shot conditions.
|
||||||
They live in the `<usr/...` namespace and are constrained to a flat
|
They live in the `<usr/...>` namespace and are constrained to a flat
|
||||||
hierarchy without any sub-directories, unlike the pidfile plugin, for
|
hierarchy without any sub-directories, unlike the pidfile plugin, for
|
||||||
instance.
|
instance.
|
||||||
|
|
||||||
@@ -92,15 +92,35 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
|||||||
Built-in Conditions
|
Built-in Conditions
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Finit comes with a set of plugins for conditions: `pidfile`, `netlink`,
|
Finit comes with a set of plugins for conditions:
|
||||||
`sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/`
|
|
||||||
(recursively) for PID files created by the monitored services, and sets
|
- `devmon` (built-in)
|
||||||
a corresponding condition in the `pid/` namespace. Similarly, the
|
- `netlink`
|
||||||
`netlink` plugin provides basic conditions for when an interface is
|
- `pidfile`
|
||||||
brought up/down and when a default route (gateway) is set, in the `net/`
|
- `sys`
|
||||||
namespace. The `sys` and `usr` plugins monitor are passive condition
|
- `usr`
|
||||||
monitors where the action is provided by `keventd`, signal handlers,
|
|
||||||
and in the case of `usr`, the end-user via the `initctl` tool
|
The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device
|
||||||
|
nodes being created and removed. It is active only when a run, task, or
|
||||||
|
service has declared a `<dev/foo>` or `<dev/dir/bar>` condition.
|
||||||
|
|
||||||
|
The `pidfile` plugin (recursively) watches `/run/` (recursively) for PID
|
||||||
|
files created by the monitored services, and sets a corresponding
|
||||||
|
condition in the `pid/` namespace.
|
||||||
|
|
||||||
|
Similarly, the `netlink` plugin provides basic conditions for when an
|
||||||
|
interface is brought up/down and when a default route (gateway) is set,
|
||||||
|
in the `net/` namespace.
|
||||||
|
|
||||||
|
The `sys` and `usr` plugins monitor are passive condition monitors where
|
||||||
|
the action is provided by `keventd`, signal handlers, and in the case of
|
||||||
|
`usr`, the end-user via the `initctl` tool.
|
||||||
|
|
||||||
|
Additionally, the various states of a run/task/sysv/service can also be
|
||||||
|
used as conditions, the image above shows the state names. The syntax
|
||||||
|
for a `service` type process: `<service/foo/STATE>`. The other types,
|
||||||
|
in particular run/task/sysv, there are the additional states `success`
|
||||||
|
and `failure`.
|
||||||
|
|
||||||
With the example listed above, finit does not start the `/sbin/netd`
|
With the example listed above, finit does not start the `/sbin/netd`
|
||||||
daemon until `setupd` and `zebra` has started *and* created their PID
|
daemon until `setupd` and `zebra` has started *and* created their PID
|
||||||
@@ -119,11 +139,14 @@ Built-in conditions:
|
|||||||
- `net/<IFNAME>/exist`
|
- `net/<IFNAME>/exist`
|
||||||
- `net/<IFNAME>/up`
|
- `net/<IFNAME>/up`
|
||||||
- `net/<IFNAME>/running`
|
- `net/<IFNAME>/running`
|
||||||
|
- `service/<NAME[:ID]>/<STATE>`
|
||||||
|
- `{run, task, sysv}/<NAME[:ID]>/{<STATE>, success, failure}`
|
||||||
- `sys/pwr/ac`
|
- `sys/pwr/ac`
|
||||||
- `sys/pwr/fail`
|
- `sys/pwr/fail`
|
||||||
- `sys/key/ctrlaltdel`
|
- `sys/key/ctrlaltdel`
|
||||||
- `usr/foo`
|
- `usr/foo`
|
||||||
- `boot/arg`
|
- `boot/arg`
|
||||||
|
- `dev/node` and `dev/dir/node`
|
||||||
|
|
||||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
||||||
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
||||||
@@ -169,7 +192,7 @@ Debugging
|
|||||||
If a service is not being started as it should, the problem might be
|
If a service is not being started as it should, the problem might be
|
||||||
that one of its conditions is not in the expected state. Use the
|
that one of its conditions is not in the expected state. Use the
|
||||||
command `initctl status` to inspect service status. Services in the
|
command `initctl status` to inspect service status. Services in the
|
||||||
`ready` state are pending a condition.
|
`waiting` state are pending a condition.
|
||||||
|
|
||||||
In that situation, running `initctl cond show` reveals which of the
|
In that situation, running `initctl cond show` reveals which of the
|
||||||
conditions that are not satisfied. Listed as `off` below.
|
conditions that are not satisfied. Listed as `off` below.
|
||||||
|
|||||||
+535
-63
@@ -1,14 +1,21 @@
|
|||||||
Configuration
|
Finit Configuration
|
||||||
=============
|
===================
|
||||||
|
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
|
* [Configuration Files](#configuration-files)
|
||||||
|
* [Runlevels](#runlevels)
|
||||||
|
* [Managing Services](#managing-services)
|
||||||
|
* [Environment Variables](#environment-variables)
|
||||||
* [Service Environment](#service-environment)
|
* [Service Environment](#service-environment)
|
||||||
|
* [Service Synchronization](#service-synchronization)
|
||||||
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||||
|
* [Templating](#templating)
|
||||||
* [Cgroups](#cgroups)
|
* [Cgroups](#cgroups)
|
||||||
* [Configuration File Syntax](#configuration-file-syntax)
|
* [Configuration File Syntax](#configuration-file-syntax)
|
||||||
* [Hostname](#hostname)
|
* [Hostname](#hostname)
|
||||||
* [Kernel Modules](#kernel-modules)
|
* [Kernel Modules](#kernel-modules)
|
||||||
* [Networking](#networking)
|
* [Networking](#networking)
|
||||||
|
* [Alternate rcS.d/](#alternate-rcs-d-)
|
||||||
* [Resource Limits](#resource-limits)
|
* [Resource Limits](#resource-limits)
|
||||||
* [Runlevels](#runlevels)
|
* [Runlevels](#runlevels)
|
||||||
* [One-shot Commands (sequence)](#one-shot-commands--sequence-)
|
* [One-shot Commands (sequence)](#one-shot-commands--sequence-)
|
||||||
@@ -21,6 +28,7 @@ Configuration
|
|||||||
* [TTYs and Consoles](#ttys-and-consoles)
|
* [TTYs and Consoles](#ttys-and-consoles)
|
||||||
* [Non-privileged Services](#non-privileged-services)
|
* [Non-privileged Services](#non-privileged-services)
|
||||||
* [Redirecting Output](#redirecting-output)
|
* [Redirecting Output](#redirecting-output)
|
||||||
|
* [SysV Init Compatibility](#sysv-init-compatibility)
|
||||||
* [Rescue Mode](#rescue-mode)
|
* [Rescue Mode](#rescue-mode)
|
||||||
* [Limitations](#limitations)
|
* [Limitations](#limitations)
|
||||||
* [/etc/finit.conf](#etcfinitconf)
|
* [/etc/finit.conf](#etcfinitconf)
|
||||||
@@ -32,29 +40,161 @@ Configuration
|
|||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Finit can be configured using only the original `/etc/finit.conf` file
|
Finit is a process starter and service monitor designed to run as PID 1
|
||||||
or in combination with `/etc/finit.d/*.conf`. Useful for package-based
|
on Linux. It consists of [plugins](plugins.md) and configuration files.
|
||||||
Linux distributions -- each package can provide its own "script" file.
|
Plugins start at [hook points](plugins.md#hooks) and can run various set
|
||||||
|
up, or install event handlers that later provide runtime services, e.g.,
|
||||||
|
PID file monitoring, or [conditions](conditions.md).
|
||||||
|
|
||||||
- `/etc/finit.conf`: main configuration file
|
> **Tip:** see [SysV Init Compatibility](#sysv-init-compatibility) for
|
||||||
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
> help to quickly get going with an existing SysV or BusyBox init setup.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
To add a new service, drop a `.conf` file in `/etc/finit.d` and run
|
### Configuration Files
|
||||||
`initctl reload`. (It is also possible to `SIGHUP` to PID 1, or call
|
|
||||||
|
Originally Finit was configured using a single file, `/etc/finit.conf`.
|
||||||
|
Although still possible to use this single configuration file, today the
|
||||||
|
following familiar layout is recommended:
|
||||||
|
|
||||||
|
/etc/
|
||||||
|
|- finit.d/
|
||||||
|
| |- available/
|
||||||
|
| | `- my-service.conf
|
||||||
|
: |- enabled/
|
||||||
|
: | `- my-service.conf -> ../available/my-service.conf
|
||||||
|
: :
|
||||||
|
: |- static-service.conf
|
||||||
|
: `- another-static.conf
|
||||||
|
:
|
||||||
|
`- finit.conf
|
||||||
|
|
||||||
|
Services in the `available/` and `enabled/` sub-directories are called
|
||||||
|
dynamic services, in contrast to static services -- the only difference
|
||||||
|
being where they are installed and if the `initctl` tool can manage them
|
||||||
|
with the `enable` and `disable` commands. An administrator can always
|
||||||
|
create files and symlinks manually.
|
||||||
|
|
||||||
|
At bootstrap, and `initctl reload`, all .conf files are read, starting
|
||||||
|
with `finit.conf`, then, in alphabetical order, all `finit.d/*.conf`
|
||||||
|
files, and finally (still in alphabetical order) all `enabled/*.conf`
|
||||||
|
files.
|
||||||
|
|
||||||
|
> **Note:** the `finit.conf` and `finit.d/` names are only defaults.
|
||||||
|
> They can be changed at compile-time with two `configure` options:
|
||||||
|
> `--with-config=/etc/foo.conf` and `--with-rcsd=/var/foo.d`.
|
||||||
|
>
|
||||||
|
> They can also be overridden from the [kernel command line](cmdline.md)
|
||||||
|
> using: `-- finit.config=/etc/bar.conf` and in that file use the
|
||||||
|
> top-level configuration directive `rcsd /path/to/finit.d`.
|
||||||
|
|
||||||
|
|
||||||
|
### Filesystem Layout
|
||||||
|
|
||||||
|
Finit is most comfortable with a traditional style Linux filesystem
|
||||||
|
layout, as specified in the [FHS][]:
|
||||||
|
|
||||||
|
/.
|
||||||
|
|- bin/
|
||||||
|
|- dev/ # Mounted automatically if devtmpfs is available
|
||||||
|
| |- pts/ # Mounted automatically by Finit if it exists
|
||||||
|
| `- shm/ # Mounted automatically by Finit if it exists
|
||||||
|
|- etc/
|
||||||
|
| |- finit.d/
|
||||||
|
| | |- available/
|
||||||
|
| | `- enabled/
|
||||||
|
| `- finit.conf
|
||||||
|
|- home/
|
||||||
|
|- lib/
|
||||||
|
|- libexec/
|
||||||
|
|- mnt/
|
||||||
|
|- proc/ # Mounted automatically by Finit if it exists
|
||||||
|
|- root/
|
||||||
|
|- run/ # Mounted automatically by Finit if it exists
|
||||||
|
| `- lock/ # Created automatically if Finit mounts /run
|
||||||
|
|- sbin/
|
||||||
|
|- sys/ # Mounted automatically by Finit if it exists
|
||||||
|
|- tmp/ # Mounted automatically by Finit if it exists
|
||||||
|
|- usr/
|
||||||
|
`- var/
|
||||||
|
|- cache/
|
||||||
|
|- db/
|
||||||
|
|- lib/
|
||||||
|
| `- misc/
|
||||||
|
|- lock/
|
||||||
|
|- log/
|
||||||
|
|- run -> ../run
|
||||||
|
|- spool/
|
||||||
|
`- tmp/
|
||||||
|
|
||||||
|
Finit starts (very early) by mounting `/dev`, `/proc/`, and `/sys`,
|
||||||
|
unless they (i.e., `/dev`) are already mounted. When all plugins and
|
||||||
|
other, core Finit functions, have been set up, all relevant filesystems
|
||||||
|
(`PASS > 0`) are checked and mounted from the selected `fstab`, either
|
||||||
|
the default `/etc/fstab`, or any custom one selected from the command
|
||||||
|
line, or at build time.
|
||||||
|
|
||||||
|
Any file system not listed `fstab` are automatically mounted by Finit,
|
||||||
|
as listed above, provided their respective mount point exists.
|
||||||
|
|
||||||
|
With all filesystems mounted, Finit calls `swapon`.
|
||||||
|
|
||||||
|
At shutdown, and after having stopped all services and other lingering
|
||||||
|
processes have been killed, filesystems are unmounted in the reverse
|
||||||
|
order, and `swapoff` is called.
|
||||||
|
|
||||||
|
[FHS]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
|
||||||
|
|
||||||
|
### Runlevels
|
||||||
|
|
||||||
|
- ` S`: bootStrap
|
||||||
|
- ` 1`: Single user mode
|
||||||
|
- `2-5`: traditional multi-user mode
|
||||||
|
- ` 6`: reboot
|
||||||
|
- `7-9`: multi-user mode (extra)
|
||||||
|
- ` 0`: shutdown
|
||||||
|
|
||||||
|
Runlevels are declared per service/run/task/sysv command. Starting in
|
||||||
|
runlevel S ([bootStrap](bootstrap.md)), usually only for tasks supposed
|
||||||
|
to run once at boot, and services like `syslogd`, which you need to
|
||||||
|
start and run throughout the whole time your system is up. Before `S`
|
||||||
|
is started, however, Finit performs a lot of housekeeping tasks like
|
||||||
|
mounting all filesystems, calling `fsck` if needed, and making sure the
|
||||||
|
everything is OK.
|
||||||
|
|
||||||
|
task [S] /lib/console-setup/console-setup.sh
|
||||||
|
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
|
||||||
|
|
||||||
|
After runlevel S, Finit proceeds to runlevel 2. (This can be changed in
|
||||||
|
`/etc/finit.conf` using the `runlevel N` directive.) Before starting
|
||||||
|
the services in runlevel 2, Finit first stops everything that is not
|
||||||
|
allowed to run in 2, and then brings up networking. Networking is
|
||||||
|
expected to be available in all runlevels except: S, 1 (single user
|
||||||
|
level), 6, and 0. Networking is enabled either by the `network script`
|
||||||
|
directive, or if you have an `/etc/network/interfaces` file, Finit calls
|
||||||
|
`ifup -a` -- at the very least the loopback interface is brought up.
|
||||||
|
|
||||||
|
> **Note:** when moving from runlevel S to 2, all run/task/services that
|
||||||
|
> were constrained to runlevel S only are dropped from bookkeeping. So
|
||||||
|
> when reaching the prompt, `initctl` will not show these run/tasks.
|
||||||
|
> This is a safety mechanism to prevent bootstrap-only tasks from
|
||||||
|
> accidentally being run again. E.g., `console-setup.sh` above.
|
||||||
|
|
||||||
|
|
||||||
|
### Managing Services
|
||||||
|
|
||||||
|
Using `initctl disable my-service` the symlink (above) is removed and
|
||||||
|
the service is queued for removal. Several changes can be made to the
|
||||||
|
system, but it is not until `initctl reload` is called that the changes
|
||||||
|
are activated.
|
||||||
|
|
||||||
|
To add a new static service, drop a `.conf` file in `/etc/finit.d/` and
|
||||||
|
run `initctl reload`. (It is also possible to `SIGHUP` PID 1, or call
|
||||||
`finit q`, but that has been deprecated with the `initctl` tool). Finit
|
`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
|
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
|
restart of any service you can touch its corresponding `.conf` file in
|
||||||
`/etc/finit.d` and call `initctl reload`. Finit handle any and all
|
`/etc/finit.d` and call `initctl reload`. Finit handles all conditions
|
||||||
conditions and dependencies between services automatically.
|
and dependencies between services automatically, see below section on
|
||||||
|
[Service Synchronization](#service-synchronization) for more details.
|
||||||
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
|
|
||||||
useful in particular to Linux distributions that may want to install all
|
|
||||||
files for a package and let the user decide when to enable a service.
|
|
||||||
|
|
||||||
On `initctl reload` the following is checked for all services:
|
On `initctl reload` the following is checked for all services:
|
||||||
|
|
||||||
@@ -68,10 +208,6 @@ On `initctl reload` the following is checked for all services:
|
|||||||
For more info on the different states of a service, see the separate
|
For more info on the different states of a service, see the separate
|
||||||
document [Finit Services](service.md).
|
document [Finit Services](service.md).
|
||||||
|
|
||||||
> When running <kbd>make install</kbd> no default `/etc/finit.conf` is
|
|
||||||
> installed since system requirements differ too much. There are some
|
|
||||||
> examples in the `contrib/` directory, which can be used as a base.
|
|
||||||
|
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
@@ -80,10 +216,12 @@ and any `*.conf`, was added. It is worth noting that these are global
|
|||||||
and *shared with all* services -- the only way to have service-local
|
and *shared with all* services -- the only way to have service-local
|
||||||
environment is detailed in the next section.
|
environment is detailed in the next section.
|
||||||
|
|
||||||
The syntax for global environment variables is straight forward:
|
The syntax for global environment variables is straight forward. In
|
||||||
|
Finit v4.4 the `set` keyword was added for completeness, but the old
|
||||||
|
syntax (without the `set ` prefix) is still honored:
|
||||||
|
|
||||||
foo=bar
|
set foo=bar
|
||||||
baz="qux"
|
set baz="qux"
|
||||||
|
|
||||||
On reload of .conf files, all tracked environment variables are cleared
|
On reload of .conf files, all tracked environment variables are cleared
|
||||||
so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf`
|
so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf`
|
||||||
@@ -131,6 +269,67 @@ the `ps` command we can see that the process is started with:
|
|||||||
> `-` is used, a missing environment file does *not* block the start.
|
> `-` is used, a missing environment file does *not* block the start.
|
||||||
|
|
||||||
|
|
||||||
|
Service Synchronization
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Finit was created for fast booting systems. Faster than a regular SysV
|
||||||
|
Init based system at the time. Early on the need for a guaranteed start
|
||||||
|
order of services (daemons) arose. I.e., service `A` must be guaranteed
|
||||||
|
to have started (and be ready!) before `B`. The model that was chosen
|
||||||
|
to determine this was very simple: PID files.
|
||||||
|
|
||||||
|
Early on in UNIX daemons were controlled with basic IPC like signals,
|
||||||
|
and the way for a user to know that a daemon was ready to respond to
|
||||||
|
signals (minimally having set up its signal handler), was to tell the
|
||||||
|
user;
|
||||||
|
|
||||||
|
> "Hey, you can send signals to me using the PID in this file:
|
||||||
|
> `/var/run/daemon.pid`".
|
||||||
|
|
||||||
|
Since most systems run fairly unchanged after boot, Finit could rely on
|
||||||
|
the PID file for `A` being created before launching `B`. This method
|
||||||
|
has worked well for a long time, and for systems based on Open Source it
|
||||||
|
was easy to either add PID file support to a daemon without support for
|
||||||
|
it, or fix ordering issues (PID file created before signal handler is
|
||||||
|
set up) in existing daemons.
|
||||||
|
|
||||||
|
However, with the advent of other Init systems (Finit is rather old),
|
||||||
|
most notably systemd and s6, other methods for signaling "readiness"
|
||||||
|
arrived and daemons were adapted to these new schemes to a larger
|
||||||
|
extent.
|
||||||
|
|
||||||
|
As of Finit v4.4 partial support for systemd and s6 is available. The
|
||||||
|
native PID file mode of operation is still default, the other's can be
|
||||||
|
defined per service, like this:
|
||||||
|
|
||||||
|
service notify:systemd foo
|
||||||
|
service notify:s6 bar
|
||||||
|
|
||||||
|
To synchronize two services the following condition can be used:
|
||||||
|
|
||||||
|
service notify:systemd A
|
||||||
|
service <service/A/ready> B
|
||||||
|
|
||||||
|
For details on the syntax and options, see below.
|
||||||
|
|
||||||
|
> **Note:** on `initctl reload` conditions are normally set in "flux",
|
||||||
|
> while figuring out which to stop, start or restart. Services that
|
||||||
|
> need to be restarted have their `ready` condition removed prior to
|
||||||
|
> Finit sending them SIGHUP (if they support that), or stop-starting
|
||||||
|
> them. A daemon is expected to reassert its readiness, e.g. systemd
|
||||||
|
> style daemons to write `READY=1\n`.
|
||||||
|
>
|
||||||
|
> However, the s6 notify mode does not support this because in s6 you
|
||||||
|
> are expected to close your notify descriptor after having written
|
||||||
|
> `\n`. This means s6 style daemons currently must be stop-started.
|
||||||
|
> (Declare the service with `<!>` in its condition statement.)
|
||||||
|
>
|
||||||
|
> For native PID file style readiness notification a daemon is expected
|
||||||
|
> to either create its PID file, if it does not exist yet, or touch it
|
||||||
|
> using `utimensat()` or similar. This triggers both the `<pid/>` and
|
||||||
|
> `<.../ready>` conditions.
|
||||||
|
|
||||||
|
|
||||||
Service Wrapper Scripts
|
Service Wrapper Scripts
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
@@ -163,6 +362,26 @@ example employs a wrapper script in `/etc/start.d`.
|
|||||||
> instead of sending SIGHUP at restart/reload events.
|
> instead of sending SIGHUP at restart/reload events.
|
||||||
|
|
||||||
|
|
||||||
|
Templating
|
||||||
|
----------
|
||||||
|
|
||||||
|
Finit comes with rudimentary support for templating, similar to that of
|
||||||
|
systemd. Best illustrated with an example:
|
||||||
|
|
||||||
|
$ initctl show avahi-autoipd@
|
||||||
|
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
|
||||||
|
|
||||||
|
To enable ZeroConf for, e.g., eth0, use
|
||||||
|
|
||||||
|
$ initctl enable avahi-autoipd@eth0.conf
|
||||||
|
|
||||||
|
The enabled symlink will be set up to avahi-autoipd@.conf and every in‐
|
||||||
|
stance of %i will in the instantiated directive be replaced with eth0.
|
||||||
|
Inspect the result with:
|
||||||
|
|
||||||
|
$ initctl status avahi-autoipd:eth0
|
||||||
|
|
||||||
|
|
||||||
Cgroups
|
Cgroups
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -227,6 +446,31 @@ Linux kernel, or the name is misspelled.
|
|||||||
Configuration File Syntax
|
Configuration File Syntax
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
The file format is line based, empty lines and comments, lines starting
|
||||||
|
with `#`, are ignored. A configuration directive starts with a keyword
|
||||||
|
followed by a space and the rest of the line is treated as the value.
|
||||||
|
|
||||||
|
As of Finit v4.4, configuration directives can be broken up in multiple
|
||||||
|
lines using the continuation character `\`, and trailing comments are
|
||||||
|
also allowed. Example:
|
||||||
|
|
||||||
|
```aconf
|
||||||
|
# Escape \# chars if you want them literal in, e.g., descriptions
|
||||||
|
service name:sysklogd [S123456789] \
|
||||||
|
env:-/etc/default/sysklogd \
|
||||||
|
syslogd -F $SYSLOGD_ARGS \
|
||||||
|
-- System log daemon \# 1 # Comments allowed now
|
||||||
|
```
|
||||||
|
|
||||||
|
The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support the
|
||||||
|
following directives:
|
||||||
|
|
||||||
|
|
||||||
|
> **Note:** not all directives are available in `/etc/finit.d/*.conf`
|
||||||
|
> and some directives are only available at [bootstrap][], runlevel `S`,
|
||||||
|
> see [Limitations](#limitations) below for details.
|
||||||
|
|
||||||
|
|
||||||
### Hostname
|
### Hostname
|
||||||
|
|
||||||
**Syntax:** `host <NAME>`, or `hostname <NAME>`
|
**Syntax:** `host <NAME>`, or `hostname <NAME>`
|
||||||
@@ -270,6 +514,27 @@ 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][]).
|
||||||
|
|
||||||
|
|
||||||
|
### Alternate finit.d/
|
||||||
|
|
||||||
|
**Syntax:** `rcsd /path/to/finit.d`
|
||||||
|
|
||||||
|
The Finit rcS.d directory is set at compile time with:
|
||||||
|
|
||||||
|
./configure --with-rcsd=/etc/finit.d
|
||||||
|
|
||||||
|
A system with multiple use-cases may be bootstrapped with different
|
||||||
|
configurations, starting with the kernel command line option:
|
||||||
|
|
||||||
|
-- finit.config=/etc/factory.conf
|
||||||
|
|
||||||
|
This file in turn can use the `rcsd` directive to tell Finit to use
|
||||||
|
another set of .conf files, e.g.:
|
||||||
|
|
||||||
|
rcsd /etc/factory.d
|
||||||
|
|
||||||
|
> **Note:** this directive is only available from the top-level
|
||||||
|
> bootstrap .conf file.
|
||||||
|
|
||||||
### Resource Limits
|
### Resource Limits
|
||||||
|
|
||||||
**Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>`
|
**Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>`
|
||||||
@@ -305,7 +570,10 @@ 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
|
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
||||||
halt.
|
halt.
|
||||||
|
|
||||||
Default: 2
|
It is recommended to keep runlevel 1 as single-user mode, because
|
||||||
|
Finit disables networking in this mode.
|
||||||
|
|
||||||
|
*Default:* 2
|
||||||
|
|
||||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||||
|
|
||||||
@@ -314,19 +582,37 @@ Default: 2
|
|||||||
|
|
||||||
**Syntax:** `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
**Syntax:** `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
||||||
|
|
||||||
One-shot command to run in sequence when entering a runlevel, with
|
|
||||||
optional arguments and description.
|
|
||||||
|
|
||||||
`run` commands are guaranteed to be completed before running the next
|
|
||||||
command. Highly useful if true serialization is needed.
|
|
||||||
|
|
||||||
> `<COND>` is described in the [Services](#services) section.
|
> `<COND>` is described in the [Services](#services) section.
|
||||||
|
|
||||||
|
One-shot command to run in sequence when entering a runlevel, with
|
||||||
|
optional arguments and description. `run` commands are guaranteed to be
|
||||||
|
completed before running the next command. Useful when serialization is
|
||||||
|
required.
|
||||||
|
|
||||||
|
> **Warning:** try to avoid the `run` command. It blocks much of the
|
||||||
|
> functionality in Finit, like (re)starting other (perhaps crashing)
|
||||||
|
> services while a `run` task is executing. Use other synchronization
|
||||||
|
> mechanisms instead, like conditions.
|
||||||
|
|
||||||
|
Incomplete list of unsupported `initctl` commands in `run` tasks:
|
||||||
|
|
||||||
|
- `initctl runlevel N`, setting runlevel
|
||||||
|
- `initctl reboot`
|
||||||
|
- `initctl halt`
|
||||||
|
- `initctl poweroff`
|
||||||
|
- `initctl suspend`
|
||||||
|
|
||||||
|
To prevent `initctl` from calling Finit when enabling and disabling
|
||||||
|
services from inside a `run` task, use the `--force` option. See
|
||||||
|
also the `--quiet` and `--batch` options.
|
||||||
|
|
||||||
|
|
||||||
### One-shot Commands (parallel)
|
### One-shot Commands (parallel)
|
||||||
|
|
||||||
**Syntax:** `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
**Syntax:** `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
||||||
|
|
||||||
|
> `<COND>` is described in the [Services](#services) section.
|
||||||
|
|
||||||
One-shot like 'run', but starts in parallel with the next command.
|
One-shot like 'run', but starts in parallel with the next command.
|
||||||
|
|
||||||
Both `run` and `task` commands are run in a shell, so pipes and
|
Both `run` and `task` commands are run in a shell, so pipes and
|
||||||
@@ -334,13 +620,13 @@ redirects can be freely used:
|
|||||||
|
|
||||||
task [s] echo "foo" | cat >/tmp/bar
|
task [s] echo "foo" | cat >/tmp/bar
|
||||||
|
|
||||||
> `<COND>` is described in the [Services](#services) section.
|
|
||||||
|
|
||||||
|
|
||||||
### SysV Init Scripts
|
### SysV Init Scripts
|
||||||
|
|
||||||
**Syntax:** `sysv [LVLS] <COND> /path/to/init-script -- Optional description`
|
**Syntax:** `sysv [LVLS] <COND> /path/to/init-script -- Optional description`
|
||||||
|
|
||||||
|
> `<COND>` is described in the [Services](#services) section.
|
||||||
|
|
||||||
Similar to `task` is the `sysv` stanza, which can be used to call SysV
|
Similar to `task` is the `sysv` stanza, which can be used to call SysV
|
||||||
style scripts. The primary intention for this command is to be able to
|
style scripts. The primary intention for this command is to be able to
|
||||||
re-use much of existing setup and init scripts in Linux distributions.
|
re-use much of existing setup and init scripts in Linux distributions.
|
||||||
@@ -358,7 +644,7 @@ file, but rather watch that file for the resulting forked-off PID. This
|
|||||||
syntax also works for forking daemons that do not have a command line
|
syntax also works for forking daemons that do not have a command line
|
||||||
option to run it in the foreground, more on this below in `service`.
|
option to run it in the foreground, more on this below in `service`.
|
||||||
|
|
||||||
> `<COND>` is described in the [Services](#services) section.
|
> **Tip:** see also [SysV Init Compatibility](#sysv-init-compatibility).
|
||||||
|
|
||||||
|
|
||||||
### Services
|
### Services
|
||||||
@@ -419,10 +705,56 @@ modified with an optional `pid:`-argument:
|
|||||||
|
|
||||||
pid[:[/path/to/]filename[.pid]]
|
pid[:[/path/to/]filename[.pid]]
|
||||||
|
|
||||||
For example, by adding `pid:/run/foo.pid` to the service `/sbin/bar`,
|
For example, by adding `pid:/run/bar.pid` to the service `/sbin/bar`,
|
||||||
that PID file will, not only be created and removed automatically, but
|
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
|
also be used by the Finit condition subsystem. So a service/run/task
|
||||||
can depend on `<pid/bar>`.
|
can depend on `<pid/bar>`, like this foo will not be started until bar
|
||||||
|
has started:
|
||||||
|
|
||||||
|
service pid:/run/bar.pid bar -- Bar Service
|
||||||
|
service <pid/bar> foo -- Foo Service
|
||||||
|
|
||||||
|
Needless to say, it is better if `bar` creates its own PID file when it
|
||||||
|
has completed starting up and is ready for service.
|
||||||
|
|
||||||
|
As an alternative "readiness" notification, Finit supports both systemd
|
||||||
|
and s6 style notification. This can be enabled by using the `notify`
|
||||||
|
option:
|
||||||
|
|
||||||
|
* `notify:systemd` -- tells Finit the service uses the `sd_notify()`
|
||||||
|
API to signal PID 1 when it has completed its startup and is ready
|
||||||
|
to service events. The [sd_notify()][] API expects `NOTIFY_SOCKET`
|
||||||
|
to be set to the socket where the application can send `"READY=1\n"`
|
||||||
|
when it is starting up or has processed a `SIGHUP`.
|
||||||
|
* `notify:s6` -- puts Finit in s6 compatibility mode. Compared to the
|
||||||
|
systemd notification, [s6 expect][] compliant daemons to send `"\n"`
|
||||||
|
and then close their socket. Finit takes care of "hard-wiring" the
|
||||||
|
READY state as long as the application is running, events across any
|
||||||
|
`SIGHUP`. Since s6 can give its applications the descriptor number
|
||||||
|
(must be >3) on then command line, Finit provides the following
|
||||||
|
syntax (`%n` is replaced by Finit with then descriptor number):
|
||||||
|
|
||||||
|
service [S12345789] notify:s6 mdevd -O 4 -D %n
|
||||||
|
|
||||||
|
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||||
|
[s6 expect]: https://skarnet.org/software/s6/notifywhenup.html
|
||||||
|
|
||||||
|
When a service is ready, either by Finit detecting its PID file, or
|
||||||
|
their respective readiness mechanism has been triggered, Finit creates
|
||||||
|
then service's ready condition which other services can depend on:
|
||||||
|
|
||||||
|
$ initctl -v cond get service/mdevd/ready
|
||||||
|
on
|
||||||
|
|
||||||
|
This can be used to synchronize the start of another run/task/service:
|
||||||
|
|
||||||
|
task [S] <service/mdevd/ready> @root:root mdevd-coldplug
|
||||||
|
|
||||||
|
Finit waits for `mdevd` to notify it, before starting `mdevd-coldplug`.
|
||||||
|
Notice how both start in runlevel S, and the coldplug task only runs in
|
||||||
|
S. When the system moves to runlevel 2 (the default), coldplug is no
|
||||||
|
longer part of the running configuration (`initctl show`), this is to
|
||||||
|
ensure that coldplug is not called more than once.
|
||||||
|
|
||||||
> For a detailed description of conditions, and how to debug them,
|
> For a detailed description of conditions, and how to debug them,
|
||||||
> see the [Finit Conditions](conditions.md) document.
|
> see the [Finit Conditions](conditions.md) document.
|
||||||
@@ -439,6 +771,24 @@ optional `name` argument:
|
|||||||
|
|
||||||
name:<service-name>
|
name:<service-name>
|
||||||
|
|
||||||
|
If multiple instances of a service, with the same `name`, exist. Set
|
||||||
|
the identifier `:ID` to prevent Finit from replacing previous instances:
|
||||||
|
|
||||||
|
service :eth1 ssdpd eth1 -- Windows discovery on eth1
|
||||||
|
service :eth2 ssdpd eth2 -- Windows discovery on eth2
|
||||||
|
|
||||||
|
If you have conflicting services and want to prevent them from starting,
|
||||||
|
use the `conflict:` argument:
|
||||||
|
|
||||||
|
service [S12345789] udevd -- Device event management daemon
|
||||||
|
run [S] conflict:udevd mdev -s -- Populating device tree
|
||||||
|
|
||||||
|
Multiple conflicting services can be separated using `,`:
|
||||||
|
|
||||||
|
service :1 abc
|
||||||
|
service :2 abc
|
||||||
|
service conflict:abc:1,abc:2 cde
|
||||||
|
|
||||||
As mentioned previously, services are automatically restarted, this is
|
As mentioned previously, services are automatically restarted, this is
|
||||||
configurable with the following options:
|
configurable with the following options:
|
||||||
|
|
||||||
@@ -458,7 +808,8 @@ configurable with the following options:
|
|||||||
was originally designed for so not the default behavior
|
was originally designed for so not the default behavior
|
||||||
* `oncrash:reboot` -- when all retries have failed, and the service
|
* `oncrash:reboot` -- when all retries have failed, and the service
|
||||||
has *crashed*, if this option is set the system is rebooted.
|
has *crashed*, if this option is set the system is rebooted.
|
||||||
Note, future releases may include other `oncrash:` actions
|
* `oncrash:script` -- similarly, but instead of rebooting, call the
|
||||||
|
`post:script` action if set, see below.
|
||||||
|
|
||||||
When stopping a service (run/task/sysv/service), either manually or
|
When stopping a service (run/task/sysv/service), either manually or
|
||||||
when moving to another runlevel, Finit starts by sending `SIGTERM`, to
|
when moving to another runlevel, Finit starts by sending `SIGTERM`, to
|
||||||
@@ -484,12 +835,9 @@ with an additional set of environment variables:
|
|||||||
the program, if it exited normally, or the signal name (`HUP`,
|
the program, if it exited normally, or the signal name (`HUP`,
|
||||||
`TERM`, etc.) if it exited due to signal
|
`TERM`, etc.) if it exited due to signal
|
||||||
|
|
||||||
The scripts have a default execution time of 3 seconds before they are
|
These script actions *must terminate*, so they have a default execution
|
||||||
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
|
time of 3 seconds before they are SIGKILLed, this can be adjusted using
|
||||||
|
the above `kill:SEC` syntax.
|
||||||
> **Note:** the `pre:script` _must_ be idempotent, because a service
|
|
||||||
> can transition between READY and HALTED states any number
|
|
||||||
> of times before going to RUNNING.
|
|
||||||
|
|
||||||
|
|
||||||
### Run-parts Scripts
|
### Run-parts Scripts
|
||||||
@@ -501,15 +849,48 @@ files, or scripts, in the directory are called, in alphabetic order.
|
|||||||
The scripts in this directory are executed at the very end of runlevel
|
The scripts in this directory are executed at the very end of runlevel
|
||||||
`S`, [bootstrap][].
|
`S`, [bootstrap][].
|
||||||
|
|
||||||
It can be beneficial to use `S01name`, `S02othername`, etc. if there
|
A common use-case for runparts scripts is to create and enable/disable
|
||||||
is a dependency order between the scripts. Symlinks to existing
|
services, which Finit will then apply when changing runlevel from S to
|
||||||
daemons can talso be used, but make sure they daemonize by default.
|
whatever the next runlevel is set to be (default 2). E.g., generate a
|
||||||
|
`/etc/chrony.conf` and call `initctl enable chronyd`.
|
||||||
|
|
||||||
Similar to the `/etc/rc.local` shell script, make sure that all your
|
**Limitations:**
|
||||||
services and programs either terminate or start in the background or
|
|
||||||
you will block Finit.
|
|
||||||
|
|
||||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
Scripts called from `runparts`, or hook scripts (see below), are limited
|
||||||
|
in what they can do and expect from Finit:
|
||||||
|
|
||||||
|
1. Finit is single threaded and all scripts executed from these two
|
||||||
|
mechanisms are limited to file system operations (early or late
|
||||||
|
hooks may not have write access!) and standard UNIX programs.
|
||||||
|
2. Thus, it is *not possible* to call `initctl` to query status of
|
||||||
|
services since that calls Finit over the `/run/finit/socket`.
|
||||||
|
3. Confusingly enough, there are some commands to `initctl` that are
|
||||||
|
available; `ls`, `enable`, `disable`, `touch`, `create`, `delete`,
|
||||||
|
and all the `cond` commands. Because all these are basically just
|
||||||
|
wrappers for manipulating files (again, write access for early/late
|
||||||
|
hooks may not be available).
|
||||||
|
|
||||||
|
Also, 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:** `runparts` scrips are only read and executed in runlevel S
|
||||||
|
> ([bootstrap][]). See [hook scripts](plugins.md#hooks) for other ways
|
||||||
|
> to run scripts at certain points during the complete lifetime of the
|
||||||
|
> system.
|
||||||
|
|
||||||
|
**Recommendations:**
|
||||||
|
|
||||||
|
It can be beneficial to use `01-name`, `02-othername`, etc., to ensure
|
||||||
|
the scripts are started in that order, e.g., if there is a dependency
|
||||||
|
order between scripts. Symlinks to existing daemons can talso be used,
|
||||||
|
but make sure they daemonize (background) themselves properly, otherwise
|
||||||
|
Finit will lock up.
|
||||||
|
|
||||||
|
If `S[0-9]foo` and `K[0-9]bar` style naming is used, the executable will
|
||||||
|
be called with an extra argument, `start` and `stop`, respectively.
|
||||||
|
E.g., `S01foo` will be called as `S01foo start`. Of course, `S01foo`
|
||||||
|
and `K01foo` may be a symlink to to `another/directory/foo`.
|
||||||
|
|
||||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||||
|
|
||||||
@@ -668,6 +1049,91 @@ Log rotation is controlled using the global `log` setting.
|
|||||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||||
|
|
||||||
|
|
||||||
|
### Misc Settings
|
||||||
|
|
||||||
|
**Syntax:** `reboot-delay <0-60>`
|
||||||
|
|
||||||
|
Optional delay at reboot (or shutdown or halt) to allow kernel
|
||||||
|
filesystem threads to complete after calling `sync(2)` before
|
||||||
|
rebooting. This applies primarily to filesystems that do not
|
||||||
|
have a reboot notifier implemented. At the point of writing,
|
||||||
|
the only known filesystems affected are: ubifs, jffs2.
|
||||||
|
|
||||||
|
*Default:* 0 (disabled)
|
||||||
|
|
||||||
|
When enabled (non-zero), this delay runs after file systems have been
|
||||||
|
unmounted and the root filesystem has been remounted read-only, and
|
||||||
|
sync(2) has been called, twice.
|
||||||
|
|
||||||
|
> "On Linux, sync is only guaranteed to schedule the dirty blocks for
|
||||||
|
> writing; it can actually take a short time before all the blocks are
|
||||||
|
> finally written.
|
||||||
|
|
||||||
|
|
||||||
|
SysV Init Compatibility
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Finit comes with a few SysV Init compatibility features to ease the
|
||||||
|
transition from a serialized boot process.
|
||||||
|
|
||||||
|
### `runparts DIRECTORY`
|
||||||
|
|
||||||
|
For a directory with traditional start/stop scripts that should run, in
|
||||||
|
order, at bootstrap, Finit provides the `runparts` directive. It runs
|
||||||
|
in runlevel S, at the very end of it (before calling `/etc/rc.local`)
|
||||||
|
making it perfect for most scenarios.
|
||||||
|
|
||||||
|
For syntax details, see [Run-parts Scripts](#run-parts-scripts) above.
|
||||||
|
Here is an example take from a Debian installation:
|
||||||
|
|
||||||
|
runparts /etc/rc2.d
|
||||||
|
|
||||||
|
Files in these directories are usually named `SNNfoo` and `KNNfoo`,
|
||||||
|
which Finit knows about and automatically appends the correct argument:
|
||||||
|
|
||||||
|
/bin/sh -c /etc/rc2.d/S01openbsd-inetd start
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
/bin/sh -c /etc/rc0.d/K01openbsd-inetd stop
|
||||||
|
|
||||||
|
Files that do not match this pattern are started similarly but without
|
||||||
|
the extra command line argument.
|
||||||
|
|
||||||
|
|
||||||
|
### Start/Stop Scripts
|
||||||
|
|
||||||
|
For syntax details, see [SysV Init Scripts](#sysv-init-scripts), above.
|
||||||
|
Here follows an example taken from a Debian installation:
|
||||||
|
|
||||||
|
sysv [2345] <pid/syslogd> /etc/init.d/openbsd-inetd -- OpenBSD inet daemon
|
||||||
|
|
||||||
|
The init script header could be parsed to extract `Default-Start:` and
|
||||||
|
other parameters for the `sysv` command to Finit. There is currently no
|
||||||
|
way to detail a generic syslogd dependency in Finit, so `Should-Start:`
|
||||||
|
in the header must be mapped to the condition system in Finit using an
|
||||||
|
absolute reference, here we depend on the sysklogd project's syslogd.
|
||||||
|
|
||||||
|
### `/etc/rc.local`
|
||||||
|
|
||||||
|
One often requested feature, early on, was a way to run a site specific
|
||||||
|
script to set up, e.g., static routes or firewall rules. A user can add
|
||||||
|
a `task` or `run` command in the Finit configuration for this, but for
|
||||||
|
compatibility reasons the more widely know `/etc/rc.local` is used if
|
||||||
|
it exists, and is executable. It is called very late in the boot process
|
||||||
|
when the system has left runlevel S, stopped all old and started all new
|
||||||
|
services in the target runlevel (default 2).
|
||||||
|
|
||||||
|
> In Finit releases before v4.5 this script blocked Finit execution and
|
||||||
|
> made it as good as impossible to call `initctl` during that time.
|
||||||
|
|
||||||
|
### `init q`
|
||||||
|
|
||||||
|
When `/sbin/finit` is installed as `/sbin/init`, it is possible to use
|
||||||
|
`init q` to reload the configuration. This is the same as calling
|
||||||
|
`initctl reload`.
|
||||||
|
|
||||||
|
|
||||||
Rescue Mode
|
Rescue Mode
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@@ -675,6 +1141,10 @@ 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
|
on the kernel command line. See [cmdline docs](cmdline.md) for how to
|
||||||
activate it.
|
activate it.
|
||||||
|
|
||||||
|
This rescue mode can be disabled at configure time using:
|
||||||
|
|
||||||
|
configure --without-rescue
|
||||||
|
|
||||||
The rescue mode comes in two flavors; *traditional* and *fallback*.
|
The rescue mode comes in two flavors; *traditional* and *fallback*.
|
||||||
|
|
||||||
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to
|
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to
|
||||||
@@ -684,15 +1154,17 @@ The rescue mode comes in two flavors; *traditional* and *fallback*.
|
|||||||
### Traditional
|
### Traditional
|
||||||
|
|
||||||
This is what most users expect. A very early maintenance login prompt,
|
This is what most users expect. A very early maintenance login prompt,
|
||||||
served by the bundled `/libexec/finit/sulogin` program, or the standard
|
served by the system `sulogin` program from util-linux, or BusyBox. If
|
||||||
`sulogin` from util-linux or BusyBox is searched for in the UNIX default
|
that is not found in `$PATH`, the bundled `/libexec/finit/sulogin`
|
||||||
`$PATH`. If a successful login is made, or the user exits (Ctrl-D), the
|
program is used instead. If a successful login is made, or if the user
|
||||||
rescue mode is ended and the system boots up normally.
|
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
|
> **Note:** the bundled sulogin in Finit can at configure time be given
|
||||||
> __the account is locked__, the user is presented with a password-less
|
> another user than the default (root). If the sulogin user does not
|
||||||
> `"Press enter to enter maintenance mode."`, prompt which opens up a
|
> have a password, or __the account is locked__, the user is presented
|
||||||
> root shell.
|
> with a password-less `"Press enter to enter maintenance mode."`,
|
||||||
|
> prompt which opens up a root shell.
|
||||||
|
|
||||||
|
|
||||||
### Fallback
|
### Fallback
|
||||||
@@ -741,9 +1213,9 @@ There is, however, nothing preventing you from having all configuration
|
|||||||
settings in `/etc/finit.conf`.
|
settings in `/etc/finit.conf`.
|
||||||
|
|
||||||
> **Note:** The `/etc/finit.d` directory was previously the default
|
> **Note:** The `/etc/finit.d` directory was previously the default
|
||||||
> Finit [runparts](#run-parts-scripts) directory. Finit no
|
> Finit [runparts](#run-parts-scripts) directory. Finit >=v4.0
|
||||||
> longer has a default `runparts`, make sure to update your
|
> no longer has a default `runparts` directory, make sure to
|
||||||
> setup, or the finit configuration, accordingly.
|
> update your setup, or the finit configuration, accordingly.
|
||||||
|
|
||||||
|
|
||||||
Watchdog
|
Watchdog
|
||||||
|
|||||||
+121
-37
@@ -8,7 +8,8 @@ Hooks & Plugins
|
|||||||
* [Shutdown Hooks](#shutdown-hooks)
|
* [Shutdown Hooks](#shutdown-hooks)
|
||||||
|
|
||||||
Finit can be extended to add general functionality in the form of I/O
|
Finit can be extended to add general functionality in the form of I/O
|
||||||
monitors, or hook plugins.
|
monitors, or hook plugins. It is even possible to run scripts at these
|
||||||
|
hook points, see below.
|
||||||
|
|
||||||
The following sections detail existing plugins and hook points. For
|
The following sections detail existing plugins and hook points. For
|
||||||
more information, see the plugins listed below.
|
more information, see the plugins listed below.
|
||||||
@@ -35,34 +36,59 @@ For your convenience a set of *optional* plugins are available:
|
|||||||
_Optional plugin._
|
_Optional plugin._
|
||||||
|
|
||||||
* *hook-scripts.so*: Trigger the execution of scripts from plugin hook
|
* *hook-scripts.so*: Trigger the execution of scripts from plugin hook
|
||||||
points (see [Hooks](#hooks)). Scripts are located in
|
points (see [Hooks](#hooks)). _Optional plugin._
|
||||||
`/libexec/finit/hook` by default, this can be build-time customized
|
|
||||||
by using the `--with-hook-scripts-path=PATH` argument to
|
This plugin is particularly useful for early boot debugging that needs
|
||||||
`configure`. _Optional plugin._
|
to take place before regular services are available.
|
||||||
|
|
||||||
|
For example, say that you want to enable some kernel tracing before
|
||||||
|
modules are loaded. With hook scripts, you can just drop in a shell
|
||||||
|
script in `/libexec/finit/hook/mount/all/` that will poke the right
|
||||||
|
control files in tracefs.
|
||||||
|
|
||||||
|
Scripts are located in `/libexec/finit/hook` by default, this can be
|
||||||
|
customized at build-time using the `--with-hook-scripts-path=PATH`
|
||||||
|
argument to `configure`.
|
||||||
|
|
||||||
* *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
|
* *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
|
||||||
available. Enabled by default.
|
available. Enabled by default.
|
||||||
|
|
||||||
|
> See the [Services](config.md#services) section in the configuration
|
||||||
|
> guide for an example how to run `mdevd`, alternative to plain mdev.
|
||||||
|
|
||||||
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
|
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
|
||||||
Enabled by default.
|
Enabled by default.
|
||||||
|
|
||||||
* *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to
|
* *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to
|
||||||
load using `modprobe`. Each file can contain multiple lines with the
|
load using `modprobe`. Each file can contain multiple lines with the
|
||||||
name of the module to load. Any line starting with the standard UNIX
|
name of the module to load. Any line starting with the standard UNIX
|
||||||
comment character, `#`, is skipped.
|
comment character, `#`, or `;`, is skipped.
|
||||||
|
|
||||||
Modules are loaded when entering runlevel `[2345]` using the `task`
|
Modules are by default loaded in runlevel `S` using the `task` stanza.
|
||||||
stanza. Each module gets a unique `name:modprobe.foo`, and
|
Each module gets a unique `name:modprobe.foo`, and optional`:ID`. The
|
||||||
optional`:ID`. The `:ID` is a globally incremented index, which can
|
runlevel can be changed per file using:
|
||||||
be disabled per file (anywhere) using the following config line:
|
|
||||||
|
set runlevel 2345
|
||||||
|
|
||||||
|
The `:ID` is a globally incremented index, which can be disabled per
|
||||||
|
file (anywhere) using the following config line:
|
||||||
|
|
||||||
set noindex
|
set noindex
|
||||||
|
|
||||||
|
To change the index used by the plugin:
|
||||||
|
|
||||||
|
set index 1234
|
||||||
|
|
||||||
**Note:** unlike the traditional .conf `module` directive, which load
|
**Note:** unlike the traditional .conf `module` directive, which load
|
||||||
any listed module immediately, this plugin creates standard `taks`
|
any listed module immediately, this plugin creates a background `task`
|
||||||
directives which load the module(s) in the background. As long as
|
which load the module(s) in the background. The program is modprobe,
|
||||||
the `modprobe` program is found in the path, these tasks will always
|
`/sbin/modprobe`, which you can override per .conf file:
|
||||||
return `[ OK ]` at boot. Check the actual status using `initctl`.
|
|
||||||
|
set modprobe /path/to/maybe-a-modprobe-wrapper
|
||||||
|
|
||||||
|
Since these tasks run in the background, they usually return `[ OK ]`
|
||||||
|
at boot, unless the modprobe tool does not exist. Check syslog for
|
||||||
|
warnings and the actual status of the operation using `initctl`.
|
||||||
|
|
||||||
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
||||||
interfaces. These events are then sent to the Finit service monitor
|
interfaces. These events are then sent to the Finit service monitor
|
||||||
@@ -91,43 +117,101 @@ state, etc. Available hook points are:
|
|||||||
Hooks
|
Hooks
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
In the below listings, the first label is the hook point for a C plugin,
|
||||||
|
the second is the condition name and hook script path. A hook script is
|
||||||
|
a plain shell script, or program, that does a very small dedicated job
|
||||||
|
at the below hook points.
|
||||||
|
|
||||||
|
See [Run-parts Scripts](config.md#run-parts-scripts) for details on the
|
||||||
|
requirements, possibilities, and *limitations* of hook scripts.
|
||||||
|
|
||||||
|
All hook scripts are called with at least one environment variable set,
|
||||||
|
the hook name, useful when reusing the same hook script for multiple
|
||||||
|
hook points:
|
||||||
|
|
||||||
|
- `FINIT_HOOK_NAME`: set to the second label, e.g., `hook/net/up`
|
||||||
|
- `FINIT_SHUTDOWN`: set for `hook/sys/shutdown` and later to one
|
||||||
|
of `halt`, `poweroff`, or `reboot`.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
$ mkdir -p /libexec/finit/hook/sys/down
|
||||||
|
$ cat <<EOF >/libexec/finit/hook/sys/down/foo.sh
|
||||||
|
#!/bin/sh
|
||||||
|
echo 'I run just before the reboot() syscall at shutdown/reboot'
|
||||||
|
echo 'I have access to /dev since devtmpfs is exempt from umount'
|
||||||
|
echo "FINIT_HOOK_NAME: $FINIT_HOOK_NAME"
|
||||||
|
if [ -n "$FINIT_SHUTDOWN" ]; then
|
||||||
|
echo "FINIT_SHUTDOWN: $FINIT_SHUTDOWN"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
$ chmod +x /libexec/finit/hook/sys/down/foo.sh
|
||||||
|
|
||||||
|
> **Note:** to use hook scripts, even for pre-bootstrap and pre-shutdown
|
||||||
|
> tasks, you must build with `configure --enable-hook-scripts-plugin`.
|
||||||
|
|
||||||
### Bootstrap Hooks
|
### Bootstrap Hooks
|
||||||
|
|
||||||
* `HOOK_BANNER`: The very first point at which a plugin can run.
|
* `HOOK_BANNER`, `hook/sys/banner`: The very first point at which a
|
||||||
Intended to be used as a banner replacement. Essentially this runs
|
plugin can run. Intended to be used as a banner replacement.
|
||||||
just before entering runlevel S. Assume nothing is available, so
|
Essentially this runs just before entering runlevel S. Assume nothing
|
||||||
be prepared to use absolute paths, etc.
|
is available, so be prepared to use absolute paths, etc.
|
||||||
|
|
||||||
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
|
* `HOOK_ROOTFS_UP`, `hook/mount/root`: When `finit.conf` has been read
|
||||||
mounted — very early
|
and `/` has is mounted — very early
|
||||||
|
|
||||||
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
|
* `HOOK_MOUNT_ERROR`, `hook/mount/error`: executed if `mount -a` fails
|
||||||
and default init signals are setup
|
|
||||||
|
|
||||||
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
|
* `HOOK_MOUNT_POST`, `hook/mount/post`: always executed after `mount -a`
|
||||||
networking is up (`lo` is up and the `network` script has run)
|
|
||||||
|
|
||||||
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
|
* `HOOK_BASEFS_UP`, `hook/mount/all`: All of `/etc/fstab` is mounted,
|
||||||
|
swap is available and default init signals are setup
|
||||||
|
|
||||||
* `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d`
|
* `HOOK_SVC_PLUGIN`, `hook/svc/plugin`: Called in `conf_init()` right
|
||||||
has been launched
|
before loading `/etc/finit.conf`. For plugins to register any early
|
||||||
|
run/task/services, please do NOT use any earlier hook point. That
|
||||||
|
will cause uninitialized rlimits that lead to unpredictable results
|
||||||
|
when Finit later tries to start the run/task/service.
|
||||||
|
|
||||||
|
* `HOOK_NETWORK_UP`, `hook/net/up`: System bootstrap, runlevel S, has
|
||||||
|
completed and networking is up (`lo` is up and the `network` script
|
||||||
|
has run)
|
||||||
|
|
||||||
|
* `HOOK_SVC_UP`, `hook/svc/up`: All services in the active runlevel have
|
||||||
|
been launched
|
||||||
|
|
||||||
|
* `HOOK_SYSTEM_UP`, `hook/sys/up`: All services *and* everything in
|
||||||
|
`/etc/finit.d` has been launched
|
||||||
|
|
||||||
### Runtime Hooks
|
### Runtime Hooks
|
||||||
|
|
||||||
* `HOOK_SVC_RECONF`: Called when the user has changed something in the
|
* `HOOK_SVC_RECONF`, N/A: Called when the user has changed something in
|
||||||
`/etc/finit.d` directory and issued `SIGHUP`. The hook is called when
|
the `/etc/finit.d` directory and issued `SIGHUP`. The hook is called
|
||||||
all modified/removed services have been stopped. When the hook has
|
when all modified/removed services have been stopped. When the hook
|
||||||
completed, Finit continues to start all modified and new services.
|
has completed, Finit continues to start all modified and new services.
|
||||||
|
|
||||||
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
|
* `HOOK_RUNLEVEL_CHANGE`, N/A: Called when the user has issued a
|
||||||
change. The hook is called when services not matching the new
|
runlevel change. The hook is called when services not matching the
|
||||||
runlevel have been been stopped. When the hook has completed, Finit
|
new runlevel have been been stopped. When the hook has completed,
|
||||||
continues to start all services in the new runlevel.
|
Finit continues to start all services in the new runlevel.
|
||||||
|
|
||||||
### Shutdown Hooks
|
### Shutdown Hooks
|
||||||
|
|
||||||
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
|
* `HOOK_NETWORK_DN`, `hook/net/down`: Called right after having changed
|
||||||
services are sent `SIGTERM`
|
to runlevel 6, or 0, when all services have received their 'stop' signal.
|
||||||
|
|
||||||
|
* `HOOK_SHUTDOWN`, `hook/sys/shutdown`: Called at shutdown/reboot, right
|
||||||
|
before all services are sent `SIGTERM`
|
||||||
|
|
||||||
|
* `HOOK_SVC_DN`, `hook/svc/down`: In shutdown/reboot, all services and
|
||||||
|
non-reserved processes have been killed. **Note:** only hook scripts
|
||||||
|
can run here!
|
||||||
|
|
||||||
|
* `HOOK_SYSTEM_DN`, `hook/sys/down`: In shutdown/reboot, called after
|
||||||
|
all non-reserved file systems have been unmounted, just before Finit
|
||||||
|
tells the kernel to reboot or shut down. **Note:** only hook scripts
|
||||||
|
can run here!
|
||||||
|
|
||||||
Plugins like `tty.so` extend finit by acting on events, they are called
|
Plugins like `tty.so` extend finit by acting on events, they are called
|
||||||
I/O plugins and are called from the finit main loop when `poll()`
|
I/O plugins and are called from the finit main loop when `poll()`
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 230 KiB |
+1
-1
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 164 KiB |
@@ -0,0 +1,141 @@
|
|||||||
|
_cond()
|
||||||
|
{
|
||||||
|
initctl -pt cond dump | awk '{print $4}' | sed 's/<\(.*\)>/\1/'
|
||||||
|
}
|
||||||
|
|
||||||
|
_ident()
|
||||||
|
{
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
initctl ident | grep -q $1
|
||||||
|
else
|
||||||
|
initctl ident
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_svc()
|
||||||
|
{
|
||||||
|
initctl ls -pt | grep $1 | sed "s/.*\/\(.*\)/\1/g" | sort -u
|
||||||
|
}
|
||||||
|
|
||||||
|
_enabled()
|
||||||
|
{
|
||||||
|
echo "$(_svc enabled)"
|
||||||
|
}
|
||||||
|
|
||||||
|
_available()
|
||||||
|
{
|
||||||
|
all=$(mktemp)
|
||||||
|
ena=$(mktemp)
|
||||||
|
echo "$(_svc available)" >$all
|
||||||
|
echo "$(_svc enabled)" >$ena
|
||||||
|
grep -v -f $ena $all
|
||||||
|
rm $all $ena
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine first non-option word. Usually the command
|
||||||
|
_firstword() {
|
||||||
|
local firstword i
|
||||||
|
|
||||||
|
firstword=
|
||||||
|
for ((i = 1; i < ${#COMP_WORDS[@]}; ++i)); do
|
||||||
|
if [[ ${COMP_WORDS[i]} != -* ]]; then
|
||||||
|
firstword=${COMP_WORDS[i]}
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo $firstword
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine last non-option word. Uusally a sub-command
|
||||||
|
_lastword() {
|
||||||
|
local lastword i
|
||||||
|
|
||||||
|
lastword=
|
||||||
|
for ((i = 1; i < ${#COMP_WORDS[@]}; ++i)); do
|
||||||
|
if [[ ${COMP_WORDS[i]} != -* ]] && [[ -n ${COMP_WORDS[i]} ]] && [[ ${COMP_WORDS[i]} != $cur ]]; then
|
||||||
|
lastword=${COMP_WORDS[i]}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo $lastword
|
||||||
|
}
|
||||||
|
|
||||||
|
_initctl()
|
||||||
|
{
|
||||||
|
local cur command
|
||||||
|
|
||||||
|
cur=${COMP_WORDS[COMP_CWORD]}
|
||||||
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
firstword=$(_firstword)
|
||||||
|
lastword=$(_lastword)
|
||||||
|
|
||||||
|
commands="status cond debug help kill ls log version list enable \
|
||||||
|
disable touch show cat edit create delete reload start \
|
||||||
|
stop restart signal cgroup ps top plugins runlevel reboot \
|
||||||
|
halt poweroff suspend utmp"
|
||||||
|
cond_cmds="set get clear status dump"
|
||||||
|
cond_types="hook net pid service task usr"
|
||||||
|
signals="int term hup stop tstp cont usr1 usr2 pwr"
|
||||||
|
options="-b --batch \
|
||||||
|
-c --create \
|
||||||
|
-d --debug \
|
||||||
|
-f --force \
|
||||||
|
-h --help \
|
||||||
|
-j --json \
|
||||||
|
-n --noerr \
|
||||||
|
-1 --once \
|
||||||
|
-p --plain \
|
||||||
|
-q --quiet \
|
||||||
|
-t --no-heading \
|
||||||
|
-v --verbose \
|
||||||
|
-V --version"
|
||||||
|
|
||||||
|
case "${firstword}" in
|
||||||
|
enable)
|
||||||
|
COMPREPLY=($(compgen -W "$(_available)" -- $cur))
|
||||||
|
;;
|
||||||
|
disable|touch)
|
||||||
|
COMPREPLY=($(compgen -W "$(_enabled)" -- $cur))
|
||||||
|
;;
|
||||||
|
show|cat|edit|delete)
|
||||||
|
COMPREPLY=($(compgen -W "$(_svc .)" -- $cur))
|
||||||
|
;;
|
||||||
|
start|stop|restart|log|status)
|
||||||
|
COMPREPLY=($(compgen -W "$(_ident)" -- $cur))
|
||||||
|
;;
|
||||||
|
signal|kill)
|
||||||
|
if $(_ident "${prev}"); then
|
||||||
|
COMPREPLY=($(compgen -W "$signals" -- $cur))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$(_ident)" -- $cur))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
cond)
|
||||||
|
case "${lastword}" in
|
||||||
|
set|clear)
|
||||||
|
compopt -o nospace
|
||||||
|
COMPREPLY=($(compgen -W "usr/" -- $cur))
|
||||||
|
;;
|
||||||
|
get)
|
||||||
|
COMPREPLY=($(compgen -W "$(_cond)" -- $cur))
|
||||||
|
;;
|
||||||
|
dump)
|
||||||
|
COMPREPLY=($(compgen -W "$cond_types" -- $cur))
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W "$cond_cmds" -- $cur))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W "$commands" -- $cur))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ $cur == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$options" -- $cur))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _initctl initctl
|
||||||
+5
-9
@@ -1,8 +1,6 @@
|
|||||||
# From https://github.com/collectd/collectd/blob/master/configure.ac
|
# From https://github.com/collectd/collectd/blob/master/configure.ac
|
||||||
# Dependency handling currently unused
|
# Dependency handling currently unused
|
||||||
|
|
||||||
define([toupper], [translit([$1], [a-z], [A-Z])])
|
|
||||||
define([sanitize], [translit([$1], [-], [_])])
|
|
||||||
define([enadis], [ifelse([$1], [yes], [disable], [enable])])
|
define([enadis], [ifelse([$1], [yes], [disable], [enable])])
|
||||||
|
|
||||||
# AC_PLUGIN(name, default, info)
|
# AC_PLUGIN(name, default, info)
|
||||||
@@ -10,10 +8,8 @@ define([enadis], [ifelse([$1], [yes], [disable], [enable])])
|
|||||||
AC_DEFUN([AC_PLUGIN],[
|
AC_DEFUN([AC_PLUGIN],[
|
||||||
m4_divert_once([HELP_ENABLE], [
|
m4_divert_once([HELP_ENABLE], [
|
||||||
Optional Plugins:])
|
Optional Plugins:])
|
||||||
enable_plugin="no"
|
define(PLUGGY, m4_translit([$1],[a-z-][A-Z-]))dnl
|
||||||
define(pluggy, sanitize($1))
|
AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [
|
||||||
define(PLUGGY, toupper(pluggy))
|
|
||||||
AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [
|
|
||||||
if test "x$enableval" = "xyes"; then
|
if test "x$enableval" = "xyes"; then
|
||||||
enable_plugin="yes"
|
enable_plugin="yes"
|
||||||
else
|
else
|
||||||
@@ -29,8 +25,8 @@ Optional Plugins:])
|
|||||||
enable_plugin="$enable_all_plugins"
|
enable_plugin="$enable_all_plugins"
|
||||||
fi])
|
fi])
|
||||||
if test "x$enable_plugin" = "xyes"; then
|
if test "x$enable_plugin" = "xyes"; then
|
||||||
AC_DEFINE([HAVE_]PLUGGY[_PLUGIN], 1, [Define to 1 if the $1 plugin is enabled.])
|
AC_DEFINE(HAVE_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, 1, [Define to 1 if the $1 plugin is enabled.])
|
||||||
plugins="$1 $plugins"
|
plugins="$1 $plugins"
|
||||||
fi
|
fi
|
||||||
AM_CONDITIONAL([BUILD_]PLUGGY[_PLUGIN], test "x$enable_plugin" = "xyes")
|
|
||||||
enable_pluggy="$enable_plugin"])
|
AM_CONDITIONAL(BUILD_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, test "x$enable_plugin" = "xyes")])
|
||||||
|
|||||||
+89
-2
@@ -2,7 +2,7 @@
|
|||||||
.\" First parameter, NAME, should be all caps
|
.\" First parameter, NAME, should be all caps
|
||||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||||
.\" other parameters are allowed: see man(7), man(1)
|
.\" other parameters are allowed: see man(7), man(1)
|
||||||
.Dd June 6, 2021
|
.Dd Apr 22, 2023
|
||||||
.\" Please adjust this date whenever revising the manpage.
|
.\" Please adjust this date whenever revising the manpage.
|
||||||
.Dt FINIT 8 SMM
|
.Dt FINIT 8 SMM
|
||||||
.Os Linux
|
.Os Linux
|
||||||
@@ -12,10 +12,16 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm /sbin/finit
|
.Nm /sbin/finit
|
||||||
.Op --
|
.Op --
|
||||||
|
.Op debug
|
||||||
|
.Op finit.cond=foo[,bar[,baz]]
|
||||||
|
.Op finit.config=/path/to/alt/finit.conf
|
||||||
.Op finit.debug
|
.Op finit.debug
|
||||||
.Op finit.fstab=/etc/fstab.alt
|
.Op finit.fstab=/etc/fstab.alt
|
||||||
.Op finit.status=<on,off>
|
.Op finit.status=<on,off>
|
||||||
.Op finit.status_style=<classic,modern>
|
.Op finit.status_style=<classic,modern>
|
||||||
|
.Op loglevel=<0-7>
|
||||||
|
.Op panic=SEC
|
||||||
|
.Op quiet
|
||||||
.Op rescue | recover
|
.Op rescue | recover
|
||||||
.Op single | S
|
.Op single | S
|
||||||
.Nm /sbin/telinit
|
.Nm /sbin/telinit
|
||||||
@@ -45,6 +51,15 @@ Sourcing environment files
|
|||||||
.It
|
.It
|
||||||
Conditions for network/process/custom dependencies
|
Conditions for network/process/custom dependencies
|
||||||
.It
|
.It
|
||||||
|
Process readiness notification for synchronizing system startup as well
|
||||||
|
as reconfiguration at runtime. Natively PID files are used, but systemd
|
||||||
|
.Cm sd_notify()
|
||||||
|
and s6 notification is also supported.
|
||||||
|
.It
|
||||||
|
Limited support for
|
||||||
|
.Xr tmpfiles.d 5 ,
|
||||||
|
no aging, attributes, or subvolumes
|
||||||
|
.It
|
||||||
Pre/Post script actions
|
Pre/Post script actions
|
||||||
.It
|
.It
|
||||||
Tooling to enable/disable services
|
Tooling to enable/disable services
|
||||||
@@ -73,6 +88,38 @@ PID 1. For more information on this, and available kernel command line
|
|||||||
options, see
|
options, see
|
||||||
.Xr bootparam 7 .
|
.Xr bootparam 7 .
|
||||||
.Bl -tag -width pid -offset indent
|
.Bl -tag -width pid -offset indent
|
||||||
|
.It Cm debug
|
||||||
|
Enable kernel debug. Debug messages are printed to the console until
|
||||||
|
Finit starts up, unless
|
||||||
|
.Cm loglevel=7
|
||||||
|
(below) is used.
|
||||||
|
.It Cm finit.cond=foo[,bar[,baz]]
|
||||||
|
set
|
||||||
|
.Cm <boot/foo>
|
||||||
|
condition, optionally multiple conditions can be set using the same
|
||||||
|
option, separated with a comma. Alternatively, multiple
|
||||||
|
.Cm foo.cond=arg
|
||||||
|
can be given. Each will result in a
|
||||||
|
.Cm <boot/arg>
|
||||||
|
condition being set to control the rest of the system bootstrap.
|
||||||
|
.Pp
|
||||||
|
Very useful for selecting different boot modes, e.g. manufacturing test,
|
||||||
|
firmware upgrade, or rescue mode.
|
||||||
|
.Pp
|
||||||
|
.Sy Note:
|
||||||
|
.Cm <boot/...>
|
||||||
|
conditions cannot be cleared with
|
||||||
|
.Nm initctl !
|
||||||
|
.It Cm finit.config=/path/to/alternative/finit.conf
|
||||||
|
override the compile-time bootstrap config file, default:
|
||||||
|
.Bd -unfilled -offset indent
|
||||||
|
./configure --with-config=/etc/finit.conf
|
||||||
|
.Ed
|
||||||
|
Useful when starting up in various rescue mode, factory, or
|
||||||
|
production test setups. Use the top-level configuration file
|
||||||
|
directive
|
||||||
|
.Cm rcsd Ar /path/to/finit.d
|
||||||
|
to override the default rcS.d directory.
|
||||||
.It Cm finit.debug
|
.It Cm finit.debug
|
||||||
Enable Finit debug messages on system console and log. Sometimes useful
|
Enable Finit debug messages on system console and log. Sometimes useful
|
||||||
when doing board bringup. Before the system log daemon has started,
|
when doing board bringup. Before the system log daemon has started,
|
||||||
@@ -121,6 +168,33 @@ Control finit boot progress, including banner. This used to be called
|
|||||||
which still works but is deprecated.
|
which still works but is deprecated.
|
||||||
.It Cm finit.status_style=<classic,modern>
|
.It Cm finit.status_style=<classic,modern>
|
||||||
Set Finit boot progress style, when enabled.
|
Set Finit boot progress style, when enabled.
|
||||||
|
.It Cm loglevel=<0-7>
|
||||||
|
set the kernel's log level, which is more granular than
|
||||||
|
.Cm debug .
|
||||||
|
Also, when
|
||||||
|
.Cm loglevel=7 ,
|
||||||
|
Finit will
|
||||||
|
.Em not disable
|
||||||
|
kernel logs to the console. This is very useful when debugging the
|
||||||
|
kernel at system bring-up. Since
|
||||||
|
.Cm loglevel=7
|
||||||
|
is the same as
|
||||||
|
.Cm debug
|
||||||
|
this means you have to use
|
||||||
|
.Cm quiet
|
||||||
|
for a quiet boot, until sysklogd takes over logging of kernel events.
|
||||||
|
.It Cm panic=SEC
|
||||||
|
By default the kernel does not reboot after a kernel panic. This
|
||||||
|
setting will cause a kernel reboot after
|
||||||
|
.Ar SEC
|
||||||
|
seconds.
|
||||||
|
.It Cm quiet
|
||||||
|
Suppress kernel logging to console, except for warnings and errors.
|
||||||
|
Also, see
|
||||||
|
.Cm loglevel
|
||||||
|
and
|
||||||
|
.Cm quiet
|
||||||
|
above.
|
||||||
.It Cm rescue | recover
|
.It Cm rescue | recover
|
||||||
Start rescue/maintenance mode. If your system comes with the bundled
|
Start rescue/maintenance mode. If your system comes with the bundled
|
||||||
.Cm sulogin
|
.Cm sulogin
|
||||||
@@ -139,6 +213,10 @@ the regular runlevel. All services and TTYs in
|
|||||||
will be started, e.g., a
|
will be started, e.g., a
|
||||||
.Cm tty [1] @console nologin
|
.Cm tty [1] @console nologin
|
||||||
configuration presents you with a root console without login.
|
configuration presents you with a root console without login.
|
||||||
|
.Pp
|
||||||
|
.Sy Note:
|
||||||
|
by definition, single user mode does not have networking enabled. This
|
||||||
|
is also true for runlevel 1.
|
||||||
.El
|
.El
|
||||||
.Sh BOOTING
|
.Sh BOOTING
|
||||||
.Pp
|
.Pp
|
||||||
@@ -192,7 +270,7 @@ file removed, service B is also stopped.
|
|||||||
.Pp
|
.Pp
|
||||||
The following condition families are available today:
|
The following condition families are available today:
|
||||||
.Pp
|
.Pp
|
||||||
.Bl -tag -width pid -offset indent
|
.Bl -tag -width service -offset indent
|
||||||
.It Cm net
|
.It Cm net
|
||||||
Linux netlink events, e.g. net/route/default, net/eth0/up, and
|
Linux netlink events, e.g. net/route/default, net/eth0/up, and
|
||||||
net/et0/running
|
net/et0/running
|
||||||
@@ -200,6 +278,14 @@ net/et0/running
|
|||||||
PID files basd on the service declaration
|
PID files basd on the service declaration
|
||||||
.Cm name:id ,
|
.Cm name:id ,
|
||||||
gives the condition pid/name:id
|
gives the condition pid/name:id
|
||||||
|
.It Cm service
|
||||||
|
Tracks run/task/service state stansitions, including readiness. E.g.,
|
||||||
|
.Cm service/foo/ready
|
||||||
|
can be used as a condition for service
|
||||||
|
.Cm bar ,
|
||||||
|
provided
|
||||||
|
.Cm foo
|
||||||
|
properly signals its readiness to Finit.
|
||||||
.It Cm sys
|
.It Cm sys
|
||||||
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
|
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
|
||||||
.It Cm usr
|
.It Cm usr
|
||||||
@@ -255,6 +341,7 @@ Runtime files, including the condition subsystem
|
|||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr finit.conf 5
|
.Xr finit.conf 5
|
||||||
.Xr initctl 8
|
.Xr initctl 8
|
||||||
|
.Xr tmpfiles.d 5
|
||||||
.Sh AUTHORS
|
.Sh AUTHORS
|
||||||
.Nm
|
.Nm
|
||||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||||
|
|||||||
+158
-47
@@ -2,7 +2,7 @@
|
|||||||
.\" First parameter, NAME, should be all caps
|
.\" First parameter, NAME, should be all caps
|
||||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||||
.\" other parameters are allowed: see man(7), man(1)
|
.\" other parameters are allowed: see man(7), man(1)
|
||||||
.Dd Oct 17, 2020
|
.Dd July 11, 2023
|
||||||
.Dt FINIT.CONF 5 SMM
|
.Dt FINIT.CONF 5 SMM
|
||||||
.Os Linux
|
.Os Linux
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -12,48 +12,79 @@
|
|||||||
.Nm /etc/finit.conf
|
.Nm /etc/finit.conf
|
||||||
.Nm /etc/finit.d/*.conf
|
.Nm /etc/finit.d/*.conf
|
||||||
.Nm /etc/finit.d/available/*.conf
|
.Nm /etc/finit.d/available/*.conf
|
||||||
|
.Nm /etc/finit.d/enabled/*.conf
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
.Nm finit
|
.Nm Finit
|
||||||
can be configured using only the original
|
based systems can be set up with a single file:
|
||||||
.Pa /etc/finit.conf
|
.Pa /etc/finit.conf .
|
||||||
file or in combination with
|
This is the traditional way of doing it which can be ideal for some
|
||||||
.Pa /etc/finit.d/*.conf .
|
setups since it gives a great overview of the system configuration.
|
||||||
Useful for package-based Linux distributions \(em each package can
|
|
||||||
provide its own "script" file.
|
|
||||||
.Pp
|
.Pp
|
||||||
.Bl -tag -width /etc/finit.d/*.conf -offset indent -compact
|
More comprehensive setups, however, require more careful planning.
|
||||||
.It Pa /etc/finit.conf
|
The recommendation is per-package
|
||||||
Main configuration file
|
.Cm *.conf
|
||||||
.It Pa /etc/finit.d/*.conf
|
files in
|
||||||
Snippets, usually one service per file
|
.Pa /etc/finit.d/available/ .
|
||||||
.El
|
This allows end users to enable and disable parts of the system
|
||||||
|
configuration at runtime. Finit tracks which tasks and services belong
|
||||||
|
to a given file, so that when the user calls
|
||||||
|
.Cm initctl reload
|
||||||
|
to activate the changes they have made, only the affected tasks and
|
||||||
|
services are stopped, started, or restarted. Hence, this approach is
|
||||||
|
useful for modern package-based Linux distributions.
|
||||||
.Pp
|
.Pp
|
||||||
Not all configuration directives are available in
|
The following sections describe the more of this in detail, starting
|
||||||
.Pa /etc/finit.d/*.conf
|
with files, the file format, and available directives. Remember to
|
||||||
and some directives are only available at bootstrap, runlevel
|
also visit the other manual pages (references at the bottom).
|
||||||
.Cm S ,
|
|
||||||
see the section
|
|
||||||
.Sx Limitations
|
|
||||||
below for details.
|
|
||||||
.Sh FILES
|
.Sh FILES
|
||||||
.Bl -tag -width /etc/finit.d/available/*.conf -compact
|
.Bl -tag -width /etc/finit.d/available/*.conf -compact
|
||||||
.It Pa /etc/finit.conf
|
.It Pa /etc/finit.conf
|
||||||
Main configuration file
|
Main configuration file, optional
|
||||||
.It Pa /etc/finit.d/*.conf
|
.It Pa /etc/finit.d/*.conf
|
||||||
Static service definitions
|
Static (system) service definitions
|
||||||
.It Pa /etc/finit.d/available/*.conf
|
.It Pa /etc/finit.d/available/*.conf
|
||||||
Available services
|
Available (installed) services
|
||||||
.It Pa /etc/finit.d/enabled/*.conf
|
.It Pa /etc/finit.d/enabled/*.conf
|
||||||
Enabled services, symlinks from available
|
Enabled services (symlink back)
|
||||||
.El
|
.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
|
.Pp
|
||||||
A configuration stanza must be on a single (long) line and
|
Static services,
|
||||||
.Em cannot
|
.Cm .conf
|
||||||
be broken up in multiple lines by use of a backslash as the last
|
files in
|
||||||
character.
|
.Pa /etc/finit.d/ ,
|
||||||
|
cannot be enabled or disabled using
|
||||||
|
.Cm initctl [enable | disable] service[.conf] .
|
||||||
|
An enabled service is a symlink back to the corresponding
|
||||||
|
.Cm ../available/
|
||||||
|
service
|
||||||
|
.Cm .conf
|
||||||
|
file. Please use the
|
||||||
|
.Cm initctl
|
||||||
|
tool to manage these symlinks to ensure proper operation.
|
||||||
|
.Pp
|
||||||
|
.Sy NOTE:
|
||||||
|
Previous versions of
|
||||||
|
.Nm Finit
|
||||||
|
created symlinks in
|
||||||
|
.Pa /etc/finit.d/
|
||||||
|
if
|
||||||
|
.Pa /etc/finit.d/enabled/
|
||||||
|
was missing, this is as of v4.4 not supported.
|
||||||
|
.Sh FILE FORMAT
|
||||||
|
The file format is line based, empty lines and comments, lines starting
|
||||||
|
with `#', are ignored. A configuration directive starts with a keyword
|
||||||
|
followed by a space and the rest of the line is treated as the value.
|
||||||
|
.Pp
|
||||||
|
As of Finit v4.4, configuration directives can be broken up in multiple
|
||||||
|
lines using the continuation character `\\', and trailing comments are
|
||||||
|
also allowed. Example:
|
||||||
|
.Bd -unfilled -offset indent
|
||||||
|
# Escape \\# chars if you want them literal in, e.g., descriptions
|
||||||
|
service name:sysklogd [S123456789] \\
|
||||||
|
env:-/etc/default/sysklogd \\
|
||||||
|
syslogd -F $SYSLOGD_ARGS \\
|
||||||
|
-- System log daemon \\# 1 # Comments allowed now
|
||||||
|
.Ed
|
||||||
.Sh DIRECTIVES
|
.Sh DIRECTIVES
|
||||||
This section lists all supported configuration directives. There also exist
|
This section lists all supported configuration directives. There also exist
|
||||||
deprecated directives, see the Markdown documentation for details on these.
|
deprecated directives, see the Markdown documentation for details on these.
|
||||||
@@ -108,6 +139,10 @@ is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
|||||||
halt. All other can be used by operating system administrators.
|
halt. All other can be used by operating system administrators.
|
||||||
Default: 2
|
Default: 2
|
||||||
.Pp
|
.Pp
|
||||||
|
It s recommended to keep runlevel 1 as single-user mode, because
|
||||||
|
.Nm
|
||||||
|
disables networking in this mode.
|
||||||
|
.Pp
|
||||||
.Sy Note:
|
.Sy Note:
|
||||||
only read and executed in runlevel S (bootstrap).
|
only read and executed in runlevel S (bootstrap).
|
||||||
.It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
|
.It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
|
||||||
@@ -275,6 +310,51 @@ can depend on the
|
|||||||
.Cm <pid/bar>
|
.Cm <pid/bar>
|
||||||
condition.
|
condition.
|
||||||
.Pp
|
.Pp
|
||||||
|
As an alternative "readiness" notification, Finit supports both systemd
|
||||||
|
and s6 style notification. This can be enabled by using the `notify`
|
||||||
|
option:
|
||||||
|
.Bl -tag -width 1n
|
||||||
|
.It Cm notify:systemd
|
||||||
|
tells Finit the service uses the
|
||||||
|
.Cm sd_notify()
|
||||||
|
API to signal PID 1 when it has completed its startup and is ready
|
||||||
|
to service events. This API expects
|
||||||
|
the environment variable
|
||||||
|
.Cm NOTIFY_SOCKET
|
||||||
|
to be set to the socket where the application can send
|
||||||
|
.Cm "READY=1\n"
|
||||||
|
when it is starting up or has processed a
|
||||||
|
.Cm SIGHUP .
|
||||||
|
For details, see:
|
||||||
|
.Pp
|
||||||
|
.Lk https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||||
|
.It Cm notify:s6
|
||||||
|
puts Finit in s6 compatibility mode. Compared to the systemd
|
||||||
|
notification, s6 expect compliant daemons to send
|
||||||
|
.Cm "\\n"
|
||||||
|
and then close their socket. For details, see:
|
||||||
|
.Pp
|
||||||
|
.Lk https://skarnet.org/software/s6/notifywhenup.html
|
||||||
|
.Pp
|
||||||
|
Finit takes care of "hard-wiring" the READY state as long as the
|
||||||
|
application is running, events across any `SIGHUP`. Since s6 can give
|
||||||
|
its applications the descriptor number (must be >3) on then command
|
||||||
|
line, Finit provides the following syntax (
|
||||||
|
.Cm %n
|
||||||
|
is replaced by Finit with then descriptor number):
|
||||||
|
.Bd -unfilled -offset indent
|
||||||
|
service notify:s6 mdevd -C -O 4 -D %n
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
When a service is ready, either by Finit detecting its PID file, or
|
||||||
|
their respective readiness mechanism has been triggered, Finit creates
|
||||||
|
then service's ready condition which other services can depend on:
|
||||||
|
.Bd -unfilled -offset indent
|
||||||
|
$ initctl -v cond get service/mdevd/ready
|
||||||
|
on
|
||||||
|
.Ed
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
If a service should not be automatically started, it can be configured
|
If a service should not be automatically started, it can be configured
|
||||||
as manual with the
|
as manual with the
|
||||||
.Cm manual:yes
|
.Cm manual:yes
|
||||||
@@ -322,10 +402,21 @@ was originally designed for so not the default behavior.
|
|||||||
when all retries have failed, and the service
|
when all retries have failed, and the service
|
||||||
has
|
has
|
||||||
.Em crashed ,
|
.Em crashed ,
|
||||||
if this option is set the system is rebooted. Note, future releases
|
if this option is set the system is rebooted.
|
||||||
may include other
|
.It Cm oncrash:script
|
||||||
.Cm oncrash:
|
Similar to
|
||||||
actions.
|
.Cm oncrash:reboot ,
|
||||||
|
but instead of rebooting this action calls the
|
||||||
|
.Cm post:script
|
||||||
|
(see below) to let the operator decide the best course of action. If
|
||||||
|
the post:script option is not set, this is a no-op.
|
||||||
|
.Pp
|
||||||
|
The post:script is called with the same environment variables
|
||||||
|
.Sy except
|
||||||
|
for the
|
||||||
|
.Cm EXIT_CODE
|
||||||
|
variable which is set to
|
||||||
|
.Cm "crashed" .
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
When stopping a service (run/task/sysv/service), either manually or when
|
When stopping a service (run/task/sysv/service), either manually or when
|
||||||
@@ -368,11 +459,13 @@ or any unique identifier specified in the .conf file. The
|
|||||||
.Cm post:script
|
.Cm post:script
|
||||||
is called with an additional set of environment variables:
|
is called with an additional set of environment variables:
|
||||||
.Bl -tag -offset indent -width 1n
|
.Bl -tag -offset indent -width 1n
|
||||||
.It Cm EXIT_CODE=[exited,signal]
|
.It Cm EXIT_CODE=[exited,signal,crashed]
|
||||||
set to one of
|
set to one of
|
||||||
.Cm exited
|
.Cm exited ,
|
||||||
|
.Cm signal ,
|
||||||
or
|
or
|
||||||
.Cm signal
|
.Cm crashed
|
||||||
|
(see above).
|
||||||
.It Cm EXIT_STATUS=[num,SIGNAME]
|
.It Cm EXIT_STATUS=[num,SIGNAME]
|
||||||
set to one of exit status code from the program, if it exited normally,
|
set to one of exit status code from the program, if it exited normally,
|
||||||
or the signal name (HUP, TERM, etc.) if it exited due to signal
|
or the signal name (HUP, TERM, etc.) if it exited due to signal
|
||||||
@@ -382,13 +475,6 @@ The scripts have a default execution time of 3 seconds before they are
|
|||||||
SIGKILLed, this can be adjusted using the
|
SIGKILLed, this can be adjusted using the
|
||||||
.Cm kill:SEC
|
.Cm kill:SEC
|
||||||
modifier syntax.
|
modifier syntax.
|
||||||
.Pp
|
|
||||||
.Sy Note:
|
|
||||||
the
|
|
||||||
.Cm pre:script
|
|
||||||
.Em must
|
|
||||||
be idempotent, because a service can transition between READY and HALTED
|
|
||||||
states any number of times before going to RUNNING.
|
|
||||||
.It Cm runparts Aq DIR
|
.It Cm runparts Aq DIR
|
||||||
Call
|
Call
|
||||||
.Xr run-parts 8
|
.Xr run-parts 8
|
||||||
@@ -757,6 +843,31 @@ the example sets
|
|||||||
to denote that it doesn't support SIGHUP. That way Finit will
|
to denote that it doesn't support SIGHUP. That way Finit will
|
||||||
stop/start the service instead of sending SIGHUP at restart/reload
|
stop/start the service instead of sending SIGHUP at restart/reload
|
||||||
events.
|
events.
|
||||||
|
.Sh TEMPLATING
|
||||||
|
Finit comes with rudimentary support for templating, similar to that of
|
||||||
|
systemd. Best illustrated with an example:
|
||||||
|
.Bd -unfilled -offset indent
|
||||||
|
$ initctl show avahi-autoipd@
|
||||||
|
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
To enable ZeroConf for, e.g.,
|
||||||
|
.Cm eth0 ,
|
||||||
|
use
|
||||||
|
.Bd -unfilled -offset indent
|
||||||
|
$ initctl enable avahi-autoipd@eth0.conf
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
The enabled symlink will be set up to
|
||||||
|
.Cm avahi-autoipd@.conf
|
||||||
|
and every instance of
|
||||||
|
.Cm %i
|
||||||
|
will in the instantiated directive be replaced with
|
||||||
|
.Cm eth0 .
|
||||||
|
Inspect the result with:
|
||||||
|
.Bd -unfilled -offset indent
|
||||||
|
$ initctl status avahi-autoipd:eth0
|
||||||
|
.Ed
|
||||||
.Sh CGROUPS
|
.Sh CGROUPS
|
||||||
There are three major cgroup configuration directives:
|
There are three major cgroup configuration directives:
|
||||||
.Pp
|
.Pp
|
||||||
@@ -873,6 +984,6 @@ settings in
|
|||||||
.Xr finit 8 ,
|
.Xr finit 8 ,
|
||||||
.Xr initctl 8
|
.Xr initctl 8
|
||||||
.Sh AUTHORS
|
.Sh AUTHORS
|
||||||
.Nm finit
|
.Nm Finit
|
||||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||||
maintained by Joachim Wiberg, with contributions by many others.
|
maintained by Joachim Wiberg, with contributions by many others.
|
||||||
|
|||||||
+65
-31
@@ -11,7 +11,7 @@
|
|||||||
.Nd Control tool for Finit
|
.Nd Control tool for Finit
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm /sbin/initctl
|
.Nm /sbin/initctl
|
||||||
.Op Fl bcfhlpqtvV
|
.Op Fl bcfhjlpqtvV
|
||||||
.Op COMMAND
|
.Op COMMAND
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
.Nm
|
.Nm
|
||||||
@@ -33,6 +33,18 @@ command.
|
|||||||
Ignore missing files and arguments, never prompt.
|
Ignore missing files and arguments, never prompt.
|
||||||
.It Fl h, -help
|
.It Fl h, -help
|
||||||
Show built-in help text.
|
Show built-in help text.
|
||||||
|
.It Fl j, -json
|
||||||
|
JSON output in
|
||||||
|
.Ar status
|
||||||
|
and
|
||||||
|
.Ar cond
|
||||||
|
commands.
|
||||||
|
.It Fl n, -noerr
|
||||||
|
When scripting
|
||||||
|
.Nm
|
||||||
|
to stop, start, restart, or signal a task or service, this option can be
|
||||||
|
used to ignore error 69 (no such task or service) and instead return 0
|
||||||
|
(OK).
|
||||||
.It Fl 1, -once
|
.It Fl 1, -once
|
||||||
Only one lap in commands like top.
|
Only one lap in commands like top.
|
||||||
.It Fl p, -plain
|
.It Fl p, -plain
|
||||||
@@ -54,9 +66,9 @@ Toggle
|
|||||||
(daemon) debug to
|
(daemon) debug to
|
||||||
.Pa /dev/console
|
.Pa /dev/console
|
||||||
.It Nm Ar help
|
.It Nm Ar help
|
||||||
Show built-in help text
|
Show built-in help text.
|
||||||
.It Nm Ar version
|
.It Nm Ar version
|
||||||
Show program version
|
Show program version.
|
||||||
.It Nm Ar ls | list
|
.It Nm Ar ls | list
|
||||||
List all
|
List all
|
||||||
.Cm *.conf
|
.Cm *.conf
|
||||||
@@ -115,13 +127,13 @@ Disable
|
|||||||
.Cm .conf
|
.Cm .conf
|
||||||
in
|
in
|
||||||
.Pa /etc/finit.d/enabled ,
|
.Pa /etc/finit.d/enabled ,
|
||||||
i.e., removes symlink
|
i.e., removes symlink.
|
||||||
.It Nm Ar reload
|
.It Nm Ar reload
|
||||||
Reload
|
Reload
|
||||||
.Cm *.conf in
|
.Cm *.conf in
|
||||||
.Pa /etc/finit.d ,
|
.Pa /etc/finit.d ,
|
||||||
i.e., activates changes
|
i.e., activates changes.
|
||||||
.It Nm Ar cond set Ar COND
|
.It Nm Ar cond set Ar COND Op COND ...
|
||||||
Set (assert) user-defined condition,
|
Set (assert) user-defined condition,
|
||||||
.Cm +usr/COND
|
.Cm +usr/COND
|
||||||
.It Nm Ar cond get Ar COND
|
.It Nm Ar cond get Ar COND
|
||||||
@@ -133,36 +145,40 @@ or
|
|||||||
then any condition can be read.
|
then any condition can be read.
|
||||||
.Pp
|
.Pp
|
||||||
The command is geared for scripting, check the exit code to get the
|
The command is geared for scripting, check the exit code to get the
|
||||||
status of the condition, or give
|
status of the condition: 0 - on, 1 - off, 255 - flux. For a more
|
||||||
.Nm
|
verbose output, use the
|
||||||
the
|
|
||||||
.Fl v
|
.Fl v
|
||||||
option for a more verbose output.
|
option.
|
||||||
.It Nm Ar cond clr | clear Ar COND
|
.It Nm Ar cond clr | clear Ar COND Op COND ...
|
||||||
Clear (deassert) user-defined condition,
|
Clear (deassert) user-defined condition,
|
||||||
.Cm -usr/COND
|
.Cm -usr/COND
|
||||||
.It Nm Ar cond status
|
.It Nm Ar cond status
|
||||||
Show condition status, default
|
Show condition status, default
|
||||||
.Cm cond
|
.Cm cond
|
||||||
command.
|
command. Also supports the
|
||||||
|
.Fl j
|
||||||
|
option for detailed JSON output.
|
||||||
.It Nm Ar cond dump Op Cm TYPE
|
.It Nm Ar cond dump Op Cm TYPE
|
||||||
Dump all, or a given type of, conditions and their status
|
Dump all, or a given type of, conditions and their status. Also
|
||||||
|
supports the
|
||||||
|
.Fl j
|
||||||
|
option for detailed JSON output.
|
||||||
.It Nm Ar ident Op Cm NAME
|
.It Nm Ar ident Op Cm NAME
|
||||||
Display indentities of all run/task/services, or only instances
|
Display indentities of all run/task/services, or only instances
|
||||||
matching
|
matching
|
||||||
.Cm NAME .
|
.Cm NAME .
|
||||||
A partial string, e.g.,
|
A partial string, e.g.,
|
||||||
.Cm NAM ,
|
.Cm NAM ,
|
||||||
will not match anything
|
will not match anything.
|
||||||
.It Nm Ar log Op Cm NAME
|
.It Nm Ar log Op Cm NAME
|
||||||
Show ten last Finit, or
|
Show ten last Finit, or
|
||||||
.Cm NAME ,
|
.Cm NAME ,
|
||||||
messages from syslog
|
messages from syslog.
|
||||||
.It Nm Ar start Cm NAME[:ID]
|
.It Nm Ar start Cm NAME[:ID]
|
||||||
Start service by name, with optional ID, e.g.,
|
Start service by name, with optional ID, e.g.,
|
||||||
.Cm initctl start tty:1
|
.Cm initctl start tty:1
|
||||||
.It Nm Ar stop Cm NAME[:ID]
|
.It Nm Ar stop Cm NAME[:ID]
|
||||||
Stop/Pause a running service by name
|
Stop/Pause a running service by name.
|
||||||
.It Nm Ar reload Cm NAME[:ID]
|
.It Nm Ar reload Cm NAME[:ID]
|
||||||
Reload service as if its configuration had changed. Internally,
|
Reload service as if its configuration had changed. Internally,
|
||||||
.Nm finit
|
.Nm finit
|
||||||
@@ -180,54 +196,72 @@ Except for two things:
|
|||||||
if the service supports HUP, it's signaled instead of stop-started, and
|
if the service supports HUP, it's signaled instead of stop-started, and
|
||||||
.It
|
.It
|
||||||
it allows restarting run/task in the same runlevel -- which is otherwise
|
it allows restarting run/task in the same runlevel -- which is otherwise
|
||||||
restricted
|
restricted.
|
||||||
.El
|
.El
|
||||||
.It Nm Ar signal Cm NAME:[ID] S
|
.It Nm Ar signal Cm NAME:[ID] S
|
||||||
Send signal S to service, by name.
|
Send signal S to service, by name.
|
||||||
.Cm S
|
.Cm S
|
||||||
may be a complete signal name such as SIGHUP, or short name such as HUP, or the signal number such as 1 (SIGHUP).
|
may be a complete signal name such as SIGHUP, or short name such as HUP, or the signal number such as 1 (SIGHUP).
|
||||||
.It Nm Ar restart Cm NAME[:ID]
|
.It Nm Ar restart Cm NAME[:ID]
|
||||||
Restart (stop/start) service by name
|
Restart (stop/start) service by name.
|
||||||
.It Nm Ar status Cm NAME[:ID]
|
.It Nm Ar status Cm NAME[:ID]
|
||||||
Show service status, by name. If only
|
Show service status, by name. If only
|
||||||
.Cm NAME
|
.Cm NAME
|
||||||
is given and multiple instances exits, a summary of all matching
|
is given and multiple instances exits, a summary of all matching
|
||||||
instances are shown. Only an exact match displays the detailed status
|
instances are shown. Only an exact match displays detailed status
|
||||||
for a particular instance
|
for a given instance.
|
||||||
|
.Pp
|
||||||
|
With the
|
||||||
|
.Fl q
|
||||||
|
option this command is silent, provided the
|
||||||
|
.Ar NAME[:ID]
|
||||||
|
selection matches a single run/task/service. The exit code of
|
||||||
|
.Nm
|
||||||
|
is non-zero if there is a problem. Zero is returned if a run/task has
|
||||||
|
run (at least) once in the current runlevel, and when a service is running
|
||||||
|
(as expected). See the
|
||||||
|
.Fl j
|
||||||
|
option for detailed JSON output suitable for machine parsing.
|
||||||
|
.Pp
|
||||||
|
.Sy Tip:
|
||||||
|
.Xr jq 1
|
||||||
|
is a useful scripting tool in combination with JSON output.
|
||||||
.It Nm Ar status
|
.It Nm Ar status
|
||||||
Show status of all services, default command
|
Show status of all services, default command. Also supports the
|
||||||
|
.Fl j
|
||||||
|
option for detailed JSON output.
|
||||||
.It Nm Ar cgroup
|
.It Nm Ar cgroup
|
||||||
List cgroup config overview
|
List cgroup config overview.
|
||||||
.It Nm Ar ps
|
.It Nm Ar ps
|
||||||
List processes based on cgroups
|
List processes based on cgroups.
|
||||||
.It Nm Ar top
|
.It Nm Ar top
|
||||||
Show top-like listing based on cgroups
|
Show top-like listing based on cgroups.
|
||||||
.It Nm Ar plugins
|
.It Nm Ar plugins
|
||||||
List installed plugins
|
List installed plugins.
|
||||||
.It Nm Ar runlevel Op Ar 0-9
|
.It Nm Ar runlevel Op Ar 0-9
|
||||||
Show or set runlevel: 0 halt, 6 reboot
|
Show or set runlevel: 0 halt, 6 reboot.
|
||||||
.It Nm Ar reboot
|
.It Nm Ar reboot
|
||||||
Reboot system, default if
|
Reboot system, default if
|
||||||
.Cm reboot
|
.Cm reboot
|
||||||
is symlinked to
|
is symlinked to
|
||||||
.Nm
|
.Nm .
|
||||||
.It Nm Ar halt
|
.It Nm Ar halt
|
||||||
Halt system, default if
|
Halt system, default if
|
||||||
.Cm halt
|
.Cm halt
|
||||||
is symlinked to
|
is symlinked to
|
||||||
.Nm
|
.Nm .
|
||||||
.It Nm Ar poweroff
|
.It Nm Ar poweroff
|
||||||
Power-off system, default if
|
Power-off system, default if
|
||||||
.Cm poweroff
|
.Cm poweroff
|
||||||
is symlinked to
|
is symlinked to
|
||||||
.Nm
|
.Nm .
|
||||||
.It Nm Ar suspend
|
.It Nm Ar suspend
|
||||||
Suspend system, default if
|
Suspend system, default if
|
||||||
.Cm suspend
|
.Cm suspend
|
||||||
is symlinked to
|
is symlinked to
|
||||||
.Nm
|
.Nm .
|
||||||
.It Nm Ar utmp show
|
.It Nm Ar utmp show
|
||||||
Raw dump of UTMP/WTMP db
|
Raw dump of UTMP/WTMP db.
|
||||||
.El
|
.El
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr finit.conf 5
|
.Xr finit.conf 5
|
||||||
|
|||||||
+2
-10
@@ -1,9 +1,9 @@
|
|||||||
pkglibdir = $(plugin_path)
|
pkglibdir = $(plugin_path)
|
||||||
AM_LDFLAGS = -module -avoid-version -shared
|
AM_LDFLAGS = -module -avoid-version -shared
|
||||||
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
|
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||||
AM_CPPFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
AM_CPPFLAGS += -D__FINIT__ $(lite_CFLAGS) $(uev_CFLAGS)
|
||||||
|
|
||||||
if STATIC
|
if STATIC
|
||||||
noinst_LTLIBRARIES = libplug.la
|
noinst_LTLIBRARIES = libplug.la
|
||||||
@@ -17,10 +17,6 @@ if BUILD_DBUS_PLUGIN
|
|||||||
libplug_la_SOURCES += dbus.c
|
libplug_la_SOURCES += dbus.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_HOTPLUG_PLUGIN
|
|
||||||
libplug_la_SOURCES += hotplug.c
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BUILD_MODULES_LOAD_PLUGIN
|
if BUILD_MODULES_LOAD_PLUGIN
|
||||||
libplug_la_SOURCES += modules-load.c
|
libplug_la_SOURCES += modules-load.c
|
||||||
endif
|
endif
|
||||||
@@ -68,10 +64,6 @@ if BUILD_HOOK_SCRIPTS_PLUGIN
|
|||||||
pkglib_LTLIBRARIES += hook-scripts.la
|
pkglib_LTLIBRARIES += hook-scripts.la
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_HOTPLUG_PLUGIN
|
|
||||||
pkglib_LTLIBRARIES += hotplug.la
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BUILD_MODULES_LOAD_PLUGIN
|
if BUILD_MODULES_LOAD_PLUGIN
|
||||||
pkglib_LTLIBRARIES += modules-load.la
|
pkglib_LTLIBRARIES += modules-load.la
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Save and restore ALSA sound settings using alsactl
|
/* Save and restore ALSA sound settings using alsactl
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -36,12 +36,12 @@
|
|||||||
static void save(void *arg)
|
static void save(void *arg)
|
||||||
{
|
{
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (whichp(ALSACTL)) {
|
if (whichp(ALSACTL)) {
|
||||||
_d("Saving sound settings ...");
|
dbg("Saving sound settings ...");
|
||||||
run_interactive(ALSACTL " -g store", "Saving sound settings");
|
run_interactive(ALSACTL " -g store", "Saving sound settings");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,12 +49,12 @@ static void save(void *arg)
|
|||||||
static void restore(void *arg)
|
static void restore(void *arg)
|
||||||
{
|
{
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (whichp(ALSACTL)) {
|
if (whichp(ALSACTL)) {
|
||||||
_d("Restoring sound settings ...");
|
dbg("Restoring sound settings ...");
|
||||||
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
|
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-87
@@ -1,6 +1,6 @@
|
|||||||
/* Setup necessary system files for, e.g. UTMP (tracking logins)
|
/* Setup necessary system files for, e.g. UTMP (tracking logins)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "tmpfiles.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct
|
|||||||
if (ftw->level == 0)
|
if (ftw->level == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
_d("Removing %s ...", fpath);
|
dbg("Removing %s ...", fpath);
|
||||||
(void)remove(fpath);
|
(void)remove(fpath);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -110,12 +111,6 @@ static void clean(void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ln(const char *target, const char *linkpath)
|
|
||||||
{
|
|
||||||
if (symlink(target, linkpath) && errno != EEXIST)
|
|
||||||
_pe("Failed creating %s -> %s symlink", target, linkpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kernel defines the following compulsory and recommended links
|
/* Kernel defines the following compulsory and recommended links
|
||||||
* https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links
|
* https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links
|
||||||
*/
|
*/
|
||||||
@@ -164,90 +159,15 @@ static void setup(void *arg)
|
|||||||
|
|
||||||
prev = umask(0);
|
prev = umask(0);
|
||||||
|
|
||||||
_d("Setting up FHS structure in /var ...");
|
/* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */
|
||||||
makedir("/var/cache", 0755);
|
kernel_links();
|
||||||
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
|
||||||
makedir("/var/games", 0755);
|
|
||||||
makedir("/var/lib", 0755);
|
|
||||||
makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
|
|
||||||
makedir("/var/lib/alarm", 0755);
|
|
||||||
makedir("/var/lib/urandom",0755);
|
|
||||||
if (fisdir("/run")) {
|
|
||||||
_d("System with new /run tmpfs ...");
|
|
||||||
if (!fisdir("/run/lock"))
|
|
||||||
makedir("/run/lock", 1777);
|
|
||||||
ln("/run/lock", "/var/lock");
|
|
||||||
ln("/dev/shm", "/run/shm");
|
|
||||||
|
|
||||||
/* compat only, should really be set up by OS/dist */
|
/* Create all system tmpfiles.d(5) */
|
||||||
ln("/run", "/var/run");
|
tmpfilesd();
|
||||||
} else {
|
|
||||||
makedir("/var/lock", 1777);
|
|
||||||
makedir("/var/run", 0755);
|
|
||||||
}
|
|
||||||
makedir("/var/log", 0755);
|
|
||||||
makedir("/var/mail", 0755);
|
|
||||||
makedir("/var/opt", 0755);
|
|
||||||
makedir("/var/spool", 0755);
|
|
||||||
makedir("/var/spool/cron", 0755);
|
|
||||||
makedir("/var/tmp", 0755);
|
|
||||||
makedir("/var/empty", 0755);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* UTMP actually needs multiple db files
|
|
||||||
*/
|
|
||||||
if (has_utmp()) {
|
|
||||||
int gid;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If /etc/group or "utmp" group is missing, default to
|
|
||||||
* "root", or "wheel", group.
|
|
||||||
*/
|
|
||||||
_d("Setting up necessary UTMP files ...");
|
|
||||||
gid = getgroup("utmp");
|
|
||||||
if (gid < 0)
|
|
||||||
gid = 0;
|
|
||||||
|
|
||||||
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
|
|
||||||
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
|
|
||||||
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
|
||||||
create("/var/log/lastlog", 0644, 0, gid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set BOOT_TIME UTMP entry */
|
/* Set BOOT_TIME UTMP entry */
|
||||||
utmp_set_boot();
|
utmp_set_boot();
|
||||||
|
|
||||||
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
|
|
||||||
touch("/etc/network/run/ifstate");
|
|
||||||
#else
|
|
||||||
erase("/etc/network/run/ifstate");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_d("Setting up misc files ...");
|
|
||||||
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
|
|
||||||
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
|
|
||||||
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
|
||||||
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
|
|
||||||
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
|
|
||||||
mksubsys("/var/run/frr", 0755, "frr", "frr");
|
|
||||||
mksubsys("/var/log/frr", 0755, "frr", "frr");
|
|
||||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
|
||||||
|
|
||||||
if (!fexist("/etc/mtab"))
|
|
||||||
ln("../proc/self/mounts", "/etc/mtab");
|
|
||||||
|
|
||||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
|
||||||
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
|
|
||||||
|
|
||||||
/* Debian has /run/sudo, ensure correct perms and SELinux label */
|
|
||||||
mksubsys("/var/run/sudo", 0711, "root", "root");
|
|
||||||
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
|
|
||||||
if (whichp("restorecon"))
|
|
||||||
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
|
|
||||||
|
|
||||||
/* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */
|
|
||||||
kernel_links();
|
|
||||||
|
|
||||||
umask(prev);
|
umask(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+90
-24
@@ -1,6 +1,6 @@
|
|||||||
/* Setup and start system message bus, D-Bus
|
/* Setup and start system message bus, D-Bus
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -47,58 +47,124 @@
|
|||||||
#define DBUS_DAEMONGROUP "messagebus"
|
#define DBUS_DAEMONGROUP "messagebus"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DBUS_DAEMONPIDFILE
|
/*
|
||||||
#define DBUS_DAEMONPIDFILE "/var/run/dbus/pid"
|
* Dumnpster diving for the D-Bus main configuration file
|
||||||
#endif
|
* where <pidfile> is defined.
|
||||||
|
*/
|
||||||
|
static char *dbus_pidfn(void)
|
||||||
|
{
|
||||||
|
char *alt[] = {
|
||||||
|
"/usr/share/dbus-1/system.conf",
|
||||||
|
"/etc/dbus-1/system.conf",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; alt[i]; i++) {
|
||||||
|
char *fn = alt[i];
|
||||||
|
char buf[256];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen(fn, "r");
|
||||||
|
if (!fp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fn = NULL;
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
pos = strstr(buf, "<pidfile>");
|
||||||
|
if (!pos)
|
||||||
|
continue;
|
||||||
|
fn = pos + strlen("<pidfile>");
|
||||||
|
pos = strstr(fn, "</pidfile>");
|
||||||
|
if (!pos) {
|
||||||
|
fn = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
if (fn)
|
||||||
|
return strdup(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
|
char *group = DBUS_DAEMONGROUP;
|
||||||
|
char *user = DBUS_DAEMONUSER;
|
||||||
char line[256];
|
char line[256];
|
||||||
|
char *pidfn;
|
||||||
mode_t prev;
|
mode_t prev;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = which(DBUS_DAEMON);
|
cmd = which(DBUS_DAEMON);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
_d("Skipping plugin, %s is not installed.", DBUS_DAEMON);
|
dbg("Skipping plugin, %s is not installed.", DBUS_DAEMON);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
prev =umask(0);
|
if (getuser(user, NULL) == -1) {
|
||||||
|
if (getuser("dbus", NULL) == -1)
|
||||||
|
user = "root"; /* fallback */
|
||||||
|
else
|
||||||
|
user = "dbus"; /* e.g., Buildroot */
|
||||||
|
}
|
||||||
|
|
||||||
_d("Creating D-Bus Required Directories ...");
|
if (getgroup(group) == -1) {
|
||||||
mksubsys("/var/run/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
if (getgroup("dbus") == -1)
|
||||||
mksubsys("/var/run/lock/subsys", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
group = "root"; /* fallback */
|
||||||
mksubsys("/var/lib/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
else
|
||||||
mksubsys("/tmp/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
group = "dbus"; /* e.g., Buildroot */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up from any previous pre-bootstrap run */
|
||||||
|
pidfn = dbus_pidfn();
|
||||||
|
if (pidfn)
|
||||||
|
remove(pidfn);
|
||||||
|
|
||||||
|
dbg("Creating D-Bus Required Directories ...");
|
||||||
|
prev = umask(0);
|
||||||
|
mksubsys("/var/run/dbus", 0755, user, group);
|
||||||
|
mksubsys("/var/run/lock/subsys", 0755, user, group);
|
||||||
|
mksubsys("/var/lib/dbus", 0755, user, group);
|
||||||
|
mksubsys("/tmp/dbus", 0755, user, group);
|
||||||
|
umask(prev);
|
||||||
|
|
||||||
/* Generate machine id for dbus */
|
/* Generate machine id for dbus */
|
||||||
if (whichp("dbus-uuidgen"))
|
if (whichp("dbus-uuidgen"))
|
||||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||||
|
|
||||||
/* Clean up from any previous pre-bootstrap run */
|
/*
|
||||||
remove(DBUS_DAEMONPIDFILE);
|
* Register service with Finit
|
||||||
|
* Note: dbus drops privs after starting up.
|
||||||
|
*/
|
||||||
|
if (pidfn) {
|
||||||
|
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus pid:!%s %s %s -- %s",
|
||||||
|
pidfn, cmd, DBUS_ARGS, DBUS_DESC);
|
||||||
|
free(pidfn);
|
||||||
|
} else
|
||||||
|
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus %s %s -- %s",
|
||||||
|
cmd, DBUS_ARGS, DBUS_DESC);
|
||||||
|
|
||||||
/* Register service with Finit */
|
|
||||||
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
|
|
||||||
DBUS_DAEMONPIDFILE, DBUS_DAEMONUSER, DBUS_DAEMONGROUP, cmd, DBUS_ARGS, DBUS_DESC);
|
|
||||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||||
_pe("Failed registering %s", DBUS_DAEMON);
|
err(1, "Failed registering %s", DBUS_DAEMON);
|
||||||
free(cmd);
|
|
||||||
|
|
||||||
umask(prev);
|
free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_BASEFS_UP] = {
|
.hook[HOOK_SVC_PLUGIN] = { .cb = setup },
|
||||||
.cb = setup
|
|
||||||
},
|
|
||||||
.depends = { "bootmisc", },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
+11
-10
@@ -26,18 +26,11 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#define CHOOSE(x, y) y
|
extern void plugin_script_run(hook_point_t no);
|
||||||
static const char *hscript_paths[] = HOOK_TYPES;
|
|
||||||
#undef CHOOSE
|
|
||||||
|
|
||||||
static void hscript_run_parts(hook_point_t hook)
|
static void hscript_run_parts(hook_point_t no)
|
||||||
{
|
{
|
||||||
char path[CMD_SIZE] = "";
|
plugin_script_run(no);
|
||||||
|
|
||||||
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
|
|
||||||
strlcat(path, hscript_paths[hook] + 4, sizeof(path));
|
|
||||||
|
|
||||||
run_parts(path, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hscript_banner(void *arg)
|
static void hscript_banner(void *arg)
|
||||||
@@ -96,6 +89,14 @@ static plugin_t plugin = {
|
|||||||
.hook[HOOK_SVC_UP] = { .cb = hscript_svc_up },
|
.hook[HOOK_SVC_UP] = { .cb = hscript_svc_up },
|
||||||
.hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up },
|
.hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up },
|
||||||
.hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown },
|
.hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown },
|
||||||
|
/*
|
||||||
|
* We have exited here, so these are handled a bit differently by Finit,
|
||||||
|
* when hook scripts are enabled, just to provide the user with the same
|
||||||
|
* interface:
|
||||||
|
*
|
||||||
|
* .hook[HOOK_SVC_DN] = { .cb = hscript_svc_down },
|
||||||
|
* .hook[HOOK_SYS_DN] = { .cb = hscript_system_down },
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
/* Heuristically find and initialize a suitable hotplug daemon
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "conf.h"
|
|
||||||
#include "finit.h"
|
|
||||||
#include "helpers.h"
|
|
||||||
#include "plugin.h"
|
|
||||||
#include "service.h"
|
|
||||||
|
|
||||||
static void setup(void *arg)
|
|
||||||
{
|
|
||||||
char cmd[256];
|
|
||||||
char *path;
|
|
||||||
|
|
||||||
if (rescue) {
|
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Populate /dev and prepare for runtime events from kernel.
|
|
||||||
* Prefer udev if mdev is also available on the system.
|
|
||||||
*/
|
|
||||||
path = which("udevd");
|
|
||||||
if (!path)
|
|
||||||
path = which("/lib/systemd/systemd-udevd");
|
|
||||||
if (path) {
|
|
||||||
/* Register udevd as a monitored service */
|
|
||||||
snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s "
|
|
||||||
"-- Device event managing daemon", path);
|
|
||||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
|
||||||
_pe("Failed registering %s", path);
|
|
||||||
} else {
|
|
||||||
snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log "
|
|
||||||
"udevadm trigger -c add -t devices "
|
|
||||||
"-- Requesting device events");
|
|
||||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log "
|
|
||||||
"udevadm trigger -c add -t subsystems "
|
|
||||||
"-- Requesting subsystem events");
|
|
||||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(path);
|
|
||||||
|
|
||||||
/* Debian has this little script to copy generated
|
|
||||||
* rules while the system was read-only. TODO: When
|
|
||||||
* this functionality was hardcoded in finit.c, this
|
|
||||||
* call was made in crank_worker(). Now that
|
|
||||||
* filesystems are mounted earlier we should be able
|
|
||||||
* to make this call directly after the triggers have
|
|
||||||
* run, but this has not been tested AT ALL. */
|
|
||||||
if (fexist("/lib/udev/udev-finish"))
|
|
||||||
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
|
||||||
} else {
|
|
||||||
path = which("mdev");
|
|
||||||
if (path) {
|
|
||||||
/* Embedded Linux systems usually have BusyBox mdev */
|
|
||||||
if (debug)
|
|
||||||
touch("/dev/mdev.log");
|
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), "%s -s", path);
|
|
||||||
free(path);
|
|
||||||
|
|
||||||
run_interactive(cmd, "Populating device tree");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static plugin_t plugin = {
|
|
||||||
.name = __FILE__,
|
|
||||||
.hook[HOOK_BASEFS_UP] = {
|
|
||||||
.cb = setup
|
|
||||||
},
|
|
||||||
.depends = { "bootmisc", "modprobe" },
|
|
||||||
};
|
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
|
||||||
{
|
|
||||||
plugin_register(&plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
PLUGIN_EXIT(plugin_exit)
|
|
||||||
{
|
|
||||||
plugin_unregister(&plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local Variables:
|
|
||||||
* indent-tabs-mode: t
|
|
||||||
* c-file-style: "linux"
|
|
||||||
* End:
|
|
||||||
*/
|
|
||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
/* Coldplug modules using modalias magic
|
/* Coldplug modules using modalias magic
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -71,14 +71,14 @@ static int modprobe(char *alias)
|
|||||||
pid = fork();
|
pid = fork();
|
||||||
switch (pid) {
|
switch (pid) {
|
||||||
case -1:
|
case -1:
|
||||||
_pe("Failed forking modprobe child");
|
err(1, "Failed forking modprobe child");
|
||||||
return 1;
|
return 1;
|
||||||
case 0:
|
case 0:
|
||||||
execvp(args[0], args);
|
execvp(args[0], args);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!complete(args[0], pid))
|
if (!complete(args[0], pid))
|
||||||
_d("Successful modprobe of %s", alias);
|
dbg("Successful modprobe of %s", alias);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+84
-28
@@ -62,62 +62,120 @@
|
|||||||
#ifndef MODULES_LOAD_PATH
|
#ifndef MODULES_LOAD_PATH
|
||||||
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||||
#endif
|
#endif
|
||||||
|
#define MODPROBE_PATH "/sbin/modprobe"
|
||||||
#define SERVICE_LINE \
|
#define SERVICE_LINE \
|
||||||
"cgroup.init name:modprobe.%s :%d [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
"cgroup.init name:modprobe.%s :%d [%s] %s %s %s --"
|
||||||
#define SERVICE_LINE_NOINDEX \
|
#define SERVICE_LINE_NOINDEX \
|
||||||
"cgroup.init name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
"cgroup.init name:modprobe.%s [%s] %s %s %s --"
|
||||||
|
|
||||||
static int load_file(const char *file, int index)
|
static int modules_load(const char *file, int index)
|
||||||
{
|
{
|
||||||
char module_path[PATH_MAX];
|
char module_path[PATH_MAX];
|
||||||
char line[256];
|
char *modprobe_path;
|
||||||
int num = 0;
|
int num = 0;
|
||||||
|
char *line;
|
||||||
|
char *lvl;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
||||||
strlcat(module_path, file, sizeof(module_path));
|
strlcat(module_path, file, sizeof(module_path));
|
||||||
|
|
||||||
fp = fopen(module_path, "r");
|
fp = fopen(module_path, "r");
|
||||||
if (!fp)
|
if (!fp) {
|
||||||
|
warnx("failed opening %s for reading, skipping ...", module_path);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), fp)) {
|
modprobe_path = strdup(MODPROBE_PATH);
|
||||||
char cmd[CMD_SIZE], *mod, *args;
|
if (!modprobe_path) {
|
||||||
|
fail:
|
||||||
|
warnx("failed allocating memory in modules-load plugin.");
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
chomp(line);
|
lvl = strdup("S");
|
||||||
|
if (!lvl) {
|
||||||
|
free(modprobe_path);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
|
||||||
|
char cmd[CMD_SIZE], *mod, *args, *set;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fparseln() skips regular UNIX comments only.
|
||||||
|
* This is for modules-load.d(5) compat.
|
||||||
|
*/
|
||||||
|
if (line[0] == ';')
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
/* trim whitespace */
|
||||||
mod = strip_line(line);
|
mod = strip_line(line);
|
||||||
if (!mod[0])
|
if (!mod[0])
|
||||||
continue;
|
goto next;
|
||||||
|
|
||||||
if (mod[0] == '#' || mod[0] == ';')
|
/* Finit extension 'set foo bar' */
|
||||||
continue;
|
if ((set = fgetval(mod, "set", "= \t"))) {
|
||||||
|
char *val = mod;
|
||||||
|
|
||||||
if (!strcmp(mod, "set noindex")) {
|
if (!strcmp(set, "noindex")) {
|
||||||
index = 0;
|
index = 0;
|
||||||
continue;
|
free(set);
|
||||||
|
goto next;
|
||||||
}
|
}
|
||||||
if (!strncmp(mod, "set index", 9)) {
|
|
||||||
mod += 9;
|
if ((val = fgetval(set, "index", "= \t"))) {
|
||||||
args = strchr(mod, '=');
|
index = atoi(val);
|
||||||
if (args) /* optional */
|
free(set);
|
||||||
mod = args + 1;
|
free(val);
|
||||||
index = atoi(mod);
|
goto next;
|
||||||
continue;
|
}
|
||||||
|
|
||||||
|
if ((val = fgetval(set, "runlevel", "= \t"))) {
|
||||||
|
free(lvl);
|
||||||
|
lvl = val;
|
||||||
|
free(set);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((val = fgetval(set, "modprobe", "= \t"))) {
|
||||||
|
if (access(val, X_OK)) {
|
||||||
|
warn("%s: cannot use %s", module_path, val);
|
||||||
|
free(set);
|
||||||
|
free(val);
|
||||||
|
free(line);
|
||||||
|
goto skip;
|
||||||
|
} else {
|
||||||
|
free(modprobe_path);
|
||||||
|
modprobe_path = val;
|
||||||
|
}
|
||||||
|
free(set);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unknown 'set' command, ignore. */
|
||||||
|
goto next;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod = strtok_r(mod, " ", &args);
|
mod = strtok_r(mod, " ", &args);
|
||||||
if (!mod)
|
if (!mod)
|
||||||
continue;
|
goto next;
|
||||||
|
|
||||||
if (!index)
|
if (!index)
|
||||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, mod, args, mod);
|
snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, lvl, modprobe_path, mod, args);
|
||||||
else
|
else
|
||||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, mod, args, mod);
|
snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, lvl, modprobe_path, mod, args);
|
||||||
|
|
||||||
|
dbg("task %s", cmd);
|
||||||
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
||||||
num++;
|
num++;
|
||||||
|
next:
|
||||||
|
free(line);
|
||||||
}
|
}
|
||||||
|
skip:
|
||||||
|
free(modprobe_path);
|
||||||
|
free(lvl);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
@@ -142,11 +200,11 @@ static void load(void *arg)
|
|||||||
int index = 1;
|
int index = 1;
|
||||||
int i, num;
|
int i, num;
|
||||||
|
|
||||||
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
|
dbg("Scanning " MODULES_LOAD_PATH " for config files ...");
|
||||||
num = scandir(MODULES_LOAD_PATH, &dentry, module_filter, alphasort);
|
num = scandir(MODULES_LOAD_PATH, &dentry, module_filter, alphasort);
|
||||||
if (num > 0) {
|
if (num > 0) {
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
index += load_file(dentry[i]->d_name, index);
|
index += modules_load(dentry[i]->d_name, index);
|
||||||
free(dentry[i]);
|
free(dentry[i]);
|
||||||
}
|
}
|
||||||
free(dentry);
|
free(dentry);
|
||||||
@@ -155,9 +213,7 @@ static void load(void *arg)
|
|||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_BASEFS_UP] = {
|
.hook[HOOK_SVC_PLUGIN] = { .cb = load },
|
||||||
.cb = load
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
+36
-36
@@ -2,7 +2,7 @@
|
|||||||
/* Netlink plugin for IFUP/IFDN and GW events
|
/* Netlink plugin for IFUP/IFDN and GW events
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
|
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
|
||||||
* Copyright (C) 2009-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (C) 2009-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -62,7 +62,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
int la;
|
int la;
|
||||||
|
|
||||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
|
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
|
||||||
_e("Packet too small or truncated!");
|
errx(1, "Packet too small or truncated!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
a = RTM_RTA(r);
|
a = RTM_RTA(r);
|
||||||
la = RTM_PAYLOAD(nlmsg);
|
la = RTM_PAYLOAD(nlmsg);
|
||||||
if (la >= len) {
|
if (la >= len) {
|
||||||
_e("Packet too large!");
|
errx(1, "Packet too large!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,18 +80,18 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
switch (a->rta_type) {
|
switch (a->rta_type) {
|
||||||
case RTA_GATEWAY:
|
case RTA_GATEWAY:
|
||||||
gw = *((int *)data);
|
gw = *((int *)data);
|
||||||
//_d("GW: 0x%04x", gw);
|
//dbg("GW: 0x%04x", gw);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTA_DST:
|
case RTA_DST:
|
||||||
dst = *((int *)data);
|
dst = *((int *)data);
|
||||||
plen = r->rtm_dst_len;
|
plen = r->rtm_dst_len;
|
||||||
//_d("Prefix LEN: 0x%04x", plen);
|
//dbg("Prefix LEN: 0x%04x", plen);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTA_OIF:
|
case RTA_OIF:
|
||||||
idx = *((int *)data);
|
idx = *((int *)data);
|
||||||
//_d("IDX: 0x%04x", idx);
|
//dbg("IDX: 0x%04x", idx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
ing.s_addr = gw;
|
ing.s_addr = gw;
|
||||||
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
|
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
|
||||||
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
|
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
|
||||||
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
|
dbg("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
|
||||||
|
|
||||||
if ((!dst && !plen) && (gw || idx)) {
|
if ((!dst && !plen) && (gw || idx)) {
|
||||||
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
|
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
|
||||||
@@ -167,7 +167,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
int la;
|
int la;
|
||||||
|
|
||||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
|
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
|
||||||
_e("Packet too small or truncated!");
|
errx(1, "Packet too small or truncated!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
|
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
|
||||||
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
|
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
|
||||||
if (la >= len) {
|
if (la >= len) {
|
||||||
_e("Packet too large!");
|
errx(1, "Packet too large!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
|
|
||||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||||
if (validate_ifname(ifname)) {
|
if (validate_ifname(ifname)) {
|
||||||
_d("Invalid interface name '%s', skipping ...", ifname);
|
dbg("Invalid interface name '%s', skipping ...", ifname);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
* New interface has appeared, or interface flags has changed.
|
* New interface has appeared, or interface flags has changed.
|
||||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||||
*/
|
*/
|
||||||
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
dbg("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||||
net_cond_set(ifname, "exist", 1);
|
net_cond_set(ifname, "exist", 1);
|
||||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||||
@@ -205,7 +205,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
|
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
/* NOTE: Interface has disappeared, not link down ... */
|
/* NOTE: Interface has disappeared, not link down ... */
|
||||||
_d("%s: Delete link", ifname);
|
dbg("%s: Delete link", ifname);
|
||||||
net_cond_set(ifname, "exist", 0);
|
net_cond_set(ifname, "exist", 0);
|
||||||
net_cond_set(ifname, "up", 0);
|
net_cond_set(ifname, "up", 0);
|
||||||
net_cond_set(ifname, "running", 0);
|
net_cond_set(ifname, "running", 0);
|
||||||
@@ -213,15 +213,15 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_NEWADDR:
|
case RTM_NEWADDR:
|
||||||
_d("%s: New Address", ifname);
|
dbg("%s: New Address", ifname);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_DELADDR:
|
case RTM_DELADDR:
|
||||||
_d("%s: Deconfig Address", ifname);
|
dbg("%s: Deconfig Address", ifname);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
dbg("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,46 +246,46 @@ static int nl_parse(int sd)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_pe("recv()");
|
err(1, "recv()");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _d("recv %zd bytes", len);
|
// dbg("recv %zd bytes", len);
|
||||||
l = (size_t)len;
|
l = (size_t)len;
|
||||||
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
|
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
|
||||||
struct nlmsgerr *nle;
|
struct nlmsgerr *nle;
|
||||||
|
|
||||||
switch (nh->nlmsg_type) {
|
switch (nh->nlmsg_type) {
|
||||||
case NLMSG_DONE:
|
case NLMSG_DONE:
|
||||||
// _d("Done with netlink messages.");
|
// dbg("Done with netlink messages.");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case NLMSG_ERROR:
|
case NLMSG_ERROR:
|
||||||
// _d("Kernel netlink comm. error.");
|
// dbg("Kernel netlink comm. error.");
|
||||||
nle = NLMSG_DATA(nh);
|
nle = NLMSG_DATA(nh);
|
||||||
if (nle) {
|
if (nle) {
|
||||||
errno = -nle->error;
|
errno = -nle->error;
|
||||||
_pe("Kernel netlink error %d", errno);
|
err(1, "Kernel netlink error %d", errno);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
case RTM_NEWROUTE:
|
case RTM_NEWROUTE:
|
||||||
case RTM_DELROUTE:
|
case RTM_DELROUTE:
|
||||||
// _d("Netlink route ...");
|
// dbg("Netlink route ...");
|
||||||
nl_route(nh, len);
|
nl_route(nh, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_NEWLINK:
|
case RTM_NEWLINK:
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
// _d("Netlink link ...");
|
// dbg("Netlink link ...");
|
||||||
nl_link(nh, len);
|
nl_link(nh, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_w("unhandled netlink message, type %d", nh->nlmsg_type);
|
warnx("unhandled netlink message, type %d", nh->nlmsg_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,21 +304,21 @@ static int nl_request(int sd, unsigned int seq, int type)
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RTM_GETROUTE:
|
case RTM_GETROUTE:
|
||||||
// _d("RTM_GETROUTE");
|
// dbg("RTM_GETROUTE");
|
||||||
nlr->rtm.rtm_family = AF_INET;
|
nlr->rtm.rtm_family = AF_INET;
|
||||||
nlr->rtm.rtm_table = RT_TABLE_MAIN;
|
nlr->rtm.rtm_table = RT_TABLE_MAIN;
|
||||||
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_GETLINK:
|
case RTM_GETLINK:
|
||||||
// _d("RTM_GETLINK");
|
// dbg("RTM_GETLINK");
|
||||||
nlr->ifi.ifi_family = AF_UNSPEC;
|
nlr->ifi.ifi_family = AF_UNSPEC;
|
||||||
nlr->ifi.ifi_change = 0xFFFFFFFF;
|
nlr->ifi.ifi_change = 0xFFFFFFFF;
|
||||||
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_w("Cannot resync, unhandled message type %d", type);
|
warnx("Cannot resync, unhandled message type %d", type);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,13 +331,13 @@ static int nl_request(int sd, unsigned int seq, int type)
|
|||||||
static void nl_resync_routes(int sd, unsigned int seq)
|
static void nl_resync_routes(int sd, unsigned int seq)
|
||||||
{
|
{
|
||||||
if (nl_request(sd, seq, RTM_GETROUTE))
|
if (nl_request(sd, seq, RTM_GETROUTE))
|
||||||
_pe("Failed netlink route request");
|
err(1, "Failed netlink route request");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nl_resync_ifaces(int sd, unsigned int seq)
|
static void nl_resync_ifaces(int sd, unsigned int seq)
|
||||||
{
|
{
|
||||||
if (nl_request(sd, seq, RTM_GETLINK))
|
if (nl_request(sd, seq, RTM_GETLINK))
|
||||||
_pe("Failed netlink link request");
|
err(1, "Failed netlink link request");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -350,12 +350,12 @@ static void nl_resync(int all)
|
|||||||
|
|
||||||
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||||||
if (sd < 0) {
|
if (sd < 0) {
|
||||||
_pe("netlink socket");
|
err(1, "netlink socket");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all) {
|
if (all) {
|
||||||
_d("============================ RESYNC =================================");
|
dbg("============================ RESYNC =================================");
|
||||||
/* this doesn't update conditions, and thus does not stop services */
|
/* this doesn't update conditions, and thus does not stop services */
|
||||||
cond_deassert("net/");
|
cond_deassert("net/");
|
||||||
|
|
||||||
@@ -364,7 +364,7 @@ static void nl_resync(int all)
|
|||||||
|
|
||||||
/* delayed update after we've corrected things */
|
/* delayed update after we've corrected things */
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
_d("=========================== RESYNCED ================================");
|
dbg("=========================== RESYNCED ================================");
|
||||||
} else
|
} else
|
||||||
nl_resync_routes(sd, seq++);
|
nl_resync_routes(sd, seq++);
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ static void nl_callback(void *arg, int sd, int events)
|
|||||||
{
|
{
|
||||||
if (nl_parse(sd) < 0) {
|
if (nl_parse(sd) < 0) {
|
||||||
if (errno == ENOBUFS) { /* netlink(7) */
|
if (errno == ENOBUFS) { /* netlink(7) */
|
||||||
_w("busy system, resynchronizing with kernel.");
|
warnx("busy system, resynchronizing with kernel.");
|
||||||
nl_resync(1);
|
nl_resync(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -386,7 +386,7 @@ static void nl_callback(void *arg, int sd, int events)
|
|||||||
* we need to check ourselves, e.g. for loss of default route.
|
* we need to check ourselves, e.g. for loss of default route.
|
||||||
*/
|
*/
|
||||||
if (nl_ifdown) {
|
if (nl_ifdown) {
|
||||||
_d("interface down, checking default route.");
|
dbg("interface down, checking default route.");
|
||||||
if (nl_defidx > 0) {
|
if (nl_defidx > 0) {
|
||||||
nl_defidx = 0;
|
nl_defidx = 0;
|
||||||
nl_resync(0);
|
nl_resync(0);
|
||||||
@@ -421,7 +421,7 @@ PLUGIN_INIT(plugin_init)
|
|||||||
|
|
||||||
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
|
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||||
if (sd < 0) {
|
if (sd < 0) {
|
||||||
_pe("socket()");
|
err(1, "socket()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,14 +431,14 @@ PLUGIN_INIT(plugin_init)
|
|||||||
sa.nl_pid = getpid();
|
sa.nl_pid = getpid();
|
||||||
|
|
||||||
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||||
_pe("bind()");
|
err(1, "bind()");
|
||||||
close(sd);
|
close(sd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nl_buf = malloc(NL_BUFSZ);
|
nl_buf = malloc(NL_BUFSZ);
|
||||||
if (!nl_buf) {
|
if (!nl_buf) {
|
||||||
_pe("malloc()");
|
err(1, "malloc()");
|
||||||
close(sd);
|
close(sd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-23
@@ -1,7 +1,7 @@
|
|||||||
/* Simple pidfile event monitor for the Finit condition engine
|
/* Simple pidfile event monitor for the Finit condition engine
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -52,7 +52,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
|
|||||||
ptr = slash++;
|
ptr = slash++;
|
||||||
slash = strchr(ptr, '/');
|
slash = strchr(ptr, '/');
|
||||||
if (slash) {
|
if (slash) {
|
||||||
_d("Path too deep, skipping.");
|
dbg("Path too deep, skipping.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,29 +71,38 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_d("path: %s, mask: %08x", fn, mask);
|
dbg("path: %s, mask: %08x", fn, mask);
|
||||||
|
|
||||||
svc = svc_find_by_pidfile(fn);
|
svc = svc_find_by_pidfile(fn);
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_d("No matching svc for %s", fn);
|
dbg("No matching svc for %s", fn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
dbg("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
||||||
|
|
||||||
mkcond(svc, cond, sizeof(cond));
|
mkcond(svc, cond, sizeof(cond));
|
||||||
|
|
||||||
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||||
|
/*
|
||||||
|
* only mark native services as started, the s6 and
|
||||||
|
* systemd style services rely on their respective
|
||||||
|
* readiness notification. Issue #343
|
||||||
|
*/
|
||||||
|
if (!svc->notify)
|
||||||
svc_started(svc);
|
svc_started(svc);
|
||||||
|
|
||||||
if (!svc_has_pidfile(svc)) {
|
if (!svc_has_pidfile(svc)) {
|
||||||
_d("Setting %s PID file to %s", svc->name, fn);
|
dbg("Setting %s PID file to %s", svc->name, fn);
|
||||||
pid_file_set(svc, fn, 1);
|
pid_file_set(svc, fn, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (svc_is_forking(svc)) {
|
if (svc_is_forking(svc)) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
pid = pid_file_read(pid_file(svc));
|
pid = pid_file_read(fn);
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
|
/* handle case when service dies immediately */
|
||||||
|
if (!fexist(fn))
|
||||||
cond_clear(cond);
|
cond_clear(cond);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -102,7 +111,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
service_forked(svc);
|
service_forked(svc);
|
||||||
|
|
||||||
if (pid != svc->pid) {
|
if (pid != svc->pid) {
|
||||||
_d("Forking service %s (cmd %s) changed PID from %d to %d",
|
dbg("Forking service %s (cmd %s) changed PID from %d to %d",
|
||||||
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid);
|
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid);
|
||||||
svc->pid = pid;
|
svc->pid = pid;
|
||||||
|
|
||||||
@@ -112,8 +121,13 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cond_set(cond);
|
cond_set(cond);
|
||||||
} else if (mask & IN_DELETE)
|
if (!svc->notify)
|
||||||
|
service_ready(svc);
|
||||||
|
} else if (mask & IN_DELETE) {
|
||||||
cond_clear(cond);
|
cond_clear(cond);
|
||||||
|
if (!svc->notify)
|
||||||
|
service_ready(svc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* synthesize events in case of new run dirs */
|
/* synthesize events in case of new run dirs */
|
||||||
@@ -135,7 +149,7 @@ static void pidfile_scandir(struct iwatch *iw, char *dir, int len)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < gl.gl_pathc; i++) {
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
_d("scan found %s", gl.gl_pathv[i]);
|
dbg("scan found %s", gl.gl_pathv[i]);
|
||||||
pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||||
}
|
}
|
||||||
globfree(&gl);
|
globfree(&gl);
|
||||||
@@ -150,7 +164,7 @@ static void pidfile_handle_dir(struct iwatch *iw, char *dir, char *name, int mas
|
|||||||
struct iwatch_path *iwp;
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
paste(path, sizeof(path), dir, name);
|
paste(path, sizeof(path), dir, name);
|
||||||
_d("path: %s", path);
|
dbg("path: %s", path);
|
||||||
|
|
||||||
iwp = iwatch_find_by_path(iw, path);
|
iwp = iwatch_find_by_path(iw, path);
|
||||||
|
|
||||||
@@ -174,7 +188,7 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
|
|
||||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
if (sz <= 0) {
|
if (sz <= 0) {
|
||||||
_pe("invalid inotify event");
|
err(1, "invalid inotify event");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
@@ -226,6 +240,9 @@ static void pidfile_reconf(void *arg)
|
|||||||
if (svc_is_changed(svc) || svc_is_starting(svc))
|
if (svc_is_changed(svc) || svc_is_starting(svc))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!svc->notify)
|
||||||
|
service_ready(svc);
|
||||||
|
|
||||||
mkcond(svc, cond, sizeof(cond));
|
mkcond(svc, cond, sizeof(cond));
|
||||||
if (cond_get(cond) == COND_ON)
|
if (cond_get(cond) == COND_ON)
|
||||||
continue;
|
continue;
|
||||||
@@ -234,9 +251,9 @@ static void pidfile_reconf(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This will call service_step(), which in turn will schedule itself
|
* This calls service_step(), which in turn schedules itself as
|
||||||
* as long as stepped services change state. Services going from
|
* long as stepped services change state. Services going from
|
||||||
* WAITING to RUNNING will reassert their conditions in that loop,
|
* PAUSED to RUNNING reassert their conditions in that loop,
|
||||||
* which in turn may unlock other services, and so on.
|
* which in turn may unlock other services, and so on.
|
||||||
*/
|
*/
|
||||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
|
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
|
||||||
@@ -248,7 +265,7 @@ static void pidfile_init(void *arg)
|
|||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
|
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
|
err(1, "Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,10 +276,10 @@ static void pidfile_init(void *arg)
|
|||||||
*/
|
*/
|
||||||
path = realpath(_PATH_VARRUN, NULL);
|
path = realpath(_PATH_VARRUN, NULL);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
|
dbg("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
|
||||||
path = realpath("/run", NULL);
|
path = realpath("/run", NULL);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
|
errx(1, "System does not have %s or /run, aborting.", _PATH_VARRUN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,15 +300,15 @@ static void pidfile_init(void *arg)
|
|||||||
*
|
*
|
||||||
* service <net/iface/lo> /sbin/dropbear ...
|
* service <net/iface/lo> /sbin/dropbear ...
|
||||||
*
|
*
|
||||||
* Which provides the <pid/dropbear> condition, will not be set by
|
* This service provides the <pid/dropbear> condition, which will not be
|
||||||
* pidfile.so during `initctl reload` because dropbear is still
|
* set by pidfile.so during `initctl reload` because dropbear is still
|
||||||
* SIGSTP:ed waiting for <net/iface/lo>.
|
* SIGSTP:ed (in state PAUSED) waiting for <net/iface/lo>.
|
||||||
*/
|
*/
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
|
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
|
||||||
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
||||||
.depends = { "netlink" },
|
.depends = { "netlink" }, /* bootmisc depends on us */
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
/* Set kernel variables from /etc/sysctl.conf et al
|
/* Set kernel variables from /etc/sysctl.conf et al
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -37,7 +37,7 @@ static void setup(void *arg)
|
|||||||
glob_t gl;
|
glob_t gl;
|
||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Setup necessary files for resolvconf
|
/* Setup necessary files for resolvconf
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
*/
|
*/
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
_d("Setting up the resolver ...");
|
dbg("Setting up the resolver ...");
|
||||||
mkpath("/etc/resolvconf", 0755);
|
mkpath("/etc/resolvconf", 0755);
|
||||||
mkpath("/etc/resolvconf/run", 0755);
|
mkpath("/etc/resolvconf/run", 0755);
|
||||||
mkpath("/var/run/resolvconf", 0755);
|
mkpath("/var/run/resolvconf", 0755);
|
||||||
@@ -45,7 +45,7 @@ static void setup(void *arg)
|
|||||||
|
|
||||||
touch("/etc/resolvconf/run/enable-updates");
|
touch("/etc/resolvconf/run/enable-updates");
|
||||||
chdir("/etc/resolvconf/run/interface");
|
chdir("/etc/resolvconf/run/interface");
|
||||||
run_parts("/etc/resolvconf/update.d", "-i");
|
run_parts("/etc/resolvconf/update.d", "-i", NULL, 0);
|
||||||
chdir("/");
|
chdir("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-11
@@ -1,6 +1,6 @@
|
|||||||
/* Save and restore RTC using hwclock
|
/* Save and restore RTC using hwclock
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
@@ -35,7 +37,13 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
|
||||||
/* Kernel RTC driver validates against this date for sanity check */
|
/* Kernel RTC driver validates against this date for sanity check */
|
||||||
#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */
|
#define RTC_TIMESTAMP_BEGIN_2000 "2000-01-01 00:00:00"
|
||||||
|
#ifdef RTC_TIMESTAMP_CUSTOM
|
||||||
|
static char *rtc_timestamp = RTC_TIMESTAMP_CUSTOM;
|
||||||
|
#else
|
||||||
|
static char *rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
|
||||||
|
#endif
|
||||||
|
static time_t rtc_date_fallback = 946684800LL;
|
||||||
|
|
||||||
static void tz_set(char *tz, size_t len)
|
static void tz_set(char *tz, size_t len)
|
||||||
{
|
{
|
||||||
@@ -89,7 +97,7 @@ static void rtc_save(void *arg)
|
|||||||
char tz[128];
|
char tz[128];
|
||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,10 +107,10 @@ static void rtc_save(void *arg)
|
|||||||
|
|
||||||
tz_set(tz, sizeof(tz));
|
tz_set(tz, sizeof(tz));
|
||||||
rc = gettimeofday(&tv, NULL);
|
rc = gettimeofday(&tv, NULL);
|
||||||
if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
if (rc < 0 || tv.tv_sec < rtc_date_fallback) {
|
||||||
print_desc(NULL, "System clock invalid, not saving to RTC");
|
print_desc(NULL, "System clock invalid, not saving to RTC");
|
||||||
invalid:
|
invalid:
|
||||||
logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC");
|
logit(LOG_ERR, "System clock invalid, before %s, not saving to RTC", rtc_timestamp);
|
||||||
rc = 2;
|
rc = 2;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -131,13 +139,15 @@ static void rtc_restore(void *arg)
|
|||||||
char tz[128];
|
char tz[128];
|
||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = rtc_open();
|
fd = rtc_open();
|
||||||
if (fd < 0)
|
if (fd < 0) {
|
||||||
|
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore.");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tz_set(tz, sizeof(tz));
|
tz_set(tz, sizeof(tz));
|
||||||
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
|
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
|
||||||
@@ -151,7 +161,7 @@ static void rtc_restore(void *arg)
|
|||||||
invalid:
|
invalid:
|
||||||
logit(LOG_ERR, "Failed restoring system clock from RTC.");
|
logit(LOG_ERR, "Failed restoring system clock from RTC.");
|
||||||
if (EINVAL == errno)
|
if (EINVAL == errno)
|
||||||
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)");
|
logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp);
|
||||||
else if (ENOENT == errno)
|
else if (ENOENT == errno)
|
||||||
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
||||||
else
|
else
|
||||||
@@ -162,18 +172,18 @@ static void rtc_restore(void *arg)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Attempt to set RTC to a sane value ... */
|
/* Attempt to set RTC to a sane value ... */
|
||||||
tv.tv_sec = RTC_TIMESTAMP_BEGIN_2000;
|
tv.tv_sec = rtc_date_fallback;
|
||||||
if (!gmtime_r(&tv.tv_sec, &tm))
|
if (!gmtime_r(&tv.tv_sec, &tm))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
logit(LOG_NOTICE, "Resetting RTC to kernel default, 2000-01-01 00:00.");
|
logit(LOG_NOTICE, "Resetting RTC to kernel default, %s.", rtc_timestamp);
|
||||||
rc = 2;
|
rc = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
||||||
tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */
|
tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */
|
||||||
tv.tv_sec = mktime(&tm);
|
tv.tv_sec = mktime(&tm);
|
||||||
if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
if (tv.tv_sec == (time_t)-1 || tv.tv_sec < rtc_date_fallback) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
@@ -199,6 +209,13 @@ static plugin_t plugin = {
|
|||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
{
|
{
|
||||||
|
struct tm tm = { 0 };
|
||||||
|
|
||||||
|
if (!strptime(rtc_timestamp, "%Y-%m-%d %H:%M", &tm))
|
||||||
|
rtc_date_fallback = mktime(&tm);
|
||||||
|
else
|
||||||
|
rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
|
||||||
|
|
||||||
plugin_register(&plugin);
|
plugin_register(&plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-8
@@ -1,6 +1,6 @@
|
|||||||
/* System condition event monitor for the Finit condition engine
|
/* System condition event monitor for the Finit condition engine
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -63,7 +63,7 @@ static void sys_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
cond += strlen(COND_BASE) + 1;
|
cond += strlen(COND_BASE) + 1;
|
||||||
_d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0);
|
dbg("cond: %s set: %d", cond, (mask & IN_CREATE) ? 1 : 0);
|
||||||
if (!cond_update(cond))
|
if (!cond_update(cond))
|
||||||
unlink(path);
|
unlink(path);
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ static void sys_scandir(struct iwatch *iw, char *dir, int len)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < gl.gl_pathc; i++) {
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
_d("scan found %s", gl.gl_pathv[i]);
|
dbg("scan found %s", gl.gl_pathv[i]);
|
||||||
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||||
}
|
}
|
||||||
globfree(&gl);
|
globfree(&gl);
|
||||||
@@ -97,7 +97,7 @@ static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
|||||||
struct iwatch_path *iwp;
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
paste(path, sizeof(path), dir, name);
|
paste(path, sizeof(path), dir, name);
|
||||||
_d("path: %s", path);
|
dbg("path: %s", path);
|
||||||
|
|
||||||
iwp = iwatch_find_by_path(iw, path);
|
iwp = iwatch_find_by_path(iw, path);
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ static void sys_callback(void *arg, int fd, int events)
|
|||||||
|
|
||||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
if (sz <= 0) {
|
if (sz <= 0) {
|
||||||
_pe("invalid inotify event");
|
err(1, "invalid inotify event");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
@@ -136,7 +136,7 @@ static void sys_callback(void *arg, int fd, int events)
|
|||||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
dbg("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||||
if (!ev->mask)
|
if (!ev->mask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -160,24 +160,28 @@ static void sys_init(void *arg)
|
|||||||
char sysdir[MAX_ARG_LEN];
|
char sysdir[MAX_ARG_LEN];
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
|
mkpath(_PATH_COND, 0755);
|
||||||
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
|
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
|
err(1, "Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = realpath(_PATH_CONDSYS, NULL);
|
path = realpath(_PATH_CONDSYS, NULL);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
|
err(1, "Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
|
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
|
||||||
iwatch_exit(&iw_sys);
|
iwatch_exit(&iw_sys);
|
||||||
|
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
|
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
|
||||||
|
.depends = { "bootmisc", },
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
+6
-6
@@ -1,7 +1,7 @@
|
|||||||
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
|
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
|
||||||
*
|
*
|
||||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||||
* Copyright (c) 2013-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2013-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -55,7 +55,7 @@ static void setup(void)
|
|||||||
|
|
||||||
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0)
|
if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0)
|
||||||
_pe("Failed starting TTY watcher");
|
err(1, "Failed starting TTY watcher");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_tty(char *tty, size_t len, int creat)
|
static void do_tty(char *tty, size_t len, int creat)
|
||||||
@@ -67,10 +67,10 @@ static void do_tty(char *tty, size_t len, int creat)
|
|||||||
svc = svc_find_by_tty(name);
|
svc = svc_find_by_tty(name);
|
||||||
if (svc) {
|
if (svc) {
|
||||||
if (svc_is_blocked(svc) && creat) {
|
if (svc_is_blocked(svc) && creat) {
|
||||||
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
|
dbg("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
|
||||||
svc_start(svc);
|
svc_start(svc);
|
||||||
} else if (svc->pid) {
|
} else if (svc->pid) {
|
||||||
_d("%s: missing ...", svc_ident(svc, NULL, 0));
|
dbg("%s: missing ...", svc_ident(svc, NULL, 0));
|
||||||
svc_missing(svc);
|
svc_missing(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ static void tty_watcher(void *arg, int fd, int events)
|
|||||||
|
|
||||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
if (sz <= 0) {
|
if (sz <= 0) {
|
||||||
_pe("invalid inotify event");
|
err(1, "invalid inotify event");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
@@ -103,7 +103,7 @@ static void tty_watcher(void *arg, int fd, int events)
|
|||||||
if (!ev->mask)
|
if (!ev->mask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("tty %s, event: 0x%08x", ev->name, ev->mask);
|
dbg("tty %s, event: 0x%08x", ev->name, ev->mask);
|
||||||
do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
|
do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
/* Setup and save random seed at boot/shutdown
|
/* Setup and save random seed at boot/shutdown
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -51,7 +51,7 @@ static void setup(void *arg)
|
|||||||
int fd, err;
|
int fd, err;
|
||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ static void setup(void *arg)
|
|||||||
umask(prev);
|
umask(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
print_desc("Initializing random number generator", NULL);
|
print_desc("Seeding random number generator", NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simply copying our saved entropy to /dev/urandom doesn't
|
* Simply copying our saved entropy to /dev/urandom doesn't
|
||||||
@@ -144,7 +144,7 @@ static void save(void *arg)
|
|||||||
mode_t prev;
|
mode_t prev;
|
||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -1,6 +1,6 @@
|
|||||||
/* User-defined condition event monitor for the Finit condition engine
|
/* User-defined condition event monitor for the Finit condition engine
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -52,7 +52,7 @@ static void usr_callback(void *arg, int fd, int events)
|
|||||||
|
|
||||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
if (sz <= 0) {
|
if (sz <= 0) {
|
||||||
_pe("invalid inotify event");
|
err(1, "invalid inotify event");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
@@ -65,7 +65,7 @@ static void usr_callback(void *arg, int fd, int events)
|
|||||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
dbg("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||||
if (!ev->mask)
|
if (!ev->mask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -84,14 +84,15 @@ static void usr_init(void *arg)
|
|||||||
char usrdir[MAX_ARG_LEN];
|
char usrdir[MAX_ARG_LEN];
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
|
mkpath(_PATH_COND, 0755);
|
||||||
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
|
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
|
err(1, "Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = realpath(_PATH_CONDUSR, NULL);
|
path = realpath(_PATH_CONDUSR, NULL);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
|
err(1, "Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +105,7 @@ static void usr_init(void *arg)
|
|||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
|
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
|
||||||
|
.depends = { "bootmisc", },
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
+4
-33
@@ -1,6 +1,6 @@
|
|||||||
/* Console setup (for X)
|
/* Console setup (for X)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -36,47 +36,18 @@
|
|||||||
|
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
const char *username = "nobody";
|
|
||||||
char line[LINE_SIZE];
|
|
||||||
mode_t prev;
|
|
||||||
#ifdef PAM_CONSOLE
|
|
||||||
int fd;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
prev = umask(0);
|
if (fexist("/var/run/console/console.lock") && which("pam_console_apply"))
|
||||||
|
|
||||||
makedir("/var/run/console", 01777);
|
|
||||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
|
||||||
touch(line);
|
|
||||||
|
|
||||||
#ifdef PAM_CONSOLE
|
|
||||||
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
|
|
||||||
write(fd, username, strlen(username));
|
|
||||||
close(fd);
|
|
||||||
run("pam_console_apply", "pam-console");
|
run("pam_console_apply", "pam-console");
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
makedir("/tmp/.X11-unix", 01777);
|
|
||||||
makedir("/tmp/.ICE-unix", 01777);
|
|
||||||
|
|
||||||
if (whichp("restorecon"))
|
|
||||||
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix", "restorecon");
|
|
||||||
|
|
||||||
umask(prev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_NETWORK_UP] = {
|
.hook[HOOK_SVC_PLUGIN] = { .cb = setup },
|
||||||
.cb = setup
|
|
||||||
},
|
|
||||||
.depends = { "bootmisc", }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ initctl
|
|||||||
keventd
|
keventd
|
||||||
logit
|
logit
|
||||||
reboot
|
reboot
|
||||||
|
runparts
|
||||||
sulogin
|
sulogin
|
||||||
watchdogd
|
watchdogd
|
||||||
Makefile
|
Makefile
|
||||||
|
|||||||
+12
-3
@@ -8,7 +8,7 @@ AM_LDFLAGS = -export-dynamic
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
sbin_PROGRAMS = finit initctl
|
sbin_PROGRAMS = finit initctl
|
||||||
pkglibexec_PROGRAMS = getty logit
|
pkglibexec_PROGRAMS = getty logit runparts
|
||||||
if SULOGIN
|
if SULOGIN
|
||||||
pkglibexec_PROGRAMS += sulogin
|
pkglibexec_PROGRAMS += sulogin
|
||||||
endif
|
endif
|
||||||
@@ -32,6 +32,11 @@ keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
|
|||||||
keventd_CFLAGS += $(lite_CFLAGS)
|
keventd_CFLAGS += $(lite_CFLAGS)
|
||||||
keventd_LDADD = $(lite_LIBS)
|
keventd_LDADD = $(lite_LIBS)
|
||||||
|
|
||||||
|
runparts_SOURCES = runparts.c
|
||||||
|
runparts_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||||
|
runparts_CFLAGS += $(lite_CFLAGS)
|
||||||
|
runparts_LDADD = $(lite_LIBS)
|
||||||
|
|
||||||
sulogin_SOURCES = sulogin.c
|
sulogin_SOURCES = sulogin.c
|
||||||
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
|
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||||
sulogin_LDADD = -lcrypt
|
sulogin_LDADD = -lcrypt
|
||||||
@@ -42,8 +47,10 @@ logit_CFLAGS += $(lite_CFLAGS)
|
|||||||
logit_LDADD = $(lite_LIBS)
|
logit_LDADD = $(lite_LIBS)
|
||||||
|
|
||||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||||
|
client.c client.h \
|
||||||
cond.c cond-w.c cond.h \
|
cond.c cond-w.c cond.h \
|
||||||
conf.c conf.h \
|
conf.c conf.h \
|
||||||
|
devmon.c devmon.h \
|
||||||
exec.c finit.c finit.h \
|
exec.c finit.c finit.h \
|
||||||
stty.c \
|
stty.c \
|
||||||
helpers.c helpers.h \
|
helpers.c helpers.h \
|
||||||
@@ -52,11 +59,12 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
|
|||||||
mdadm.c mount.c \
|
mdadm.c mount.c \
|
||||||
pid.c pid.h \
|
pid.c pid.h \
|
||||||
plugin.c plugin.h private.h \
|
plugin.c plugin.h private.h \
|
||||||
schedule.c schedule.h \
|
runparts.c schedule.c schedule.h \
|
||||||
service.c service.h \
|
service.c service.h \
|
||||||
sig.c sig.h \
|
sig.c sig.h \
|
||||||
sm.c sm.h \
|
sm.c sm.h \
|
||||||
svc.c svc.h \
|
svc.c svc.h \
|
||||||
|
tmpfiles.c tmpfiles.h \
|
||||||
tty.c tty.h \
|
tty.c tty.h \
|
||||||
util.c util.h \
|
util.c util.h \
|
||||||
utmp-api.c utmp-api.h
|
utmp-api.c utmp-api.h
|
||||||
@@ -67,6 +75,7 @@ endif
|
|||||||
pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.h \
|
pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.h \
|
||||||
plugin.h svc.h service.h
|
plugin.h svc.h service.h
|
||||||
|
|
||||||
|
finit_CPPFLAGS = $(AM_CPPFLAGS) -D__FINIT__
|
||||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||||
@@ -78,7 +87,7 @@ endif
|
|||||||
|
|
||||||
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||||
client.c client.h cond.c cond.h reboot.c \
|
client.c client.h cond.c cond.h reboot.c \
|
||||||
serv.c serv.h svc.h util.c util.h
|
serv.c serv.h svc.h util.c util.h log.h
|
||||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||||
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
|
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* External client API, using UNIX domain socket.
|
/* External client API, using UNIX domain socket.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -50,7 +50,6 @@
|
|||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
extern svc_t *wdog;
|
|
||||||
static uev_t api_watcher;
|
static uev_t api_watcher;
|
||||||
|
|
||||||
static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
|
static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
|
||||||
@@ -63,8 +62,10 @@ static int stop(svc_t *svc, void *user_data)
|
|||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
service_timeout_cancel(svc);
|
||||||
svc_stop(svc);
|
svc_stop(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -74,8 +75,10 @@ static int start(svc_t *svc, void *user_data)
|
|||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
service_timeout_cancel(svc);
|
||||||
svc_start(svc);
|
svc_start(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -93,6 +96,7 @@ static int restart(svc_t *svc, void *user_data)
|
|||||||
if (!svc_is_running(svc))
|
if (!svc_is_running(svc))
|
||||||
return start(svc, user_data);
|
return start(svc, user_data);
|
||||||
|
|
||||||
|
service_timeout_cancel(svc);
|
||||||
service_stop(svc);
|
service_stop(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
|
||||||
@@ -106,6 +110,8 @@ static int reload(svc_t *svc, void *user_data)
|
|||||||
|
|
||||||
if (svc_is_blocked(svc))
|
if (svc_is_blocked(svc))
|
||||||
svc_start(svc);
|
svc_start(svc);
|
||||||
|
else
|
||||||
|
service_timeout_cancel(svc);
|
||||||
|
|
||||||
svc_mark_dirty(svc);
|
svc_mark_dirty(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
@@ -206,26 +212,15 @@ static svc_t *do_find(char *buf, size_t len)
|
|||||||
|
|
||||||
static svc_t *do_find_byc(char *buf, size_t len)
|
static svc_t *do_find_byc(char *buf, size_t len)
|
||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
|
||||||
char *input;
|
char *input;
|
||||||
|
|
||||||
input = sanitize(buf, len);
|
input = sanitize(buf, len);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
_d("Invalid input");
|
dbg("Invalid input");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
return svc_find_by_cond(input);
|
||||||
char cond[MAX_COND_LEN];
|
|
||||||
|
|
||||||
mkcond(svc, cond, sizeof(cond));
|
|
||||||
_d("Comparing input %s with cond %s", input, cond);
|
|
||||||
if (string_compare(cond, input))
|
|
||||||
return svc;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("None found");
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bypass_shutdown(void *);
|
static void bypass_shutdown(void *);
|
||||||
@@ -268,25 +263,25 @@ static int do_reboot(int cmd, int timeout, char *buf, size_t len)
|
|||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case INIT_CMD_REBOOT:
|
case INIT_CMD_REBOOT:
|
||||||
_d("reboot");
|
dbg("reboot");
|
||||||
halt = SHUT_REBOOT;
|
halt = SHUT_REBOOT;
|
||||||
service_runlevel(6);
|
service_runlevel(6);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_HALT:
|
case INIT_CMD_HALT:
|
||||||
_d("halt");
|
dbg("halt");
|
||||||
halt = SHUT_HALT;
|
halt = SHUT_HALT;
|
||||||
service_runlevel(0);
|
service_runlevel(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_POWEROFF:
|
case INIT_CMD_POWEROFF:
|
||||||
_d("poweroff");
|
dbg("poweroff");
|
||||||
halt = SHUT_OFF;
|
halt = SHUT_OFF;
|
||||||
service_runlevel(0);
|
service_runlevel(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_SUSPEND:
|
case INIT_CMD_SUSPEND:
|
||||||
_d("suspend");
|
dbg("suspend");
|
||||||
sync();
|
sync();
|
||||||
rc = suspend();
|
rc = suspend();
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@@ -327,7 +322,7 @@ static void send_svc(int sd, svc_t *svc)
|
|||||||
|
|
||||||
len = write(sd, svc, sizeof(*svc));
|
len = write(sd, svc, sizeof(*svc));
|
||||||
if (len != sizeof(*svc))
|
if (len != sizeof(*svc))
|
||||||
_d("Failed sending svc_t to client");
|
dbg("Failed sending svc_t to client");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void api_cb(uev_t *w, void *arg, int events)
|
static void api_cb(uev_t *w, void *arg, int events)
|
||||||
@@ -339,7 +334,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
|
|
||||||
sd = accept(w->fd, NULL, NULL);
|
sd = accept(w->fd, NULL, NULL);
|
||||||
if (sd < 0) {
|
if (sd < 0) {
|
||||||
_pe("Failed serving API request");
|
err(1, "Failed serving API request");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,14 +355,33 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
if (ECONNRESET == errno)
|
if (ECONNRESET == errno)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
errx(1, "Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||||
_e("Invalid initctl request");
|
errx(1, "Invalid initctl request");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rq.cmd) {
|
||||||
|
case INIT_CMD_RUNLVL:
|
||||||
|
case INIT_CMD_RELOAD:
|
||||||
|
case INIT_CMD_START_SVC:
|
||||||
|
case INIT_CMD_RESTART_SVC:
|
||||||
|
case INIT_CMD_STOP_SVC:
|
||||||
|
case INIT_CMD_RELOAD_SVC:
|
||||||
|
case INIT_CMD_REBOOT:
|
||||||
|
case INIT_CMD_HALT:
|
||||||
|
case INIT_CMD_POWEROFF:
|
||||||
|
case INIT_CMD_SUSPEND:
|
||||||
|
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||||
|
warnx("Unsupported command in runlevel S and 6.");
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +394,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
case '0'...'9':
|
case '0'...'9':
|
||||||
_d("Setting new runlevel %c", rq.runlevel);
|
dbg("Setting new runlevel %c", rq.runlevel);
|
||||||
lvl = rq.runlevel - '0';
|
lvl = rq.runlevel - '0';
|
||||||
if (lvl == 0)
|
if (lvl == 0)
|
||||||
halt = SHUT_OFF;
|
halt = SHUT_OFF;
|
||||||
@@ -390,41 +404,41 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_d("Unsupported runlevel: %d", rq.runlevel);
|
dbg("Unsupported runlevel: %d", rq.runlevel);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_DEBUG:
|
case INIT_CMD_DEBUG:
|
||||||
_d("debug");
|
dbg("debug");
|
||||||
log_debug();
|
log_debug();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
||||||
_d("reload");
|
dbg("reload");
|
||||||
service_reload_dynamic();
|
service_reload_dynamic();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_START_SVC:
|
case INIT_CMD_START_SVC:
|
||||||
_d("start %s", rq.data);
|
dbg("start %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_start(rq.data, sizeof(rq.data));
|
result = do_start(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_RESTART_SVC:
|
case INIT_CMD_RESTART_SVC:
|
||||||
_d("restart %s", rq.data);
|
dbg("restart %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_restart(rq.data, sizeof(rq.data));
|
result = do_restart(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_STOP_SVC:
|
case INIT_CMD_STOP_SVC:
|
||||||
_d("stop %s", rq.data);
|
dbg("stop %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_stop(rq.data, sizeof(rq.data));
|
result = do_stop(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_RELOAD_SVC:
|
case INIT_CMD_RELOAD_SVC:
|
||||||
_d("reload %s", rq.data);
|
dbg("reload %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_reload(rq.data, sizeof(rq.data));
|
result = do_reload(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
@@ -438,7 +452,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_GET_RUNLEVEL:
|
case INIT_CMD_GET_RUNLEVEL:
|
||||||
_d("get runlevel");
|
dbg("get runlevel");
|
||||||
rq.runlevel = runlevel;
|
rq.runlevel = runlevel;
|
||||||
rq.sleeptime = prevlevel;
|
rq.sleeptime = prevlevel;
|
||||||
break;
|
break;
|
||||||
@@ -451,17 +465,17 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_ACK:
|
case INIT_CMD_ACK:
|
||||||
_d("Client failed reading ACK");
|
dbg("Client failed reading ACK");
|
||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
case INIT_CMD_WDOG_HELLO:
|
case INIT_CMD_WDOG_HELLO:
|
||||||
_d("wdog hello");
|
dbg("wdog hello");
|
||||||
if (rq.runlevel <= 0) {
|
if (rq.runlevel <= 0) {
|
||||||
result = 1;
|
result = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
dbg("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
||||||
svc = svc_find_by_pid(rq.runlevel);
|
svc = svc_find_by_pid(rq.runlevel);
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
||||||
@@ -485,7 +499,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_SVC_ITER:
|
case INIT_CMD_SVC_ITER:
|
||||||
// _d("svc iter, first: %d", rq.runlevel);
|
// dbg("svc iter, first: %d", rq.runlevel);
|
||||||
/*
|
/*
|
||||||
* XXX: This severely limits the number of
|
* XXX: This severely limits the number of
|
||||||
* simultaneous client connections, but will
|
* simultaneous client connections, but will
|
||||||
@@ -496,32 +510,46 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
case INIT_CMD_SVC_QUERY:
|
case INIT_CMD_SVC_QUERY:
|
||||||
_d("svc query: %s", rq.data);
|
dbg("svc query: %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_query(rq.data, sizeof(rq.data));
|
result = do_query(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_SVC_FIND:
|
case INIT_CMD_SVC_FIND:
|
||||||
_d("svc find: %s", rq.data);
|
dbg("svc find: %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
|
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
|
||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
case INIT_CMD_SVC_FIND_BYC:
|
case INIT_CMD_SVC_FIND_BYC:
|
||||||
_d("svc find by cond: %s", rq.data);
|
dbg("svc find by cond: %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
send_svc(sd, do_find_byc(rq.data, sizeof(rq.data)));
|
send_svc(sd, do_find_byc(rq.data, sizeof(rq.data)));
|
||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
case INIT_CMD_SIGNAL:
|
case INIT_CMD_SIGNAL:
|
||||||
/* runlevel is reused for signal */
|
/* runlevel is reused for signal */
|
||||||
_d("svc signal %d: %s", rq.runlevel, rq.data);
|
dbg("svc signal %d: %s", rq.runlevel, rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
|
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case INIT_CMD_NOTIFY_SOCKET:
|
||||||
|
svc = svc_find_by_pid(rq.runlevel);
|
||||||
|
if (!svc) {
|
||||||
|
errx(1, "Unknown PID, cannot register notify socket");
|
||||||
|
result = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uev_io_init(ctx, &svc->notify_watcher, service_notify_cb, svc, sd, UEV_READ)) {
|
||||||
|
err(1, "Falied initializing %s readiness notifier", svc_ident(svc, NULL, 0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return; /* Don't close sd, used for notify */
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_d("Unsupported cmd: %d", rq.cmd);
|
dbg("Unsupported cmd: %d", rq.cmd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +559,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
rq.cmd = INIT_CMD_ACK;
|
rq.cmd = INIT_CMD_ACK;
|
||||||
len = write(sd, &rq, sizeof(rq));
|
len = write(sd, &rq, sizeof(rq));
|
||||||
if (len != sizeof(rq))
|
if (len != sizeof(rq))
|
||||||
_d("Failed sending ACK/NACK back to client");
|
dbg("Failed sending ACK/NACK back to client");
|
||||||
}
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
@@ -542,7 +570,7 @@ leave:
|
|||||||
error:
|
error:
|
||||||
api_exit();
|
api_exit();
|
||||||
if (api_init(w->ctx))
|
if (api_init(w->ctx))
|
||||||
_e("Unrecoverable error on API socket");
|
errx(1, "Unrecoverable error on API socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
int api_init(uev_ctx_t *ctx)
|
int api_init(uev_ctx_t *ctx)
|
||||||
@@ -555,10 +583,10 @@ int api_init(uev_ctx_t *ctx)
|
|||||||
int uid, gid;
|
int uid, gid;
|
||||||
int sd;
|
int sd;
|
||||||
|
|
||||||
_d("Setting up external API socket ...");
|
dbg("Setting up external API socket ...");
|
||||||
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
||||||
if (-1 == sd) {
|
if (-1 == sd) {
|
||||||
_pe("Failed starting external API socket");
|
err(1, "Failed starting external API socket");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,14 +602,14 @@ int api_init(uev_ctx_t *ctx)
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (chown(INIT_SOCKET, uid, gid))
|
if (chown(INIT_SOCKET, uid, gid))
|
||||||
_pe("Failed setting group %s on %s", DEFGROUP, INIT_SOCKET);
|
err(1, "Failed setting group %s on %s", DEFGROUP, INIT_SOCKET);
|
||||||
|
|
||||||
umask(oldmask);
|
umask(oldmask);
|
||||||
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
|
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
_pe("Failed initializing API socket");
|
err(1, "Failed initializing API socket");
|
||||||
umask(oldmask);
|
umask(oldmask);
|
||||||
close(sd);
|
close(sd);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
+29
-29
@@ -1,6 +1,6 @@
|
|||||||
/* Finit control group support functions
|
/* Finit control group support functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -63,16 +63,16 @@ static void cgset(const char *path, char *ctrl, char *prop)
|
|||||||
{
|
{
|
||||||
char *val;
|
char *val;
|
||||||
|
|
||||||
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
|
dbg("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
|
||||||
if (!path || !ctrl) {
|
if (!path || !ctrl) {
|
||||||
_e("Missing path or controller, skipping!");
|
errx(1, "Missing path or controller, skipping!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prop) {
|
if (!prop) {
|
||||||
prop = strchr(ctrl, '.');
|
prop = strchr(ctrl, '.');
|
||||||
if (!prop) {
|
if (!prop) {
|
||||||
_e("Invalid cgroup ctrl syntax: %s", ctrl);
|
errx(1, "Invalid cgroup ctrl syntax: %s", ctrl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,20 +81,20 @@ static void cgset(const char *path, char *ctrl, char *prop)
|
|||||||
|
|
||||||
val = strchr(prop, ':');
|
val = strchr(prop, ':');
|
||||||
if (!val) {
|
if (!val) {
|
||||||
_e("Missing cgroup ctrl value, prop %s", prop);
|
errx(1, "Missing cgroup ctrl value, prop %s", prop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*val++ = 0;
|
*val++ = 0;
|
||||||
|
|
||||||
/* disallow sneaky relative paths */
|
/* disallow sneaky relative paths */
|
||||||
if (strstr(ctrl, "..") || strstr(prop, "..")) {
|
if (strstr(ctrl, "..") || strstr(prop, "..")) {
|
||||||
_e("Possible security violation; '..' not allowed in cgroup config!");
|
errx(1, "Possible security violation; '..' not allowed in cgroup config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("%s/%s.%s <= %s", path, ctrl, prop, val);
|
dbg("%s/%s.%s <= %s", path, ctrl, prop, val);
|
||||||
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
|
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
|
||||||
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
|
err(1, "Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -106,16 +106,16 @@ static void group_init(char *path, int leaf, const char *cfg)
|
|||||||
{
|
{
|
||||||
char *ptr, *s;
|
char *ptr, *s;
|
||||||
|
|
||||||
_d("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
|
dbg("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
|
||||||
if (!fisdir(path)) {
|
if (!fisdir(path)) {
|
||||||
if (mkdir(path, 0755)) {
|
if (mkdir(path, 0755)) {
|
||||||
_pe("Failed creating cgroup %s", path);
|
err(1, "Failed creating cgroup %s", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enable detected controllers on domain groups */
|
/* enable detected controllers on domain groups */
|
||||||
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
|
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
|
||||||
_pe("Failed enabling %s for %s", controllers, path);
|
err(1, "Failed enabling %s for %s", controllers, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cfg || !cfg[0])
|
if (!cfg || !cfg[0])
|
||||||
@@ -123,14 +123,14 @@ static void group_init(char *path, int leaf, const char *cfg)
|
|||||||
|
|
||||||
s = strdupa(cfg);
|
s = strdupa(cfg);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
_pe("Failed activating cgroup cfg for %s", path);
|
err(1, "Failed activating cgroup cfg for %s", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("%s <=> %s", path, s);
|
dbg("%s <=> %s", path, s);
|
||||||
ptr = strtok(s, ",");
|
ptr = strtok(s, ",");
|
||||||
while (ptr) {
|
while (ptr) {
|
||||||
_d("ptr: %s", ptr);
|
dbg("ptr: %s", ptr);
|
||||||
if (!strncmp("mem.", ptr, 4))
|
if (!strncmp("mem.", ptr, 4))
|
||||||
cgset(path, "memory", &ptr[4]);
|
cgset(path, "memory", &ptr[4]);
|
||||||
else
|
else
|
||||||
@@ -144,7 +144,7 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
|
|||||||
{
|
{
|
||||||
char path[256];
|
char path[256];
|
||||||
|
|
||||||
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
dbg("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
||||||
if (pid < 0 || pid == 1) {
|
if (pid < 0 || pid == 1) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -156,7 +156,7 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
|
|||||||
|
|
||||||
/* move process to new group */
|
/* move process to new group */
|
||||||
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
|
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
|
||||||
_pe("Failed moving pid %d to group %s", pid, path);
|
err(1, "Failed moving pid %d to group %s", pid, path);
|
||||||
|
|
||||||
strlcat(path, "/cgroup.events", sizeof(path));
|
strlcat(path, "/cgroup.events", sizeof(path));
|
||||||
|
|
||||||
@@ -211,13 +211,13 @@ static void cgroup_handle_event(char *event, uint32_t mask)
|
|||||||
char *ptr;
|
char *ptr;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
_d("event: '%s', mask: %08x", event, mask);
|
dbg("event: '%s', mask: %08x", event, mask);
|
||||||
if (!(mask & IN_MODIFY))
|
if (!(mask & IN_MODIFY))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fp = fopen(event, "r");
|
fp = fopen(event, "r");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
_d("Failed opening %s, skipping ...", event);
|
dbg("Failed opening %s, skipping ...", event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
|
|||||||
|
|
||||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
if (sz <= 0) {
|
if (sz <= 0) {
|
||||||
_pe("invalid inotify event");
|
err(1, "invalid inotify event");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
@@ -287,7 +287,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
|
|||||||
cgroup_handle_event(iwp->path, ev->mask);
|
cgroup_handle_event(iwp->path, ev->mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_AUTO_RELOAD
|
#ifdef AUTO_RELOAD
|
||||||
if (conf_any_change())
|
if (conf_any_change())
|
||||||
service_reload_dynamic();
|
service_reload_dynamic();
|
||||||
#endif
|
#endif
|
||||||
@@ -364,12 +364,12 @@ int cgroup_add(char *name, char *cfg, int is_protected)
|
|||||||
if (!cg) {
|
if (!cg) {
|
||||||
cg = malloc(sizeof(struct cg));
|
cg = malloc(sizeof(struct cg));
|
||||||
if (!cg) {
|
if (!cg) {
|
||||||
_pe("Failed allocating 'struct cg' for %s", name);
|
err(1, "Failed allocating 'struct cg' for %s", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
cg->name = strdup(name);
|
cg->name = strdup(name);
|
||||||
if (!cg->name) {
|
if (!cg->name) {
|
||||||
_pe("Failed setting cgroup name %s", name);
|
err(1, "Failed setting cgroup name %s", name);
|
||||||
free(cg);
|
free(cg);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -379,7 +379,7 @@ int cgroup_add(char *name, char *cfg, int is_protected)
|
|||||||
|
|
||||||
cg->cfg = strdup(cfg);
|
cg->cfg = strdup(cfg);
|
||||||
if (!cg->cfg) {
|
if (!cg->cfg) {
|
||||||
_pe("Failed add/update of cgroup %s", name);
|
err(1, "Failed add/update of cgroup %s", name);
|
||||||
TAILQ_REMOVE(&cgroups, cg, link);
|
TAILQ_REMOVE(&cgroups, cg, link);
|
||||||
free(cg->name);
|
free(cg->name);
|
||||||
free(cg);
|
free(cg);
|
||||||
@@ -414,7 +414,7 @@ int cgroup_del(char *dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rmdir(dir) && errno != ENOENT) {
|
if (rmdir(dir) && errno != ENOENT) {
|
||||||
_d("Failed removing %s: %s", dir, strerror(errno));
|
dbg("Failed removing %s: %s", dir, strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +469,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
|||||||
else if (errno == EPERM) /* Probably inside an unpriviliged container */
|
else if (errno == EPERM) /* Probably inside an unpriviliged container */
|
||||||
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
|
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
|
||||||
else
|
else
|
||||||
_pe("Failed mounting cgroup v2");
|
err(1, "Failed mounting cgroup v2");
|
||||||
avail = 0;
|
avail = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -478,7 +478,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
|||||||
/* Find available controllers */
|
/* Find available controllers */
|
||||||
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
|
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
|
err(1, "Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
|
||||||
abort:
|
abort:
|
||||||
umount(FINIT_CGPATH);
|
umount(FINIT_CGPATH);
|
||||||
avail = 0;
|
avail = 0;
|
||||||
@@ -505,7 +505,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
|||||||
|
|
||||||
/* Enable all controllers */
|
/* Enable all controllers */
|
||||||
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) {
|
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) {
|
||||||
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
|
err(1, "Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +515,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
|||||||
goto abort;
|
goto abort;
|
||||||
|
|
||||||
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
||||||
_pe("Failed setting up cgroup.events watcher");
|
err(1, "Failed setting up cgroup.events watcher");
|
||||||
iwatch_exit(&iw_cgroup);
|
iwatch_exit(&iw_cgroup);
|
||||||
close(fd);
|
close(fd);
|
||||||
goto abort;
|
goto abort;
|
||||||
@@ -529,7 +529,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
|||||||
|
|
||||||
/* Move ourselves to init (best effort, otherwise run in 'root' group */
|
/* Move ourselves to init (best effort, otherwise run in 'root' group */
|
||||||
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) {
|
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) {
|
||||||
_pe("Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init");
|
err(1, "Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init");
|
||||||
uev_io_stop(&cgw);
|
uev_io_stop(&cgw);
|
||||||
iwatch_exit(&iw_cgroup);
|
iwatch_exit(&iw_cgroup);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/* Finit control group support functions
|
/* Finit control group support functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
#include "cgutil.h"
|
#include "cgutil.h"
|
||||||
#include "initctl.h"
|
#include "initctl.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
#define CDIM plain ? "" : "\e[2m"
|
#define CDIM plain ? "" : "\e[2m"
|
||||||
#define CRST plain ? "" : "\e[0m"
|
#define CRST plain ? "" : "\e[0m"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/* Cgroup utility functions for initctl
|
/* Cgroup utility functions for initctl
|
||||||
*
|
*
|
||||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+61
-28
@@ -1,6 +1,6 @@
|
|||||||
/* External client API, using UNIX domain socket.
|
/* External client API, using UNIX domain socket.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "initctl.h"
|
#include "log.h"
|
||||||
|
|
||||||
static int sd = -1;
|
static int sd = -1;
|
||||||
|
|
||||||
@@ -40,15 +40,15 @@ int client_connect(void)
|
|||||||
|
|
||||||
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||||
if (-1 == sd) {
|
if (-1 == sd) {
|
||||||
WARN("Failed creating UNIX domain socket");
|
warn("Failed creating UNIX domain socket");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||||
if (errno != ENOENT)
|
if (errno != ENOENT)
|
||||||
WARNX("Failed connecting to finit");
|
warnx("Failed connecting to finit");
|
||||||
close(sd);
|
close(sd);
|
||||||
return -1;
|
sd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sd;
|
return sd;
|
||||||
@@ -69,10 +69,15 @@ int client_disconnect(void)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int client_send(struct init_request *rq, ssize_t len)
|
/* Only valid after client_connect() */
|
||||||
|
int client_socket(void)
|
||||||
|
{
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int client_request(struct init_request *rq, ssize_t len)
|
||||||
{
|
{
|
||||||
struct pollfd pfd = { 0 };
|
struct pollfd pfd = { 0 };
|
||||||
int result = 255;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (client_connect() == -1)
|
if (client_connect() == -1)
|
||||||
@@ -81,13 +86,23 @@ int client_send(struct init_request *rq, ssize_t len)
|
|||||||
pfd.fd = sd;
|
pfd.fd = sd;
|
||||||
pfd.events = POLLOUT;
|
pfd.events = POLLOUT;
|
||||||
if (poll(&pfd, 1, 2000) <= 0) {
|
if (poll(&pfd, 1, 2000) <= 0) {
|
||||||
WARN("Timed out waiting for Finit, errno %d", errno);
|
warn("Timed out waiting for Finit, errno %d", errno);
|
||||||
goto exit;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write(sd, rq, len) != len) {
|
if (write(sd, rq, len) != len) {
|
||||||
WARN("Failed communicating with Finit, errno %d", errno);
|
warn("Failed communicating with Finit, errno %d", errno);
|
||||||
goto exit;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exception, only command that don't need to wait for a
|
||||||
|
* response from Finit. Registers the client socket as a
|
||||||
|
* readiness notification socket with Finit
|
||||||
|
*/
|
||||||
|
if (rq->cmd == INIT_CMD_NOTIFY_SOCKET) {
|
||||||
|
rq->cmd = INIT_CMD_ACK;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pfd.fd = sd;
|
pfd.fd = sd;
|
||||||
@@ -95,26 +110,45 @@ int client_send(struct init_request *rq, ssize_t len)
|
|||||||
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
|
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
|
||||||
if (rc) {
|
if (rc) {
|
||||||
if (errno == EINTR) /* shutdown/reboot */
|
if (errno == EINTR) /* shutdown/reboot */
|
||||||
goto exit;
|
return -1;
|
||||||
WARN("poll(), errno %d", errno);
|
warn("poll(), errno %d", errno);
|
||||||
} else
|
} else
|
||||||
WARNX("Timed out waiting for reply from Finit.");
|
warnx("Timed out waiting for reply from Finit.");
|
||||||
goto exit;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read(sd, rq, len) != len) {
|
if (read(sd, rq, len) != len) {
|
||||||
WARN("Failed reading reply from Finit, errno %d", errno);
|
warn("Failed reading reply from Finit, errno %d", errno);
|
||||||
goto exit;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rq->cmd == INIT_CMD_NACK)
|
if (rq->cmd == INIT_CMD_NACK)
|
||||||
result = 1;
|
return -1;
|
||||||
else
|
|
||||||
result = 0;
|
return 0;
|
||||||
exit:
|
}
|
||||||
|
|
||||||
|
int client_send(struct init_request *rq, ssize_t len)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (client_request(rq, len))
|
||||||
|
rc = 255;
|
||||||
|
|
||||||
client_disconnect();
|
client_disconnect();
|
||||||
|
|
||||||
return result;
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int client_command(int cmd)
|
||||||
|
{
|
||||||
|
struct init_request rq = {
|
||||||
|
.magic = INIT_MAGIC,
|
||||||
|
.runlevel = getpid(),
|
||||||
|
.cmd = cmd,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client_request(&rq, sizeof(rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
svc_t *client_svc_iterator(int first)
|
svc_t *client_svc_iterator(int first)
|
||||||
@@ -144,14 +178,14 @@ svc_t *client_svc_iterator(int first)
|
|||||||
|
|
||||||
return &svc;
|
return &svc;
|
||||||
error:
|
error:
|
||||||
WARN("Failed communicating with finit, error %d", errno);
|
warn("Failed communicating with finit, error %d", errno);
|
||||||
client_disconnect();
|
client_disconnect();
|
||||||
sd = -1;
|
sd = -1;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
svc_t *do_cmd(int cmd, const char *arg)
|
static svc_t *do_find(int cmd, const char *arg)
|
||||||
{
|
{
|
||||||
struct init_request rq = {
|
struct init_request rq = {
|
||||||
.magic = INIT_MAGIC,
|
.magic = INIT_MAGIC,
|
||||||
@@ -175,20 +209,19 @@ svc_t *do_cmd(int cmd, const char *arg)
|
|||||||
return &svc;
|
return &svc;
|
||||||
error:
|
error:
|
||||||
client_disconnect();
|
client_disconnect();
|
||||||
WARN("Failed communicating with finit, error %d", errno);
|
warn("Failed communicating with finit, error %d", errno);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
svc_t *client_svc_find(const char *arg)
|
svc_t *client_svc_find(const char *arg)
|
||||||
{
|
{
|
||||||
return do_cmd(INIT_CMD_SVC_FIND, arg);
|
return do_find(INIT_CMD_SVC_FIND, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
svc_t *client_svc_find_by_cond(const char *arg)
|
svc_t *client_svc_find_by_cond(const char *arg)
|
||||||
{
|
{
|
||||||
return do_cmd(INIT_CMD_SVC_FIND_BYC, arg);
|
return do_find(INIT_CMD_SVC_FIND_BYC, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,6 @@
|
|||||||
/* External client API, using UNIX domain socket.
|
/* External client API, using UNIX domain socket.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,8 +29,12 @@
|
|||||||
|
|
||||||
int client_connect (void);
|
int client_connect (void);
|
||||||
int client_disconnect (void);
|
int client_disconnect (void);
|
||||||
|
int client_socket (void);
|
||||||
|
|
||||||
|
int client_request (struct init_request *rq, ssize_t len);
|
||||||
int client_send (struct init_request *rq, ssize_t len);
|
int client_send (struct init_request *rq, ssize_t len);
|
||||||
|
int client_command (int cmd);
|
||||||
|
|
||||||
svc_t *client_svc_iterator (int first);
|
svc_t *client_svc_iterator (int first);
|
||||||
svc_t *client_svc_find (const char *arg);
|
svc_t *client_svc_find (const char *arg);
|
||||||
svc_t *client_svc_find_by_cond (const char *arg);
|
svc_t *client_svc_find_by_cond (const char *arg);
|
||||||
|
|||||||
+76
-50
@@ -60,7 +60,7 @@ void cond_boot_parse(char *arg)
|
|||||||
|
|
||||||
node = malloc(sizeof(*node));
|
node = malloc(sizeof(*node));
|
||||||
if (!node) {
|
if (!node) {
|
||||||
_pe("Out of memory cannot track boot conditions");
|
err(1, "Out of memory cannot track boot conditions");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,10 @@ static void cond_boot_strap(void)
|
|||||||
while (ptr) {
|
while (ptr) {
|
||||||
char cond[strlen(ptr) + 6];
|
char cond[strlen(ptr) + 6];
|
||||||
|
|
||||||
|
if (strncmp(ptr, "boot/", 5))
|
||||||
snprintf(cond, sizeof(cond), "boot/%s", ptr);
|
snprintf(cond, sizeof(cond), "boot/%s", ptr);
|
||||||
|
else
|
||||||
|
snprintf(cond, sizeof(cond), "%s", ptr);
|
||||||
cond_set_oneshot_noupdate(cond);
|
cond_set_oneshot_noupdate(cond);
|
||||||
|
|
||||||
ptr = strtok(NULL, ",");
|
ptr = strtok(NULL, ",");
|
||||||
@@ -172,7 +175,7 @@ static void cond_bump_reconf(void)
|
|||||||
rgen++;
|
rgen++;
|
||||||
|
|
||||||
if (cond_set_gen(_PATH_RECONF, rgen))
|
if (cond_set_gen(_PATH_RECONF, rgen))
|
||||||
_pe("Failed setting %s to gen %d", _PATH_RECONF, rgen);
|
err(1, "Failed setting %s to gen %d", _PATH_RECONF, rgen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cond_checkpath(const char *path)
|
static int cond_checkpath(const char *path)
|
||||||
@@ -182,41 +185,45 @@ static int cond_checkpath(const char *path)
|
|||||||
strlcpy(buf, path, sizeof(buf));
|
strlcpy(buf, path, sizeof(buf));
|
||||||
dir = dirname(buf);
|
dir = dirname(buf);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
_e("Invalid path '%s' for condition", path);
|
errx(1, "Invalid path '%s' for condition", path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mkpath(dir, 0755) && errno != EEXIST) {
|
if (mkpath(dir, 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
err(1, "Failed creating dir '%s' for condition '%s'", dir, path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Intended for 'service/foo/`, nothing else */
|
static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||||
|
{
|
||||||
|
const char *cond, *ptr;
|
||||||
|
|
||||||
|
if (ftw->level == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ptr = strstr(fpath, COND_BASE);
|
||||||
|
if (!ptr) {
|
||||||
|
warnx("%s does not seem to be a Finit condition, skipping", fpath);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remove(fpath))
|
||||||
|
err(1, "Failed removing condition %s", fpath);
|
||||||
|
|
||||||
|
cond = ptr + strlen(COND_BASE) + 1;
|
||||||
|
cond_update(cond);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void cond_delpath(const char *path)
|
static void cond_delpath(const char *path)
|
||||||
{
|
{
|
||||||
char fn[PATH_MAX];
|
nftw(path, do_delete, 20, FTW_DEPTH);
|
||||||
struct dirent *d;
|
if (remove(path) && errno != ENOENT)
|
||||||
DIR *dir;
|
err(1, "Failed removing condition path %s", path);
|
||||||
|
|
||||||
dir = opendir(path);
|
|
||||||
if (!dir)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while ((d = readdir(dir))) {
|
|
||||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
paste(fn, sizeof(fn), path, d->d_name);
|
|
||||||
if (remove(fn))
|
|
||||||
_pe("Failed removing condition path, file %s", fn);
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
if (unlink(path) && errno != EISDIR)
|
|
||||||
_pe("Failed removing condition path %s", fn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cond_set_path(const char *path, enum cond_state next)
|
int cond_set_path(const char *path, enum cond_state next)
|
||||||
@@ -224,20 +231,19 @@ int cond_set_path(const char *path, enum cond_state next)
|
|||||||
enum cond_state prev;
|
enum cond_state prev;
|
||||||
unsigned int rgen;
|
unsigned int rgen;
|
||||||
|
|
||||||
_d("%s <= %d", path, next);
|
dbg("%s <= %d", path, next);
|
||||||
|
|
||||||
rgen = cond_get_gen(_PATH_RECONF);
|
|
||||||
if (!rgen) {
|
|
||||||
_e("Unable to read configuration generation (%s)", path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = cond_get_path(path);
|
prev = cond_get_path(path);
|
||||||
|
|
||||||
switch (next) {
|
switch (next) {
|
||||||
case COND_ON:
|
case COND_ON:
|
||||||
if (cond_checkpath(path))
|
if (cond_checkpath(path))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
rgen = cond_get_gen(_PATH_RECONF);
|
||||||
|
if (!rgen) {
|
||||||
|
errx(1, "Unable to read configuration generation (%s)", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
cond_set_gen(path, rgen);
|
cond_set_gen(path, rgen);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -250,14 +256,14 @@ int cond_set_path(const char *path, enum cond_state next)
|
|||||||
cond_delpath(path);
|
cond_delpath(path);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_pe("Failed removing condition '%s'", path);
|
err(1, "Failed removing condition '%s'", path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_e("Invalid condition state");
|
errx(1, "Invalid condition state");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,13 +276,19 @@ int cond_update(const char *name)
|
|||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
int affects = 0;
|
int affects = 0;
|
||||||
|
|
||||||
_d("%s", name);
|
dbg("%s", name);
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
affects++;
|
affects++;
|
||||||
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
|
dbg("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
|
||||||
|
/* Fix bug #314: race condition between crashing services and conditions */
|
||||||
|
if (svc_is_restart(svc) && cond_get_agg(svc->cond) == COND_OFF) {
|
||||||
|
dbg("%s: cancel timer & unblock => WAITING state.", name ?: "nil");
|
||||||
|
service_timeout_cancel(svc);
|
||||||
|
svc_unblock(svc);
|
||||||
|
}
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +297,7 @@ int cond_update(const char *name)
|
|||||||
|
|
||||||
int cond_set_noupdate(const char *name)
|
int cond_set_noupdate(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
dbg("%s", name);
|
||||||
if (string_compare(name, "nop"))
|
if (string_compare(name, "nop"))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -297,7 +309,7 @@ int cond_set_noupdate(const char *name)
|
|||||||
|
|
||||||
void cond_set(const char *name)
|
void cond_set(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
dbg("%s", name);
|
||||||
if (cond_set_noupdate(name))
|
if (cond_set_noupdate(name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -312,13 +324,13 @@ int cond_set_oneshot_noupdate(const char *name)
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
path = cond_path(name);
|
path = cond_path(name);
|
||||||
_d("%s => %s", name, path);
|
dbg("%s => %s", name, path);
|
||||||
|
|
||||||
if (cond_checkpath(path))
|
if (cond_checkpath(path))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
|
if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
|
||||||
_pe("Failed creating onshot cond %s", name);
|
err(1, "Failed creating onshot cond %s", name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +339,7 @@ int cond_set_oneshot_noupdate(const char *name)
|
|||||||
|
|
||||||
void cond_set_oneshot(const char *name)
|
void cond_set_oneshot(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
dbg("%s", name);
|
||||||
if (cond_set_oneshot_noupdate(name))
|
if (cond_set_oneshot_noupdate(name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -336,7 +348,7 @@ void cond_set_oneshot(const char *name)
|
|||||||
|
|
||||||
int cond_clear_noupdate(const char *name)
|
int cond_clear_noupdate(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
dbg("%s", name);
|
||||||
if (string_compare(name, "nop"))
|
if (string_compare(name, "nop"))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -348,7 +360,7 @@ int cond_clear_noupdate(const char *name)
|
|||||||
|
|
||||||
void cond_clear(const char *name)
|
void cond_clear(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
dbg("%s", name);
|
||||||
if (cond_clear_noupdate(name))
|
if (cond_clear_noupdate(name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -357,7 +369,7 @@ void cond_clear(const char *name)
|
|||||||
|
|
||||||
void cond_reload(void)
|
void cond_reload(void)
|
||||||
{
|
{
|
||||||
_d("");
|
dbg("");
|
||||||
|
|
||||||
cond_bump_reconf();
|
cond_bump_reconf();
|
||||||
}
|
}
|
||||||
@@ -374,12 +386,12 @@ static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct
|
|||||||
|
|
||||||
nm = strstr((char *)fpath, COND_BASE);
|
nm = strstr((char *)fpath, COND_BASE);
|
||||||
if (!nm) {
|
if (!nm) {
|
||||||
_e("Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de");
|
errx(1, "Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nm += strlen(COND_BASE);
|
nm += strlen(COND_BASE);
|
||||||
_d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
|
dbg("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
|
||||||
if (set)
|
if (set)
|
||||||
cond_set(nm);
|
cond_set(nm);
|
||||||
else
|
else
|
||||||
@@ -404,7 +416,7 @@ static int deassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
|||||||
*/
|
*/
|
||||||
void cond_reassert(const char *pat)
|
void cond_reassert(const char *pat)
|
||||||
{
|
{
|
||||||
_d("%s", pat);
|
dbg("%s", pat);
|
||||||
nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
|
nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,16 +425,25 @@ void cond_reassert(const char *pat)
|
|||||||
*/
|
*/
|
||||||
void cond_deassert(const char *pat)
|
void cond_deassert(const char *pat)
|
||||||
{
|
{
|
||||||
_d("%s", pat);
|
dbg("%s", pat);
|
||||||
nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
|
nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we have bootstrapped enough of the system to use conditions.
|
||||||
|
* Will answer 'No' before bootstrap done *and* at shutdown/reboot.
|
||||||
|
*/
|
||||||
|
int cond_is_available(void)
|
||||||
|
{
|
||||||
|
return fisdir(_PATH_COND);
|
||||||
|
}
|
||||||
|
|
||||||
void cond_init(void)
|
void cond_init(void)
|
||||||
{
|
{
|
||||||
char path[MAX_ARG_LEN];
|
char path[MAX_ARG_LEN];
|
||||||
|
|
||||||
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
|
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating condition base directory '%s'", _PATH_COND);
|
err(1, "Failed creating condition base directory '%s'", _PATH_COND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,6 +451,11 @@ void cond_init(void)
|
|||||||
cond_boot_strap();
|
cond_boot_strap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cond_exit(void)
|
||||||
|
{
|
||||||
|
cond_delpath(_PATH_COND);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
+1
-1
@@ -112,7 +112,7 @@ enum cond_state cond_get_agg(const char *names)
|
|||||||
|
|
||||||
int cond_affects(const char *name, const char *names)
|
int cond_affects(const char *name, const char *names)
|
||||||
{
|
{
|
||||||
static char conds[MAX_COND_LEN];
|
char conds[MAX_COND_LEN];
|
||||||
char *cond;
|
char *cond;
|
||||||
|
|
||||||
if (!name || !names)
|
if (!name || !names)
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
#define COND_BASE "finit/cond"
|
#define COND_BASE "finit/cond"
|
||||||
|
#define COND_DEV "dev/"
|
||||||
#define COND_PID "pid/"
|
#define COND_PID "pid/"
|
||||||
#define COND_SYS "sys/"
|
#define COND_SYS "sys/"
|
||||||
#define COND_USR "usr/"
|
#define COND_USR "usr/"
|
||||||
|
|
||||||
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||||
|
#define _PATH_CONDDEV _PATH_COND COND_DEV
|
||||||
#define _PATH_CONDPID _PATH_COND COND_PID
|
#define _PATH_CONDPID _PATH_COND COND_PID
|
||||||
#define _PATH_CONDSYS _PATH_COND COND_SYS
|
#define _PATH_CONDSYS _PATH_COND COND_SYS
|
||||||
#define _PATH_CONDUSR _PATH_COND COND_USR
|
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||||
@@ -46,7 +48,10 @@ int cond_clear_noupdate(const char *name);
|
|||||||
void cond_reassert (const char *pat);
|
void cond_reassert (const char *pat);
|
||||||
void cond_deassert (const char *pat);
|
void cond_deassert (const char *pat);
|
||||||
|
|
||||||
|
int cond_is_available(void);
|
||||||
|
|
||||||
void cond_init (void);
|
void cond_init (void);
|
||||||
|
void cond_exit (void);
|
||||||
|
|
||||||
#endif /* FINIT_COND_H_ */
|
#endif /* FINIT_COND_H_ */
|
||||||
|
|
||||||
|
|||||||
+400
-93
@@ -1,6 +1,6 @@
|
|||||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
|
#include "devmon.h"
|
||||||
#include "iwatch.h"
|
#include "iwatch.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define BOOTSTRAP (runlevel == 0)
|
#define BOOTSTRAP (runlevel == INIT_LEVEL)
|
||||||
|
|
||||||
int logfile_size_max = 200000; /* 200 kB */
|
int logfile_size_max = 200000; /* 200 kB */
|
||||||
int logfile_count_max = 5;
|
int logfile_count_max = 5;
|
||||||
@@ -61,6 +62,9 @@ static TAILQ_HEAD(, env_entry) env_list = TAILQ_HEAD_INITIALIZER(env_list);
|
|||||||
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
||||||
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
|
char *fsck_mode = "";
|
||||||
|
char *fsck_repair = "-p";
|
||||||
|
|
||||||
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
|
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
|
||||||
|
|
||||||
struct conf_change {
|
struct conf_change {
|
||||||
@@ -69,14 +73,18 @@ struct conf_change {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct iwatch iw_conf;
|
static struct iwatch iw_conf;
|
||||||
|
static int iwatch_fd;
|
||||||
static uev_t etcw;
|
static uev_t etcw;
|
||||||
|
|
||||||
static TAILQ_HEAD(, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
static TAILQ_HEAD(, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
||||||
|
|
||||||
|
static char *path;
|
||||||
|
static char *shell;
|
||||||
|
|
||||||
static int parse_conf(char *file, int is_rcsd);
|
static int parse_conf(char *file, int is_rcsd);
|
||||||
static void drop_changes(void);
|
static void drop_changes(void);
|
||||||
|
|
||||||
int get_bool(char *arg, int default_value)
|
static int get_bool(char *arg, int default_value)
|
||||||
{
|
{
|
||||||
if (!arg)
|
if (!arg)
|
||||||
goto fallback;
|
goto fallback;
|
||||||
@@ -89,7 +97,19 @@ fallback:
|
|||||||
return default_value;
|
return default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int validate_arg(char *arg, const char *opt)
|
||||||
|
{
|
||||||
|
if (!arg) {
|
||||||
|
errx(1, "option %s missing argument, skipping.", opt);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* finit.cond = foo (=> <boot/foo>)
|
||||||
|
* finit.config = /path/to/etc/alt-finit.conf
|
||||||
* finit.debug = [on,off]
|
* finit.debug = [on,off]
|
||||||
* finit.fstab = /path/to/etc/fstab.aternative
|
* finit.fstab = /path/to/etc/fstab.aternative
|
||||||
* finit.status = [on,off] (compat finit.show_status)
|
* finit.status = [on,off] (compat finit.show_status)
|
||||||
@@ -103,26 +123,39 @@ static void parse_finit_opts(char *opt)
|
|||||||
if (arg)
|
if (arg)
|
||||||
*arg++ = 0;
|
*arg++ = 0;
|
||||||
|
|
||||||
|
if (string_compare(opt, "cond")) {
|
||||||
|
if (validate_arg(arg, "finit.cond"))
|
||||||
|
return;
|
||||||
|
cond_boot_parse(arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string_compare(opt, "config")) {
|
||||||
|
if (validate_arg(arg, "finit.config"))
|
||||||
|
return;
|
||||||
|
if (finit_conf)
|
||||||
|
free(finit_conf);
|
||||||
|
finit_conf = strdup(arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (string_compare(opt, "debug")) {
|
if (string_compare(opt, "debug")) {
|
||||||
debug = get_bool(arg, 1);
|
debug = get_bool(arg, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string_compare(opt, "fstab")) {
|
if (string_compare(opt, "fstab")) {
|
||||||
fstab = arg;
|
if (validate_arg(arg, "finit.fstab"))
|
||||||
return;
|
return;
|
||||||
}
|
if (fstab)
|
||||||
|
free(fstab);
|
||||||
if (string_compare(opt, "cond")) {
|
fstab = strdup(arg);
|
||||||
cond_boot_parse(arg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string_compare(opt, "status_style")) {
|
if (string_compare(opt, "status_style")) {
|
||||||
if (!arg) {
|
if (validate_arg(arg, "finit.status_style"))
|
||||||
_e("status_style option requires an argument, skipping.");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (string_compare(arg, "old") || string_compare(arg, "classic"))
|
if (string_compare(arg, "old") || string_compare(arg, "classic"))
|
||||||
show_progress(PROGRESS_CLASSIC);
|
show_progress(PROGRESS_CLASSIC);
|
||||||
@@ -137,6 +170,43 @@ static void parse_finit_opts(char *opt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_fsck_opts(char *opt)
|
||||||
|
{
|
||||||
|
char *arg;
|
||||||
|
|
||||||
|
arg = strchr(opt, '=');
|
||||||
|
if (arg)
|
||||||
|
*arg++ = 0;
|
||||||
|
|
||||||
|
if (string_compare(opt, "mode")) {
|
||||||
|
if (validate_arg(arg, "fsck.mode"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!strcmp(arg, "skip"))
|
||||||
|
fsck_mode = NULL;
|
||||||
|
if (!strcmp(arg, "auto"))
|
||||||
|
fsck_mode = "";
|
||||||
|
if (!strcmp(arg, "force"))
|
||||||
|
fsck_mode = "-f";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string_compare(opt, "repair")) {
|
||||||
|
if (validate_arg(arg, "fsck.repair"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!strcmp(arg, "no"))
|
||||||
|
fsck_repair = "-n";
|
||||||
|
if (!strcmp(arg, "preen"))
|
||||||
|
fsck_repair = "-p";
|
||||||
|
if (!strcmp(arg, "yes"))
|
||||||
|
fsck_repair = "-y";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When runlevel (single integer) is given on the command line,
|
* When runlevel (single integer) is given on the command line,
|
||||||
* it overrides the runlevel in finit.conf and the built-in
|
* it overrides the runlevel in finit.conf and the built-in
|
||||||
@@ -157,7 +227,7 @@ static int parse_runlevel(char *arg)
|
|||||||
|
|
||||||
num = strtonum(arg, 1, 9, &err);
|
num = strtonum(arg, 1, 9, &err);
|
||||||
if (err || num == 6) {
|
if (err || num == 6) {
|
||||||
_d("Not a valid runlevel (%s), valid levels are [1-9], excluding 6, skipping.", arg);
|
dbg("Not a valid runlevel (%s), valid levels are [1-9], excluding 6, skipping.", arg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,10 +241,17 @@ static void parse_arg(char *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strncmp(arg, "fsck.", 5)) {
|
||||||
|
parse_fsck_opts(&arg[5]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RESCUE_MODE
|
||||||
if (string_compare(arg, "rescue") || string_compare(arg, "recover")) {
|
if (string_compare(arg, "rescue") || string_compare(arg, "recover")) {
|
||||||
rescue = 1;
|
rescue = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (string_compare(arg, "single") || string_compare(arg, "S")) {
|
if (string_compare(arg, "single") || string_compare(arg, "S")) {
|
||||||
single = 1;
|
single = 1;
|
||||||
@@ -210,7 +287,7 @@ static void parse_kernel_cmdline(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmdline = chomp(line);
|
cmdline = chomp(line);
|
||||||
_d("%s", cmdline);
|
dbg("%s", cmdline);
|
||||||
|
|
||||||
while ((tok = strtok(cmdline, " \t"))) {
|
while ((tok = strtok(cmdline, " \t"))) {
|
||||||
cmdline = NULL;
|
cmdline = NULL;
|
||||||
@@ -239,7 +316,7 @@ static void parse_kernel_loglevel(void)
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
ptr = chomp(line);
|
ptr = chomp(line);
|
||||||
_d("%s", ptr);
|
dbg("%s", ptr);
|
||||||
val = atoi(ptr);
|
val = atoi(ptr);
|
||||||
if (val >= 7)
|
if (val >= 7)
|
||||||
kerndebug = 1;
|
kerndebug = 1;
|
||||||
@@ -250,6 +327,13 @@ static void parse_kernel_loglevel(void)
|
|||||||
*/
|
*/
|
||||||
void conf_parse_cmdline(int argc, char *argv[])
|
void conf_parse_cmdline(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
/* Set up defaults */
|
||||||
|
fstab = strdup(FINIT_FSTAB);
|
||||||
|
finit_conf = strdup(FINIT_CONF);
|
||||||
|
finit_rcsd = strdup(FINIT_RCSD);
|
||||||
|
path = getenv("PATH");
|
||||||
|
shell = getenv("SHELL");
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
parse_arg(argv[i]);
|
parse_arg(argv[i]);
|
||||||
|
|
||||||
@@ -274,12 +358,41 @@ void conf_reset_env(void)
|
|||||||
free(node);
|
free(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path)
|
||||||
|
setenv("PATH", path, 1);
|
||||||
|
else
|
||||||
setenv("PATH", _PATH_STDPATH, 1);
|
setenv("PATH", _PATH_STDPATH, 1);
|
||||||
|
if (shell)
|
||||||
|
setenv("SHELL", shell, 1);
|
||||||
|
else
|
||||||
setenv("SHELL", _PATH_BSHELL, 1);
|
setenv("SHELL", _PATH_BSHELL, 1);
|
||||||
setenv("LOGNAME", "root", 1);
|
setenv("LOGNAME", "root", 1);
|
||||||
setenv("USER", "root", 1);
|
setenv("USER", "root", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sourced mainly by initctl and other Finit helper tools
|
||||||
|
*/
|
||||||
|
void conf_saverc(void)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
mkpath(_PATH_VARRUN "finit", 0755);
|
||||||
|
fp = fopen(_PATH_VARRUN "finit/.initrc", "w");
|
||||||
|
if (!fp) {
|
||||||
|
err(1, "failed creating .finitrc");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "FINIT_CONF=%s\n", finit_conf);
|
||||||
|
fprintf(fp, "FINIT_RCSD=%s\n", finit_rcsd);
|
||||||
|
fprintf(fp, "FINIT_CGPATH=%s\n", FINIT_CGPATH);
|
||||||
|
fprintf(fp, "INIT_SOCKET=%s\n", INIT_SOCKET);
|
||||||
|
fprintf(fp, "INIT_MAGIC=0x%08x\n", INIT_MAGIC);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line
|
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line
|
||||||
* from finit.conf, or other .conf file. Note, PATH is always reset in
|
* from finit.conf, or other .conf file. Note, PATH is always reset in
|
||||||
@@ -342,7 +455,7 @@ static void parse_env(char *line)
|
|||||||
|
|
||||||
node = malloc(sizeof(*node));
|
node = malloc(sizeof(*node));
|
||||||
if (!node) {
|
if (!node) {
|
||||||
_pe("Out of memory cannot track env vars");
|
err(1, "Out of memory cannot track env vars");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node->name = strdup(key);
|
node->name = strdup(key);
|
||||||
@@ -375,7 +488,7 @@ static void kmod_load(char *mod)
|
|||||||
char module[64] = { 0 };
|
char module[64] = { 0 };
|
||||||
char cmd[CMD_SIZE];
|
char cmd[CMD_SIZE];
|
||||||
|
|
||||||
if (runlevel != 0)
|
if (runlevel != INIT_LEVEL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Strip args for progress below and kmod_exists() */
|
/* Strip args for progress below and kmod_exists() */
|
||||||
@@ -408,15 +521,16 @@ int conf_parse_runlevels(char *runlevels)
|
|||||||
break;
|
break;
|
||||||
if ('!' == lvl) {
|
if ('!' == lvl) {
|
||||||
not = 1;
|
not = 1;
|
||||||
bitmask = 0x3FE;
|
bitmask = 0x7FE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('s' == lvl || 'S' == lvl)
|
if ('s' == lvl || 'S' == lvl)
|
||||||
lvl = '0';
|
level = INIT_LEVEL;
|
||||||
|
else
|
||||||
level = lvl - '0';
|
level = lvl - '0';
|
||||||
if (level > 9 || level < 0)
|
|
||||||
|
if (level > INIT_LEVEL || level < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (not)
|
if (not)
|
||||||
@@ -432,9 +546,10 @@ void conf_parse_cond(svc_t *svc, char *cond)
|
|||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
char *c;
|
||||||
|
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_e("Invalid service pointer");
|
errx(1, "Invalid service pointer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,16 +574,17 @@ void conf_parse_cond(svc_t *svc, char *cond)
|
|||||||
ptr[i] = 0;
|
ptr[i] = 0;
|
||||||
|
|
||||||
if (i >= sizeof(svc->cond)) {
|
if (i >= sizeof(svc->cond)) {
|
||||||
logit(LOG_WARNING, "Too long event list in declaration of %s: %s", svc_ident(svc, NULL, 0), ptr);
|
logit(LOG_WARNING, "%s: too long list of conditions: %s", svc_ident(svc, NULL, 0), ptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp(ptr, "svc/", 4)) {
|
svc->cond[0] = 0;
|
||||||
logit(LOG_ERR, "Unsupported cond syntax for %s: <%s", svc_ident(svc, NULL, 0), ptr);
|
for (i = 0, c = strtok(ptr, ","); c; c = strtok(NULL, ","), i++) {
|
||||||
return;
|
devmon_add_cond(c);
|
||||||
|
if (i)
|
||||||
|
strlcat(svc->cond, ",", sizeof(svc->cond));
|
||||||
|
strlcat(svc->cond, c, sizeof(svc->cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcpy(svc->cond, ptr, sizeof(svc->cond));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct rlimit_name {
|
struct rlimit_name {
|
||||||
@@ -654,18 +770,29 @@ static int parse_static(char *line, int is_rcsd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (BOOTSTRAP && MATCH_CMD(line, "rcsd ", x)) {
|
||||||
|
if (finit_rcsd) free(finit_rcsd);
|
||||||
|
finit_rcsd = strdup(strip_line(x));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
|
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
|
||||||
if (runparts) free(runparts);
|
if (runparts) free(runparts);
|
||||||
runparts = strdup(strip_line(x));
|
runparts = strdup(strip_line(x));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (BOOTSTRAP && MATCH_CMD(line, "set ", x)) {
|
||||||
|
parse_env(x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (MATCH_CMD(line, "include ", x)) {
|
if (MATCH_CMD(line, "include ", x)) {
|
||||||
char *file = strip_line(x);
|
char *file = strip_line(x);
|
||||||
|
|
||||||
strlcpy(cmd, file, sizeof(cmd));
|
strlcpy(cmd, file, sizeof(cmd));
|
||||||
if (!fexist(cmd)) {
|
if (!fexist(cmd)) {
|
||||||
_e("Cannot find include file %s, absolute path required!", x);
|
errx(1, "Cannot find include file %s, absolute path required!", x);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,6 +844,11 @@ static int parse_static(char *line, int is_rcsd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MATCH_CMD(line, "reboot-delay ", x)) {
|
||||||
|
syncsec = strtonum(strip_line(x), 0, 60, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Periodic check and instability index leveler, seconds
|
* Periodic check and instability index leveler, seconds
|
||||||
*/
|
*/
|
||||||
@@ -732,7 +864,7 @@ static int parse_static(char *line, int is_rcsd)
|
|||||||
|
|
||||||
service_interval = val * 1000; /* to milliseconds */
|
service_interval = val * 1000; /* to milliseconds */
|
||||||
if (disabled)
|
if (disabled)
|
||||||
service_init();
|
service_init(NULL);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -795,12 +927,79 @@ static int parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Very simple and crude implementation, only supports '%i'
|
||||||
|
*/
|
||||||
|
static char *instantiate(char *line, char *name)
|
||||||
|
{
|
||||||
|
char *ptr, *end = strchr(line, 0);
|
||||||
|
char *pos = line;
|
||||||
|
size_t num = 0;
|
||||||
|
|
||||||
|
if (!name[0] || !end)
|
||||||
|
return line;
|
||||||
|
|
||||||
|
while ((ptr = strchr(pos, '%'))) {
|
||||||
|
num++;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = realloc(line, strlen(line) + num * strlen(name) + 1);
|
||||||
|
if (!ptr)
|
||||||
|
return line;
|
||||||
|
|
||||||
|
pos = line = ptr;
|
||||||
|
while ((ptr = strchr(pos, '%'))) {
|
||||||
|
if (!strncmp(ptr, "%i", 2)) {
|
||||||
|
char *rest = &ptr[2];
|
||||||
|
char *next = ptr + strlen(name);
|
||||||
|
|
||||||
|
memmove(next, rest, strlen(rest) + 1);
|
||||||
|
memcpy(ptr, name, strlen(name));
|
||||||
|
|
||||||
|
pos += strlen(name);
|
||||||
|
} else
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_template(const char *file, char *name, size_t len)
|
||||||
|
{
|
||||||
|
char *ptr, *nm;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
ptr = strchr(file, '@');
|
||||||
|
if (!ptr)
|
||||||
|
return 0; /* not a template */
|
||||||
|
|
||||||
|
nm = ptr + 1;
|
||||||
|
ptr = strstr(nm, ".conf");
|
||||||
|
if (!strcmp(nm, ".conf") || !ptr)
|
||||||
|
return 1; /* template itself or invalid */
|
||||||
|
|
||||||
|
while (nm < ptr && i < len - 1)
|
||||||
|
name[i++] = *nm++;
|
||||||
|
name[i] = 0;
|
||||||
|
|
||||||
|
return 1; /* instantiated template */
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_conf(char *file, int is_rcsd)
|
static int parse_conf(char *file, int is_rcsd)
|
||||||
{
|
{
|
||||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
char line[LINE_SIZE] = "";
|
char name[65] = { 0 };
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
|
if (is_template(file, name, sizeof(name))) {
|
||||||
|
if (!name[0]) {
|
||||||
|
dbg("*** Skipping template file %s", file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dbg("*** instantiating %s from %s ...", name, file);
|
||||||
|
}
|
||||||
|
|
||||||
fp = fopen(file, "r");
|
fp = fopen(file, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -811,28 +1010,27 @@ static int parse_conf(char *file, int is_rcsd)
|
|||||||
cgroup_current[0] = 0;
|
cgroup_current[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("*** Parsing %s", file);
|
dbg("*** Parsing %s", file);
|
||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
char *x;
|
char *line;
|
||||||
|
|
||||||
if (!fgets(line, sizeof(line), fp))
|
line = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCCOMM);
|
||||||
|
if (!line)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
chomp(line);
|
|
||||||
tabstospaces(line);
|
tabstospaces(line);
|
||||||
//DEV _d("%s", line);
|
// dbg("raw: %s", line);
|
||||||
|
line = instantiate(line, name);
|
||||||
/* Skip comments, i.e. lines beginning with # */
|
// dbg("ins: %s", line);
|
||||||
if (MATCH_CMD(line, "#", x))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!parse_static(line, is_rcsd))
|
if (!parse_static(line, is_rcsd))
|
||||||
continue;
|
;
|
||||||
if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file))
|
else if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file))
|
||||||
continue;
|
;
|
||||||
|
else
|
||||||
/* Not static or dynamic conf, check if it is a global env. */
|
|
||||||
parse_env(line);
|
parse_env(line);
|
||||||
|
|
||||||
|
free(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -840,17 +1038,41 @@ static int parse_conf(char *file, int is_rcsd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void glob_append(glob_t *gl, int append, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
size_t len;
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len = vsnprintf(NULL, 0, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
path = alloca(++len);
|
||||||
|
if (!path) {
|
||||||
|
warn("failed alloca() in glob_append()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(path, len, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
dbg("conf_reload(): glob %s ...", path);
|
||||||
|
glob(path, append ? GLOB_APPEND : 0, NULL, gl);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reload /etc/finit.conf and all *.conf in /etc/finit.d/
|
* Reload /etc/finit.conf and all *.conf in /etc/finit.d/
|
||||||
*/
|
*/
|
||||||
int conf_reload(void)
|
int conf_reload(void)
|
||||||
{
|
{
|
||||||
size_t i;
|
|
||||||
glob_t gl;
|
glob_t gl;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
/* Set time according to current time zone */
|
/* Set time according to current time zone */
|
||||||
tzset();
|
tzset();
|
||||||
_d("Set time daylight: %d timezone: %ld tzname: %s %s",
|
dbg("Set time daylight: %d timezone: %ld tzname: %s %s",
|
||||||
daylight, timezone, tzname[0], tzname[1]);
|
daylight, timezone, tzname[0], tzname[1]);
|
||||||
|
|
||||||
/* Mark and sweep */
|
/* Mark and sweep */
|
||||||
@@ -863,9 +1085,17 @@ int conf_reload(void)
|
|||||||
*/
|
*/
|
||||||
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When built with --disable-rescue mode many other 'if (rescue)'
|
||||||
|
* code paths are #ifdeffed out. This one, and the ones in the
|
||||||
|
* plugins, are not because a hook or plugin can still trigger
|
||||||
|
* the alternative rescue.conf instead of finit.conf. This for
|
||||||
|
* very advanced use-cases where files on /etc are generated at
|
||||||
|
* bootstrap, including users and passwords, from other sources.
|
||||||
|
*/
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
int rc;
|
int rc;
|
||||||
char line[80] = "tty [12345] rescue";
|
char line[80] = "tty [12345789] rescue";
|
||||||
|
|
||||||
/* If rescue.conf is missing, fall back to a root shell */
|
/* If rescue.conf is missing, fall back to a root shell */
|
||||||
rc = parse_conf(RESCUE_CONF, 0);
|
rc = parse_conf(RESCUE_CONF, 0);
|
||||||
@@ -877,7 +1107,7 @@ int conf_reload(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* First, read /etc/finit.conf */
|
/* First, read /etc/finit.conf */
|
||||||
parse_conf(FINIT_CONF, 0);
|
parse_conf(finit_conf, 0);
|
||||||
|
|
||||||
/* Set global limits */
|
/* Set global limits */
|
||||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||||
@@ -886,25 +1116,45 @@ int conf_reload(void)
|
|||||||
rlim2str(i), lim2str(&global_rlimit[i]));
|
rlim2str(i), lim2str(&global_rlimit[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Next, read all *.conf in /etc/finit.d/ */
|
/*
|
||||||
glob(FINIT_RCSD "/*.conf", 0, NULL, &gl);
|
* Next, read all *.conf in /lib/finit/system and /etc/finit.d/
|
||||||
glob(FINIT_RCSD "/enabled/*.conf", GLOB_APPEND, NULL, &gl);
|
* The system files were previously created at runtime by plugins
|
||||||
|
* but are now regular files that can be overridden by files in
|
||||||
|
* /etc/finit.d -- similar to how tmfiles.d(5) work. E.g., add
|
||||||
|
* an override .conf, or an ignore by symlinking to /dev/null
|
||||||
|
*/
|
||||||
|
glob_append(&gl, 0, "%s/*.conf", finit_rcsd);
|
||||||
|
glob_append(&gl, 1, "%s/*.conf", FINIT_SYSPATH);
|
||||||
|
glob_append(&gl, 1, "%s/enabled/*.conf", finit_rcsd);
|
||||||
|
|
||||||
for (i = 0; i < gl.gl_pathc; i++) {
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
char *path = gl.gl_pathv[i];
|
char *path = gl.gl_pathv[i];
|
||||||
char *rp = NULL;
|
char *rp = NULL;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
size_t len;
|
size_t j, len;
|
||||||
|
|
||||||
|
/* check for FINIT_SYSPATH overrides */
|
||||||
|
for (j = i + 1; j < gl.gl_pathc; j++) {
|
||||||
|
if (strncmp(path, FINIT_SYSPATH, strlen(FINIT_SYSPATH)))
|
||||||
|
continue;
|
||||||
|
if (strcmp(basename(path), basename(gl.gl_pathv[j])))
|
||||||
|
continue;
|
||||||
|
path = NULL; /* replacement later in list, skip this */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path)
|
||||||
|
continue; /* skip, override exists */
|
||||||
|
|
||||||
/* Check that it's an actual file ... beyond any symlinks */
|
/* Check that it's an actual file ... beyond any symlinks */
|
||||||
if (lstat(path, &st)) {
|
if (lstat(path, &st)) {
|
||||||
_d("Skipping %s, cannot access: %s", path, strerror(errno));
|
dbg("Skipping %s, cannot access: %s", path, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Skip directories */
|
/* Skip directories */
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
_d("Skipping directory %s", path);
|
dbg("Skipping directory %s", path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -920,7 +1170,7 @@ int conf_reload(void)
|
|||||||
/* Check that file ends with '.conf' */
|
/* Check that file ends with '.conf' */
|
||||||
len = strlen(path);
|
len = strlen(path);
|
||||||
if (len < 6 || strcmp(&path[len - 5], ".conf"))
|
if (len < 6 || strcmp(&path[len - 5], ".conf"))
|
||||||
_d("Skipping %s, not a Finit .conf file ... ", path);
|
dbg("Skipping %s, not a Finit .conf file ... ", path);
|
||||||
else
|
else
|
||||||
parse_conf(path, 1);
|
parse_conf(path, 1);
|
||||||
|
|
||||||
@@ -933,6 +1183,9 @@ int conf_reload(void)
|
|||||||
/* Mark any reverse deps as chenaged. */
|
/* Mark any reverse deps as chenaged. */
|
||||||
service_update_rdeps();
|
service_update_rdeps();
|
||||||
|
|
||||||
|
/* Prune according to if:[!]ident or if:<[!]cond> */
|
||||||
|
service_mark_unavail();
|
||||||
|
|
||||||
/* Set up top-level cgroups */
|
/* Set up top-level cgroups */
|
||||||
cgroup_config();
|
cgroup_config();
|
||||||
done:
|
done:
|
||||||
@@ -986,39 +1239,48 @@ static void drop_changes(void)
|
|||||||
drop_change(node);
|
drop_change(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_change(char *dir, char *name, uint32_t mask)
|
static int conf_change_act(char *dir, char *name, uint32_t mask)
|
||||||
{
|
{
|
||||||
char fn[strlen(dir) + strlen(name) + 2];
|
char fn[strlen(dir) + strlen(name) + 2];
|
||||||
struct conf_change *node;
|
struct conf_change *node;
|
||||||
|
char *rp = NULL;
|
||||||
|
|
||||||
|
if (name[0])
|
||||||
paste(fn, sizeof(fn), dir, name);
|
paste(fn, sizeof(fn), dir, name);
|
||||||
_d("path: %s mask: %08x", fn, mask);
|
else
|
||||||
|
strlcpy(fn, dir, sizeof(fn));
|
||||||
|
dbg("path: %s mask: %08x", fn, mask);
|
||||||
|
|
||||||
node = conf_find(fn);
|
/* Handle disabling/removal of service */
|
||||||
if (mask & (IN_DELETE | IN_MOVED_FROM)) {
|
rp = realpath(fn, NULL);
|
||||||
drop_change(node);
|
if (!rp) {
|
||||||
return 0;
|
if (errno != ENOENT)
|
||||||
|
goto fail;
|
||||||
|
rp = strdup(fn);
|
||||||
|
if (!rp)
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node = conf_find(rp);
|
||||||
if (node) {
|
if (node) {
|
||||||
_d("Event already registered for %s ...", name);
|
dbg("event already registered for %s ...", name);
|
||||||
|
free(rp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = malloc(sizeof(*node));
|
node = malloc(sizeof(*node));
|
||||||
if (!node)
|
if (!node) {
|
||||||
return 1;
|
free(rp);
|
||||||
|
goto fail;
|
||||||
node->name = strdup(fn);
|
|
||||||
if (!node->name) {
|
|
||||||
free(node);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Event registered for %s, mask 0x%x", fn, mask);
|
node->name = rp;
|
||||||
TAILQ_INSERT_HEAD(&conf_change_list, node, link);
|
TAILQ_INSERT_HEAD(&conf_change_list, node, link);
|
||||||
|
dbg("event registered for %s, mask 0x%x", rp, mask);
|
||||||
return 0;
|
return 0;
|
||||||
|
fail:
|
||||||
|
warn("failed registering %s event mask %08x", fn, mask);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int conf_any_change(void)
|
int conf_any_change(void)
|
||||||
@@ -1048,18 +1310,16 @@ int conf_changed(char *file)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void conf_cb(uev_t *w, void *arg, int events)
|
static int conf_iwatch_read(int fd)
|
||||||
{
|
{
|
||||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
struct inotify_event *ev;
|
struct inotify_event *ev;
|
||||||
ssize_t sz;
|
ssize_t sz;
|
||||||
size_t off;
|
size_t off;
|
||||||
|
|
||||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
if (sz <= 0) {
|
if (sz <= 0)
|
||||||
_pe("invalid inotify event");
|
return -1;
|
||||||
return;
|
|
||||||
}
|
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
|
|
||||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||||
@@ -1075,30 +1335,46 @@ static void conf_cb(uev_t *w, void *arg, int events)
|
|||||||
if (!ev->mask)
|
if (!ev->mask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
dbg("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||||
|
|
||||||
/* Find base path for this event */
|
/* Find base path for this event */
|
||||||
iwp = iwatch_find_by_wd(&iw_conf, ev->wd);
|
iwp = iwatch_find_by_wd(&iw_conf, ev->wd);
|
||||||
if (!iwp)
|
if (!iwp)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (do_change(iwp->path, ev->name, ev->mask)) {
|
if (conf_change_act(iwp->path, ev->name, ev->mask))
|
||||||
_pe(" Out of memory");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void conf_cb(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
if (conf_iwatch_read(w->fd)) {
|
||||||
|
err(1, "invalid inotify event");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_AUTO_RELOAD
|
|
||||||
|
#ifdef AUTO_RELOAD
|
||||||
if (conf_any_change())
|
if (conf_any_change())
|
||||||
service_reload_dynamic();
|
service_reload_dynamic();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void conf_flush_events(void)
|
||||||
|
{
|
||||||
|
while (!conf_iwatch_read(iwatch_fd))
|
||||||
|
dbg("emptying inotify queue ...");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up inotify watcher and load all *.conf in /etc/finit.d/
|
* Set up inotify watcher and load all *.conf in /etc/finit.d/
|
||||||
*/
|
*/
|
||||||
int conf_monitor(void)
|
int conf_monitor(void)
|
||||||
{
|
{
|
||||||
|
char path[strlen(finit_rcsd) + 16];
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1107,10 +1383,12 @@ int conf_monitor(void)
|
|||||||
* have or not have symlinks in place. We need to monitor for
|
* have or not have symlinks in place. We need to monitor for
|
||||||
* changes to either symlink or target.
|
* changes to either symlink or target.
|
||||||
*/
|
*/
|
||||||
rc += iwatch_add(&iw_conf, FINIT_RCSD, IN_ONLYDIR);
|
rc += iwatch_add(&iw_conf, finit_rcsd, IN_ONLYDIR);
|
||||||
rc += iwatch_add(&iw_conf, FINIT_RCSD "/available/", IN_ONLYDIR | IN_DONT_FOLLOW);
|
snprintf(path, sizeof(path), "%s/available/", finit_rcsd);
|
||||||
rc += iwatch_add(&iw_conf, FINIT_RCSD "/enabled/", IN_ONLYDIR | IN_DONT_FOLLOW);
|
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
|
||||||
rc += iwatch_add(&iw_conf, FINIT_CONF, 0);
|
snprintf(path, sizeof(path), "%s/enabled/", finit_rcsd);
|
||||||
|
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
|
||||||
|
rc += iwatch_add(&iw_conf, finit_conf, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Systems with /etc/default, /etc/conf.d, or similar, can also
|
* Systems with /etc/default, /etc/conf.d, or similar, can also
|
||||||
@@ -1131,8 +1409,6 @@ int conf_monitor(void)
|
|||||||
*/
|
*/
|
||||||
int conf_init(uev_ctx_t *ctx)
|
int conf_init(uev_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
int fd;
|
|
||||||
|
|
||||||
/* default hostname */
|
/* default hostname */
|
||||||
hostname = strdup(DEFHOST);
|
hostname = strdup(DEFHOST);
|
||||||
|
|
||||||
@@ -1150,20 +1426,51 @@ int conf_init(uev_ctx_t *ctx)
|
|||||||
/* Initialize global rlimits, e.g. for built-in services */
|
/* Initialize global rlimits, e.g. for built-in services */
|
||||||
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start built-in watchdogd as soon as possible, if enabled
|
||||||
|
*/
|
||||||
|
#ifdef WDT_DEVNODE
|
||||||
|
if (whichp(FINIT_EXECPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
|
||||||
|
service_register(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init name:watchdog :finit " FINIT_EXECPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
|
||||||
|
wdog = svc_find("watchdog", "finit");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Start kernel event daemon as soon as possible, if enabled
|
||||||
|
*/
|
||||||
|
if (whichp(FINIT_EXECPATH_ "/keventd"))
|
||||||
|
service_register(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init " FINIT_EXECPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
|
||||||
|
|
||||||
|
dbg("Allow plugins to register early runlevel 1 run/task/services ...");
|
||||||
|
plugin_run_hooks(HOOK_SVC_PLUGIN);
|
||||||
|
|
||||||
/* Read global rlimits and global cgroup setup from /etc/finit.conf */
|
/* Read global rlimits and global cgroup setup from /etc/finit.conf */
|
||||||
parse_conf(FINIT_CONF, 0);
|
parse_conf(finit_conf, 0);
|
||||||
|
|
||||||
/* prepare /etc watcher */
|
/* prepare /etc watcher */
|
||||||
fd = iwatch_init(&iw_conf);
|
iwatch_fd = iwatch_init(&iw_conf);
|
||||||
if (fd < 0)
|
if (iwatch_fd < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (uev_io_init(ctx, &etcw, conf_cb, NULL, fd, UEV_READ)) {
|
if (uev_io_init(ctx, &etcw, conf_cb, NULL, iwatch_fd, UEV_READ)) {
|
||||||
_pe("Failed setting up I/O callback for /etc watcher");
|
err(1, "Failed setting up I/O callback for /etc watcher");
|
||||||
close(fd);
|
close(iwatch_fd);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Background startup scripts in the runparts directory, if any.
|
||||||
|
*/
|
||||||
|
if (runparts && fisdir(runparts) && !rescue) {
|
||||||
|
char conf[sizeof(_PATH_RUNPARTS) + strlen(runparts) + 100];
|
||||||
|
char *args = debug ? "-d" : "-p";
|
||||||
|
|
||||||
|
snprintf(conf, sizeof(conf), "[S] <int/bootstrap> log:console %s %s %s"
|
||||||
|
" -- Calling runparts %s in the background",
|
||||||
|
_PATH_RUNPARTS, args, runparts, runparts);
|
||||||
|
service_register(SVC_TYPE_TASK, conf, global_rlimit, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -43,6 +43,7 @@ int conf_changed (char *file);
|
|||||||
int conf_monitor (void);
|
int conf_monitor (void);
|
||||||
|
|
||||||
void conf_reset_env (void);
|
void conf_reset_env (void);
|
||||||
|
void conf_saverc (void);
|
||||||
void conf_parse_cmdline (int argc, char *argv[]);
|
void conf_parse_cmdline (int argc, char *argv[]);
|
||||||
int conf_parse_runlevels (char *runlevels);
|
int conf_parse_runlevels (char *runlevels);
|
||||||
void conf_parse_cond (svc_t *svc, char *cond);
|
void conf_parse_cond (svc_t *svc, char *cond);
|
||||||
|
|||||||
+275
@@ -0,0 +1,275 @@
|
|||||||
|
/* Device node monitor for the Finit condition engine
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <paths.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "pid.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "iwatch.h"
|
||||||
|
|
||||||
|
static struct iwatch iw_devmon;
|
||||||
|
static uev_t devw;
|
||||||
|
static int fd;
|
||||||
|
|
||||||
|
struct dev_node {
|
||||||
|
TAILQ_ENTRY(dev_node) link;
|
||||||
|
char *name;
|
||||||
|
size_t refcnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
static TAILQ_HEAD(, dev_node) dev_node_list = TAILQ_HEAD_INITIALIZER(dev_node_list);
|
||||||
|
|
||||||
|
static struct dev_node *find_node(const char *cond)
|
||||||
|
{
|
||||||
|
struct dev_node *node, *tmp;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(node, &dev_node_list, link, tmp) {
|
||||||
|
if (string_compare(node->name, cond))
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drop_node(struct dev_node *node)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
node->refcnt--;
|
||||||
|
if (node->refcnt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&dev_node_list, node, link);
|
||||||
|
free(node->name);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void devmon_add_cond(const char *cond)
|
||||||
|
{
|
||||||
|
struct dev_node *node;
|
||||||
|
|
||||||
|
if (!cond || strncmp(cond, "dev/", 4)) {
|
||||||
|
dbg("no match");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbg("%s", cond);
|
||||||
|
node = find_node(cond);
|
||||||
|
if (node) {
|
||||||
|
node->refcnt++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = malloc(sizeof(*node));
|
||||||
|
if (!node) {
|
||||||
|
fail:
|
||||||
|
warn("failed allocating node %s", cond);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->name = strdup(cond);
|
||||||
|
if (!node->name) {
|
||||||
|
free(node);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
node->refcnt = 1;
|
||||||
|
|
||||||
|
TAILQ_INSERT_TAIL(&dev_node_list, node, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
void devmon_del_cond(const char *cond)
|
||||||
|
{
|
||||||
|
if (!cond || strcmp(cond, "dev/"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
drop_node(find_node(cond));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int devmon_add_path(struct iwatch *iw, char *path)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
ptr = strstr(path, "dev/");
|
||||||
|
if (ptr) {
|
||||||
|
char *slash;
|
||||||
|
|
||||||
|
ptr += 4;
|
||||||
|
slash = strchr(ptr, '/');
|
||||||
|
if (slash) {
|
||||||
|
ptr = slash++;
|
||||||
|
slash = strchr(ptr, '/');
|
||||||
|
if (slash) {
|
||||||
|
dbg("Path too deep, skipping.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iwatch_add1(iw, path, IN_CREATE | IN_DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void devmon_update_conds(char *dir, char *name, uint32_t mask)
|
||||||
|
{
|
||||||
|
char fn[PATH_MAX];
|
||||||
|
char *cond;
|
||||||
|
|
||||||
|
paste(fn, sizeof(fn), dir, name);
|
||||||
|
cond = &fn[1];
|
||||||
|
|
||||||
|
// dbg("path: %s, mask: %08x, cond: %s", fn, mask, cond);
|
||||||
|
if (!find_node(cond)) {
|
||||||
|
// dbg("unregistered, skipping %s", cond);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & IN_CREATE)
|
||||||
|
cond_set(cond);
|
||||||
|
else if (mask & IN_DELETE)
|
||||||
|
cond_clear(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* synthesize events in case of new run dirs */
|
||||||
|
static void devmon_scandir(struct iwatch *iw, char *dir, int len)
|
||||||
|
{
|
||||||
|
char path[len + 6];
|
||||||
|
glob_t gl;
|
||||||
|
size_t i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/*", dir);
|
||||||
|
rc = glob(path, GLOB_NOSORT, NULL, &gl);
|
||||||
|
if (rc && rc != GLOB_NOMATCH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
|
// dbg("scan found %s", gl.gl_pathv[i]);
|
||||||
|
devmon_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||||
|
}
|
||||||
|
globfree(&gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create/remove sub-directory in monitored directory
|
||||||
|
*/
|
||||||
|
static void devmon_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||||
|
{
|
||||||
|
char path[strlen(dir) + strlen(name) + 2];
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
|
paste(path, sizeof(path), dir, name);
|
||||||
|
dbg("path: %s", path);
|
||||||
|
|
||||||
|
iwp = iwatch_find_by_path(iw, path);
|
||||||
|
|
||||||
|
if (mask & IN_CREATE) {
|
||||||
|
if (!iwp) {
|
||||||
|
if (!devmon_add_path(iw, path))
|
||||||
|
devmon_scandir(iw, path, sizeof(path));
|
||||||
|
}
|
||||||
|
} else if (mask & IN_DELETE) {
|
||||||
|
if (iwp)
|
||||||
|
iwatch_del(&iw_devmon, iwp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void devmon_cb(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
|
struct inotify_event *ev;
|
||||||
|
ssize_t sz;
|
||||||
|
size_t off;
|
||||||
|
|
||||||
|
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
|
if (sz <= 0) {
|
||||||
|
err(1, "invalid inotify event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev_buf[sz] = 0;
|
||||||
|
|
||||||
|
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
|
if (off + sizeof(*ev) > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ev = (struct inotify_event *)&ev_buf[off];
|
||||||
|
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!ev->mask)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Find base path for this event */
|
||||||
|
iwp = iwatch_find_by_wd(&iw_devmon, ev->wd);
|
||||||
|
if (!iwp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ev->mask & IN_ISDIR) {
|
||||||
|
devmon_handle_dir(&iw_devmon, iwp->path, ev->name, ev->mask);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg("Got %s", ev->name);
|
||||||
|
if (ev->mask & (IN_CREATE | IN_DELETE))
|
||||||
|
devmon_update_conds(iwp->path, ev->name, ev->mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void devmon_init(uev_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
char dir[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
fd = iwatch_init(&iw_devmon);
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mkpath(pid_runpath(_PATH_CONDDEV, dir, sizeof(dir)), 0755) && errno != EEXIST) {
|
||||||
|
err(1, "Failed creating %s condition directory, %s", COND_DEV, _PATH_CONDDEV);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uev_io_init(ctx, &devw, devmon_cb, NULL, fd, UEV_READ)) {
|
||||||
|
err(1, "Failed setting up I/O callback for /dev watcher");
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(dir, _PATH_DEV, sizeof(dir));
|
||||||
|
if (devmon_add_path(&iw_devmon, dir))
|
||||||
|
iwatch_exit(&iw_devmon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/* Device node monitor for the Finit condition engine
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_DEVMON_H_
|
||||||
|
#define FINIT_DEVMON_H_
|
||||||
|
|
||||||
|
void devmon_add_cond(const char *cond);
|
||||||
|
void devmon_del_cond(const char *cond);
|
||||||
|
|
||||||
|
void devmon_init(uev_ctx_t *ctx);
|
||||||
|
|
||||||
|
#endif /* FINIT_DEVMON_H_ */
|
||||||
+25
-80
@@ -1,7 +1,7 @@
|
|||||||
/* Functions for exec'ing processes
|
/* Functions for exec'ing processes
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
|
|
||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
#include <ctype.h> /* isdigit() */
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
@@ -59,11 +57,11 @@ int complete(char *cmd, int pid)
|
|||||||
|
|
||||||
if (waitpid(pid, &status, 0) == -1) {
|
if (waitpid(pid, &status, 0) == -1) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
_e("Caught unblocked signal waiting for %s, aborting", cmd);
|
warnx("Caught unblocked signal waiting for %s, aborting", cmd);
|
||||||
else if (errno == ECHILD)
|
else if (errno == ECHILD)
|
||||||
_e("Caught SIGCHLD waiting for %s, aborting", cmd);
|
warnx("Caught SIGCHLD waiting for %s, aborting", cmd);
|
||||||
else
|
else
|
||||||
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
|
warnx("Failed starting %s, error %d: %s", cmd, errno, strerror(errno));
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -131,7 +129,7 @@ int run(char *cmd, char *log)
|
|||||||
args[i] = NULL;
|
args[i] = NULL;
|
||||||
|
|
||||||
if (i == NUM_ARGS && arg) {
|
if (i == NUM_ARGS && arg) {
|
||||||
_e("Command too long: %s", cmd);
|
errx(1, "Command too long: %s", cmd);
|
||||||
free(backup);
|
free(backup);
|
||||||
errno = EOVERFLOW;
|
errno = EOVERFLOW;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -166,7 +164,7 @@ int run(char *cmd, char *log)
|
|||||||
|
|
||||||
_exit(rc);
|
_exit(rc);
|
||||||
} else if (-1 == pid) {
|
} else if (-1 == pid) {
|
||||||
_pe("%s", !log ? args[0] : cmd);
|
err(1, "%s", !log ? args[0] : cmd);
|
||||||
if (backup)
|
if (backup)
|
||||||
free(backup);
|
free(backup);
|
||||||
|
|
||||||
@@ -182,11 +180,11 @@ int run(char *cmd, char *log)
|
|||||||
|
|
||||||
result = WEXITSTATUS(status);
|
result = WEXITSTATUS(status);
|
||||||
if (WIFEXITED(status)) {
|
if (WIFEXITED(status)) {
|
||||||
_d("Started '%s' and exit without signal, status: %d", cmd, result);
|
dbg("Started '%s' and exit without signal, status: %d", cmd, result);
|
||||||
} else if (WIFSIGNALED(status)) {
|
} else if (WIFSIGNALED(status)) {
|
||||||
_d("Process '%s' terminated by signal %d", cmd, WTERMSIG(status));
|
dbg("Process '%s' terminated by signal %d", cmd, WTERMSIG(status));
|
||||||
if (!result)
|
if (!result)
|
||||||
result = 1; /* Must alert callee that the command did complete successfully.
|
result = 1; /* Must alert callee the command did not complete successfully.
|
||||||
* This is necessary since not all programs trap signals and
|
* This is necessary since not all programs trap signals and
|
||||||
* change their return code accordingly. --Jocke */
|
* change their return code accordingly. --Jocke */
|
||||||
}
|
}
|
||||||
@@ -272,12 +270,12 @@ int exec_runtask(char *cmd, char *args[])
|
|||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
strlcat(buf, cmd, sizeof(buf));
|
strlcat(buf, cmd, sizeof(buf));
|
||||||
for (i = 1; args[i]; i++) {
|
for (i = 1; args && args[i]; i++) {
|
||||||
strlcat(buf, " ", sizeof(buf));
|
strlcat(buf, " ", sizeof(buf));
|
||||||
strlcat(buf, args[i], sizeof(buf));
|
strlcat(buf, args[i], sizeof(buf));
|
||||||
}
|
}
|
||||||
logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf);
|
logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf);
|
||||||
_d("Calling %s %s", _PATH_BSHELL, buf);
|
dbg("Calling %s %s", _PATH_BSHELL, buf);
|
||||||
|
|
||||||
return execvp(_PATH_BSHELL, argv);
|
return execvp(_PATH_BSHELL, argv);
|
||||||
}
|
}
|
||||||
@@ -419,8 +417,10 @@ int sh(char *tty)
|
|||||||
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||||
len = strlen(_PATH_BSHELL) + 2;
|
len = strlen(_PATH_BSHELL) + 2;
|
||||||
arg0 = malloc(len);
|
arg0 = malloc(len);
|
||||||
if (!arg0)
|
if (!arg0) {
|
||||||
err(1, "Failed allocating memory");
|
err(1, "Failed allocating memory");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||||
args[0] = arg0;
|
args[0] = arg0;
|
||||||
|
|
||||||
@@ -441,75 +441,20 @@ pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run_parts(char *dir, char *cmd)
|
pid_t run_bg(char *cmd, char *args[])
|
||||||
{
|
{
|
||||||
struct dirent **e;
|
pid_t pid;
|
||||||
int i, num;
|
|
||||||
|
|
||||||
num = scandir(dir, &e, NULL, alphasort);
|
|
||||||
if (num < 0) {
|
|
||||||
_d("No files found in %s, skipping ...", dir);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
|
||||||
const char *name = e[i]->d_name;
|
|
||||||
char path[strlen(dir) + strlen(name) + 2];
|
|
||||||
struct stat st;
|
|
||||||
char *argv[4] = {
|
|
||||||
"sh",
|
|
||||||
"-c",
|
|
||||||
path,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
pid_t pid = 0;
|
|
||||||
int status;
|
|
||||||
int exit_status;
|
|
||||||
|
|
||||||
paste(path, sizeof(path), dir, name);
|
|
||||||
if (stat(path, &st)) {
|
|
||||||
_d("Failed stat(%s): %s", path, strerror(errno));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
|
||||||
_d("Skipping %s ...", path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the callee didn't supply a run_parts() argument */
|
|
||||||
if (!cmd) {
|
|
||||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
|
||||||
_d("Checking if %s is a sysvinit startstop script ...", name);
|
|
||||||
if (name[0] == 'S' && isdigit(name[1]))
|
|
||||||
strlcat(path, " start", sizeof(path));
|
|
||||||
else if (name[0] == 'K' && isdigit(name[1]))
|
|
||||||
strlcat(path, " stop", sizeof(path));
|
|
||||||
} else {
|
|
||||||
strlcat(path, cmd, sizeof(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
print_desc("Calling ", path);
|
|
||||||
pid = fork();
|
|
||||||
if (!pid) {
|
|
||||||
sig_unblock();
|
|
||||||
return execvp(_PATH_BSHELL, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
status = complete(path, pid);
|
|
||||||
exit_status = WEXITSTATUS(status);
|
|
||||||
if (WIFEXITED(status) && exit_status)
|
|
||||||
_w("%s exited with status %d", path, exit_status);
|
|
||||||
else if (WIFSIGNALED(status))
|
|
||||||
_w("%s terminated by signad %d", path, WTERMSIG(status));
|
|
||||||
print_result(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (num--)
|
|
||||||
free(e[num]);
|
|
||||||
free(e);
|
|
||||||
|
|
||||||
|
if (access(cmd, X_OK))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
print(-1, "Calling %s in the background", cmd);
|
||||||
|
pid = fork();
|
||||||
|
if (!pid)
|
||||||
|
_exit(exec_runtask(cmd, args));
|
||||||
|
print_result(pid == -1);
|
||||||
|
|
||||||
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+58
-171
@@ -1,7 +1,7 @@
|
|||||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -48,6 +48,7 @@
|
|||||||
#include "cgroup.h"
|
#include "cgroup.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
|
#include "devmon.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
@@ -57,10 +58,8 @@
|
|||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
#include "schedule.h"
|
|
||||||
#include "watchdog.h"
|
|
||||||
|
|
||||||
int runlevel = 0; /* Bootstrap 'S' */
|
int runlevel = INIT_LEVEL; /* Bootstrap 'S' */
|
||||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||||
int cmdlevel = 0; /* runlevel override from cmdline */
|
int cmdlevel = 0; /* runlevel override from cmdline */
|
||||||
int prevlevel = -1;
|
int prevlevel = -1;
|
||||||
@@ -69,11 +68,13 @@ int rescue = 0; /* rescue mode from kernel cmdline */
|
|||||||
int single = 0; /* single user mode from kernel cmdline */
|
int single = 0; /* single user mode from kernel cmdline */
|
||||||
int bootstrap = 1; /* set while bootrapping (for TTYs) */
|
int bootstrap = 1; /* set while bootrapping (for TTYs) */
|
||||||
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
|
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
|
||||||
char *fstab = FINIT_FSTAB;
|
int syncsec = 0; /* reboot delay */
|
||||||
|
char *finit_conf= NULL;
|
||||||
|
char *finit_rcsd= NULL;
|
||||||
|
char *fstab = NULL;
|
||||||
char *sdown = NULL;
|
char *sdown = NULL;
|
||||||
char *network = NULL;
|
char *network = NULL;
|
||||||
char *hostname = NULL;
|
char *hostname = NULL;
|
||||||
char *rcsd = FINIT_RCSD;
|
|
||||||
char *runparts = NULL;
|
char *runparts = NULL;
|
||||||
char *osheading = NULL;
|
char *osheading = NULL;
|
||||||
|
|
||||||
@@ -114,8 +115,8 @@ static int sulogin(int do_reboot)
|
|||||||
{
|
{
|
||||||
int rc = EX_OSFILE;
|
int rc = EX_OSFILE;
|
||||||
char *cmd[] = {
|
char *cmd[] = {
|
||||||
_PATH_SULOGIN,
|
|
||||||
"sulogin",
|
"sulogin",
|
||||||
|
_PATH_SULOGIN,
|
||||||
};
|
};
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
@@ -204,17 +205,17 @@ static int fsck(int pass)
|
|||||||
|
|
||||||
fp = setmntent(fstab, "r");
|
fp = setmntent(fstab, "r");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
_pe("Failed opening fstab: %s", fstab);
|
err(1, "Failed opening fstab: %s", fstab);
|
||||||
sulogin(1);
|
sulogin(1);
|
||||||
}
|
}
|
||||||
_d("Opened %s, pass %d", fstab, pass);
|
dbg("Opened %s, pass %d", fstab, pass);
|
||||||
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) {
|
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) {
|
||||||
int fsck_rc = 0;
|
int fsck_rc = 0;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char cmd[256];
|
char cmd[256];
|
||||||
char *dev;
|
char *dev;
|
||||||
|
|
||||||
_d("got: fsname '%s' dir '%s' type '%s' opts '%s' freq '%d' passno '%d'",
|
dbg("got: fsname '%s' dir '%s' type '%s' opts '%s' freq '%d' passno '%d'",
|
||||||
mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts,
|
mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts,
|
||||||
mnt->mnt_freq, mnt->mnt_passno);
|
mnt->mnt_freq, mnt->mnt_passno);
|
||||||
|
|
||||||
@@ -244,22 +245,22 @@ static int fsck(int pass)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
_d("Cannot fsck %s, not a block device: %s", dev, strerror(errno));
|
dbg("Cannot fsck %s, not a block device: %s", dev, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ismnt("/proc/mounts", mnt->mnt_dir, "rw")) {
|
if (ismnt("/proc/mounts", mnt->mnt_dir, "rw")) {
|
||||||
_d("Skipping fsck of %s, already mounted rw on %s.", dev, mnt->mnt_dir);
|
dbg("Skipping fsck of %s, already mounted rw on %s.", dev, mnt->mnt_dir);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FSCK_FIX
|
#ifdef FSCK_FIX
|
||||||
snprintf(cmd, sizeof(cmd), "fsck -yf %s", dev);
|
snprintf(cmd, sizeof(cmd), "fsck -yf %s", dev);
|
||||||
#else
|
#else
|
||||||
snprintf(cmd, sizeof(cmd), "fsck -a %s", dev);
|
snprintf(cmd, sizeof(cmd), "fsck %s %s %s", fsck_mode, fsck_repair, dev);
|
||||||
#endif
|
#endif
|
||||||
_d("Running pass %d fsck command %s", pass, cmd);
|
dbg("Running pass %d fsck command %s", pass, cmd);
|
||||||
fsck_rc = run_interactive(cmd, "Checking filesystem %s", dev);
|
fsck_rc = run_interactive(cmd, "Checking filesystem %s", dev);
|
||||||
/*
|
/*
|
||||||
* "failure" is defined as exiting with a return code of
|
* "failure" is defined as exiting with a return code of
|
||||||
@@ -284,6 +285,9 @@ static int fsck_all(void)
|
|||||||
#ifndef FAST_BOOT
|
#ifndef FAST_BOOT
|
||||||
int pass;
|
int pass;
|
||||||
|
|
||||||
|
if (!fsck_mode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (pass = 1; pass < 10; pass++) {
|
for (pass = 1; pass < 10; pass++) {
|
||||||
rc = fsck(pass);
|
rc = fsck(pass);
|
||||||
if (rc)
|
if (rc)
|
||||||
@@ -302,7 +306,7 @@ static void fs_mount(const char *src, const char *tgt, const char *fstype,
|
|||||||
|
|
||||||
rc = mount(src, tgt, fstype, flags, data);
|
rc = mount(src, tgt, fstype, flags, data);
|
||||||
if (rc && errno != EBUSY)
|
if (rc && errno != EBUSY)
|
||||||
_pe("Failed %s %s on %s", msg, src, tgt);
|
err(1, "Failed %s %s on %s", msg, src, tgt);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SYSROOT
|
#ifndef SYSROOT
|
||||||
@@ -456,7 +460,7 @@ static void fs_mount_all(void)
|
|||||||
if (!rescue)
|
if (!rescue)
|
||||||
fs_remount_root(fsck_all());
|
fs_remount_root(fsck_all());
|
||||||
|
|
||||||
_d("Root FS up, calling hooks ...");
|
dbg("Root FS up, calling hooks ...");
|
||||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||||
|
|
||||||
if (fstab && strcmp(fstab, "/etc/fstab"))
|
if (fstab && strcmp(fstab, "/etc/fstab"))
|
||||||
@@ -465,14 +469,17 @@ static void fs_mount_all(void)
|
|||||||
if (run_interactive(cmd, "Mounting filesystems from %s", fstab))
|
if (run_interactive(cmd, "Mounting filesystems from %s", fstab))
|
||||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||||
|
|
||||||
_d("Calling extra mount hook, after mount -a ...");
|
dbg("Calling extra mount hook, after mount -a ...");
|
||||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||||
|
|
||||||
_d("Enable any swap ...");
|
dbg("Enable any swap ...");
|
||||||
fs_swapon(cmd, sizeof(cmd));
|
fs_swapon(cmd, sizeof(cmd));
|
||||||
|
|
||||||
_d("Finalize, ensure common file systems are available ...");
|
dbg("Finalize, ensure common file systems are available ...");
|
||||||
fs_finalize();
|
fs_finalize();
|
||||||
|
|
||||||
|
dbg("Base FS up, calling hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -513,110 +520,6 @@ static void fs_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle bootstrap transition to configured runlevel, start TTYs
|
|
||||||
*
|
|
||||||
* This is the final stage of bootstrap. It changes to the default
|
|
||||||
* (configured) runlevel, calls all external start scripts and final
|
|
||||||
* bootstrap hooks before bringing up TTYs.
|
|
||||||
*
|
|
||||||
* We must ensure that all declared `task [S]` and `run [S]` jobs in
|
|
||||||
* finit.conf, or *.conf in finit.d/, run to completion before we
|
|
||||||
* finalize the bootstrap process by calling this function.
|
|
||||||
*/
|
|
||||||
static void finalize(void *unused)
|
|
||||||
{
|
|
||||||
/* Clean up bootstrap-only tasks/services that never started */
|
|
||||||
_d("Clean up all bootstrap-only tasks/services ...");
|
|
||||||
svc_prune_bootstrap();
|
|
||||||
|
|
||||||
/* All services/tasks/etc. in configure runlevel have started */
|
|
||||||
_d("Running svc up hooks ...");
|
|
||||||
plugin_run_hooks(HOOK_SVC_UP);
|
|
||||||
service_step_all(SVC_TYPE_ANY);
|
|
||||||
|
|
||||||
/* Convenient SysV compat for when you just don't care ... */
|
|
||||||
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
|
|
||||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
|
||||||
|
|
||||||
/* Hooks that should run at the very end */
|
|
||||||
_d("Calling all system up hooks ...");
|
|
||||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
|
||||||
service_step_all(SVC_TYPE_ANY);
|
|
||||||
|
|
||||||
/* Disable progress output at normal runtime */
|
|
||||||
enable_progress(0);
|
|
||||||
|
|
||||||
/* System bootrapped, launch TTYs et al */
|
|
||||||
bootstrap = 0;
|
|
||||||
service_step_all(SVC_TYPE_RESPAWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Start cranking the big state machine
|
|
||||||
*/
|
|
||||||
static void crank_worker(void *unused)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Initialize state machine and start all bootstrap tasks
|
|
||||||
* NOTE: no network available!
|
|
||||||
*/
|
|
||||||
sm_init(&sm);
|
|
||||||
sm_step(&sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
|
||||||
* allowed to complete their work in [S], or timeout, before we switch
|
|
||||||
* to the configured runlevel and call finalize(), should not take more
|
|
||||||
* than 120 sec.
|
|
||||||
*/
|
|
||||||
static void bootstrap_worker(void *work)
|
|
||||||
{
|
|
||||||
static struct wq final = {
|
|
||||||
.cb = finalize,
|
|
||||||
.delay = 10
|
|
||||||
};
|
|
||||||
static int cnt = 120 * 10; /* We run with 100ms period */
|
|
||||||
int level = cfglevel;
|
|
||||||
|
|
||||||
_d("Step all services ...");
|
|
||||||
service_step_all(SVC_TYPE_ANY);
|
|
||||||
|
|
||||||
if (cnt-- > 0 && !service_completed()) {
|
|
||||||
_d("Not all bootstrap run/tasks have completed yet ... %d", cnt);
|
|
||||||
schedule_work(work);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cnt > 0)
|
|
||||||
_d("All run/task have completed, resuming bootstrap.");
|
|
||||||
else
|
|
||||||
_d("Timeout, resuming bootstrap.");
|
|
||||||
|
|
||||||
_d("Starting runlevel change finalize ...");
|
|
||||||
schedule_work(&final);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Run startup scripts in the runparts directory, if any.
|
|
||||||
*/
|
|
||||||
if (runparts && fisdir(runparts) && !rescue)
|
|
||||||
run_parts(runparts, NULL);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Start all tasks/services in the configured runlevel, or jump
|
|
||||||
* into the runlevel selected from the command line.
|
|
||||||
*/
|
|
||||||
if (cmdlevel) {
|
|
||||||
_d("Runlevel %d requested from command line, starting all services ...", cmdlevel);
|
|
||||||
level = cmdlevel;
|
|
||||||
} else
|
|
||||||
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
|
|
||||||
|
|
||||||
service_runlevel(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int version(int rc)
|
static int version(int rc)
|
||||||
{
|
{
|
||||||
puts(PACKAGE_STRING);
|
puts(PACKAGE_STRING);
|
||||||
@@ -647,7 +550,7 @@ static int usage(int rc)
|
|||||||
" 2-9 Change runlevel\n"
|
" 2-9 Change runlevel\n"
|
||||||
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
|
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
|
||||||
" if modified, same as initctl reload or SIGHUP to PID 1\n"
|
" if modified, same as initctl reload or SIGHUP to PID 1\n"
|
||||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
" 1, s, S Enter single user mode, no networking, runlevel 1\n"
|
||||||
"\n", prognm);
|
"\n", prognm);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@@ -692,31 +595,27 @@ static int telinit(int argc, char *argv[])
|
|||||||
return systemf("initctl -b runlevel %c", req);
|
return systemf("initctl -b runlevel %c", req);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: add non-pid1 process monitor here
|
|
||||||
*
|
|
||||||
* finit -f ~/.config/finit.conf &
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
return usage(1);
|
return usage(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct wq crank_work = {
|
|
||||||
.cb = crank_worker,
|
|
||||||
.delay = 10
|
|
||||||
};
|
|
||||||
struct wq bootstrap_work = {
|
|
||||||
.cb = bootstrap_worker,
|
|
||||||
.delay = 100
|
|
||||||
};
|
|
||||||
uev_ctx_t loop;
|
uev_ctx_t loop;
|
||||||
|
|
||||||
/* user calling telinit or init */
|
/* user calling telinit or init */
|
||||||
if (getpid() != 1)
|
if (getpid() != 1) {
|
||||||
|
if (geteuid() == 0)
|
||||||
return telinit(argc, argv);
|
return telinit(argc, argv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Issue #301: add non-pid1 process monitor here.
|
||||||
|
* Default to read ~/.config/finit.conf with the
|
||||||
|
* API socket in /run/user/$UID/. Lot of things
|
||||||
|
* to refactor before then though.
|
||||||
|
*/
|
||||||
|
return EX_NOPERM;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Need /dev, /proc, and /sys for console=, remount and cgroups
|
* Need /dev, /proc, and /sys for console=, remount and cgroups
|
||||||
*/
|
*/
|
||||||
@@ -745,13 +644,15 @@ int main(int argc, char *argv[])
|
|||||||
conf_reset_env();
|
conf_reset_env();
|
||||||
|
|
||||||
if (chdir("/"))
|
if (chdir("/"))
|
||||||
_pe("Failed cd /");
|
err(1, "Failed cd /");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case of emergency.
|
* In case of emergency.
|
||||||
*/
|
*/
|
||||||
|
#ifdef RESCUE_MODE
|
||||||
if (rescue)
|
if (rescue)
|
||||||
rescue = sulogin(0);
|
rescue = sulogin(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load plugins early, the first hook is in banner(), so we
|
* Load plugins early, the first hook is in banner(), so we
|
||||||
@@ -786,7 +687,11 @@ int main(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
fs_mount_all();
|
fs_mount_all();
|
||||||
|
|
||||||
/* Bootstrap conditions, needed for hooks */
|
/*
|
||||||
|
* Base FS up, enable standard SysV init signals and
|
||||||
|
* Bootstrap conditions, needed for hooks
|
||||||
|
*/
|
||||||
|
sig_setup(&loop);
|
||||||
cond_init();
|
cond_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -797,29 +702,12 @@ int main(int argc, char *argv[])
|
|||||||
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
|
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize .conf system and load static /etc/finit.conf.
|
* Initialize .conf system and load static /etc/finit.conf then
|
||||||
|
* tell the world what we used.
|
||||||
*/
|
*/
|
||||||
|
devmon_init(&loop);
|
||||||
conf_init(&loop);
|
conf_init(&loop);
|
||||||
|
conf_saverc();
|
||||||
/*
|
|
||||||
* 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("watchdog", "finit");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Start kernel event daemon as soon as possible, if enabled
|
|
||||||
*/
|
|
||||||
if (whichp(FINIT_LIBPATH_ "/keventd"))
|
|
||||||
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init " FINIT_LIBPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
|
|
||||||
|
|
||||||
/* Base FS up, enable standard SysV init signals */
|
|
||||||
sig_setup(&loop);
|
|
||||||
|
|
||||||
_d("Base FS up, calling hooks ...");
|
|
||||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up inotify watcher for /etc/finit.conf, /etc/finit.d, and
|
* Set up inotify watcher for /etc/finit.conf, /etc/finit.d, and
|
||||||
@@ -827,24 +715,23 @@ int main(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
conf_monitor();
|
conf_monitor();
|
||||||
|
|
||||||
_d("Starting initctl API responder ...");
|
dbg("Starting initctl API responder ...");
|
||||||
api_init(&loop);
|
api_init(&loop);
|
||||||
|
|
||||||
_d("Starting the big state machine ...");
|
dbg("Starting service interval monitor ...");
|
||||||
schedule_work(&crank_work);
|
service_init(&loop);
|
||||||
|
|
||||||
_d("Starting bootstrap finalize timer ...");
|
|
||||||
schedule_work(&bootstrap_work);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Background service tasks
|
* Initialize state machine and start all bootstrap tasks
|
||||||
|
* NOTE: no network available!
|
||||||
*/
|
*/
|
||||||
service_init();
|
sm_init(&sm);
|
||||||
|
sm_step(&sm);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enter main loop to monitor /dev/initctl and services
|
* Enter main loop to monitor /dev/initctl and services
|
||||||
*/
|
*/
|
||||||
_d("Entering main loop ...");
|
dbg("Entering main loop ...");
|
||||||
return uev_run(&loop, 0);
|
return uev_run(&loop, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-8
@@ -1,7 +1,7 @@
|
|||||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -54,19 +54,21 @@
|
|||||||
#define FINIT_CGPATH "/sys/fs/cgroup"
|
#define FINIT_CGPATH "/sys/fs/cgroup"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* To use these, include config.h, or define FINIT_LIBPATH_ */
|
/* To use these, include config.h, or define FINIT_EXECPATH_ */
|
||||||
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
|
#define _PATH_LOGIT FINIT_EXECPATH_ "/logit"
|
||||||
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
|
#define _PATH_RUNPARTS FINIT_EXECPATH_ "/runparts"
|
||||||
#define _PATH_GETTY FINIT_LIBPATH_ "/getty"
|
#define _PATH_SULOGIN FINIT_EXECPATH_ "/sulogin"
|
||||||
|
#define _PATH_GETTY FINIT_EXECPATH_ "/getty"
|
||||||
|
|
||||||
#define CMD_SIZE 256
|
#define CMD_SIZE 1024
|
||||||
#define LINE_SIZE 1024
|
#define LINE_SIZE 1024
|
||||||
#define BUF_SIZE 4096
|
#define BUF_SIZE 4096
|
||||||
|
|
||||||
/* We extend the INIT_CMD_ range for the new initctl tool. */
|
|
||||||
#define INIT_SOCKET _PATH_VARRUN "finit/socket"
|
#define INIT_SOCKET _PATH_VARRUN "finit/socket"
|
||||||
#define INIT_MAGIC 0x03091969
|
#define INIT_MAGIC 0x03091969
|
||||||
|
#define INIT_LEVEL 10
|
||||||
|
|
||||||
|
/* We extend the INIT_CMD_ range for the new initctl tool. */
|
||||||
#define INIT_CMD_START 0
|
#define INIT_CMD_START 0
|
||||||
#define INIT_CMD_RUNLVL 1
|
#define INIT_CMD_RUNLVL 1
|
||||||
#define INIT_CMD_POWERFAIL 2
|
#define INIT_CMD_POWERFAIL 2
|
||||||
@@ -96,6 +98,7 @@
|
|||||||
#define INIT_CMD_SVC_FIND 131
|
#define INIT_CMD_SVC_FIND 131
|
||||||
#define INIT_CMD_SVC_FIND_BYC 132
|
#define INIT_CMD_SVC_FIND_BYC 132
|
||||||
#define INIT_CMD_SIGNAL 133
|
#define INIT_CMD_SIGNAL 133
|
||||||
|
#define INIT_CMD_NOTIFY_SOCKET 200 /* For readiness notification socket */
|
||||||
#define INIT_CMD_NACK 254
|
#define INIT_CMD_NACK 254
|
||||||
#define INIT_CMD_ACK 255
|
#define INIT_CMD_ACK 255
|
||||||
|
|
||||||
@@ -117,11 +120,11 @@ extern int rescue;
|
|||||||
extern int single;
|
extern int single;
|
||||||
extern int bootstrap;
|
extern int bootstrap;
|
||||||
extern int kerndebug;
|
extern int kerndebug;
|
||||||
|
extern int syncsec;
|
||||||
extern char *fstab;
|
extern char *fstab;
|
||||||
extern char *sdown;
|
extern char *sdown;
|
||||||
extern char *network;
|
extern char *network;
|
||||||
extern char *hostname;
|
extern char *hostname;
|
||||||
extern char *rcsd;
|
|
||||||
extern char *runparts;
|
extern char *runparts;
|
||||||
extern char *osheading;
|
extern char *osheading;
|
||||||
extern uev_ctx_t *ctx;
|
extern uev_ctx_t *ctx;
|
||||||
|
|||||||
+136
-14
@@ -1,6 +1,6 @@
|
|||||||
/* Initialize and serve a login terminal
|
/* Initialize and serve a login terminal
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h> /* sysconf() */
|
#include <unistd.h> /* sysconf() */
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
@@ -48,9 +49,81 @@
|
|||||||
#define LOGIN_NAME_MIN 64
|
#define LOGIN_NAME_MIN 64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct osrel {
|
||||||
|
char name[32];
|
||||||
|
char pretty[64];
|
||||||
|
char id[32];
|
||||||
|
char version[32];
|
||||||
|
char version_id[32];
|
||||||
|
char home_url[128];
|
||||||
|
char doc_url[128];
|
||||||
|
char bug_url[128];
|
||||||
|
char support_url[128];
|
||||||
|
};
|
||||||
|
|
||||||
static long logname_len = 32; /* useradd(1) limit at 32 chars */
|
static long logname_len = 32; /* useradd(1) limit at 32 chars */
|
||||||
static int passenv = 0; /* Set /bin/login -p or not */
|
static int passenv = 0; /* Set /bin/login -p or not */
|
||||||
|
|
||||||
|
static void get_val(char *line, const char *key, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *val;
|
||||||
|
|
||||||
|
if ((val = fgetval(line, key, "="))) {
|
||||||
|
strlcpy(buf, val, len);
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse os-release
|
||||||
|
*/
|
||||||
|
static int osrel(struct osrel *rel)
|
||||||
|
{
|
||||||
|
char codename[30];
|
||||||
|
char *line;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen("/etc/os-release", "r");
|
||||||
|
if (!fp) {
|
||||||
|
fp = fopen("/usr/lib/os-release", "r");
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(rel->version, 0, sizeof(rel->version));
|
||||||
|
memset(codename, 0, sizeof(codename));
|
||||||
|
|
||||||
|
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
|
||||||
|
get_val(line, "NAME", rel->name, sizeof(rel->name));
|
||||||
|
get_val(line, "PRETTY_NAME", rel->pretty, sizeof(rel->pretty));
|
||||||
|
get_val(line, "ID", rel->id, sizeof(rel->id));
|
||||||
|
get_val(line, "VERSION", rel->version, sizeof(rel->version));
|
||||||
|
get_val(line, "VERSION_ID", rel->version_id, sizeof(rel->version_id));
|
||||||
|
get_val(line, "VERSION_CODENAME", codename, sizeof(codename));
|
||||||
|
get_val(line, "HOME_URL", rel->home_url, sizeof(rel->home_url));
|
||||||
|
get_val(line, "DOCUMENTATION_URL", rel->doc_url, sizeof(rel->doc_url));
|
||||||
|
get_val(line, "SUPPORT_URL", rel->support_url, sizeof(rel->support_url));
|
||||||
|
get_val(line, "BUG_REPORT_URL", rel->bug_url, sizeof(rel->bug_url));
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Many distros don't set VERSION or VERSION_ID for their
|
||||||
|
* rolling, or unstable, branches. However, most set the
|
||||||
|
* version code name, so we use that as VERSION.
|
||||||
|
*/
|
||||||
|
if (!rel->version[0] && codename[0]) {
|
||||||
|
if (codename[0] == '(')
|
||||||
|
strlcpy(rel->version, codename, sizeof(rel->version));
|
||||||
|
else
|
||||||
|
snprintf(rel->version, sizeof(rel->version), "(%s)", codename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read one character from stdin.
|
* Read one character from stdin.
|
||||||
*/
|
*/
|
||||||
@@ -68,10 +141,19 @@ static int readch(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse and display a line from /etc/issue
|
* Parse and display a line from /etc/issue
|
||||||
|
*
|
||||||
|
* Includes Finit-specific extensions to display information from
|
||||||
|
* /etc/os-release instead of relying on legacy uname. Activated
|
||||||
|
* only if /etc/os-release or /usr/lib/os-release exist.
|
||||||
|
*
|
||||||
|
* https://www.unix.com/man-page/Linux/8/getty/
|
||||||
|
* https://www.systutorials.com/docs/linux/man/8-mingetty/
|
||||||
*/
|
*/
|
||||||
static void do_parse(char *line, struct utsname *uts, char *tty)
|
static void parseln(char *line, struct utsname *uts, struct osrel *rel, char *tty, int compat)
|
||||||
{
|
{
|
||||||
|
char buf[32] = { 0 };
|
||||||
char *s, *s0;
|
char *s, *s0;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
s0 = line;
|
s0 = line;
|
||||||
for (s = line; *s != 0; s++) {
|
for (s = line; *s != 0; s++) {
|
||||||
@@ -80,12 +162,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
|||||||
dprint(STDOUT_FILENO, s0, s - s0);
|
dprint(STDOUT_FILENO, s0, s - s0);
|
||||||
s0 = s + 2;
|
s0 = s + 2;
|
||||||
switch (*++s) {
|
switch (*++s) {
|
||||||
|
case 'B':
|
||||||
|
if (!compat)
|
||||||
|
dprint(STDOUT_FILENO, rel->bug_url, 0);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (!compat)
|
||||||
|
dprint(STDOUT_FILENO, rel->doc_url, 0);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
if (!compat)
|
||||||
|
dprint(STDOUT_FILENO, rel->home_url, 0);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
if (!compat)
|
||||||
|
dprint(STDOUT_FILENO, rel->id, 0);
|
||||||
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
dprint(STDOUT_FILENO, tty, 0);
|
dprint(STDOUT_FILENO, tty, 0);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
dprint(STDOUT_FILENO, uts->machine, 0);
|
dprint(STDOUT_FILENO, uts->machine, 0);
|
||||||
break;
|
break;
|
||||||
|
case 'N':
|
||||||
|
if (!compat)
|
||||||
|
dprint(STDOUT_FILENO, rel->name, 0);
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
dprint(STDOUT_FILENO, uts->nodename, 0);
|
dprint(STDOUT_FILENO, uts->nodename, 0);
|
||||||
break;
|
break;
|
||||||
@@ -95,13 +197,31 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case 'r':
|
case 'r':
|
||||||
|
if (compat)
|
||||||
dprint(STDOUT_FILENO, uts->release, 0);
|
dprint(STDOUT_FILENO, uts->release, 0);
|
||||||
|
else
|
||||||
|
dprint(STDOUT_FILENO, rel->version_id, 0);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
if (!compat)
|
||||||
|
dprint(STDOUT_FILENO, rel->support_url, 0);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
|
if (compat)
|
||||||
dprint(STDOUT_FILENO, uts->sysname, 0);
|
dprint(STDOUT_FILENO, uts->sysname, 0);
|
||||||
|
else
|
||||||
|
dprint(STDOUT_FILENO, rel->pretty, 0);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
now = time(NULL);
|
||||||
|
ctime_r(&now, buf);
|
||||||
|
dprint(STDOUT_FILENO, chomp(buf), 0);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
|
if (compat)
|
||||||
dprint(STDOUT_FILENO, uts->version, 0);
|
dprint(STDOUT_FILENO, uts->version, 0);
|
||||||
|
else
|
||||||
|
dprint(STDOUT_FILENO, rel->version, 0);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
goto leave;
|
goto leave;
|
||||||
@@ -119,28 +239,31 @@ leave:
|
|||||||
/*
|
/*
|
||||||
* Parse and display /etc/issue
|
* Parse and display /etc/issue
|
||||||
*/
|
*/
|
||||||
static void do_issue(char *tty)
|
static void issue(char *tty)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
|
||||||
char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n";
|
char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n";
|
||||||
struct utsname uts;
|
struct utsname uts;
|
||||||
|
struct osrel rel;
|
||||||
|
int compat;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get data about this machine.
|
* Get data about this machine.
|
||||||
*/
|
*/
|
||||||
uname(&uts);
|
uname(&uts);
|
||||||
|
compat = osrel(&rel);
|
||||||
|
|
||||||
fp = fopen("/etc/issue", "r");
|
fp = fopen("/etc/issue", "r");
|
||||||
if (fp) {
|
if (fp) {
|
||||||
while (fgets(buf, sizeof(buf), fp))
|
while (fgets(buf, sizeof(buf), fp))
|
||||||
do_parse(buf, &uts, tty);
|
parseln(buf, &uts, &rel, tty, compat);
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
} else {
|
} else {
|
||||||
do_parse(buf, &uts, tty);
|
parseln(buf, &uts, &rel, tty, compat);
|
||||||
}
|
}
|
||||||
|
|
||||||
do_parse("\\n login: ", &uts, tty);
|
parseln("\\n login: ", &uts, &rel, tty, compat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -157,7 +280,7 @@ static int get_logname(char *tty, char *name, size_t len)
|
|||||||
ch = ' ';
|
ch = ' ';
|
||||||
*name = '\0';
|
*name = '\0';
|
||||||
while (ch != '\n') {
|
while (ch != '\n') {
|
||||||
do_issue(tty);
|
issue(tty);
|
||||||
|
|
||||||
np = name;
|
np = name;
|
||||||
while ((ch = readch()) != '\n') {
|
while ((ch = readch()) != '\n') {
|
||||||
@@ -178,11 +301,10 @@ static int get_logname(char *tty, char *name, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute the login(1) command with the current
|
* Start login(1) with the current username as its argument. It replies
|
||||||
* username as its argument. It will reply to the
|
* to the calling user by typing "Password: " ...
|
||||||
* calling user by typing "Password: "...
|
|
||||||
*/
|
*/
|
||||||
static int do_login(char *name)
|
static int exec_login(char *name)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
@@ -242,12 +364,12 @@ restart:
|
|||||||
if (term && term[0])
|
if (term && term[0])
|
||||||
setenv("TERM", term, 1);
|
setenv("TERM", term, 1);
|
||||||
|
|
||||||
return do_login(name);
|
return exec_login(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usage(int rc)
|
static int usage(int rc)
|
||||||
{
|
{
|
||||||
warnx("usage: getty [-h?] tty [speed [term]]");
|
warnx("usage: getty [-h?p] tty [speed [term]]");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-7
@@ -1,7 +1,7 @@
|
|||||||
/* Misc. utility functions for finit and its plugins
|
/* Misc. utility functions for finit and its plugins
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "log.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -83,6 +84,9 @@ char *console(void)
|
|||||||
|
|
||||||
void console_init(void)
|
void console_init(void)
|
||||||
{
|
{
|
||||||
|
/* Enable line wrap, if disabled previously, e.g., qemu */
|
||||||
|
dprint(STDOUT_FILENO, "\033[?7h", 5);
|
||||||
|
|
||||||
log_init();
|
log_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,6 +370,7 @@ int print_result(int fail)
|
|||||||
int getuser(char *username, char **home)
|
int getuser(char *username, char **home)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_STATIC
|
#ifdef ENABLE_STATIC
|
||||||
|
if (home)
|
||||||
*home = "/"; /* XXX: Fixme */
|
*home = "/"; /* XXX: Fixme */
|
||||||
return fgetint("/etc/passwd", "x:\n", username);
|
return fgetint("/etc/passwd", "x:\n", username);
|
||||||
#else
|
#else
|
||||||
@@ -443,8 +448,10 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
|||||||
gid = 0;
|
gid = 0;
|
||||||
|
|
||||||
rc = makedir(dir, mode);
|
rc = makedir(dir, mode);
|
||||||
|
if (rc && errno == EEXIST)
|
||||||
|
rc = chmod(dir, mode);
|
||||||
if (chown(dir, uid, gid))
|
if (chown(dir, uid, gid))
|
||||||
_pe("Failed chown(%s, %d, %d)", dir, uid, gid);
|
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
umask(omask);
|
umask(omask);
|
||||||
@@ -482,7 +489,7 @@ void set_hostname(char **hostname)
|
|||||||
done:
|
done:
|
||||||
if (*hostname) {
|
if (*hostname) {
|
||||||
if (sethostname(*hostname, strlen(*hostname)))
|
if (sethostname(*hostname, strlen(*hostname)))
|
||||||
_pe("Failed sethostnaem(%s)", *hostname);
|
err(1, "Failed sethostname(%s)", *hostname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,9 +503,9 @@ void networking(int updown)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (updown)
|
if (updown)
|
||||||
_d("Setting up networking ...");
|
dbg("Setting up networking ...");
|
||||||
else
|
else
|
||||||
_d("Taking down networking ...");
|
dbg("Taking down networking ...");
|
||||||
|
|
||||||
/* Run user network start script if enabled */
|
/* Run user network start script if enabled */
|
||||||
if (updown && network) {
|
if (updown && network) {
|
||||||
@@ -536,7 +543,7 @@ void networking(int updown)
|
|||||||
char buf[256];
|
char buf[256];
|
||||||
|
|
||||||
while (fgets(buf, sizeof(buf), pp))
|
while (fgets(buf, sizeof(buf), pp))
|
||||||
logit(LOG_NOTICE, "network: %s", buf);
|
logit(LOG_NOTICE, "network: %s", chomp(buf));
|
||||||
|
|
||||||
rc = pclose(pp);
|
rc = pclose(pp);
|
||||||
}
|
}
|
||||||
@@ -557,8 +564,11 @@ done:
|
|||||||
|
|
||||||
/* Hooks that rely on loopback, or basic networking being up. */
|
/* Hooks that rely on loopback, or basic networking being up. */
|
||||||
if (updown) {
|
if (updown) {
|
||||||
_d("Calling all network up hooks ...");
|
dbg("Calling all network up hooks ...");
|
||||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||||
|
} else {
|
||||||
|
dbg("Calling all network down hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_NETWORK_DN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+96
-5
@@ -1,7 +1,7 @@
|
|||||||
/* Misc. utility functions and C-library extensions for finit and its plugins
|
/* Misc. utility functions and C-library extensions for finit and its plugins
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,8 +25,12 @@
|
|||||||
#ifndef FINIT_HELPERS_H_
|
#ifndef FINIT_HELPERS_H_
|
||||||
#define FINIT_HELPERS_H_
|
#define FINIT_HELPERS_H_
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
@@ -84,18 +88,59 @@ int run_interactive (char *cmd, char *fmt, ...) __attribute__ ((format (prin
|
|||||||
int exec_runtask (char *cmd, char *args[]);
|
int exec_runtask (char *cmd, char *args[]);
|
||||||
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||||
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
|
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
|
||||||
int run_parts (char *dir, char *cmd);
|
pid_t run_bg (char *cmd, char *args[]);
|
||||||
|
int run_parts (char *dir, char *cmd, const char *env[], int progress);
|
||||||
|
|
||||||
static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
/*
|
||||||
|
* Defaults to user "root" and group "wheel" (root) if:
|
||||||
|
* 1) the user is missing
|
||||||
|
* 2) the group or the file /etc/group is missing
|
||||||
|
*/
|
||||||
|
static inline int create(char *path, mode_t mode, char *user, char *group)
|
||||||
{
|
{
|
||||||
if (touch(path) || chmod(path, mode) || chown(path, uid, gid)) {
|
int fd, uid, gid;
|
||||||
_w("Failed creating %s properly.", path);
|
|
||||||
|
if (!path) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid = getuser(user, NULL);
|
||||||
|
if (uid < 0)
|
||||||
|
uid = 0;
|
||||||
|
gid = getgroup(group);
|
||||||
|
if (gid < 0)
|
||||||
|
gid = 0;
|
||||||
|
|
||||||
|
fd = creat(path, mode);
|
||||||
|
if (fd == -1) {
|
||||||
|
warn("Failed creating %s properly", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
if (chown(path, uid, gid)) {
|
||||||
|
warn("Failed chowning %s properly", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int ln(const char *target, const char *linkpath)
|
||||||
|
{
|
||||||
|
if (!target || !linkpath) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symlink(target, linkpath)) {
|
||||||
|
if (errno != EEXIST)
|
||||||
|
warn("Failed creating %s -> %s symlink", target, linkpath);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
static inline int dprint(int fd, const char *s, size_t len)
|
static inline int dprint(int fd, const char *s, size_t len)
|
||||||
{
|
{
|
||||||
size_t loop = 3;
|
size_t loop = 3;
|
||||||
@@ -114,6 +159,52 @@ static inline int dprint(int fd, const char *s, size_t len)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline char *fgetval(char *line, const char *key, char *sep)
|
||||||
|
{
|
||||||
|
char *ptr, *str, *copy;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
str = copy = strdup(line);
|
||||||
|
if (!copy)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ptr = strsep(&str, sep);
|
||||||
|
if (ptr && strcmp(ptr, key)) {
|
||||||
|
fail:
|
||||||
|
free(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we should strip optional separator, e.g.,
|
||||||
|
* 'foo = bar' => '= bar' should be stripped to 'bar'
|
||||||
|
*/
|
||||||
|
while (str && strchr(sep, str[0])) {
|
||||||
|
ptr = strsep(&str, sep);
|
||||||
|
if (!ptr)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
ptr = str;
|
||||||
|
|
||||||
|
if (*ptr == '"' || *ptr == '\'') {
|
||||||
|
char *end = &ptr[strlen(ptr) - 1];
|
||||||
|
char q = *ptr;
|
||||||
|
|
||||||
|
while (end > ptr && isspace(*end))
|
||||||
|
*end-- = 0;
|
||||||
|
|
||||||
|
if (*end == q) {
|
||||||
|
*end = 0;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen(ptr) + 1;
|
||||||
|
memmove(copy, ptr, len);
|
||||||
|
|
||||||
|
return realloc(copy, len);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* FINIT_HELPERS_H_ */
|
#endif /* FINIT_HELPERS_H_ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+386
-67
@@ -1,6 +1,6 @@
|
|||||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <syslog.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <utmp.h>
|
#include <utmp.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
@@ -55,11 +56,16 @@ struct cmd {
|
|||||||
int (*cb_multiarg)(int argc, char **argv);
|
int (*cb_multiarg)(int argc, char **argv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *finit_conf = NULL;
|
||||||
|
char *finit_rcsd = NULL;
|
||||||
|
|
||||||
int icreate = 0;
|
int icreate = 0;
|
||||||
int iforce = 0;
|
int iforce = 0;
|
||||||
int ionce = 0;
|
int ionce = 0;
|
||||||
int debug = 0;
|
int debug = 0;
|
||||||
int heading = 1;
|
int heading = 1;
|
||||||
|
int json = 0;
|
||||||
|
int noerr = 0;
|
||||||
int verbose = 0;
|
int verbose = 0;
|
||||||
int plain = 0;
|
int plain = 0;
|
||||||
int quiet = 0;
|
int quiet = 0;
|
||||||
@@ -97,8 +103,8 @@ static void col_widths(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* adjust for min col width */
|
/* adjust for min col width */
|
||||||
if (iw < 5)
|
if (iw < 6)
|
||||||
iw = 5;
|
iw = 6;
|
||||||
if (pw < 3)
|
if (pw < 3)
|
||||||
pw = 3;
|
pw = 3;
|
||||||
}
|
}
|
||||||
@@ -187,7 +193,7 @@ static int show_log(char *arg)
|
|||||||
if (arg) {
|
if (arg) {
|
||||||
svc = client_svc_find(arg);
|
svc = client_svc_find(arg);
|
||||||
if (!svc)
|
if (!svc)
|
||||||
ERRX(69, "no such task or service(s): %s", arg);
|
ERRX(noerr ? 0 : 69, "no such task or service(s): %s", arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return do_log(svc, "");
|
return do_log(svc, "");
|
||||||
@@ -203,19 +209,26 @@ static int do_runlevel(char *arg)
|
|||||||
if (!arg) {
|
if (!arg) {
|
||||||
int prevlevel = 0;
|
int prevlevel = 0;
|
||||||
int currlevel;
|
int currlevel;
|
||||||
char prev;
|
char prev, curr;
|
||||||
|
|
||||||
currlevel = runlevel_get(&prevlevel);
|
currlevel = runlevel_get(&prevlevel);
|
||||||
if (255 == currlevel) {
|
switch (currlevel) {
|
||||||
|
case 255:
|
||||||
printf("unknown\n");
|
printf("unknown\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
case INIT_LEVEL:
|
||||||
|
curr = 'S';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
curr = currlevel + '0';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
prev = prevlevel + '0';
|
prev = prevlevel + '0';
|
||||||
if (prev <= '0' || prev > '9')
|
if (prev <= '0' || prev > '9')
|
||||||
prev = 'N';
|
prev = 'N';
|
||||||
|
|
||||||
printf("%c %d\n", prev , currlevel);
|
printf("%c %c\n", prev , curr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +269,7 @@ static int do_startstop(int cmd, char *arg)
|
|||||||
|
|
||||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||||
if (client_send(&rq, sizeof(rq)))
|
if (client_send(&rq, sizeof(rq)))
|
||||||
ERRX(69, "no such task or service(s): %s", arg);
|
ERRX(noerr ? 0 : 69, "no such task or service(s): %s", arg);
|
||||||
|
|
||||||
return do_svc(cmd, arg);
|
return do_svc(cmd, arg);
|
||||||
}
|
}
|
||||||
@@ -275,7 +288,7 @@ static int do_reload (char *arg)
|
|||||||
static int do_restart(char *arg)
|
static int do_restart(char *arg)
|
||||||
{
|
{
|
||||||
if (do_startstop(INIT_CMD_RESTART_SVC, arg))
|
if (do_startstop(INIT_CMD_RESTART_SVC, arg))
|
||||||
ERRX(7, "failed restarting %s", arg);
|
ERRX(noerr ? 0 : 7, "failed restarting %s", arg);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -303,7 +316,7 @@ int do_signal(int argc, char *argv[])
|
|||||||
|
|
||||||
strlcpy(rq.data, argv[0], sizeof(rq.data));
|
strlcpy(rq.data, argv[0], sizeof(rq.data));
|
||||||
if (client_send(&rq, sizeof(rq)))
|
if (client_send(&rq, sizeof(rq)))
|
||||||
ERRX(69, "no such task or service(s): %s", argv[0]);
|
ERRX(noerr ? 0 : 69, "no such task or service(s): %s", argv[0]);
|
||||||
|
|
||||||
signo = str2sig(argv[1]);
|
signo = str2sig(argv[1]);
|
||||||
if (signo == -1) {
|
if (signo == -1) {
|
||||||
@@ -323,6 +336,7 @@ int do_signal(int argc, char *argv[])
|
|||||||
return client_send(&rq, sizeof(rq));
|
return client_send(&rq, sizeof(rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dump_once;
|
||||||
char *dump_filter;
|
char *dump_filter;
|
||||||
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||||
{
|
{
|
||||||
@@ -361,6 +375,21 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
|||||||
pid = 1;
|
pid = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
if (!dump_once)
|
||||||
|
puts("[");
|
||||||
|
|
||||||
|
printf("%s {\n"
|
||||||
|
" \"pid\": %d,\n"
|
||||||
|
" \"identity\": \"%s\",\n"
|
||||||
|
" \"status\": \"%s\",\n"
|
||||||
|
" \"condition\": \"%s\"\n"
|
||||||
|
" }",
|
||||||
|
dump_once ? ",\n" : "",
|
||||||
|
pid, nm, asserted, cond);
|
||||||
|
|
||||||
|
dump_once++;
|
||||||
|
} else
|
||||||
printf("%-*d %-*s %-6s <%s>\n", pw, pid, iw, nm, asserted, cond);
|
printf("%-*d %-*s %-6s <%s>\n", pw, pid, iw, nm, asserted, cond);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -369,32 +398,64 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
|||||||
static int do_cond_dump(char *arg)
|
static int do_cond_dump(char *arg)
|
||||||
{
|
{
|
||||||
col_widths();
|
col_widths();
|
||||||
if (heading)
|
if (heading && !json)
|
||||||
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
||||||
"STATUS", "CONDITION");
|
"STATUS", "CONDITION");
|
||||||
|
|
||||||
|
dump_once = 0;
|
||||||
dump_filter = arg;
|
dump_filter = arg;
|
||||||
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
|
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
|
||||||
WARNX("Failed parsing %s", _PATH_COND);
|
WARNX("Failed parsing %s", _PATH_COND);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (dump_once)
|
||||||
|
puts("\n]");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum { COND_CLR, COND_SET, COND_GET } condop_t;
|
typedef enum { COND_CLR, COND_SET, COND_GET } condop_t;
|
||||||
|
|
||||||
static int do_cond_act(char *arg, condop_t op)
|
static cond_state_t cond_read(char *path)
|
||||||
{
|
{
|
||||||
|
int now, gen;
|
||||||
|
|
||||||
|
if (fngetint(path, &gen) == -1)
|
||||||
|
return COND_OFF;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we cannot read the reconf generation, then either sth is
|
||||||
|
* very wrong, or we are called very early/late boot/shutdown.
|
||||||
|
*/
|
||||||
|
if (fngetint(_PATH_RECONF, &now) == -1)
|
||||||
|
return COND_FLUX; /* classify as flux */
|
||||||
|
|
||||||
|
if (now != gen)
|
||||||
|
return COND_FLUX;
|
||||||
|
|
||||||
|
return COND_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cond get allows only one argument
|
||||||
|
* cond set|clr iterate over multiple args
|
||||||
|
*/
|
||||||
|
static int do_cond_act(char *args, condop_t op)
|
||||||
|
{
|
||||||
|
cond_state_t cstate;
|
||||||
char path[256];
|
char path[256];
|
||||||
|
char *arg;
|
||||||
|
|
||||||
|
if (!args || !args[0])
|
||||||
|
ERRX(2, "Invalid condition (empty)");
|
||||||
|
|
||||||
|
arg = strtok(args, " \t");
|
||||||
|
while (arg) {
|
||||||
size_t off;
|
size_t off;
|
||||||
|
|
||||||
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
|
if (strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
|
||||||
arg += strlen(COND_USR);
|
arg += strlen(COND_USR);
|
||||||
|
|
||||||
if (!arg || !arg[0])
|
|
||||||
ERRX(2, "Invalid condition (empty)");
|
|
||||||
|
|
||||||
/* allowed to read any condition, but not set/clr */
|
/* allowed to read any condition, but not set/clr */
|
||||||
if (op != COND_GET) {
|
if (op != COND_GET) {
|
||||||
if (strchr(arg, '/'))
|
if (strchr(arg, '/'))
|
||||||
@@ -411,10 +472,21 @@ static int do_cond_act(char *arg, condop_t op)
|
|||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case COND_GET:
|
case COND_GET:
|
||||||
off = !fexist(path);
|
cstate = cond_read(path);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
puts(off ? "off" : "on");
|
puts(condstr(cstate));
|
||||||
return off;
|
|
||||||
|
switch (cstate) {
|
||||||
|
case COND_ON:
|
||||||
|
return 0;
|
||||||
|
case COND_OFF:
|
||||||
|
return 1;
|
||||||
|
case COND_FLUX:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 255;
|
||||||
|
|
||||||
case COND_SET:
|
case COND_SET:
|
||||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||||
ERR(73, "Failed asserting condition <%s>", &path[off]);
|
ERR(73, "Failed asserting condition <%s>", &path[off]);
|
||||||
@@ -425,6 +497,9 @@ static int do_cond_act(char *arg, condop_t op)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arg = strtok(NULL, " \t");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,7 +507,7 @@ static int do_cond_get(char *arg) { return do_cond_act(arg, COND_GET); }
|
|||||||
static int do_cond_set(char *arg) { return do_cond_act(arg, COND_SET); }
|
static int do_cond_set(char *arg) { return do_cond_act(arg, COND_SET); }
|
||||||
static int do_cond_clr(char *arg) { return do_cond_act(arg, COND_CLR); }
|
static int do_cond_clr(char *arg) { return do_cond_act(arg, COND_CLR); }
|
||||||
|
|
||||||
static char *svc_cond(svc_t *svc, char *buf, size_t len)
|
static char *svc_cond(svc_t *svc, char *buf, size_t len, int ansi)
|
||||||
{
|
{
|
||||||
char *cond, *conds;
|
char *cond, *conds;
|
||||||
|
|
||||||
@@ -445,11 +520,18 @@ static char *svc_cond(svc_t *svc, char *buf, size_t len)
|
|||||||
if (!conds)
|
if (!conds)
|
||||||
return buf;
|
return buf;
|
||||||
|
|
||||||
strlcat(buf, "<", len);
|
if (json)
|
||||||
|
strlcat(buf, "[ ", len);
|
||||||
|
|
||||||
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
||||||
if (cond != conds)
|
if (cond != conds) {
|
||||||
strlcat(buf, ",", len);
|
strlcat(buf, ",", len);
|
||||||
|
if (json)
|
||||||
|
strlcat(buf, " ", len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
strlcat(buf, "\"", len);
|
||||||
|
|
||||||
switch (cond_get(cond)) {
|
switch (cond_get(cond)) {
|
||||||
case COND_ON:
|
case COND_ON:
|
||||||
@@ -458,26 +540,30 @@ static char *svc_cond(svc_t *svc, char *buf, size_t len)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case COND_FLUX:
|
case COND_FLUX:
|
||||||
if (!plain)
|
if (ansi)
|
||||||
strlcat(buf, "\e[1m", len);
|
strlcat(buf, "\e[1m", len);
|
||||||
strlcat(buf, "~", len);
|
strlcat(buf, "~", len);
|
||||||
strlcat(buf, cond, len);
|
strlcat(buf, cond, len);
|
||||||
if (!plain)
|
if (ansi)
|
||||||
strlcat(buf, "\e[0m", len);
|
strlcat(buf, "\e[0m", len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COND_OFF:
|
case COND_OFF:
|
||||||
if (!plain)
|
if (ansi)
|
||||||
strlcat(buf, "\e[1m", len);
|
strlcat(buf, "\e[1m", len);
|
||||||
strlcat(buf, "-", len);
|
strlcat(buf, "-", len);
|
||||||
strlcat(buf, cond, len);
|
strlcat(buf, cond, len);
|
||||||
if (!plain)
|
if (ansi)
|
||||||
strlcat(buf, "\e[0m", len);
|
strlcat(buf, "\e[0m", len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
strlcat(buf, "\"", len);
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcat(buf, ">", len);
|
if (json)
|
||||||
|
strlcat(buf, " ]", len);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@@ -486,10 +572,11 @@ static int do_cond_show(char *arg)
|
|||||||
{
|
{
|
||||||
enum cond_state cond;
|
enum cond_state cond;
|
||||||
char buf[512];
|
char buf[512];
|
||||||
|
int once = 0;
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
col_widths();
|
col_widths();
|
||||||
if (heading)
|
if (heading && !json)
|
||||||
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
||||||
"STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
"STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
||||||
|
|
||||||
@@ -498,8 +585,28 @@ static int do_cond_show(char *arg)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
cond = cond_get_agg(svc->cond);
|
cond = cond_get_agg(svc->cond);
|
||||||
|
|
||||||
svc_ident(svc, buf, sizeof(buf));
|
svc_ident(svc, buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
if (!once)
|
||||||
|
puts("[");
|
||||||
|
|
||||||
|
printf("%s {\n"
|
||||||
|
" \"pid\": %d,\n"
|
||||||
|
" \"identity\": \"%s\",\n"
|
||||||
|
" \"status\": \"%s\",\n",
|
||||||
|
once ? ",\n" : "",
|
||||||
|
svc->pid,
|
||||||
|
buf,
|
||||||
|
condstr(cond));
|
||||||
|
svc_cond(svc, buf, sizeof(buf), !plain);
|
||||||
|
printf(" \"condition\": %s\n"
|
||||||
|
" }", buf);
|
||||||
|
|
||||||
|
once++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
printf("%-*d %-*s ", pw, svc->pid, iw, buf);
|
printf("%-*d %-*s ", pw, svc->pid, iw, buf);
|
||||||
|
|
||||||
if (cond == COND_ON)
|
if (cond == COND_ON)
|
||||||
@@ -507,9 +614,15 @@ static int do_cond_show(char *arg)
|
|||||||
else
|
else
|
||||||
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
|
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
|
||||||
|
|
||||||
puts(svc_cond(svc, buf, sizeof(buf)));
|
svc_cond(svc, buf, sizeof(buf), !plain);
|
||||||
|
if (buf[0])
|
||||||
|
printf("<%s>", buf);
|
||||||
|
puts("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json && once)
|
||||||
|
puts("\n]");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,7 +724,7 @@ static int plugins_list(char *arg)
|
|||||||
.magic = INIT_MAGIC,
|
.magic = INIT_MAGIC,
|
||||||
.cmd = INIT_CMD_GET_PLUGINS,
|
.cmd = INIT_CMD_GET_PLUGINS,
|
||||||
};
|
};
|
||||||
char buf[sizeof(rq.data)];
|
char buf[sizeof(rq.data) + 1];
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -622,7 +735,8 @@ static int plugins_list(char *arg)
|
|||||||
else
|
else
|
||||||
ERRX(69, "No such command");
|
ERRX(69, "No such command");
|
||||||
}
|
}
|
||||||
memcpy(buf, rq.data, sizeof(buf));
|
memcpy(buf, rq.data, sizeof(buf) - 1);
|
||||||
|
buf[sizeof(rq.data)] = 0;
|
||||||
|
|
||||||
if (heading)
|
if (heading)
|
||||||
print_header("%-18s %s", "PLUGIN", "DEPENDENCIES");
|
print_header("%-18s %s", "PLUGIN", "DEPENDENCIES");
|
||||||
@@ -653,18 +767,19 @@ static int plugins_list(char *arg)
|
|||||||
*/
|
*/
|
||||||
char *runlevel_string(int currlevel, int levels)
|
char *runlevel_string(int currlevel, int levels)
|
||||||
{
|
{
|
||||||
int i, pos = 1;
|
|
||||||
static char lvl[21];
|
static char lvl[21];
|
||||||
|
int i = INIT_LEVEL;
|
||||||
|
int pos = 1;
|
||||||
|
|
||||||
memset(lvl, 0, sizeof(lvl));
|
memset(lvl, 0, sizeof(lvl));
|
||||||
lvl[0] = '[';
|
lvl[0] = '[';
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
do {
|
||||||
if (ISSET(levels, i)) {
|
if (ISSET(levels, i)) {
|
||||||
if (!plain && currlevel == i)
|
if (!plain && currlevel == i)
|
||||||
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
|
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
|
||||||
|
|
||||||
if (i == 0)
|
if (i == INIT_LEVEL)
|
||||||
lvl[pos++] = 'S';
|
lvl[pos++] = 'S';
|
||||||
else
|
else
|
||||||
lvl[pos++] = '0' + i;
|
lvl[pos++] = '0' + i;
|
||||||
@@ -674,7 +789,14 @@ char *runlevel_string(int currlevel, int levels)
|
|||||||
} else {
|
} else {
|
||||||
lvl[pos++] = '-';
|
lvl[pos++] = '-';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/* XXX: ugly hack to get order right: S0123456789 */
|
||||||
|
if (i == INIT_LEVEL)
|
||||||
|
i = 0;
|
||||||
|
else
|
||||||
|
i++;
|
||||||
|
} while (i < INIT_LEVEL);
|
||||||
|
|
||||||
|
|
||||||
lvl[pos++] = ']';
|
lvl[pos++] = ']';
|
||||||
lvl[pos] = 0;
|
lvl[pos] = 0;
|
||||||
@@ -682,6 +804,32 @@ char *runlevel_string(int currlevel, int levels)
|
|||||||
return lvl;
|
return lvl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *runlevel_arr(int levels)
|
||||||
|
{
|
||||||
|
static char lvl[42];
|
||||||
|
int i = INIT_LEVEL;
|
||||||
|
int p = 2, s = 0;
|
||||||
|
|
||||||
|
strlcpy(lvl, "[ ", sizeof(lvl));
|
||||||
|
do {
|
||||||
|
if (ISSET(levels, i)) {
|
||||||
|
if (i == INIT_LEVEL)
|
||||||
|
p += snprintf(&lvl[p], sizeof(lvl) - p, "\"S\"");
|
||||||
|
else
|
||||||
|
p += snprintf(&lvl[p], sizeof(lvl) - p, "%s%c", s ? ", " : "", '0' + i);
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
/* XXX: ugly hack to get order right: S0123456789 */
|
||||||
|
if (i == INIT_LEVEL)
|
||||||
|
i = 0;
|
||||||
|
else
|
||||||
|
i++;
|
||||||
|
} while (i < INIT_LEVEL);
|
||||||
|
strlcat(lvl, " ]", sizeof(lvl));
|
||||||
|
|
||||||
|
return lvl;
|
||||||
|
}
|
||||||
|
|
||||||
static int missing(svc_t *svc)
|
static int missing(svc_t *svc)
|
||||||
{
|
{
|
||||||
if (svc->state == SVC_HALTED_STATE && svc_is_missing(svc))
|
if (svc->state == SVC_HALTED_STATE && svc_is_missing(svc))
|
||||||
@@ -690,11 +838,11 @@ static int missing(svc_t *svc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *svc_command(svc_t *svc, char *buf, size_t len)
|
static char *svc_command(svc_t *svc, char *buf, size_t len, int ansi)
|
||||||
{
|
{
|
||||||
int bold = missing(svc);
|
int bold = missing(svc) && ansi;
|
||||||
|
|
||||||
if (plain || whichp(svc->cmd))
|
if (whichp(svc->cmd))
|
||||||
bold = 0;
|
bold = 0;
|
||||||
|
|
||||||
strlcpy(buf, bold ? "\e[1m" : "", len);
|
strlcpy(buf, bold ? "\e[1m" : "", len);
|
||||||
@@ -720,11 +868,11 @@ static char *svc_command(svc_t *svc, char *buf, size_t len)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *svc_environ(svc_t *svc, char *buf, size_t len)
|
static char *svc_environ(svc_t *svc, char *buf, size_t len, int ansi)
|
||||||
{
|
{
|
||||||
int bold = missing(svc);
|
int bold = missing(svc);
|
||||||
|
|
||||||
if (plain || svc_checkenv(svc) || svc->env[0] == '-')
|
if (!ansi || svc_checkenv(svc) || svc->env[0] == '-')
|
||||||
bold = 0;
|
bold = 0;
|
||||||
|
|
||||||
strlcpy(buf, bold ? "\e[1m" : "", len);
|
strlcpy(buf, bold ? "\e[1m" : "", len);
|
||||||
@@ -737,15 +885,21 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len)
|
|||||||
static char *exit_status(svc_t *svc, char *buf, size_t len)
|
static char *exit_status(svc_t *svc, char *buf, size_t len)
|
||||||
{
|
{
|
||||||
int rc, sig;
|
int rc, sig;
|
||||||
|
char *str;
|
||||||
|
|
||||||
rc = WEXITSTATUS(svc->status);
|
rc = WEXITSTATUS(svc->status);
|
||||||
sig = WTERMSIG(svc->status);
|
sig = WTERMSIG(svc->status);
|
||||||
|
|
||||||
if (WIFEXITED(svc->status))
|
if (WIFEXITED(svc->status)) {
|
||||||
snprintf(buf, len, " (code=exited, status=%d%s%s)", rc, code2str(rc),
|
str = code2str(rc);
|
||||||
|
snprintf(buf, len, " (code=exited, status=%d%s%s%s)", rc,
|
||||||
|
str[0] ? "/" : "", str,
|
||||||
svc->manual ? ", manual=yes" : "");
|
svc->manual ? ", manual=yes" : "");
|
||||||
else if (WIFSIGNALED(svc->status))
|
}
|
||||||
snprintf(buf, len, " (code=signal, status=%d%s)", sig, sig2str(sig));
|
else if (WIFSIGNALED(svc->status)) {
|
||||||
|
str = sig2str(sig);
|
||||||
|
snprintf(buf, len, " (code=signal, status=%d%s%s)", sig, str[0] ? "/" : "", str);
|
||||||
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@@ -838,6 +992,84 @@ static int svc_compare(svc_t *svc, char *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev)
|
||||||
|
{
|
||||||
|
long now = jiffies();
|
||||||
|
char *pidfn = NULL;
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
pidfn = svc->pidfile;
|
||||||
|
if (pidfn[0] == '!')
|
||||||
|
pidfn++;
|
||||||
|
else if (pidfn[0] == 0)
|
||||||
|
pidfn = "none";
|
||||||
|
|
||||||
|
fprintf(fp,
|
||||||
|
"%s"
|
||||||
|
"%s{\n"
|
||||||
|
"%s \"identity\": \"%s\",\n"
|
||||||
|
"%s \"description\": \"%s\",\n"
|
||||||
|
"%s \"type\": \"%s\",\n"
|
||||||
|
"%s \"forking\": %s,\n"
|
||||||
|
"%s \"status\": \"%s\",\n",
|
||||||
|
prev ? ",\n" : indent, prev ? indent : "",
|
||||||
|
indent, svc_ident(svc, NULL, 0),
|
||||||
|
indent, svc->desc,
|
||||||
|
indent, svc_typestr(svc),
|
||||||
|
indent, svc->forking ? "true" : "false",
|
||||||
|
indent, svc_status(svc));
|
||||||
|
if (svc->state != SVC_RUNNING_STATE) {
|
||||||
|
int rc, sig;
|
||||||
|
|
||||||
|
rc = WEXITSTATUS(svc->status);
|
||||||
|
sig = WTERMSIG(svc->status);
|
||||||
|
|
||||||
|
if (WIFEXITED(svc->status))
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"exit\": { \"%s\": %d },\n",
|
||||||
|
indent, "code", rc);
|
||||||
|
else if (WIFSIGNALED(svc->status))
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"exit\": { \"%s\": %d },\n",
|
||||||
|
indent, "signal", sig);
|
||||||
|
}
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"origin\": \"%s\",\n"
|
||||||
|
"%s \"command\": \"%s\",\n",
|
||||||
|
indent, svc->file[0] ? svc->file : "built-in",
|
||||||
|
indent, svc_command(svc, buf, sizeof(buf), 0));
|
||||||
|
svc_environ(svc, buf, sizeof(buf), 0);
|
||||||
|
if (buf[0])
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"environment\": \"%s\",\n", indent, buf);
|
||||||
|
svc_cond(svc, buf, sizeof(buf), 0);
|
||||||
|
if (buf[0])
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"condition\": %s,\n", indent, buf);
|
||||||
|
if (svc->manual)
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"starts\": %d,\n", indent, svc->once);
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"restarts\": %d,\n", indent, svc->restart_tot); /* XXX: add restart_cnt and restart_max */
|
||||||
|
fprintf(fp,
|
||||||
|
"%s \"pidfile\": \"%s\",\n"
|
||||||
|
"%s \"pid\": %d,\n"
|
||||||
|
"%s \"user\": \"%s\",\n"
|
||||||
|
"%s \"group\": \"%s\",\n"
|
||||||
|
"%s \"uptime\": %ld,\n"
|
||||||
|
"%s \"runlevels\": %s\n"
|
||||||
|
"%s}",
|
||||||
|
indent, pidfn,
|
||||||
|
indent, svc->pid,
|
||||||
|
indent, svc->username,
|
||||||
|
indent, svc->group,
|
||||||
|
indent, svc->pid ? now - svc->start_time : 0,
|
||||||
|
indent, runlevel_arr(svc->runlevels),
|
||||||
|
indent);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int show_status(char *arg)
|
static int show_status(char *arg)
|
||||||
{
|
{
|
||||||
char ident[MAX_IDENT_LEN];
|
char ident[MAX_IDENT_LEN];
|
||||||
@@ -860,18 +1092,25 @@ static int show_status(char *arg)
|
|||||||
|
|
||||||
svc = client_svc_find(arg);
|
svc = client_svc_find(arg);
|
||||||
if (!svc)
|
if (!svc)
|
||||||
ERRX(69, "no such task or service(s): %s", arg);
|
ERRX(noerr ? 0 : 69, "no such task or service(s): %s", arg);
|
||||||
|
|
||||||
if (quiet) {
|
if (quiet) {
|
||||||
if (svc_is_runtask(svc))
|
if (svc_is_runtask(svc)) {
|
||||||
if (svc->started)
|
if (svc->started)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
|
||||||
return 1;
|
return 1;
|
||||||
else
|
}
|
||||||
return svc->state != SVC_RUNNING_STATE;
|
return svc->state != SVC_RUNNING_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = json_status_one(stdout, svc, "", 0);
|
||||||
|
puts("");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
pidfn = svc->pidfile;
|
pidfn = svc->pidfile;
|
||||||
if (pidfn[0] == '!')
|
if (pidfn[0] == '!')
|
||||||
pidfn++;
|
pidfn++;
|
||||||
@@ -882,9 +1121,13 @@ static int show_status(char *arg)
|
|||||||
printf(" Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
printf(" Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
||||||
printf("Description : %s\n", svc->desc);
|
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)));
|
svc_environ(svc, buf, sizeof(buf), !plain);
|
||||||
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
|
if (buf[0])
|
||||||
printf(" Command : %s\n", svc_command(svc, buf, sizeof(buf)));
|
printf("Environment : %s\n", buf);
|
||||||
|
svc_cond(svc, buf, sizeof(buf), !plain);
|
||||||
|
if (buf[0])
|
||||||
|
printf("Condition(s): <%s>\n", buf);
|
||||||
|
printf(" Command : %s\n", svc_command(svc, buf, sizeof(buf), !plain));
|
||||||
printf(" PID file : %s\n", pidfn);
|
printf(" PID file : %s\n", pidfn);
|
||||||
printf(" PID : %d\n", svc->pid);
|
printf(" PID : %d\n", svc->pid);
|
||||||
printf(" User : %s\n", svc->username);
|
printf(" User : %s\n", svc->username);
|
||||||
@@ -918,11 +1161,29 @@ static int show_status(char *arg)
|
|||||||
return do_log(svc, "| tail -10");
|
return do_log(svc, "| tail -10");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
int prev = 0;
|
||||||
|
|
||||||
|
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||||
|
svc_ident(svc, ident, sizeof(ident));
|
||||||
|
if (num && !svc_compare(svc, arg))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!prev)
|
||||||
|
fputs("[\n", stdout);
|
||||||
|
json_status_one(stdout, svc, " ", prev++);
|
||||||
|
}
|
||||||
|
if (prev)
|
||||||
|
fputs("\n]\n", stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
col_widths();
|
col_widths();
|
||||||
if (heading) {
|
if (heading) {
|
||||||
char title[80];
|
char title[80];
|
||||||
|
|
||||||
snprintf(title, sizeof(title), "%-*s %-*s %-8s %-12s ",
|
snprintf(title, sizeof(title), "%-*s %-*s %-8s %-13s ",
|
||||||
pw, "PID", iw, "IDENT", "STATUS", "RUNLEVELS");
|
pw, "PID", iw, "IDENT", "STATUS", "RUNLEVELS");
|
||||||
if (!verbose)
|
if (!verbose)
|
||||||
strlcat(title, "DESCRIPTION", sizeof(title));
|
strlcat(title, "DESCRIPTION", sizeof(title));
|
||||||
@@ -944,14 +1205,14 @@ static int show_status(char *arg)
|
|||||||
|
|
||||||
lvls = runlevel_string(runlevel, svc->runlevels);
|
lvls = runlevel_string(runlevel, svc->runlevels);
|
||||||
if (strchr(lvls, '\e'))
|
if (strchr(lvls, '\e'))
|
||||||
printf("%-20.20s ", lvls);
|
printf("%-21.21s ", lvls);
|
||||||
else
|
else
|
||||||
printf("%-12.12s ", lvls);
|
printf("%-13.13s ", lvls);
|
||||||
|
|
||||||
if (!verbose)
|
if (!verbose)
|
||||||
puts(svc->desc);
|
puts(svc->desc);
|
||||||
else
|
else
|
||||||
puts(svc_command(svc, buf, sizeof(buf)));
|
puts(svc_command(svc, buf, sizeof(buf), !plain));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -999,9 +1260,9 @@ static int transform(char *nm)
|
|||||||
|
|
||||||
static int has_conf(char *path, size_t len, char *name)
|
static int has_conf(char *path, size_t len, char *name)
|
||||||
{
|
{
|
||||||
paste(path, len, FINIT_RCSD, name);
|
paste(path, len, finit_rcsd, name);
|
||||||
if (!fisdir(path)) {
|
if (!fisdir(path)) {
|
||||||
strlcpy(path, FINIT_RCSD, len);
|
strlcpy(path, finit_rcsd, len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1010,7 +1271,7 @@ static int has_conf(char *path, size_t len, char *name)
|
|||||||
|
|
||||||
static int usage(int rc)
|
static int usage(int rc)
|
||||||
{
|
{
|
||||||
int has_rcsd = fisdir(FINIT_RCSD);
|
int has_rcsd = fisdir(finit_rcsd);
|
||||||
int has_ena;
|
int has_ena;
|
||||||
char avail[256];
|
char avail[256];
|
||||||
char ena[256];
|
char ena[256];
|
||||||
@@ -1026,6 +1287,8 @@ static int usage(int rc)
|
|||||||
" -c, --create Create missing paths (and files) as needed\n"
|
" -c, --create Create missing paths (and files) as needed\n"
|
||||||
" -f, --force Ignore missing files and arguments, never prompt\n"
|
" -f, --force Ignore missing files and arguments, never prompt\n"
|
||||||
" -h, --help This help text\n"
|
" -h, --help This help text\n"
|
||||||
|
" -j, --json JSON output in 'status' and 'cond' commands\n"
|
||||||
|
" -n, --noerr Ignore error, e.g., already started/enabled/...\n"
|
||||||
" -1, --once Only one lap in commands like 'top'\n"
|
" -1, --once Only one lap in commands like 'top'\n"
|
||||||
" -p, --plain Use plain table headings, no ctrl chars\n"
|
" -p, --plain Use plain table headings, no ctrl chars\n"
|
||||||
" -q, --quiet Silent, only return status of command\n"
|
" -q, --quiet Silent, only return status of command\n"
|
||||||
@@ -1041,16 +1304,16 @@ static int usage(int rc)
|
|||||||
|
|
||||||
if (has_rcsd)
|
if (has_rcsd)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
" ls | list List all .conf in " FINIT_RCSD "\n"
|
" ls | list List all .conf in %s\n"
|
||||||
" create <CONF> Create .conf in %s\n"
|
" create <CONF> Create .conf in %s\n"
|
||||||
" delete <CONF> Delete .conf in %s\n"
|
" delete <CONF> Delete .conf in %s\n"
|
||||||
" show <CONF> Show .conf in %s\n"
|
" show <CONF> Show .conf in %s\n"
|
||||||
" edit <CONF> Edit .conf in %s\n"
|
" edit <CONF> Edit .conf in %s\n"
|
||||||
" touch <CONF> Change .conf in %s\n",
|
" touch <CONF> Change .conf in %s\n",
|
||||||
avail, avail, avail, avail, avail);
|
finit_rcsd, avail, avail, avail, avail, avail);
|
||||||
else
|
else
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
" show Show %s\n", FINIT_CONF);
|
" show Show %s\n", finit_conf);
|
||||||
|
|
||||||
if (has_ena)
|
if (has_ena)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@@ -1060,16 +1323,16 @@ static int usage(int rc)
|
|||||||
" disable <CONF> Disable .conf in %s\n", ena);
|
" disable <CONF> Disable .conf in %s\n", ena);
|
||||||
if (has_rcsd)
|
if (has_rcsd)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
" reload Reload *.conf in " FINIT_RCSD " (activate changes)\n");
|
" reload Reload *.conf in %s (activate changes)\n", finit_rcsd);
|
||||||
else
|
else
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
" reload Reload " FINIT_CONF " (activate changes)\n");
|
" reload Reload %s (activate changes)\n", finit_conf);
|
||||||
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"\n"
|
"\n"
|
||||||
" cond set <COND> Set (assert) user-defined condition +usr/COND\n"
|
" cond set <COND> Set (assert) user-defined conditions +usr/COND\n"
|
||||||
" cond get <COND> Get status of user-defined condition, see $? and -v\n"
|
" cond get <COND> Get status of user-defined condition, see $? and -v\n"
|
||||||
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
|
" cond clear <COND> Clear (deassert) user-defined conditions -usr/COND\n"
|
||||||
" cond status Show condition status, default cond command\n"
|
" cond status Show condition status, default cond command\n"
|
||||||
" cond dump [TYPE] Dump all, or a type of, conditions and their status\n"
|
" cond dump [TYPE] Dump all, or a type of, conditions and their status\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -1167,6 +1430,49 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
|
|||||||
return command[0].cb(NULL); /* default cmd */
|
return command[0].cb(NULL); /* default cmd */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cleanup(void)
|
||||||
|
{
|
||||||
|
if (finit_conf)
|
||||||
|
free(finit_conf);
|
||||||
|
if (finit_rcsd)
|
||||||
|
free(finit_rcsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sourcerc(void)
|
||||||
|
{
|
||||||
|
char *line;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen(_PATH_VARRUN "finit/.initrc", "r");
|
||||||
|
if (!fp)
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
|
||||||
|
char *val;
|
||||||
|
|
||||||
|
if ((val = fgetval(line, "FINIT_CONF", "="))) {
|
||||||
|
if (finit_conf)
|
||||||
|
free(finit_conf);
|
||||||
|
finit_conf = val;
|
||||||
|
}
|
||||||
|
if ((val = fgetval(line, "FINIT_RCSD", "="))) {
|
||||||
|
if (finit_rcsd)
|
||||||
|
free(finit_rcsd);
|
||||||
|
finit_rcsd = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
if (!finit_conf)
|
||||||
|
finit_conf = strdup(FINIT_CONF);
|
||||||
|
if (!finit_rcsd)
|
||||||
|
finit_rcsd = strdup(FINIT_RCSD);
|
||||||
|
|
||||||
|
atexit(cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct option long_options[] = {
|
struct option long_options[] = {
|
||||||
@@ -1175,6 +1481,8 @@ int main(int argc, char *argv[])
|
|||||||
{ "debug", 0, NULL, 'd' },
|
{ "debug", 0, NULL, 'd' },
|
||||||
{ "force", 0, NULL, 'f' },
|
{ "force", 0, NULL, 'f' },
|
||||||
{ "help", 0, NULL, 'h' },
|
{ "help", 0, NULL, 'h' },
|
||||||
|
{ "json", 0, NULL, 'j' },
|
||||||
|
{ "noerr", 0, NULL, 'n' },
|
||||||
{ "once", 0, NULL, '1' },
|
{ "once", 0, NULL, '1' },
|
||||||
{ "plain", 0, NULL, 'p' },
|
{ "plain", 0, NULL, 'p' },
|
||||||
{ "quiet", 0, NULL, 'q' },
|
{ "quiet", 0, NULL, 'q' },
|
||||||
@@ -1207,6 +1515,7 @@ int main(int argc, char *argv[])
|
|||||||
{ "disable", NULL, serv_disable, NULL, NULL },
|
{ "disable", NULL, serv_disable, NULL, NULL },
|
||||||
{ "touch", NULL, serv_touch, NULL, NULL },
|
{ "touch", NULL, serv_touch, NULL, NULL },
|
||||||
{ "show", NULL, serv_show, NULL, NULL },
|
{ "show", NULL, serv_show, NULL, NULL },
|
||||||
|
{ "cat", NULL, serv_show, NULL, NULL }, /* alias */
|
||||||
{ "edit", NULL, serv_edit, NULL, NULL },
|
{ "edit", NULL, serv_edit, NULL, NULL },
|
||||||
{ "create", NULL, serv_creat, NULL, NULL },
|
{ "create", NULL, serv_creat, NULL, NULL },
|
||||||
{ "delete", NULL, serv_delete, NULL, NULL },
|
{ "delete", NULL, serv_delete, NULL, NULL },
|
||||||
@@ -1219,6 +1528,7 @@ int main(int argc, char *argv[])
|
|||||||
{ "stop", NULL, do_stop, NULL, NULL },
|
{ "stop", NULL, do_stop, NULL, NULL },
|
||||||
{ "restart", NULL, do_restart, NULL, NULL },
|
{ "restart", NULL, do_restart, NULL, NULL },
|
||||||
{ "signal", NULL, NULL, NULL, do_signal },
|
{ "signal", NULL, NULL, NULL, do_signal },
|
||||||
|
{ "kill", NULL, NULL, NULL, do_signal }, /* alias */
|
||||||
|
|
||||||
{ "cgroup", NULL, show_cgroup, &cgrp, NULL },
|
{ "cgroup", NULL, show_cgroup, &cgrp, NULL },
|
||||||
{ "ps", NULL, show_cgps, &cgrp, NULL },
|
{ "ps", NULL, show_cgps, &cgrp, NULL },
|
||||||
@@ -1241,10 +1551,11 @@ int main(int argc, char *argv[])
|
|||||||
return reboot_main(argc, argv);
|
return reboot_main(argc, argv);
|
||||||
|
|
||||||
/* Enable functionality depending on system capabilities */
|
/* Enable functionality depending on system capabilities */
|
||||||
|
sourcerc();
|
||||||
cgrp = cgroup_avail();
|
cgrp = cgroup_avail();
|
||||||
utmp = has_utmp();
|
utmp = has_utmp();
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "1bcdfh?pqtvV", long_options, NULL)) != EOF) {
|
while ((c = getopt_long(argc, argv, "1bcdfh?jnpqtvV", long_options, NULL)) != EOF) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case '1':
|
case '1':
|
||||||
ionce = 1;
|
ionce = 1;
|
||||||
@@ -1270,6 +1581,14 @@ int main(int argc, char *argv[])
|
|||||||
case '?':
|
case '?':
|
||||||
return usage(0);
|
return usage(0);
|
||||||
|
|
||||||
|
case 'j':
|
||||||
|
json = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
noerr = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
plain = 1;
|
plain = 1;
|
||||||
break;
|
break;
|
||||||
|
|||||||
+6
-1
@@ -1,6 +1,6 @@
|
|||||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,10 +29,15 @@
|
|||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
extern char *finit_conf;
|
||||||
|
extern char *finit_rcsd;
|
||||||
|
|
||||||
extern int icreate; /* initctl -c */
|
extern int icreate; /* initctl -c */
|
||||||
extern int iforce; /* initctl -f */
|
extern int iforce; /* initctl -f */
|
||||||
extern int ionce; /* initctl -1 */
|
extern int ionce; /* initctl -1 */
|
||||||
extern int heading; /* initctl -t */
|
extern int heading; /* initctl -t */
|
||||||
|
extern int json; /* initctl -j */
|
||||||
|
extern int noerr; /* initctl -n */
|
||||||
extern int verbose; /* initctl -v */
|
extern int verbose; /* initctl -v */
|
||||||
extern int plain; /* initctl -p */
|
extern int plain; /* initctl -p */
|
||||||
extern int quiet; /* initctl -q */
|
extern int quiet; /* initctl -q */
|
||||||
|
|||||||
+16
-11
@@ -1,7 +1,7 @@
|
|||||||
/* inotify watcher for files or directories
|
/* inotify watcher for files or directories
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -52,7 +52,7 @@ int iwatch_init(struct iwatch *iw)
|
|||||||
|
|
||||||
iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
if (iw->fd < 0) {
|
if (iw->fd < 0) {
|
||||||
_pe("Failed creating inotify descriptor");
|
warn("Failed creating inotify descriptor");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ int iwatch_init(struct iwatch *iw)
|
|||||||
len = sizeof(sz);
|
len = sizeof(sz);
|
||||||
if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len)) {
|
if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len)) {
|
||||||
if (setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)))
|
if (setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)))
|
||||||
_pe("Failed increasing size of inotify socket");
|
warn("Failed increasing size of inotify socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
@@ -87,7 +87,7 @@ void iwatch_exit(struct iwatch *iw)
|
|||||||
initialized = 0;
|
initialized = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
|
||||||
{
|
{
|
||||||
struct iwatch_path *iwp;
|
struct iwatch_path *iwp;
|
||||||
char *path;
|
char *path;
|
||||||
@@ -97,18 +97,18 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!fexist(file)) {
|
if (!fexist(file)) {
|
||||||
_d("skipping %s: no such file or directory", file);
|
dbg("skipping %s: no such file or directory", file);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
_d("adding new watcher for path %s", file);
|
dbg("adding new watcher for path %s", file);
|
||||||
|
|
||||||
path = strdup(file);
|
path = strdup(file);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
_pe("Out of memory");
|
warn("Out of memory");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
|
wd = inotify_add_watch(iw->fd, path, mask);
|
||||||
if (wd < 0) {
|
if (wd < 0) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case EEXIST:
|
case EEXIST:
|
||||||
@@ -124,7 +124,7 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
|||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_pe("Failed adding watcher for %s", path);
|
warn("Failed adding watcher for %s", path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
|||||||
|
|
||||||
iwp = malloc(sizeof(struct iwatch_path));
|
iwp = malloc(sizeof(struct iwatch_path));
|
||||||
if (!iwp) {
|
if (!iwp) {
|
||||||
_pe("Failed allocating new `struct iwatch_path`");
|
warn("Failed allocating new `struct iwatch_path`");
|
||||||
inotify_rm_watch(iw->fd, wd);
|
inotify_rm_watch(iw->fd, wd);
|
||||||
free(path);
|
free(path);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -147,12 +147,17 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||||
|
{
|
||||||
|
return iwatch_add1(iw, file, IWATCH_MASK | mask);
|
||||||
|
}
|
||||||
|
|
||||||
int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp)
|
int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp)
|
||||||
{
|
{
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
_d("Removing watcher for removed path %s", iwp->path);
|
dbg("Removing watcher for removed path %s", iwp->path);
|
||||||
|
|
||||||
TAILQ_REMOVE(&iw->iwp_list, iwp, link);
|
TAILQ_REMOVE(&iw->iwp_list, iwp, link);
|
||||||
inotify_rm_watch(iw->fd, iwp->wd);
|
inotify_rm_watch(iw->fd, iwp->wd);
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
/* inotify watcher for files or directories
|
/* inotify watcher for files or directories
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -59,6 +59,7 @@ struct iwatch {
|
|||||||
int iwatch_init (struct iwatch *iw);
|
int iwatch_init (struct iwatch *iw);
|
||||||
void iwatch_exit (struct iwatch *iw);
|
void iwatch_exit (struct iwatch *iw);
|
||||||
|
|
||||||
|
int iwatch_add1 (struct iwatch *iw, char *file, uint32_t mask);
|
||||||
int iwatch_add (struct iwatch *iw, char *file, uint32_t mask);
|
int iwatch_add (struct iwatch *iw, char *file, uint32_t mask);
|
||||||
int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp);
|
int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp);
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
/* Listens to kernel events like AC power status and manages sys/ conditions
|
/* Listens to kernel events like AC power status and manages sys/ conditions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -152,7 +152,7 @@ static void init(void)
|
|||||||
|
|
||||||
for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
|
for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
|
||||||
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
|
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating %s condition directory, %s", COND_SYS,
|
warn("Failed creating %s condition directory, %s", COND_SYS,
|
||||||
cond_dirs[i]);
|
cond_dirs[i]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Finit daemon log functions
|
/* Finit daemon log functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <time.h>
|
||||||
#ifdef _LIBITE_LITE
|
#ifdef _LIBITE_LITE
|
||||||
# include <libite/lite.h>
|
# include <libite/lite.h>
|
||||||
#else
|
#else
|
||||||
@@ -99,6 +100,21 @@ void log_debug(void)
|
|||||||
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
|
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *l2s(int prio)
|
||||||
|
{
|
||||||
|
int level = prio & ~LOG_CONSOLE;
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case LOG_ERR: return "ERR";
|
||||||
|
case LOG_WARNING: return "WRN";
|
||||||
|
case LOG_NOTICE: return "NOT";
|
||||||
|
case LOG_INFO: return "NFO";
|
||||||
|
case LOG_DEBUG: return "DBG";
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return "UNK";
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Log to /dev/kmsg until syslogd has started, then openlog()
|
* Log to /dev/kmsg until syslogd has started, then openlog()
|
||||||
* and continue logging as a regular daemon.
|
* and continue logging as a regular daemon.
|
||||||
@@ -119,8 +135,16 @@ void logit(int prio, const char *fmt, ...)
|
|||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) {
|
if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) {
|
||||||
vfprintf(stderr, fmt, ap);
|
static char buf[512];
|
||||||
fputs("\n", stderr);
|
time_t now;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
now = time(NULL);
|
||||||
|
pos = (int)strftime(buf, sizeof(buf), "%FT%T", localtime(&now));
|
||||||
|
pos += snprintf(&buf[pos], sizeof(buf) - pos, " [%s]: ", l2s(prio));
|
||||||
|
vsnprintf(&buf[pos], sizeof(buf) - pos, fmt, ap);
|
||||||
|
strlcat(buf, "\n", sizeof(buf));
|
||||||
|
fputs(buf, stderr);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Finit daemon log functions
|
/* Finit daemon log functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,14 +31,43 @@
|
|||||||
#define LOG_CONSOLE (14<<3)
|
#define LOG_CONSOLE (14<<3)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef __FINIT__
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int debug;
|
||||||
|
|
||||||
|
static __attribute__ ((format (printf, 1, 2))) inline void dbg(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (!debug)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fputs("\n", stderr);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
#define info(fmt, args...) warnx(fmt, ##args)
|
||||||
|
#define note(fmt, args...) warnx(fmt, ##args)
|
||||||
|
#else
|
||||||
/*
|
/*
|
||||||
* Developer error and debug messages, otherwise --> use logit() <--
|
* General log macros, similar to those used by initctl. Initially intended
|
||||||
* ~~~~~~~~~~~
|
* only for bridging client.c in Finit and initctl.
|
||||||
* All of these prepend the function, so only use for critical warnings
|
*/
|
||||||
* errors and debug messages. For all other user messages, see logit()
|
#define dbg(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
|
||||||
*
|
#define info(fmt, args...) logit(LOG_INFO, "%s():" fmt, __func__, ##args)
|
||||||
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
|
#define note(fmt, args...) logit(LOG_NOTICE, "%s():" fmt, __func__, ##args)
|
||||||
* use `initctl debug` or add `debug` to the kernel cmdline.
|
#define warnx(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
|
||||||
|
#define warn(fmt, args...) logit(LOG_WARNING, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
|
||||||
|
#define errx(rc, fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
|
||||||
|
#define err(rc, fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DEPRECATED developer error and debug messages, please migrate to above!
|
||||||
|
* Will be removed without further warning in a future Finit release.
|
||||||
*/
|
*/
|
||||||
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
|
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
|
||||||
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
|
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
|
||||||
@@ -48,9 +77,10 @@
|
|||||||
void log_init (void);
|
void log_init (void);
|
||||||
void log_exit (void);
|
void log_exit (void);
|
||||||
|
|
||||||
void log_debug (void);
|
void log_debug(void);
|
||||||
|
|
||||||
void logit (int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
void logit (int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
void flog (char *file, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
void flog (char *file, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* FINIT_LOG_H_ */
|
#endif /* FINIT_LOG_H_ */
|
||||||
|
|||||||
+7
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -208,8 +208,13 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ident) {
|
||||||
|
ident = getenv("LOGNAME");
|
||||||
if (!ident)
|
if (!ident)
|
||||||
ident = getenv("LOGNAME") ?: getenv("USER");
|
ident = getenv("USER");
|
||||||
|
if (!ident)
|
||||||
|
ident = PACKAGE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
if (optind < argc) {
|
if (optind < argc) {
|
||||||
size_t pos = 0, len = sizeof(buf);
|
size_t pos = 0, len = sizeof(buf);
|
||||||
|
|||||||
+2
-3
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -97,8 +97,7 @@ int logrotate(char *file, int num, off_t sz)
|
|||||||
} else {
|
} else {
|
||||||
fallback:
|
fallback:
|
||||||
if (truncate(file, 0))
|
if (truncate(file, 0))
|
||||||
syslog(LOG_ERR, "Failed truncating %s during logrotate: %s",
|
syslog(LOG_ERR, "Failed truncating %s: %s", file, strerror(errno));
|
||||||
file, strerror(errno));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/* Handle MD arrays
|
/* Handle MD arrays
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,6 @@
|
|||||||
/* Mount and unmount helpers
|
/* Mount and unmount helpers
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -97,13 +97,14 @@ static int unmount(const char *target)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
dbg("Unmounting %s", target);
|
||||||
|
|
||||||
rc = umount(target);
|
rc = umount(target);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
if (errno != EBUSY)
|
if (errno != EBUSY)
|
||||||
print(2, "Failed unmounting %s, error %d: %s", target, errno, strerror(errno));
|
print(2, "Failed unmounting %s, error %d: %s", target, errno, strerror(errno));
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
print(0, "Unmounting %s", target);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* PID and PID file helpers
|
/* PID and PID file helpers
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -149,6 +149,8 @@ int pid_file_create(svc_t *svc)
|
|||||||
|
|
||||||
int pid_file_set(svc_t *svc, char *file, int not)
|
int pid_file_set(svc_t *svc, char *file, int not)
|
||||||
{
|
{
|
||||||
|
char buf[sizeof(svc->pidfile)];
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
file = pid_file(svc);
|
file = pid_file(svc);
|
||||||
if (!file)
|
if (!file)
|
||||||
@@ -165,7 +167,9 @@ int pid_file_set(svc_t *svc, char *file, int not)
|
|||||||
not = 1;
|
not = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not);
|
de_dotdot(file);
|
||||||
|
pid_runpath(file, buf, sizeof(buf));
|
||||||
|
strlcpy(&svc->pidfile[not], buf, sizeof(svc->pidfile) - not);
|
||||||
if (not)
|
if (not)
|
||||||
svc->pidfile[0] = '!';
|
svc->pidfile[0] = '!';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* PID and PID file helpers
|
/* PID and PID file helpers
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -51,6 +51,7 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
|
|||||||
{
|
{
|
||||||
static char *prefix = "/var/run";
|
static char *prefix = "/var/run";
|
||||||
static int unknown = 1;
|
static int unknown = 1;
|
||||||
|
char *ptr;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (unknown) {
|
if (unknown) {
|
||||||
@@ -59,14 +60,13 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
|
|||||||
unknown = 0;
|
unknown = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp(file, "/var/run/", 9))
|
ptr = strstr(file, "/run/");
|
||||||
file += 9;
|
if (ptr)
|
||||||
else if (!strncmp(file, "/run/", 5))
|
file = ptr + 5;
|
||||||
file += 5;
|
|
||||||
|
|
||||||
rc = paste(path, len, prefix, file);
|
rc = paste(path, len, prefix, file);
|
||||||
if (rc < 0 || (size_t)rc >= len)
|
if (rc < 0 || (size_t)rc >= len)
|
||||||
_e("File path '%s' truncated, should end with '%s'", path, file);
|
err(1, "File path '%s' truncated, should end with '%s'", path, file);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-22
@@ -1,6 +1,6 @@
|
|||||||
/* Plugin based services architecture for finit
|
/* Plugin based services architecture for finit
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
#include "sig.h"
|
||||||
|
|
||||||
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
||||||
#define SEARCH_PLUGIN(str) \
|
#define SEARCH_PLUGIN(str) \
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
return p; \
|
return p; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int plugloaded = 0; /* Set while plugins are loaded */
|
||||||
static char *plugpath = NULL; /* Set by first load. */
|
static char *plugpath = NULL; /* Set by first load. */
|
||||||
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
|
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ int plugin_register(plugin_t *plugin)
|
|||||||
|
|
||||||
/* Already registered? */
|
/* Already registered? */
|
||||||
if (plugin_find(plugin->name)) {
|
if (plugin_find(plugin->name)) {
|
||||||
_d("... %s already loaded", plugin->name);
|
dbg("... %s already loaded", plugin->name);
|
||||||
free(plugin->name);
|
free(plugin->name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -120,10 +122,10 @@ int plugin_unregister(plugin_t *plugin)
|
|||||||
#ifndef ENABLE_STATIC
|
#ifndef ENABLE_STATIC
|
||||||
TAILQ_REMOVE(&plugins, plugin, link);
|
TAILQ_REMOVE(&plugins, plugin, link);
|
||||||
|
|
||||||
_d("%s exiting ...", plugin->name);
|
dbg("%s exiting ...", plugin->name);
|
||||||
free(plugin->name);
|
free(plugin->name);
|
||||||
#else
|
#else
|
||||||
_d("Finit built statically, cannot unload %s ...", plugin->name);
|
dbg("Finit built statically, cannot unload %s ...", plugin->name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -158,8 +160,8 @@ plugin_t *plugin_find(char *name)
|
|||||||
SEARCH_PLUGIN(name);
|
SEARCH_PLUGIN(name);
|
||||||
|
|
||||||
if (plugpath && name[0] != '/') {
|
if (plugpath && name[0] != '/') {
|
||||||
int noext;
|
|
||||||
char path[CMD_SIZE];
|
char path[CMD_SIZE];
|
||||||
|
int noext;
|
||||||
|
|
||||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||||
snprintf(path, sizeof(path), "%s%s%s%s", plugpath,
|
snprintf(path, sizeof(path), "%s%s%s%s", plugpath,
|
||||||
@@ -195,22 +197,73 @@ int plugin_exists(hook_point_t no)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_HOOK_SCRIPTS_PLUGIN) && defined(PLUGIN_HOOK_SCRIPTS_PATH)
|
||||||
|
#define CHOOSE(x, y) y
|
||||||
|
static const char *hscript_paths[] = HOOK_TYPES;
|
||||||
|
#undef CHOOSE
|
||||||
|
|
||||||
|
void plugin_script_run(hook_point_t no)
|
||||||
|
{
|
||||||
|
const char *hook_name = hscript_paths[no];
|
||||||
|
const char *env[] = {
|
||||||
|
"FINIT_HOOK_NAME", hook_name,
|
||||||
|
"FINIT_SHUTDOWN", NULL,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
char path[CMD_SIZE] = "";
|
||||||
|
|
||||||
|
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
|
||||||
|
strlcat(path, hook_name + 4, sizeof(path));
|
||||||
|
|
||||||
|
if (no >= HOOK_SHUTDOWN) {
|
||||||
|
switch (halt) {
|
||||||
|
case SHUT_OFF:
|
||||||
|
env[3] = "poweroff";
|
||||||
|
break;
|
||||||
|
case SHUT_HALT:
|
||||||
|
env[3] = "halt";
|
||||||
|
break;
|
||||||
|
case SHUT_REBOOT:
|
||||||
|
env[3] = "reboot";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
env[2] = NULL;
|
||||||
|
|
||||||
|
run_parts(path, NULL, env, 0);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void plugin_script_run(hook_point_t no)
|
||||||
|
{
|
||||||
|
(void)no;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Some hooks are called with a fixed argument */
|
/* Some hooks are called with a fixed argument */
|
||||||
void plugin_run_hook(hook_point_t no, void *arg)
|
void plugin_run_hook(hook_point_t no, void *arg)
|
||||||
{
|
{
|
||||||
plugin_t *p, *tmp;
|
plugin_t *p, *tmp;
|
||||||
|
|
||||||
|
#ifdef HAVE_HOOK_SCRIPTS_PLUGIN
|
||||||
|
if (!cond_is_available() && !plugloaded) {
|
||||||
|
dbg("conditions not available, calling script based hooks only!");
|
||||||
|
plugin_script_run(no);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
PLUGIN_ITERATOR(p, tmp) {
|
PLUGIN_ITERATOR(p, tmp) {
|
||||||
if (p->hook[no].cb) {
|
if (p->hook[no].cb) {
|
||||||
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL");
|
dbg("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL");
|
||||||
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Conditions are stored in /run, so don't try to signal
|
/*
|
||||||
|
* Conditions are stored in /run, so don't try to signal
|
||||||
* conditions for any hooks before filesystems have been
|
* conditions for any hooks before filesystems have been
|
||||||
* mounted. */
|
* mounted.
|
||||||
if (no >= HOOK_MOUNT_ERROR)
|
*/
|
||||||
|
if (cond_is_available() && no >= HOOK_BASEFS_UP && no <= HOOK_SHUTDOWN)
|
||||||
cond_set_oneshot(hook_cond[no]);
|
cond_set_oneshot(hook_cond[no]);
|
||||||
|
|
||||||
service_step_all(SVC_TYPE_RUNTASK);
|
service_step_all(SVC_TYPE_RUNTASK);
|
||||||
@@ -235,7 +288,7 @@ static void generic_io_cb(uev_t *w, void *arg, int events)
|
|||||||
/* Stop watcher, callback may close descriptor on us ... */
|
/* Stop watcher, callback may close descriptor on us ... */
|
||||||
uev_io_stop(w);
|
uev_io_stop(w);
|
||||||
|
|
||||||
_d("Calling I/O %s from runloop...", basename(p->name));
|
dbg("Calling I/O %s from runloop...", basename(p->name));
|
||||||
p->io.cb(p->io.arg, w->fd, events);
|
p->io.cb(p->io.arg, w->fd, events);
|
||||||
|
|
||||||
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
||||||
@@ -248,9 +301,9 @@ int plugin_io_init(plugin_t *p)
|
|||||||
if (!is_io_plugin(p))
|
if (!is_io_plugin(p))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
_d("Initializing plugin %s for I/O", basename(p->name));
|
dbg("Initializing plugin %s for I/O", basename(p->name));
|
||||||
if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) {
|
if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) {
|
||||||
_e("Failed setting up I/O plugin %s", basename(p->name));
|
warn("Failed setting up I/O plugin %s", basename(p->name));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,16 +355,18 @@ static int load_one(char *path, char *name)
|
|||||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||||
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
|
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
|
||||||
|
|
||||||
_d("Loading plugin %s ...", sofile);
|
dbg("Loading plugin %s ...", sofile);
|
||||||
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
|
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
_e("Failed loading plugin %s: %s", sofile, dlerror());
|
char *error = dlerror();
|
||||||
|
|
||||||
|
warn("Failed loading plugin %s: %s", sofile, error ? error : "unknown error");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin = TAILQ_LAST(&plugins, plugin_head);
|
plugin = TAILQ_LAST(&plugins, plugin_head);
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
_e("Plugin %s failed to register, unloading from memory", sofile);
|
warn("Plugin %s failed to register, unloading from memory", sofile);
|
||||||
dlclose(handle);
|
dlclose(handle);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -338,9 +393,9 @@ static void check_plugin_depends(plugin_t *plugin)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
|
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
|
||||||
// _d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
|
// dbg("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
|
||||||
if (plugin_find(plugin->depends[i])) {
|
if (plugin_find(plugin->depends[i])) {
|
||||||
// _d("OK plugin %s was already loaded.", plugin->depends[i]);
|
// dbg("OK plugin %s was already loaded.", plugin->depends[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,15 +405,15 @@ static void check_plugin_depends(plugin_t *plugin)
|
|||||||
|
|
||||||
static int load_plugins(char *path)
|
static int load_plugins(char *path)
|
||||||
{
|
{
|
||||||
|
struct dirent *entry;
|
||||||
int fail = 0;
|
int fail = 0;
|
||||||
DIR *dp;
|
DIR *dp;
|
||||||
struct dirent *entry;
|
|
||||||
|
|
||||||
dp = opendir(path);
|
dp = opendir(path);
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
return 0;
|
return 0;
|
||||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
warn("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
plugpath = path;
|
plugpath = path;
|
||||||
@@ -434,7 +489,7 @@ int plugin_init(uev_ctx_t *ctx)
|
|||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
if (paths) {
|
if (paths) {
|
||||||
_d("Loading external plugins from %s ...", paths);
|
dbg("Loading external plugins from %s ...", paths);
|
||||||
path = strtok(paths, ":");
|
path = strtok(paths, ":");
|
||||||
while (path) {
|
while (path) {
|
||||||
load_plugins(path);
|
load_plugins(path);
|
||||||
@@ -442,6 +497,8 @@ int plugin_init(uev_ctx_t *ctx)
|
|||||||
}
|
}
|
||||||
free(paths);
|
free(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugloaded = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return init_plugins(ctx);
|
return init_plugins(ctx);
|
||||||
@@ -453,9 +510,13 @@ void plugin_exit(void)
|
|||||||
plugin_t *p, *tmp;
|
plugin_t *p, *tmp;
|
||||||
|
|
||||||
PLUGIN_ITERATOR(p, tmp) {
|
PLUGIN_ITERATOR(p, tmp) {
|
||||||
if (dlclose(p->handle))
|
if (dlclose(p->handle)) {
|
||||||
_e("Failed: unloading plugin %s: %s", p->name, dlerror());
|
char *error = dlerror();
|
||||||
|
warn("Failed unloading plugin %s: %s", p->name, error ? error : "unknown error");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugloaded = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,6 @@
|
|||||||
/* Plugin based services architecture for finit
|
/* Plugin based services architecture for finit
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
||||||
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
|
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
|
||||||
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
||||||
|
CHOOSE(HOOK_SVC_PLUGIN, "hook/svc/plugin"), \
|
||||||
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
||||||
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
||||||
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
||||||
@@ -81,7 +82,10 @@
|
|||||||
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
|
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
|
||||||
\
|
\
|
||||||
/* Shutdown hooks, runlevel [06] */ \
|
/* Shutdown hooks, runlevel [06] */ \
|
||||||
|
CHOOSE(HOOK_NETWORK_DN, "hook/net/down"), \
|
||||||
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
|
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
|
||||||
|
CHOOSE(HOOK_SVC_DN, "hook/svc/down"), \
|
||||||
|
CHOOSE(HOOK_SYS_DN, "hook/sys/down"), \
|
||||||
CHOOSE(HOOK_MAX_NUM, "nop") \
|
CHOOSE(HOOK_MAX_NUM, "nop") \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-2
@@ -1,7 +1,7 @@
|
|||||||
/* Private header file for main finit daemon, not for plugins
|
/* Private header file for main finit daemon, not for plugins
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -32,17 +32,28 @@
|
|||||||
#define MATCH_CMD(l, c, x) \
|
#define MATCH_CMD(l, c, x) \
|
||||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||||
|
|
||||||
|
#define IS_RESERVED_RUNLEVEL(l) (l == 0 || l == 6 || l == INIT_LEVEL)
|
||||||
|
|
||||||
|
extern char *finit_conf;
|
||||||
|
extern char *finit_rcsd;
|
||||||
|
extern svc_t *wdog;
|
||||||
extern int service_interval;
|
extern int service_interval;
|
||||||
|
extern char *fsck_mode;
|
||||||
|
extern char *fsck_repair;
|
||||||
|
|
||||||
int api_init (uev_ctx_t *ctx);
|
int api_init (uev_ctx_t *ctx);
|
||||||
int api_exit (void);
|
int api_exit (void);
|
||||||
|
void conf_flush_events(void);
|
||||||
|
|
||||||
void service_monitor (pid_t lost, int status);
|
void service_monitor (pid_t lost, int status);
|
||||||
|
void service_notify_cb(uev_t *w, void *arg, int events);
|
||||||
|
|
||||||
const char *plugin_hook_str(hook_point_t no);
|
const char *plugin_hook_str (hook_point_t no);
|
||||||
int plugin_exists (hook_point_t no);
|
int plugin_exists (hook_point_t no);
|
||||||
|
|
||||||
void plugin_run_hook (hook_point_t no, void *arg);
|
void plugin_run_hook (hook_point_t no, void *arg);
|
||||||
void plugin_run_hooks (hook_point_t no);
|
void plugin_run_hooks (hook_point_t no);
|
||||||
|
void plugin_script_run(hook_point_t no);
|
||||||
|
|
||||||
int plugin_init (uev_ctx_t *ctx);
|
int plugin_init (uev_ctx_t *ctx);
|
||||||
void plugin_exit (void);
|
void plugin_exit (void);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/* reboot, halt, poweroff, shutdown, suspend for finit
|
/* reboot, halt, poweroff, shutdown, suspend for finit
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2009-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+222
@@ -0,0 +1,222 @@
|
|||||||
|
#include <ctype.h> /* isdigit() */
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <paths.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#ifdef _LIBITE_LITE
|
||||||
|
# include <libite/conio.h>
|
||||||
|
# include <libite/lite.h>
|
||||||
|
#else
|
||||||
|
# include <lite/conio.h>
|
||||||
|
# include <lite/lite.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "sig.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifdef __FINIT__
|
||||||
|
#include "helpers.h"
|
||||||
|
#else
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
|
||||||
|
int debug;
|
||||||
|
|
||||||
|
static void print_desc(const char *prefix, const char *msg)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\e[1m[\e[0m\e[1;33m ⋯ \e[0m\e[1m]\e[0m %s %s", prefix, msg);
|
||||||
|
}
|
||||||
|
static void print_result(int rc)
|
||||||
|
{
|
||||||
|
if (rc)
|
||||||
|
fputs("\r\e[1m[\e[0m\e[1;31mFAIL\e[0m\e[1m]\e[0m\n", stderr);
|
||||||
|
else
|
||||||
|
fputs("\r\e[1m[\e[0m\e[1;32m OK \e[0m\e[1m]\e[0m\n", stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sig_unblock(void)
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
sigset_t nmask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sigemptyset(&nmask);
|
||||||
|
sigaddset(&nmask, SIGHUP);
|
||||||
|
sigaddset(&nmask, SIGCHLD);
|
||||||
|
sigaddset(&nmask, SIGINT);
|
||||||
|
sigaddset(&nmask, SIGPWR);
|
||||||
|
sigaddset(&nmask, SIGSTOP);
|
||||||
|
sigaddset(&nmask, SIGTSTP);
|
||||||
|
sigaddset(&nmask, SIGCONT);
|
||||||
|
sigaddset(&nmask, SIGTERM);
|
||||||
|
sigaddset(&nmask, SIGUSR1);
|
||||||
|
sigaddset(&nmask, SIGUSR2);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||||
|
|
||||||
|
/* Reset signal handlers that were set by the parent process */
|
||||||
|
for (i = 1; i < NSIG; i++)
|
||||||
|
DFLSIG(sa, i, 0);
|
||||||
|
}
|
||||||
|
#endif /* __FINIT__ */
|
||||||
|
|
||||||
|
static void run_env(const char *env[])
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
if (!env)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (env[i]) {
|
||||||
|
size_t j = i + 1;
|
||||||
|
|
||||||
|
if (!env[j])
|
||||||
|
break;
|
||||||
|
|
||||||
|
setenv(env[i], env[j], 1);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_parts(char *dir, char *cmd, const char *env[], int progress)
|
||||||
|
{
|
||||||
|
size_t cmdlen = cmd ? strlen(cmd) : strlen("start");
|
||||||
|
struct dirent **d;
|
||||||
|
int i, num;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
num = scandir(dir, &d, NULL, alphasort);
|
||||||
|
if (num < 0) {
|
||||||
|
dbg("No files found in %s, skipping ...", dir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
char path[strlen(dir) + strlen(d[i]->d_name) + 3 + cmdlen];
|
||||||
|
const char *name = d[i]->d_name;
|
||||||
|
char *argv[4] = {
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
path,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
struct stat st;
|
||||||
|
int result = 0;
|
||||||
|
pid_t pid = 0;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* skip backup files */
|
||||||
|
if (name[strlen(name) - 1] == '~')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* cannot rely on d_type, not supported on all filesystems */
|
||||||
|
paste(path, sizeof(path), dir, name);
|
||||||
|
if (stat(path, &st)) {
|
||||||
|
warn("failed stat(%s)", path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip non-executable files and directories */
|
||||||
|
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||||
|
dbg("skipping %s not an executable or is a directory", path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the callee didn't supply a run_parts() argument */
|
||||||
|
if (!cmd) {
|
||||||
|
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||||
|
if (name[0] == 'S' && isdigit(name[1]))
|
||||||
|
strlcat(path, " start", sizeof(path));
|
||||||
|
else if (name[0] == 'K' && isdigit(name[1]))
|
||||||
|
strlcat(path, " stop", sizeof(path));
|
||||||
|
} else {
|
||||||
|
strlcat(path, " ", sizeof(path));
|
||||||
|
strlcat(path, cmd, sizeof(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress)
|
||||||
|
print_desc("Calling", path);
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (!pid) {
|
||||||
|
sig_unblock();
|
||||||
|
run_env(env);
|
||||||
|
|
||||||
|
_exit(execvp(_PATH_BSHELL, argv));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitpid(pid, &status, 0) == -1) {
|
||||||
|
warnx("failed starting %s, error %d: %s", path, errno, strerror(errno));
|
||||||
|
result = 1;
|
||||||
|
} else {
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
result = WEXITSTATUS(status);
|
||||||
|
dbg("%s exited with status %d", path, result);
|
||||||
|
} else if (WIFSIGNALED(status)) {
|
||||||
|
warnx("%s terminated by signal %d", path, WTERMSIG(status));
|
||||||
|
result = 1;
|
||||||
|
} else
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress)
|
||||||
|
print_result(result);
|
||||||
|
rc += result;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (num--)
|
||||||
|
free(d[num]);
|
||||||
|
free(d);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __FINIT__
|
||||||
|
static int usage(int rc)
|
||||||
|
{
|
||||||
|
warnx("usage: runparts [-dh?] DIRECTORY");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int rc, c, progress = 0;
|
||||||
|
char *dir;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "dh?p")) != EOF) {
|
||||||
|
switch(c) {
|
||||||
|
case 'd':
|
||||||
|
debug = 1;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
case '?':
|
||||||
|
return usage(0);
|
||||||
|
case 'p':
|
||||||
|
progress = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return usage(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind >= argc)
|
||||||
|
return usage(1);
|
||||||
|
|
||||||
|
dir = argv[optind];
|
||||||
|
if (!fisdir(dir))
|
||||||
|
return usage(1);
|
||||||
|
|
||||||
|
prctl(PR_SET_CHILD_SUBREAPER, 1);
|
||||||
|
|
||||||
|
rc = run_parts(dir, NULL, NULL, progress);
|
||||||
|
if (rc == -1)
|
||||||
|
err(1, "failed run-parts %s", dir);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#endif /* __FINIT__ */
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/* Work queue helper functions
|
/* Work queue helper functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/* Work queue helper functions
|
/* Work queue helper functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+124
-52
@@ -1,6 +1,6 @@
|
|||||||
/* List and enable/disable service configurations
|
/* List and enable/disable service configurations
|
||||||
*
|
*
|
||||||
* Copyright (c) 2017-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2017-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -41,6 +41,10 @@ static int is_builtin(char *arg)
|
|||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
|
/* skip check, we may be in non-responsive bootstrap */
|
||||||
|
if (iforce)
|
||||||
|
return 0;
|
||||||
|
|
||||||
svc = client_svc_find(arg);
|
svc = client_svc_find(arg);
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -66,7 +70,7 @@ static int calc_width(char *arr[], size_t len)
|
|||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_list(const char *path)
|
static void do_list(const char *path, char *title, int comma)
|
||||||
{
|
{
|
||||||
static int once = 0;
|
static int once = 0;
|
||||||
int width, num, prev;
|
int width, num, prev;
|
||||||
@@ -78,6 +82,7 @@ static void do_list(const char *path)
|
|||||||
|
|
||||||
if (!fexist(path))
|
if (!fexist(path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dir = strdupa(path);
|
dir = strdupa(path);
|
||||||
if (!dir)
|
if (!dir)
|
||||||
return;
|
return;
|
||||||
@@ -88,6 +93,11 @@ static void do_list(const char *path)
|
|||||||
else
|
else
|
||||||
ptr = dir;
|
ptr = dir;
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
printf("%s\"%s\": \"%s\"", comma ? "," :"", title, path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (heading)
|
if (heading)
|
||||||
print_header("%s%s ", once ? "\n" : "", dir);
|
print_header("%s%s ", once ? "\n" : "", dir);
|
||||||
puts(ptr);
|
puts(ptr);
|
||||||
@@ -101,7 +111,7 @@ static void do_list(const char *path)
|
|||||||
if (gl.gl_pathc <= 0)
|
if (gl.gl_pathc <= 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (plain) {
|
if (plain && !json) {
|
||||||
if (heading)
|
if (heading)
|
||||||
print_header("%s%s ", once ? "\n" : "", path);
|
print_header("%s%s ", once ? "\n" : "", path);
|
||||||
once++;
|
once++;
|
||||||
@@ -119,8 +129,10 @@ static void do_list(const char *path)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heading)
|
if (heading && !json)
|
||||||
print_header("%s ", path);
|
print_header("%s ", path);
|
||||||
|
if (json)
|
||||||
|
printf("%s\"%s\": [", comma ? "," : "", title);
|
||||||
|
|
||||||
width = calc_width(gl.gl_pathv, gl.gl_pathc);
|
width = calc_width(gl.gl_pathv, gl.gl_pathc);
|
||||||
if (width <= 0)
|
if (width <= 0)
|
||||||
@@ -132,16 +144,23 @@ static void do_list(const char *path)
|
|||||||
|
|
||||||
prev = 0;
|
prev = 0;
|
||||||
for (i = 0; i < gl.gl_pathc; i++) {
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
if (i > 0 && !(i % num)) {
|
if (i > 0 && !(i % num) && !json) {
|
||||||
puts("");
|
puts("");
|
||||||
prev = 0;
|
prev = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prev)
|
if (prev)
|
||||||
printf(" ");
|
printf("%s", json ? "," : " ");
|
||||||
|
if (json)
|
||||||
|
printf("\"%s\"", gl.gl_pathv[i]);
|
||||||
|
else
|
||||||
printf("%-*s", width, gl.gl_pathv[i]);
|
printf("%-*s", width, gl.gl_pathv[i]);
|
||||||
prev++;
|
prev++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
fputs("]", stdout);
|
||||||
|
else
|
||||||
puts("\n");
|
puts("\n");
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@@ -151,29 +170,40 @@ done:
|
|||||||
int serv_list(char *arg)
|
int serv_list(char *arg)
|
||||||
{
|
{
|
||||||
char path[256];
|
char path[256];
|
||||||
|
int pre = 0;
|
||||||
|
|
||||||
if (arg && arg[0]) {
|
if (arg && arg[0]) {
|
||||||
paste(path, sizeof(path), FINIT_RCSD, arg);
|
paste(path, sizeof(path), finit_rcsd, arg);
|
||||||
if (fisdir(path)) {
|
if (fisdir(path)) {
|
||||||
do_list(path);
|
if (json)
|
||||||
|
fputs("[", stdout);
|
||||||
|
do_list(path, NULL, 0);
|
||||||
|
if (json)
|
||||||
|
fputs("]\n", stdout);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* fall back to list all */
|
/* fall back to list all */
|
||||||
}
|
}
|
||||||
|
|
||||||
paste(path, sizeof(path), FINIT_RCSD, "available");
|
if (json)
|
||||||
|
fputs("{", stdout);
|
||||||
|
|
||||||
|
paste(path, sizeof(path), finit_rcsd, "available");
|
||||||
if (fisdir(path))
|
if (fisdir(path))
|
||||||
do_list(path);
|
do_list(path, "available", pre++);
|
||||||
|
|
||||||
paste(path, sizeof(path), FINIT_RCSD, "enabled");
|
paste(path, sizeof(path), finit_rcsd, "enabled");
|
||||||
if (fisdir(path))
|
if (fisdir(path))
|
||||||
do_list(path);
|
do_list(path, "enabled", pre++);
|
||||||
|
|
||||||
if (fisdir(FINIT_RCSD))
|
if (fisdir(finit_rcsd))
|
||||||
do_list(FINIT_RCSD);
|
do_list(finit_rcsd, "static", pre++);
|
||||||
|
|
||||||
if (fexist(FINIT_CONF))
|
if (fexist(finit_conf))
|
||||||
do_list(FINIT_CONF);
|
do_list(finit_conf, "finit.conf", pre++);
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
fputs("}\n", stdout);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -197,7 +227,7 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
|||||||
char corr[40];
|
char corr[40];
|
||||||
|
|
||||||
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
|
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
|
||||||
strlcpy(path, FINIT_CONF, len);
|
strlcpy(path, finit_conf, len);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,85 +236,127 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
|||||||
name = corr;
|
name = corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fisdir(FINIT_RCSD))
|
if (!fisdir(finit_rcsd))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
paste(path, len, FINIT_RCSD, "available/");
|
paste(path, len, finit_rcsd, "available/");
|
||||||
if (!fisdir(path)) {
|
if (!fisdir(path)) {
|
||||||
if (creat && mkdir(path, 0755) && errno != EEXIST)
|
if (creat && mkdir(path, 0755) && errno != EEXIST)
|
||||||
return NULL;
|
return NULL;
|
||||||
else
|
else
|
||||||
paste(path, len, FINIT_RCSD, name);
|
paste(path, len, finit_rcsd, name);
|
||||||
} else
|
} else
|
||||||
strlcat(path, name, len);
|
strlcat(path, name, len);
|
||||||
|
|
||||||
/* fall back to static service unless edit/create */
|
/* fall back to static service unless edit/create */
|
||||||
if (!creat && !fexist(path))
|
if (!creat && !fexist(path))
|
||||||
paste(path, len, FINIT_RCSD, name);
|
paste(path, len, finit_rcsd, name);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
int serv_enable(char *arg)
|
int serv_enable(char *arg)
|
||||||
{
|
{
|
||||||
char corr[40];
|
char *serv, *ptr, *src, *dst;
|
||||||
char path[256];
|
size_t len;
|
||||||
int ena;
|
int rc;
|
||||||
|
|
||||||
if (!arg || !arg[0]) {
|
if (!arg || !arg[0]) {
|
||||||
WARNX("missing argument to enable, may be one of:");
|
WARNX("missing argument to enable, may be one of:");
|
||||||
return serv_list("available");
|
return serv_list("available");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strstr(arg, ".conf")) {
|
if (arg[0] == '/') {
|
||||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
WARNX("absolute paths not supported, this command manages %s/available/", finit_rcsd);
|
||||||
arg = corr;
|
return serv_list("available");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chdir(FINIT_RCSD))
|
len = strlen(arg);
|
||||||
ERR(72, "failed cd %s", FINIT_RCSD);
|
serv = alloca(len + 6);
|
||||||
|
if (!serv)
|
||||||
|
ERR(71, "failed allocating stack");
|
||||||
|
|
||||||
|
strlcpy(serv, arg, len + 6);
|
||||||
|
if (len < 6 || strcmp(&serv[len - 5], ".conf"))
|
||||||
|
strlcat(serv, ".conf", len + 6);
|
||||||
|
|
||||||
|
if (chdir(finit_rcsd))
|
||||||
|
ERR(72, "failed cd %s", finit_rcsd);
|
||||||
|
|
||||||
if (icreate && mkdir("enabled", 0755) && EEXIST != errno)
|
if (icreate && mkdir("enabled", 0755) && EEXIST != errno)
|
||||||
ERR(73, "failed creating %s/enabled directory", FINIT_RCSD);
|
ERR(73, "failed creating %s/enabled directory", finit_rcsd);
|
||||||
ena = !chdir("enabled"); /* System *may* have enabled/ dir. */
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg);
|
if (asprintf(&src, "%s/available/%s", finit_rcsd, serv) == -1)
|
||||||
if (!fexist(path))
|
goto fail;
|
||||||
ERRX(72, "cannot find %s", conf(path, sizeof(path), arg, 0));
|
if (asprintf(&dst, "%s/enabled/%s", finit_rcsd, serv) == -1) {
|
||||||
|
free(src);
|
||||||
|
fail:
|
||||||
|
ERR(71, "failed allocating memory");
|
||||||
|
}
|
||||||
|
|
||||||
if (fexist(arg))
|
if (fexist(dst)) {
|
||||||
ERRX(1, "%s already enabled", arg);
|
WARNX("%s already enabled", arg);
|
||||||
|
rc = noerr ? 0 : 1;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
return symlink(path, arg) != 0;
|
/* check for template instantiation */
|
||||||
|
ptr = strchr(src, '@');
|
||||||
|
if (ptr)
|
||||||
|
strcpy(++ptr, ".conf");
|
||||||
|
|
||||||
|
if (!fexist(src)) {
|
||||||
|
WARNX("cannot find %s%s", ptr ? "template ": "", src);
|
||||||
|
rc = 72;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = symlink(src, dst);
|
||||||
|
if (rc)
|
||||||
|
WARN("failed enabling %s", arg);
|
||||||
|
done:
|
||||||
|
free(src);
|
||||||
|
free(dst);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_disable(char *arg, int check)
|
int do_disable(char *arg, int check)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char corr[40];
|
size_t len;
|
||||||
|
char *serv;
|
||||||
|
|
||||||
if (!arg || !arg[0]) {
|
if (!arg || !arg[0]) {
|
||||||
WARNX("missing argument to disable, may be one of:");
|
WARNX("missing argument to disable, may be one of:");
|
||||||
return serv_list("enabled");
|
return serv_list("enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strstr(arg, ".conf")) {
|
if (arg[0] == '/') {
|
||||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
WARNX("absolute paths not supported, this command manages %s/enabled/", finit_rcsd);
|
||||||
arg = corr;
|
return serv_list("available");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chdir(FINIT_RCSD))
|
len = strlen(arg);
|
||||||
ERR(72, "failed cd %s", FINIT_RCSD);
|
serv = alloca(len + 6);
|
||||||
if (chdir("enabled")) /* System *may* have enabled/ dir. */
|
if (!serv)
|
||||||
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
|
ERR(71, "failed allocating stack");
|
||||||
|
|
||||||
if (check && stat(arg, &st))
|
strlcpy(serv, arg, len + 6);
|
||||||
ERRX(6, "%s not (an) enabled (service).", arg);
|
if (len < 6 || strcmp(&serv[len - 5], ".conf"))
|
||||||
|
strlcat(serv, ".conf", len + 6);
|
||||||
|
|
||||||
|
if (chdir(finit_rcsd))
|
||||||
|
ERR(72, "failed cd %s", finit_rcsd);
|
||||||
|
if (chdir("enabled")) /* System *may* have enabled/ dir. */
|
||||||
|
dbg("Failed changing to %s/enabled/: %s", finit_rcsd, strerror(errno));
|
||||||
|
|
||||||
|
if (check && stat(serv, &st))
|
||||||
|
ERRX(noerr ? 0 : 6, "%s not (an) enabled (service).", serv);
|
||||||
|
|
||||||
if (check && (st.st_mode & S_IFMT) == S_IFLNK)
|
if (check && (st.st_mode & S_IFMT) == S_IFLNK)
|
||||||
ERRX(1, "cannot disable %s, not a symlink.", arg);
|
ERRX(noerr ? 0 : 1, "cannot disable %s, not a symlink.", serv);
|
||||||
|
|
||||||
return remove(arg) != 0;
|
return remove(serv) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int serv_disable(char *arg)
|
int serv_disable(char *arg)
|
||||||
@@ -309,7 +381,7 @@ int serv_touch(char *arg)
|
|||||||
if (is_builtin(arg))
|
if (is_builtin(arg))
|
||||||
ERRX(4, "%s is a built-in service.", arg);
|
ERRX(4, "%s is a built-in service.", arg);
|
||||||
|
|
||||||
strlcpy(path, FINIT_CONF, sizeof(path));
|
strlcpy(path, finit_conf, sizeof(path));
|
||||||
fn = path;
|
fn = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,7 +463,7 @@ static int do_edit(char *arg, int creat)
|
|||||||
int serv_edit(char *arg)
|
int serv_edit(char *arg)
|
||||||
{
|
{
|
||||||
if (!arg || !arg[0]) {
|
if (!arg || !arg[0]) {
|
||||||
if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF))
|
if (!yorn("Do you want to edit %s (y/N)? ", finit_conf))
|
||||||
return 0;
|
return 0;
|
||||||
arg = "";
|
arg = "";
|
||||||
}
|
}
|
||||||
@@ -449,7 +521,7 @@ int serv_delete(char *arg)
|
|||||||
if (!fn) {
|
if (!fn) {
|
||||||
if (is_builtin(arg))
|
if (is_builtin(arg))
|
||||||
ERRX(4, "%s is a built-in service.", arg);
|
ERRX(4, "%s is a built-in service.", arg);
|
||||||
ERRX(72, FINIT_RCSD " missing on system.");
|
ERRX(72, "%s missing on system.", finit_rcsd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fexist(fn))
|
if (!fexist(fn))
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/* List and enable/disable service configurations
|
/* List and enable/disable service configurations
|
||||||
*
|
*
|
||||||
* Copyright (c) 2017-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2017-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+664
-153
File diff suppressed because it is too large
Load Diff
+7
-2
@@ -1,7 +1,7 @@
|
|||||||
/* Finit service monitor, task starter and generic API for managing svc_t
|
/* Finit service monitor, task starter and generic API for managing svc_t
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -34,11 +34,15 @@ void service_unregister (svc_t *svc);
|
|||||||
void service_runtask_clean (void);
|
void service_runtask_clean (void);
|
||||||
void service_reload_dynamic (void);
|
void service_reload_dynamic (void);
|
||||||
void service_update_rdeps (void);
|
void service_update_rdeps (void);
|
||||||
|
void service_mark_unavail (void);
|
||||||
|
|
||||||
|
void service_ready_script (svc_t *svc); /* XXX: only for pidfile plugin before notify framework */
|
||||||
|
|
||||||
int service_timeout_after (svc_t *svc, int timeout, void (*cb)(svc_t *svc));
|
int service_timeout_after (svc_t *svc, int timeout, void (*cb)(svc_t *svc));
|
||||||
int service_timeout_cancel (svc_t *svc);
|
int service_timeout_cancel (svc_t *svc);
|
||||||
|
|
||||||
void service_forked (svc_t *svc);
|
void service_forked (svc_t *svc);
|
||||||
|
void service_ready (svc_t *svc);
|
||||||
|
|
||||||
int service_stop (svc_t *svc);
|
int service_stop (svc_t *svc);
|
||||||
int service_step (svc_t *svc);
|
int service_step (svc_t *svc);
|
||||||
@@ -46,8 +50,9 @@ void service_step_all (int types);
|
|||||||
void service_worker (void *unused);
|
void service_worker (void *unused);
|
||||||
|
|
||||||
int service_completed (void);
|
int service_completed (void);
|
||||||
|
void service_notify_reconf (void);
|
||||||
|
|
||||||
void service_init (void);
|
void service_init (uev_ctx_t *ctx);
|
||||||
|
|
||||||
#endif /* FINIT_SERVICE_H_ */
|
#endif /* FINIT_SERVICE_H_ */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Signal management - Be conservative with what finit responds to!
|
/* Signal management - Be conservative with what finit responds to!
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -97,8 +97,6 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
|
||||||
extern svc_t *wdog;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
|
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
|
||||||
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
|
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
|
||||||
@@ -231,12 +229,12 @@ void do_iterate_proc(int (*cb)(int, void *), void *data)
|
|||||||
|
|
||||||
if (fgets(file, sizeof(file), fp)) {
|
if (fgets(file, sizeof(file), fp)) {
|
||||||
if (strstr(file, "gdbserver"))
|
if (strstr(file, "gdbserver"))
|
||||||
_d("Skipping %s ...", file);
|
dbg("Skipping %s ...", file);
|
||||||
else if (file[0] == '@')
|
else if (file[0] == '@')
|
||||||
_d("Skipping %s ...", &file[1]);
|
dbg("Skipping %s ...", &file[1]);
|
||||||
else
|
else
|
||||||
if (cb(pid, data)) {
|
if (cb(pid, data)) {
|
||||||
_d("PID %d is still alive (%s)", pid, file);
|
dbg("PID %d is still alive (%s)", pid, file);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -302,6 +300,7 @@ void do_shutdown(shutop_t op)
|
|||||||
sched_setscheduler(1, SCHED_RR, &sched_param);
|
sched_setscheduler(1, SCHED_RR, &sched_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
halt = op;
|
||||||
if (sdown)
|
if (sdown)
|
||||||
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
|
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
|
||||||
|
|
||||||
@@ -318,14 +317,19 @@ void do_shutdown(shutop_t op)
|
|||||||
do_iterate_proc(kill_cb, &signo);
|
do_iterate_proc(kill_cb, &signo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exit plugins and API gracefully */
|
/* Exit plugins gracefully */
|
||||||
plugin_exit();
|
plugin_exit();
|
||||||
api_exit();
|
|
||||||
|
/* We can no longer act on conditions, activate script runner */
|
||||||
|
cond_exit();
|
||||||
|
|
||||||
/* Reap 'em */
|
/* Reap 'em */
|
||||||
while (waitpid(-1, NULL, WNOHANG) > 0)
|
while (waitpid(-1, NULL, WNOHANG) > 0)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/* All services and (non-critical) processes have stopped. */
|
||||||
|
plugin_script_run(HOOK_SVC_DN);
|
||||||
|
|
||||||
if (in_cont) {
|
if (in_cont) {
|
||||||
if (osheading)
|
if (osheading)
|
||||||
logit(LOG_CONSOLE | LOG_NOTICE, "%s, shutting down container.", osheading);
|
logit(LOG_CONSOLE | LOG_NOTICE, "%s, shutting down container.", osheading);
|
||||||
@@ -351,6 +355,7 @@ void do_shutdown(shutop_t op)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Unmount any tmpfs before unmounting swap ... */
|
/* Unmount any tmpfs before unmounting swap ... */
|
||||||
|
print(0, "Unmounting filesystems ...");
|
||||||
unmount_tmpfs();
|
unmount_tmpfs();
|
||||||
fs_swapoff();
|
fs_swapoff();
|
||||||
|
|
||||||
@@ -371,8 +376,27 @@ void do_shutdown(shutop_t op)
|
|||||||
/* Call mdadm to mark any RAID array(s) as clean before halting. */
|
/* Call mdadm to mark any RAID array(s) as clean before halting. */
|
||||||
mdadm_wait();
|
mdadm_wait();
|
||||||
|
|
||||||
|
/* Last chance to run scripts (all .so plugins have exited already) */
|
||||||
|
print(0, "Calling hook/svc/down scripts ...");
|
||||||
|
plugin_script_run(HOOK_SYS_DN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Optional reboot-delay, needed on systems running ubifs, jffs,
|
||||||
|
* or similar. From fileutils-4.0, sync(8); "On Linux, sync is
|
||||||
|
* only guaranteed to schedule the dirty blocks for writing; it
|
||||||
|
* can actually take a short time before all the blocks are
|
||||||
|
* finally written." -- https://linux.die.net/man/8/sync
|
||||||
|
*/
|
||||||
|
sync();
|
||||||
|
if (syncsec > 0) {
|
||||||
|
print(-1, "Reboot delay, waiting for filesystem sync, %d sec", syncsec);
|
||||||
|
do_sleep(syncsec);
|
||||||
|
print(0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Reboot via watchdog or kernel, or shutdown? */
|
/* Reboot via watchdog or kernel, or shutdown? */
|
||||||
if (op == SHUT_REBOOT) {
|
if (op == SHUT_REBOOT) {
|
||||||
|
print(0, "Rebooting ...");
|
||||||
if (wdog && wdog->pid > 1) {
|
if (wdog && wdog->pid > 1) {
|
||||||
int timeout = 10;
|
int timeout = 10;
|
||||||
|
|
||||||
@@ -382,15 +406,15 @@ void do_shutdown(shutop_t op)
|
|||||||
do_sleep(1);
|
do_sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Rebooting ...");
|
dbg("Rebooting ...");
|
||||||
reboot(RB_AUTOBOOT);
|
reboot(RB_AUTOBOOT);
|
||||||
} else if (op == SHUT_OFF) {
|
} else if (op == SHUT_OFF) {
|
||||||
_d("Powering down ...");
|
print(0, "Powering down ...");
|
||||||
reboot(RB_POWER_OFF);
|
reboot(RB_POWER_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Also fallback if any of the other two fails */
|
/* Also fallback if any of the other two fails */
|
||||||
_d("Halting ...");
|
print(0, "Halting ...");
|
||||||
reboot(RB_HALT_SYSTEM);
|
reboot(RB_HALT_SYSTEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,9 +423,14 @@ void do_shutdown(shutop_t op)
|
|||||||
*/
|
*/
|
||||||
static void sighup_cb(uev_t *w, void *arg, int events)
|
static void sighup_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
dbg("...");
|
||||||
if (UEV_ERROR == events) {
|
if (UEV_ERROR == events) {
|
||||||
_e("Unrecoverable error in signal watcher");
|
errx(1, "Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||||
|
warnx("SIGHUP ignored in runlevel S and 6.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,9 +449,9 @@ static void sighup_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
static void sigint_cb(uev_t *w, void *arg, int events)
|
static void sigint_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
dbg("...");
|
||||||
if (UEV_ERROR == events) {
|
if (UEV_ERROR == events) {
|
||||||
_e("Unrecoverable error in signal watcher");
|
errx(1, "Unrecoverable error in signal watcher");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,9 +465,9 @@ static void sigint_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
static void sigpwr_cb(uev_t *w, void *arg, int events)
|
static void sigpwr_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
dbg("...");
|
||||||
if (UEV_ERROR == events) {
|
if (UEV_ERROR == events) {
|
||||||
_e("Unrecoverable error in signal watcher");
|
errx(1, "Unrecoverable error in signal watcher");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,9 +479,14 @@ static void sigpwr_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
static void sigusr1_cb(uev_t *w, void *arg, int events)
|
static void sigusr1_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
dbg("...");
|
||||||
if (UEV_ERROR == events) {
|
if (UEV_ERROR == events) {
|
||||||
_e("Unrecoverable error in signal watcher");
|
errx(1, "Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||||
|
warnx("SIGUSR1 ignored in runlevel S and 6.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,9 +500,14 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
static void sigusr2_cb(uev_t *w, void *arg, int events)
|
static void sigusr2_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
dbg("...");
|
||||||
if (UEV_ERROR == events) {
|
if (UEV_ERROR == events) {
|
||||||
_e("Unrecoverable error in signal watcher");
|
errx(1, "Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||||
|
warnx("SIGUSR2 ignored in runlevel S and 6.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,9 +520,14 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
static void sigterm_cb(uev_t *w, void *arg, int events)
|
static void sigterm_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
dbg("...");
|
||||||
if (UEV_ERROR == events) {
|
if (UEV_ERROR == events) {
|
||||||
_e("Unrecoverable error in signal watcher");
|
errx(1, "Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||||
|
warnx("SIGTERM ignored in runlevel S and 6.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +544,7 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
if (UEV_ERROR == events) {
|
if (UEV_ERROR == events) {
|
||||||
_e("Unrecoverable error in signal watcher");
|
errx(1, "Unrecoverable error in signal watcher");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +557,7 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Collected child %d", pid);
|
dbg("Collected child PID %d, status: %d", pid, status);
|
||||||
service_monitor(pid, status);
|
service_monitor(pid, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -603,7 +647,7 @@ void sig_setup(uev_ctx_t *ctx)
|
|||||||
{
|
{
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
_d("Setup signals");
|
dbg("Setup signals");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Standard SysV init calls ctrl-alt-delete handler
|
* Standard SysV init calls ctrl-alt-delete handler
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Signal management - Be conservative with what finit responds to!
|
/* Signal management - Be conservative with what finit responds to!
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
#include "schedule.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
@@ -43,12 +44,53 @@ sm_t sm;
|
|||||||
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */
|
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void sm_init(sm_t *sm)
|
/*
|
||||||
|
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||||
|
* allowed to complete their work in [S], or timeout, before we switch
|
||||||
|
* to the configured runlevel and call finalize(), should not take more
|
||||||
|
* than 120 sec.
|
||||||
|
*/
|
||||||
|
static void sm_check_bootstrap(void *work)
|
||||||
{
|
{
|
||||||
sm->state = SM_BOOTSTRAP_STATE;
|
static int cnt = 120 * 10; /* We run with 100ms period */
|
||||||
sm->newlevel = -1;
|
int level = cfglevel;
|
||||||
sm->reload = 0;
|
|
||||||
sm->in_teardown = 0;
|
dbg("Step all services ...");
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
|
if (cnt-- > 0 && !service_completed()) {
|
||||||
|
dbg("Not all bootstrap run/tasks have completed yet ... %d", cnt);
|
||||||
|
schedule_work(work);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cnt > 0)
|
||||||
|
dbg("All run/task have completed, resuming bootstrap.");
|
||||||
|
else
|
||||||
|
dbg("Timeout, resuming bootstrap.");
|
||||||
|
|
||||||
|
dbg("Flushing pending .conf file events ...");
|
||||||
|
conf_flush_events();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start all tasks/services in the configured runlevel, or jump
|
||||||
|
* into the runlevel selected from the command line.
|
||||||
|
*/
|
||||||
|
if (cmdlevel) {
|
||||||
|
dbg("Runlevel %d requested from command line, starting all services ...", cmdlevel);
|
||||||
|
level = cmdlevel;
|
||||||
|
} else
|
||||||
|
dbg("Change to default runlevel(%d), starting all services ...", cfglevel);
|
||||||
|
|
||||||
|
service_runlevel(level);
|
||||||
|
|
||||||
|
/* Clean up bootstrap-only tasks/services that never started */
|
||||||
|
dbg("Clean up all bootstrap-only tasks/services ...");
|
||||||
|
svc_prune_bootstrap();
|
||||||
|
|
||||||
|
/* All services/tasks/etc. in configure runlevel have started */
|
||||||
|
dbg("Running svc up hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_SVC_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *sm_status(sm_state_t state)
|
static char *sm_status(sm_state_t state)
|
||||||
@@ -77,6 +119,13 @@ static char *sm_status(sm_state_t state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char sm_runlevel(int lvl)
|
||||||
|
{
|
||||||
|
if (lvl == INIT_LEVEL)
|
||||||
|
return 'S';
|
||||||
|
return lvl + '0';
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disable login in single user mode and shutdown/reboot
|
* Disable login in single user mode and shutdown/reboot
|
||||||
*
|
*
|
||||||
@@ -86,13 +135,29 @@ static char *sm_status(sm_state_t state)
|
|||||||
*/
|
*/
|
||||||
static void nologin(void)
|
static void nologin(void)
|
||||||
{
|
{
|
||||||
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
|
if (runlevel == 1 || IS_RESERVED_RUNLEVEL(runlevel))
|
||||||
touch(FINIT_NOLOGIN_PATH);
|
touch(FINIT_NOLOGIN_PATH);
|
||||||
|
|
||||||
if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6)
|
if (prevlevel == 1 || IS_RESERVED_RUNLEVEL(prevlevel))
|
||||||
erase(FINIT_NOLOGIN_PATH);
|
erase(FINIT_NOLOGIN_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sm_init(sm_t *sm)
|
||||||
|
{
|
||||||
|
static struct wq work = {
|
||||||
|
.cb = sm_check_bootstrap,
|
||||||
|
.delay = 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
sm->state = SM_BOOTSTRAP_STATE;
|
||||||
|
sm->newlevel = -1;
|
||||||
|
sm->reload = 0;
|
||||||
|
sm->in_teardown = 0;
|
||||||
|
|
||||||
|
dbg("Starting bootstrap finalize timer ...");
|
||||||
|
schedule_work(&work);
|
||||||
|
}
|
||||||
|
|
||||||
void sm_set_runlevel(sm_t *sm, int newlevel)
|
void sm_set_runlevel(sm_t *sm, int newlevel)
|
||||||
{
|
{
|
||||||
sm->newlevel = newlevel;
|
sm->newlevel = newlevel;
|
||||||
@@ -110,23 +175,62 @@ int sm_is_in_teardown(sm_t *sm)
|
|||||||
|
|
||||||
void sm_step(sm_t *sm)
|
void sm_step(sm_t *sm)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
|
||||||
sm_state_t old_state;
|
sm_state_t old_state;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
restart:
|
restart:
|
||||||
old_state = sm->state;
|
old_state = sm->state;
|
||||||
|
|
||||||
_d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d",
|
dbg("state: %s, runlevel: %c, newlevel: %d, teardown: %d, reload: %d",
|
||||||
sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload);
|
sm_status(sm->state), sm_runlevel(runlevel), sm->newlevel, sm->in_teardown, sm->reload);
|
||||||
|
|
||||||
switch (sm->state) {
|
switch (sm->state) {
|
||||||
case SM_BOOTSTRAP_STATE:
|
case SM_BOOTSTRAP_STATE:
|
||||||
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
dbg("Bootstrapping all services in runlevel S from %s", finit_conf);
|
||||||
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE);
|
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE);
|
||||||
|
|
||||||
|
sm->state = SM_BOOTSTRAP_WAIT_STATE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle bootstrap transition to configured runlevel, start TTYs
|
||||||
|
*
|
||||||
|
* This is the final stage of bootstrap. It changes to the default
|
||||||
|
* (configured) runlevel, calls all external start scripts and final
|
||||||
|
* bootstrap hooks before bringing up TTYs.
|
||||||
|
*
|
||||||
|
* We must ensure that all declared `task [S]` and `run [S]` jobs in
|
||||||
|
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||||
|
* finalize the bootstrap process by calling this function.
|
||||||
|
*/
|
||||||
|
case SM_BOOTSTRAP_WAIT_STATE:
|
||||||
|
/* We come here from bootstrap, runlevel change and conf reload */
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
|
/* Allow runparts to start */
|
||||||
|
cond_set("int/bootstrap");
|
||||||
|
|
||||||
|
if (sm->newlevel == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Hooks that should run at the very end */
|
||||||
|
dbg("Calling all system up hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
|
/* Disable progress output at normal runtime */
|
||||||
|
enable_progress(0);
|
||||||
|
|
||||||
|
/* System bootrapped, launch TTYs et al */
|
||||||
|
bootstrap = 0;
|
||||||
|
service_step_all(SVC_TYPE_RESPAWN);
|
||||||
sm->state = SM_RUNNING_STATE;
|
sm->state = SM_RUNNING_STATE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SM_RUNNING_STATE:
|
case SM_RUNNING_STATE:
|
||||||
|
/* We come here from bootstrap, runlevel change and conf reload */
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
/* runlevel changed? */
|
/* runlevel changed? */
|
||||||
if (sm->newlevel >= 0 && sm->newlevel <= 9) {
|
if (sm->newlevel >= 0 && sm->newlevel <= 9) {
|
||||||
if (runlevel == sm->newlevel) {
|
if (runlevel == sm->newlevel) {
|
||||||
@@ -136,6 +240,7 @@ restart:
|
|||||||
sm->state = SM_RUNLEVEL_CHANGE_STATE;
|
sm->state = SM_RUNLEVEL_CHANGE_STATE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reload ? */
|
/* reload ? */
|
||||||
if (sm->reload) {
|
if (sm->reload) {
|
||||||
sm->reload = 0;
|
sm->reload = 0;
|
||||||
@@ -150,15 +255,16 @@ restart:
|
|||||||
|
|
||||||
/* Restore terse mode and run hooks before shutdown */
|
/* Restore terse mode and run hooks before shutdown */
|
||||||
if (runlevel == 0 || runlevel == 6) {
|
if (runlevel == 0 || runlevel == 6) {
|
||||||
|
api_exit();
|
||||||
log_exit();
|
log_exit();
|
||||||
plugin_run_hooks(HOOK_SHUTDOWN);
|
plugin_run_hooks(HOOK_SHUTDOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
|
dbg("Setting new runlevel --> %c <-- previous %c", sm_runlevel(runlevel), sm_runlevel(prevlevel));
|
||||||
if (osheading)
|
if (osheading)
|
||||||
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %d", osheading, runlevel);
|
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %c", osheading, sm_runlevel(runlevel));
|
||||||
else
|
else
|
||||||
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %d", runlevel);
|
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %c", sm_runlevel(runlevel));
|
||||||
runlevel_set(prevlevel, runlevel);
|
runlevel_set(prevlevel, runlevel);
|
||||||
|
|
||||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||||
@@ -173,7 +279,7 @@ restart:
|
|||||||
/* Reset once flag of runtasks */
|
/* Reset once flag of runtasks */
|
||||||
service_runtask_clean();
|
service_runtask_clean();
|
||||||
|
|
||||||
_d("Stopping services not allowed in new runlevel ...");
|
dbg("Stopping services not allowed in new runlevel ...");
|
||||||
sm->in_teardown = 1;
|
sm->in_teardown = 1;
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
@@ -187,32 +293,33 @@ restart:
|
|||||||
*/
|
*/
|
||||||
svc = svc_stop_completed();
|
svc = svc_stop_completed();
|
||||||
if (svc) {
|
if (svc) {
|
||||||
_d("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
|
dbg("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prev runlevel services stopped, call hooks before starting new runlevel ... */
|
/* Prev runlevel services stopped, call hooks before starting new runlevel ... */
|
||||||
_d("All services have been stopped, calling runlevel change hooks ...");
|
dbg("All services have been stopped, calling runlevel change hooks ...");
|
||||||
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
||||||
|
|
||||||
_d("Starting services new to this runlevel ...");
|
dbg("Starting services new to this runlevel ...");
|
||||||
sm->in_teardown = 0;
|
sm->in_teardown = 0;
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
/* Cleanup stale services */
|
/* Cleanup stale services */
|
||||||
svc_clean_dynamic(service_unregister);
|
svc_clean_dynamic(service_unregister);
|
||||||
|
|
||||||
|
/* Compat SysV init */
|
||||||
|
if (prevlevel == INIT_LEVEL && !rescue)
|
||||||
|
run_bg(FINIT_RC_LOCAL, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "I've seen things you people wouldn't believe. Attack ships on fire off
|
* "I've seen things you people wouldn't believe. Attack ships on fire off
|
||||||
* the shoulder of Orion. I watched C-beams glitter in the dark near the
|
* the shoulder of Orion. I watched C-beams glitter in the dark near the
|
||||||
* Tannhäuser Gate. All those .. moments .. will be lost in time, like
|
* Tannhäuser Gate. All those .. moments .. will be lost in time, like
|
||||||
* tears ... in ... rain."
|
* tears ... in ... rain."
|
||||||
*/
|
*/
|
||||||
if (runlevel == 0 || runlevel == 6) {
|
if (runlevel == 0 || runlevel == 6)
|
||||||
do_shutdown(halt);
|
do_shutdown(halt);
|
||||||
sm->state = SM_RUNNING_STATE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sm->state = SM_RUNNING_STATE;
|
sm->state = SM_RUNNING_STATE;
|
||||||
break;
|
break;
|
||||||
@@ -225,7 +332,7 @@ restart:
|
|||||||
* Then, mark all affected service conditions as in-flux and
|
* Then, mark all affected service conditions as in-flux and
|
||||||
* let all affected services move to WAITING/HALTED
|
* let all affected services move to WAITING/HALTED
|
||||||
*/
|
*/
|
||||||
_d("Stopping services not allowed after reconf ...");
|
dbg("Stopping services not allowed after reconf ...");
|
||||||
sm->in_teardown = 1;
|
sm->in_teardown = 1;
|
||||||
cond_reload();
|
cond_reload();
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
@@ -240,24 +347,25 @@ restart:
|
|||||||
*/
|
*/
|
||||||
svc = svc_stop_completed();
|
svc = svc_stop_completed();
|
||||||
if (svc) {
|
if (svc) {
|
||||||
_d("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
|
dbg("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sm->in_teardown = 0;
|
sm->in_teardown = 0;
|
||||||
|
|
||||||
_d("Starting services after reconf ...");
|
dbg("Starting services after reconf ...");
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
/* Cleanup stale services */
|
/* Cleanup stale services */
|
||||||
svc_clean_dynamic(service_unregister);
|
svc_clean_dynamic(service_unregister);
|
||||||
|
|
||||||
_d("Calling reconf hooks ...");
|
dbg("Calling reconf hooks ...");
|
||||||
plugin_run_hooks(HOOK_SVC_RECONF);
|
plugin_run_hooks(HOOK_SVC_RECONF);
|
||||||
|
|
||||||
service_step_all(SVC_TYPE_ANY);
|
dbg("Update configuration generation of unmodified non-native services ...");
|
||||||
_d("Reconfiguration done");
|
service_notify_reconf();
|
||||||
|
|
||||||
|
dbg("Reconfiguration done");
|
||||||
sm->state = SM_RUNNING_STATE;
|
sm->state = SM_RUNNING_STATE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
|
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
|
||||||
|
SM_BOOTSTRAP_WAIT_STATE, /* Waiting for bootstrap to complete */
|
||||||
SM_RUNNING_STATE, /* Normal state, services running */
|
SM_RUNNING_STATE, /* Normal state, services running */
|
||||||
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
|
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
|
||||||
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */
|
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
/* Reset terminal line settings (stty sane)
|
/* Reset terminal line settings (stty sane)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -132,7 +132,7 @@ void stty(int fd, speed_t speed)
|
|||||||
dprint(fd, "\033[?25h", 6);
|
dprint(fd, "\033[?25h", 6);
|
||||||
|
|
||||||
/* Enable line wrap, if disabled previously */
|
/* Enable line wrap, if disabled previously */
|
||||||
dprint(fd, "\033[7h", 4);
|
dprint(fd, "\033[?7h", 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+10
-4
@@ -1,6 +1,6 @@
|
|||||||
/* poor man's sulogin, may be statically linked
|
/* poor man's sulogin, may be statically linked
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include <crypt.h>
|
#include <crypt.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
@@ -32,6 +34,10 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#ifndef SULOGIN_USER
|
||||||
|
#define SULOGIN_USER "root"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* getpwnam() cannot be used when statically linked */
|
/* getpwnam() cannot be used when statically linked */
|
||||||
static int get_passwd(struct passwd *pw)
|
static int get_passwd(struct passwd *pw)
|
||||||
{
|
{
|
||||||
@@ -44,13 +50,13 @@ static int get_passwd(struct passwd *pw)
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
while ((tmp = fgets(buf, sizeof(buf), fp))) {
|
while ((tmp = fgets(buf, sizeof(buf), fp))) {
|
||||||
if (!strstr(buf, ":0:0:"))
|
|
||||||
continue; /* not uid 0 */
|
|
||||||
|
|
||||||
ptr = strsep(&tmp, ":");
|
ptr = strsep(&tmp, ":");
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
pw->pw_name = strdup(ptr);
|
pw->pw_name = strdup(ptr);
|
||||||
|
if (!pw->pw_name || strcmp(pw->pw_name, SULOGIN_USER))
|
||||||
|
continue;
|
||||||
|
|
||||||
ptr = strsep(&tmp, ":");
|
ptr = strsep(&tmp, ":");
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Low-level service primitives and generic API for managing svc_t structures
|
/* Low-level service primitives and generic API for managing svc_t structures
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -70,12 +70,12 @@ static void maybe_clear_cond(svc_t *svc)
|
|||||||
if (!string_compare(cond, c))
|
if (!string_compare(cond, c))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("Not clearing cond %s from gc svc %s, provided by new active service %s",
|
dbg("Not clearing cond %s from gc svc %s, provided by new active service %s",
|
||||||
cond, ident, svc_ident(s, NULL, 0));
|
cond, ident, svc_ident(s, NULL, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Cleaning out %s, clearing any conditions ...", svc->name);
|
dbg("Cleaning out %s, clearing any conditions ...", svc->name);
|
||||||
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +372,33 @@ svc_t *svc_find(char *name, char *id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_find_by_pid - Find an service object by its PID
|
* svc_find_by_str - Find a service object by name:id
|
||||||
|
* @str: name[:id]
|
||||||
|
*
|
||||||
|
* Like svc_find() but takes 'name[:id]' argument
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
|
*/
|
||||||
|
svc_t *svc_find_by_str(const char *str)
|
||||||
|
{
|
||||||
|
char *name, *id;
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
name = strdupa(str);
|
||||||
|
id = strchr(name, ':');
|
||||||
|
if (id)
|
||||||
|
*id++ = 0;
|
||||||
|
else
|
||||||
|
id = "";
|
||||||
|
|
||||||
|
return svc_find(name, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_find_by_pid - Find a service object by its PID
|
||||||
* @pid: Process ID to match
|
* @pid: Process ID to match
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
@@ -391,7 +417,29 @@ svc_t *svc_find_by_pid(pid_t pid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_find_by_jobid - Find an service object by its JOB:ID
|
* svc_find_by_cond - Find a service object by its pid/foo condition
|
||||||
|
* @cond: Full "pid/foo" condition
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
|
*/
|
||||||
|
svc_t *svc_find_by_cond(const char *cond)
|
||||||
|
{
|
||||||
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
|
char buf[MAX_COND_LEN];
|
||||||
|
|
||||||
|
mkcond(svc, buf, sizeof(buf));
|
||||||
|
if (string_compare(buf, cond))
|
||||||
|
return svc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_find_by_jobid - Find a service object by its JOB:ID
|
||||||
* @job: Job n:o
|
* @job: Job n:o
|
||||||
* @id: Optional instance id
|
* @id: Optional instance id
|
||||||
*
|
*
|
||||||
@@ -478,6 +526,14 @@ svc_t *svc_find_by_pidfile(char *fn)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void svc_mark(svc_t *svc)
|
||||||
|
{
|
||||||
|
if (!svc || svc->protect)
|
||||||
|
return;
|
||||||
|
|
||||||
|
*((int *)&svc->removed) = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
||||||
*
|
*
|
||||||
@@ -492,12 +548,8 @@ void svc_mark_dynamic(void)
|
|||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
|
||||||
if (svc->protect)
|
svc_mark(svc);
|
||||||
continue;
|
|
||||||
|
|
||||||
*((int *)&svc->removed) = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void svc_mark_dirty(svc_t *svc)
|
void svc_mark_dirty(svc_t *svc)
|
||||||
@@ -542,7 +594,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
|
|||||||
*/
|
*/
|
||||||
int svc_clean_bootstrap(svc_t *svc)
|
int svc_clean_bootstrap(svc_t *svc)
|
||||||
{
|
{
|
||||||
if (!ISOTHER(svc->runlevels, 0)) {
|
if (!ISOTHER(svc->runlevels, INIT_LEVEL)) {
|
||||||
svc->pid = 0;
|
svc->pid = 0;
|
||||||
svc_del(svc);
|
svc_del(svc);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -597,6 +649,103 @@ int svc_enabled(svc_t *svc)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_conflicts - Check if svc conflicts with any other running svc
|
||||||
|
* @svc:
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* %TRUE(1) or %FALSE(0)
|
||||||
|
*/
|
||||||
|
int svc_conflicts(svc_t *svc)
|
||||||
|
{
|
||||||
|
char *ptr, *nm;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (!svc->conflict[0])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ptr = strdup(svc->conflict);
|
||||||
|
if (!ptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nm = strtok(ptr, ",");
|
||||||
|
while (nm) {
|
||||||
|
svc_t *s;
|
||||||
|
|
||||||
|
s = svc_find_by_str(nm);
|
||||||
|
if (s) {
|
||||||
|
if (s->state >= SVC_STOPPING_STATE)
|
||||||
|
rc = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm = strtok(NULL, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_ifthen - Check if: statement in declaration
|
||||||
|
* @ident: svc_t identififcation (name:id)
|
||||||
|
* @stmt: if: statement from .conf file
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* %TRUE(1) yes, use this svc_t, %FALSE(0) prune
|
||||||
|
*/
|
||||||
|
int svc_ifthen(const char *ident, char *stmt)
|
||||||
|
{
|
||||||
|
int not = 0;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
if (!stmt || !stmt[0])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (stmt[0] == '<') {
|
||||||
|
enum cond_state cond;
|
||||||
|
char *ptr = ++stmt;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (stmt[0] == '!') {
|
||||||
|
stmt++;
|
||||||
|
not++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ptr[i] != '>' && ptr[i] != 0)
|
||||||
|
i++;
|
||||||
|
ptr[i] = 0;
|
||||||
|
|
||||||
|
cond = cond_get(stmt);
|
||||||
|
if (not && cond == COND_ON) {
|
||||||
|
logit(LOG_NOTICE, "skipping %s, cond %s is on.", ident, stmt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!not && cond == COND_OFF) {
|
||||||
|
logit(LOG_NOTICE, "skipping %s, cond %s is off.", ident, stmt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stmt[0] == '!') {
|
||||||
|
stmt++;
|
||||||
|
not++;
|
||||||
|
}
|
||||||
|
|
||||||
|
svc = svc_find_by_str(stmt);
|
||||||
|
if (not && svc) {
|
||||||
|
logit(LOG_NOTICE, "skipping %s, %s already loaded.", ident, svc_ident(svc, NULL, 0));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!not && !svc) {
|
||||||
|
logit(LOG_NOTICE, "skipping %s, %s not available.", ident, stmt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* break up "job:id job:id job:id ..." into several "job:id" tokens */
|
/* break up "job:id job:id job:id ..." into several "job:id" tokens */
|
||||||
static char *tokstr(char *str, size_t len)
|
static char *tokstr(char *str, size_t len)
|
||||||
{
|
{
|
||||||
@@ -635,17 +784,17 @@ int svc_parse_jobstr(char *str, size_t len, void *user_data, int (*found)(svc_t
|
|||||||
char *input, *token;
|
char *input, *token;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
_d("Got str:'%s'", str);
|
dbg("Got str:'%s'", str);
|
||||||
input = tokstr(str, len);
|
input = tokstr(str, len);
|
||||||
while (input) {
|
while (input) {
|
||||||
char *id = NULL;
|
char *id = NULL;
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
_d("Got token:'%s'", input);
|
dbg("Got token:'%s'", input);
|
||||||
token = sanitize(input, strlen(input) + 1);
|
token = sanitize(input, strlen(input) + 1);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
_d("Sanitation of token:'%s' failed", input);
|
dbg("Sanitation of token:'%s' failed", input);
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
ptr = strchr(token, ':');
|
ptr = strchr(token, ':');
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Low-level service primitives and generic API for managing svc_t structures
|
/* Low-level service primitives and generic API for managing svc_t structures
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -61,8 +61,8 @@ typedef enum {
|
|||||||
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
||||||
SVC_CLEANUP_STATE, /* Running post: script */
|
SVC_CLEANUP_STATE, /* Running post: script */
|
||||||
SVC_SETUP_STATE, /* Running pre: script */
|
SVC_SETUP_STATE, /* Running pre: script */
|
||||||
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */
|
||||||
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
SVC_WAITING_STATE, /* Enabled but condition not satisfied */
|
||||||
SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */
|
SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */
|
||||||
SVC_RUNNING_STATE, /* Process running */
|
SVC_RUNNING_STATE, /* Process running */
|
||||||
} svc_state_t;
|
} svc_state_t;
|
||||||
@@ -74,15 +74,18 @@ typedef enum {
|
|||||||
SVC_BLOCK_USER,
|
SVC_BLOCK_USER,
|
||||||
SVC_BLOCK_BUSY,
|
SVC_BLOCK_BUSY,
|
||||||
SVC_BLOCK_RESTARTING,
|
SVC_BLOCK_RESTARTING,
|
||||||
|
SVC_BLOCK_CONFLICT,
|
||||||
} svc_block_t;
|
} svc_block_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SVC_ONCRASH_IGNORE = 0,
|
SVC_ONCRASH_IGNORE = 0,
|
||||||
SVC_ONCRASH_REBOOT = 1,
|
SVC_ONCRASH_REBOOT,
|
||||||
|
SVC_ONCRASH_SCRIPT,
|
||||||
} svc_oncrash_action_t;
|
} svc_oncrash_action_t;
|
||||||
|
|
||||||
#define MAX_ID_LEN 16
|
#define MAX_ID_LEN 16
|
||||||
#define MAX_ARG_LEN 64
|
#define MAX_ARG_LEN 64
|
||||||
|
#define MAX_CMD_LEN 256
|
||||||
#define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1)
|
#define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1)
|
||||||
#define MAX_STR_LEN 64
|
#define MAX_STR_LEN 64
|
||||||
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
||||||
@@ -116,7 +119,7 @@ typedef struct svc {
|
|||||||
int sighalt; /* Signal to stop process, default: SIGTERM */
|
int sighalt; /* Signal to stop process, default: SIGTERM */
|
||||||
int killdelay; /* Delay in msec before sending SIGKILL */
|
int killdelay; /* Delay in msec before sending SIGKILL */
|
||||||
pid_t oldpid, pid;
|
pid_t oldpid, pid;
|
||||||
char pidfile[256];
|
char pidfile[MAX_CMD_LEN];
|
||||||
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||||
int started; /* Set for run/task/sysv to track if started */
|
int started; /* Set for run/task/sysv to track if started */
|
||||||
int status; /* From waitpid() when process is collected */
|
int status; /* From waitpid() when process is collected */
|
||||||
@@ -124,6 +127,7 @@ typedef struct svc {
|
|||||||
svc_type_t type; /* Service, run, task, ... */
|
svc_type_t type; /* Service, run, task, ... */
|
||||||
char protect; /* Services like dbus-daemon & udev by Finit */
|
char protect; /* Services like dbus-daemon & udev by Finit */
|
||||||
char manual; /* run/task that require `initctl start foo` */
|
char manual; /* run/task that require `initctl start foo` */
|
||||||
|
char nowarn; /* Skip or log warning if cmd missing or conflicts */
|
||||||
const int dirty; /* 0: unmodified, 1: modified */
|
const int dirty; /* 0: unmodified, 1: modified */
|
||||||
const int removed;
|
const int removed;
|
||||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||||
@@ -137,12 +141,14 @@ typedef struct svc {
|
|||||||
int job; /* For intenal use only, canonical ref is NAME:ID */
|
int job; /* For intenal use only, canonical ref is NAME:ID */
|
||||||
char name[MAX_ARG_LEN];
|
char name[MAX_ARG_LEN];
|
||||||
char id[MAX_ID_LEN]; /* :ID */
|
char id[MAX_ID_LEN]; /* :ID */
|
||||||
|
char ifstmt[MAX_IDENT_LEN];
|
||||||
|
|
||||||
/* Counters */
|
/* Counters */
|
||||||
char once; /* run/task, (at least) once per runlevel */
|
char once; /* run/task, (at least) once per runlevel */
|
||||||
unsigned int restart_tot; /* Total restarts ever, summarized, including `initctl restart` */
|
unsigned int restart_tot; /* Total restarts ever, summarized, including `initctl restart` */
|
||||||
int restart_max; /* Maximum number of restarts allowed */
|
int restart_max; /* Maximum number of restarts allowed */
|
||||||
int restart_tmo; /* Time required for the service to start. */
|
int restart_saved; /* INTERNAL, saved copy of .conf value */
|
||||||
|
int restart_tmo; /* Time before restarting a crashing service */
|
||||||
unsigned char oncrash_action; /* Action to perform in crashed state. */
|
unsigned char oncrash_action; /* Action to perform in crashed state. */
|
||||||
char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */
|
char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */
|
||||||
const char restart_cnt; /* Incremented for each restart by service monitor. */
|
const char restart_cnt; /* Incremented for each restart by service monitor. */
|
||||||
@@ -176,13 +182,15 @@ typedef struct svc {
|
|||||||
char group[MAX_USER_LEN];
|
char group[MAX_USER_LEN];
|
||||||
|
|
||||||
/* Command, arguments and service description */
|
/* Command, arguments and service description */
|
||||||
char cmd[MAX_ARG_LEN];
|
char cmd[MAX_CMD_LEN];
|
||||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
char args[MAX_NUM_SVC_ARGS][MAX_CMD_LEN];
|
||||||
int args_dirty;
|
int args_dirty;
|
||||||
|
char conflict[MAX_ARG_LEN];
|
||||||
char desc[MAX_STR_LEN];
|
char desc[MAX_STR_LEN];
|
||||||
char env[MAX_ARG_LEN];
|
char env[MAX_CMD_LEN];
|
||||||
char pre_script[MAX_ARG_LEN];
|
char pre_script[MAX_CMD_LEN];
|
||||||
char post_script[MAX_ARG_LEN];
|
char post_script[MAX_CMD_LEN];
|
||||||
|
char ready_script[MAX_CMD_LEN];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to forcefully kill services that won't shutdown on
|
* Used to forcefully kill services that won't shutdown on
|
||||||
@@ -191,6 +199,12 @@ typedef struct svc {
|
|||||||
uev_t timer;
|
uev_t timer;
|
||||||
void (*timer_cb)(struct svc *svc);
|
void (*timer_cb)(struct svc *svc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Readiness notification socket: systemd, s6
|
||||||
|
*/
|
||||||
|
int notify; /* 0: none, 1: systemd, 2: s6 */
|
||||||
|
uev_t notify_watcher; /* i/o watcher */
|
||||||
|
|
||||||
/* time at svc_del(), used by gc timer */
|
/* time at svc_del(), used by gc timer */
|
||||||
struct timespec gc;
|
struct timespec gc;
|
||||||
} svc_t;
|
} svc_t;
|
||||||
@@ -200,7 +214,9 @@ int svc_del (svc_t *svc);
|
|||||||
void svc_validate (svc_t *svc);
|
void svc_validate (svc_t *svc);
|
||||||
|
|
||||||
svc_t *svc_find (char *name, char *id);
|
svc_t *svc_find (char *name, char *id);
|
||||||
|
svc_t *svc_find_by_str (const char *str);
|
||||||
svc_t *svc_find_by_pid (pid_t pid);
|
svc_t *svc_find_by_pid (pid_t pid);
|
||||||
|
svc_t *svc_find_by_cond (const char *cond);
|
||||||
svc_t *svc_find_by_jobid (int job, char *id);
|
svc_t *svc_find_by_jobid (int job, char *id);
|
||||||
svc_t *svc_find_by_tty (char *dev);
|
svc_t *svc_find_by_tty (char *dev);
|
||||||
svc_t *svc_find_by_pidfile (char *fn);
|
svc_t *svc_find_by_pidfile (char *fn);
|
||||||
@@ -214,6 +230,7 @@ void svc_foreach_type (int types, int (*cb)(svc_t *));
|
|||||||
|
|
||||||
svc_t *svc_stop_completed (void);
|
svc_t *svc_stop_completed (void);
|
||||||
|
|
||||||
|
void svc_mark (svc_t *svc);
|
||||||
void svc_mark_dynamic (void);
|
void svc_mark_dynamic (void);
|
||||||
void svc_mark_dirty (svc_t *svc);
|
void svc_mark_dirty (svc_t *svc);
|
||||||
void svc_mark_clean (svc_t *svc);
|
void svc_mark_clean (svc_t *svc);
|
||||||
@@ -223,6 +240,8 @@ void svc_prune_bootstrap (void);
|
|||||||
|
|
||||||
void svc_enable (svc_t *svc);
|
void svc_enable (svc_t *svc);
|
||||||
int svc_enabled (svc_t *svc);
|
int svc_enabled (svc_t *svc);
|
||||||
|
int svc_conflicts (svc_t *svc);
|
||||||
|
int svc_ifthen (const char *ident, char *ifstm);
|
||||||
|
|
||||||
int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *));
|
int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *));
|
||||||
|
|
||||||
@@ -238,6 +257,7 @@ static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sigh
|
|||||||
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
||||||
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
|
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
|
||||||
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
|
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
|
||||||
|
static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];}
|
||||||
|
|
||||||
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
||||||
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||||
@@ -251,6 +271,9 @@ static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty
|
|||||||
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||||
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||||
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
|
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
|
||||||
|
static inline int svc_is_crashing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_CRASHING; }
|
||||||
|
static inline int svc_is_restart (svc_t *svc) { return svc && svc->block == SVC_BLOCK_RESTARTING; }
|
||||||
|
static inline int svc_is_conflict (svc_t *svc) { return svc && svc->block == SVC_BLOCK_CONFLICT; }
|
||||||
|
|
||||||
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
||||||
#define svc_start(svc) svc_unblock(svc)
|
#define svc_start(svc) svc_unblock(svc)
|
||||||
@@ -259,6 +282,7 @@ static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOC
|
|||||||
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
|
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
|
||||||
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
|
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
|
||||||
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
|
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
|
||||||
|
static inline void svc_conflict (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CONFLICT; }
|
||||||
|
|
||||||
/* Has condition in configuration and cond is allowed? */
|
/* Has condition in configuration and cond is allowed? */
|
||||||
static inline int svc_has_cond(svc_t *svc)
|
static inline int svc_has_cond(svc_t *svc)
|
||||||
@@ -331,6 +355,9 @@ static inline char *svc_status(svc_t *svc)
|
|||||||
|
|
||||||
case SVC_BLOCK_RESTARTING:
|
case SVC_BLOCK_RESTARTING:
|
||||||
return "restart";
|
return "restart";
|
||||||
|
case SVC_BLOCK_CONFLICT:
|
||||||
|
return "conflict";
|
||||||
|
|
||||||
}
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
|
|
||||||
@@ -350,17 +377,20 @@ static inline char *svc_status(svc_t *svc)
|
|||||||
return "stopping";
|
return "stopping";
|
||||||
}
|
}
|
||||||
|
|
||||||
case SVC_WAITING_STATE:
|
|
||||||
return "waiting";
|
|
||||||
|
|
||||||
case SVC_CLEANUP_STATE:
|
case SVC_CLEANUP_STATE:
|
||||||
return "cleanup";
|
return "cleanup";
|
||||||
|
|
||||||
case SVC_SETUP_STATE:
|
case SVC_SETUP_STATE:
|
||||||
return "setup";
|
return "setup";
|
||||||
|
|
||||||
case SVC_READY_STATE:
|
case SVC_PAUSED_STATE:
|
||||||
return "ready";
|
return "paused";
|
||||||
|
|
||||||
|
case SVC_WAITING_STATE:
|
||||||
|
return "waiting";
|
||||||
|
|
||||||
|
case SVC_STARTING_STATE:
|
||||||
|
return "starting";
|
||||||
|
|
||||||
case SVC_RUNNING_STATE:
|
case SVC_RUNNING_STATE:
|
||||||
return "running";
|
return "running";
|
||||||
|
|||||||
+530
@@ -0,0 +1,530 @@
|
|||||||
|
/* Limited tmpfiles.d implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Joachim Wiberg <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "tmpfiles.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static int glob_do(const char *path, int (*cb)(const char *))
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
glob_t gl;
|
||||||
|
|
||||||
|
rc = glob(path, GLOB_NOESCAPE, NULL, &gl);
|
||||||
|
if (rc) {
|
||||||
|
if (rc == GLOB_NOMATCH) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < gl.gl_pathc; i++)
|
||||||
|
rc += cb(gl.gl_pathv[i]);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_mm(char *arg, int *major, int *minor)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (!arg) {
|
||||||
|
inval:
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = strchr(arg, ':');
|
||||||
|
if (!ptr)
|
||||||
|
goto inval;
|
||||||
|
|
||||||
|
*ptr++ = 0;
|
||||||
|
*major = atoi(arg);
|
||||||
|
*minor = atoi(ptr);
|
||||||
|
|
||||||
|
if (!*major || !*minor)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||||
|
{
|
||||||
|
if (ftw->level == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (remove(fpath))
|
||||||
|
warn("Failed removing condition %s", fpath);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmrf(const char *path)
|
||||||
|
{
|
||||||
|
if (!fisdir(path))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nftw(path, do_delete, 20, FTW_DEPTH);
|
||||||
|
if (remove(path) && errno != ENOENT)
|
||||||
|
warn("Failed removing path %s", path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mkparent(char *path, mode_t mode)
|
||||||
|
{
|
||||||
|
mkpath(dirname(strdupa(path)), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *write_hex(FILE *fp, char *p)
|
||||||
|
{
|
||||||
|
unsigned char val;
|
||||||
|
char num[4], *ptr;
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
|
for (i = 0; p[i] && len < 2; i++) {
|
||||||
|
char c = p[i];
|
||||||
|
|
||||||
|
if ((c >= '0' && c <= '7') ||
|
||||||
|
(c >= 'a' && c <= 'f') ||
|
||||||
|
(c >= 'A' && c <= 'F'))
|
||||||
|
num[len++] = c;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
num[len] = 0;
|
||||||
|
if (len == 0) {
|
||||||
|
fputs("x", fp);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
val = strtoul(num, &ptr, 16);
|
||||||
|
if (errno || ptr == num)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
fputc(val & 0xff, fp);
|
||||||
|
end:
|
||||||
|
return &p[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *write_num(FILE *fp, char *p)
|
||||||
|
{
|
||||||
|
unsigned char val;
|
||||||
|
char num[4], *ptr;
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
|
for (i = 0; p[i] && len < 3; i++) {
|
||||||
|
char c = p[i];
|
||||||
|
|
||||||
|
if (c >= '0' && c <= '7')
|
||||||
|
num[len++] = c;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
num[len] = 0;
|
||||||
|
if (len == 0)
|
||||||
|
return p;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
val = strtoul(num, &ptr, 8);
|
||||||
|
if (errno || ptr == num)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
fputc(val & 0xff, fp);
|
||||||
|
end:
|
||||||
|
return &p[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_arg(FILE *fp, char *arg)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (*arg && (p = strchr(arg, '\\'))) {
|
||||||
|
fwrite(arg, sizeof(char), p - arg, fp);
|
||||||
|
|
||||||
|
*p++ = 0;
|
||||||
|
switch (*p) {
|
||||||
|
case '\\':
|
||||||
|
fputc('\\', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
fputc('\'', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
fputc('"', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
fputc('\a', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
fputc('\b', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
fputc('\e', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
fputc('\n', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
fputc('\t', fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
arg = write_hex(fp, &p[1]);
|
||||||
|
break;
|
||||||
|
case '0' ... '7':
|
||||||
|
arg = write_num(fp, p);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fputc(*p, fp);
|
||||||
|
arg = p + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fputs(arg, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The configuration format is one line per path, containing type, path,
|
||||||
|
* mode, ownership, age, and argument fields. The lines are separated by
|
||||||
|
* newlines, the fields by whitespace:
|
||||||
|
*
|
||||||
|
* #Type Path Mode User Group Age Argument…
|
||||||
|
* d /run/user 0755 root root 10d -
|
||||||
|
* L /tmp/foobar - - - - /dev/null
|
||||||
|
*
|
||||||
|
* https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
|
||||||
|
*/
|
||||||
|
static void tmpfiles(char *line)
|
||||||
|
{
|
||||||
|
char *type, *path, *token, *user, *group, *age, *arg;
|
||||||
|
char *dst = NULL, *opts = "";
|
||||||
|
struct stat st, ast;
|
||||||
|
int major, minor;
|
||||||
|
int strc, rc = 0;
|
||||||
|
mode_t mode = 0;
|
||||||
|
FILE *fp = NULL;
|
||||||
|
char buf[1024];
|
||||||
|
glob_t gl;
|
||||||
|
|
||||||
|
type = strtok(line, "\t ");
|
||||||
|
if (!type)
|
||||||
|
return;
|
||||||
|
|
||||||
|
path = strtok(NULL, "\t ");
|
||||||
|
if (!path)
|
||||||
|
return;
|
||||||
|
if (strchr(path, '%')) {
|
||||||
|
errx(1, "Path name specifiers unsupported, skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, "\t ");
|
||||||
|
if (token) {
|
||||||
|
errno = 0;
|
||||||
|
mode = strtoul(token, &arg, 8);
|
||||||
|
if (errno || arg == token)
|
||||||
|
mode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = strtok(NULL, "\t ");
|
||||||
|
if (!user || !strcmp(user, "-"))
|
||||||
|
user = "root";
|
||||||
|
group = strtok(NULL, "\t ");
|
||||||
|
if (!group || !strcmp(group, "-"))
|
||||||
|
group = "root";
|
||||||
|
|
||||||
|
age = strtok(NULL, "\t ");
|
||||||
|
(void)age; /* unused atm. */
|
||||||
|
arg = strtok(NULL, "\n");
|
||||||
|
|
||||||
|
strc = stat(path, &st);
|
||||||
|
|
||||||
|
switch (type[0]) {
|
||||||
|
case 'b':
|
||||||
|
rc = parse_mm(arg, &major, &minor);
|
||||||
|
if (rc)
|
||||||
|
break;
|
||||||
|
if (!strc) {
|
||||||
|
if (type[1] != '+')
|
||||||
|
break;
|
||||||
|
erase(path);
|
||||||
|
}
|
||||||
|
mkparent(path, 0755);
|
||||||
|
rc = blkdev(path, mode ?: 0644, major, minor);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
rc = parse_mm(arg, &major, &minor);
|
||||||
|
if (rc)
|
||||||
|
break;
|
||||||
|
if (!strc) {
|
||||||
|
if (type[1] != '+')
|
||||||
|
break;
|
||||||
|
erase(path);
|
||||||
|
}
|
||||||
|
mkparent(path, 0755);
|
||||||
|
rc = chardev(path, mode ?: 0644, major, minor);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
if (!arg) {
|
||||||
|
paste(buf, sizeof(buf), "/usr/share/factory", path);
|
||||||
|
arg = buf;
|
||||||
|
}
|
||||||
|
if (!strc) {
|
||||||
|
struct dirent **namelist;
|
||||||
|
int num;
|
||||||
|
|
||||||
|
if (!S_ISDIR(st.st_mode))
|
||||||
|
break;
|
||||||
|
num = scandir(path, &namelist, NULL, NULL);
|
||||||
|
free(namelist);
|
||||||
|
if (num >= 3)
|
||||||
|
break; /* not empty */
|
||||||
|
}
|
||||||
|
if (fisdir(arg) && !fisslashdir(arg)) {
|
||||||
|
size_t len = strlen(arg) + 2;
|
||||||
|
|
||||||
|
dst = malloc(len);
|
||||||
|
if (!dst)
|
||||||
|
break;
|
||||||
|
snprintf(dst, len, "%s/", arg);
|
||||||
|
arg = dst;
|
||||||
|
}
|
||||||
|
mkparent(path, 0755);
|
||||||
|
rc = rsync(arg, path, LITE_FOPT_KEEP_MTIME, NULL);
|
||||||
|
if (rc && errno == ENOENT)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
mkparent(path, 0755);
|
||||||
|
rc = mksubsys(path, mode ?: 0755, user, group);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < gl.gl_pathc; i++)
|
||||||
|
rc += mksubsys(gl.gl_pathv[i], mode ?: 0755, user, group);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
mkparent(path, 0755);
|
||||||
|
if (type[1] == '+' || type[0] == 'F') {
|
||||||
|
/* f+/F will create or truncate the file */
|
||||||
|
if (!arg) {
|
||||||
|
rc = create(path, mode ?: 0644, user, group);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fp = fopen(path, "w+");
|
||||||
|
} else {
|
||||||
|
/* f will create the file if it doesn't exist */
|
||||||
|
if (strc)
|
||||||
|
fp = fopen(path, "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fp) {
|
||||||
|
write_arg(fp, arg);
|
||||||
|
rc = fclose(fp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l': /* Finit extension, like 'L' but only if target exists */
|
||||||
|
if (!arg) {
|
||||||
|
paste(buf, sizeof(buf), "/usr/share/factory", path);
|
||||||
|
if (stat(buf, &ast))
|
||||||
|
break;
|
||||||
|
} else if (arg[0] != '/') {
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
tmp = dirname(strdupa(path));
|
||||||
|
paste(buf, sizeof(buf), tmp, arg);
|
||||||
|
dst = realpath(buf, NULL);
|
||||||
|
if (!dst)
|
||||||
|
break;
|
||||||
|
if (stat(dst, &ast))
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (stat(arg, &ast))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fallthrough */
|
||||||
|
case 'L':
|
||||||
|
if (!strc) {
|
||||||
|
if (type[1] != '+')
|
||||||
|
break;
|
||||||
|
rmrf(path);
|
||||||
|
}
|
||||||
|
mkparent(path, 0755);
|
||||||
|
if (!arg) {
|
||||||
|
paste(buf, sizeof(buf), "/usr/share/factory", path);
|
||||||
|
arg = buf;
|
||||||
|
}
|
||||||
|
rc = ln(arg, path);
|
||||||
|
if (rc && errno == EEXIST)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if (!strc) {
|
||||||
|
if (type[1] != '+')
|
||||||
|
break;
|
||||||
|
erase(path);
|
||||||
|
}
|
||||||
|
mkparent(path, 0755);
|
||||||
|
rc = mkfifo(path, mode ?: 0644);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
rc = glob_do(path, erase);
|
||||||
|
if (rc && errno == ENOENT)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
rc = glob_do(path, rmrf);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
if (!arg)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < gl.gl_pathc; i++) {
|
||||||
|
fp = fopen(gl.gl_pathv[i], type[1] == '+' ? "a" : "w");
|
||||||
|
if (fp) {
|
||||||
|
write_arg(fp, arg);
|
||||||
|
rc = fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
opts = "-R";
|
||||||
|
/* fallthrough */
|
||||||
|
case 'z':
|
||||||
|
if (!whichp("restorecon"))
|
||||||
|
break;
|
||||||
|
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < gl.gl_pathc; i++) {
|
||||||
|
snprintf(buf, sizeof(buf), "restorecon %s %s", opts, gl.gl_pathv[i]);
|
||||||
|
run(buf, "restorecon");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errx(1, "Unsupported tmpfiles command '%s'", type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst)
|
||||||
|
free(dst);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
warn("Failed %s operation on path %s", type, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only the three last tmpfiles.d/ directories are defined in
|
||||||
|
* tmpfiles.d(5) as system search paths. Finit adds two more
|
||||||
|
* before that to have Finit specific ones sorted first, and
|
||||||
|
* a configure prefix specific one after that for user needs.
|
||||||
|
*/
|
||||||
|
void tmpfilesd(void)
|
||||||
|
{
|
||||||
|
/* in priority order */
|
||||||
|
char *dir[] = {
|
||||||
|
FINIT_TMPFILES "/*.conf",
|
||||||
|
TMPFILES_PATH "/*.conf",
|
||||||
|
"/usr/lib/tmpfiles.d/*.conf",
|
||||||
|
"/run/tmpfiles.d/*.conf",
|
||||||
|
"/etc/tmpfiles.d/*.conf", /* local admin overrides */
|
||||||
|
};
|
||||||
|
int flags = GLOB_NOESCAPE;
|
||||||
|
glob_t gl;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < NELEMS(dir); i++) {
|
||||||
|
glob(dir[i], flags, NULL, &gl);
|
||||||
|
flags |= GLOB_APPEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
|
char *fn = gl.gl_pathv[i];
|
||||||
|
size_t j;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
/* check for overrides */
|
||||||
|
for (j = i + 1; j < gl.gl_pathc; j++) {
|
||||||
|
if (strcmp(basename(fn), basename(gl.gl_pathv[j])))
|
||||||
|
continue;
|
||||||
|
fn = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fn)
|
||||||
|
continue; /* skip, override exists */
|
||||||
|
|
||||||
|
fp = fopen(fn, "r");
|
||||||
|
if (!fp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// info("Parsing %s ...", fn);
|
||||||
|
while (!feof(fp)) {
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
line = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCCOMM);
|
||||||
|
if (!line)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tmpfiles(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
globfree(&gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user