mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d15e28ea6b | ||
|
|
836bfff08b | ||
|
|
59152b557c | ||
|
|
293b0ffa79 | ||
|
|
d5d9cd5645 | ||
|
|
2f281665af | ||
|
|
b00e7394f8 | ||
|
|
d5a5fffa52 | ||
|
|
4d24c34b28 | ||
|
|
355265651e | ||
|
|
41093df2a9 | ||
|
|
0a816d1fa4 | ||
|
|
3bfc0f0fca | ||
|
|
0aa6ee895c | ||
|
|
b1526fb2d6 | ||
|
|
b2624ac160 | ||
|
|
2c340f83ce | ||
|
|
337ee003bb | ||
|
|
791df0c986 | ||
|
|
f29e92be21 | ||
|
|
379a63fce4 | ||
|
|
b1f9a11809 | ||
|
|
2d9c63e48e | ||
|
|
d66455496a | ||
|
|
77cf72cb40 | ||
|
|
6cc587068b | ||
|
|
cc96e8908a | ||
|
|
adb25d52cd | ||
|
|
7d09567ec8 | ||
|
|
9130af1c77 | ||
|
|
cbea83b369 | ||
|
|
42cd8d284b | ||
|
|
72ebc92622 | ||
|
|
06adecaaea | ||
|
|
efc0592961 | ||
|
|
ed88469276 | ||
|
|
cbf96a1de0 | ||
|
|
cc979f27f1 | ||
|
|
fca5ce57ea | ||
|
|
53aa195ad5 | ||
|
|
e60211168e | ||
|
|
dc3ee9e467 | ||
|
|
6918f3b711 | ||
|
|
66020f48ea | ||
|
|
47b0d4cccf | ||
|
|
92c2efa655 | ||
|
|
b69aa8a8bd | ||
|
|
ca90bbfb04 | ||
|
|
0b5c555c7f | ||
|
|
6443995fc9 | ||
|
|
180faf8a40 | ||
|
|
0ebdd32f6b | ||
|
|
60bf858302 | ||
|
|
7c86b40190 | ||
|
|
4f3f140a09 | ||
|
|
10c24b1674 | ||
|
|
2b4f89ecd1 | ||
|
|
c42a6f2869 | ||
|
|
89bb609a7e | ||
|
|
da6298aecd | ||
|
|
576318cc29 | ||
|
|
0d7d331ad4 | ||
|
|
c886ecb5cc | ||
|
|
304a096877 | ||
|
|
064c7682fc | ||
|
|
58239b897b | ||
|
|
5281d82e80 | ||
|
|
5ff28c4dc6 | ||
|
|
f2218836af | ||
|
|
fbb3779d17 | ||
|
|
68f44f7a97 | ||
|
|
50e587f447 | ||
|
|
fb4c560196 | ||
|
|
8b5fa5eeaa | ||
|
|
88ec21e9cc | ||
|
|
84adec4006 | ||
|
|
19b2b2a279 | ||
|
|
e4163b7221 | ||
|
|
eaef302420 | ||
|
|
d5738da307 | ||
|
|
c9f930c7ad | ||
|
|
e2ee372b5d | ||
|
|
7ff7f93364 | ||
|
|
d5a95760ef | ||
|
|
24d1425b74 | ||
|
|
43d9e25ba7 | ||
|
|
f27998ae4f | ||
|
|
c8e0b6b9ef | ||
|
|
f7e8fccb2f | ||
|
|
7bbccb1213 | ||
|
|
d38c333e04 | ||
|
|
095f6b645c | ||
|
|
b4d10cbade | ||
|
|
5d703fd39e | ||
|
|
eaaf8d862e | ||
|
|
2468f11cd8 | ||
|
|
f8a6a119ac | ||
|
|
1889b88aab | ||
|
|
0d70023bfe | ||
|
|
41648a073b | ||
|
|
33cee0ba5f | ||
|
|
2235fb8352 | ||
|
|
eb9e94935e | ||
|
|
fb230f2137 | ||
|
|
6aa48a8b32 | ||
|
|
5b9cebbfd3 | ||
|
|
38791616e0 | ||
|
|
fede3fd2e1 | ||
|
|
a46a33238d | ||
|
|
afe0488bf2 | ||
|
|
ec5e42694d | ||
|
|
2e73563eea | ||
|
|
338521a748 | ||
|
|
6ae1083c99 | ||
|
|
846806747b | ||
|
|
8904ca993e | ||
|
|
efed0db16b | ||
|
|
11946f12d3 | ||
|
|
2ba359ed80 | ||
|
|
fafd524701 | ||
|
|
1ee6c9f46b | ||
|
|
a88eb82a68 | ||
|
|
a436a1c8cb | ||
|
|
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 | ||
|
|
a61a8015d7 | ||
|
|
e2f070649d | ||
|
|
f08d375c36 | ||
|
|
1c4bad5b73 | ||
|
|
6c874895dc | ||
|
|
ef1a05425d | ||
|
|
fc3bad2160 | ||
|
|
31ac933d64 | ||
|
|
caa398816d | ||
|
|
0d57a507f3 | ||
|
|
96257cc5c7 | ||
|
|
fbcd7d2384 | ||
|
|
d9dbfa23c7 | ||
|
|
91f077bd08 | ||
|
|
5fb4a6ae8f | ||
|
|
e84333662e | ||
|
|
ddb42fda70 | ||
|
|
621eb21b42 | ||
|
|
6cc44a87c9 | ||
|
|
5f908cae49 | ||
|
|
6de67ed107 | ||
|
|
c129f06195 | ||
|
|
d11dc63bc2 | ||
|
|
d904ec1602 | ||
|
|
2a98b2f055 | ||
|
|
6286ede985 | ||
|
|
4a8ca787d3 | ||
|
|
561c88a868 | ||
|
|
f8dfcdf024 | ||
|
|
f50b5a4ac6 | ||
|
|
59525b80e3 | ||
|
|
0ce028add1 | ||
|
|
75c5c85f07 | ||
|
|
ccb84ced2e | ||
|
|
5ef669fb6c | ||
|
|
96ff9c115c | ||
|
|
83aa6abb1c | ||
|
|
9304024841 | ||
|
|
0de5981948 | ||
|
|
c39106906d | ||
|
|
929a6e7aac | ||
|
|
ceb4d04d23 | ||
|
|
f483a8aeaa | ||
|
|
e3e220ce8e | ||
|
|
fc653e4293 | ||
|
|
94d55cb7dd | ||
|
|
0e84ecf143 | ||
|
|
0e93dfd640 | ||
|
|
4dff8237f1 | ||
|
|
35400f2a8b | ||
|
|
9a76f0862c | ||
|
|
fb8a7caa99 | ||
|
|
f8bed5c7af | ||
|
|
da6fa482e7 | ||
|
|
fdf8e48c99 | ||
|
|
195155a7f5 | ||
|
|
6934ba4dbc | ||
|
|
fdbd25c1b5 | ||
|
|
467f1cca81 | ||
|
|
d6fdf4a806 | ||
|
|
4be9d97e61 | ||
|
|
021d011493 | ||
|
|
e988d38f71 | ||
|
|
8690fc732b |
@@ -9,9 +9,9 @@ For email questions/requests/alerts there is always the risk of memory
|
||||
exhaustion on the part of the maintainer(s).
|
||||
|
||||
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,
|
||||
so we can discuss it. Talking about the code first is the best way to
|
||||
get started before submitting a pull request.
|
||||
open an issue with `"[RFC: Unsure if this is a bug ... ?"`, or use the
|
||||
GitHub discussions forum, so we can discuss it. Talking about the code
|
||||
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
|
||||
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].
|
||||
|
||||
|
||||
[github]: https://github.com/troglobit/finit/
|
||||
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
|
||||
[gitbook]: https://git-scm.com/book/ch5-2.html
|
||||
[conduct]: CODE-OF-CONDUCT.md
|
||||
|
||||
+17
-20
@@ -15,7 +15,7 @@ jobs:
|
||||
build:
|
||||
# Verify we can build on latest Ubuntu with both gcc and clang
|
||||
name: ${{ matrix.compiler }}
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [gcc, clang]
|
||||
@@ -27,15 +27,8 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.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
|
||||
sudo apt-get -y install pkg-config tree jq libuev-dev libite-dev
|
||||
- uses: actions/checkout@v4
|
||||
- name: Static Finit
|
||||
run: |
|
||||
./autogen.sh
|
||||
@@ -43,28 +36,32 @@ jobs:
|
||||
make V=1
|
||||
- name: Regular Finit
|
||||
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 V=1
|
||||
- name: Install to ~/tmp and Inspect
|
||||
- name: Install to /tmp
|
||||
run: |
|
||||
DESTDIR=/tmp make install-strip
|
||||
tree /tmp
|
||||
tree /tmp || true
|
||||
- name: Check dependencies
|
||||
run: |
|
||||
ldd /tmp/sbin/finit
|
||||
size /tmp/sbin/finit
|
||||
ldd /tmp/sbin/initctl
|
||||
size /tmp/sbin/initctl
|
||||
ldd /tmp/sbin/reboot
|
||||
size /tmp/sbin/reboot
|
||||
/tmp/sbin/finit -h
|
||||
/tmp/sbin/initctl -h
|
||||
- name: Verify starting and showing usage text
|
||||
run: |
|
||||
sudo /tmp/sbin/finit -h
|
||||
sudo /tmp/sbin/initctl -h
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
|
||||
dir=finit-$ver/_build/sub
|
||||
make -j1 distcheck || (cat $dir/test/setup-root.log; cat $dir/test/test-suite.log; false)
|
||||
make check || (cat test/test-suite.log; false)
|
||||
- name: Upload Test Results
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: finit-test-${{ matrix.compiler }}
|
||||
path: test/*
|
||||
path: test/*.log
|
||||
|
||||
@@ -15,40 +15,59 @@ jobs:
|
||||
coverity:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
id: coverity-toolchain-cache
|
||||
with:
|
||||
path: cov-analysis-linux64
|
||||
key: ${{ runner.os }}-coverity
|
||||
- name: Download Coverity Scan
|
||||
if: steps.coverity-toolchain-cache.outputs.cache-hit != 'true'
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch latest Coverity Scan MD5
|
||||
id: var
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
run: |
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 \
|
||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}" \
|
||||
-O cov-analysis-linux64.tar.gz
|
||||
mkdir cov-analysis-linux64
|
||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 \
|
||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
|
||||
-O coverity-latest.tar.gz.md5
|
||||
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
|
||||
- uses: actions/cache@v3
|
||||
id: cache
|
||||
with:
|
||||
path: coverity-latest.tar.gz
|
||||
key: ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }}
|
||||
${{ runner.os }}-coverity-
|
||||
${{ runner.os }}-coverity
|
||||
- name: Download Coverity Scan
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
run: |
|
||||
if [ ! -f coverity-latest.tar.gz ]; then
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 \
|
||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}" \
|
||||
-O coverity-latest.tar.gz
|
||||
else
|
||||
echo "Latest Coverity Scan available from cache :-)"
|
||||
md5sum coverity-latest.tar.gz
|
||||
fi
|
||||
mkdir coverity
|
||||
tar xzf coverity-latest.tar.gz --strip 1 -C coverity
|
||||
- name: Install dependencies
|
||||
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)
|
||||
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.5.2/libite-2.5.2.tar.gz
|
||||
tar xf libuev-2.4.0.tar.xz
|
||||
tar xf libite-2.5.2.tar.gz
|
||||
(cd libuev-2.4.0 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
(cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
sudo ldconfig
|
||||
- name: Build
|
||||
- name: Configure
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
|
||||
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
|
||||
- name: Build
|
||||
run: |
|
||||
export PATH=`pwd`/coverity/bin:$PATH
|
||||
cov-build --dir cov-int make
|
||||
- name: Submit result to Coverity Scan
|
||||
- name: Submit results to Coverity Scan
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
run: |
|
||||
@@ -61,3 +80,8 @@ jobs:
|
||||
--form version=trunk \
|
||||
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
|
||||
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
|
||||
- name: Upload build.log
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverity-build.log
|
||||
path: cov-int/build-log.txt
|
||||
|
||||
@@ -7,63 +7,53 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Create GitHub release
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and upload release tarball
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
release_id: ${{ steps.create_release.outputs.id }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MAKEFLAGS: -j3
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Installing dependencies ...
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
|
||||
- name: Setting release variables ...
|
||||
id: build
|
||||
run: |
|
||||
ver=${GITHUB_REF#refs/tags/}
|
||||
echo "ver=${ver}" >> $GITHUB_OUTPUT
|
||||
if echo $ver | grep -qE '^[0-9]+\.[0-9]+(\.[0-9]+)?(-alpha|-beta|-rc)[0-9]*$'; then
|
||||
echo "pre=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "pre=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
if echo $ver | grep -qE '^[0-9.]+\.[0-9.]+(\.[0-9]+)?$'; then
|
||||
echo "latest=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "latest=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Creating Makefiles ...
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
|
||||
--with-keventd
|
||||
- name: Build release ...
|
||||
run: |
|
||||
make release || (cat test/test-suite.log; false)
|
||||
ls -lF ../
|
||||
mkdir -p artifacts/
|
||||
mv ../*.tar.* artifacts/
|
||||
- name: Extract ChangeLog entry ...
|
||||
# Hack to extract latest entry for body_path below
|
||||
run: |
|
||||
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|
||||
|head -n -1 > release.md
|
||||
cat release.md
|
||||
- name: Create release ...
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Finit v${{ github.ref }}
|
||||
body_path: release.md
|
||||
draft: false
|
||||
prerelease: false
|
||||
tarball:
|
||||
name: Build and upload release tarball
|
||||
needs: release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Installing dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
|
||||
tar xf libuev-2.3.2.tar.xz
|
||||
tar xf libite-2.3.1.tar.gz
|
||||
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||
sudo ldconfig
|
||||
- name: Creating Makefiles
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
|
||||
- name: Building release
|
||||
run: |
|
||||
make release
|
||||
ls -lF ../
|
||||
mkdir -p artifacts/
|
||||
mv ../*.tar.* artifacts/
|
||||
- name: Uploading release artifacts
|
||||
uses: skx/github-action-publish-binaries@release-0.15
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
releaseId: ${{ needs.release.outputs.release_id }}
|
||||
args: artifacts/*
|
||||
name: Finit v${{ github.ref_name }}
|
||||
prerelease: ${{ steps.build.outputs.pre }}
|
||||
makeLatest: ${{ steps.build.outputs.latest }}
|
||||
bodyFile: "release.md"
|
||||
artifacts: "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@v4
|
||||
- name: Check for any commits since last week
|
||||
id: schedule
|
||||
run: |
|
||||
commits="$(git log --format=%H --since='1 week ago')"
|
||||
if [ -n "$commits" ]; then
|
||||
echo "run=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "run=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
distcheck:
|
||||
needs: schedule
|
||||
if: ${{ needs.schedule.outputs.run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
|
||||
- uses: actions/checkout@v4
|
||||
- name: Base run
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
|
||||
make -j9 V=1
|
||||
- name: Install to /tmp
|
||||
run: |
|
||||
DESTDIR=/tmp make install-strip
|
||||
- name: 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)
|
||||
@@ -14,6 +14,7 @@ GTAGS
|
||||
/autom4te.cache
|
||||
/aclocal.m4
|
||||
/ar-lib
|
||||
/backup/
|
||||
/compile
|
||||
/config.cache
|
||||
/config.h
|
||||
@@ -36,6 +37,9 @@ GTAGS
|
||||
/test-driver
|
||||
/test-suite.log
|
||||
|
||||
# Misc
|
||||
/.gdb_history
|
||||
|
||||
# TroglOS build system
|
||||
/.config
|
||||
/.stamp
|
||||
|
||||
@@ -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/:
|
||||
+524
-3
@@ -4,7 +4,510 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[4.3][UNRELEASED]
|
||||
[4.7][] - 2024-01-07
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
- Silence "not available" messages for run/task/service with `nowarn`
|
||||
- Update docs, `cgroup.root` workaround for `SCHED_RR` tasks
|
||||
- Drop runlevels 0 and 6 from `keventd`, not needed at reboot/poweroff
|
||||
- Mount `/dev/shm` with mode 1777 (sticky bit)
|
||||
- Mount `/dev/mqueue` if available, inspiration from Alpine Linux
|
||||
- Update Alpine Linux installer and configuration files, tested on
|
||||
Alpine v3.19, some assembly still required
|
||||
|
||||
### Fixes
|
||||
- Fix confusing warning message when tmpfiles.d fails to install symlink
|
||||
- Fix tmpfiles.d legacy `/run/lock/subsys`, ordering
|
||||
- Add missing `/var/tmp` and `/var/lock -> /run/lock` (tmpfiles.d)
|
||||
- Fix #388: log redirection broken unless Finit runs in debug mode.
|
||||
Found and fixed by Ryan Rorison
|
||||
- Fix #389: must libc requires `libgen.h` for `basename()` function.
|
||||
Reported and worked around with new `basenm()` function by Stargirl
|
||||
|
||||
|
||||
[4.6][] - 2023-11-13
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
- Add support for service `notify:pid` and `readiness none` global
|
||||
option to change how Finit expects readiness notification, issue #386
|
||||
|
||||
### Fixes
|
||||
- Fix #383: dbus and runparts regression in Finit v4.5. The configure
|
||||
script must expand `FINIT_RUNPATH_` before defining it in `config.h`
|
||||
- Fix #384: service environment variables drop everything but the first
|
||||
argument, e.g., `VAR="foo bar qux"` drops everything but `foo`
|
||||
- Fix #385: internal conditions, e.g., `<int/bootstrap>` turn into flux
|
||||
when leaving bootstrap, causing depending services to stop
|
||||
- Fix #387: global environment variables declared with `set VAR=NAME` do
|
||||
not drop leading `set `, causing `'set VAR'='NAME'` in env.
|
||||
- Sanity check environment variables, for services and globally. Ensure
|
||||
the variable name (key) does not contain spaces, or a leading `set `
|
||||
|
||||
|
||||
[4.5][] - 2023-10-30
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
- Refactor `runparts` and `/etc/rc.local` to no longer block the main
|
||||
loop, allowing `initctl` calls to interact with Finit. Issue #356
|
||||
- Refactor the `run` stanza to no longer block the main loop, issue #362
|
||||
- Allow `sulogin` with a user different from `root`, issue #357
|
||||
- Allow disabling invocation of rescue mode from kernel command line
|
||||
- Add `initctl -f` to force-skip asking Finit for existing services
|
||||
when creating new services during bootstrap, e.g. `/etc/rc.local`
|
||||
- `initctl runlevel` now returns `N S` instead of `N 10` in bootstrap
|
||||
- `initctl runlevel N` during bootstrap is now allowed. It changes
|
||||
the next runlevel to go to when bootstrap has completed. Effectively
|
||||
overriding the `runlevel N` statement in `/etc/finit.conf`
|
||||
- Improved logging on failure to `execvp()` a run/task/service, now
|
||||
with `errno`, e.g., "No such file or directory" when the command
|
||||
is missing from `$PATH`
|
||||
- Add Bash completion support for `initctl`, configurable, issue #360
|
||||
- Handle absolute path to `initctl [enable|disable]`, not supported
|
||||
- Update `finit.conf(5)` man page with the recommended directory
|
||||
hierarchy in `/etc/finit.d/`
|
||||
- The `runparts` code has been split into `/libexec/finit/runparts`
|
||||
- The `runparts` command now takes two options: `sysv` and `progress`.
|
||||
The former ensures only `SNNfoo` and `KNNfoo` scripts are run.
|
||||
- Add SysV Init Compatibility section to documentation
|
||||
- Increased MAX path for commands, and arguments: 64 -> 256
|
||||
- The bundled watchdog daemon no longer tries to log at startup, because
|
||||
syslog is not available yet, any startup message leak to console
|
||||
- Extend `if:` option with runtime evaluation of conditions. E.g., start
|
||||
a task only `if:<run/foo/failure>` (here the run task 'foo' failed)
|
||||
- Document new `if:`, `conflict:`, `nowarn` options for run/task/service
|
||||
first introduced in v4.4
|
||||
- Failure to open fstab should log to console, not just log to `/dev/kmsg`
|
||||
- Rename `/lib/finit/system/*.conf`, added numbered prefix to ensure
|
||||
proper execution order, e.g., `udevd` should always run first
|
||||
- Plugins and bundled services: dbus, keventd, watchdogd, and runparts,
|
||||
are now loaded *after* all services in `/lib/finit/system/`. A new
|
||||
runtime-only path (for inspection) in `/run/finit/system/` is used
|
||||
- Redirect `log*` output to console when `finit.debug` is enabled
|
||||
- Assert `<int/container>` condition if we detect running in container
|
||||
- Add support for mdev's netlink daemon mode, issue #367
|
||||
- Add support for mdevd in `10-hotplug.conf`, preferred over plain mdev
|
||||
- Disable modprobe plugin by default, udevd and mdev/mdevd loads modules
|
||||
- Update documentation for run/task shell limitations, issue #376
|
||||
- Update documentation regarding automount of `/run` and `/tmp`
|
||||
- Update plugin documentation, add section about limited tmpfiles.d(5) support
|
||||
- Skip registering service when `if:!name` matches a known service.
|
||||
This allows conditional loading of alternative services, e.g. if udevd
|
||||
is already loaded we do not need mdevd
|
||||
- Drop `doc/bootstrap.md`, inaccurate and confusing to users
|
||||
|
||||
### Fixes
|
||||
- Fix #227: believed to have been fixed in v4.3, the root cause was
|
||||
actually that Finit was waiting for a process that was no longer in
|
||||
the system. The fix is to ask the kernel on process-stop-timeout and
|
||||
replay the lost PID so that Finit can continue with reboot/shutdown
|
||||
- Fix #358: fix inotify events for `/etc/finit.conf`, improved log
|
||||
messages and error handling
|
||||
- Fix #361: cgroup move fail if run/task/services start as non-root.
|
||||
Regression in the v4.4 release cycle while adding support for the
|
||||
pre:/post:/ready: scripts. Now the latter scripts also properly run
|
||||
in their correct cgroup
|
||||
- Fix #366: document `fsck.*` kernel command line options and simplify
|
||||
the configure flags `--enable-fsckfix` and `--enable-fastboot` to
|
||||
only adjust the default values for the `fsck.*` options.
|
||||
- Fix #371: swap load order of `/lib/finit/system/*` vs `/etc/finit.d/*`
|
||||
We must run `10-hotplug.conf` first to ensure devices and modules are
|
||||
up and loaded before the user's run/task/services are called. The order
|
||||
at bootstrap is now saved in `/run/finit/conf.order` for inspection,
|
||||
`/run/finit/exec.order` shows the start order of each process
|
||||
- Fix #372: lost `udevadm` calls due to overloading
|
||||
- Adjust final `udevadm settle` timeout: 5 -> 30 sec
|
||||
- Fixed dbus plugin, the function that located `<pidfile> ...` in the
|
||||
`dbus/system.conf` caused spurious line breaks which led to the
|
||||
service not being loaded properly
|
||||
- The `runparts` executor now skips backup files (`foo~`)
|
||||
- The `runparts` stanza now properly appends ` start` to scripts that
|
||||
start with `S[0-9]+`. This has been broken for a very long time.
|
||||
- Fix #377: expand service `env:file` variables, allow constructs like:
|
||||
|
||||
RUNDIR=/var/run/somesvc
|
||||
DAEMON_ARGS=--workdir $RUNDIR --other-args...
|
||||
- Fix #378: warn on console if run/task times out during bootstrap
|
||||
- Fix #378: add run/task support for `<!>` to allow transition from
|
||||
bootstrap to multi-user runlevel even though task has not run yet.
|
||||
- Fix #382: do not clear `<service/foo/STATE>` conditions on reload.
|
||||
Introduced back in v4.3-rc2, 82cc10be8, the support for automatic
|
||||
service conditions have had a weird and unintended behavior. Any
|
||||
change in state (see `doc/svc-machine.png`) caused Finit to clear
|
||||
out *all* previously acquired service conditions.
|
||||
|
||||
However, when moving between RUNNING and PAUSED states, a service
|
||||
should not have its conditions cleared. The PAUSED state, seen
|
||||
also by all conditions moving to FLUX, is only temporary while an
|
||||
`initctl reload` is processed. If a service has no changes to be
|
||||
applied it will move back to RUNNING.
|
||||
|
||||
Also, we cannot clear the service conditions because other run/task
|
||||
or services may depend on it and clearing them would cause Finit to
|
||||
`SIGTERM` these processes (since they are no longer eligible to run).
|
||||
|
||||
|
||||
[4.4][] - 2023-05-15
|
||||
--------------------
|
||||
|
||||
> **Note 1:** this release contains changes to the `.conf` parser. If you
|
||||
> have .conf file statements with comment character (`#`) in the command
|
||||
> options or description, you must now escape them (`\#`). Issue #186
|
||||
>
|
||||
> **Note 2:** prior to this release, runlevel `S` and `0` were after
|
||||
> boot treated as the same runlevel. This caused `task [06] ...` to
|
||||
> also run at bootstrap instead of just at shutdown and reboot. The
|
||||
> changes made to Finit to separate `S` from `0` require you to update
|
||||
> the allowed runlevels for services that are allowed to continue
|
||||
> running at shutdown. I.e., change `[S123456789]` to `[S0123456789]`
|
||||
> for, e.g., `sysklogd`. Issue #352
|
||||
|
||||
### Changes
|
||||
* Add limited tmpfiles.d(5) support
|
||||
|
||||
This change adds very basic `tmpfiles.d/` support to Finit. Most of
|
||||
the basic types are supported, but not all, so for now, please check
|
||||
the code and examples for details on what is working.
|
||||
|
||||
* If a run/task/service command does not exist, skip registering it
|
||||
|
||||
This changes the semantics of Finit a bit by checking for the command
|
||||
to run when registering it, skipping commands that cannot be found in
|
||||
the absolute path provided in the command, or in `$PATH`
|
||||
|
||||
This change includes a new `nowarn` flag that can be used to prevent
|
||||
Finit from warning for missing commands. See below for an example.
|
||||
|
||||
* Add run/task/service support for `conflict:foo` handling
|
||||
* Add run/task/service support for `if:[!]ident` and `if:<[!]cond>`
|
||||
|
||||
Conditional loading of stanza depending on ident is already loaded (or
|
||||
not), or condition satisfied (or not). E.g., do not run `mdev` if we
|
||||
found and registered `udevd`, or load service only if `<boot/testing>`
|
||||
condition is set.
|
||||
|
||||
The optional leading `!` negates the comparison, if NOT foo then ...
|
||||
|
||||
* Add support for static services in `/lib/finit/system/*.conf`
|
||||
|
||||
Slowly migrating away from hard-coded services in plugins. This way
|
||||
it's possible for the user to both inspect and override as needed.
|
||||
|
||||
* Migrate hotplug plugin to a conditional `/lib/finit/system/hotplug.conf`
|
||||
|
||||
This is the first example of the just minted advanced stanza syntax with
|
||||
`if:`-statements, `conflict:` handling, and `nowarn` flags.
|
||||
|
||||
* Initial support for template services, `foo@.conf`, similar to systemd
|
||||
|
||||
$ initctl show avahi-autoipd@
|
||||
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
|
||||
|
||||
To enable ZeroConf for, e.g., `eth0`, use
|
||||
|
||||
$ initctl enable avahi-autoipd@eth0.conf
|
||||
|
||||
The enabled symlink will be set up to `avahi-autoipd@.conf` and every
|
||||
instance of `%i` will in the instantiated directive be replaced with
|
||||
`eth0`. Inspect the result with:
|
||||
|
||||
$ initctl status avahi-autoipd:eth0
|
||||
|
||||
* Add `devmon`, a `<dev/foo>` condition provider, issue #185
|
||||
* Support for line continuation character `\` in .conf files, issue #186
|
||||
|
||||
service name:sysklogd [S123456789] \
|
||||
env:-/etc/default/sysklogd \
|
||||
syslogd -F $SYSLOGD_ARGS \
|
||||
-- System log daemon
|
||||
|
||||
* `HOOK_BASEFS_UP` has been moved! External plugins that need to call
|
||||
`service_register()`, please use `HOOK_SVC_PLUGIN` from now on.
|
||||
Apologies for any inconveniences this might cause!
|
||||
* getty: add support for `/etc/os-release` to replace `uname` output
|
||||
This change, which has a fallback to `/usr/lib/os-release`, overrides
|
||||
traditional modifiers with the os-release variant. These values were
|
||||
taken from `uname`, which on Linux systems are pretty useless since
|
||||
they always return the kernel name and version instead of the
|
||||
distro/OS values.
|
||||
|
||||
E.g., \s becomes `PRETTY_NAME` instead of 'Linux' and \v becomes the
|
||||
pretty `VERSION`, while \r becomes `VERSION_ID`.
|
||||
* Support for overriding `/etc/finit.conf` and `/etc/finit.d` issue #235
|
||||
New (kernel) command line option `finit.config=PATH` which can be used
|
||||
to redirect Finit to start up with, e.g., `/etc/factory.conf` instead of
|
||||
`/etc/finit.conf`.
|
||||
|
||||
For the complete experience a new top-level configuration file directive
|
||||
`rcsd PATH` has aslo been added. It in turn can be used by `factory.conf`
|
||||
as follows to override `/etc/finit.d`:
|
||||
|
||||
rcsd /etc/factory.d
|
||||
|
||||
* Support for overriding `/etc/finit.d` from the alternate `finit.conf`
|
||||
with a new `rcsd /path/to/dot.d/` .conf file directive
|
||||
* Support for `fsck_mode=[auto,skip,force]` + `fsck_repair=[preen,no,yes]`
|
||||
* Add `set` keyword for environment variables set in `/etc/finit.conf`
|
||||
* Support `finit.cond=foo` cmdline `<boot/foo>` conditions, issue #250
|
||||
* `initctl` JSON output support for status and conditions, issue #273
|
||||
Example:
|
||||
|
||||
root@infix:~$ initctl status -j resolvconf
|
||||
{
|
||||
"identity": "resolvconf",
|
||||
"description": "Update DNS configuration",
|
||||
"type": "task",
|
||||
"forking": false,
|
||||
"status": "done",
|
||||
"exit": { "code": 0 },
|
||||
"origin": "/etc/finit.d/enabled/sysrepo.conf",
|
||||
"command": "resolvconf -u",
|
||||
"restarts": 0,
|
||||
"pidfile": "none",
|
||||
"pid": 0,
|
||||
"user": "root",
|
||||
"group": "root",
|
||||
"uptime": 0,
|
||||
"runlevels": [ 1, 2, 3, 4, 5, 7, 8, 9 ]
|
||||
}
|
||||
|
||||
The excellent tool `jq` can be used to extract parts of the output for
|
||||
further scripting. E.g. `initctl status -j foo | jq .exit.status`
|
||||
|
||||
* Add JSON support to `initctl ls` command
|
||||
|
||||
This allows for easy access to the *disabled* services:
|
||||
|
||||
root@anarchy:~# initctl ls --json |jq '.available - .enabled'
|
||||
[
|
||||
"chronyd.conf",
|
||||
"dnsmasq.conf",
|
||||
"gdbserver.conf",
|
||||
"inadyn.conf",
|
||||
"inetd.conf",
|
||||
"isisd.conf",
|
||||
"lldpd.conf",
|
||||
"mstpd.conf",
|
||||
"ntpd.conf",
|
||||
"ospf6d.conf",
|
||||
"ospfd.conf",
|
||||
"querierd.conf",
|
||||
"ripd.conf",
|
||||
"ripng.conf",
|
||||
"sshd.conf",
|
||||
"syslogd.conf",
|
||||
"telnetd.conf",
|
||||
"uftpd.conf",
|
||||
"wpa_supplicant.conf",
|
||||
"zebra.conf"
|
||||
]
|
||||
|
||||
* Allow `manual:yes` on sysv/service/run/task stanzas, issue #274
|
||||
* Add support for `oncrash:script` to call the `post:script` action, if
|
||||
defined, for a crashing service. The `EXIT_CODE` variable sent to the
|
||||
script is set to `crashed`. Issue #282
|
||||
* Search for plugins in `/usr` and `/usr/local` as well, issue #284
|
||||
* tty: add support for `passenv` flag to `/bin/login`, issue #286
|
||||
* Add reboot/shutdown/poweroff timeout `-t SEC` to initctl, issue #295
|
||||
* Add support for s6 and systemd readiness notification, issue #299.
|
||||
Service readiness notification to support daemons employing systemd
|
||||
and s6 notification. Complementing the native Finit readiness support
|
||||
using PID files that exist already.
|
||||
|
||||
The two have slightly different ways of implementing readiness:
|
||||
|
||||
- https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||
- https://skarnet.org/software/s6/notifywhenup.html
|
||||
|
||||
Finit now provides both a `NOTIFY_SOCKET` environment variable, for
|
||||
systemd, and a way to start s6 daemons with a descriptor argument.
|
||||
|
||||
For details on the syntax, see the `service` documentation.
|
||||
|
||||
This change also renames internal states for run/task/services to
|
||||
avoid any confusion with the introduction of `ready:scripts`:
|
||||
|
||||
- `WAITING -> PAUSED`
|
||||
- `READY -> WAITING`
|
||||
|
||||
A service condition that used, e.g., `<service/foo/ready>` should now
|
||||
instead use `<service/foo/waiting>`
|
||||
|
||||
* Add `ready:script` for services, called when daemon is ready, issue #300
|
||||
* Add support for running scripts at shutdown at two new hook points
|
||||
during the shutdown process, issue #302. See doc/plugins.md for details:
|
||||
|
||||
- `HOOK_SVC_DN`: after all services and non-reserved processes have been
|
||||
killed (and collected)
|
||||
- `HOOK_SYS_DN`: after all file systems have been unmounted, *just prior*
|
||||
to Finit calling `reboot()` to shut down or reboot the system
|
||||
|
||||
* The `modules-load` plugin now default to runlevel `[S]`, in previous
|
||||
releases it was `[2345]`. This breaking change is to align it more
|
||||
with what users mostly want (modules loaded before services start) and
|
||||
can be changed back to the old behavior with a per-file setting:
|
||||
|
||||
set runlevel 2345
|
||||
|
||||
* The `modules-load` plugin now adds silent tasks for modprobe. This to
|
||||
prevent confusing `[ OK ]` boot messages when in fact modprobe failed.
|
||||
* The `modules-load` plugin now support `set modprobe /path/to/modprobe`
|
||||
* The header files `finit/conf.h` and `finit/service.h` are now exported
|
||||
for external plugins
|
||||
* Add support for multiple args to `initctl cond set/clr`, issue #329
|
||||
* Silence confusing `[ OK ]` progress from modules-load plugin, issue #332
|
||||
|
||||
This change drops the confusing status progress output, which was always
|
||||
OK since the actual modprobe operation runs in the background. No need
|
||||
to show status of the "added a task to finit, found modprobe" command.
|
||||
|
||||
* dbus plugin: adapt to other operating systems
|
||||
|
||||
Not all Linux systems are based on Debian, and even if they are inspired
|
||||
by Debian (Buildroot), they do not necessarily use the same defaults.
|
||||
Probes the system at runtime for:
|
||||
|
||||
- dbus user and group
|
||||
- dbus PID file
|
||||
|
||||
If the user/group cannot be found we fall back to `root`, if the PID
|
||||
file cannot be determined we ignore PID file readiness.
|
||||
|
||||
* Improve documentation for runparts and hook scripts. Issue #315, #320
|
||||
* Add `HOOK_NETWORK_DN`, called after change to runlevel 6 or 0, issue #319
|
||||
* Use sysklogd `logger` tool instead of legacy `logit` tool, issue #344
|
||||
|
||||
For log redirection Finit has the legacy `logit` tool. This change
|
||||
allows Finit to use the `sysklogd` project's extended `logger` tool
|
||||
instead, when available. Allowing logging with the process' PID.
|
||||
|
||||
* Add `initctl` aliases: `cat -> show`, `kill -> signal`
|
||||
* Add `initctl -n,--noerr` to return OK(0) if services do not exist, for
|
||||
integration with openresolv and scripts with similar requirements
|
||||
* Add `initctl plugins`, list installed plugins
|
||||
* Add timestamp to log messages in fallback and logging to `stderr`.
|
||||
When there is no log daemon, and we are running in a container, or we
|
||||
cannot log to the kernel ring buffer, then we log to `stderr`. This
|
||||
change improves log output by prefixing each message with a timestamp.
|
||||
|
||||
### Fixes
|
||||
* Fix #254: document limitations in `rc.local` and `runparts` scripts
|
||||
* Fix #269: add compulsory kernel symlinks in `/dev`
|
||||
* Fix #275: `initctl status foo` should list all instances, regression
|
||||
introduced in v4.3
|
||||
* Fix #278: enforce conditions also for running `pre:` scripts
|
||||
* Fix #279: allow `restart:always`, of crashing services. Similar to
|
||||
`respawn` but honors `restart_sec`
|
||||
* Fix #280: allow calling `initctl restart foo` from within foo
|
||||
* Fix #283: too quick timeout at bootstrap of lingering tasks
|
||||
* Fix #285: `initctl restart` should start crashed service
|
||||
* Fix #288: enable built-in `sulogin` in Alpine and Void Linux builds
|
||||
* Fix #293: modprobe plugin: support for coldplugging more devices. It
|
||||
turns out, not all buses in Linux add `modalias` attributes to their
|
||||
devices in sysfs. One notable exception are MDIO buses. The plugin's
|
||||
scan routine would thus not pick them up.
|
||||
* Fix #294: `shutdown --help` mistakenly shuts down system
|
||||
* Fix #310: Always use configured restart delay for crashing services.
|
||||
If no delay is configured, default to an initial 2000 msec for forking
|
||||
daemons and start-stop scripts, and 1 msec for non-forking daemons
|
||||
* Fix #311: document how to combine device tree with conditions
|
||||
* Fix #312: restart services with respawn set, e.g. ttys, immediately
|
||||
* Fix #313: Cancel pending restart timer on stop/start/restart/reload
|
||||
* Fix #314: skip run/task/service restart if conditions are lost
|
||||
* Fix #315: add environment variables to hook scripts
|
||||
|
||||
All hook scripts are called with at least one environment variable
|
||||
set, `FINIT_HOOK_NAME`, useful when reusing the same hook script for
|
||||
multiple hook points. It is set to the string name, also used by the
|
||||
path, e.g., `hook/net/up`
|
||||
|
||||
For all hook points from hook/sys/shutdown and later, `FINIT_SHUTDOWN`
|
||||
is also set, to one of: `halt`, `poweroff`, `reboot`
|
||||
|
||||
* Fix #317: make sure hook scripts don't run twice, also fixes #316
|
||||
* Fix #318: only show `[ OK ] Calling foo` progress for `runparts ...`
|
||||
* Fix #320: the API/IPC socket is closed immediately at shutdown/reboot
|
||||
to protect hook scripts or services calling initctl. There is no way
|
||||
to service these requests safely at that time
|
||||
* Fix #333: consider a service dirty if command line args have changed
|
||||
|
||||
This fixes `initctl reload` correctly restarting all daemons that have
|
||||
new command line arguments.
|
||||
|
||||
Previously command line arguments changes were only acted upon if the
|
||||
service was explicitly reloaded `initctl reload myservice`.
|
||||
|
||||
Found and fixed by Jack Newman
|
||||
|
||||
* Fix #338: ensure shutdown hooks are called properly; `hook/sys/down`
|
||||
and `hook/svc/down` hook scripts, found and fixed by Jack Newman
|
||||
* Fix #339: use absolute path in `/etc/finit.d/enabled/` symlinks, for
|
||||
use-cases when `/etc` is read-only and `/etc/finit.d/enabled ->
|
||||
/mnt/finit.d/enabled`, reported by Jack Newman
|
||||
* Fix #340: Finit ignores deleted/moved `.conf` file sin `/etc/finit.d`
|
||||
* Fix #342: in runlevel S (bootstrap), not all `initctl` commands can be
|
||||
supported, block the following: runlevel, reload, start/stop, restart,
|
||||
reload, halt, poweroff, suspend. Also, prevent `SIGHUP` and `SIGUSR1`
|
||||
signals when in shutdown or reboot
|
||||
* Fix #352: separate runlevel S from runlevel 0
|
||||
* Fix #355: regression in v3.2 stopping a process and its group
|
||||
|
||||
In Finit v3.2 a regression was introduced that affects the way Finit
|
||||
stops a supervised process and its process group.
|
||||
|
||||
Instead of sending SIGTERM to the process, and thus delegating the
|
||||
responsibility to that process to inform any children it may have, as
|
||||
of commit 91a9c83 Finit sends SIGTERM to the entire process group.
|
||||
For SIGKILL this is fine, SIGKILL only runs as cleanup and as a last
|
||||
ditch effort if the process doesn't respond to SIGTERM.
|
||||
|
||||
This regression, introduced in v3.2, directly affects services like
|
||||
`avahi-autoipd` that have forked off children that it needs to tell to
|
||||
exit cleanly before it returns. With the patch in question these
|
||||
children are never allowed to complete, which in turn causes lingering
|
||||
169.254 link-local addresses on interfaces.
|
||||
|
||||
* Fix bootmisc plugin: octal permission on `/run/lock` and `/var/lock`
|
||||
|
||||
* Ensure `initctl cond get` support the flux state (exit code 255)
|
||||
* Fix potential socket leak at bootstrap and shutdown
|
||||
* Fix potential NULL pointer deref in kernel command line parser
|
||||
* Fix lingering condition in service after reload of service with new
|
||||
config that has no condition
|
||||
* Fix wrong path to command in service after reload, may have changed
|
||||
* `logit`: fall back to package name if `$LOGNAME` and `$USER` are
|
||||
unset. **Note:** you should probably not use `logit` in your own
|
||||
scripts, it is only meant for internal use by Finit. We recommend
|
||||
using `logger` from the `bsdutils` or `sysklogd` packages instead
|
||||
* Fix issue where `env:`/`pre:`/`post:`/etc. is removed from a service
|
||||
|
||||
The trick is when reloading a service like this:
|
||||
|
||||
service env:/etc/env serv -np -e foo:bar
|
||||
|
||||
into this:
|
||||
|
||||
service pre:/bin/pre.sh serv -np
|
||||
|
||||
In the second the `env:` has been removed and `pre:` added. Prior to
|
||||
this patch, `env:` was kept leading to unintended behavior.
|
||||
* Fix parse/diff of command line args, e.g., `nginx -g 'daemon off;'`
|
||||
|
||||
Starting a service like this works fine:
|
||||
|
||||
service [2345789] env:-/etc/default/nginx nginx -g 'daemon off;'
|
||||
|
||||
However, on `initctl reload` (and no change to .conf files) the arg
|
||||
list was lost while parsing the .conf files. Leading to a false
|
||||
positive 'diff' in args causing nginx to be unnecessarily restarted.
|
||||
* Fix issue with disabled "linewrap" on the console TTY after login.
|
||||
The root cause is `qemu-system-x86_64 -nographics` disabling it when
|
||||
starting up. The correct `\e[?7h` escape code is now used.
|
||||
|
||||
|
||||
[4.3][] - 2022-05-15
|
||||
--------------------
|
||||
|
||||
Critical bug fix release. If you run a 32-bit target with GLIBC 2.34
|
||||
@@ -63,6 +566,14 @@ you *need* to upgrade!
|
||||
* Document secret service option `respawn`, which bypasses the crash
|
||||
semantics, allowing endless restarts
|
||||
* Document secret `HOOK_BANNER`, the first hook point before the banner
|
||||
* Document slightly confusing `initctl reload foo` command. It does
|
||||
*not* reload the service's `.conf` file! Issue #263
|
||||
* Log changes; all instances where previously the `basename cmd` of a
|
||||
service was used to identify the service, now the proper `name:id` is
|
||||
used instead. Meaning, a service without a custom `:ID` or `name:`
|
||||
will display the same as before, but with any of those customizations
|
||||
the name and name:id will now be shown. **Note:** this may affect any
|
||||
log scrapers out there!
|
||||
* New plugin: `hook-scripts`, allows [run-parts(8)][] style scripts to
|
||||
run on any hook point. Contributed by Tobias Waldekranz
|
||||
* `initctl` (`reboot`) falls back to `-f` when it detects it is in
|
||||
@@ -75,6 +586,12 @@ you *need* to upgrade!
|
||||
in lexicographic order and UNIX backup files (`foo.conf~`) are skipped
|
||||
* The `name:id` tuple is now more consistently used in all log and debug
|
||||
messages instead of the basename of the command
|
||||
* Simplify error output of `initctl start/stop/restart/signal`, no more
|
||||
extra usage help, just a plain error message
|
||||
* Exit codes of `initctl` have changed to use LSB script standard and BSD
|
||||
sysexits.h exit codes. As before, a non-zero exit is error or missing
|
||||
* Add support for `initctl -q` to more commands: stop, start, restart,
|
||||
reload, signal, etc.
|
||||
|
||||
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
|
||||
@@ -93,7 +610,7 @@ you *need* to upgrade!
|
||||
* Fix #233: initctl shows wrong status for run/task, by Sergio Morlans
|
||||
and Ming Liu, Atlas Copco
|
||||
* Fix #248: source `env:file` also in `pre:` and `post` scripts
|
||||
* Fix #260: drop limit on device name in `Checking filesystem`...` output
|
||||
* Fix #260: drop limit on device name in `Checking filesystem...` output
|
||||
* Fix start/stop and monitoring (restart) of SysV init scripts and
|
||||
forking services, see the updated documentation for details
|
||||
* Fix call to `swapoff` at shutdown, does not support `-e` flag
|
||||
@@ -1138,7 +1655,11 @@ Major bug fix release.
|
||||
|
||||
* Initial release
|
||||
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.7...HEAD
|
||||
[4.7]: https://github.com/troglobit/finit/compare/4.6...4.7
|
||||
[4.6]: https://github.com/troglobit/finit/compare/4.5...4.6
|
||||
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
|
||||
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
|
||||
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
|
||||
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
|
||||
[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-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
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
|
||||
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
||||
|
||||
@@ -10,6 +10,10 @@ if DOC
|
||||
SUBDIRS += doc
|
||||
endif
|
||||
|
||||
if BASH
|
||||
bashdir = $(BASH_DIR)
|
||||
dist_bash_DATA = initctl
|
||||
endif
|
||||
# Compat hook
|
||||
install-dev:
|
||||
@make -C src install-pkgincludeHEADERS
|
||||
@@ -30,4 +34,7 @@ release: distcheck
|
||||
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
|
||||
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 \
|
||||
--with-bash-completion-dir=no
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Features](#features)
|
||||
* [Bootstrap](doc/bootstrap.md#bootstrap)
|
||||
* [Runlevels](#runlevels)
|
||||
* [Syntax](doc/config.md#syntax)
|
||||
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
|
||||
@@ -23,11 +22,10 @@ Introduction
|
||||
------------
|
||||
|
||||
Fast init for Linux systems. Reverse engineered from the [EeePC
|
||||
fastinit][] over ten years ago by [Claudio Matsuoka][] — "gaps filled
|
||||
with frog DNA …"
|
||||
fastinit][] by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
|
||||
|
||||

|
||||
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
|
||||

|
||||
*Figure 1: Screenshot of Finit booting [Alpine Linux (HowTo)](https://github.com/troglobit/finit/tree/master/contrib/alpine).*
|
||||
|
||||
Features include:
|
||||
|
||||
@@ -37,7 +35,11 @@ Features include:
|
||||
* Process supervision similar to [systemd][]
|
||||
* Sourcing environment files
|
||||
* 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
|
||||
* Rudimentary [templating support](doc/config.md#templating)
|
||||
* Tooling to enable/disable services
|
||||
* Built-in getty
|
||||
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
|
||||
@@ -45,19 +47,26 @@ Features include:
|
||||
* Cgroups v2, both configuration and monitoring in `initctl top`
|
||||
* Plugin support for customization
|
||||
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
|
||||
* Integration with [watchdogd][] for full system supervision
|
||||
* Logging to kernel ring buffer before `syslogd` has started, see the
|
||||
recommended [sysklogd][] project for complete logging integration
|
||||
and how to log to the kernel ring buffer from scripts using `logger`
|
||||
|
||||
Focus is on small and embedded systems, although Finit is fully usable
|
||||
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/),
|
||||
* [Alpine Linux](contrib/alpine/), and
|
||||
* [Debian GNU/Linux](contrib/debian/)
|
||||
* [Debian GNU/Linux](contrib/debian/), also works on Ubuntu/Linux Mint
|
||||
|
||||
> **Note:** support for various Linux distributions does not mean Finit
|
||||
> installs easily on all architectures. The bundled install scripts are
|
||||
> examples for standard installations, tested on x86_64 systems. Custom
|
||||
> setups, targeted at embedded systems, can be found in [myLinux][].
|
||||
> examples for standard installations, tested on amd64 (x86_64) systems.
|
||||
> Custom setups, e.g., for embedded systems, can be found in any of the
|
||||
> following [Buildroot][] based examples: [myLinux][], [Infix][], or the
|
||||
> plain [br2-finit-demo](https://github.com/troglobit/br2-finit-demo).
|
||||
|
||||
|
||||
Example
|
||||
@@ -66,14 +75,25 @@ Example
|
||||
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
|
||||
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
|
||||
# Fallback if /etc/hostname is missing
|
||||
host default
|
||||
|
||||
# Runlevel to start after bootstrap, 'S', default: 2
|
||||
runlevel 2
|
||||
#runlevel 2
|
||||
|
||||
# Support for setting global environment variables, using foo=bar syntax
|
||||
# be careful though with variables like PATH, SHELL, LOGNAME, etc.
|
||||
#PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
|
||||
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||
@@ -102,6 +122,11 @@ service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
|
||||
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd -- Preparing console
|
||||
|
||||
# Hidden from boot progress, using empty `--` description
|
||||
#sysv [S] /etc/init.d/keyboard-setup --
|
||||
#sysv [S] /etc/init.d/acpid --
|
||||
#task [S] /etc/init.d/kbd --
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
|
||||
@@ -220,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
|
||||
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
|
||||
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
|
||||
`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 can *extend* the functionality of Finit and *hook into* the
|
||||
@@ -268,8 +330,8 @@ unnecessary overhead, which can be removed at build-time using:
|
||||
|
||||
**Cgroups**
|
||||
|
||||
Finit supports cgroups v2 and comes with the following default groups
|
||||
in which services and user sessions are placed in:
|
||||
Finit supports cgroups v2 and comes with the following default groups in
|
||||
which services and user sessions are placed in:
|
||||
|
||||
/sys/fs/cgroup
|
||||
|-- init/ # cpu.weight:100
|
||||
@@ -305,6 +367,10 @@ The `initctl` tool has three commands to help debug and optimize the
|
||||
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
|
||||
commands for details.
|
||||
|
||||
> **Note:** systems that do not support cgroups, specifically version 2,
|
||||
> are automatically detected. On such systems the above functionality
|
||||
> is disabled early at boot.
|
||||
|
||||
|
||||
Runparts & /etc/rc.local
|
||||
------------------------
|
||||
@@ -325,8 +391,34 @@ Right after the runlevel change when all services have started properly,
|
||||
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
|
||||
If it exists and is an executable shell script Finit calls it at the very
|
||||
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
|
||||
in [doc/plugins.md](doc/plugins.md#hooks), and about the system bootstrap
|
||||
in [doc/bootstrap.md](doc/bootstrap.md).
|
||||
in [doc/plugins.md](doc/plugins.md#hooks).
|
||||
|
||||
|
||||
### 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
|
||||
@@ -414,24 +506,25 @@ services, called `initctl`. Unlike `telinit` the `initctl` tool does
|
||||
not return until the given command has fully completed.
|
||||
|
||||
```
|
||||
alpine:~# initctl help
|
||||
Usage: initctl [OPTIONS] [COMMAND]
|
||||
|
||||
Options:
|
||||
-b, --batch Batch mode, no screen size probing
|
||||
-c, --create Create missing paths (and files) as needed
|
||||
-f, --force Ignore missing files and arguments, never prompt
|
||||
-h, --help This help text
|
||||
-j, --json JSON output in 'status' and 'cond' commands
|
||||
-1, --once Only one lap in commands like 'top'
|
||||
-p, --plain Use plain table headings, no ctrl chars
|
||||
-q, --quiet Silent, only return status of command
|
||||
-t, --no-heading Skip table headings
|
||||
-v, --verbose Verbose output
|
||||
-h, --help This help text
|
||||
-V, --version Show program version
|
||||
|
||||
Commands:
|
||||
debug Toggle Finit (daemon) debug
|
||||
help This help text
|
||||
version Show Finit version
|
||||
version Show program version
|
||||
|
||||
ls | list List all .conf in /etc/finit.d
|
||||
create <CONF> Create .conf in /etc/finit.d/available
|
||||
@@ -443,16 +536,21 @@ Commands:
|
||||
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
||||
reload Reload *.conf in /etc/finit.d (activate changes)
|
||||
|
||||
cond set <COND> Set (assert) user-defined condition +usr/COND
|
||||
cond clear <COND> Clear (deassert) 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 clear <COND> Clear (deassert) user-defined conditions -usr/COND
|
||||
cond status Show condition status, default cond command
|
||||
cond dump Dump all conditions and their status
|
||||
cond dump [TYPE] Dump all, or a type of, conditions and their status
|
||||
|
||||
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||
start <NAME>[:ID] Start service by name, with optional ID
|
||||
stop <NAME>[:ID] Stop/Pause a running service by name
|
||||
reload <NAME>[:ID] Reload service by name (SIGHUP or restart)
|
||||
reload <NAME>[:ID] Reload service as if .conf changed (SIGHUP or restart)
|
||||
This allows restart of run/tasks that have already run
|
||||
Note: Finit .conf file(s) are *not* reloaded!
|
||||
restart <NAME>[:ID] Restart (stop/start) service by name
|
||||
signal <NAME>[:ID] <S> Send signal S to service by name, with optional ID
|
||||
ident [NAME] Show matching identities for NAME, or all
|
||||
status <NAME>[:ID] Show service status, by name
|
||||
status Show status of services, default command
|
||||
|
||||
@@ -460,11 +558,15 @@ Commands:
|
||||
ps List processes 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
|
||||
reboot Reboot system
|
||||
halt Halt system
|
||||
poweroff Halt and power off system
|
||||
suspend Suspend system
|
||||
|
||||
utmp show Raw dump of UTMP/WTMP db
|
||||
```
|
||||
|
||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||
@@ -483,24 +585,24 @@ scripting and documentation:
|
||||
|
||||
```
|
||||
alpine:~# initctl -p
|
||||
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] ACPI daemon
|
||||
1509 crond running [--2345----] Cron daemon
|
||||
1489 dropbear running [--2345----] Dropbear SSH daemon
|
||||
1511 klogd running [S12345----] Kernel log daemon
|
||||
1512 ntpd running [--2345----] NTP daemon
|
||||
1473 syslogd running [S12345----] Syslog daemon
|
||||
1506 acpid running [---2345----] ACPI daemon
|
||||
1509 crond running [---2345----] Cron daemon
|
||||
1489 dropbear running [---2345----] Dropbear SSH daemon
|
||||
1511 klogd running [S-12345----] Kernel log daemon
|
||||
1512 ntpd running [---2345----] NTP daemon
|
||||
1473 syslogd running [S-12345----] Syslog daemon
|
||||
|
||||
alpine:~# initctl -pv
|
||||
PID IDENT STATUS RUNLEVELS COMMAND
|
||||
PID IDENT STATUS RUNLEVELS COMMAND
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] acpid -f
|
||||
1509 crond running [--2345----] crond -f -S $CRON_OPTS
|
||||
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
|
||||
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
|
||||
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
|
||||
1473 syslogd running [S12345----] syslogd -n
|
||||
1506 acpid running [---2345----] acpid -f
|
||||
1509 crond running [---2345----] crond -f -S $CRON_OPTS
|
||||
1489 dropbear running [---2345----] dropbear -R -F $DROPBEAR_OPTS
|
||||
1511 klogd running [S-12345----] klogd -n $KLOGD_OPTS
|
||||
1512 ntpd running [---2345----] ntpd -n $NTPD_OPTS
|
||||
1473 syslogd running [S-12345----] syslogd -n
|
||||
```
|
||||
|
||||
The environment variables to each of the services above are read from,
|
||||
@@ -525,7 +627,7 @@ Condition(s):
|
||||
User : root
|
||||
Group : root
|
||||
Uptime : 2 hour 46 min 56 sec
|
||||
Runlevels : [--2345----]
|
||||
Runlevels : [---2345----]
|
||||
Memory : 1.2M
|
||||
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|
||||
|- 1485 dropbear -R -F
|
||||
@@ -596,14 +698,22 @@ and proposed extensions.
|
||||
[upstart]: https://upstart.ubuntu.com/
|
||||
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||
[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
|
||||
[tmpfiles.d(5)]: https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
|
||||
[original finit]: http://helllabs.org/finit/
|
||||
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
||||
[Joachim Wiberg]: https://troglobit.com
|
||||
[Buildroot]: https://buildroot.org
|
||||
[Infix]: https://kernelkit.github.io
|
||||
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
|
||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||
[GitHub]: https://github.com/troglobit/myLinux/
|
||||
[myLinux]: https://github.com/troglobit/myLinux/
|
||||
[watchdogd]: https://github.com/troglobit/watchdogd/
|
||||
[sysklogd]: https://github.com/troglobit/sysklogd/
|
||||
[GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
|
||||
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
|
||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||
|
||||
@@ -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
|
||||
+93
-19
@@ -1,10 +1,11 @@
|
||||
AC_INIT(Finit, 4.3-rc2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/projects/finit/)
|
||||
AC_INIT([Finit], [4.7], [https://github.com/troglobit/finit/issues],
|
||||
[finit], [https://troglobit.com/projects/finit/])
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/finit.c])
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_FILES([Makefile
|
||||
contrib/Makefile
|
||||
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
|
||||
@@ -14,8 +15,10 @@ AC_CONFIG_FILES([Makefile
|
||||
man/Makefile
|
||||
plugins/Makefile
|
||||
src/Makefile
|
||||
system/Makefile system/10-hotplug.conf
|
||||
test/test.env test/Makefile
|
||||
test/tenv/Makefile test/common/Makefile])
|
||||
test/lib/Makefile test/src/Makefile
|
||||
tmpfiles.d/Makefile])
|
||||
|
||||
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
||||
#m4_include([m4/plugin.m4])
|
||||
@@ -32,7 +35,6 @@ AC_PROG_INSTALL
|
||||
AC_PROG_MKDIR_P
|
||||
|
||||
# Configuration.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h])
|
||||
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r])
|
||||
|
||||
@@ -70,7 +72,7 @@ AC_ARG_ENABLE(redirect,
|
||||
enable_redirect=yes])
|
||||
|
||||
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])
|
||||
|
||||
AC_ARG_ENABLE(doc,
|
||||
@@ -81,22 +83,33 @@ AC_ARG_ENABLE(contrib,
|
||||
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
|
||||
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
|
||||
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([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([modprobe], [yes], [Coldplug modules using modalias magic])
|
||||
AC_PLUGIN([modprobe], [no], [Coldplug modules using modalias magic])
|
||||
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
|
||||
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
|
||||
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
|
||||
AC_PLUGIN([hook-scripts], [no], [Trigger script execution from hook points])
|
||||
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
|
||||
AC_PLUGIN([hotplug], [yes], [Start udevd or mdev kernel event datamon])
|
||||
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
|
||||
AC_PLUGIN([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([testserv], [no], [Test plugin to start test serv daemon])
|
||||
|
||||
# Check for extra arguments or packages
|
||||
AC_ARG_WITH([bash-completion-dir],
|
||||
AS_HELP_STRING([--with-bash-completion-dir[=PATH]], [Directory to install Bash completion, default: auto]),
|
||||
[bash_dir=$withval], [bash_dir=yes])
|
||||
|
||||
AC_ARG_WITH(fstab,
|
||||
AS_HELP_STRING([--with-fstab=FILE], [System default fstab, default $sysconfdir/fstab]),
|
||||
[fstab=$withval], [fstab=yes])
|
||||
@@ -141,16 +154,23 @@ AC_ARG_WITH(keventd,
|
||||
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
|
||||
|
||||
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,
|
||||
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=])
|
||||
|
||||
AC_ARG_WITH(hook-scripts-path,
|
||||
AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]),
|
||||
[hook_scripts_path=$withval], [hook_scripts_path=yes])
|
||||
|
||||
AC_ARG_WITH(plugin-path,
|
||||
AS_HELP_STRING([--with-plugin-path=DIR], [Search path for external plugins, default /usr/lib/finit/plugins:/usr/local/lib/finit/plugins]),
|
||||
[plugin_path=$withval], [plugin_path=yes])
|
||||
|
||||
AC_ARG_WITH(rtc-date,
|
||||
AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM", default "2000-01-01 00:00"]),
|
||||
[rtc_date=$withval], [rtc_date=no])
|
||||
|
||||
### Enable features ###########################################################################
|
||||
|
||||
@@ -174,9 +194,18 @@ AS_IF([test "x$enable_redirect" = "xyes"], [
|
||||
AS_IF([test "x$enable_logrotate" != "xno"], [
|
||||
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"])
|
||||
|
||||
### With features ##############################################################################
|
||||
AS_IF([test "x$bash_dir" = "xyes"], [
|
||||
PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
|
||||
[BASH_DIR="$(pkg-config --variable=completionsdir bash-completion)"],
|
||||
[BASH_DIR="$datadir/bash-completion/completions"])], [
|
||||
BASH_DIR="$bash_dir"])
|
||||
|
||||
AS_IF([test "x$with_fstab" != "xno"], [
|
||||
AS_IF([test "x$fstab" = "xyes"], [fstab=$sysconfdir/fstab])
|
||||
AC_EXPAND_DIR(fstab_path, "$fstab")
|
||||
@@ -235,9 +264,18 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
||||
AC_EXPAND_DIR(random_path, "$random_seed")
|
||||
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
|
||||
|
||||
AS_IF([test "x$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_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$watchdog" = "xyes"], [
|
||||
@@ -246,37 +284,70 @@ AS_IF([test "x$with_watchdog" != "xno"], [
|
||||
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
|
||||
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])
|
||||
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
|
||||
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
|
||||
|
||||
AS_IF([test "x$with_plugin_path" != "xno"], [
|
||||
AS_IF([test "x$plugin_path" = "xyes"], [plugin_path=/usr/lib/finit/plugins:/usr/local/lib/finit/plugins])
|
||||
AC_EXPAND_DIR(plugin_path, "$plugin_path")
|
||||
AC_DEFINE_UNQUOTED(EXTERNAL_PLUGIN_PATH, "$plugin_path", [Built-in default: /usr/lib/finit/plugins:/usr/local/lib/finit/plugins])])
|
||||
|
||||
# Control build with automake flags
|
||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(BASH, [test "x$bash_dir" != "xno"])
|
||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
|
||||
AM_CONDITIONAL(SULOGIN, [test "x$with_sulogin" != "xno"])
|
||||
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
|
||||
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
|
||||
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
|
||||
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"])
|
||||
|
||||
# Workaround for as-of-yet unreleased runstatedir support, planned for
|
||||
# autoconf 2.70, which some major distros have backported.
|
||||
AS_IF([test -z "$runstatedir"], runstatedir="$localstatedir/run")
|
||||
AC_SUBST(runstatedir)
|
||||
|
||||
# Override default libdir, used for plugins and rescue.conf
|
||||
pkglibdir=$libdir/finit
|
||||
AC_SUBST(pkglibexecdir)
|
||||
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(finit_runpath, "$runstatedir/finit/system")
|
||||
AC_DEFINE_UNQUOTED(FINIT_RUNPATH_, "$finit_runpath", [Finit runtime dynamic system services])
|
||||
|
||||
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
|
||||
AC_EXPAND_DIR(plugin_path, "$pkglibdir/plugins")
|
||||
AC_SUBST(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_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_EXPAND_DIR(bash_completion_dir, "$BASH_DIR")
|
||||
AC_SUBST(BASH_DIR, "$bash_completion_dir")
|
||||
|
||||
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
|
||||
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
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -301,17 +372,20 @@ cat <<EOF
|
||||
Behavior:
|
||||
Boot heading..........: $heading
|
||||
Plugins...............: $plugins
|
||||
RTC restore date......: $RTC_DATE
|
||||
|
||||
Optional features:
|
||||
Install doc/..........: $enable_doc
|
||||
Install contrib/......: $enable_contrib
|
||||
Bash completion.......: $bash_completion_dir
|
||||
Built-in keventd......: $with_keventd
|
||||
Built-in sulogin......: $with_sulogin
|
||||
Built-in sulogin......: $with_sulogin $sulogin
|
||||
Built-in watchdogd....: $with_watchdog $watchdog
|
||||
Built-in logrotate....: $enable_logrotate
|
||||
Skip fsck check.......: $enable_fastboot
|
||||
Run fsck fix mode.....: $enable_fsckfix
|
||||
Redirect output.......: $enable_redirect
|
||||
Rescue mode...........: $enable_rescue
|
||||
Default hostname......: $hostname
|
||||
Default group.........: $group
|
||||
Default runlevel......: $runlevel
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
HowTo: Finit on Alpine Linux 3.4-3.6
|
||||
====================================
|
||||
HowTo: Finit on Alpine Linux 3.4...3.19
|
||||
=======================================
|
||||
|
||||
HowTo use Finit to boot an [Alpine Linux][] system. It is assumed that
|
||||
the user has already installed make, a compiler, C library header files,
|
||||
|
||||
@@ -17,8 +17,8 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
||||
--prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin --with-watchdog \
|
||||
--with-keventd
|
||||
--enable-alsa-utils-plugin --without-watchdog \
|
||||
--with-keventd --with-sulogin
|
||||
|
||||
echo
|
||||
echo "*** Building ..."
|
||||
@@ -35,4 +35,3 @@ fi
|
||||
echo
|
||||
echo "*** Done"
|
||||
echo
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
SUBDIRS = available
|
||||
docsdir := @docdir@/contrib/alpine/finit.d
|
||||
d_LINKS = acpid.conf crond.conf dropbear.conf getty.conf \
|
||||
keymap.conf modules.conf ntpd.conf \
|
||||
klogd.conf syslogd.conf
|
||||
keymap.conf modules.conf ntpd.conf syslogd.conf
|
||||
|
||||
install-exec-hook:
|
||||
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
docsdir := @docdir@/contrib/alpine/finit.d/available
|
||||
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
||||
lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf
|
||||
lxdm.conf modules.conf ntpd.conf syslogd.conf watchdog.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
#module evdev
|
||||
#module mousedev
|
||||
#module 8139cp
|
||||
module softdog
|
||||
#module softdog
|
||||
|
||||
+35
-29
@@ -1,45 +1,53 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "x`id -u`" != "x0" ]; then
|
||||
echo
|
||||
echo "*** This script must run as root"
|
||||
echo
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
printf "\n\e[1m*** This script must run as root\e[0m\n\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC3037,SC3045,SC2154
|
||||
yorn()
|
||||
{
|
||||
printf "\e[1m> %s (y/N)?\e[0m " "$*"
|
||||
read -rn1 yorn
|
||||
echo
|
||||
|
||||
if [ "$yorn" = "y" ] || [ "$yorn" = "Y" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Adjust base directory
|
||||
grep -q alpine Makefile 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
if grep -q contrib/alpine Makefile 2>/dev/null; then
|
||||
cd ../..
|
||||
elif [ ! -e configure.ac ]; then
|
||||
echo "*** Please run this script from the Finit base directory."
|
||||
fi
|
||||
|
||||
if [ ! -e configure.ac ]; then
|
||||
printf "\n\e[1m*** Please run this script from the Finit base directory.\e[0m\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "*** Install Finit on Alpine Linux"
|
||||
echo "========================================================================"
|
||||
echo "/sbin/finit - PID 1"
|
||||
echo "/lib/finit/plugins/* - All enabled Finit plugins"
|
||||
echo "/etc/finit.conf - Finit configuration file"
|
||||
echo "/etc/finit.d/ - Finit services"
|
||||
echo
|
||||
read -p "Do you want to continue (y/N)? " yorn
|
||||
printf "\n\e[1mInstall Finit on Alpine Linux\n"
|
||||
printf "========================================================================\e[0m\n"
|
||||
printf "/sbin/finit - PID 1\n"
|
||||
printf "/lib/finit/plugins/* - All enabled Finit plugins\n"
|
||||
printf "/etc/finit.conf - Finit configuration file\n"
|
||||
printf "/etc/finit.d/ - Finit services\n"
|
||||
|
||||
if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
|
||||
echo
|
||||
echo "*** Installing Finit files ..."
|
||||
if yorn "Do you want to continue"; then
|
||||
printf "\n\e[1m*** Installing Finit files ...\e[0m\n"
|
||||
make install
|
||||
cd /usr/share/doc/finit/contrib/alpine
|
||||
cd /usr/share/doc/finit/contrib/alpine || exit 1
|
||||
for file in finit.conf rc.local; do
|
||||
install -vbD $file /etc/$file
|
||||
done
|
||||
cp -va finit.d /etc/
|
||||
|
||||
read -p "*** Install Finit as the system default Init (y/N)? " yorn
|
||||
if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
|
||||
echo "*** Updating /sbin/init symlink --> finit ..."
|
||||
cd /sbin
|
||||
if yorn "Install Finit as the system default Init"; then
|
||||
printf "\e[1m*** Updating /sbin/init symlink --> finit ...\e[0m\n"
|
||||
cd /sbin || exit 1
|
||||
rm init
|
||||
ln -s finit init
|
||||
|
||||
@@ -48,10 +56,8 @@ if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
|
||||
ln -s reboot shutdown
|
||||
ln -s reboot suspend
|
||||
fi
|
||||
echo "*** Done"
|
||||
printf "\n\e[1m*** Done\e[0m\n"
|
||||
echo
|
||||
else
|
||||
echo
|
||||
echo "*** Aborting install."
|
||||
echo
|
||||
printf "\n\e[1m*** Aborting install.\e[0m\n\n"
|
||||
fi
|
||||
|
||||
@@ -59,6 +59,3 @@ else
|
||||
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
|
||||
echo
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin --with-watchdog \
|
||||
--with-keventd
|
||||
--with-keventd --with-sulogin
|
||||
|
||||
echo
|
||||
echo "*** Building ..."
|
||||
@@ -35,4 +35,3 @@ fi
|
||||
echo
|
||||
echo "*** Done"
|
||||
echo
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
Bootstrap
|
||||
=========
|
||||
|
||||
0. Get bearings, disable kernel logs, initialize TTY and print banner,
|
||||
unless `HOOK_BANNER` is defined, then call that instead
|
||||
1. Prepare emergency shell, if enabled
|
||||
2. Set up initial signal handlers
|
||||
3. Set up default $PATH early
|
||||
4. Mount `/proc` and `/sys`
|
||||
5. Check kernel command line for `debug` to figure out log level
|
||||
6. Load all `.so` plugins
|
||||
7. Call `fsck` on file systems listed in `/etc/fstab`
|
||||
8. Populate `/dev` using either udev or mdev, depending on system type
|
||||
9. Parse `/etc/finit.conf`
|
||||
10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This
|
||||
means any WDT that requires a kernel module need to be either
|
||||
compiled into the kernel, or insmod'ed in `/etc/finit.conf`
|
||||
11. Load all `/etc/finit.d/*.conf` files and set hostname
|
||||
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
|
||||
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
|
||||
14. Mount all file systems listed in `/etc/fstab` and swap, if available.
|
||||
On mount error `HOOK_MOUNT_ERROR` is called. After mount, regardless
|
||||
of error, `HOOK_MOUNT_POST` is called
|
||||
15. Enable SysV init signals
|
||||
16. Call 2nd level hooks, `HOOK_BASEFS_UP`
|
||||
17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin
|
||||
18. Load kernel params from `/etc/sysctl.d/*.conf`, `/etc/sysctl.conf`
|
||||
et al. (Supports all locations that SysV init does.), handled by
|
||||
`procps` plugin
|
||||
19. Start all 'S' runlevel tasks and services
|
||||
20. Bring up loopback interface and all `/etc/network/interfaces`, if
|
||||
the `.conf` setting `network <SCRIPT>` is set, it is called instead
|
||||
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
||||
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
||||
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
||||
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
|
||||
(re)loaded and new services, tasks, and blocking run commands are
|
||||
started. Provided they are allowed in the new runlevel and all of
|
||||
their conditions, if any, are set.
|
||||
24. Call 4th level hooks, `HOOK_SVC_UP`
|
||||
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
||||
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||
27. Start all configured TTYs
|
||||
|
||||
In (19) and (23) tasks and services defined in `/etc/finit.conf` and
|
||||
`/etc/finit.d/*.conf` are started.
|
||||
|
||||
Notice the seven hook points that are called at various point in the
|
||||
bootstrap process. This is where plugins can extend the boot in any
|
||||
way they please. There are other hook points available, for more on
|
||||
this, see [plugins.md](plugins.md).
|
||||
|
||||
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
|
||||
USB stick, convert/copy its contents to the system's `/etc/` directory,
|
||||
well before all 'S' runlevel tasks are started. This could be used with
|
||||
system images that are created read-only and all configuration is stored
|
||||
on external media.
|
||||
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
+1
-1
@@ -65,7 +65,7 @@ Below are a few of the main switches to configure:
|
||||
|
||||
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
|
||||
|
||||
* `--with-sulgin`: Enable bundled `sulogin` program. Default is to use the
|
||||
* `--with-sulogin`: Enable bundled `sulogin` program. Default is to use the
|
||||
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
|
||||
login if the `root` user is disabled or has no password at all.
|
||||
|
||||
|
||||
+37
-4
@@ -12,6 +12,36 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
* `debug`: Enable kernel debug. Debug messages are printed to the
|
||||
console until Finit starts up, unless `loglevel=7` (below) is used.
|
||||
|
||||
* `fsck.mode=<auto,force,skip>`, default: `auto`, unless built with
|
||||
`configure --enable-fastboot`, in which case the default is `skip`
|
||||
|
||||
* `fsck.repair=<preen,yes,no>`, default: `preen`, unless built with
|
||||
`configure --enable-fsckfix`, in which case the default is `yes`.
|
||||
This configure option also sets `fsck.mode=force`, unless fastboot
|
||||
is selected at the same time. I.e., the `--enable-fastboot` build
|
||||
option overrides the `fsck.mode` default value.
|
||||
|
||||
* `finit.cond=foo[,bar[,baz]]`: set `<boot/foo>` condition, optionally
|
||||
multiple conditions can be set using the same option, separated with a
|
||||
comma. Alternatively, multiple `foo.cond=arg` can be given. Each will
|
||||
result in a `<boot/arg>` condition being set to control the rest of the
|
||||
system bootstrap.
|
||||
|
||||
Very useful for selecting different boot modes, e.g. manufacturing test,
|
||||
firmware upgrade, or rescue mode.
|
||||
|
||||
> Note: `<boot/...>` conditions cannot be cleared with `initctl`!
|
||||
|
||||
* `finit.config=/path/to/alternative/finit.conf`: override the
|
||||
compile-time bootstrap config file, default:
|
||||
|
||||
./configure --with-config=/etc/finit.conf
|
||||
|
||||
Useful when starting up in various rescue mode, factory, or
|
||||
production test setups. Use the top-level configuration file
|
||||
directive `rcsd /path/to/finit.d` to override the default
|
||||
rcS.d directory.
|
||||
|
||||
* `finit.debug[=bool]`: Enable finit debug. This is operated
|
||||
independently of the kernel `debug` setting. New as of Finit v4.
|
||||
|
||||
@@ -65,13 +95,16 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
the system booted in a limited fallback mode. See [config.md][]
|
||||
for more information.
|
||||
|
||||
This option can be disabled with `configure --without-rescue`
|
||||
|
||||
**Note:** in this mode `initctl` will not work. Correct the problem
|
||||
and use `reboot -f` to force reboot.
|
||||
|
||||
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
|
||||
debug problems with the regular runlevel. All services and TTYs in
|
||||
`[1]` will be started, so a `tty [1] @console nologin` configuration
|
||||
presents you with a root console without login.
|
||||
* `single`, or `S`: Overrides the configured runlevel (default: 2) to go
|
||||
to after bootstrap by forcing it to runlevel 1, this is also known
|
||||
as single user mode. Useful to debug startup problems. All services
|
||||
and TTYs in `[1]` will be started, so a `tty [1] @console nologin`
|
||||
configuration presents you with a root console without login.
|
||||
|
||||
* `1-9`, except `6`: override the configured `runlevel`. Like the `S`
|
||||
and `rescue`, giving a single number on the kernel command line tells
|
||||
|
||||
+73
-19
@@ -13,9 +13,9 @@ Finit Conditions
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Conditions are a new addition to Finit, introduced in v3, with the
|
||||
intention of providing a mechanism for common synchronization problems.
|
||||
For example:
|
||||
Conditions are a relatively new addition to Finit, introduced in v3,
|
||||
with the intention of providing a mechanism for common synchronization
|
||||
problems. For example:
|
||||
|
||||
- *"wait for service A to start before starting service B"*, or
|
||||
- *"wait for basic network access to be available"*
|
||||
@@ -27,6 +27,16 @@ specified separated by comma. Multiple conditions are logically AND'ed
|
||||
during evaluation, i.e. all conditions must be satisfied in order for a
|
||||
service to run.
|
||||
|
||||
A special syntax, using a leading `!` in run/task/service conditions,
|
||||
denote if a:
|
||||
|
||||
- service does not support `SIGHUP`
|
||||
- run/task should not block runlevel changes (i.e., bootstrap)
|
||||
|
||||
Finit guarantees by default that all run/tasks run (at least) once
|
||||
per runlevel. For most tasks this is a good default, for example
|
||||
checking SSH host keys or loading keymap. However, for conditions
|
||||
that are unlikely to happen it is not. (See example below.)
|
||||
|
||||
### Example
|
||||
|
||||
@@ -35,22 +45,40 @@ service to run.
|
||||
In this example the Network monitor daemon `netd` is not started until
|
||||
both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A
|
||||
`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
|
||||
asserted. I.e., if the Zebra process above stops or restarts, netd
|
||||
will also stop or restart.
|
||||
|
||||
Another example is `dropbear`, it does not support `SIGHUP`, but we can
|
||||
also see optional sourcing of arguments from an environment file:
|
||||
|
||||
service [2345789] <!> env:-/etc/default/dropbear dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
|
||||
|
||||
Finally, the weird "block runlevel changes" example. Here we see what
|
||||
happens when Finit receives `SIGPWR`, sent from a power daemon like
|
||||
[powstatd(8)][]. A condition is asserted and a user can set up their
|
||||
own task to act on it. We do not want this task to block Finit from
|
||||
moving to the next runlevel after bootstrap, so we set `<!>`:
|
||||
|
||||
task [S0123456789] <!sys/pwr/fail> name:pwrfail initctl poweroff -- Power failure, shutting down
|
||||
|
||||
|
||||
Triggering
|
||||
----------
|
||||
|
||||
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
|
||||
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
|
||||
also supports user-defined conditions, sometimes referred to as static
|
||||
or one-shot conditions. They live in the `<usr/...` namespace and are
|
||||
constrained to a flat hierarchy without any sub-directories, unlike the
|
||||
pidfile plugin, for instance.
|
||||
|
||||
- `netlink.so`: provides `<net/...>`
|
||||
- `pidfile.so`: provides `<pid/...>`
|
||||
- Cmdline `finit.cond=arg`: provides `<boot/arg>`
|
||||
|
||||
See below for built-in conditions. Finit also supports user-defined
|
||||
conditions, sometimes referred to as static or one-shot conditions.
|
||||
They live in the `<usr/...>` namespace and are constrained to a flat
|
||||
hierarchy without any sub-directories, unlike the pidfile plugin, for
|
||||
instance.
|
||||
|
||||
User-defined conditions are controlled using the `initctl cond set` and
|
||||
`initctl cond clear` commands:
|
||||
@@ -87,15 +115,35 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||
Built-in Conditions
|
||||
-------------------
|
||||
|
||||
Finit comes with a set of plugins for conditions: `pidfile`, `netlink`,
|
||||
`sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/`
|
||||
(recursively) for PID files created by the monitored services, and sets
|
||||
a corresponding condition in the `pid/` namespace. Similarly, the
|
||||
`netlink` plugin provides basic conditions for when an interface is
|
||||
brought up/down and when a default route (gateway) is set, in the `net/`
|
||||
namespace. The `sys` and `usr` plugins monitor are passive condition
|
||||
monitors where the action is provided by `keventd`, signal handlers,
|
||||
and in the case of `usr`, the end-user via the `initctl` tool
|
||||
Finit comes with a set of plugins for conditions:
|
||||
|
||||
- `devmon` (built-in)
|
||||
- `netlink`
|
||||
- `pidfile`
|
||||
- `sys`
|
||||
- `usr`
|
||||
|
||||
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`
|
||||
daemon until `setupd` and `zebra` has started *and* created their PID
|
||||
@@ -114,10 +162,14 @@ Built-in conditions:
|
||||
- `net/<IFNAME>/exist`
|
||||
- `net/<IFNAME>/up`
|
||||
- `net/<IFNAME>/running`
|
||||
- `service/<NAME[:ID]>/<STATE>`
|
||||
- `{run, task, sysv}/<NAME[:ID]>/{<STATE>, success, failure}`
|
||||
- `sys/pwr/ac`
|
||||
- `sys/pwr/fail`
|
||||
- `sys/key/ctrlaltdel`
|
||||
- `usr/foo`
|
||||
- `boot/arg`
|
||||
- `dev/node` and `dev/dir/node`
|
||||
|
||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
||||
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
||||
@@ -163,7 +215,7 @@ Debugging
|
||||
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
|
||||
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
|
||||
conditions that are not satisfied. Listed as `off` below.
|
||||
@@ -231,3 +283,5 @@ Therefore, any plugin that supplies Finit with conditions must ensure
|
||||
that their state is updated after each reconfiguration. This can be
|
||||
done by binding to the `HOOK_SVC_RECONF` hook. For an example of how
|
||||
to do this, see `plugins/pidfile.c`.
|
||||
|
||||
[powstatd(8)]: https://manpages.ubuntu.com/manpages/trusty/en/man8/powstatd.8.html
|
||||
|
||||
+696
-83
@@ -1,14 +1,21 @@
|
||||
Configuration
|
||||
=============
|
||||
Finit Configuration
|
||||
===================
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Configuration Files](#configuration-files)
|
||||
* [Runlevels](#runlevels)
|
||||
* [Managing Services](#managing-services)
|
||||
* [Environment Variables](#environment-variables)
|
||||
* [Service Environment](#service-environment)
|
||||
* [Service Synchronization](#service-synchronization)
|
||||
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||
* [Templating](#templating)
|
||||
* [Cgroups](#cgroups)
|
||||
* [Configuration File Syntax](#configuration-file-syntax)
|
||||
* [Hostname](#hostname)
|
||||
* [Kernel Modules](#kernel-modules)
|
||||
* [Networking](#networking)
|
||||
* [Alternate rcS.d/](#alternate-rcs-d-)
|
||||
* [Resource Limits](#resource-limits)
|
||||
* [Runlevels](#runlevels)
|
||||
* [One-shot Commands (sequence)](#one-shot-commands--sequence-)
|
||||
@@ -21,6 +28,7 @@ Configuration
|
||||
* [TTYs and Consoles](#ttys-and-consoles)
|
||||
* [Non-privileged Services](#non-privileged-services)
|
||||
* [Redirecting Output](#redirecting-output)
|
||||
* [SysV Init Compatibility](#sysv-init-compatibility)
|
||||
* [Rescue Mode](#rescue-mode)
|
||||
* [Limitations](#limitations)
|
||||
* [/etc/finit.conf](#etcfinitconf)
|
||||
@@ -32,29 +40,219 @@ Configuration
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Finit can be configured using only the original `/etc/finit.conf` file
|
||||
or in combination with `/etc/finit.d/*.conf`. Useful for package-based
|
||||
Linux distributions -- each package can provide its own "script" file.
|
||||
Finit is a process starter and service monitor designed to run as PID 1
|
||||
on Linux. It consists of [plugins](plugins.md) and configuration files.
|
||||
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
|
||||
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
||||
> **Tip:** see [SysV Init Compatibility](#sysv-init-compatibility) for
|
||||
> 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
|
||||
`initctl reload`. (It is also possible to `SIGHUP` to PID 1, or call
|
||||
### Configuration Files
|
||||
|
||||
Originally Finit was configured using a single file, `/etc/finit.conf`,
|
||||
and although still possible to use a single configuration file, today
|
||||
the following 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
|
||||
|- lib
|
||||
| `- finit/
|
||||
| `- system/
|
||||
| |- 10-hotplug.conf
|
||||
| `- ...
|
||||
`- run/
|
||||
`- finit/
|
||||
`- system/
|
||||
|- dbus.conf
|
||||
|- keventd.conf
|
||||
|- runparts.conf
|
||||
|- watchdogd.conf
|
||||
`- ...
|
||||
|
||||
Configuration files in `/etc` are provided by the user, or projects like
|
||||
[finit-skel](https://github.com/troglobit/finit-skel) and extended by
|
||||
the user.
|
||||
|
||||
The files in `/lib/finit/system/*.conf` are system critical services and
|
||||
setup provided by Finit, e.g. udev/mdev that must run very early at system
|
||||
bootstrap. This system directory was introduced in Finit v4.4 to replace
|
||||
the hard-coded services provided by plugins before. All .conf files in this
|
||||
directory be either replaced by a system administrator or overridden by a
|
||||
file with the same name in `/etc/finit.d/`.
|
||||
|
||||
The files in `/run/finit/system/*.conf` are created by plugins and Finit
|
||||
bundled services like runparts, the [watchdog](#watchdog), and `keventd`
|
||||
if they are enabled. Like `/lib/finit/system/*.conf`, these files can
|
||||
be overridden by file with the same name in `/etc/finit.d/`.
|
||||
|
||||
Services in the `available/` and `enabled/` sub-directories are called
|
||||
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 `/lib/finit/system/*.conf`, `finit.d/*.conf`,
|
||||
and finally all `finit.d/enabled/*.conf` files. Each directory is a
|
||||
unique group, where files within each group are sorted alphabetically.
|
||||
|
||||
**Example:**
|
||||
|
||||
/lib/finit/system/10-hotplug.conf
|
||||
/lib/finit/system/90-testserv.conf
|
||||
/run/finit/system/dbus.conf
|
||||
/run/finit/system/runparts.conf
|
||||
/etc/finit.d/10-abc.conf
|
||||
/etc/finit.d/20-abc.conf
|
||||
/etc/finit.d/enabled/1-aaa.conf
|
||||
/etc/finit.d/enabled/1-abc.conf
|
||||
|
||||
The resulting combined configuration is read line by line, each `run`,
|
||||
`task`, and `service` added to an ordered list that ensures they are
|
||||
started in the same order. This is important because of the blocking
|
||||
properties of the `run` statement. For an example on the relation of
|
||||
`service` and `run` statements, and dependency handling between them,
|
||||
see [Conditional Loading](#conditional-loading), below.
|
||||
|
||||
> **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 by mounting the critical file systems `/dev`, `/proc/`, and
|
||||
`/sys`, unless they are already mounted. When all plugins and other,
|
||||
core Finit functions, have been set up, all relevant filesystems (where
|
||||
`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.
|
||||
|
||||
To provide a smooth ride, file system not listed in the given `fstab`,
|
||||
e.g. `/tmp` and `/run`, are automatically mounted by Finit, as listed
|
||||
above, provided their respective mount point exists.
|
||||
|
||||
With all filesystems mounted, Finit calls `swapon`.
|
||||
|
||||
> **Tip:** to see what happens when all filesystems are mounted, have a
|
||||
> look at the [`bootmisc.so` plugin](plugins.md).
|
||||
|
||||
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), 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, or by a script
|
||||
running in runlevel S that calls, e.g., `initctl runlevel 9`. The
|
||||
latter is useful if startup scripts detect problems outside of Finit's
|
||||
control, e.g., critical services/devices missing or hardware problems.
|
||||
|
||||
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
|
||||
monitors all known active `.conf` files, so if you want to force a
|
||||
restart of any service you can touch its corresponding `.conf` file in
|
||||
`/etc/finit.d` and call `initctl reload`. Finit handle any and all
|
||||
conditions and dependencies between services automatically.
|
||||
|
||||
It is also possible to drop `.conf` files in `/etc/finit.d/available/`
|
||||
and use `initctl enable` to enable a service `.conf` file. This may be
|
||||
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.
|
||||
`/etc/finit.d` and call `initctl reload`. Finit handles all conditions
|
||||
and dependencies between services automatically, see below section on
|
||||
[Service Synchronization](#service-synchronization) for more details.
|
||||
|
||||
On `initctl reload` the following is checked for all services:
|
||||
|
||||
@@ -68,10 +266,6 @@ On `initctl reload` the following is checked for all services:
|
||||
For more info on the different states of a service, see the separate
|
||||
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
|
||||
|
||||
@@ -80,10 +274,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
|
||||
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
|
||||
baz="qux"
|
||||
set foo=bar
|
||||
set baz="qux"
|
||||
|
||||
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`
|
||||
@@ -131,6 +327,81 @@ the `ps` command we can see that the process is started with:
|
||||
> `-` 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 style readiness
|
||||
notification is available, and the native PID file mode of operation is,
|
||||
as of Finit v4.6 optional, by default it is still enabled, but this can
|
||||
be changed in `finit.conf`:
|
||||
|
||||
readiness none
|
||||
|
||||
This will be made the default in Finit 5.0. In this mode of operation,
|
||||
every service needs to explicitly declare their readiness notification,
|
||||
like this:
|
||||
|
||||
service notify:pid watchdogd
|
||||
service notify:systemd foo
|
||||
service notify:s6 bar
|
||||
service notify:none qux
|
||||
|
||||
The `notify:none` syntax is for completeness in systems which run in
|
||||
`readiness pid` mode (default). Services declared with `notify:none`
|
||||
will assert ready condition as soon as Finit has started them, e.g.,
|
||||
`service/qux/ready`.
|
||||
|
||||
To synchronize two services the following condition can be used:
|
||||
|
||||
service notify:pid watchdogd
|
||||
service <service/watchdogd/ready> stress-ng --cpu 8
|
||||
|
||||
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
|
||||
-----------------------
|
||||
|
||||
@@ -163,12 +434,32 @@ example employs a wrapper script in `/etc/start.d`.
|
||||
> 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
|
||||
-------
|
||||
|
||||
There are three major cgroup configuration directives:
|
||||
|
||||
1. Global top-level group: init, system, user, or a custom group
|
||||
1. Global top-level group: `init`, `system`, `user`, or a custom group
|
||||
2. Selecting a top-level group for a set of run/task/services
|
||||
3. Per run/task/service limits
|
||||
|
||||
@@ -219,6 +510,10 @@ except for expanding the shorthand `mem.` to `memory.`, if the file is
|
||||
not available, either the cgroup controller is not available in your
|
||||
Linux kernel, or the name is misspelled.
|
||||
|
||||
A daemon using `SCHED_RR` currently need to run outside the default cgroups.
|
||||
|
||||
service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process
|
||||
|
||||
> Linux cgroups and details surrounding values are not explained in the
|
||||
> Finit documentation. The Linux admin-guide cover this well:
|
||||
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
|
||||
@@ -227,6 +522,31 @@ Linux kernel, or the name is misspelled.
|
||||
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
|
||||
|
||||
**Syntax:** `host <NAME>`, or `hostname <NAME>`
|
||||
@@ -236,7 +556,7 @@ the contents of that file is used.
|
||||
|
||||
Deprecated. We recommend using `/etc/hostname` instead.
|
||||
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
|
||||
|
||||
### Kernel Modules
|
||||
@@ -254,7 +574,7 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
|
||||
|
||||
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
|
||||
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
|
||||
|
||||
### Networking
|
||||
@@ -267,9 +587,30 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
|
||||
or `/etc/network/interfaces` if you have a system with `ifupdown`, like
|
||||
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
||||
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
**Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>`
|
||||
@@ -301,46 +642,70 @@ stanzas can share the same rlimits if they are in the same .conf.
|
||||
|
||||
**Syntax:** `runlevel <N>`
|
||||
|
||||
The system runlevel to go to after [bootstrap][] (S) has completed. `N`
|
||||
The system runlevel to go to after bootstrap (S) has completed. `N`
|
||||
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
||||
halt.
|
||||
|
||||
Default: 2
|
||||
It is recommended to keep runlevel 1 as single-user mode, because
|
||||
Finit disables networking in this mode.
|
||||
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
*Default:* 2
|
||||
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
|
||||
|
||||
### One-shot Commands (sequence)
|
||||
|
||||
**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.
|
||||
|
||||
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)
|
||||
|
||||
**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.
|
||||
|
||||
Both `run` and `task` commands are run in a shell, so pipes and
|
||||
redirects can be freely used:
|
||||
Both `run` and `task` commands are run in a shell, so basic pipes and
|
||||
redirects can be used:
|
||||
|
||||
task [s] echo "foo" | cat >/tmp/bar
|
||||
|
||||
> `<COND>` is described in the [Services](#services) section.
|
||||
Please note, `;`, `&&`, `||`, and similar are *not supported*. Any
|
||||
non-trivial constructs are better placed in a separate shell script.
|
||||
|
||||
|
||||
### SysV Init Scripts
|
||||
|
||||
**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
|
||||
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.
|
||||
@@ -358,7 +723,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
|
||||
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
|
||||
@@ -419,10 +784,56 @@ modified with an optional `pid:`-argument:
|
||||
|
||||
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
|
||||
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,
|
||||
> see the [Finit Conditions](conditions.md) document.
|
||||
@@ -439,6 +850,24 @@ optional `name` argument:
|
||||
|
||||
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
|
||||
configurable with the following options:
|
||||
|
||||
@@ -450,13 +879,16 @@ configurable with the following options:
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
* `restart:always` -- no upper limit on the number of times Finit
|
||||
tries to restart a crashing service. Same as `restart:-1`
|
||||
* `norestart` -- dont restart on failures, same as `restart:0`
|
||||
* `respawn` -- bypasses the `restart` mechanism completely, allows
|
||||
endless restarts. Useful in many use-cases, but not what `service`
|
||||
was originally designed for so not the default behavior
|
||||
* `oncrash:reboot` -- when all retries have failed, and the service
|
||||
has *crashed*, if this option is set the system is rebooted.
|
||||
Note, future releases may include other `oncrash:` actions
|
||||
* `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 moving to another runlevel, Finit starts by sending `SIGTERM`, to
|
||||
@@ -482,32 +914,123 @@ with an additional set of environment variables:
|
||||
the program, if it exited normally, or the signal name (`HUP`,
|
||||
`TERM`, etc.) if it exited due to signal
|
||||
|
||||
The scripts have a default execution time of 3 seconds before they are
|
||||
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
|
||||
These script actions *must terminate*, so they have a default execution
|
||||
time of 3 seconds before they are SIGKILLed, this can be adjusted using
|
||||
the above `kill:SEC` syntax.
|
||||
|
||||
> **Note:** the `pre:script` _must_ be idempotent, because a service
|
||||
> can transition between READY and HALTED states any number
|
||||
> of times before going to RUNNING.
|
||||
#### Conditional Loading
|
||||
|
||||
Finit support conditional loading of stanzas. The following example is
|
||||
take from the `system/hotplug.conf` file in the Finit distribution.
|
||||
Here we only show a simplified subset.
|
||||
|
||||
Starting with the `nowarn` option.
|
||||
|
||||
service nowarn name:udevd pid:udevd /lib/systemd/systemd-udevd
|
||||
service nowarn name:udevd pid:udevd udevd
|
||||
|
||||
When loading the .conf file Finit looks for `/lib/systemd/systemd-udevd`
|
||||
if that is not found Finit automatically logs a warning. The `nowarn`
|
||||
option disables this warning so that the second line can be evaluated,
|
||||
which also provides a service named `udevd`.
|
||||
|
||||
run nowarn if:udevd <pid/udevd> :1 udevadm settle -t 0
|
||||
|
||||
This line is only loaded if we know of a service named `udevd`. Again,
|
||||
we do not warn if `udevadm` is not found, Execution will also stop here
|
||||
until the PID condition is asserted, i.e., Finit detecting udevd has
|
||||
started.
|
||||
|
||||
run nowarn conflict:udevd [S] mdev -s -- Populating device tree
|
||||
|
||||
If `udevd` is not available, we try to run `mdev`, but if that is not
|
||||
found, again we do not warn.
|
||||
|
||||
Conditional loading statements can also be negated, so the previous stanza
|
||||
can also be written as:
|
||||
|
||||
run nowarn if:!udevd [S] mdev -s -- Populating device tree
|
||||
|
||||
The reason for using `confict` in this example is that a conflict can be
|
||||
resolved. Stanzas marked with `conflict:foo` are rechecked at runtime.
|
||||
|
||||
#### Conditional Execution
|
||||
|
||||
Similar to conditional loading of stanzas there is conditional runtime
|
||||
execution. This can be confusing at first, since Finit already has a
|
||||
condition subsystem, but this is more akin to the qualification to a
|
||||
runlevel. E.g., a `task [123]` is qualified to run only in runlevel 1,
|
||||
2, and 3. It is not considered for other runlevels.
|
||||
|
||||
Conditional execution qualify a run/task/service based on a condition.
|
||||
Consider this (simplified) example from the Infix operating system:
|
||||
|
||||
run [S] name:startup <pid/sysrepo> confd -b --load startup-config
|
||||
run [S] if:<usr/fail-startup> name:failure <pid/sysrepo> confd --load failure-config
|
||||
|
||||
The two run statements reside in the same .conf file so Finit runs them
|
||||
in true sequence. If loading the file `startup-config` fails confd sets
|
||||
the condition `usr/fail-startup`, thus allowing the next run statement
|
||||
to load `failure-config`.
|
||||
|
||||
Notice critical the difference between `<pid/sysrepo>` condition and
|
||||
`if:<usr/fail-startup>`. The former is a condition for starting and the
|
||||
latter is a condition to check if a run/task/service is qualified to
|
||||
even be considered.
|
||||
|
||||
Conditional execution statements can also be negated, so provided the
|
||||
file loaded did the opposite, i.e., set a condition on success, the
|
||||
previous stanza can also be written as:
|
||||
|
||||
run [S] if:<!usr/startup-ok> name:failure <pid/sysrepo> confd ...
|
||||
|
||||
### Run-parts Scripts
|
||||
|
||||
**Syntax:** `runparts <DIR>`
|
||||
**Syntax:** `runparts [progress] [sysv] <DIR>`
|
||||
|
||||
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
||||
files, or scripts, in the directory are called, in alphabetic order.
|
||||
The scripts in this directory are executed at the very end of runlevel
|
||||
`S`, [bootstrap][].
|
||||
files in the directory are called, in alphabetic order. The scripts in
|
||||
this directory are executed at the very end of runlevel `S`.
|
||||
|
||||
It can be beneficial to use `S01name`, `S02othername`, etc. if there
|
||||
is a dependency order between the scripts. Symlinks to existing
|
||||
daemons can talso be used, but make sure they daemonize by default.
|
||||
A common use-case for runparts scripts is to create and enable/disable
|
||||
services, which Finit will then apply when changing runlevel from S to
|
||||
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
|
||||
services and programs either terminate or start in the background or
|
||||
you will block Finit.
|
||||
**Options:**
|
||||
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
- `progress`: display the progress of each script being executed
|
||||
- `sysv`: run only SysV style scripts, i.e., `SNNfoo`, or `KNNbar`,
|
||||
where `NN` is a number (0-99).
|
||||
|
||||
If global debug mode is enabled, the `runparts` program is also called
|
||||
with the debug flag.
|
||||
|
||||
**Limitations:**
|
||||
|
||||
Scripts called from `runparts`, or hook scripts (see below), are limited
|
||||
in their interaction with Finit. Like the standalone `run` stanza and
|
||||
the `/etc/rc.local` shell script, Finit waits for their completion
|
||||
before continuing. None of them can issue commands to start, stop, or
|
||||
restart other services. Also, ensure 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.
|
||||
> 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
|
||||
|
||||
@@ -607,8 +1130,8 @@ can be omitted to keep the kernel default.
|
||||
tty [12345] @console noclear vt220
|
||||
|
||||
On really bare bones systems, or for board bringup, Finit can give you a
|
||||
shell prompt as soon as [bootstrap][] is done, without opening any
|
||||
device node:
|
||||
shell prompt as soon as bootstrap is done, without opening any device
|
||||
node:
|
||||
|
||||
tty [12345789] notty
|
||||
|
||||
@@ -666,6 +1189,93 @@ Log rotation is controlled using the global `log` setting.
|
||||
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
|
||||
-----------------------
|
||||
|
||||
It is not possible to run unmodified SysV init systems with Finit. This
|
||||
was never the intention and is not the strength of Finit. However, it
|
||||
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
|
||||
-----------
|
||||
|
||||
@@ -673,6 +1283,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
|
||||
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*.
|
||||
|
||||
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to
|
||||
@@ -682,15 +1296,17 @@ The rescue mode comes in two flavors; *traditional* and *fallback*.
|
||||
### Traditional
|
||||
|
||||
This is what most users expect. A very early maintenance login prompt,
|
||||
served by the bundled `/libexec/finit/sulogin` program, or the standard
|
||||
`sulogin` from util-linux or BusyBox is searched for in the UNIX default
|
||||
`$PATH`. If a successful login is made, or the user exits (Ctrl-D), the
|
||||
rescue mode is ended and the system boots up normally.
|
||||
served by the system `sulogin` program from util-linux, or BusyBox. If
|
||||
that is not found in `$PATH`, the bundled `/libexec/finit/sulogin`
|
||||
program is used instead. If a successful login is made, or if the user
|
||||
exits (Ctrl-D), the rescue mode is ended and the system boots up
|
||||
normally.
|
||||
|
||||
> **Note:** if the user (UID 0 and GID 0) does not have a password, or
|
||||
> __the account is locked__, the user is presented with a password-less
|
||||
> `"Press enter to enter maintenance mode."`, prompt which opens up a
|
||||
> root shell.
|
||||
> **Note:** the bundled sulogin in Finit can at configure time be given
|
||||
> another user than the default (root). If the sulogin user does not
|
||||
> have a password, or __the account is locked__, the user is presented
|
||||
> with a password-less `"Press enter to enter maintenance mode."`,
|
||||
> prompt which opens up a root shell.
|
||||
|
||||
|
||||
### Fallback
|
||||
@@ -739,9 +1355,9 @@ There is, however, nothing preventing you from having all configuration
|
||||
settings in `/etc/finit.conf`.
|
||||
|
||||
> **Note:** The `/etc/finit.d` directory was previously the default
|
||||
> Finit [runparts](#run-parts-scripts) directory. Finit no
|
||||
> longer has a default `runparts`, make sure to update your
|
||||
> setup, or the finit configuration, accordingly.
|
||||
> Finit [runparts](#run-parts-scripts) directory. Finit >=v4.0
|
||||
> no longer has a default `runparts` directory, make sure to
|
||||
> update your setup, or the finit configuration, accordingly.
|
||||
|
||||
|
||||
Watchdog
|
||||
@@ -750,12 +1366,11 @@ Watchdog
|
||||
When built `--with-watchdog` a separate service is built and installed
|
||||
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
|
||||
device node exists, Finit will start it and treat it as the elected
|
||||
watchdog service to delegate its reboot to. See [bootstrap][] for
|
||||
details. This delegation is to ensure that the system is rebooted by a
|
||||
hardware watchdog timer -- on many embedded systems this is crucial to
|
||||
ensure all circuits on the board are properly reset for the next boot,
|
||||
in effect ensuring the system works the same after both a power-on and
|
||||
reboot event.
|
||||
watchdog service to delegate its reboot to. This delegation is to
|
||||
ensure that the system is rebooted by a hardware watchdog timer -- on
|
||||
many embedded systems this is crucial to ensure all circuits on the
|
||||
board are properly reset for the next boot, in effect ensuring the
|
||||
system works the same after both a power-on and reboot event.
|
||||
|
||||
The delegation is performed at the very last steps of system shutdown,
|
||||
if reboot has been selected and an elected watchdog is known, first a
|
||||
@@ -777,8 +1392,6 @@ device descriptor. If the kernel driver has been built without this,
|
||||
the only option is to remove `/libexec/finit/watchdogd` or build without
|
||||
it at configure time.
|
||||
|
||||
[bootstrap]: bootstrap.md
|
||||
|
||||
|
||||
keventd
|
||||
-------
|
||||
|
||||
+138
-42
@@ -8,7 +8,8 @@ Hooks & Plugins
|
||||
* [Shutdown Hooks](#shutdown-hooks)
|
||||
|
||||
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
|
||||
more information, see the plugins listed below.
|
||||
@@ -27,21 +28,43 @@ For your convenience a set of *optional* plugins are available:
|
||||
system and runs at `HOOK_BASEFS_UP`. The `/var`, `/run`, and `/dev`
|
||||
file systems must be writable for this plugin to work.
|
||||
|
||||
Note: On an embedded system both `/var` and `/run` can be `tmpfs` RAM
|
||||
disks and `/dev` is usually a `devtmpfs`. This must be defined in the
|
||||
`/etc/fstab` file and in the Linux kernel config.
|
||||
This plugin is a wrapper for the [tmpfiles.d(5)][] implementation that
|
||||
Finit has. Capable but limited: no aging, attributes, or subvolumes.
|
||||
|
||||
By default, `/lib/finit/tmpfiles.d` carries all the default .conf
|
||||
files distributed with Finit. It is read first but can be overridden
|
||||
by any of the standard tmpfiles.d directories, e.g. `/etc/tmpfiles.d`.
|
||||
|
||||
> **Note:** On an embedded system both `/var` and `/run` can be `tmpfs`
|
||||
> RAM disks and `/dev` is usually a `devtmpfs`. This must be defined
|
||||
> in the `/etc/fstab` file and in the Linux kernel config.
|
||||
|
||||
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
|
||||
_Optional plugin._
|
||||
|
||||
* *hook-scripts.so*: Trigger the execution of scripts from plugin hook
|
||||
points (see [Hooks](#hooks)). Scripts are located in
|
||||
`/libexec/finit/hook` by default, this can be build-time customized
|
||||
by using the `--with-hook-scripts-path=PATH` argument to
|
||||
`configure`. _Optional plugin._
|
||||
points (see [Hooks](#hooks)). _Optional plugin._
|
||||
|
||||
* *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
|
||||
available. Enabled by default.
|
||||
This plugin is particularly useful for early boot debugging that needs
|
||||
to take place before regular services are available.
|
||||
|
||||
For example, say that you want to enable some kernel tracing before
|
||||
modules are loaded. With hook scripts, you can just drop in a shell
|
||||
script in `/libexec/finit/hook/mount/all/` that will poke the right
|
||||
control files in tracefs.
|
||||
|
||||
Scripts are located in `/libexec/finit/hook` by default, this can be
|
||||
customized at build-time using the `--with-hook-scripts-path=PATH`
|
||||
argument to `configure`.
|
||||
|
||||
* *hotplug.so*: Replaced with `/lib/finit/system/10-hotplug.conf`, which
|
||||
checks for `udevd` and `mdev` at boot. This file can be overridden by a
|
||||
file in `/etc/finit.d/10-hotplug.conf`.
|
||||
|
||||
Enabled by default.
|
||||
|
||||
> See the [Services](config.md#services) section in the configuration
|
||||
> guide for an example how to run `mdevd`, alternative to plain mdev.
|
||||
|
||||
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
|
||||
Enabled by default.
|
||||
@@ -49,20 +72,33 @@ For your convenience a set of *optional* plugins are available:
|
||||
* *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to
|
||||
load using `modprobe`. Each file can contain multiple lines with the
|
||||
name of the module to load. Any line starting with the standard UNIX
|
||||
comment character, `#`, is skipped.
|
||||
comment character, `#`, or `;`, is skipped.
|
||||
|
||||
Modules are loaded when entering runlevel `[2345]` using the `task`
|
||||
stanza. Each module gets a unique `name:modprobe.foo`, and
|
||||
optional`:ID`. The `:ID` is a globally incremented index, which can
|
||||
be disabled per file (anywhere) using the following config line:
|
||||
Modules are by default loaded in runlevel `S` using the `task` stanza.
|
||||
Each module gets a unique `name:modprobe.foo`, and optional`:ID`. The
|
||||
runlevel can be changed per file using:
|
||||
|
||||
set runlevel 2345
|
||||
|
||||
The `:ID` is a globally incremented index, which can be disabled per
|
||||
file (anywhere) using the following config line:
|
||||
|
||||
set noindex
|
||||
|
||||
To change the index used by the plugin:
|
||||
|
||||
set index 1234
|
||||
|
||||
**Note:** unlike the traditional .conf `module` directive, which load
|
||||
any listed module immediately, this plugin creates standard `taks`
|
||||
directives which load the module(s) in the background. As long as
|
||||
the `modprobe` program is found in the path, these tasks will always
|
||||
return `[ OK ]` at boot. Check the actual status using `initctl`.
|
||||
any listed module immediately, this plugin creates a background `task`
|
||||
which load the module(s) in the background. The program is modprobe,
|
||||
`/sbin/modprobe`, which you can override per .conf file:
|
||||
|
||||
set modprobe /path/to/maybe-a-modprobe-wrapper
|
||||
|
||||
Since these tasks run in the background, they usually return `[ OK ]`
|
||||
at boot, unless the modprobe tool does not exist. Check syslog for
|
||||
warnings and the actual status of the operation using `initctl`.
|
||||
|
||||
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
||||
interfaces. These events are then sent to the Finit service monitor
|
||||
@@ -91,45 +127,105 @@ state, etc. Available hook points are:
|
||||
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
|
||||
|
||||
* `HOOK_BANNER`: The very first point at which a plugin can run.
|
||||
Intended to be used as a banner replacement. Essentially this runs
|
||||
just before entering runlevel S. Assume nothing is available, so
|
||||
be prepared to use absolute paths, etc.
|
||||
* `HOOK_BANNER`, `hook/sys/banner`: The very first point at which a
|
||||
plugin can run. Intended to be used as a banner replacement.
|
||||
Essentially this runs just before entering runlevel S. Assume nothing
|
||||
is available, so be prepared to use absolute paths, etc.
|
||||
|
||||
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
|
||||
mounted — very early
|
||||
* `HOOK_ROOTFS_UP`, `hook/mount/root`: When `finit.conf` has been read
|
||||
and `/` has is mounted — very early
|
||||
|
||||
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
|
||||
and default init signals are setup
|
||||
* `HOOK_MOUNT_ERROR`, `hook/mount/error`: executed if `mount -a` fails
|
||||
|
||||
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
|
||||
networking is up (`lo` is up and the `network` script has run)
|
||||
* `HOOK_MOUNT_POST`, `hook/mount/post`: always executed after `mount -a`
|
||||
|
||||
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
|
||||
* `HOOK_BASEFS_UP`, `hook/mount/all`: All of `/etc/fstab` is mounted,
|
||||
swap is available and default init signals are setup
|
||||
|
||||
* `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d`
|
||||
has been launched
|
||||
* `HOOK_SVC_PLUGIN`, `hook/svc/plugin`: Called in `conf_init()` right
|
||||
before loading `/etc/finit.conf`. For plugins to register any early
|
||||
run/task/services, please do NOT use any earlier hook point. That
|
||||
will cause uninitialized rlimits that lead to unpredictable results
|
||||
when Finit later tries to start the run/task/service.
|
||||
|
||||
* `HOOK_NETWORK_UP`, `hook/net/up`: System bootstrap, runlevel S, has
|
||||
completed and networking is up (`lo` is up and the `network` script
|
||||
has run)
|
||||
|
||||
* `HOOK_SVC_UP`, `hook/svc/up`: All services in the active runlevel have
|
||||
been launched
|
||||
|
||||
* `HOOK_SYSTEM_UP`, `hook/sys/up`: All services *and* everything in
|
||||
`/etc/finit.d` has been launched
|
||||
|
||||
### Runtime Hooks
|
||||
|
||||
* `HOOK_SVC_RECONF`: Called when the user has changed something in the
|
||||
`/etc/finit.d` directory and issued `SIGHUP`. The hook is called when
|
||||
all modified/removed services have been stopped. When the hook has
|
||||
completed, Finit continues to start all modified and new services.
|
||||
* `HOOK_SVC_RECONF`, N/A: Called when the user has changed something in
|
||||
the `/etc/finit.d` directory and issued `SIGHUP`. The hook is called
|
||||
when all modified/removed services have been stopped. When the hook
|
||||
has completed, Finit continues to start all modified and new services.
|
||||
|
||||
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
|
||||
change. The hook is called when services not matching the new
|
||||
runlevel have been been stopped. When the hook has completed, Finit
|
||||
continues to start all services in the new runlevel.
|
||||
* `HOOK_RUNLEVEL_CHANGE`, N/A: Called when the user has issued a
|
||||
runlevel change. The hook is called when services not matching the
|
||||
new runlevel have been been stopped. When the hook has completed,
|
||||
Finit continues to start all services in the new runlevel.
|
||||
|
||||
### Shutdown Hooks
|
||||
|
||||
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
|
||||
services are sent `SIGTERM`
|
||||
* `HOOK_NETWORK_DN`, `hook/net/down`: Called right after having changed
|
||||
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
|
||||
I/O plugins and are called from the finit main loop when `poll()`
|
||||
detects an event. See the source code for `plugins/*.c` for more help
|
||||
and ideas.
|
||||
|
||||
[tmpfiles.d(5)]: https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 230 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 164 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 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
|
||||
+19
-23
@@ -1,8 +1,6 @@
|
||||
# From https://github.com/collectd/collectd/blob/master/configure.ac
|
||||
# Dependency handling currently unused
|
||||
|
||||
define([toupper], [translit([$1], [a-z], [A-Z])])
|
||||
define([sanitize], [translit([$1], [-], [_])])
|
||||
define([enadis], [ifelse([$1], [yes], [disable], [enable])])
|
||||
|
||||
# AC_PLUGIN(name, default, info)
|
||||
@@ -10,27 +8,25 @@ define([enadis], [ifelse([$1], [yes], [disable], [enable])])
|
||||
AC_DEFUN([AC_PLUGIN],[
|
||||
m4_divert_once([HELP_ENABLE], [
|
||||
Optional Plugins:])
|
||||
define(PLUGGY, m4_translit([$1],[a-z-][A-Z-]))dnl
|
||||
AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [
|
||||
if test "x$enableval" = "xyes"; then
|
||||
enable_plugin="yes"
|
||||
else
|
||||
enable_plugin="no"
|
||||
define(pluggy, sanitize($1))
|
||||
define(PLUGGY, toupper(pluggy))
|
||||
AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [
|
||||
if test "x$enableval" = "xyes"; then
|
||||
enable_plugin="yes"
|
||||
fi], [
|
||||
if test "x$enable_all_plugins" = "xauto"; then
|
||||
if test "x$2" = "xyes"; then
|
||||
enable_plugin="yes"
|
||||
else
|
||||
enable_plugin="no"
|
||||
fi], [
|
||||
if test "x$enable_all_plugins" = "xauto"; then
|
||||
if test "x$2" = "xyes"; then
|
||||
enable_plugin="yes"
|
||||
else
|
||||
enable_plugin="no"
|
||||
fi
|
||||
else
|
||||
enable_plugin="$enable_all_plugins"
|
||||
fi])
|
||||
if test "x$enable_plugin" = "xyes"; then
|
||||
AC_DEFINE([HAVE_]PLUGGY[_PLUGIN], 1, [Define to 1 if the $1 plugin is enabled.])
|
||||
plugins="$1 $plugins"
|
||||
enable_plugin="no"
|
||||
fi
|
||||
AM_CONDITIONAL([BUILD_]PLUGGY[_PLUGIN], test "x$enable_plugin" = "xyes")
|
||||
enable_pluggy="$enable_plugin"])
|
||||
else
|
||||
enable_plugin="$enable_all_plugins"
|
||||
fi])
|
||||
if test "x$enable_plugin" = "xyes"; then
|
||||
AC_DEFINE(HAVE_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, 1, [Define to 1 if the $1 plugin is enabled.])
|
||||
plugins="$1 $plugins"
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, test "x$enable_plugin" = "xyes")])
|
||||
|
||||
+90
-3
@@ -2,7 +2,7 @@
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd June 6, 2021
|
||||
.Dd Apr 22, 2023
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.Dt FINIT 8 SMM
|
||||
.Os Linux
|
||||
@@ -12,10 +12,16 @@
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/finit
|
||||
.Op --
|
||||
.Op debug
|
||||
.Op finit.cond=foo[,bar[,baz]]
|
||||
.Op finit.config=/path/to/alt/finit.conf
|
||||
.Op finit.debug
|
||||
.Op finit.fstab=/etc/fstab.alt
|
||||
.Op finit.status=<on,off>
|
||||
.Op finit.status_style=<classic,modern>
|
||||
.Op loglevel=<0-7>
|
||||
.Op panic=SEC
|
||||
.Op quiet
|
||||
.Op rescue | recover
|
||||
.Op single | S
|
||||
.Nm /sbin/telinit
|
||||
@@ -45,6 +51,15 @@ Sourcing environment files
|
||||
.It
|
||||
Conditions for network/process/custom dependencies
|
||||
.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
|
||||
.It
|
||||
Tooling to enable/disable services
|
||||
@@ -73,6 +88,38 @@ PID 1. For more information on this, and available kernel command line
|
||||
options, see
|
||||
.Xr bootparam 7 .
|
||||
.Bl -tag -width pid -offset indent
|
||||
.It Cm debug
|
||||
Enable kernel debug. Debug messages are printed to the console until
|
||||
Finit starts up, unless
|
||||
.Cm loglevel=7
|
||||
(below) is used.
|
||||
.It Cm finit.cond=foo[,bar[,baz]]
|
||||
set
|
||||
.Cm <boot/foo>
|
||||
condition, optionally multiple conditions can be set using the same
|
||||
option, separated with a comma. Alternatively, multiple
|
||||
.Cm foo.cond=arg
|
||||
can be given. Each will result in a
|
||||
.Cm <boot/arg>
|
||||
condition being set to control the rest of the system bootstrap.
|
||||
.Pp
|
||||
Very useful for selecting different boot modes, e.g. manufacturing test,
|
||||
firmware upgrade, or rescue mode.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
.Cm <boot/...>
|
||||
conditions cannot be cleared with
|
||||
.Nm initctl !
|
||||
.It Cm finit.config=/path/to/alternative/finit.conf
|
||||
override the compile-time bootstrap config file, default:
|
||||
.Bd -unfilled -offset indent
|
||||
./configure --with-config=/etc/finit.conf
|
||||
.Ed
|
||||
Useful when starting up in various rescue mode, factory, or
|
||||
production test setups. Use the top-level configuration file
|
||||
directive
|
||||
.Cm rcsd Ar /path/to/finit.d
|
||||
to override the default rcS.d directory.
|
||||
.It Cm finit.debug
|
||||
Enable Finit debug messages on system console and log. Sometimes useful
|
||||
when doing board bringup. Before the system log daemon has started,
|
||||
@@ -121,6 +168,33 @@ Control finit boot progress, including banner. This used to be called
|
||||
which still works but is deprecated.
|
||||
.It Cm finit.status_style=<classic,modern>
|
||||
Set Finit boot progress style, when enabled.
|
||||
.It Cm loglevel=<0-7>
|
||||
set the kernel's log level, which is more granular than
|
||||
.Cm debug .
|
||||
Also, when
|
||||
.Cm loglevel=7 ,
|
||||
Finit will
|
||||
.Em not disable
|
||||
kernel logs to the console. This is very useful when debugging the
|
||||
kernel at system bring-up. Since
|
||||
.Cm loglevel=7
|
||||
is the same as
|
||||
.Cm debug
|
||||
this means you have to use
|
||||
.Cm quiet
|
||||
for a quiet boot, until sysklogd takes over logging of kernel events.
|
||||
.It Cm panic=SEC
|
||||
By default the kernel does not reboot after a kernel panic. This
|
||||
setting will cause a kernel reboot after
|
||||
.Ar SEC
|
||||
seconds.
|
||||
.It Cm quiet
|
||||
Suppress kernel logging to console, except for warnings and errors.
|
||||
Also, see
|
||||
.Cm loglevel
|
||||
and
|
||||
.Cm quiet
|
||||
above.
|
||||
.It Cm rescue | recover
|
||||
Start rescue/maintenance mode. If your system comes with the bundled
|
||||
.Cm sulogin
|
||||
@@ -139,6 +213,10 @@ the regular runlevel. All services and TTYs in
|
||||
will be started, e.g., a
|
||||
.Cm tty [1] @console nologin
|
||||
configuration presents you with a root console without login.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
by definition, single user mode does not have networking enabled. This
|
||||
is also true for runlevel 1.
|
||||
.El
|
||||
.Sh BOOTING
|
||||
.Pp
|
||||
@@ -192,14 +270,22 @@ file removed, service B is also stopped.
|
||||
.Pp
|
||||
The following condition families are available today:
|
||||
.Pp
|
||||
.Bl -tag -width pid -offset indent
|
||||
.Bl -tag -width service -offset indent
|
||||
.It Cm net
|
||||
Linux netlink events, e.g. net/route/default, net/eth0/up, and
|
||||
net/et0/running
|
||||
.It Cm pid
|
||||
PID files basd on the service declaration
|
||||
PID files based on the service declaration
|
||||
.Cm 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
|
||||
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
|
||||
.It Cm usr
|
||||
@@ -255,6 +341,7 @@ Runtime files, including the condition subsystem
|
||||
.Sh SEE ALSO
|
||||
.Xr finit.conf 5
|
||||
.Xr initctl 8
|
||||
.Xr tmpfiles.d 5
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
|
||||
+162
-47
@@ -2,7 +2,7 @@
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd Oct 17, 2020
|
||||
.Dd July 11, 2023
|
||||
.Dt FINIT.CONF 5 SMM
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
@@ -12,48 +12,79 @@
|
||||
.Nm /etc/finit.conf
|
||||
.Nm /etc/finit.d/*.conf
|
||||
.Nm /etc/finit.d/available/*.conf
|
||||
.Nm /etc/finit.d/enabled/*.conf
|
||||
.Sh DESCRIPTION
|
||||
.Nm finit
|
||||
can be configured using only the original
|
||||
.Pa /etc/finit.conf
|
||||
file or in combination with
|
||||
.Pa /etc/finit.d/*.conf .
|
||||
Useful for package-based Linux distributions \(em each package can
|
||||
provide its own "script" file.
|
||||
.Nm Finit
|
||||
based systems can be set up with a single file:
|
||||
.Pa /etc/finit.conf .
|
||||
This is the traditional way of doing it which can be ideal for some
|
||||
setups since it gives a great overview of the system configuration.
|
||||
.Pp
|
||||
.Bl -tag -width /etc/finit.d/*.conf -offset indent -compact
|
||||
.It Pa /etc/finit.conf
|
||||
Main configuration file
|
||||
.It Pa /etc/finit.d/*.conf
|
||||
Snippets, usually one service per file
|
||||
.El
|
||||
More comprehensive setups, however, require more careful planning.
|
||||
The recommendation is per-package
|
||||
.Cm *.conf
|
||||
files in
|
||||
.Pa /etc/finit.d/available/ .
|
||||
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
|
||||
Not all configuration directives are available in
|
||||
.Pa /etc/finit.d/*.conf
|
||||
and some directives are only available at bootstrap, runlevel
|
||||
.Cm S ,
|
||||
see the section
|
||||
.Sx Limitations
|
||||
below for details.
|
||||
The following sections describe the more of this in detail, starting
|
||||
with files, the file format, and available directives. Remember to
|
||||
also visit the other manual pages (references at the bottom).
|
||||
.Sh FILES
|
||||
.Bl -tag -width /etc/finit.d/available/*.conf -compact
|
||||
.It Pa /etc/finit.conf
|
||||
Main configuration file
|
||||
Main configuration file, optional
|
||||
.It Pa /etc/finit.d/*.conf
|
||||
Static service definitions
|
||||
Static (system) service definitions
|
||||
.It Pa /etc/finit.d/available/*.conf
|
||||
Available services
|
||||
Available (installed) services
|
||||
.It Pa /etc/finit.d/enabled/*.conf
|
||||
Enabled services, symlinks from available
|
||||
Enabled services (symlink back)
|
||||
.El
|
||||
.Sh FILE FORMAT
|
||||
Lines starting with `#' are ignored. Note that end-of-line comments are
|
||||
NOT supported, comments must be on a line of their own.
|
||||
.Pp
|
||||
A configuration stanza must be on a single (long) line and
|
||||
.Em cannot
|
||||
be broken up in multiple lines by use of a backslash as the last
|
||||
character.
|
||||
Static services,
|
||||
.Cm .conf
|
||||
files in
|
||||
.Pa /etc/finit.d/ ,
|
||||
cannot be enabled or disabled using
|
||||
.Cm initctl [enable | disable] service[.conf] .
|
||||
An enabled service is a symlink back to the corresponding
|
||||
.Cm ../available/
|
||||
service
|
||||
.Cm .conf
|
||||
file. Please use the
|
||||
.Cm initctl
|
||||
tool to manage these symlinks to ensure proper operation.
|
||||
.Pp
|
||||
.Sy NOTE:
|
||||
Previous versions of
|
||||
.Nm Finit
|
||||
created symlinks in
|
||||
.Pa /etc/finit.d/
|
||||
if
|
||||
.Pa /etc/finit.d/enabled/
|
||||
was missing, this is as of v4.4 not supported.
|
||||
.Sh FILE FORMAT
|
||||
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
|
||||
This section lists all supported configuration directives. There also exist
|
||||
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.
|
||||
Default: 2
|
||||
.Pp
|
||||
It s recommended to keep runlevel 1 as single-user mode, because
|
||||
.Nm
|
||||
disables networking in this mode.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
only read and executed in runlevel S (bootstrap).
|
||||
.It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
|
||||
@@ -275,6 +310,51 @@ can depend on the
|
||||
.Cm <pid/bar>
|
||||
condition.
|
||||
.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
|
||||
as manual with the
|
||||
.Cm manual:yes
|
||||
@@ -304,6 +384,10 @@ number of seconds before Finit tries to restart
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
.It Cm restart:always
|
||||
no upper limit on the number of times Finit tries to restart a crashing
|
||||
service. Same as
|
||||
.Cm restart:-1
|
||||
.It Cm norestart
|
||||
dont restart on failures, same as
|
||||
.Cm restart:0
|
||||
@@ -318,10 +402,21 @@ was originally designed for so not the default behavior.
|
||||
when all retries have failed, and the service
|
||||
has
|
||||
.Em crashed ,
|
||||
if this option is set the system is rebooted. Note, future releases
|
||||
may include other
|
||||
.Cm oncrash:
|
||||
actions.
|
||||
if this option is set the system is rebooted.
|
||||
.It Cm oncrash:script
|
||||
Similar to
|
||||
.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
|
||||
.Pp
|
||||
When stopping a service (run/task/sysv/service), either manually or when
|
||||
@@ -364,11 +459,13 @@ or any unique identifier specified in the .conf file. The
|
||||
.Cm post:script
|
||||
is called with an additional set of environment variables:
|
||||
.Bl -tag -offset indent -width 1n
|
||||
.It Cm EXIT_CODE=[exited,signal]
|
||||
.It Cm EXIT_CODE=[exited,signal,crashed]
|
||||
set to one of
|
||||
.Cm exited
|
||||
.Cm exited ,
|
||||
.Cm signal ,
|
||||
or
|
||||
.Cm signal
|
||||
.Cm crashed
|
||||
(see above).
|
||||
.It Cm EXIT_STATUS=[num,SIGNAME]
|
||||
set to one of exit status code from the program, if it exited normally,
|
||||
or the signal name (HUP, TERM, etc.) if it exited due to signal
|
||||
@@ -378,13 +475,6 @@ The scripts have a default execution time of 3 seconds before they are
|
||||
SIGKILLed, this can be adjusted using the
|
||||
.Cm kill:SEC
|
||||
modifier syntax.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
the
|
||||
.Cm pre:script
|
||||
.Em must
|
||||
be idempotent, because a service can transition between READY and HALTED
|
||||
states any number of times before going to RUNNING.
|
||||
.It Cm runparts Aq DIR
|
||||
Call
|
||||
.Xr run-parts 8
|
||||
@@ -753,6 +843,31 @@ the example sets
|
||||
to denote that it doesn't support SIGHUP. That way Finit will
|
||||
stop/start the service instead of sending SIGHUP at restart/reload
|
||||
events.
|
||||
.Sh 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
|
||||
There are three major cgroup configuration directives:
|
||||
.Pp
|
||||
@@ -869,6 +984,6 @@ settings in
|
||||
.Xr finit 8 ,
|
||||
.Xr initctl 8
|
||||
.Sh AUTHORS
|
||||
.Nm finit
|
||||
.Nm Finit
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
maintained by Joachim Wiberg, with contributions by many others.
|
||||
|
||||
+113
-30
@@ -11,7 +11,7 @@
|
||||
.Nd Control tool for Finit
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/initctl
|
||||
.Op Fl bcfhlpqtvV
|
||||
.Op Fl bcfhjlpqtvV
|
||||
.Op COMMAND
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
@@ -24,13 +24,27 @@ This program follows the usual UNIX command line syntax, with long
|
||||
options starting with two dashes (`-'). The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl b, -batch
|
||||
Batch mode, no screen size probing.
|
||||
Batch mode, no screen size probing. Useful for scripting.
|
||||
.It Fl c, -create
|
||||
Create missing paths (and files) as needed. Useful with the edit command.
|
||||
Create missing paths (and files) as needed. Useful with the
|
||||
.Cm edit
|
||||
command.
|
||||
.It Fl f, -force
|
||||
Ignore missing files and arguments, never prompt.
|
||||
.It Fl h, -help
|
||||
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
|
||||
Only one lap in commands like top.
|
||||
.It Fl p, -plain
|
||||
@@ -52,9 +66,9 @@ Toggle
|
||||
(daemon) debug to
|
||||
.Pa /dev/console
|
||||
.It Nm Ar help
|
||||
Show built-in help text
|
||||
Show built-in help text.
|
||||
.It Nm Ar version
|
||||
Show program version
|
||||
Show program version.
|
||||
.It Nm Ar ls | list
|
||||
List all
|
||||
.Cm *.conf
|
||||
@@ -64,7 +78,16 @@ files in
|
||||
Create
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.Pa /etc/finit.d/available .
|
||||
.Pp
|
||||
Piping the contents into
|
||||
.Nm
|
||||
works as one would expect:
|
||||
.Bd -literal -offset indent
|
||||
echo "task name:foo /tmp/foo.sh -- Foo logger" | initctl create foo
|
||||
initctl enable foo
|
||||
initctl reload
|
||||
.Ed
|
||||
.It Nm Ar delete Ar CONF
|
||||
Delete
|
||||
.Cm .conf
|
||||
@@ -104,86 +127,146 @@ Disable
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/enabled ,
|
||||
i.e., removes symlink
|
||||
i.e., removes symlink.
|
||||
.It Nm Ar reload
|
||||
Reload
|
||||
.Cm *.conf in
|
||||
.Pa /etc/finit.d ,
|
||||
i.e., activates changes
|
||||
.It Nm Ar cond set Ar COND
|
||||
i.e., activates changes.
|
||||
.It Nm Ar cond set Ar COND Op COND ...
|
||||
Set (assert) user-defined condition,
|
||||
.Cm +usr/COND
|
||||
.It Nm Ar cond clr | clear Ar COND
|
||||
.It Nm Ar cond get Ar COND
|
||||
Get (quietly) the status of any condition. Defaults to user-defined
|
||||
condions, but if a slash is detected, e.g.,
|
||||
.Cm pid/foo
|
||||
or
|
||||
.Cm net/eth0/exist ,
|
||||
then any condition can be read.
|
||||
.Pp
|
||||
The command is geared for scripting, check the exit code to get the
|
||||
status of the condition: 0 - on, 1 - off, 255 - flux. For a more
|
||||
verbose output, use the
|
||||
.Fl v
|
||||
option.
|
||||
.It Nm Ar cond clr | clear Ar COND Op COND ...
|
||||
Clear (deassert) user-defined condition,
|
||||
.Cm -usr/COND
|
||||
.It Nm Ar cond status
|
||||
Show condition status, default
|
||||
.Cm cond
|
||||
command.
|
||||
.It Nm Ar cond dump
|
||||
Dump all conditions and their status
|
||||
command. Also supports the
|
||||
.Fl j
|
||||
option for detailed JSON output.
|
||||
.It Nm Ar cond dump Op Cm TYPE
|
||||
Dump all, or a given type of, conditions and their status. Also
|
||||
supports the
|
||||
.Fl j
|
||||
option for detailed JSON output.
|
||||
.It Nm Ar ident Op Cm NAME
|
||||
Display indentities of all run/task/services, or only instances
|
||||
matching
|
||||
.Cm NAME .
|
||||
A partial string, e.g.,
|
||||
.Cm NAM ,
|
||||
will not match anything
|
||||
will not match anything.
|
||||
.It Nm Ar log Op Cm NAME
|
||||
Show ten last Finit, or
|
||||
.Cm NAME ,
|
||||
messages from syslog
|
||||
messages from syslog.
|
||||
.It Nm Ar start Cm NAME[:ID]
|
||||
Start service by name, with optional ID, e.g.,
|
||||
.Cm initctl start tty:1
|
||||
.It Nm Ar stop Cm NAME[:ID]
|
||||
Stop/Pause a running service by name
|
||||
Stop/Pause a running service by name.
|
||||
.It Nm Ar reload Cm NAME[:ID]
|
||||
Reload service by name (SIGHUP or restart)
|
||||
Reload service as if its configuration had changed. Internally,
|
||||
.Nm finit
|
||||
marks the named service as "dirty" and turns the state machine,
|
||||
resulting in a SIGHUP or restart of (at least) the named service.
|
||||
Any dependant services are also restarted.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
no .conf file is reloaded with this variant of the command. Essentially
|
||||
it is the same as calling
|
||||
.Cm restart .
|
||||
Except for two things:
|
||||
.Bl -enum -offset indent -compact
|
||||
.It
|
||||
if the service supports HUP, it's signaled instead of stop-started, and
|
||||
.It
|
||||
it allows restarting run/task in the same runlevel -- which is otherwise
|
||||
restricted.
|
||||
.El
|
||||
.It Nm Ar signal Cm NAME:[ID] S
|
||||
Send signal S to service, by name.
|
||||
.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).
|
||||
.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]
|
||||
Show service status, by name. If only
|
||||
.Cm NAME
|
||||
is given and multiple instances exits, a summary of all matching
|
||||
instances are shown. Only an exact match displays the detailed status
|
||||
for a particular instance
|
||||
instances are shown. Only an exact match displays detailed status
|
||||
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
|
||||
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
|
||||
List cgroup config overview
|
||||
List cgroup config overview.
|
||||
.It Nm Ar ps
|
||||
List processes based on cgroups
|
||||
List processes based on cgroups.
|
||||
.It Nm Ar top
|
||||
Show top-like listing based on cgroups
|
||||
Show top-like listing based on cgroups.
|
||||
.It Nm Ar plugins
|
||||
List installed plugins.
|
||||
.It Nm Ar runlevel Op Ar 0-9
|
||||
Show or set runlevel: 0 halt, 6 reboot
|
||||
Show or set runlevel: 0 halt, 6 reboot.
|
||||
.Pp
|
||||
If called at boot (runlevel S) to set the runlevel, Finit only schedules
|
||||
the change, effectively overriding the configured runlevel from
|
||||
.Pa /etc/finit.conf .
|
||||
Useful to trigger a fail-safe mode, or similar.
|
||||
.It Nm Ar reboot
|
||||
Reboot system, default if
|
||||
.Cm reboot
|
||||
is symlinked to
|
||||
.Nm
|
||||
.Nm .
|
||||
.It Nm Ar halt
|
||||
Halt system, default if
|
||||
.Cm halt
|
||||
is symlinked to
|
||||
.Nm
|
||||
.Nm .
|
||||
.It Nm Ar poweroff
|
||||
Power-off system, default if
|
||||
.Cm poweroff
|
||||
is symlinked to
|
||||
.Nm
|
||||
.Nm .
|
||||
.It Nm Ar suspend
|
||||
Suspend system, default if
|
||||
.Cm suspend
|
||||
is symlinked to
|
||||
.Nm
|
||||
.Nm .
|
||||
.It Nm Ar utmp show
|
||||
Raw dump of UTMP/WTMP db
|
||||
Raw dump of UTMP/WTMP db.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr finit.conf 5
|
||||
|
||||
+2
-10
@@ -1,9 +1,9 @@
|
||||
pkglibdir = $(plugin_path)
|
||||
AM_LDFLAGS = -module -avoid-version -shared
|
||||
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 += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
AM_CPPFLAGS += -D__FINIT__ $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
@@ -17,10 +17,6 @@ if BUILD_DBUS_PLUGIN
|
||||
libplug_la_SOURCES += dbus.c
|
||||
endif
|
||||
|
||||
if BUILD_HOTPLUG_PLUGIN
|
||||
libplug_la_SOURCES += hotplug.c
|
||||
endif
|
||||
|
||||
if BUILD_MODULES_LOAD_PLUGIN
|
||||
libplug_la_SOURCES += modules-load.c
|
||||
endif
|
||||
@@ -68,10 +64,6 @@ if BUILD_HOOK_SCRIPTS_PLUGIN
|
||||
pkglib_LTLIBRARIES += hook-scripts.la
|
||||
endif
|
||||
|
||||
if BUILD_HOTPLUG_PLUGIN
|
||||
pkglib_LTLIBRARIES += hotplug.la
|
||||
endif
|
||||
|
||||
if BUILD_MODULES_LOAD_PLUGIN
|
||||
pkglib_LTLIBRARIES += modules-load.la
|
||||
endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Save and restore ALSA sound settings using alsactl
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,12 +36,12 @@
|
||||
static void save(void *arg)
|
||||
{
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (whichp(ALSACTL)) {
|
||||
_d("Saving sound settings ...");
|
||||
dbg("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)
|
||||
{
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (whichp(ALSACTL)) {
|
||||
_d("Restoring sound settings ...");
|
||||
dbg("Restoring sound settings ...");
|
||||
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
|
||||
}
|
||||
}
|
||||
|
||||
+43
-81
@@ -1,6 +1,6 @@
|
||||
/* Setup necessary system files for, e.g. UTMP (tracking logins)
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "tmpfiles.h"
|
||||
#include "util.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)
|
||||
return 1;
|
||||
|
||||
_d("Removing %s ...", fpath);
|
||||
dbg("Removing %s ...", fpath);
|
||||
(void)remove(fpath);
|
||||
|
||||
return 0;
|
||||
@@ -110,10 +111,43 @@ static void clean(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void ln(const char *target, const char *linkpath)
|
||||
/* Kernel defines the following compulsory and recommended links
|
||||
* https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links
|
||||
*/
|
||||
static void kernel_links(void)
|
||||
{
|
||||
if (symlink(target, linkpath) && errno != EEXIST)
|
||||
_pe("Failed creating %s -> %s symlink", target, linkpath);
|
||||
struct {
|
||||
char *tgt, *lnk;
|
||||
int optional;
|
||||
} k[] = {
|
||||
{ "/proc/self/fd", "/dev/fd", 0 },
|
||||
{ "fd/0", "/dev/stdin", 0 },
|
||||
{ "fd/1", "/dev/stdout", 0 },
|
||||
{ "fd/2", "/dev/stderr", 0 },
|
||||
{ "socksys", "/dev/nfsd", 0 },
|
||||
{ "null", "/dev/X0R", 0 },
|
||||
{ "/proc/kcore", "/dev/core", 1 },
|
||||
{ "ram0", "/dev/ramdisk", 1 },
|
||||
{ "qft0", "/dev/ftape", 1 },
|
||||
{ "video0", "/dev/bttv0", 1 },
|
||||
{ "radio0", "/dev/radio", 1 },
|
||||
};
|
||||
char *fn, buf[80];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(k); i++) {
|
||||
fn = k[i].tgt;
|
||||
|
||||
if (k[i].optional) {
|
||||
if (fn[0] != '/') {
|
||||
snprintf(buf, sizeof(buf), "/dev/%s", k[i].tgt);
|
||||
fn = buf;
|
||||
}
|
||||
if (!fexist(fn))
|
||||
continue;
|
||||
}
|
||||
ln(k[i].tgt, k[i].lnk);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -125,87 +159,15 @@ static void setup(void *arg)
|
||||
|
||||
prev = umask(0);
|
||||
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
makedir("/var/cache", 0755);
|
||||
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
||||
makedir("/var/games", 0755);
|
||||
makedir("/var/lib", 0755);
|
||||
makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
|
||||
makedir("/var/lib/alarm", 0755);
|
||||
makedir("/var/lib/urandom",0755);
|
||||
if (fisdir("/run")) {
|
||||
_d("System with new /run tmpfs ...");
|
||||
if (!fisdir("/run/lock"))
|
||||
makedir("/run/lock", 1777);
|
||||
ln("/run/lock", "/var/lock");
|
||||
ln("/dev/shm", "/run/shm");
|
||||
/* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */
|
||||
kernel_links();
|
||||
|
||||
/* compat only, should really be set up by OS/dist */
|
||||
ln("/run", "/var/run");
|
||||
} else {
|
||||
makedir("/var/lock", 1777);
|
||||
makedir("/var/run", 0755);
|
||||
}
|
||||
makedir("/var/log", 0755);
|
||||
makedir("/var/mail", 0755);
|
||||
makedir("/var/opt", 0755);
|
||||
makedir("/var/spool", 0755);
|
||||
makedir("/var/spool/cron", 0755);
|
||||
makedir("/var/tmp", 0755);
|
||||
makedir("/var/empty", 0755);
|
||||
|
||||
/*
|
||||
* UTMP actually needs multiple db files
|
||||
*/
|
||||
if (has_utmp()) {
|
||||
int gid;
|
||||
|
||||
/*
|
||||
* If /etc/group or "utmp" group is missing, default to
|
||||
* "root", or "wheel", group.
|
||||
*/
|
||||
_d("Setting up necessary UTMP files ...");
|
||||
gid = getgroup("utmp");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
|
||||
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
|
||||
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
||||
create("/var/log/lastlog", 0644, 0, gid);
|
||||
}
|
||||
/* Create all system tmpfiles.d(5) */
|
||||
tmpfilesd();
|
||||
|
||||
/* Set BOOT_TIME UTMP entry */
|
||||
utmp_set_boot();
|
||||
|
||||
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
|
||||
touch("/etc/network/run/ifstate");
|
||||
#else
|
||||
erase("/etc/network/run/ifstate");
|
||||
#endif
|
||||
|
||||
_d("Setting up misc files ...");
|
||||
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
|
||||
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
|
||||
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
||||
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/run/frr", 0755, "frr", "frr");
|
||||
mksubsys("/var/log/frr", 0755, "frr", "frr");
|
||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||
|
||||
if (!fexist("/etc/mtab"))
|
||||
ln("../proc/self/mounts", "/etc/mtab");
|
||||
|
||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
||||
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
|
||||
|
||||
/* Debian has /run/sudo, ensure correct perms and SELinux label */
|
||||
mksubsys("/var/run/sudo", 0711, "root", "root");
|
||||
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
|
||||
if (whichp("restorecon"))
|
||||
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
|
||||
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
|
||||
+103
-28
@@ -1,6 +1,6 @@
|
||||
/* Setup and start system message bus, D-Bus
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -35,60 +35,135 @@
|
||||
#include "service.h"
|
||||
#include "conf.h"
|
||||
|
||||
#define DAEMON "dbus-daemon"
|
||||
#define ARGS "--nofork --system --syslog-only"
|
||||
#define DESC "D-Bus message bus daemon"
|
||||
#define DBUS_DAEMON "dbus-daemon"
|
||||
#define DBUS_ARGS "--nofork --system --syslog-only"
|
||||
#define DBUS_DESC "D-Bus message bus daemon"
|
||||
|
||||
#ifndef DAEMONUSER
|
||||
#define DAEMONUSER "messagebus"
|
||||
#ifndef DBUS_DAEMONUSER
|
||||
#define DBUS_DAEMONUSER "messagebus"
|
||||
#endif
|
||||
|
||||
#ifndef DAEMONPIDFILE
|
||||
#define DAEMONPIDFILE "/var/run/dbus/pid"
|
||||
#ifndef DBUS_DAEMONGROUP
|
||||
#define DBUS_DAEMONGROUP "messagebus"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dumnpster diving for the D-Bus main configuration file
|
||||
* where <pidfile> is defined.
|
||||
*/
|
||||
static char *dbus_pidfn(void)
|
||||
{
|
||||
char *alt[] = {
|
||||
"/usr/share/dbus-1/system.conf",
|
||||
"/etc/dbus-1/system.conf",
|
||||
NULL
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; alt[i]; i++) {
|
||||
char *fn = alt[i];
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
continue;
|
||||
|
||||
fn = NULL;
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *pos;
|
||||
|
||||
pos = strstr(buf, "<pidfile>");
|
||||
if (!pos)
|
||||
continue;
|
||||
fn = pos + strlen("<pidfile>");
|
||||
pos = strstr(fn, "</pidfile>");
|
||||
if (!pos) {
|
||||
fn = NULL;
|
||||
break;
|
||||
}
|
||||
*pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
if (fn)
|
||||
return strdup(fn);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
char *group = DBUS_DAEMONGROUP;
|
||||
char *user = DBUS_DAEMONUSER;
|
||||
char line[256];
|
||||
char *pidfn;
|
||||
mode_t prev;
|
||||
char *cmd;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = which(DAEMON);
|
||||
cmd = which(DBUS_DAEMON);
|
||||
if (!cmd) {
|
||||
_d("Skipping plugin, %s is not installed.", DAEMON);
|
||||
dbg("Skipping plugin, %s is not installed.", DBUS_DAEMON);
|
||||
return;
|
||||
}
|
||||
|
||||
prev =umask(0);
|
||||
if (getuser(user, NULL) == -1) {
|
||||
if (getuser("dbus", NULL) == -1)
|
||||
user = "root"; /* fallback */
|
||||
else
|
||||
user = "dbus"; /* e.g., Buildroot */
|
||||
}
|
||||
|
||||
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
|
||||
if (whichp("dbus-uuidgen"))
|
||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||
if (getgroup(group) == -1) {
|
||||
if (getgroup("dbus") == -1)
|
||||
group = "root"; /* fallback */
|
||||
else
|
||||
group = "dbus"; /* e.g., Buildroot */
|
||||
}
|
||||
|
||||
/* Clean up from any previous pre-bootstrap run */
|
||||
remove(DAEMONPIDFILE);
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
|
||||
DAEMONPIDFILE, DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
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 */
|
||||
if (whichp("dbus-uuidgen"))
|
||||
run_interactive("dbus-uuidgen --ensure", "Verifying D-Bus machine UUID");
|
||||
|
||||
/*
|
||||
* Register service with Finit
|
||||
* Note: dbus drops privs after starting up.
|
||||
*/
|
||||
if (pidfn) {
|
||||
snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus pid:!%s %s %s -- %s",
|
||||
pidfn, cmd, DBUS_ARGS, DBUS_DESC);
|
||||
free(pidfn);
|
||||
} else
|
||||
snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus %s %s -- %s",
|
||||
cmd, DBUS_ARGS, DBUS_DESC);
|
||||
|
||||
conf_save_service(SVC_TYPE_SERVICE, line, "dbus.conf");
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_SVC_PLUGIN] = { .cb = setup },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+11
-10
@@ -26,18 +26,11 @@
|
||||
#include "plugin.h"
|
||||
#include "config.h"
|
||||
|
||||
#define CHOOSE(x, y) y
|
||||
static const char *hscript_paths[] = HOOK_TYPES;
|
||||
#undef CHOOSE
|
||||
extern void plugin_script_run(hook_point_t no);
|
||||
|
||||
static void hscript_run_parts(hook_point_t hook)
|
||||
static void hscript_run_parts(hook_point_t no)
|
||||
{
|
||||
char path[CMD_SIZE] = "";
|
||||
|
||||
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
|
||||
strlcat(path, hscript_paths[hook] + 4, sizeof(path));
|
||||
|
||||
run_parts(path, NULL);
|
||||
plugin_script_run(no);
|
||||
}
|
||||
|
||||
static void hscript_banner(void *arg)
|
||||
@@ -96,6 +89,14 @@ static plugin_t plugin = {
|
||||
.hook[HOOK_SVC_UP] = { .cb = hscript_svc_up },
|
||||
.hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up },
|
||||
.hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown },
|
||||
/*
|
||||
* We have exited here, so these are handled a bit differently by Finit,
|
||||
* when hook scripts are enabled, just to provide the user with the same
|
||||
* interface:
|
||||
*
|
||||
* .hook[HOOK_SVC_DN] = { .cb = hscript_svc_down },
|
||||
* .hook[HOOK_SYS_DN] = { .cb = hscript_system_down },
|
||||
*/
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
@@ -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" },
|
||||
};
|
||||
|
||||
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:
|
||||
*/
|
||||
+72
-13
@@ -1,6 +1,6 @@
|
||||
/* Coldplug modules using modalias magic
|
||||
*
|
||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -71,14 +71,14 @@ static int modprobe(char *alias)
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
_pe("Failed forking modprobe child");
|
||||
err(1, "Failed forking modprobe child");
|
||||
return 1;
|
||||
case 0:
|
||||
execvp(args[0], args);
|
||||
break;
|
||||
default:
|
||||
if (!complete(args[0], pid))
|
||||
_d("Successful modprobe of %s", alias);
|
||||
dbg("Successful modprobe of %s", alias);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ static void alias_remove(struct module *m)
|
||||
TAILQ_REMOVE(&modules, m, link);
|
||||
free(m->alias);
|
||||
free(m);
|
||||
}
|
||||
}
|
||||
|
||||
static int alias_exist(char *alias)
|
||||
{
|
||||
@@ -120,27 +120,86 @@ static int alias_exist(char *alias)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_alias(const char *path, const struct stat *st, int flag, struct FTW *unused)
|
||||
static void alias_add_uniq(char *alias)
|
||||
{
|
||||
if (alias_exist(alias))
|
||||
return;
|
||||
|
||||
alias_add(alias);
|
||||
}
|
||||
|
||||
static FILE *maybe_fopen_alias(const char *file, const char *path)
|
||||
{
|
||||
const char *basename;
|
||||
|
||||
basename = basenm(path);
|
||||
if (!strcmp(basename, path))
|
||||
return NULL;
|
||||
|
||||
if (strcmp(file, basename))
|
||||
return NULL;
|
||||
|
||||
return fopen(path, "r");
|
||||
}
|
||||
|
||||
static int scan_modalias(const char *path)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
|
||||
if (fnmatch("*/modalias", path, 0))
|
||||
return 0;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
fp = maybe_fopen_alias("modalias", path);
|
||||
if (!fp)
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
chomp(buf);
|
||||
if (!alias_exist(buf))
|
||||
alias_add(buf);
|
||||
alias_add_uniq(buf);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_uevent(const char *path)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
|
||||
fp = maybe_fopen_alias("uevent", path);
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (strstr(buf, "MODALIAS=") != buf)
|
||||
continue;
|
||||
|
||||
chomp(buf);
|
||||
|
||||
alias_add_uniq(buf + strlen("MODALIAS="));
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_alias(const char *path, const struct stat *st,
|
||||
int flag, struct FTW *unused)
|
||||
{
|
||||
/* Sanity check: is this a regular file we can read? */
|
||||
if (!(S_ISREG(st->st_mode) && (st->st_mode & S_IRUSR)))
|
||||
return 0;
|
||||
|
||||
if (!scan_uevent(path))
|
||||
return 0;
|
||||
|
||||
scan_modalias(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coldplug(void *arg)
|
||||
{
|
||||
struct module *m, *tmp;
|
||||
|
||||
+87
-31
@@ -62,62 +62,120 @@
|
||||
#ifndef MODULES_LOAD_PATH
|
||||
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||
#endif
|
||||
#define MODPROBE_PATH "/sbin/modprobe"
|
||||
#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 \
|
||||
"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 line[256];
|
||||
char *modprobe_path;
|
||||
int num = 0;
|
||||
char *line;
|
||||
char *lvl;
|
||||
FILE *fp;
|
||||
|
||||
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
||||
strlcat(module_path, file, sizeof(module_path));
|
||||
|
||||
fp = fopen(module_path, "r");
|
||||
if (!fp)
|
||||
if (!fp) {
|
||||
warnx("failed opening %s for reading, skipping ...", module_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
char cmd[CMD_SIZE], *mod, *args;
|
||||
modprobe_path = strdup(MODPROBE_PATH);
|
||||
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);
|
||||
if (!mod[0])
|
||||
continue;
|
||||
goto next;
|
||||
|
||||
if (mod[0] == '#' || mod[0] == ';')
|
||||
continue;
|
||||
/* Finit extension 'set foo bar' */
|
||||
if ((set = fgetval(mod, "set", "= \t"))) {
|
||||
char *val = mod;
|
||||
|
||||
if (!strcmp(mod, "set noindex")) {
|
||||
index = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(mod, "set index", 9)) {
|
||||
mod += 9;
|
||||
args = strchr(mod, '=');
|
||||
if (args) /* optional */
|
||||
mod = args + 1;
|
||||
index = atoi(mod);
|
||||
continue;
|
||||
if (!strcmp(set, "noindex")) {
|
||||
index = 0;
|
||||
free(set);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if ((val = fgetval(set, "index", "= \t"))) {
|
||||
index = atoi(val);
|
||||
free(set);
|
||||
free(val);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if ((val = fgetval(set, "runlevel", "= \t"))) {
|
||||
free(lvl);
|
||||
lvl = val;
|
||||
free(set);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if ((val = fgetval(set, "modprobe", "= \t"))) {
|
||||
if (access(val, X_OK)) {
|
||||
warn("%s: cannot use %s", module_path, val);
|
||||
free(set);
|
||||
free(val);
|
||||
free(line);
|
||||
goto skip;
|
||||
} else {
|
||||
free(modprobe_path);
|
||||
modprobe_path = val;
|
||||
}
|
||||
free(set);
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* unknown 'set' command, ignore. */
|
||||
goto next;
|
||||
}
|
||||
|
||||
mod = strtok_r(mod, " ", &args);
|
||||
if (!mod)
|
||||
continue;
|
||||
goto next;
|
||||
|
||||
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
|
||||
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);
|
||||
num++;
|
||||
next:
|
||||
free(line);
|
||||
}
|
||||
skip:
|
||||
free(modprobe_path);
|
||||
free(lvl);
|
||||
fclose(fp);
|
||||
|
||||
return num;
|
||||
@@ -142,11 +200,11 @@ static void load(void *arg)
|
||||
int index = 1;
|
||||
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);
|
||||
if (num > 0) {
|
||||
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);
|
||||
@@ -154,10 +212,8 @@ static void load(void *arg)
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = load
|
||||
},
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_SVC_PLUGIN] = { .cb = load },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+36
-36
@@ -2,7 +2,7 @@
|
||||
/* Netlink plugin for IFUP/IFDN and GW events
|
||||
*
|
||||
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
|
||||
* Copyright (C) 2009-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (C) 2009-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* 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;
|
||||
|
||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
|
||||
_e("Packet too small or truncated!");
|
||||
errx(1, "Packet too small or truncated!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
a = RTM_RTA(r);
|
||||
la = RTM_PAYLOAD(nlmsg);
|
||||
if (la >= len) {
|
||||
_e("Packet too large!");
|
||||
errx(1, "Packet too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,18 +80,18 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
switch (a->rta_type) {
|
||||
case RTA_GATEWAY:
|
||||
gw = *((int *)data);
|
||||
//_d("GW: 0x%04x", gw);
|
||||
//dbg("GW: 0x%04x", gw);
|
||||
break;
|
||||
|
||||
case RTA_DST:
|
||||
dst = *((int *)data);
|
||||
plen = r->rtm_dst_len;
|
||||
//_d("Prefix LEN: 0x%04x", plen);
|
||||
//dbg("Prefix LEN: 0x%04x", plen);
|
||||
break;
|
||||
|
||||
case RTA_OIF:
|
||||
idx = *((int *)data);
|
||||
//_d("IDX: 0x%04x", idx);
|
||||
//dbg("IDX: 0x%04x", idx);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
ing.s_addr = gw;
|
||||
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
|
||||
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
|
||||
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
|
||||
dbg("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
|
||||
|
||||
if ((!dst && !plen) && (gw || idx)) {
|
||||
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
|
||||
@@ -167,7 +167,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
int la;
|
||||
|
||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
|
||||
_e("Packet too small or truncated!");
|
||||
errx(1, "Packet too small or truncated!");
|
||||
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)));
|
||||
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
|
||||
if (la >= len) {
|
||||
_e("Packet too large!");
|
||||
errx(1, "Packet too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
|
||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||
if (validate_ifname(ifname)) {
|
||||
_d("Invalid interface name '%s', skipping ...", ifname);
|
||||
dbg("Invalid interface name '%s', skipping ...", ifname);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
* New interface has appeared, or interface flags has changed.
|
||||
* 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, "up", i->ifi_flags & IFF_UP);
|
||||
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:
|
||||
/* 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, "up", 0);
|
||||
net_cond_set(ifname, "running", 0);
|
||||
@@ -213,15 +213,15 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
_d("%s: New Address", ifname);
|
||||
dbg("%s: New Address", ifname);
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
_d("%s: Deconfig Address", ifname);
|
||||
dbg("%s: Deconfig Address", ifname);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||
dbg("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -246,46 +246,46 @@ static int nl_parse(int sd)
|
||||
break;
|
||||
|
||||
default:
|
||||
_pe("recv()");
|
||||
err(1, "recv()");
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// _d("recv %zd bytes", len);
|
||||
// dbg("recv %zd bytes", len);
|
||||
l = (size_t)len;
|
||||
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
|
||||
struct nlmsgerr *nle;
|
||||
|
||||
switch (nh->nlmsg_type) {
|
||||
case NLMSG_DONE:
|
||||
// _d("Done with netlink messages.");
|
||||
// dbg("Done with netlink messages.");
|
||||
return 0;
|
||||
|
||||
case NLMSG_ERROR:
|
||||
// _d("Kernel netlink comm. error.");
|
||||
// dbg("Kernel netlink comm. error.");
|
||||
nle = NLMSG_DATA(nh);
|
||||
if (nle) {
|
||||
errno = -nle->error;
|
||||
_pe("Kernel netlink error %d", errno);
|
||||
err(1, "Kernel netlink error %d", errno);
|
||||
}
|
||||
return -1;
|
||||
|
||||
case RTM_NEWROUTE:
|
||||
case RTM_DELROUTE:
|
||||
// _d("Netlink route ...");
|
||||
// dbg("Netlink route ...");
|
||||
nl_route(nh, len);
|
||||
break;
|
||||
|
||||
case RTM_NEWLINK:
|
||||
case RTM_DELLINK:
|
||||
// _d("Netlink link ...");
|
||||
// dbg("Netlink link ...");
|
||||
nl_link(nh, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
_w("unhandled netlink message, type %d", nh->nlmsg_type);
|
||||
warnx("unhandled netlink message, type %d", nh->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -304,21 +304,21 @@ static int nl_request(int sd, unsigned int seq, int type)
|
||||
|
||||
switch (type) {
|
||||
case RTM_GETROUTE:
|
||||
// _d("RTM_GETROUTE");
|
||||
// dbg("RTM_GETROUTE");
|
||||
nlr->rtm.rtm_family = AF_INET;
|
||||
nlr->rtm.rtm_table = RT_TABLE_MAIN;
|
||||
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
break;
|
||||
|
||||
case RTM_GETLINK:
|
||||
// _d("RTM_GETLINK");
|
||||
// dbg("RTM_GETLINK");
|
||||
nlr->ifi.ifi_family = AF_UNSPEC;
|
||||
nlr->ifi.ifi_change = 0xFFFFFFFF;
|
||||
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
break;
|
||||
|
||||
default:
|
||||
_w("Cannot resync, unhandled message type %d", type);
|
||||
warnx("Cannot resync, unhandled message type %d", type);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (sd < 0) {
|
||||
_pe("netlink socket");
|
||||
err(1, "netlink socket");
|
||||
return;
|
||||
}
|
||||
|
||||
if (all) {
|
||||
_d("============================ RESYNC =================================");
|
||||
dbg("============================ RESYNC =================================");
|
||||
/* this doesn't update conditions, and thus does not stop services */
|
||||
cond_deassert("net/");
|
||||
|
||||
@@ -364,7 +364,7 @@ static void nl_resync(int all)
|
||||
|
||||
/* delayed update after we've corrected things */
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
_d("=========================== RESYNCED ================================");
|
||||
dbg("=========================== RESYNCED ================================");
|
||||
} else
|
||||
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 (errno == ENOBUFS) { /* netlink(7) */
|
||||
_w("busy system, resynchronizing with kernel.");
|
||||
warnx("busy system, resynchronizing with kernel.");
|
||||
nl_resync(1);
|
||||
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.
|
||||
*/
|
||||
if (nl_ifdown) {
|
||||
_d("interface down, checking default route.");
|
||||
dbg("interface down, checking default route.");
|
||||
if (nl_defidx > 0) {
|
||||
nl_defidx = 0;
|
||||
nl_resync(0);
|
||||
@@ -421,7 +421,7 @@ PLUGIN_INIT(plugin_init)
|
||||
|
||||
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (sd < 0) {
|
||||
_pe("socket()");
|
||||
err(1, "socket()");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -431,14 +431,14 @@ PLUGIN_INIT(plugin_init)
|
||||
sa.nl_pid = getpid();
|
||||
|
||||
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
_pe("bind()");
|
||||
err(1, "bind()");
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
|
||||
nl_buf = malloc(NL_BUFSZ);
|
||||
if (!nl_buf) {
|
||||
_pe("malloc()");
|
||||
err(1, "malloc()");
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
|
||||
+45
-27
@@ -1,7 +1,7 @@
|
||||
/* Simple pidfile event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -52,7 +52,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
|
||||
ptr = slash++;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
_d("Path too deep, skipping.");
|
||||
dbg("Path too deep, skipping.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -71,30 +71,39 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
||||
return;
|
||||
|
||||
_d("path: %s, mask: %08x", fn, mask);
|
||||
dbg("path: %s, mask: %08x", fn, mask);
|
||||
|
||||
svc = svc_find_by_pidfile(fn);
|
||||
if (!svc) {
|
||||
_d("No matching svc for %s", fn);
|
||||
dbg("No matching svc for %s", fn);
|
||||
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_ident(svc, NULL, 0), fn, svc->pid);
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
|
||||
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
/*
|
||||
* only mark native services as started, the s6 and
|
||||
* systemd style services rely on their respective
|
||||
* readiness notification. Issue #343
|
||||
*/
|
||||
if (svc->notify == SVC_NOTIFY_PID)
|
||||
svc_started(svc);
|
||||
|
||||
if (!svc_has_pidfile(svc)) {
|
||||
_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);
|
||||
}
|
||||
|
||||
if (svc_is_forking(svc)) {
|
||||
pid_t pid;
|
||||
|
||||
pid = pid_file_read(pid_file(svc));
|
||||
pid = pid_file_read(fn);
|
||||
if (pid == -1) {
|
||||
cond_clear(cond);
|
||||
/* handle case when service dies immediately */
|
||||
if (!fexist(fn))
|
||||
cond_clear(cond);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,8 +111,8 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
service_forked(svc);
|
||||
|
||||
if (pid != svc->pid) {
|
||||
_d("Forking service %s changed PID from %d to %d",
|
||||
svc->cmd, svc->pid, pid);
|
||||
dbg("Forking service %s (cmd %s) changed PID from %d to %d",
|
||||
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid);
|
||||
svc->pid = pid;
|
||||
|
||||
/* Complement log in service.c for non-forking services */
|
||||
@@ -111,9 +120,15 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
}
|
||||
}
|
||||
|
||||
cond_set(cond);
|
||||
} else if (mask & IN_DELETE)
|
||||
if (svc->notify == SVC_NOTIFY_PID)
|
||||
cond_set(cond);
|
||||
if (svc->notify == SVC_NOTIFY_PID || svc->notify == SVC_NOTIFY_NONE)
|
||||
service_ready(svc, 1);
|
||||
} else if (mask & IN_DELETE) {
|
||||
cond_clear(cond);
|
||||
if (svc->notify == SVC_NOTIFY_PID)
|
||||
service_ready(svc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* synthesize events in case of new run dirs */
|
||||
@@ -135,7 +150,7 @@ static void pidfile_scandir(struct iwatch *iw, char *dir, int len)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
globfree(&gl);
|
||||
@@ -150,7 +165,7 @@ static void pidfile_handle_dir(struct iwatch *iw, char *dir, char *name, int mas
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
_d("path: %s", path);
|
||||
dbg("path: %s", path);
|
||||
|
||||
iwp = iwatch_find_by_path(iw, path);
|
||||
|
||||
@@ -174,7 +189,7 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
err(1, "invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
@@ -226,6 +241,9 @@ static void pidfile_reconf(void *arg)
|
||||
if (svc_is_changed(svc) || svc_is_starting(svc))
|
||||
continue;
|
||||
|
||||
if (svc->notify == SVC_NOTIFY_PID)
|
||||
service_ready(svc, 1);
|
||||
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
if (cond_get(cond) == COND_ON)
|
||||
continue;
|
||||
@@ -234,9 +252,9 @@ static void pidfile_reconf(void *arg)
|
||||
}
|
||||
|
||||
/*
|
||||
* This will call service_step(), which in turn will schedule itself
|
||||
* as long as stepped services change state. Services going from
|
||||
* WAITING to RUNNING will reassert their conditions in that loop,
|
||||
* This calls service_step(), which in turn schedules itself as
|
||||
* long as stepped services change state. Services going from
|
||||
* PAUSED to RUNNING reassert their conditions in that loop,
|
||||
* which in turn may unlock other services, and so on.
|
||||
*/
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
|
||||
@@ -248,7 +266,7 @@ static void pidfile_init(void *arg)
|
||||
char *path;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
|
||||
err(1, "Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -259,10 +277,10 @@ static void pidfile_init(void *arg)
|
||||
*/
|
||||
path = realpath(_PATH_VARRUN, NULL);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -283,15 +301,15 @@ static void pidfile_init(void *arg)
|
||||
*
|
||||
* service <net/iface/lo> /sbin/dropbear ...
|
||||
*
|
||||
* Which provides the <pid/dropbear> condition, will not be set by
|
||||
* pidfile.so during `initctl reload` because dropbear is still
|
||||
* SIGSTP:ed waiting for <net/iface/lo>.
|
||||
* This service provides the <pid/dropbear> condition, which will not be
|
||||
* set by pidfile.so during `initctl reload` because dropbear is still
|
||||
* SIGSTP:ed (in state PAUSED) waiting for <net/iface/lo>.
|
||||
*/
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
|
||||
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
||||
.depends = { "netlink" },
|
||||
.depends = { "netlink" }, /* bootmisc depends on us */
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* Set kernel variables from /etc/sysctl.conf et al
|
||||
*
|
||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -37,7 +37,7 @@ static void setup(void *arg)
|
||||
glob_t gl;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Setup necessary files for resolvconf
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -37,7 +37,7 @@
|
||||
*/
|
||||
static void setup(void *arg)
|
||||
{
|
||||
_d("Setting up the resolver ...");
|
||||
dbg("Setting up the resolver ...");
|
||||
mkpath("/etc/resolvconf", 0755);
|
||||
mkpath("/etc/resolvconf/run", 0755);
|
||||
mkpath("/var/run/resolvconf", 0755);
|
||||
@@ -45,7 +45,7 @@ static void setup(void *arg)
|
||||
|
||||
touch("/etc/resolvconf/run/enable-updates");
|
||||
chdir("/etc/resolvconf/run/interface");
|
||||
run_parts("/etc/resolvconf/update.d", "-i");
|
||||
run_parts("/etc/resolvconf/update.d", "-i", NULL, 0, 0);
|
||||
chdir("/");
|
||||
}
|
||||
|
||||
|
||||
+28
-11
@@ -1,6 +1,6 @@
|
||||
/* Save and restore RTC using hwclock
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,6 +21,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/rtc.h>
|
||||
@@ -35,7 +37,13 @@
|
||||
#include "plugin.h"
|
||||
|
||||
/* 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)
|
||||
{
|
||||
@@ -89,7 +97,7 @@ static void rtc_save(void *arg)
|
||||
char tz[128];
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -99,10 +107,10 @@ static void rtc_save(void *arg)
|
||||
|
||||
tz_set(tz, sizeof(tz));
|
||||
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");
|
||||
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;
|
||||
goto out;
|
||||
}
|
||||
@@ -131,13 +139,15 @@ static void rtc_restore(void *arg)
|
||||
char tz[128];
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = rtc_open();
|
||||
if (fd < 0)
|
||||
if (fd < 0) {
|
||||
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore.");
|
||||
return;
|
||||
}
|
||||
|
||||
tz_set(tz, sizeof(tz));
|
||||
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
|
||||
@@ -151,7 +161,7 @@ static void rtc_restore(void *arg)
|
||||
invalid:
|
||||
logit(LOG_ERR, "Failed restoring system clock from RTC.");
|
||||
if (EINVAL == errno)
|
||||
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)");
|
||||
logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp);
|
||||
else if (ENOENT == errno)
|
||||
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
||||
else
|
||||
@@ -162,18 +172,18 @@ static void rtc_restore(void *arg)
|
||||
goto out;
|
||||
|
||||
/* 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))
|
||||
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;
|
||||
}
|
||||
|
||||
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
||||
tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */
|
||||
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;
|
||||
goto invalid;
|
||||
}
|
||||
@@ -199,6 +209,13 @@ static plugin_t plugin = {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+12
-8
@@ -1,6 +1,6 @@
|
||||
/* System condition event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -63,7 +63,7 @@ static void sys_update_conds(char *dir, char *name, uint32_t mask)
|
||||
return;
|
||||
|
||||
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))
|
||||
unlink(path);
|
||||
}
|
||||
@@ -82,7 +82,7 @@ static void sys_scandir(struct iwatch *iw, char *dir, int len)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
globfree(&gl);
|
||||
@@ -97,7 +97,7 @@ static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
_d("path: %s", path);
|
||||
dbg("path: %s", 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);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
err(1, "invalid inotify event");
|
||||
return;
|
||||
}
|
||||
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)
|
||||
break;
|
||||
|
||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
dbg("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
@@ -160,24 +160,28 @@ static void sys_init(void *arg)
|
||||
char sysdir[MAX_ARG_LEN];
|
||||
char *path;
|
||||
|
||||
mkpath(_PATH_COND, 0755);
|
||||
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;
|
||||
}
|
||||
|
||||
path = realpath(_PATH_CONDSYS, NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
|
||||
iwatch_exit(&iw_sys);
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
|
||||
.depends = { "bootmisc", },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
/* 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-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2013-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -55,7 +55,7 @@ static void setup(void)
|
||||
|
||||
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)
|
||||
_pe("Failed starting TTY watcher");
|
||||
err(1, "Failed starting TTY watcher");
|
||||
}
|
||||
|
||||
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);
|
||||
if (svc) {
|
||||
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);
|
||||
} else if (svc->pid) {
|
||||
_d("%s: missing ...", svc_ident(svc, NULL, 0));
|
||||
dbg("%s: missing ...", svc_ident(svc, NULL, 0));
|
||||
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);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
err(1, "invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
@@ -103,7 +103,7 @@ static void tty_watcher(void *arg, int fd, int events)
|
||||
if (!ev->mask)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
/* Setup and save random seed at boot/shutdown
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -51,7 +51,7 @@ static void setup(void *arg)
|
||||
int fd, err;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ static void setup(void *arg)
|
||||
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
|
||||
@@ -144,7 +144,7 @@ static void save(void *arg)
|
||||
mode_t prev;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -1,6 +1,6 @@
|
||||
/* User-defined condition event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -52,7 +52,7 @@ static void usr_callback(void *arg, int fd, int events)
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
err(1, "invalid inotify event");
|
||||
return;
|
||||
}
|
||||
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)
|
||||
break;
|
||||
|
||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
dbg("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
@@ -84,14 +84,15 @@ static void usr_init(void *arg)
|
||||
char usrdir[MAX_ARG_LEN];
|
||||
char *path;
|
||||
|
||||
mkpath(_PATH_COND, 0755);
|
||||
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;
|
||||
}
|
||||
|
||||
path = realpath(_PATH_CONDUSR, NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -104,6 +105,7 @@ static void usr_init(void *arg)
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
|
||||
.depends = { "bootmisc", },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+4
-33
@@ -1,6 +1,6 @@
|
||||
/* Console setup (for X)
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,47 +36,18 @@
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
const char *username = "nobody";
|
||||
char line[LINE_SIZE];
|
||||
mode_t prev;
|
||||
#ifdef PAM_CONSOLE
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
prev = umask(0);
|
||||
|
||||
makedir("/var/run/console", 01777);
|
||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
||||
touch(line);
|
||||
|
||||
#ifdef PAM_CONSOLE
|
||||
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
|
||||
write(fd, username, strlen(username));
|
||||
close(fd);
|
||||
if (fexist("/var/run/console/console.lock") && which("pam_console_apply"))
|
||||
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 = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_NETWORK_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", }
|
||||
.hook[HOOK_SVC_PLUGIN] = { .cb = setup },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
@@ -5,6 +5,7 @@ initctl
|
||||
keventd
|
||||
logit
|
||||
reboot
|
||||
runparts
|
||||
sulogin
|
||||
watchdogd
|
||||
Makefile
|
||||
|
||||
+16
-4
@@ -8,7 +8,7 @@ AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
sbin_PROGRAMS = finit initctl
|
||||
pkglibexec_PROGRAMS = getty logit
|
||||
pkglibexec_PROGRAMS = getty logit runparts
|
||||
if SULOGIN
|
||||
pkglibexec_PROGRAMS += sulogin
|
||||
endif
|
||||
@@ -19,6 +19,8 @@ if WATCHDOGD
|
||||
pkglibexec_PROGRAMS += watchdogd
|
||||
endif
|
||||
|
||||
dist_pkglibexec_SCRIPTS = coldplug
|
||||
|
||||
finit_pkglibdir = $(pkglibdir)
|
||||
finit_pkglib_SCRIPTS = rescue.conf sample.conf
|
||||
|
||||
@@ -32,6 +34,11 @@ keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
keventd_CFLAGS += $(lite_CFLAGS)
|
||||
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_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
sulogin_LDADD = -lcrypt
|
||||
@@ -42,8 +49,10 @@ logit_CFLAGS += $(lite_CFLAGS)
|
||||
logit_LDADD = $(lite_LIBS)
|
||||
|
||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
client.c client.h \
|
||||
cond.c cond-w.c cond.h \
|
||||
conf.c conf.h \
|
||||
devmon.c devmon.h \
|
||||
exec.c finit.c finit.h \
|
||||
stty.c \
|
||||
helpers.c helpers.h \
|
||||
@@ -52,11 +61,12 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
mdadm.c mount.c \
|
||||
pid.c pid.h \
|
||||
plugin.c plugin.h private.h \
|
||||
schedule.c schedule.h \
|
||||
runparts.c schedule.c schedule.h \
|
||||
service.c service.h \
|
||||
sig.c sig.h \
|
||||
sm.c sm.h \
|
||||
svc.c svc.h \
|
||||
tmpfiles.c tmpfiles.h \
|
||||
tty.c tty.h \
|
||||
util.c util.h \
|
||||
utmp-api.c utmp-api.h
|
||||
@@ -64,8 +74,10 @@ if LOGROTATE
|
||||
finit_SOURCES += logrotate.c
|
||||
endif
|
||||
|
||||
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
|
||||
pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.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 += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
@@ -77,7 +89,7 @@ endif
|
||||
|
||||
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||
client.c client.h cond.c cond.h reboot.c \
|
||||
serv.c serv.h svc.h util.c util.h
|
||||
serv.c serv.h svc.h util.c util.h log.h
|
||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* External client API, using UNIX domain socket.
|
||||
*
|
||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -45,11 +45,11 @@
|
||||
#include "log.h"
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "schedule.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "util.h"
|
||||
|
||||
extern svc_t *wdog;
|
||||
static uev_t api_watcher;
|
||||
|
||||
static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
|
||||
@@ -62,8 +62,10 @@ static int stop(svc_t *svc, void *user_data)
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
svc_stop(svc);
|
||||
service_step(svc);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -73,8 +75,10 @@ static int start(svc_t *svc, void *user_data)
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
svc_start(svc);
|
||||
service_step(svc);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -89,8 +93,11 @@ static int restart(svc_t *svc, void *user_data)
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
svc_start(svc);
|
||||
if (!svc_is_running(svc))
|
||||
return start(svc, user_data);
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
service_stop(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
@@ -103,6 +110,8 @@ static int reload(svc_t *svc, void *user_data)
|
||||
|
||||
if (svc_is_blocked(svc))
|
||||
svc_start(svc);
|
||||
else
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
service_step(svc);
|
||||
@@ -203,53 +212,76 @@ static svc_t *do_find(char *buf, size_t len)
|
||||
|
||||
static svc_t *do_find_byc(char *buf, size_t len)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
char *input;
|
||||
|
||||
input = sanitize(buf, len);
|
||||
if (!input) {
|
||||
_d("Invalid input");
|
||||
dbg("Invalid input");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
_d("Comparing input %s with cond %s", input, cond);
|
||||
if (string_compare(cond, input))
|
||||
return svc;
|
||||
}
|
||||
|
||||
_d("None found");
|
||||
return NULL;
|
||||
return svc_find_by_cond(input);
|
||||
}
|
||||
|
||||
static int do_reboot(int cmd, char *buf, size_t len)
|
||||
static void bypass_shutdown(void *);
|
||||
struct wq emergency = { .cb = bypass_shutdown };
|
||||
|
||||
static void bypass_shutdown(void *unused)
|
||||
{
|
||||
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
|
||||
do_shutdown(halt);
|
||||
}
|
||||
|
||||
static int do_reboot(int cmd, int timeout, char *buf, size_t len)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
switch (cmd) {
|
||||
case INIT_CMD_REBOOT:
|
||||
_d("reboot");
|
||||
halt = SHUT_REBOOT;
|
||||
break;
|
||||
|
||||
case INIT_CMD_HALT:
|
||||
halt = SHUT_HALT;
|
||||
break;
|
||||
|
||||
case INIT_CMD_POWEROFF:
|
||||
halt = SHUT_OFF;
|
||||
break;
|
||||
|
||||
case INIT_CMD_SUSPEND:
|
||||
break;
|
||||
|
||||
default:
|
||||
return 255;
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
emergency.delay = timeout * 1000;
|
||||
schedule_work(&emergency);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case INIT_CMD_REBOOT:
|
||||
dbg("reboot");
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(6);
|
||||
break;
|
||||
|
||||
case INIT_CMD_HALT:
|
||||
_d("halt");
|
||||
dbg("halt");
|
||||
halt = SHUT_HALT;
|
||||
service_runlevel(0);
|
||||
break;
|
||||
|
||||
case INIT_CMD_POWEROFF:
|
||||
_d("poweroff");
|
||||
dbg("poweroff");
|
||||
halt = SHUT_OFF;
|
||||
service_runlevel(0);
|
||||
break;
|
||||
|
||||
case INIT_CMD_SUSPEND:
|
||||
_d("suspend");
|
||||
dbg("suspend");
|
||||
sync();
|
||||
rc = suspend();
|
||||
if (rc) {
|
||||
@@ -261,7 +293,7 @@ static int do_reboot(int cmd, char *buf, size_t len)
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = 255;
|
||||
/* Handled previously */
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -290,7 +322,7 @@ static void send_svc(int sd, svc_t *svc)
|
||||
|
||||
len = write(sd, svc, 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)
|
||||
@@ -302,7 +334,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
sd = accept(w->fd, NULL, NULL);
|
||||
if (sd < 0) {
|
||||
_pe("Failed serving API request");
|
||||
err(1, "Failed serving API request");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -319,19 +351,49 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
if (EAGAIN == errno)
|
||||
break;
|
||||
|
||||
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||
/* we get here when client restarts itself */
|
||||
if (ECONNRESET == errno)
|
||||
break;
|
||||
|
||||
errx(1, "Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||
_e("Invalid initctl request");
|
||||
errx(1, "Invalid initctl request");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RELOAD:
|
||||
case INIT_CMD_START_SVC:
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
case INIT_CMD_STOP_SVC:
|
||||
case INIT_CMD_RELOAD_SVC:
|
||||
case INIT_CMD_REBOOT:
|
||||
case INIT_CMD_HALT:
|
||||
case INIT_CMD_POWEROFF:
|
||||
case INIT_CMD_SUSPEND:
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||
warnx("Unsupported command in runlevel S and 6/0.");
|
||||
goto leave;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RUNLVL:
|
||||
/* Allow changing cfglevel in runlevel S */
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||
if (runlevel != INIT_LEVEL) {
|
||||
warnx("Cannot abort runlevel 6/0.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (rq.runlevel) {
|
||||
case 's':
|
||||
case 'S':
|
||||
@@ -339,57 +401,70 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
/* fallthrough */
|
||||
|
||||
case '0'...'9':
|
||||
_d("Setting new runlevel %c", rq.runlevel);
|
||||
dbg("Setting new runlevel %c", rq.runlevel);
|
||||
lvl = rq.runlevel - '0';
|
||||
if (lvl == 0)
|
||||
halt = SHUT_OFF;
|
||||
if (lvl == 6)
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(lvl);
|
||||
|
||||
/* User requested change in next runlevel */
|
||||
if (runlevel == INIT_LEVEL)
|
||||
cfglevel = lvl;
|
||||
else
|
||||
service_runlevel(lvl);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported runlevel: %d", rq.runlevel);
|
||||
dbg("Unsupported runlevel: %d", rq.runlevel);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case INIT_CMD_DEBUG:
|
||||
_d("debug");
|
||||
dbg("debug");
|
||||
log_debug();
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
||||
_d("reload");
|
||||
dbg("reload");
|
||||
service_reload_dynamic();
|
||||
break;
|
||||
|
||||
case INIT_CMD_START_SVC:
|
||||
_d("start %s", rq.data);
|
||||
dbg("start %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_start(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
_d("restart %s", rq.data);
|
||||
dbg("restart %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_restart(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_STOP_SVC:
|
||||
_d("stop %s", rq.data);
|
||||
dbg("stop %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_stop(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD_SVC:
|
||||
_d("reload %s", rq.data);
|
||||
dbg("reload %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_reload(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_PLUGINS:
|
||||
result = plugin_list(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_PLUGIN_DEPS:
|
||||
result = plugin_deps(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_RUNLEVEL:
|
||||
_d("get runlevel");
|
||||
dbg("get runlevel");
|
||||
rq.runlevel = runlevel;
|
||||
rq.sleeptime = prevlevel;
|
||||
break;
|
||||
@@ -398,21 +473,21 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
case INIT_CMD_HALT:
|
||||
case INIT_CMD_POWEROFF:
|
||||
case INIT_CMD_SUSPEND:
|
||||
result = do_reboot(rq.cmd, rq.data, sizeof(rq.data));
|
||||
result = do_reboot(rq.cmd, rq.sleeptime, rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_ACK:
|
||||
_d("Client failed reading ACK");
|
||||
dbg("Client failed reading ACK");
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_WDOG_HELLO:
|
||||
_d("wdog hello");
|
||||
dbg("wdog hello");
|
||||
if (rq.runlevel <= 0) {
|
||||
result = 1;
|
||||
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);
|
||||
if (!svc) {
|
||||
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
||||
@@ -436,7 +511,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
break;
|
||||
|
||||
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
|
||||
* simultaneous client connections, but will
|
||||
@@ -447,32 +522,46 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_SVC_QUERY:
|
||||
_d("svc query: %s", rq.data);
|
||||
dbg("svc query: %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_query(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_SVC_FIND:
|
||||
_d("svc find: %s", rq.data);
|
||||
dbg("svc find: %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
|
||||
goto leave;
|
||||
|
||||
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));
|
||||
send_svc(sd, do_find_byc(rq.data, sizeof(rq.data)));
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_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));
|
||||
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
|
||||
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:
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
dbg("Unsupported cmd: %d", rq.cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -482,7 +571,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
rq.cmd = INIT_CMD_ACK;
|
||||
len = write(sd, &rq, sizeof(rq));
|
||||
if (len != sizeof(rq))
|
||||
_d("Failed sending ACK/NACK back to client");
|
||||
dbg("Failed sending ACK/NACK back to client");
|
||||
}
|
||||
|
||||
leave:
|
||||
@@ -493,7 +582,7 @@ leave:
|
||||
error:
|
||||
api_exit();
|
||||
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)
|
||||
@@ -506,10 +595,10 @@ int api_init(uev_ctx_t *ctx)
|
||||
int uid, gid;
|
||||
int sd;
|
||||
|
||||
_d("Setting up external API socket ...");
|
||||
dbg("Setting up external API socket ...");
|
||||
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
||||
if (-1 == sd) {
|
||||
_pe("Failed starting external API socket");
|
||||
err(1, "Failed starting external API socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -525,14 +614,14 @@ int api_init(uev_ctx_t *ctx)
|
||||
goto error;
|
||||
|
||||
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);
|
||||
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
|
||||
return 0;
|
||||
|
||||
error:
|
||||
_pe("Failed initializing API socket");
|
||||
err(1, "Failed initializing API socket");
|
||||
umask(oldmask);
|
||||
close(sd);
|
||||
return 1;
|
||||
|
||||
+32
-29
@@ -1,6 +1,6 @@
|
||||
/* Finit control group support functions
|
||||
*
|
||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -63,16 +63,16 @@ static void cgset(const char *path, char *ctrl, char *prop)
|
||||
{
|
||||
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) {
|
||||
_e("Missing path or controller, skipping!");
|
||||
errx(1, "Missing path or controller, skipping!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prop) {
|
||||
prop = strchr(ctrl, '.');
|
||||
if (!prop) {
|
||||
_e("Invalid cgroup ctrl syntax: %s", ctrl);
|
||||
errx(1, "Invalid cgroup ctrl syntax: %s", ctrl);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,20 +81,20 @@ static void cgset(const char *path, char *ctrl, char *prop)
|
||||
|
||||
val = strchr(prop, ':');
|
||||
if (!val) {
|
||||
_e("Missing cgroup ctrl value, prop %s", prop);
|
||||
errx(1, "Missing cgroup ctrl value, prop %s", prop);
|
||||
return;
|
||||
}
|
||||
*val++ = 0;
|
||||
|
||||
/* disallow sneaky relative paths */
|
||||
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;
|
||||
}
|
||||
|
||||
_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))
|
||||
_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;
|
||||
|
||||
_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 (mkdir(path, 0755)) {
|
||||
_pe("Failed creating cgroup %s", path);
|
||||
err(1, "Failed creating cgroup %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
/* enable detected controllers on domain groups */
|
||||
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
|
||||
_pe("Failed enabling %s for %s", controllers, path);
|
||||
err(1, "Failed enabling %s for %s", controllers, path);
|
||||
}
|
||||
|
||||
if (!cfg || !cfg[0])
|
||||
@@ -123,14 +123,14 @@ static void group_init(char *path, int leaf, const char *cfg)
|
||||
|
||||
s = strdupa(cfg);
|
||||
if (!s) {
|
||||
_pe("Failed activating cgroup cfg for %s", path);
|
||||
err(1, "Failed activating cgroup cfg for %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("%s <=> %s", path, s);
|
||||
dbg("%s <=> %s", path, s);
|
||||
ptr = strtok(s, ",");
|
||||
while (ptr) {
|
||||
_d("ptr: %s", ptr);
|
||||
dbg("ptr: %s", ptr);
|
||||
if (!strncmp("mem.", ptr, 4))
|
||||
cgset(path, "memory", &ptr[4]);
|
||||
else
|
||||
@@ -144,7 +144,7 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
||||
dbg("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
||||
if (pid < 0 || pid == 1) {
|
||||
errno = EINVAL;
|
||||
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 */
|
||||
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));
|
||||
|
||||
@@ -211,13 +211,13 @@ static void cgroup_handle_event(char *event, uint32_t mask)
|
||||
char *ptr;
|
||||
FILE *fp;
|
||||
|
||||
_d("event: '%s', mask: %08x", event, mask);
|
||||
dbg("event: '%s', mask: %08x", event, mask);
|
||||
if (!(mask & IN_MODIFY))
|
||||
return;
|
||||
|
||||
fp = fopen(event, "r");
|
||||
if (!fp) {
|
||||
_d("Failed opening %s, skipping ...", event);
|
||||
dbg("Failed opening %s, skipping ...", event);
|
||||
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);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
err(1, "invalid inotify event");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AUTO_RELOAD
|
||||
#ifdef AUTO_RELOAD
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
#endif
|
||||
@@ -364,12 +364,12 @@ int cgroup_add(char *name, char *cfg, int is_protected)
|
||||
if (!cg) {
|
||||
cg = malloc(sizeof(struct cg));
|
||||
if (!cg) {
|
||||
_pe("Failed allocating 'struct cg' for %s", name);
|
||||
err(1, "Failed allocating 'struct cg' for %s", name);
|
||||
return -1;
|
||||
}
|
||||
cg->name = strdup(name);
|
||||
if (!cg->name) {
|
||||
_pe("Failed setting cgroup name %s", name);
|
||||
err(1, "Failed setting cgroup name %s", name);
|
||||
free(cg);
|
||||
return -1;
|
||||
}
|
||||
@@ -379,7 +379,7 @@ int cgroup_add(char *name, char *cfg, int is_protected)
|
||||
|
||||
cg->cfg = strdup(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);
|
||||
free(cg->name);
|
||||
free(cg);
|
||||
@@ -414,7 +414,7 @@ int cgroup_del(char *dir)
|
||||
}
|
||||
|
||||
if (rmdir(dir) && errno != ENOENT) {
|
||||
_d("Failed removing %s: %s", dir, strerror(errno));
|
||||
dbg("Failed removing %s: %s", dir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -469,7 +469,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
else if (errno == EPERM) /* Probably inside an unpriviliged container */
|
||||
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
|
||||
else
|
||||
_pe("Failed mounting cgroup v2");
|
||||
err(1, "Failed mounting cgroup v2");
|
||||
avail = 0;
|
||||
return;
|
||||
}
|
||||
@@ -478,7 +478,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
/* Find available controllers */
|
||||
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
|
||||
if (!fp) {
|
||||
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
|
||||
err(1, "Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
|
||||
abort:
|
||||
umount(FINIT_CGPATH);
|
||||
avail = 0;
|
||||
@@ -505,14 +505,17 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
|
||||
/* Enable all controllers */
|
||||
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;
|
||||
}
|
||||
|
||||
/* prepare cgroup.events watcher */
|
||||
fd = iwatch_init(&iw_cgroup);
|
||||
if (fd == -1)
|
||||
goto abort;
|
||||
|
||||
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);
|
||||
close(fd);
|
||||
goto abort;
|
||||
@@ -526,7 +529,7 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
|
||||
/* Move ourselves to init (best effort, otherwise run in 'root' group */
|
||||
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) {
|
||||
_pe("Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init");
|
||||
err(1, "Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init");
|
||||
uev_io_stop(&cgw);
|
||||
iwatch_exit(&iw_cgroup);
|
||||
close(fd);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* Finit control group support functions
|
||||
*
|
||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+11
-9
@@ -1,6 +1,6 @@
|
||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
@@ -39,6 +38,7 @@
|
||||
|
||||
#include "cgutil.h"
|
||||
#include "initctl.h"
|
||||
#include "log.h"
|
||||
|
||||
#define CDIM plain ? "" : "\e[2m"
|
||||
#define CRST plain ? "" : "\e[0m"
|
||||
@@ -244,7 +244,7 @@ static float cgroup_cpuload(struct cg *cg)
|
||||
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
err(1, "Cannot open %s", fn);
|
||||
ERR(72, "Cannot open %s", fn);
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
uint64_t curr;
|
||||
@@ -280,13 +280,13 @@ static struct cg *append(char *path)
|
||||
if (access(fn, F_OK)) {
|
||||
/* older kernels, 4.19, don't have summary cpu.stat in root */
|
||||
if (strcmp(path, FINIT_CGPATH))
|
||||
warn("not a cgroup path with cpu controller, %s", path);
|
||||
WARN("not a cgroup path with cpu controller, %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cg = calloc(1, sizeof(struct cg));
|
||||
if (!cg)
|
||||
err(1, "failed allocating struct cg");
|
||||
ERR(71, "failed allocating struct cg");
|
||||
|
||||
cg->cg_path = strdup(path);
|
||||
if (list)
|
||||
@@ -296,7 +296,7 @@ static struct cg *append(char *path)
|
||||
item.key = cg->cg_path;
|
||||
item.data = cg;
|
||||
if (!hsearch(item, ENTER))
|
||||
err(1, "failed adding to hash table");
|
||||
ERR(70, "failed adding to hash table");
|
||||
|
||||
return cg;
|
||||
}
|
||||
@@ -434,7 +434,6 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
||||
pid_comm(pid, comm, sizeof(comm));
|
||||
if (pid_cmdline(pid, buf, sizeof(buf))) {
|
||||
char proc[ttcols];
|
||||
int len;
|
||||
|
||||
switch (mode) {
|
||||
case 1:
|
||||
@@ -456,6 +455,8 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
||||
if (plain) {
|
||||
strlcat(row, proc, rplen);
|
||||
} else {
|
||||
int len;
|
||||
|
||||
strlcat(row, CDIM, rlen);
|
||||
strlcat(row, proc, rlen);
|
||||
|
||||
@@ -600,7 +601,6 @@ int show_cgtop(char *arg)
|
||||
uev_t timer, input, sigint, sigterm, sigquit;
|
||||
char path[512];
|
||||
uev_ctx_t ctx;
|
||||
int flags;
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
@@ -610,7 +610,7 @@ int show_cgtop(char *arg)
|
||||
}
|
||||
|
||||
if (!hcreate(ttrows + 25))
|
||||
err(1, "failed creating hash table");
|
||||
ERR(70, "failed creating hash table");
|
||||
|
||||
sysinfo(&si);
|
||||
total_ram = si.totalram * si.mem_unit;
|
||||
@@ -619,6 +619,8 @@ int show_cgtop(char *arg)
|
||||
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
|
||||
|
||||
if (!ionce && !plain) {
|
||||
int flags;
|
||||
|
||||
atexit(cleanup);
|
||||
ttraw();
|
||||
hidecursor();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* Cgroup utility functions for initctl
|
||||
*
|
||||
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+57
-29
@@ -1,6 +1,6 @@
|
||||
/* External client API, using UNIX domain socket.
|
||||
*
|
||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,13 +21,13 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "client.h"
|
||||
#include "log.h"
|
||||
|
||||
static int sd = -1;
|
||||
|
||||
@@ -48,7 +48,7 @@ int client_connect(void)
|
||||
if (errno != ENOENT)
|
||||
warnx("Failed connecting to finit");
|
||||
close(sd);
|
||||
return -1;
|
||||
sd = -1;
|
||||
}
|
||||
|
||||
return sd;
|
||||
@@ -69,26 +69,40 @@ int client_disconnect(void)
|
||||
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 };
|
||||
int sd, result = 255;
|
||||
int rc;
|
||||
|
||||
sd = client_connect();
|
||||
if (-1 == sd)
|
||||
if (client_connect() == -1)
|
||||
return -1;
|
||||
|
||||
pfd.fd = sd;
|
||||
pfd.events = POLLOUT;
|
||||
if (poll(&pfd, 1, 2000) <= 0) {
|
||||
warn("Timed out waiting for Finit, errno %d", errno);
|
||||
goto exit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(sd, rq, len) != len) {
|
||||
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;
|
||||
@@ -96,39 +110,56 @@ int client_send(struct init_request *rq, ssize_t len)
|
||||
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
|
||||
if (rc) {
|
||||
if (errno == EINTR) /* shutdown/reboot */
|
||||
goto exit;
|
||||
return -1;
|
||||
warn("poll(), errno %d", errno);
|
||||
} else
|
||||
warnx("Timed out waiting for reply from Finit.");
|
||||
goto exit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read(sd, rq, len) != len) {
|
||||
warn("Failed reading reply from Finit, errno %d", errno);
|
||||
goto exit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rq->cmd == INIT_CMD_NACK)
|
||||
result = 1;
|
||||
else
|
||||
result = 0;
|
||||
exit:
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int client_send(struct init_request *rq, ssize_t len)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (client_request(rq, len))
|
||||
rc = 255;
|
||||
|
||||
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)
|
||||
{
|
||||
int sd = -1;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SVC_ITER,
|
||||
};
|
||||
static svc_t svc;
|
||||
|
||||
sd = client_connect();
|
||||
if (sd == -1)
|
||||
if (client_connect() == -1)
|
||||
return NULL;
|
||||
|
||||
if (first)
|
||||
@@ -147,24 +178,22 @@ svc_t *client_svc_iterator(int first)
|
||||
|
||||
return &svc;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
warn("Failed communicating with finit, error %d", errno);
|
||||
client_disconnect();
|
||||
sd = -1;
|
||||
|
||||
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 = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = cmd,
|
||||
};
|
||||
static svc_t svc;
|
||||
int sd = -1;
|
||||
|
||||
sd = client_connect();
|
||||
if (sd == -1)
|
||||
if (client_connect() == -1)
|
||||
return NULL;
|
||||
|
||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||
@@ -180,20 +209,19 @@ svc_t *do_cmd(int cmd, const char *arg)
|
||||
return &svc;
|
||||
error:
|
||||
client_disconnect();
|
||||
perror("Failed communicating with finit");
|
||||
warn("Failed communicating with finit, error %d", errno);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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.
|
||||
*
|
||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,8 +29,12 @@
|
||||
|
||||
int client_connect (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_command (int cmd);
|
||||
|
||||
svc_t *client_svc_iterator (int first);
|
||||
svc_t *client_svc_find (const char *arg);
|
||||
svc_t *client_svc_find_by_cond (const char *arg);
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
# With the daemon mode ('mdev -df') we no longer need the hotplug helper
|
||||
echo "" > /proc/sys/kernel/hotplug
|
||||
|
||||
# Wait for 'mdev -df' to set up its event queue.
|
||||
sleep 1
|
||||
|
||||
# Trigger 'add' event for all devices and subsystems to load all modules
|
||||
# and any firmware needed by the hardware.
|
||||
find /sys/devices -name uevent -print | while IFS='' read X; do echo add > $X; done
|
||||
+144
-42
@@ -36,6 +36,70 @@
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
struct cond_boot {
|
||||
TAILQ_ENTRY(cond_boot) link;
|
||||
char *name;
|
||||
};
|
||||
static TAILQ_HEAD(, cond_boot) cond_boot_list = TAILQ_HEAD_INITIALIZER(cond_boot_list);
|
||||
|
||||
|
||||
/*
|
||||
* Parse finit.cond=cond[,cond[,...]] from command line. It creates a
|
||||
* temporary linked list of conds which are freed when finit reaches
|
||||
* cond_init(), which calls cond_boot_strap() to realize the conditions
|
||||
* in the file system as regular static conditions. Policy, however,
|
||||
* dictates that these are read-only and should not be possible to
|
||||
* manage from initctl.
|
||||
*/
|
||||
void cond_boot_parse(char *arg)
|
||||
{
|
||||
struct cond_boot *node;
|
||||
|
||||
if (!arg)
|
||||
return;
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node) {
|
||||
err(1, "Out of memory cannot track boot conditions");
|
||||
return;
|
||||
}
|
||||
|
||||
node->name = strdup(arg);
|
||||
TAILQ_INSERT_HEAD(&cond_boot_list, node, link);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert all conditions read from the kernel command line (above) into
|
||||
* proper finit conditions.
|
||||
*/
|
||||
static void cond_boot_strap(void)
|
||||
{
|
||||
struct cond_boot *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &cond_boot_list, link, tmp) {
|
||||
TAILQ_REMOVE(&cond_boot_list, node, link);
|
||||
if (node->name) {
|
||||
char *ptr;
|
||||
|
||||
ptr = strtok(node->name, ",");
|
||||
while (ptr) {
|
||||
char cond[strlen(ptr) + 6];
|
||||
|
||||
if (strncmp(ptr, "boot/", 5))
|
||||
snprintf(cond, sizeof(cond), "boot/%s", ptr);
|
||||
else
|
||||
snprintf(cond, sizeof(cond), "%s", ptr);
|
||||
cond_set_oneshot_noupdate(cond);
|
||||
|
||||
ptr = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(node->name);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The service condition name is constructed from the 'pid/' prefix and
|
||||
* the unique NAME:ID tuple that identify each process in Finit. Here
|
||||
@@ -111,7 +175,7 @@ static void cond_bump_reconf(void)
|
||||
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)
|
||||
@@ -121,35 +185,53 @@ static int cond_checkpath(const char *path)
|
||||
strlcpy(buf, path, sizeof(buf));
|
||||
dir = dirname(buf);
|
||||
if (!dir) {
|
||||
_e("Invalid path '%s' for condition", path);
|
||||
errx(1, "Invalid path '%s' for condition", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
char fn[PATH_MAX];
|
||||
struct dirent *d;
|
||||
DIR *dir;
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
while ((d = readdir(dir))) {
|
||||
paste(fn, sizeof(fn), path, d->d_name);
|
||||
remove(fn);
|
||||
nftw(path, do_delete, 20, FTW_DEPTH);
|
||||
if (remove(path)) {
|
||||
switch (errno) {
|
||||
case ENOENT: /* Ignore */
|
||||
case ENOTEMPTY: /* Only at shutdown and for directories */
|
||||
break;
|
||||
default:
|
||||
err(1, "Failed removing condition path %s", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state next)
|
||||
@@ -157,20 +239,19 @@ int cond_set_path(const char *path, enum cond_state next)
|
||||
enum cond_state prev;
|
||||
unsigned int rgen;
|
||||
|
||||
_d("%s <= %d", path, next);
|
||||
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen) {
|
||||
_e("Unable to read configuration generation (%s)", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbg("%s <= %d", path, next);
|
||||
prev = cond_get_path(path);
|
||||
|
||||
switch (next) {
|
||||
case COND_ON:
|
||||
if (cond_checkpath(path))
|
||||
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);
|
||||
break;
|
||||
|
||||
@@ -183,14 +264,14 @@ int cond_set_path(const char *path, enum cond_state next)
|
||||
cond_delpath(path);
|
||||
break;
|
||||
default:
|
||||
_pe("Failed removing condition '%s'", path);
|
||||
err(1, "Failed removing condition '%s'", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
_e("Invalid condition state");
|
||||
errx(1, "Invalid condition state");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -203,13 +284,19 @@ int cond_update(const char *name)
|
||||
svc_t *svc, *iter = NULL;
|
||||
int affects = 0;
|
||||
|
||||
_d("%s", name);
|
||||
dbg("%s", name);
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
||||
continue;
|
||||
|
||||
affects++;
|
||||
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -218,7 +305,7 @@ int cond_update(const char *name)
|
||||
|
||||
int cond_set_noupdate(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
dbg("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return 1;
|
||||
|
||||
@@ -230,7 +317,7 @@ int cond_set_noupdate(const char *name)
|
||||
|
||||
void cond_set(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
dbg("%s", name);
|
||||
if (cond_set_noupdate(name))
|
||||
return;
|
||||
|
||||
@@ -245,13 +332,13 @@ int cond_set_oneshot_noupdate(const char *name)
|
||||
return 1;
|
||||
|
||||
path = cond_path(name);
|
||||
_d("%s => %s", name, path);
|
||||
dbg("%s => %s", name, path);
|
||||
|
||||
if (cond_checkpath(path))
|
||||
return 1;
|
||||
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
|
||||
_pe("Failed creating onshot cond %s", name);
|
||||
err(1, "Failed creating onshot cond %s", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -260,7 +347,7 @@ int cond_set_oneshot_noupdate(const char *name)
|
||||
|
||||
void cond_set_oneshot(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
dbg("%s", name);
|
||||
if (cond_set_oneshot_noupdate(name))
|
||||
return;
|
||||
|
||||
@@ -269,7 +356,7 @@ void cond_set_oneshot(const char *name)
|
||||
|
||||
int cond_clear_noupdate(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
dbg("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return 1;
|
||||
|
||||
@@ -281,7 +368,7 @@ int cond_clear_noupdate(const char *name)
|
||||
|
||||
void cond_clear(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
dbg("%s", name);
|
||||
if (cond_clear_noupdate(name))
|
||||
return;
|
||||
|
||||
@@ -290,7 +377,7 @@ void cond_clear(const char *name)
|
||||
|
||||
void cond_reload(void)
|
||||
{
|
||||
_d("");
|
||||
dbg("");
|
||||
|
||||
cond_bump_reconf();
|
||||
}
|
||||
@@ -307,12 +394,12 @@ static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct
|
||||
|
||||
nm = strstr((char *)fpath, COND_BASE);
|
||||
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;
|
||||
}
|
||||
|
||||
nm += strlen(COND_BASE);
|
||||
_d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
|
||||
dbg("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
|
||||
if (set)
|
||||
cond_set(nm);
|
||||
else
|
||||
@@ -337,7 +424,7 @@ static int deassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
*/
|
||||
void cond_reassert(const char *pat)
|
||||
{
|
||||
_d("%s", pat);
|
||||
dbg("%s", pat);
|
||||
nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
|
||||
}
|
||||
|
||||
@@ -346,20 +433,35 @@ void cond_reassert(const char *pat)
|
||||
*/
|
||||
void cond_deassert(const char *pat)
|
||||
{
|
||||
_d("%s", pat);
|
||||
dbg("%s", pat);
|
||||
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)
|
||||
{
|
||||
char path[MAX_ARG_LEN];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cond_bump_reconf();
|
||||
cond_boot_strap();
|
||||
}
|
||||
|
||||
void cond_exit(void)
|
||||
{
|
||||
cond_delpath(_PATH_COND);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ enum cond_state cond_get_agg(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;
|
||||
|
||||
if (!name || !names)
|
||||
|
||||
+9
-3
@@ -6,11 +6,13 @@
|
||||
#include "svc.h"
|
||||
|
||||
#define COND_BASE "finit/cond"
|
||||
#define COND_DEV "dev/"
|
||||
#define COND_PID "pid/"
|
||||
#define COND_SYS "sys/"
|
||||
#define COND_USR "usr/"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||
#define _PATH_CONDDEV _PATH_COND COND_DEV
|
||||
#define _PATH_CONDPID _PATH_COND COND_PID
|
||||
#define _PATH_CONDSYS _PATH_COND COND_SYS
|
||||
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||
@@ -31,6 +33,7 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
void cond_boot_parse (char *arg);
|
||||
int cond_update (const char *name);
|
||||
int cond_set_path (const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
@@ -38,14 +41,17 @@ void cond_set_oneshot (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
|
||||
int cond_set_noupdate (const char *name);
|
||||
int cond_set_oneshot_noupdate(const char *name);
|
||||
int cond_clear_noupdate(const char *name);
|
||||
int cond_set_noupdate(const char *name);
|
||||
int cond_set_oneshot_noupdate(const char *name);
|
||||
int cond_clear_noupdate(const char *name);
|
||||
|
||||
void cond_reassert (const char *pat);
|
||||
void cond_deassert (const char *pat);
|
||||
|
||||
int cond_is_available(void);
|
||||
|
||||
void cond_init (void);
|
||||
void cond_exit (void);
|
||||
|
||||
#endif /* FINIT_COND_H_ */
|
||||
|
||||
|
||||
+596
-98
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -1,6 +1,6 @@
|
||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -43,6 +43,9 @@ int conf_changed (char *file);
|
||||
int conf_monitor (void);
|
||||
|
||||
void conf_reset_env (void);
|
||||
void conf_saverc (void);
|
||||
void conf_save_exec_order (svc_t *svc, char *cmdline, int result);
|
||||
void conf_save_service (int type, char *cfg, char *file);
|
||||
void conf_parse_cmdline (int argc, char *argv[]);
|
||||
int conf_parse_runlevels (char *runlevels);
|
||||
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-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "helpers.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "iwatch.h"
|
||||
|
||||
static struct iwatch iw_devmon;
|
||||
static uev_t devw;
|
||||
static int fd;
|
||||
|
||||
struct dev_node {
|
||||
TAILQ_ENTRY(dev_node) link;
|
||||
char *name;
|
||||
size_t refcnt;
|
||||
};
|
||||
|
||||
static TAILQ_HEAD(, dev_node) dev_node_list = TAILQ_HEAD_INITIALIZER(dev_node_list);
|
||||
|
||||
static struct dev_node *find_node(const char *cond)
|
||||
{
|
||||
struct dev_node *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &dev_node_list, link, tmp) {
|
||||
if (string_compare(node->name, cond))
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drop_node(struct dev_node *node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
node->refcnt--;
|
||||
if (node->refcnt)
|
||||
return;
|
||||
|
||||
TAILQ_REMOVE(&dev_node_list, node, link);
|
||||
free(node->name);
|
||||
free(node);
|
||||
}
|
||||
|
||||
void devmon_add_cond(const char *cond)
|
||||
{
|
||||
struct dev_node *node;
|
||||
|
||||
if (!cond || strncmp(cond, "dev/", 4)) {
|
||||
dbg("no match");
|
||||
return;
|
||||
}
|
||||
|
||||
// dbg("%s", cond);
|
||||
node = find_node(cond);
|
||||
if (node) {
|
||||
node->refcnt++;
|
||||
return;
|
||||
}
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node) {
|
||||
fail:
|
||||
warn("failed allocating node %s", cond);
|
||||
return;
|
||||
}
|
||||
|
||||
node->name = strdup(cond);
|
||||
if (!node->name) {
|
||||
free(node);
|
||||
goto fail;
|
||||
}
|
||||
node->refcnt = 1;
|
||||
|
||||
TAILQ_INSERT_TAIL(&dev_node_list, node, link);
|
||||
}
|
||||
|
||||
void devmon_del_cond(const char *cond)
|
||||
{
|
||||
if (!cond || strcmp(cond, "dev/"))
|
||||
return;
|
||||
|
||||
drop_node(find_node(cond));
|
||||
}
|
||||
|
||||
static int devmon_add_path(struct iwatch *iw, char *path)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
ptr = strstr(path, "dev/");
|
||||
if (ptr) {
|
||||
char *slash;
|
||||
|
||||
ptr += 4;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
ptr = slash++;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
dbg("Path too deep, skipping.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iwatch_add1(iw, path, IN_CREATE | IN_DELETE);
|
||||
}
|
||||
|
||||
static void devmon_update_conds(char *dir, char *name, uint32_t mask)
|
||||
{
|
||||
char fn[PATH_MAX];
|
||||
char *cond;
|
||||
|
||||
paste(fn, sizeof(fn), dir, name);
|
||||
cond = &fn[1];
|
||||
|
||||
// dbg("path: %s, mask: %08x, cond: %s", fn, mask, cond);
|
||||
if (!find_node(cond)) {
|
||||
// dbg("unregistered, skipping %s", cond);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mask & IN_CREATE)
|
||||
cond_set(cond);
|
||||
else if (mask & IN_DELETE)
|
||||
cond_clear(cond);
|
||||
}
|
||||
|
||||
/* synthesize events in case of new run dirs */
|
||||
static void devmon_scandir(struct iwatch *iw, char *dir, int len)
|
||||
{
|
||||
char path[len + 6];
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
int rc;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/*", dir);
|
||||
rc = glob(path, GLOB_NOSORT, NULL, &gl);
|
||||
if (rc && rc != GLOB_NOMATCH)
|
||||
return;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
// dbg("scan found %s", gl.gl_pathv[i]);
|
||||
devmon_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
|
||||
/*
|
||||
* create/remove sub-directory in monitored directory
|
||||
*/
|
||||
static void devmon_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||
{
|
||||
char path[strlen(dir) + strlen(name) + 2];
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
dbg("path: %s", path);
|
||||
|
||||
iwp = iwatch_find_by_path(iw, path);
|
||||
|
||||
if (mask & IN_CREATE) {
|
||||
if (!iwp) {
|
||||
if (!devmon_add_path(iw, path))
|
||||
devmon_scandir(iw, path, sizeof(path));
|
||||
}
|
||||
} else if (mask & IN_DELETE) {
|
||||
if (iwp)
|
||||
iwatch_del(&iw_devmon, iwp);
|
||||
}
|
||||
}
|
||||
|
||||
static void devmon_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
err(1, "invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
/* Find base path for this event */
|
||||
iwp = iwatch_find_by_wd(&iw_devmon, ev->wd);
|
||||
if (!iwp)
|
||||
continue;
|
||||
|
||||
if (ev->mask & IN_ISDIR) {
|
||||
devmon_handle_dir(&iw_devmon, iwp->path, ev->name, ev->mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
// dbg("Got %s", ev->name);
|
||||
if (ev->mask & (IN_CREATE | IN_DELETE))
|
||||
devmon_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
|
||||
void devmon_init(uev_ctx_t *ctx)
|
||||
{
|
||||
char dir[MAX_ARG_LEN];
|
||||
|
||||
fd = iwatch_init(&iw_devmon);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDDEV, dir, sizeof(dir)), 0755) && errno != EEXIST) {
|
||||
err(1, "Failed creating %s condition directory, %s", COND_DEV, _PATH_CONDDEV);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uev_io_init(ctx, &devw, devmon_cb, NULL, fd, UEV_READ)) {
|
||||
err(1, "Failed setting up I/O callback for /dev watcher");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(dir, _PATH_DEV, sizeof(dir));
|
||||
if (devmon_add_path(&iw_devmon, dir))
|
||||
iwatch_exit(&iw_devmon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Device node monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2022-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_DEVMON_H_
|
||||
#define FINIT_DEVMON_H_
|
||||
|
||||
void devmon_add_cond(const char *cond);
|
||||
void devmon_del_cond(const char *cond);
|
||||
|
||||
void devmon_init(uev_ctx_t *ctx);
|
||||
|
||||
#endif /* FINIT_DEVMON_H_ */
|
||||
+26
-81
@@ -1,7 +1,7 @@
|
||||
/* Functions for exec'ing processes
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <sysexits.h>
|
||||
@@ -59,11 +57,11 @@ int complete(char *cmd, int pid)
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
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)
|
||||
_e("Caught SIGCHLD waiting for %s, aborting", cmd);
|
||||
warnx("Caught SIGCHLD waiting for %s, aborting", cmd);
|
||||
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;
|
||||
}
|
||||
@@ -131,7 +129,7 @@ int run(char *cmd, char *log)
|
||||
args[i] = NULL;
|
||||
|
||||
if (i == NUM_ARGS && arg) {
|
||||
_e("Command too long: %s", cmd);
|
||||
errx(1, "Command too long: %s", cmd);
|
||||
free(backup);
|
||||
errno = EOVERFLOW;
|
||||
return 1;
|
||||
@@ -166,7 +164,7 @@ int run(char *cmd, char *log)
|
||||
|
||||
_exit(rc);
|
||||
} else if (-1 == pid) {
|
||||
_pe("%s", !log ? args[0] : cmd);
|
||||
err(1, "%s", !log ? args[0] : cmd);
|
||||
if (backup)
|
||||
free(backup);
|
||||
|
||||
@@ -182,11 +180,11 @@ int run(char *cmd, char *log)
|
||||
|
||||
result = WEXITSTATUS(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)) {
|
||||
_d("Process '%s' terminated by signal %d", cmd, WTERMSIG(status));
|
||||
dbg("Process '%s' terminated by signal %d", cmd, WTERMSIG(status));
|
||||
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
|
||||
* change their return code accordingly. --Jocke */
|
||||
}
|
||||
@@ -272,12 +270,12 @@ int exec_runtask(char *cmd, char *args[])
|
||||
size_t i;
|
||||
|
||||
strlcat(buf, cmd, sizeof(buf));
|
||||
for (i = 1; args[i]; i++) {
|
||||
for (i = 1; args && args[i]; i++) {
|
||||
strlcat(buf, " ", sizeof(buf));
|
||||
strlcat(buf, args[i], sizeof(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);
|
||||
}
|
||||
@@ -329,7 +327,7 @@ static int activate_console(int noclear, int nowait)
|
||||
static const char clr[] = "\r\e[K";
|
||||
static const char cup[] = "\e[A";
|
||||
struct termios orig;
|
||||
char c;
|
||||
char ch;
|
||||
int rc;
|
||||
|
||||
if (nowait || rescue)
|
||||
@@ -351,7 +349,7 @@ static int activate_console(int noclear, int nowait)
|
||||
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, msg, strlen(msg));
|
||||
while ((rc = read(STDIN_FILENO, &c, 1)) > 0 && c != '\r' && c != '\n')
|
||||
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
|
||||
continue;
|
||||
|
||||
/* On any error (likely EINTR), we avoid starting getty */
|
||||
@@ -419,8 +417,10 @@ int sh(char *tty)
|
||||
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||
len = strlen(_PATH_BSHELL) + 2;
|
||||
arg0 = malloc(len);
|
||||
if (!arg0)
|
||||
if (!arg0) {
|
||||
err(1, "Failed allocating memory");
|
||||
return 1;
|
||||
}
|
||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||
args[0] = arg0;
|
||||
|
||||
@@ -441,75 +441,20 @@ pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||
return rc;
|
||||
}
|
||||
|
||||
int run_parts(char *dir, char *cmd)
|
||||
pid_t run_bg(char *cmd, char *args[])
|
||||
{
|
||||
struct dirent **e;
|
||||
int i, num;
|
||||
pid_t pid;
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("No files found in %s, skipping ...", dir);
|
||||
return -1;
|
||||
}
|
||||
if (access(cmd, X_OK))
|
||||
return 0;
|
||||
|
||||
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;
|
||||
print(-1, "Calling %s in the background", cmd);
|
||||
pid = fork();
|
||||
if (!pid)
|
||||
_exit(exec_runtask(cmd, args));
|
||||
print_result(pid == -1);
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
if (stat(path, &st)) {
|
||||
_d("Failed stat(%s): %s", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||
_d("Skipping %s ...", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the callee didn't supply a run_parts() argument */
|
||||
if (!cmd) {
|
||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||
_d("Checking if %s is a sysvinit startstop script ...", name);
|
||||
if (name[0] == 'S' && isdigit(name[1]))
|
||||
strlcat(path, " start", sizeof(path));
|
||||
else if (name[0] == 'K' && isdigit(name[1]))
|
||||
strlcat(path, " stop", sizeof(path));
|
||||
} else {
|
||||
strlcat(path, cmd, sizeof(path));
|
||||
}
|
||||
|
||||
print_desc("Calling ", path);
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
sig_unblock();
|
||||
return execvp(_PATH_BSHELL, argv);
|
||||
}
|
||||
|
||||
status = complete(path, pid);
|
||||
exit_status = WEXITSTATUS(status);
|
||||
if (WIFEXITED(status) && exit_status)
|
||||
_w("%s exited with status %d", path, exit_status);
|
||||
else if (WIFSIGNALED(status))
|
||||
_w("%s terminated by signad %d", path, WTERMSIG(status));
|
||||
print_result(status);
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
return 0;
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+79
-182
@@ -1,7 +1,7 @@
|
||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "cgroup.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "devmon.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "plugin.h"
|
||||
@@ -57,10 +58,8 @@
|
||||
#include "tty.h"
|
||||
#include "util.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 cmdlevel = 0; /* runlevel override from cmdline */
|
||||
int prevlevel = -1;
|
||||
@@ -69,12 +68,14 @@ int rescue = 0; /* rescue mode from kernel cmdline */
|
||||
int single = 0; /* single user mode from kernel cmdline */
|
||||
int bootstrap = 1; /* set while bootrapping (for TTYs) */
|
||||
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
|
||||
char *fstab = FINIT_FSTAB;
|
||||
int syncsec = 0; /* reboot delay */
|
||||
int readiness = SVC_NOTIFY_PID;
|
||||
char *finit_conf= NULL;
|
||||
char *finit_rcsd= NULL;
|
||||
char *fstab = NULL;
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *hostname = NULL;
|
||||
char *rcsd = FINIT_RCSD;
|
||||
char *runparts = NULL;
|
||||
char *osheading = NULL;
|
||||
|
||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||
@@ -114,14 +115,17 @@ static int sulogin(int do_reboot)
|
||||
{
|
||||
int rc = EX_OSFILE;
|
||||
char *cmd[] = {
|
||||
_PATH_SULOGIN,
|
||||
"sulogin",
|
||||
_PATH_SULOGIN,
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(cmd); i++) {
|
||||
char *path = which(cmd[i]);
|
||||
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
if (access(path, X_OK)) {
|
||||
free(path);
|
||||
continue;
|
||||
@@ -201,17 +205,17 @@ static int fsck(int pass)
|
||||
|
||||
fp = setmntent(fstab, "r");
|
||||
if (!fp) {
|
||||
_pe("Failed opening fstab: %s", fstab);
|
||||
logit(LOG_CONSOLE | LOG_ALERT, "Failed opening fstab: %s", fstab);
|
||||
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)))) {
|
||||
int fsck_rc = 0;
|
||||
struct stat st;
|
||||
char cmd[256];
|
||||
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_freq, mnt->mnt_passno);
|
||||
|
||||
@@ -241,22 +245,18 @@ static int fsck(int pass)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef FSCK_FIX
|
||||
snprintf(cmd, sizeof(cmd), "fsck -yf %s", dev);
|
||||
#else
|
||||
snprintf(cmd, sizeof(cmd), "fsck -a %s", dev);
|
||||
#endif
|
||||
_d("Running pass %d fsck command %s", pass, cmd);
|
||||
snprintf(cmd, sizeof(cmd), "fsck %s %s %s", fsck_mode, fsck_repair, dev);
|
||||
dbg("Running pass %d fsck command %s", pass, cmd);
|
||||
fsck_rc = run_interactive(cmd, "Checking filesystem %s", dev);
|
||||
/*
|
||||
* "failure" is defined as exiting with a return code of
|
||||
@@ -278,15 +278,17 @@ static int fsck(int pass)
|
||||
static int fsck_all(void)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifndef FAST_BOOT
|
||||
int pass;
|
||||
|
||||
if (!fsck_mode)
|
||||
return 0;
|
||||
|
||||
for (pass = 1; pass < 10; pass++) {
|
||||
rc = fsck(pass);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -299,7 +301,7 @@ static void fs_mount(const char *src, const char *tgt, const char *fstype,
|
||||
|
||||
rc = mount(src, tgt, fstype, flags, data);
|
||||
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
|
||||
@@ -353,6 +355,8 @@ static void fs_remount_root(int fsckerr)
|
||||
*/
|
||||
static void fs_finalize(void)
|
||||
{
|
||||
int flags = MS_NOEXEC | MS_NOSUID;
|
||||
|
||||
/*
|
||||
* Some systems rely on us to both create /dev/shm and, to mount
|
||||
* a tmpfs there. Any system with dbus needs shared memory, so
|
||||
@@ -360,8 +364,8 @@ static void fs_finalize(void)
|
||||
* the /etc/fstab file already.
|
||||
*/
|
||||
if (!fismnt("/dev/shm")) {
|
||||
makedir("/dev/shm", 0777);
|
||||
fs_mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
|
||||
makedir("/dev/shm", 1777);
|
||||
fs_mount("shm", "/dev/shm", "tmpfs", flags | MS_NODEV, "mode=1777");
|
||||
}
|
||||
|
||||
/* Modern systems use /dev/pts */
|
||||
@@ -379,7 +383,13 @@ static void fs_finalize(void)
|
||||
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
|
||||
|
||||
makedir("/dev/pts", 0755);
|
||||
fs_mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
|
||||
fs_mount("devpts", "/dev/pts", "devpts", flags, opts);
|
||||
}
|
||||
|
||||
/* Needed on systems like Alpine Linux */
|
||||
if (!fismnt("/dev/mqueue")) {
|
||||
makedir("/dev/mqueue", 1777);
|
||||
fs_mount("mqueue", "/dev/mqueue", "tmpfs", flags | MS_NODEV, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -453,7 +463,7 @@ static void fs_mount_all(void)
|
||||
if (!rescue)
|
||||
fs_remount_root(fsck_all());
|
||||
|
||||
_d("Root FS up, calling hooks ...");
|
||||
dbg("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
|
||||
if (fstab && strcmp(fstab, "/etc/fstab"))
|
||||
@@ -462,14 +472,17 @@ static void fs_mount_all(void)
|
||||
if (run_interactive(cmd, "Mounting filesystems from %s", fstab))
|
||||
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);
|
||||
|
||||
_d("Enable any swap ...");
|
||||
dbg("Enable any swap ...");
|
||||
fs_swapon(cmd, sizeof(cmd));
|
||||
|
||||
_d("Finalize, ensure common file systems are available ...");
|
||||
dbg("Finalize, ensure common file systems are available ...");
|
||||
fs_finalize();
|
||||
|
||||
dbg("Base FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -510,110 +523,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;
|
||||
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)
|
||||
{
|
||||
puts(PACKAGE_STRING);
|
||||
@@ -644,7 +553,7 @@ static int usage(int rc)
|
||||
" 2-9 Change runlevel\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"
|
||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||
" 1, s, S Enter single user mode, no networking, runlevel 1\n"
|
||||
"\n", prognm);
|
||||
|
||||
return rc;
|
||||
@@ -689,30 +598,26 @@ static int telinit(int argc, char *argv[])
|
||||
return systemf("initctl -b runlevel %c", req);
|
||||
}
|
||||
|
||||
/* XXX: add non-pid1 process monitor here
|
||||
*
|
||||
* finit -f ~/.config/finit.conf &
|
||||
*
|
||||
*/
|
||||
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* user calling telinit or init */
|
||||
if (getpid() != 1)
|
||||
return telinit(argc, argv);
|
||||
if (getpid() != 1) {
|
||||
if (geteuid() == 0)
|
||||
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
|
||||
@@ -742,13 +647,15 @@ int main(int argc, char *argv[])
|
||||
conf_reset_env();
|
||||
|
||||
if (chdir("/"))
|
||||
_pe("Failed cd /");
|
||||
err(1, "Failed cd /");
|
||||
|
||||
/*
|
||||
* In case of emergency.
|
||||
*/
|
||||
#ifdef RESCUE_MODE
|
||||
if (rescue)
|
||||
rescue = sulogin(0);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Load plugins early, the first hook is in banner(), so we
|
||||
@@ -783,7 +690,11 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
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();
|
||||
|
||||
/*
|
||||
@@ -793,30 +704,17 @@ int main(int argc, char *argv[])
|
||||
cond_set_oneshot(plugin_hook_str(HOOK_BANNER));
|
||||
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
|
||||
|
||||
/* Some bootstrap tasks may need to know if we're in a container. */
|
||||
if (in_container())
|
||||
cond_set_oneshot("int/container");
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
conf_saverc();
|
||||
|
||||
/*
|
||||
* Set up inotify watcher for /etc/finit.conf, /etc/finit.d, and
|
||||
@@ -824,24 +722,23 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
conf_monitor();
|
||||
|
||||
_d("Starting initctl API responder ...");
|
||||
dbg("Starting initctl API responder ...");
|
||||
api_init(&loop);
|
||||
|
||||
_d("Starting the big state machine ...");
|
||||
schedule_work(&crank_work);
|
||||
|
||||
_d("Starting bootstrap finalize timer ...");
|
||||
schedule_work(&bootstrap_work);
|
||||
dbg("Starting service interval monitor ...");
|
||||
service_init(&loop);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
_d("Entering main loop ...");
|
||||
dbg("Entering main loop ...");
|
||||
return uev_run(&loop, 0);
|
||||
}
|
||||
|
||||
|
||||
+14
-10
@@ -1,7 +1,7 @@
|
||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -54,19 +54,21 @@
|
||||
#define FINIT_CGPATH "/sys/fs/cgroup"
|
||||
#endif
|
||||
|
||||
/* To use these, include config.h, or define FINIT_LIBPATH_ */
|
||||
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
|
||||
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
|
||||
#define _PATH_GETTY FINIT_LIBPATH_ "/getty"
|
||||
/* To use these, include config.h, or define FINIT_EXECPATH_ */
|
||||
#define _PATH_LOGIT FINIT_EXECPATH_ "/logit"
|
||||
#define _PATH_RUNPARTS FINIT_EXECPATH_ "/runparts"
|
||||
#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 BUF_SIZE 4096
|
||||
|
||||
/* We extend the INIT_CMD_ range for the new initctl tool. */
|
||||
#define INIT_SOCKET _PATH_VARRUN "finit/socket"
|
||||
#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_RUNLVL 1
|
||||
#define INIT_CMD_POWERFAIL 2
|
||||
@@ -83,8 +85,8 @@
|
||||
#define INIT_CMD_STOP_SVC 11
|
||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
|
||||
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
|
||||
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
||||
#define INIT_CMD_GET_PLUGINS 14 /* Fill data[] with loaded plugins */
|
||||
#define INIT_CMD_PLUGIN_DEPS 15 /* Fill data[] with plugin deps */
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
#define INIT_CMD_REBOOT 20
|
||||
#define INIT_CMD_HALT 21
|
||||
@@ -96,6 +98,7 @@
|
||||
#define INIT_CMD_SVC_FIND 131
|
||||
#define INIT_CMD_SVC_FIND_BYC 132
|
||||
#define INIT_CMD_SIGNAL 133
|
||||
#define INIT_CMD_NOTIFY_SOCKET 200 /* For readiness notification socket */
|
||||
#define INIT_CMD_NACK 254
|
||||
#define INIT_CMD_ACK 255
|
||||
|
||||
@@ -117,11 +120,12 @@ extern int rescue;
|
||||
extern int single;
|
||||
extern int bootstrap;
|
||||
extern int kerndebug;
|
||||
extern int syncsec;
|
||||
extern int readiness;
|
||||
extern char *fstab;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
extern char *rcsd;
|
||||
extern char *runparts;
|
||||
extern char *osheading;
|
||||
extern uev_ctx_t *ctx;
|
||||
|
||||
+148
-19
@@ -1,6 +1,6 @@
|
||||
/* Initialize and serve a login terminal
|
||||
*
|
||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h> /* sysconf() */
|
||||
|
||||
#include "finit.h"
|
||||
@@ -48,7 +49,80 @@
|
||||
#define LOGIN_NAME_MIN 64
|
||||
#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 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.
|
||||
@@ -67,10 +141,19 @@ static int readch(void)
|
||||
|
||||
/*
|
||||
* 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;
|
||||
time_t now;
|
||||
|
||||
s0 = line;
|
||||
for (s = line; *s != 0; s++) {
|
||||
@@ -79,12 +162,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
dprint(STDOUT_FILENO, s0, s - s0);
|
||||
s0 = s + 2;
|
||||
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':
|
||||
dprint(STDOUT_FILENO, tty, 0);
|
||||
break;
|
||||
case 'm':
|
||||
dprint(STDOUT_FILENO, uts->machine, 0);
|
||||
break;
|
||||
case 'N':
|
||||
if (!compat)
|
||||
dprint(STDOUT_FILENO, rel->name, 0);
|
||||
break;
|
||||
case 'n':
|
||||
dprint(STDOUT_FILENO, uts->nodename, 0);
|
||||
break;
|
||||
@@ -94,13 +197,31 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
break;
|
||||
#endif
|
||||
case 'r':
|
||||
dprint(STDOUT_FILENO, uts->release, 0);
|
||||
if (compat)
|
||||
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;
|
||||
case 's':
|
||||
dprint(STDOUT_FILENO, uts->sysname, 0);
|
||||
if (compat)
|
||||
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;
|
||||
case 'v':
|
||||
dprint(STDOUT_FILENO, uts->version, 0);
|
||||
if (compat)
|
||||
dprint(STDOUT_FILENO, uts->version, 0);
|
||||
else
|
||||
dprint(STDOUT_FILENO, rel->version, 0);
|
||||
break;
|
||||
case 0:
|
||||
goto leave;
|
||||
@@ -118,28 +239,31 @@ leave:
|
||||
/*
|
||||
* 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";
|
||||
struct utsname uts;
|
||||
struct osrel rel;
|
||||
int compat;
|
||||
FILE *fp;
|
||||
|
||||
/*
|
||||
* Get data about this machine.
|
||||
*/
|
||||
uname(&uts);
|
||||
compat = osrel(&rel);
|
||||
|
||||
fp = fopen("/etc/issue", "r");
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
do_parse(buf, &uts, tty);
|
||||
parseln(buf, &uts, &rel, tty, compat);
|
||||
|
||||
fclose(fp);
|
||||
} 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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -156,7 +280,7 @@ static int get_logname(char *tty, char *name, size_t len)
|
||||
ch = ' ';
|
||||
*name = '\0';
|
||||
while (ch != '\n') {
|
||||
do_issue(tty);
|
||||
issue(tty);
|
||||
|
||||
np = name;
|
||||
while ((ch = readch()) != '\n') {
|
||||
@@ -177,15 +301,17 @@ static int get_logname(char *tty, char *name, size_t len)
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the login(1) command with the current
|
||||
* username as its argument. It will reply to the
|
||||
* calling user by typing "Password: "...
|
||||
* Start login(1) with the current username as its argument. It replies
|
||||
* to the calling user by typing "Password: " ...
|
||||
*/
|
||||
static int do_login(char *name)
|
||||
static int exec_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
if (passenv)
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL);
|
||||
else
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
* Failed to exec login, should not happen on normal systems.
|
||||
@@ -238,12 +364,12 @@ restart:
|
||||
if (term && term[0])
|
||||
setenv("TERM", term, 1);
|
||||
|
||||
return do_login(name);
|
||||
return exec_login(name);
|
||||
}
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
warnx("usage: getty [-h?] tty [speed [term]]");
|
||||
warnx("usage: getty [-h?p] tty [speed [term]]");
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -252,11 +378,14 @@ int main(int argc, char *argv[])
|
||||
char *tty, *speed = "0", *term = NULL;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "h?")) != EOF) {
|
||||
while ((c = getopt(argc, argv, "h?p")) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
case 'p':
|
||||
passenv = 1;
|
||||
break;
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
+18
-8
@@ -1,7 +1,7 @@
|
||||
/* Misc. utility functions for finit and its plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "log.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "util.h"
|
||||
@@ -83,6 +84,9 @@ char *console(void)
|
||||
|
||||
void console_init(void)
|
||||
{
|
||||
/* Enable line wrap, if disabled previously, e.g., qemu */
|
||||
dprint(STDOUT_FILENO, "\033[?7h", 5);
|
||||
|
||||
log_init();
|
||||
}
|
||||
|
||||
@@ -366,7 +370,8 @@ int print_result(int fail)
|
||||
int getuser(char *username, char **home)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
*home = "/"; /* XXX: Fixme */
|
||||
if (home)
|
||||
*home = "/"; /* XXX: Fixme */
|
||||
return fgetint("/etc/passwd", "x:\n", username);
|
||||
#else
|
||||
struct passwd *usr;
|
||||
@@ -443,8 +448,10 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
gid = 0;
|
||||
|
||||
rc = makedir(dir, mode);
|
||||
if (rc && errno == EEXIST)
|
||||
rc = chmod(dir, mode);
|
||||
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);
|
||||
@@ -482,7 +489,7 @@ void set_hostname(char **hostname)
|
||||
done:
|
||||
if (*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;
|
||||
|
||||
if (updown)
|
||||
_d("Setting up networking ...");
|
||||
dbg("Setting up networking ...");
|
||||
else
|
||||
_d("Taking down networking ...");
|
||||
dbg("Taking down networking ...");
|
||||
|
||||
/* Run user network start script if enabled */
|
||||
if (updown && network) {
|
||||
@@ -536,7 +543,7 @@ void networking(int updown)
|
||||
char buf[256];
|
||||
|
||||
while (fgets(buf, sizeof(buf), pp))
|
||||
logit(LOG_NOTICE, "network: %s", buf);
|
||||
logit(LOG_NOTICE, "network: %s", chomp(buf));
|
||||
|
||||
rc = pclose(pp);
|
||||
}
|
||||
@@ -557,8 +564,11 @@ done:
|
||||
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
if (updown) {
|
||||
_d("Calling all network up hooks ...");
|
||||
dbg("Calling all network up hooks ...");
|
||||
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
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,8 +25,12 @@
|
||||
#ifndef FINIT_HELPERS_H_
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
@@ -84,18 +88,59 @@ int run_interactive (char *cmd, char *fmt, ...) __attribute__ ((format (prin
|
||||
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_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, int sysv);
|
||||
|
||||
static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
/*
|
||||
* Defaults to user "root" and group "wheel" (root) if:
|
||||
* 1) the user is missing
|
||||
* 2) the group or the file /etc/group is missing
|
||||
*/
|
||||
static inline int create(char *path, mode_t mode, char *user, char *group)
|
||||
{
|
||||
if (touch(path) || chmod(path, mode) || chown(path, uid, gid)) {
|
||||
_w("Failed creating %s properly.", path);
|
||||
int fd, uid, gid;
|
||||
|
||||
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 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)
|
||||
{
|
||||
size_t loop = 3;
|
||||
@@ -114,6 +159,52 @@ static inline int dprint(int fd, const char *s, size_t len)
|
||||
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_ */
|
||||
|
||||
/**
|
||||
|
||||
+559
-181
File diff suppressed because it is too large
Load Diff
+14
-1
@@ -1,6 +1,6 @@
|
||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -24,15 +24,28 @@
|
||||
#ifndef FINIT_INITCTL_H_
|
||||
#define FINIT_INITCTL_H_
|
||||
|
||||
#include <err.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "util.h"
|
||||
|
||||
extern char *finit_conf;
|
||||
extern char *finit_rcsd;
|
||||
|
||||
extern int icreate; /* initctl -c */
|
||||
extern int iforce; /* initctl -f */
|
||||
extern int ionce; /* initctl -1 */
|
||||
extern int heading; /* initctl -t */
|
||||
extern int json; /* initctl -j */
|
||||
extern int noerr; /* initctl -n */
|
||||
extern int verbose; /* initctl -v */
|
||||
extern int plain; /* initctl -p */
|
||||
extern int quiet; /* initctl -q */
|
||||
|
||||
#define ERR(rc, fmt, args...) do { if (!quiet) err(rc, fmt, ##args); else exit(rc); } while (0)
|
||||
#define ERRX(rc, fmt, args...) do { if (!quiet) errx(rc, fmt, ##args); else exit(rc); } while (0)
|
||||
#define WARN(fmt, args...) do { if (!quiet) warn(fmt, ##args); } while (0)
|
||||
#define WARNX(fmt, args...) do { if (!quiet) warnx(fmt, ##args); } while (0)
|
||||
|
||||
extern void print_header(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
|
||||
+16
-11
@@ -1,7 +1,7 @@
|
||||
/* inotify watcher for files or directories
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -52,7 +52,7 @@ int iwatch_init(struct iwatch *iw)
|
||||
|
||||
iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (iw->fd < 0) {
|
||||
_pe("Failed creating inotify descriptor");
|
||||
warn("Failed creating inotify descriptor");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ int iwatch_init(struct iwatch *iw)
|
||||
len = sizeof(sz);
|
||||
if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len)) {
|
||||
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;
|
||||
@@ -87,7 +87,7 @@ void iwatch_exit(struct iwatch *iw)
|
||||
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;
|
||||
char *path;
|
||||
@@ -97,18 +97,18 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||
return -1;
|
||||
|
||||
if (!fexist(file)) {
|
||||
_d("skipping %s: no such file or directory", file);
|
||||
dbg("skipping %s: no such file or directory", file);
|
||||
return 0;
|
||||
}
|
||||
_d("adding new watcher for path %s", file);
|
||||
dbg("adding new watcher for path %s", file);
|
||||
|
||||
path = strdup(file);
|
||||
if (!path) {
|
||||
_pe("Out of memory");
|
||||
warn("Out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
|
||||
wd = inotify_add_watch(iw->fd, path, mask);
|
||||
if (wd < 0) {
|
||||
switch (errno) {
|
||||
case EEXIST:
|
||||
@@ -124,7 +124,7 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||
/* fallthrough */
|
||||
|
||||
default:
|
||||
_pe("Failed adding watcher for %s", path);
|
||||
warn("Failed adding watcher for %s", path);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||
|
||||
iwp = malloc(sizeof(struct iwatch_path));
|
||||
if (!iwp) {
|
||||
_pe("Failed allocating new `struct iwatch_path`");
|
||||
warn("Failed allocating new `struct iwatch_path`");
|
||||
inotify_rm_watch(iw->fd, wd);
|
||||
free(path);
|
||||
return -1;
|
||||
@@ -147,12 +147,17 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||
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)
|
||||
{
|
||||
if (!initialized)
|
||||
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);
|
||||
inotify_rm_watch(iw->fd, iwp->wd);
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
/* inotify watcher for files or directories
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -59,6 +59,7 @@ struct iwatch {
|
||||
int iwatch_init (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_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
|
||||
*
|
||||
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2021-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -152,7 +152,7 @@ static void init(void)
|
||||
|
||||
for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
|
||||
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]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Finit daemon log functions
|
||||
*
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
@@ -99,6 +100,21 @@ void log_debug(void)
|
||||
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()
|
||||
* and continue logging as a regular daemon.
|
||||
@@ -119,8 +135,16 @@ void logit(int prio, const char *fmt, ...)
|
||||
goto done;
|
||||
|
||||
if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fputs("\n", stderr);
|
||||
static char buf[512];
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Finit daemon log functions
|
||||
*
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -31,26 +31,56 @@
|
||||
#define LOG_CONSOLE (14<<3)
|
||||
#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() <--
|
||||
* ~~~~~~~~~~~
|
||||
* All of these prepend the function, so only use for critical warnings
|
||||
* errors and debug messages. For all other user messages, see logit()
|
||||
*
|
||||
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
|
||||
* use `initctl debug` or add `debug` to the kernel cmdline.
|
||||
* General log macros, similar to those used by initctl. Initially intended
|
||||
* only for bridging client.c in Finit and initctl.
|
||||
*/
|
||||
#define dbg(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
|
||||
#define info(fmt, args...) logit(LOG_INFO, "%s():" fmt, __func__, ##args)
|
||||
#define note(fmt, args...) logit(LOG_NOTICE, "%s():" fmt, __func__, ##args)
|
||||
#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 _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
|
||||
#define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
|
||||
#define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
|
||||
|
||||
void log_init (void);
|
||||
void log_exit (void);
|
||||
void log_init (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 flog (char *file, 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)));
|
||||
#endif
|
||||
|
||||
#endif /* FINIT_LOG_H_ */
|
||||
|
||||
+8
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -208,8 +208,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (!ident)
|
||||
ident = getenv("LOGNAME") ?: getenv("USER");
|
||||
if (!ident) {
|
||||
ident = getenv("LOGNAME");
|
||||
if (!ident)
|
||||
ident = getenv("USER");
|
||||
if (!ident)
|
||||
ident = PACKAGE_NAME;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
size_t pos = 0, len = sizeof(buf);
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -49,7 +49,6 @@ static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
*/
|
||||
int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
@@ -60,6 +59,7 @@ int logrotate(char *file, int num, off_t sz)
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
int cnt;
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
@@ -97,8 +97,7 @@ int logrotate(char *file, int num, off_t sz)
|
||||
} else {
|
||||
fallback:
|
||||
if (truncate(file, 0))
|
||||
syslog(LOG_ERR, "Failed truncating %s during logrotate: %s",
|
||||
file, strerror(errno));
|
||||
syslog(LOG_ERR, "Failed truncating %s: %s", file, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-6
@@ -1,6 +1,6 @@
|
||||
/* Handle MD arrays
|
||||
*
|
||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
static glob_t *get_arrays(void)
|
||||
@@ -48,17 +49,18 @@ static glob_t *get_arrays(void)
|
||||
*/
|
||||
void mdadm_wait(void)
|
||||
{
|
||||
size_t i;
|
||||
glob_t *gl;
|
||||
char cmd[160];
|
||||
|
||||
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
|
||||
gl = get_arrays();
|
||||
if (gl) {
|
||||
for (i = 0; i < gl->gl_pathc; i++) {
|
||||
char *array;
|
||||
size_t i;
|
||||
|
||||
array = basename(gl->gl_pathv[i]);
|
||||
for (i = 0; i < gl->gl_pathc; i++) {
|
||||
const char *array;
|
||||
char cmd[160];
|
||||
|
||||
array = basenm(gl->gl_pathv[i]);
|
||||
snprintf(cmd, sizeof(cmd), "mdadm --wait-clean /dev/%s >/dev/null", array);
|
||||
run_interactive(cmd, "Marking MD array %s as clean", array);
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/* Mount and unmount helpers
|
||||
*
|
||||
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -97,13 +97,14 @@ static int unmount(const char *target)
|
||||
{
|
||||
int rc;
|
||||
|
||||
dbg("Unmounting %s", target);
|
||||
|
||||
rc = umount(target);
|
||||
if (rc) {
|
||||
if (errno != EBUSY)
|
||||
print(2, "Failed unmounting %s, error %d: %s", target, errno, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
print(0, "Unmounting %s", target);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* PID and PID file helpers
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -149,6 +149,8 @@ int pid_file_create(svc_t *svc)
|
||||
|
||||
int pid_file_set(svc_t *svc, char *file, int not)
|
||||
{
|
||||
char buf[sizeof(svc->pidfile)];
|
||||
|
||||
if (!file) {
|
||||
file = pid_file(svc);
|
||||
if (!file)
|
||||
@@ -165,7 +167,9 @@ int pid_file_set(svc_t *svc, char *file, int not)
|
||||
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)
|
||||
svc->pidfile[0] = '!';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* PID and PID file helpers
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -51,6 +51,7 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
|
||||
{
|
||||
static char *prefix = "/var/run";
|
||||
static int unknown = 1;
|
||||
char *ptr;
|
||||
int rc;
|
||||
|
||||
if (unknown) {
|
||||
@@ -59,14 +60,13 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
|
||||
unknown = 0;
|
||||
}
|
||||
|
||||
if (!strncmp(file, "/var/run/", 9))
|
||||
file += 9;
|
||||
else if (!strncmp(file, "/run/", 5))
|
||||
file += 5;
|
||||
ptr = strstr(file, "/run/");
|
||||
if (ptr)
|
||||
file = ptr + 5;
|
||||
|
||||
rc = paste(path, len, prefix, file);
|
||||
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;
|
||||
}
|
||||
|
||||
+153
-32
@@ -1,6 +1,6 @@
|
||||
/* Plugin based services architecture for finit
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,6 +42,8 @@
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "util.h"
|
||||
|
||||
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
||||
#define SEARCH_PLUGIN(str) \
|
||||
@@ -50,7 +52,8 @@
|
||||
return p; \
|
||||
}
|
||||
|
||||
static char *plugpath = NULL; /* Set by first load. */
|
||||
static int plugloaded = 0; /* Set while plugins are loaded */
|
||||
static char *plugpath = NULL; /* Set by first load. */
|
||||
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
@@ -75,27 +78,30 @@ static char *trim_ext(char *name)
|
||||
|
||||
int plugin_register(plugin_t *plugin)
|
||||
{
|
||||
const char *nm;
|
||||
|
||||
if (!plugin) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Setup default name if none is provided */
|
||||
if (!plugin->name) {
|
||||
nm = plugin->name;
|
||||
if (!nm) {
|
||||
#ifndef ENABLE_STATIC
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(plugin, &info) && info.dli_fname)
|
||||
plugin->name = basename(info.dli_fname);
|
||||
nm = basenm(info.dli_fname);
|
||||
#endif
|
||||
if (!plugin->name)
|
||||
plugin->name = "unknown";
|
||||
if (!nm)
|
||||
nm = "unknown";
|
||||
}
|
||||
plugin->name = trim_ext(strdup(plugin->name));
|
||||
plugin->name = trim_ext(strdup(nm));
|
||||
|
||||
/* Already registered? */
|
||||
if (plugin_find(plugin->name)) {
|
||||
_d("... %s already loaded", plugin->name);
|
||||
dbg("... %s already loaded", plugin->name);
|
||||
free(plugin->name);
|
||||
|
||||
return 0;
|
||||
@@ -120,10 +126,10 @@ int plugin_unregister(plugin_t *plugin)
|
||||
#ifndef ENABLE_STATIC
|
||||
TAILQ_REMOVE(&plugins, plugin, link);
|
||||
|
||||
_d("%s exiting ...", plugin->name);
|
||||
dbg("%s exiting ...", plugin->name);
|
||||
free(plugin->name);
|
||||
#else
|
||||
_d("Finit built statically, cannot unload %s ...", plugin->name);
|
||||
dbg("Finit built statically, cannot unload %s ...", plugin->name);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@@ -158,8 +164,8 @@ plugin_t *plugin_find(char *name)
|
||||
SEARCH_PLUGIN(name);
|
||||
|
||||
if (plugpath && name[0] != '/') {
|
||||
int noext;
|
||||
char path[CMD_SIZE];
|
||||
int noext;
|
||||
|
||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||
snprintf(path, sizeof(path), "%s%s%s%s", plugpath,
|
||||
@@ -195,22 +201,73 @@ int plugin_exists(hook_point_t no)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(HAVE_HOOK_SCRIPTS_PLUGIN) && defined(PLUGIN_HOOK_SCRIPTS_PATH)
|
||||
#define CHOOSE(x, y) y
|
||||
static const char *hscript_paths[] = HOOK_TYPES;
|
||||
#undef CHOOSE
|
||||
|
||||
void plugin_script_run(hook_point_t no)
|
||||
{
|
||||
const char *hook_name = hscript_paths[no];
|
||||
const char *env[] = {
|
||||
"FINIT_HOOK_NAME", hook_name,
|
||||
"FINIT_SHUTDOWN", NULL,
|
||||
NULL,
|
||||
};
|
||||
char path[CMD_SIZE] = "";
|
||||
|
||||
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
|
||||
strlcat(path, hook_name + 4, sizeof(path));
|
||||
|
||||
if (no >= HOOK_SHUTDOWN) {
|
||||
switch (halt) {
|
||||
case SHUT_OFF:
|
||||
env[3] = "poweroff";
|
||||
break;
|
||||
case SHUT_HALT:
|
||||
env[3] = "halt";
|
||||
break;
|
||||
case SHUT_REBOOT:
|
||||
env[3] = "reboot";
|
||||
break;
|
||||
}
|
||||
} else
|
||||
env[2] = NULL;
|
||||
|
||||
run_parts(path, NULL, env, 0, 0);
|
||||
}
|
||||
#else
|
||||
void plugin_script_run(hook_point_t no)
|
||||
{
|
||||
(void)no;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Some hooks are called with a fixed argument */
|
||||
void plugin_run_hook(hook_point_t no, void *arg)
|
||||
{
|
||||
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) {
|
||||
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) ...", basenm(p->name), no, arg ?: "NIL");
|
||||
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
|
||||
* mounted. */
|
||||
if (no >= HOOK_MOUNT_ERROR)
|
||||
* mounted.
|
||||
*/
|
||||
if (cond_is_available() && no >= HOOK_BASEFS_UP && no <= HOOK_SHUTDOWN)
|
||||
cond_set_oneshot(hook_cond[no]);
|
||||
|
||||
service_step_all(SVC_TYPE_RUNTASK);
|
||||
@@ -235,7 +292,7 @@ static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||
/* Stop watcher, callback may close descriptor on us ... */
|
||||
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);
|
||||
|
||||
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
||||
@@ -248,9 +305,9 @@ int plugin_io_init(plugin_t *p)
|
||||
if (!is_io_plugin(p))
|
||||
return 0;
|
||||
|
||||
_d("Initializing plugin %s for I/O", basename(p->name));
|
||||
dbg("Initializing plugin %s for I/O", basenm(p->name));
|
||||
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", basenm(p->name));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -260,8 +317,8 @@ int plugin_io_init(plugin_t *p)
|
||||
/* Setup any I/O callbacks for plugins that use them */
|
||||
static int init_plugins(uev_ctx_t *ctx)
|
||||
{
|
||||
int fail = 0;
|
||||
plugin_t *p, *tmp;
|
||||
int fail = 0;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (plugin_io_init(p))
|
||||
@@ -302,16 +359,18 @@ static int load_one(char *path, char *name)
|
||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
|
||||
|
||||
_d("Loading plugin %s ...", basename(sofile));
|
||||
dbg("Loading plugin %s ...", sofile);
|
||||
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
|
||||
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;
|
||||
}
|
||||
|
||||
plugin = TAILQ_LAST(&plugins, plugin_head);
|
||||
if (!plugin) {
|
||||
_e("Plugin %s failed to register, unloading from memory", sofile);
|
||||
warn("Plugin %s failed to register, unloading from memory", sofile);
|
||||
dlclose(handle);
|
||||
return 1;
|
||||
}
|
||||
@@ -338,9 +397,9 @@ static void check_plugin_depends(plugin_t *plugin)
|
||||
int 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])) {
|
||||
// _d("OK plugin %s was already loaded.", plugin->depends[i]);
|
||||
// dbg("OK plugin %s was already loaded.", plugin->depends[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -350,13 +409,15 @@ static void check_plugin_depends(plugin_t *plugin)
|
||||
|
||||
static int load_plugins(char *path)
|
||||
{
|
||||
struct dirent *entry;
|
||||
int fail = 0;
|
||||
DIR *dp;
|
||||
struct dirent *entry;
|
||||
|
||||
dp = opendir(path);
|
||||
if (!dp) {
|
||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
warn("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
plugpath = path;
|
||||
@@ -381,14 +442,70 @@ static int load_plugins(char *path)
|
||||
}
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
int plugin_list(char *buf, size_t len)
|
||||
{
|
||||
#ifndef ENABLE_STATIC
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
buf[0] = 0;
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (buf[0])
|
||||
strlcat(buf, " ", len);
|
||||
strlcat(buf, p->name, len);
|
||||
}
|
||||
#else
|
||||
buf[0] = 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_deps(char *buf, size_t len)
|
||||
{
|
||||
#ifndef ENABLE_STATIC
|
||||
plugin_t *p;
|
||||
|
||||
p = plugin_find(buf);
|
||||
buf[0] = 0;
|
||||
if (p) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PLUGIN_DEP_MAX; i++) {
|
||||
if (!p->depends[i])
|
||||
continue;
|
||||
|
||||
if (buf[0])
|
||||
strlcat(buf, " ", len);
|
||||
strlcat(buf, p->depends[i], len);
|
||||
}
|
||||
}
|
||||
#else
|
||||
buf[0] = 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int fail = 1;
|
||||
load_plugins(PLUGIN_PATH);
|
||||
|
||||
if (!load_plugins(PLUGIN_PATH))
|
||||
fail = init_plugins(ctx);
|
||||
#ifdef EXTERNAL_PLUGIN_PATH
|
||||
char *paths = strdup(EXTERNAL_PLUGIN_PATH);
|
||||
char *path;
|
||||
|
||||
return fail;
|
||||
if (paths) {
|
||||
dbg("Loading external plugins from %s ...", paths);
|
||||
path = strtok(paths, ":");
|
||||
while (path) {
|
||||
load_plugins(path);
|
||||
path = strtok(NULL, ":");
|
||||
}
|
||||
free(paths);
|
||||
}
|
||||
|
||||
plugloaded = 1;
|
||||
#endif
|
||||
|
||||
return init_plugins(ctx);
|
||||
}
|
||||
|
||||
void plugin_exit(void)
|
||||
@@ -397,9 +514,13 @@ void plugin_exit(void)
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (dlclose(p->handle))
|
||||
_e("Failed: unloading plugin %s: %s", p->name, dlerror());
|
||||
if (dlclose(p->handle)) {
|
||||
char *error = dlerror();
|
||||
warn("Failed unloading plugin %s: %s", p->name, error ? error : "unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
plugloaded = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -1,6 +1,6 @@
|
||||
/* Plugin based services architecture for finit
|
||||
*
|
||||
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -72,6 +72,7 @@
|
||||
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
||||
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
|
||||
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
||||
CHOOSE(HOOK_SVC_PLUGIN, "hook/svc/plugin"), \
|
||||
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
||||
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
||||
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
||||
@@ -81,7 +82,10 @@
|
||||
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
|
||||
\
|
||||
/* Shutdown hooks, runlevel [06] */ \
|
||||
CHOOSE(HOOK_NETWORK_DN, "hook/net/down"), \
|
||||
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") \
|
||||
}
|
||||
|
||||
@@ -146,6 +150,9 @@ typedef struct plugin {
|
||||
/* Public plugin API */
|
||||
int plugin_io_init (plugin_t *plugin);
|
||||
|
||||
int plugin_list (char *buf, size_t len);
|
||||
int plugin_deps (char *buf, size_t len);
|
||||
|
||||
int plugin_register (plugin_t *plugin);
|
||||
int plugin_unregister (plugin_t *plugin);
|
||||
|
||||
|
||||
+25
-11
@@ -1,7 +1,7 @@
|
||||
/* Private header file for main finit daemon, not for plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,20 +29,34 @@
|
||||
#include "plugin.h"
|
||||
|
||||
#define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */
|
||||
extern int service_interval;
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
|
||||
int api_init (uev_ctx_t *ctx);
|
||||
int api_exit (void);
|
||||
#define IS_RESERVED_RUNLEVEL(l) (l == 0 || l == 6 || l == INIT_LEVEL)
|
||||
|
||||
void service_monitor (pid_t lost, int status);
|
||||
extern char *finit_conf;
|
||||
extern char *finit_rcsd;
|
||||
extern svc_t *wdog;
|
||||
extern int service_interval;
|
||||
extern char *fsck_mode;
|
||||
extern char *fsck_repair;
|
||||
|
||||
const char *plugin_hook_str(hook_point_t no);
|
||||
int plugin_exists (hook_point_t no);
|
||||
void plugin_run_hook (hook_point_t no, void *arg);
|
||||
void plugin_run_hooks (hook_point_t no);
|
||||
int api_init (uev_ctx_t *ctx);
|
||||
int api_exit (void);
|
||||
void conf_flush_events(void);
|
||||
|
||||
int plugin_init (uev_ctx_t *ctx);
|
||||
void plugin_exit (void);
|
||||
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);
|
||||
int plugin_exists (hook_point_t no);
|
||||
|
||||
void plugin_run_hook (hook_point_t no, void *arg);
|
||||
void plugin_run_hooks (hook_point_t no);
|
||||
void plugin_script_run(hook_point_t no);
|
||||
|
||||
int plugin_init (uev_ctx_t *ctx);
|
||||
void plugin_exit (void);
|
||||
|
||||
#endif /* FINIT_PRIVATE_H_ */
|
||||
|
||||
|
||||
+22
-10
@@ -1,6 +1,6 @@
|
||||
/* reboot, halt, poweroff, shutdown, suspend for finit
|
||||
*
|
||||
* Copyright (c) 2009-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2009-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -43,6 +43,8 @@ static cmd_t cmd = CMD_UNKNOWN;
|
||||
static char *msg = NULL;
|
||||
|
||||
/* initctl API */
|
||||
extern int timeout;
|
||||
|
||||
extern int do_reboot (char *arg);
|
||||
extern int do_halt (char *arg);
|
||||
extern int do_poweroff(char *arg);
|
||||
@@ -103,12 +105,13 @@ static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
|
||||
"Options:\n"
|
||||
" --help This help text\n"
|
||||
" -f, --force Force unsafe %s now, do not contact the init system.\n"
|
||||
" --halt Halt system, regardless of how the command is called.\n"
|
||||
" -h Halt or power off after shutdown.\n"
|
||||
" -P, --poweroff Power-off system, regardless of how the command is called.\n"
|
||||
" -r, --reboot Reboot system, regardless of how the command is called.\n"
|
||||
" -?, --help This help text\n"
|
||||
" -f, --force Force unsafe %s now, do not contact the init system.\n"
|
||||
" --halt Halt system, regardless of how the command is called.\n"
|
||||
" -h Halt or power off after shutdown.\n"
|
||||
" -P, --poweroff Power-off system, regardless of how the command is called.\n"
|
||||
" -r, --reboot Reboot system, regardless of how the command is called.\n"
|
||||
" -t, --timeout=SEC Force reboot/shutdown after a given timeout\n"
|
||||
"\n", prognm, msg);
|
||||
|
||||
return rc;
|
||||
@@ -116,20 +119,22 @@ static int usage(int rc)
|
||||
|
||||
int reboot_main(int argc, char *argv[])
|
||||
{
|
||||
int c, force = 0;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"help", 0, NULL, '?'},
|
||||
{"force", 0, NULL, 'f'},
|
||||
{"halt", 0, NULL, 'H'},
|
||||
{"poweroff", 0, NULL, 'p'},
|
||||
{"reboot", 0, NULL, 'r'},
|
||||
{"timeout", 1, NULL, 't'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
int c, force = 0;
|
||||
int tmp;
|
||||
|
||||
/* Initial command taken from program name */
|
||||
transform(prognm);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h?fHPpr", long_options, NULL)) != EOF) {
|
||||
while ((c = getopt_long(argc, argv, "h?fHPprt:", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case '?':
|
||||
return usage(0);
|
||||
@@ -152,6 +157,13 @@ int reboot_main(int argc, char *argv[])
|
||||
cmd = CMD_REBOOT;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
tmp = atoi(optarg);
|
||||
if (tmp < 1)
|
||||
errx(1, "Invalid forced timeout value.");
|
||||
timeout = tmp;
|
||||
break;
|
||||
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
#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, int sysv)
|
||||
{
|
||||
size_t cmdlen = cmd ? strlen(cmd) : strlen("start");
|
||||
struct dirent **d;
|
||||
int i, num;
|
||||
int rc = 0;
|
||||
|
||||
num = scandir(dir, &d, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
dbg("No files found in %s, skipping ...", dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char path[strlen(dir) + strlen(d[i]->d_name) + 3 + cmdlen];
|
||||
const char *name = d[i]->d_name;
|
||||
char *argv[4] = {
|
||||
"sh",
|
||||
"-c",
|
||||
path,
|
||||
NULL
|
||||
};
|
||||
struct stat st;
|
||||
int result = 0;
|
||||
pid_t pid = 0;
|
||||
int status;
|
||||
|
||||
/* skip backup files */
|
||||
if (name[strlen(name) - 1] == '~')
|
||||
continue;
|
||||
|
||||
/* cannot rely on d_type, not supported on all filesystems */
|
||||
paste(path, sizeof(path), dir, name);
|
||||
if (stat(path, &st)) {
|
||||
warn("failed stat(%s)", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* skip non-executable files and directories */
|
||||
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||
dbg("skipping %s not an executable or is a directory", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sysv && name[0] != 'S' && name[0] != 'K') {
|
||||
dbg("S-only flag set, skipping non-SysV script: %s", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the callee didn't supply a run_parts() argument */
|
||||
if (!cmd) {
|
||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||
if (name[0] == 'S' && isdigit(name[1]))
|
||||
strlcat(path, " start", sizeof(path));
|
||||
else if (name[0] == 'K' && isdigit(name[1]))
|
||||
strlcat(path, " stop", sizeof(path));
|
||||
} else {
|
||||
strlcat(path, " ", sizeof(path));
|
||||
strlcat(path, cmd, sizeof(path));
|
||||
}
|
||||
|
||||
if (progress)
|
||||
print_desc("Calling", path);
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
sig_unblock();
|
||||
run_env(env);
|
||||
|
||||
_exit(execvp(_PATH_BSHELL, argv));
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
warnx("failed starting %s, error %d: %s", path, errno, strerror(errno));
|
||||
result = 1;
|
||||
} else {
|
||||
if (WIFEXITED(status)) {
|
||||
result = WEXITSTATUS(status);
|
||||
dbg("%s exited with status %d", path, result);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
warnx("%s terminated by signal %d", path, WTERMSIG(status));
|
||||
result = 1;
|
||||
} else
|
||||
result = 1;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
print_result(result);
|
||||
rc += result;
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(d[num]);
|
||||
free(d);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef __FINIT__
|
||||
static int usage(int rc)
|
||||
{
|
||||
warnx("usage: runparts [-dhps?] DIRECTORY");
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc, c, progress = 0, sysv = 0;
|
||||
char *dir;
|
||||
|
||||
while ((c = getopt(argc, argv, "dh?ps")) != EOF) {
|
||||
switch(c) {
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
case 'p':
|
||||
progress = 1;
|
||||
break;
|
||||
case 's':
|
||||
sysv = 1;
|
||||
break;
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
return usage(1);
|
||||
|
||||
dir = argv[optind];
|
||||
if (!fisdir(dir))
|
||||
return usage(1);
|
||||
|
||||
prctl(PR_SET_CHILD_SUBREAPER, 1);
|
||||
|
||||
rc = run_parts(dir, NULL, NULL, progress, sysv);
|
||||
if (rc == -1)
|
||||
err(1, "failed run-parts %s", dir);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* __FINIT__ */
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* Work queue helper functions
|
||||
*
|
||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* Work queue helper functions
|
||||
*
|
||||
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2018-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+146
-75
@@ -1,6 +1,6 @@
|
||||
/* List and enable/disable service configurations
|
||||
*
|
||||
* Copyright (c) 2017-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2017-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <fcntl.h> /* Definition of AT_* constants */
|
||||
#include <glob.h>
|
||||
#include <stdio.h>
|
||||
@@ -42,6 +41,10 @@ static int is_builtin(char *arg)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
/* skip check, we may be in non-responsive bootstrap */
|
||||
if (iforce)
|
||||
return 0;
|
||||
|
||||
svc = client_svc_find(arg);
|
||||
if (!svc)
|
||||
return 0;
|
||||
@@ -67,7 +70,7 @@ static int calc_width(char *arr[], size_t len)
|
||||
return width;
|
||||
}
|
||||
|
||||
static void do_list(const char *path)
|
||||
static void do_list(const char *path, char *title, int comma)
|
||||
{
|
||||
static int once = 0;
|
||||
int width, num, prev;
|
||||
@@ -79,6 +82,7 @@ static void do_list(const char *path)
|
||||
|
||||
if (!fexist(path))
|
||||
return;
|
||||
|
||||
dir = strdupa(path);
|
||||
if (!dir)
|
||||
return;
|
||||
@@ -89,6 +93,11 @@ static void do_list(const char *path)
|
||||
else
|
||||
ptr = dir;
|
||||
|
||||
if (json) {
|
||||
printf("%s\"%s\": \"%s\"", comma ? "," :"", title, path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (heading)
|
||||
print_header("%s%s ", once ? "\n" : "", dir);
|
||||
puts(ptr);
|
||||
@@ -102,7 +111,7 @@ static void do_list(const char *path)
|
||||
if (gl.gl_pathc <= 0)
|
||||
goto done;
|
||||
|
||||
if (plain) {
|
||||
if (plain && !json) {
|
||||
if (heading)
|
||||
print_header("%s%s ", once ? "\n" : "", path);
|
||||
once++;
|
||||
@@ -120,8 +129,10 @@ static void do_list(const char *path)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (heading)
|
||||
if (heading && !json)
|
||||
print_header("%s ", path);
|
||||
if (json)
|
||||
printf("%s\"%s\": [", comma ? "," : "", title);
|
||||
|
||||
width = calc_width(gl.gl_pathv, gl.gl_pathc);
|
||||
if (width <= 0)
|
||||
@@ -133,17 +144,24 @@ static void do_list(const char *path)
|
||||
|
||||
prev = 0;
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
if (i > 0 && !(i % num)) {
|
||||
if (i > 0 && !(i % num) && !json) {
|
||||
puts("");
|
||||
prev = 0;
|
||||
}
|
||||
|
||||
if (prev)
|
||||
printf(" ");
|
||||
printf("%-*s", width, gl.gl_pathv[i]);
|
||||
printf("%s", json ? "," : " ");
|
||||
if (json)
|
||||
printf("\"%s\"", gl.gl_pathv[i]);
|
||||
else
|
||||
printf("%-*s", width, gl.gl_pathv[i]);
|
||||
prev++;
|
||||
}
|
||||
puts("\n");
|
||||
|
||||
if (json)
|
||||
fputs("]", stdout);
|
||||
else
|
||||
puts("\n");
|
||||
|
||||
done:
|
||||
globfree(&gl);
|
||||
@@ -152,29 +170,40 @@ done:
|
||||
int serv_list(char *arg)
|
||||
{
|
||||
char path[256];
|
||||
int pre = 0;
|
||||
|
||||
if (arg && arg[0]) {
|
||||
paste(path, sizeof(path), FINIT_RCSD, arg);
|
||||
paste(path, sizeof(path), finit_rcsd, arg);
|
||||
if (fisdir(path)) {
|
||||
do_list(path);
|
||||
if (json)
|
||||
fputs("[", stdout);
|
||||
do_list(path, NULL, 0);
|
||||
if (json)
|
||||
fputs("]\n", stdout);
|
||||
return 0;
|
||||
}
|
||||
/* 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))
|
||||
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))
|
||||
do_list(path);
|
||||
do_list(path, "enabled", pre++);
|
||||
|
||||
if (fisdir(FINIT_RCSD))
|
||||
do_list(FINIT_RCSD);
|
||||
if (fisdir(finit_rcsd))
|
||||
do_list(finit_rcsd, "static", pre++);
|
||||
|
||||
if (fexist(FINIT_CONF))
|
||||
do_list(FINIT_CONF);
|
||||
if (fexist(finit_conf))
|
||||
do_list(finit_conf, "finit.conf", pre++);
|
||||
|
||||
if (json)
|
||||
fputs("}\n", stdout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -198,7 +227,7 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
||||
char corr[40];
|
||||
|
||||
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
|
||||
strlcpy(path, FINIT_CONF, len);
|
||||
strlcpy(path, finit_conf, len);
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -207,85 +236,127 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
||||
name = corr;
|
||||
}
|
||||
|
||||
if (!fisdir(FINIT_RCSD))
|
||||
if (!fisdir(finit_rcsd))
|
||||
return NULL;
|
||||
|
||||
paste(path, len, FINIT_RCSD, "available/");
|
||||
paste(path, len, finit_rcsd, "available/");
|
||||
if (!fisdir(path)) {
|
||||
if (creat && mkdir(path, 0755) && errno != EEXIST)
|
||||
return NULL;
|
||||
else
|
||||
paste(path, len, FINIT_RCSD, name);
|
||||
paste(path, len, finit_rcsd, name);
|
||||
} else
|
||||
strlcat(path, name, len);
|
||||
|
||||
/* fall back to static service unless edit/create */
|
||||
if (!creat && !fexist(path))
|
||||
paste(path, len, FINIT_RCSD, name);
|
||||
paste(path, len, finit_rcsd, name);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
int serv_enable(char *arg)
|
||||
{
|
||||
char corr[40];
|
||||
char path[256];
|
||||
int ena;
|
||||
char *serv, *ptr, *src, *dst;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (!strstr(arg, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||
arg = corr;
|
||||
if (arg[0] == '/') {
|
||||
WARNX("absolute paths not supported, this command manages %s/available/", finit_rcsd);
|
||||
return serv_list("available");
|
||||
}
|
||||
|
||||
if (chdir(FINIT_RCSD))
|
||||
err(1, "failed cd %s", FINIT_RCSD);
|
||||
len = strlen(arg);
|
||||
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)
|
||||
err(1, "failed creating %s/enabled directory", FINIT_RCSD);
|
||||
ena = !chdir("enabled"); /* System *may* have enabled/ dir. */
|
||||
ERR(73, "failed creating %s/enabled directory", finit_rcsd);
|
||||
|
||||
snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg);
|
||||
if (!fexist(path))
|
||||
errx(1, "cannot find %s", conf(path, sizeof(path), arg, 0));
|
||||
if (asprintf(&src, "%s/available/%s", finit_rcsd, serv) == -1)
|
||||
goto fail;
|
||||
if (asprintf(&dst, "%s/enabled/%s", finit_rcsd, serv) == -1) {
|
||||
free(src);
|
||||
fail:
|
||||
ERR(71, "failed allocating memory");
|
||||
}
|
||||
|
||||
if (fexist(arg))
|
||||
errx(1, "%s already enabled", arg);
|
||||
if (fexist(dst)) {
|
||||
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)
|
||||
{
|
||||
struct stat st;
|
||||
char corr[40];
|
||||
size_t len;
|
||||
char *serv;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (!strstr(arg, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||
arg = corr;
|
||||
if (arg[0] == '/') {
|
||||
WARNX("absolute paths not supported, this command manages %s/enabled/", finit_rcsd);
|
||||
return serv_list("available");
|
||||
}
|
||||
|
||||
if (chdir(FINIT_RCSD))
|
||||
err(1, "failed cd %s", FINIT_RCSD);
|
||||
if (chdir("enabled")) /* System *may* have enabled/ dir. */
|
||||
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
|
||||
len = strlen(arg);
|
||||
serv = alloca(len + 6);
|
||||
if (!serv)
|
||||
ERR(71, "failed allocating stack");
|
||||
|
||||
if (check && stat(arg, &st))
|
||||
errx(1, "%s not (an) enabled (service).", arg);
|
||||
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 (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)
|
||||
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)
|
||||
@@ -299,24 +370,24 @@ int serv_touch(char *arg)
|
||||
char *fn;
|
||||
|
||||
if (!arg || !arg[0]) {
|
||||
warnx("missing argument to touch, may be one of:");
|
||||
WARNX("missing argument to touch, may be one of:");
|
||||
return serv_list("enabled");
|
||||
}
|
||||
|
||||
fn = conf(path, sizeof(path), arg, 0);
|
||||
if (!fexist(fn)) {
|
||||
if (!strstr(arg, "finit.conf"))
|
||||
errx(1, "%s not available.", arg);
|
||||
ERRX(72, "%s not available.", arg);
|
||||
if (is_builtin(arg))
|
||||
errx(1, "%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;
|
||||
}
|
||||
|
||||
/* libite:touch() follows symlinks */
|
||||
if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW))
|
||||
err(1, "failed marking %s for reload", fn);
|
||||
ERR(71, "failed marking %s for reload", fn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -329,9 +400,9 @@ int serv_show(char *arg)
|
||||
fn = conf(path, sizeof(path), arg, 0);
|
||||
if (!fexist(fn)) {
|
||||
if (is_builtin(arg))
|
||||
errx(1, "%s is a built-in service.", arg);
|
||||
ERRX(4, "%s is a built-in service.", arg);
|
||||
|
||||
warnx("Cannot find %s", arg);
|
||||
WARNX("Cannot find %s", arg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -366,10 +437,10 @@ static int do_edit(char *arg, int creat)
|
||||
fn = conf(path, sizeof(path), arg, creat);
|
||||
if (!fexist(fn)) {
|
||||
if (is_builtin(arg))
|
||||
errx(1, "%s is a built-in service.", arg);
|
||||
ERRX(4, "%s is a built-in service.", arg);
|
||||
|
||||
if (!creat) {
|
||||
warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
|
||||
WARNX("Cannot find %s, use -c flag, create command, or select one of:", arg);
|
||||
return serv_list(NULL);
|
||||
}
|
||||
|
||||
@@ -378,7 +449,7 @@ static int do_edit(char *arg, int creat)
|
||||
copyfile(SAMPLE_CONF, fn, 0, 0);
|
||||
#endif
|
||||
} else if (creat)
|
||||
warnx("the file %s already exists, falling back to edit.", fn);
|
||||
WARNX("the file %s already exists, falling back to edit.", fn);
|
||||
|
||||
for (size_t i = 0; i < NELEMS(editor); i++) {
|
||||
if (systemf("%s %s 2>/dev/null", editor[i], path))
|
||||
@@ -392,7 +463,7 @@ static int do_edit(char *arg, int creat)
|
||||
int serv_edit(char *arg)
|
||||
{
|
||||
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;
|
||||
arg = "";
|
||||
}
|
||||
@@ -407,10 +478,10 @@ int serv_creat(char *arg)
|
||||
FILE *fp;
|
||||
|
||||
if (!arg || !arg[0])
|
||||
errx(1, "missing argument to create");
|
||||
ERRX(2, "missing argument to create");
|
||||
|
||||
if (is_builtin(arg))
|
||||
errx(1, "%s is a built-in service.", arg);
|
||||
ERRX(4, "%s is a built-in service.", arg);
|
||||
|
||||
/* Input from a pipe or a proper TTY? */
|
||||
if (isatty(STDIN_FILENO))
|
||||
@@ -419,16 +490,16 @@ int serv_creat(char *arg)
|
||||
/* Open fn for writing from pipe */
|
||||
fn = conf(buf, sizeof(buf), arg, 1);
|
||||
if (!fn)
|
||||
err(1, "failed creating conf %s", arg);
|
||||
ERR(73, "failed creating conf %s", arg);
|
||||
|
||||
if (!icreate && fexist(fn)) {
|
||||
warnx("%s already exists, skipping (use -c to override)", fn);
|
||||
WARNX("%s already exists, skipping (use -c to override)", fn);
|
||||
fn = "/dev/null";
|
||||
}
|
||||
|
||||
fp = fopen(fn, "w");
|
||||
if (!fp)
|
||||
err(1, "failed opening %s for writing", fn);
|
||||
ERR(73, "failed opening %s for writing", fn);
|
||||
|
||||
while (fgets(buf, sizeof(buf), stdin))
|
||||
fputs(buf, fp);
|
||||
@@ -442,24 +513,24 @@ int serv_delete(char *arg)
|
||||
char *fn;
|
||||
|
||||
if (!arg || !arg[0]) {
|
||||
warnx("missing argument to delete, may be one of:");
|
||||
WARNX("missing argument to delete, may be one of:");
|
||||
return serv_list("available");
|
||||
}
|
||||
|
||||
fn = conf(buf, sizeof(buf), arg, 0);
|
||||
if (!fn) {
|
||||
if (is_builtin(arg))
|
||||
errx(1, "%s is a built-in service.", arg);
|
||||
errx(1, FINIT_RCSD " missing on system.");
|
||||
ERRX(4, "%s is a built-in service.", arg);
|
||||
ERRX(72, "%s missing on system.", finit_rcsd);
|
||||
}
|
||||
|
||||
if (!fexist(fn))
|
||||
warnx("cannot find %s", fn);
|
||||
WARNX("cannot find %s", fn);
|
||||
|
||||
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
|
||||
do_disable(arg, 0);
|
||||
if (remove(fn))
|
||||
err(1, "Failed removing %s", fn);
|
||||
ERR(1, "Failed removing %s", fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* List and enable/disable service configurations
|
||||
*
|
||||
* Copyright (c) 2017-2022 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2017-2024 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+875
-241
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user