mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ceb2945da9 | ||
|
|
1b8a0951f5 | ||
|
|
a27b8462d1 | ||
|
|
edc2d53923 | ||
|
|
4aae3403f5 | ||
|
|
73423699fd | ||
|
|
0cce69892a | ||
|
|
3ca6256f87 | ||
|
|
eca3098c0e | ||
|
|
7af3461e3c | ||
|
|
b62c975dc4 | ||
|
|
b368bd1420 | ||
|
|
060e97d64e | ||
|
|
9b17ce0b7c | ||
|
|
50f2cbf600 | ||
|
|
837bca3947 | ||
|
|
16180675db | ||
|
|
d3c6351ed8 | ||
|
|
7e37ac348e | ||
|
|
30e68658ae | ||
|
|
a179308819 | ||
|
|
4cdd5f8ccc | ||
|
|
7f19474d8d | ||
|
|
a5941165a0 | ||
|
|
181d9556a1 | ||
|
|
bf72e6b9c8 | ||
|
|
8db521d379 | ||
|
|
f46ac20fb0 | ||
|
|
d70d86e9fe | ||
|
|
38db52aa4e | ||
|
|
bf62bee440 | ||
|
|
e50fe3b060 | ||
|
|
d6c55282ef | ||
|
|
1015124530 | ||
|
|
38885aaa67 | ||
|
|
a685b698c2 | ||
|
|
9bce8f9584 | ||
|
|
d04b04d73a | ||
|
|
72125430e6 | ||
|
|
561387ce57 | ||
|
|
e2d1930e6c | ||
|
|
6f2a124240 | ||
|
|
10392e9639 | ||
|
|
605c489937 | ||
|
|
26409bc860 | ||
|
|
b01f9f38f2 | ||
|
|
2ddbb805ca | ||
|
|
ed999d9a80 | ||
|
|
82cc10be88 | ||
|
|
30fb9780d7 | ||
|
|
09f46f62b4 | ||
|
|
bc4e7a8d8d | ||
|
|
e2753d75f8 | ||
|
|
addb8c4019 | ||
|
|
0c4a9b62c2 | ||
|
|
57c97d8190 | ||
|
|
b0d0ca76cf | ||
|
|
5240c0cf19 | ||
|
|
a02f369a89 | ||
|
|
fa2a113cbf | ||
|
|
4c5ac38e2c | ||
|
|
f89ebb91a8 | ||
|
|
477bc064d5 | ||
|
|
96376126b3 | ||
|
|
1c150928cb | ||
|
|
7a8d4e2c6b | ||
|
|
fb956fd2f5 | ||
|
|
e6e4d814f3 | ||
|
|
544d44886a | ||
|
|
cec2d5a4db | ||
|
|
539d215d0e | ||
|
|
10ef0e7de6 | ||
|
|
05e6b3e752 | ||
|
|
3e4be9a122 | ||
|
|
5593372ffb | ||
|
|
05be281301 | ||
|
|
108bbf56dd | ||
|
|
a61aa73419 | ||
|
|
f39626f132 | ||
|
|
c0b6f3af08 | ||
|
|
5d8fcfdf13 | ||
|
|
e4830c5614 | ||
|
|
f33261af6b | ||
|
|
e7516f2b63 | ||
|
|
d18167fe1d | ||
|
|
7d3f95c0a9 | ||
|
|
86c5bbb03d | ||
|
|
7530bb74f6 | ||
|
|
54dd726d16 | ||
|
|
bd9bb92ca0 | ||
|
|
01cb088e51 | ||
|
|
3d91112e0d | ||
|
|
16aef80682 | ||
|
|
fd34914936 | ||
|
|
21a890a04e | ||
|
|
7f5f7e8ef6 | ||
|
|
ff937576ea | ||
|
|
7d831170ae | ||
|
|
6a21ac55ad | ||
|
|
a0820c7f20 | ||
|
|
6d58c9ed0b | ||
|
|
bc2ffbc767 | ||
|
|
4c1fc2410e | ||
|
|
70e1aa889a | ||
|
|
c8c5e4e88a | ||
|
|
86c1398778 | ||
|
|
b37fa2b417 | ||
|
|
a91e197553 | ||
|
|
5adb92ca1b | ||
|
|
cfc99c4e8b | ||
|
|
3e51ed1c55 | ||
|
|
b5df0a6274 | ||
|
|
cbe8d56042 | ||
|
|
a3ec792b44 | ||
|
|
c24855d4d5 | ||
|
|
014e457fda | ||
|
|
21d89c7e24 | ||
|
|
639fb76f5a | ||
|
|
30c189b116 | ||
|
|
e2a8803f7a | ||
|
|
209e140ddd | ||
|
|
2e3fa72924 | ||
|
|
1502838a0d | ||
|
|
377c8bce8c | ||
|
|
a39d958d25 | ||
|
|
7f8a96a73e | ||
|
|
5c2577214d | ||
|
|
f03206a694 | ||
|
|
f0e328edc9 | ||
|
|
13342f2bd9 | ||
|
|
c926160853 | ||
|
|
462d5b22b4 | ||
|
|
b1bfab08f3 | ||
|
|
aeb48ef04b | ||
|
|
b8b200ad75 | ||
|
|
c148d8d783 | ||
|
|
27ba3b27f5 | ||
|
|
b4af86255d | ||
|
|
01a007b388 | ||
|
|
0e28741dcb | ||
|
|
c993feeb44 | ||
|
|
ec17f0aa23 | ||
|
|
6baf2990e8 | ||
|
|
6416e2a1a4 | ||
|
|
7c42986800 | ||
|
|
6f802a7ff1 | ||
|
|
879721839e | ||
|
|
20290a4166 | ||
|
|
ba77e4f217 | ||
|
|
e7b18feca5 | ||
|
|
4a90a3e8ef | ||
|
|
6d14ff84d3 | ||
|
|
89cc6017df | ||
|
|
f6fafdbd23 | ||
|
|
3480e42687 | ||
|
|
053c1643a7 | ||
|
|
2018cac050 | ||
|
|
f9090f535d | ||
|
|
10577f3e48 | ||
|
|
07f7caef0d | ||
|
|
093b3ba467 | ||
|
|
c63279630b | ||
|
|
4755e5206b | ||
|
|
2002157032 | ||
|
|
094e00d405 | ||
|
|
e01836c506 | ||
|
|
685f332c6e | ||
|
|
ae09272bcc | ||
|
|
d16f7e0c88 | ||
|
|
063d645d95 | ||
|
|
f87e9d8880 | ||
|
|
e8099f1bfa | ||
|
|
7dc7f9a968 | ||
|
|
996ab8c6c6 | ||
|
|
16c2198304 | ||
|
|
ecca01dea0 | ||
|
|
bda3cdd3ad | ||
|
|
4811f76294 | ||
|
|
0aba704312 | ||
|
|
57329832d0 | ||
|
|
078a1d0c95 | ||
|
|
0e695aa0c7 | ||
|
|
f7a1514131 | ||
|
|
11bc87ecc3 | ||
|
|
e1c59a2515 | ||
|
|
2faefd44a2 | ||
|
|
6fa3aa41df | ||
|
|
ef9e9ec786 | ||
|
|
d77e3a6113 | ||
|
|
0b1258b44c | ||
|
|
62dad82ba8 | ||
|
|
5423b5b0a2 | ||
|
|
6b10174bcb | ||
|
|
ec94f4d321 | ||
|
|
69800537f2 | ||
|
|
61faedb0b5 | ||
|
|
61014431aa | ||
|
|
49bd63994c | ||
|
|
85b29d9407 | ||
|
|
b83fdea49b | ||
|
|
beec0ee590 | ||
|
|
4121113688 | ||
|
|
62da379472 | ||
|
|
3c9053b055 | ||
|
|
71b07ce2bd | ||
|
|
ebea3d273e | ||
|
|
f3a76b8a63 | ||
|
|
ba93791077 | ||
|
|
b994858907 | ||
|
|
74ad34a0c5 | ||
|
|
1b5b37c06b | ||
|
|
c8e1bd0e9a | ||
|
|
e9c8d3d740 | ||
|
|
f567b866a0 | ||
|
|
3471d3ee2d | ||
|
|
6e197f5751 | ||
|
|
b568c18346 | ||
|
|
1b29876ba8 | ||
|
|
4c6f3d2d69 | ||
|
|
8d3f7eded2 | ||
|
|
0222feeda1 | ||
|
|
f10cf1a9a0 | ||
|
|
85b2089fd3 | ||
|
|
885808c64c | ||
|
|
78069b42e7 | ||
|
|
3e0063e874 | ||
|
|
6ec1ab3d4f | ||
|
|
33b4540ad9 | ||
|
|
a434b51653 | ||
|
|
222d8d3575 | ||
|
|
8e2d006048 | ||
|
|
fd9f204b6d | ||
|
|
f0f358a138 | ||
|
|
cfffcd4513 | ||
|
|
e51587c66e | ||
|
|
f930953e6a | ||
|
|
ebdc10d1c5 | ||
|
|
d12370568f | ||
|
|
176125deca | ||
|
|
cb934ca39f | ||
|
|
4fd95fdcbe | ||
|
|
cb62349054 | ||
|
|
1b72119673 | ||
|
|
add474e825 | ||
|
|
f32e4d4b30 | ||
|
|
eff87b3dc5 | ||
|
|
ea4821fd18 | ||
|
|
6285668dcd | ||
|
|
449bc7d12b | ||
|
|
ee867e7f3b | ||
|
|
b4c7ae9c8c | ||
|
|
95995a45cf | ||
|
|
823bb82b8c | ||
|
|
9a5bb46f78 | ||
|
|
950ca953f5 | ||
|
|
495c8c9eac | ||
|
|
2887795586 | ||
|
|
6a893f50ae | ||
|
|
5d08353284 | ||
|
|
a37037ded4 | ||
|
|
d331b72dfd | ||
|
|
3b21d35f23 | ||
|
|
62a694b8b7 | ||
|
|
eeba2960f9 | ||
|
|
91550c926b | ||
|
|
184525687c | ||
|
|
cdede817fd | ||
|
|
df42c2f6fe | ||
|
|
b53f2f4a60 | ||
|
|
5ff27c4fe2 | ||
|
|
e5fff5117d | ||
|
|
615cd5aac3 | ||
|
|
621d95a89e | ||
|
|
cd383167f9 | ||
|
|
cb2969002c | ||
|
|
1cf204e23d | ||
|
|
393834b253 | ||
|
|
4ca8256e18 | ||
|
|
280d91b9bf | ||
|
|
980152fd53 | ||
|
|
d994420d4d | ||
|
|
38f3ecf01f | ||
|
|
5cd0d05b31 | ||
|
|
62519c3638 | ||
|
|
27bb2deb1a | ||
|
|
a2c8e71f14 | ||
|
|
d1f56e7c2e | ||
|
|
a9fd0cbf02 | ||
|
|
be8c89ec00 | ||
|
|
4f53752693 | ||
|
|
393673222d | ||
|
|
a88f4524d5 | ||
|
|
3a02066a66 | ||
|
|
7e3981d1a3 | ||
|
|
ec2900e4dd | ||
|
|
d718a27abf | ||
|
|
db8027911c | ||
|
|
aef3b4117d | ||
|
|
6728b77aca | ||
|
|
a7c0b42965 | ||
|
|
667b6ecbbd | ||
|
|
b852878a4a | ||
|
|
940f1f7486 | ||
|
|
49c705b5ad | ||
|
|
5163e8a433 | ||
|
|
71e9603e4e | ||
|
|
c71102f44e | ||
|
|
9b9158a765 | ||
|
|
595e2b4e81 | ||
|
|
ae8e205343 | ||
|
|
00b33fbd42 | ||
|
|
841b06df37 | ||
|
|
522a06f864 | ||
|
|
fa14ed1649 | ||
|
|
64e41e066b | ||
|
|
2a71f35005 | ||
|
|
961a337a5e | ||
|
|
f0622a692e | ||
|
|
ef7042dd9d | ||
|
|
046ecfa329 | ||
|
|
47fbe49861 | ||
|
|
2f34315634 | ||
|
|
c8d6ad6546 | ||
|
|
3dfae3074e | ||
|
|
97bebe52c9 | ||
|
|
8e1845f57b | ||
|
|
6cd6769997 | ||
|
|
03884d3566 | ||
|
|
899925e249 | ||
|
|
efefa398ce | ||
|
|
1ee24c69bc | ||
|
|
b16a36980c | ||
|
|
c1b52bbc04 | ||
|
|
9461ad4d4d | ||
|
|
e2ef6e9c34 | ||
|
|
cdc8873f5b | ||
|
|
b82b205d92 | ||
|
|
39d879f240 | ||
|
|
9c1db9cab3 | ||
|
|
2f5c00f2bd | ||
|
|
dc36b313ff | ||
|
|
093fa0107a | ||
|
|
e8f5ec2579 | ||
|
|
62241668df | ||
|
|
ee67c08b64 | ||
|
|
41a14391a2 | ||
|
|
6eccca31b9 | ||
|
|
d497942a9f | ||
|
|
68c7352108 | ||
|
|
0d40e43c4b | ||
|
|
9867fd82df | ||
|
|
1c941fdfbb | ||
|
|
0ba67a72f8 | ||
|
|
09b9e26b2d | ||
|
|
d25c1d5564 | ||
|
|
d4358ed3f7 | ||
|
|
cc6ffa35fc | ||
|
|
55e9cb2f58 | ||
|
|
2641256254 | ||
|
|
473116f603 | ||
|
|
e18d63df4a | ||
|
|
4b1b3f6913 | ||
|
|
f6da5e4136 | ||
|
|
d1d5d39144 | ||
|
|
754358113f | ||
|
|
c24207516d | ||
|
|
2a23bce78d | ||
|
|
a19b82ea5f | ||
|
|
ed9590aef1 | ||
|
|
25683221cc | ||
|
|
9cc9e3ddbb | ||
|
|
654f8aeb19 | ||
|
|
e349abb897 | ||
|
|
3027a6755f | ||
|
|
5404aa6037 | ||
|
|
a319e32438 | ||
|
|
2eaf57fc67 | ||
|
|
88ec4a55cb | ||
|
|
0acbc60ec1 | ||
|
|
4362971c6a | ||
|
|
94c0d1b833 | ||
|
|
1e795d4e16 | ||
|
|
93a027ca23 | ||
|
|
5f3958fab4 | ||
|
|
1351b708fe | ||
|
|
e867e039bb | ||
|
|
1d1087a3b3 | ||
|
|
e91f1ea1b2 | ||
|
|
55d16edadc | ||
|
|
b79245ddb3 | ||
|
|
e706300b6d | ||
|
|
d08cbd77aa | ||
|
|
b070ae73a4 | ||
|
|
2c30eef9a1 | ||
|
|
31cdc113fc | ||
|
|
f6f813498b | ||
|
|
4c8baaf7c0 | ||
|
|
57ca937f27 | ||
|
|
d13e39e345 | ||
|
|
f52c141062 | ||
|
|
40042da8ff | ||
|
|
c251304146 | ||
|
|
c047e37a79 | ||
|
|
d2c57447f0 | ||
|
|
79e9dabc11 | ||
|
|
b04dc4d457 | ||
|
|
f86e7810be | ||
|
|
2018c82ea1 | ||
|
|
c27f3603e6 | ||
|
|
6f82b806ad | ||
|
|
cbb8935660 | ||
|
|
21ebbb942f | ||
|
|
988ecd95e3 | ||
|
|
3f3e70c686 | ||
|
|
073b040981 | ||
|
|
b1a058ccdd | ||
|
|
cbc7b8c3c7 | ||
|
|
13340e4200 | ||
|
|
0076c1f218 | ||
|
|
6fc71b7528 | ||
|
|
b42f1e99e2 | ||
|
|
afd6ad06a6 | ||
|
|
ea0bfaa2f3 | ||
|
|
5c3ec176c2 | ||
|
|
40425ee9ec | ||
|
|
424db825a3 | ||
|
|
b9cc4cd629 | ||
|
|
243b8f025b | ||
|
|
6c60b67588 | ||
|
|
910bb13711 | ||
|
|
add55cfc2a | ||
|
|
565820325b | ||
|
|
6bb4d67d92 |
@@ -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
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
name: Bob the Builder
|
||||
|
||||
# Run on all branches, including all pull requests, except the 'dev'
|
||||
# branch since that's where we run Coverity Scan (limited tokens/day)
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- '!dev'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Verify we can build on latest Ubuntu with both gcc and clang
|
||||
name: ${{ matrix.compiler }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [gcc, clang]
|
||||
fail-fast: false
|
||||
env:
|
||||
MAKEFLAGS: -j3
|
||||
CC: ${{ matrix.compiler }}
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config tree jq libuev-dev libite-dev
|
||||
- uses: actions/checkout@v3
|
||||
- name: Static Finit
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix= --enable-static
|
||||
make V=1
|
||||
- name: Regular Finit
|
||||
run: |
|
||||
./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
|
||||
run: |
|
||||
DESTDIR=/tmp make install-strip
|
||||
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
|
||||
- name: Verify starting and showing usage text
|
||||
run: |
|
||||
sudo /tmp/sbin/finit -h
|
||||
sudo /tmp/sbin/initctl -h
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
make check || (cat test/test-suite.log; false)
|
||||
- name: Upload Test Results
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: finit-test-${{ matrix.compiler }}
|
||||
path: test/*.log
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Coverity Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'dev'
|
||||
|
||||
env:
|
||||
PROJECT_NAME: finit
|
||||
CONTACT_EMAIL: troglobit@gmail.com
|
||||
COVERITY_NAME: troglobit-finit
|
||||
COVERITY_PROJ: troglobit%2Ffinit
|
||||
|
||||
jobs:
|
||||
coverity:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
id: coverity-toolchain-cache
|
||||
with:
|
||||
path: cov-analysis-linux64
|
||||
key: ${{ runner.os }}-coverity
|
||||
- name: Download Coverity Scan
|
||||
if: steps.coverity-toolchain-cache.outputs.cache-hit != 'true'
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
run: |
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 \
|
||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}" \
|
||||
-O cov-analysis-linux64.tar.gz
|
||||
mkdir cov-analysis-linux64
|
||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.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
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
|
||||
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
|
||||
cov-build --dir cov-int make
|
||||
- name: Submit result to Coverity Scan
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
run: |
|
||||
tar czvf ${PROJECT_NAME}.tgz cov-int
|
||||
curl \
|
||||
--form project=${COVERITY_NAME} \
|
||||
--form token=$TOKEN \
|
||||
--form email=${CONTACT_EMAIL} \
|
||||
--form file=@${PROJECT_NAME}.tgz \
|
||||
--form version=trunk \
|
||||
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
|
||||
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Release General
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build and upload release tarball
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Installing dependencies ...
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
|
||||
- name: Creating Makefiles ...
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
|
||||
--with-keventd
|
||||
- name: Build release ...
|
||||
run: |
|
||||
make release || (cat test/test-suite.log; false)
|
||||
ls -lF ../
|
||||
mkdir -p artifacts/
|
||||
mv ../*.tar.* artifacts/
|
||||
- name: Extract ChangeLog entry ...
|
||||
run: |
|
||||
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|
||||
|head -n -1 > release.md
|
||||
cat release.md
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: Finit v${{ github.ref_name }}
|
||||
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@v3
|
||||
- name: Check for any commits since last week
|
||||
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@v3
|
||||
- name: Base run
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
|
||||
make -j9 V=1
|
||||
- name: Install to /tmp
|
||||
run: |
|
||||
DESTDIR=/tmp make install-strip
|
||||
- name: Check dist tarball
|
||||
run: |
|
||||
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
|
||||
dir=finit-$ver/_build/sub
|
||||
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
||||
@@ -45,3 +45,6 @@ GTAGS
|
||||
/.autoconf
|
||||
/.automake
|
||||
/.built
|
||||
|
||||
# VS Code user settings
|
||||
.vscode
|
||||
|
||||
@@ -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/:
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
# Travis CI integration -- https://travis-ci.org/troglobit/finit
|
||||
# Defaults to GNU GCC and autotools: ./configure && make && make test
|
||||
language: c
|
||||
|
||||
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
|
||||
sudo: false
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
# The secure: declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
env:
|
||||
global:
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig
|
||||
- CFLAGS="-W -Wall -Wextra"
|
||||
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- tree
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "troglobit/finit"
|
||||
description: "finit | Fast & Extensible init for Linux"
|
||||
notification_email: troglobit@gmail.com
|
||||
build_command_prepend: "./autogen.sh && ./configure --prefix= --disable-doc --disable-contrib --with-watchdog"
|
||||
build_command: "make"
|
||||
branch_pattern: dev
|
||||
|
||||
install:
|
||||
- wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
|
||||
- wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
|
||||
- tar xf libuev-2.3.2.tar.xz
|
||||
- tar xf libite-2.3.1.tar.gz
|
||||
- (cd libuev-2.3.2 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- (cd libite-2.3.1 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --prefix= --enable-static
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- ./configure --prefix= --enable-x11-common-plugin --with-watchdog
|
||||
- cat config.log
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- make install-strip DESTDIR=/tmp/finit
|
||||
- tree /tmp/finit
|
||||
- ldd /tmp/finit/sbin/finit
|
||||
- size /tmp/finit/sbin/finit
|
||||
- size /tmp/finit/sbin/initctl
|
||||
- size /tmp/finit/sbin/reboot
|
||||
- LD_LIBRARY_PATH=/tmp/lib /tmp/finit/sbin/initctl -h
|
||||
- unshare -V
|
||||
- nsenter -V
|
||||
- chroot --version
|
||||
- 'LD_LIBRARY_PATH=/tmp/lib make distcheck || (cat finit-4.0-beta1/_build/sub/test/test-suite.log; exit 1)'
|
||||
+667
-13
@@ -4,10 +4,586 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[4.1][UNRELEASED]
|
||||
-----------------
|
||||
[4.5][] - 2023-09-14
|
||||
--------------------
|
||||
|
||||
Bug fix release.
|
||||
### Changes
|
||||
- Refactor `runparts` and `/etc/rc.local` to no longer block the main
|
||||
loop, allowing these scripts to call `initctl` to interact with Finit.
|
||||
Still in a limited way due to them being used at bootstrap, issue #356
|
||||
- Refactor the `run` stanza to no longer block the main loop, issue #362
|
||||
- Allow `sulogin` with a user different from `root`, issue #357
|
||||
- Allow disabling invocation of rescue mode from kernel command line
|
||||
- Add `initctl -f` to force-skip asking Finit for existing services
|
||||
when creating new services during bootstrap, e.g. `/etc/rc.local`
|
||||
- `initctl runlevel` now returns `N S` instead of `N 10` in bootstrap
|
||||
- Improved logging on failure to `execvp()` a run/task/service, now
|
||||
with `errno`, e.g., "No such file or directory" when the command
|
||||
is missing from `$PATH`
|
||||
- Add support for Bash completion to `initctl`, issue #360
|
||||
- Handle absolute path to `initctl [enable|disable]`, not supported
|
||||
- Update `finit.conf(5)` man page with the recommended directory
|
||||
hierarchy in `/etc/finit.d/`
|
||||
- The `runparts` code has been split into `/libexec/finit/runparts`
|
||||
- Add SysV Init Compatibility section to documentation
|
||||
|
||||
### Fixes
|
||||
- Fix #227: believed to have been fixed in [4.3][], the root cause was
|
||||
actually that Finit was waiting for a process that was no longer in
|
||||
the system. The fix is to ask the kernel on process-stop-timeout and
|
||||
replay the lost PID so that Finit can continue with reboot/shutdown
|
||||
- Fix #358: fix inotify events for `/etc/finit.conf`, improved log
|
||||
messages and error handling
|
||||
- Fix #361: cgroup move fail if run/task/services start as non-root.
|
||||
Regression in the [4.4][] release cycle while adding support for the
|
||||
pre:/post:/ready: scripts. Now the latter scripts also properly run
|
||||
in their correct cgroup
|
||||
- The `runparts` executor now skips backup files (`foo~`)
|
||||
- The `runparts` stanza now properly appends ` start` to scripts that
|
||||
start with `S[0-9]+`. This has been broken for a very long time.
|
||||
|
||||
|
||||
[4.4][] - 2023-05-15
|
||||
--------------------
|
||||
|
||||
> **Note 1:** this release contains changes to the `.conf` parser. If you
|
||||
> have .conf file statements with comment character (`#`) in the command
|
||||
> options or description, you must now escape them (`\#`). Issue #186
|
||||
>
|
||||
> **Note 2:** prior to this release, runlevel `S` and `0` were after
|
||||
> boot treated as the same runlevel. This caused `task [06] ...` to
|
||||
> also run at bootstrap instead of just at shutdown and reboot. The
|
||||
> changes made to Finit to separate `S` from `0` require you to update
|
||||
> the allowed runlevels for services that are allowed to continue
|
||||
> running at shutdown. I.e., change `[S123456789]` to `[S0123456789]`
|
||||
> for, e.g., `sysklogd`. Issue #352
|
||||
|
||||
### Changes
|
||||
* Add limited tmpfiles.d(5) support
|
||||
|
||||
This change adds very basic `tmpfiles.d/` support to Finit. Most of
|
||||
the basic types are supported, but not all, so for now, please check
|
||||
the code and examples for details on what is working.
|
||||
|
||||
* If a run/task/service command does not exist, skip registering it
|
||||
|
||||
This changes the semantics of Finit a bit by checking for the command
|
||||
to run when registering it, skipping commands that cannot be found in
|
||||
the absolute path provided in the command, or in `$PATH`
|
||||
|
||||
This change includes a new `nowarn` flag that can be used to prevent
|
||||
Finit from warning for missing commands. See below for an example.
|
||||
|
||||
* Add run/task/service support for `conflict:foo` handling
|
||||
* Add run/task/service support for `if:[!]ident` and `if:<[!]cond>`
|
||||
|
||||
Conditional loading of stanza depending on ident is already loaded (or
|
||||
not), or condition satisfied (or not). E.g., do not run `mdev` if we
|
||||
found and registered `udevd`, or load service only if `<boot/testing>`
|
||||
condition is set.
|
||||
|
||||
The optional leading `!` negates the comparison, if NOT foo then ...
|
||||
|
||||
* Add support for static services in `/lib/finit/system/*.conf`
|
||||
|
||||
Slowly migrating away from hard-coded services in plugins. This way
|
||||
it's possible for the user to both inspect and override as needed.
|
||||
|
||||
* Migrate hotplug plugin to a conditional `/lib/finit/system/hotplug.conf`
|
||||
|
||||
This is the first example of the just minted advanced stanza syntax with
|
||||
`if:`-statements, `conflict:` handling, and `nowarn` flags.
|
||||
|
||||
* Initial support for template services, `foo@.conf`, similar to systemd
|
||||
|
||||
$ initctl show avahi-autoipd@
|
||||
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
|
||||
|
||||
To enable ZeroConf for, e.g., `eth0`, use
|
||||
|
||||
$ initctl enable avahi-autoipd@eth0.conf
|
||||
|
||||
The enabled symlink will be set up to `avahi-autoipd@.conf` and every
|
||||
instance of `%i` will in the instantiated directive be replaced with
|
||||
`eth0`. Inspect the result with:
|
||||
|
||||
$ initctl status avahi-autoipd:eth0
|
||||
|
||||
* Add `devmon`, a `<dev/foo>` condition provider, issue #185
|
||||
* Support for line continuation character `\` in .conf files, issue #186
|
||||
|
||||
service name:sysklogd [S123456789] \
|
||||
env:-/etc/default/sysklogd \
|
||||
syslogd -F $SYSLOGD_ARGS \
|
||||
-- System log daemon
|
||||
|
||||
* `HOOK_BASEFS_UP` has been moved! External plugins that need to call
|
||||
`service_register()`, please use `HOOK_SVC_PLUGIN` from now on.
|
||||
Apologies for any inconveniences this might cause!
|
||||
* getty: add support for `/etc/os-release` to replace `uname` output
|
||||
This change, which has a fallback to `/usr/lib/os-release`, overrides
|
||||
traditional modifiers with the os-release variant. These values were
|
||||
taken from `uname`, which on Linux systems are pretty useless since
|
||||
they always return the kernel name and version instead of the
|
||||
distro/OS values.
|
||||
|
||||
E.g., \s becomes `PRETTY_NAME` instead of 'Linux' and \v becomes the
|
||||
pretty `VERSION`, while \r becomes `VERSION_ID`.
|
||||
* Support for overriding `/etc/finit.conf` and `/etc/finit.d` issue #235
|
||||
New (kernel) command line option `finit.config=PATH` which can be used
|
||||
to redirect Finit to start up with, e.g., `/etc/factory.conf` instead of
|
||||
`/etc/finit.conf`.
|
||||
|
||||
For the complete experience a new top-level configuration file directive
|
||||
`rcsd PATH` has aslo been added. It in turn can be used by `factory.conf`
|
||||
as follows to override `/etc/finit.d`:
|
||||
|
||||
rcsd /etc/factory.d
|
||||
|
||||
* Support for overriding `/etc/finit.d` from the alternate `finit.conf`
|
||||
with a new `rcsd /path/to/dot.d/` .conf file directive
|
||||
* Support for `fsck_mode=[auto,skip,force]` + `fsck_repair=[preen,no,yes]`
|
||||
* Add `set` keyword for environment variables set in `/etc/finit.conf`
|
||||
* Support `finit.cond=foo` cmdline `<boot/foo>` conditions, issue #250
|
||||
* `initctl` JSON output support for status and conditions, issue #273
|
||||
Example:
|
||||
|
||||
root@infix:~$ initctl status -j resolvconf
|
||||
{
|
||||
"identity": "resolvconf",
|
||||
"description": "Update DNS configuration",
|
||||
"type": "task",
|
||||
"forking": false,
|
||||
"status": "done",
|
||||
"exit": { "code": 0 },
|
||||
"origin": "/etc/finit.d/enabled/sysrepo.conf",
|
||||
"command": "resolvconf -u",
|
||||
"restarts": 0,
|
||||
"pidfile": "none",
|
||||
"pid": 0,
|
||||
"user": "root",
|
||||
"group": "root",
|
||||
"uptime": 0,
|
||||
"runlevels": [ 1, 2, 3, 4, 5, 7, 8, 9 ]
|
||||
}
|
||||
|
||||
The excellent tool `jq` can be used to extract parts of the output for
|
||||
further scripting. E.g. `initctl status -j foo | jq .exit.status`
|
||||
|
||||
* Add JSON support to `initctl ls` command
|
||||
|
||||
This allows for easy access to the *disabled* services:
|
||||
|
||||
root@anarchy:~# initctl ls --json |jq '.available - .enabled'
|
||||
[
|
||||
"chronyd.conf",
|
||||
"dnsmasq.conf",
|
||||
"gdbserver.conf",
|
||||
"inadyn.conf",
|
||||
"inetd.conf",
|
||||
"isisd.conf",
|
||||
"lldpd.conf",
|
||||
"mstpd.conf",
|
||||
"ntpd.conf",
|
||||
"ospf6d.conf",
|
||||
"ospfd.conf",
|
||||
"querierd.conf",
|
||||
"ripd.conf",
|
||||
"ripng.conf",
|
||||
"sshd.conf",
|
||||
"syslogd.conf",
|
||||
"telnetd.conf",
|
||||
"uftpd.conf",
|
||||
"wpa_supplicant.conf",
|
||||
"zebra.conf"
|
||||
]
|
||||
|
||||
* Allow `manual:yes` on sysv/service/run/task stanzas, issue #274
|
||||
* Add support for `oncrash:script` to call the `post:script` action, if
|
||||
defined, for a crashing service. The `EXIT_CODE` variable sent to the
|
||||
script is set to `crashed`. Issue #282
|
||||
* Search for plugins in `/usr` and `/usr/local` as well, issue #284
|
||||
* tty: add support for `passenv` flag to `/bin/login`, issue #286
|
||||
* Add reboot/shutdown/poweroff timeout `-t SEC` to initctl, issue #295
|
||||
* Add support for s6 and systemd readiness notification, issue #299.
|
||||
Service readiness notification to support daemons employing systemd
|
||||
and s6 notification. Complementing the native Finit readiness support
|
||||
using PID files that exist already.
|
||||
|
||||
The two have slightly different ways of implementing readiness:
|
||||
|
||||
- https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||
- https://skarnet.org/software/s6/notifywhenup.html
|
||||
|
||||
Finit now provides both a `NOTIFY_SOCKET` environment variable, for
|
||||
systemd, and a way to start s6 daemons with a descriptor argument.
|
||||
|
||||
For details on the syntax, see the `service` documentation.
|
||||
|
||||
This change also renames internal states for run/task/services to
|
||||
avoid any confusion with the introduction of `ready:scripts`:
|
||||
|
||||
- `WAITING -> PAUSED`
|
||||
- `READY -> WAITING`
|
||||
|
||||
A service condition that used, e.g., `<service/foo/ready>` should now
|
||||
instead use `<service/foo/waiting>`
|
||||
|
||||
* Add `ready:script` for services, called when daemon is ready, issue #300
|
||||
* Add support for running scripts at shutdown at two new hook points
|
||||
during the shutdown process, issue #302. See doc/plugins.md for details:
|
||||
|
||||
- `HOOK_SVC_DN`: after all services and non-reserved processes have been
|
||||
killed (and collected)
|
||||
- `HOOK_SYS_DN`: after all file systems have been unmounted, *just prior*
|
||||
to Finit calling `reboot()` to shut down or reboot the system
|
||||
|
||||
* The `modules-load` plugin now default to runlevel `[S]`, in previous
|
||||
releases it was `[2345]`. This breaking change is to align it more
|
||||
with what users mostly want (modules loaded before services start) and
|
||||
can be changed back to the old behavior with a per-file setting:
|
||||
|
||||
set runlevel 2345
|
||||
|
||||
* The `modules-load` plugin now adds silent tasks for modprobe. This to
|
||||
prevent confusing `[ OK ]` boot messages when in fact modprobe failed.
|
||||
* The `modules-load` plugin now support `set modprobe /path/to/modprobe`
|
||||
* The header files `finit/conf.h` and `finit/service.h` are now exported
|
||||
for external plugins
|
||||
* Add support for multiple args to `initctl cond set/clr`, issue #329
|
||||
* Silence confusing `[ OK ]` progress from modules-load plugin, issue #332
|
||||
|
||||
This change drops the confusing status progress output, which was always
|
||||
OK since the actual modprobe operation runs in the background. No need
|
||||
to show status of the "added a task to finit, found modprobe" command.
|
||||
|
||||
* dbus plugin: adapt to other operating systems
|
||||
|
||||
Not all Linux systems are based on Debian, and even if they are inspired
|
||||
by Debian (Buildroot), they do not necessarily use the same defaults.
|
||||
Probes the system at runtime for:
|
||||
|
||||
- dbus user and group
|
||||
- dbus PID file
|
||||
|
||||
If the user/group cannot be found we fall back to `root`, if the PID
|
||||
file cannot be determined we ignore PID file readiness.
|
||||
|
||||
* Improve documentation for runparts and hook scripts. Issue #315, #320
|
||||
* Add `HOOK_NETWORK_DN`, called after change to runlevel 6 or 0, issue #319
|
||||
* Use sysklogd `logger` tool instead of legacy `logit` tool, issue #344
|
||||
|
||||
For log redirection Finit has the legacy `logit` tool. This change
|
||||
allows Finit to use the `sysklogd` project's extended `logger` tool
|
||||
instead, when available. Allowing logging with the process' PID.
|
||||
|
||||
* Add `initctl` aliases: `cat -> show`, `kill -> signal`
|
||||
* Add `initctl -n,--noerr` to return OK(0) if services do not exist, for
|
||||
integration with openresolv and scripts with similar requirements
|
||||
* Add `initctl plugins`, list installed plugins
|
||||
* Add timestamp to log messages in fallback and logging to `stderr`.
|
||||
When there is no log daemon, and we are running in a container, or we
|
||||
cannot log to the kernel ring buffer, then we log to `stderr`. This
|
||||
change improves log output by prefixing each message with a timestamp.
|
||||
|
||||
### Fixes
|
||||
* Fix #254: document limitations in `rc.local` and `runparts` scripts
|
||||
* Fix #269: add compulsory kernel symlinks in `/dev`
|
||||
* Fix #275: `initctl status foo` should list all instances, regression
|
||||
introduced in v4.3
|
||||
* Fix #278: enforce conditions also for running `pre:` scripts
|
||||
* Fix #279: allow `restart:always`, of crashing services. Similar to
|
||||
`respawn` but honors `restart_sec`
|
||||
* Fix #280: allow calling `initctl restart foo` from within foo
|
||||
* Fix #283: too quick timeout at bootstrap of lingering tasks
|
||||
* Fix #285: `initctl restart` should start crashed service
|
||||
* Fix #288: enable built-in `sulogin` in Alpine and Void Linux builds
|
||||
* Fix #293: modprobe plugin: support for coldplugging more devices. It
|
||||
turns out, not all buses in Linux add `modalias` attributes to their
|
||||
devices in sysfs. One notable exception are MDIO buses. The plugin's
|
||||
scan routine would thus not pick them up.
|
||||
* Fix #294: `shutdown --help` mistakenly shuts down system
|
||||
* Fix #310: Always use configured restart delay for crashing services.
|
||||
If no delay is configured, default to an initial 2000 msec for forking
|
||||
daemons and start-stop scripts, and 1 msec for non-forking daemons
|
||||
* Fix #311: document how to combine device tree with conditions
|
||||
* Fix #312: restart services with respawn set, e.g. ttys, immediately
|
||||
* Fix #313: Cancel pending restart timer on stop/start/restart/reload
|
||||
* Fix #314: skip run/task/service restart if conditions are lost
|
||||
* Fix #315: add environment variables to hook scripts
|
||||
|
||||
All hook scripts are called with at least one environment variable
|
||||
set, `FINIT_HOOK_NAME`, useful when reusing the same hook script for
|
||||
multiple hook points. It is set to the string name, also used by the
|
||||
path, e.g., `hook/net/up`
|
||||
|
||||
For all hook points from hook/sys/shutdown and later, `FINIT_SHUTDOWN`
|
||||
is also set, to one of: `halt`, `poweroff`, `reboot`
|
||||
|
||||
* Fix #317: make sure hook scripts don't run twice, also fixes #316
|
||||
* Fix #318: only show `[ OK ] Calling foo` progress for `runparts ...`
|
||||
* Fix #320: the API/IPC socket is closed immediately at shutdown/reboot
|
||||
to protect hook scripts or services calling initctl. There is no way
|
||||
to service these requests safely at that time
|
||||
* Fix #333: consider a service dirty if command line args have changed
|
||||
|
||||
This fixes `initctl reload` correctly restarting all daemons that have
|
||||
new command line arguments.
|
||||
|
||||
Previously command line arguments changes were only acted upon if the
|
||||
service was explicitly reloaded `initctl reload myservice`.
|
||||
|
||||
Found and fixed by Jack Newman
|
||||
|
||||
* Fix #338: ensure shutdown hooks are called properly; `hook/sys/down`
|
||||
and `hook/svc/down` hook scripts, found and fixed by Jack Newman
|
||||
* Fix #339: use absolute path in `/etc/finit.d/enabled/` symlinks, for
|
||||
use-cases when `/etc` is read-only and `/etc/finit.d/enabled ->
|
||||
/mnt/finit.d/enabled`, reported by Jack Newman
|
||||
* Fix #340: Finit ignores deleted/moved `.conf` file sin `/etc/finit.d`
|
||||
* Fix #342: in runlevel S (bootstrap), not all `initctl` commands can be
|
||||
supported, block the following: runlevel, reload, start/stop, restart,
|
||||
reload, halt, poweroff, suspend. Also, prevent `SIGHUP` and `SIGUSR1`
|
||||
signals when in shutdown or reboot
|
||||
* Fix #352: separate runlevel S from runlevel 0
|
||||
* Fix #355: regression in v3.2 stopping a process and its group
|
||||
|
||||
In Finit v3.2 a regression was introduced that affects the way Finit
|
||||
stops a supervised process and its process group.
|
||||
|
||||
Instead of sending SIGTERM to the process, and thus delegating the
|
||||
responsibility to that process to inform any children it may have, as
|
||||
of commit 91a9c83 Finit sends SIGTERM to the entire process group.
|
||||
For SIGKILL this is fine, SIGKILL only runs as cleanup and as a last
|
||||
ditch effort if the process doesn't respond to SIGTERM.
|
||||
|
||||
This regression, introduced in v3.2, directly affects services like
|
||||
`avahi-autoipd` that have forked off children that it needs to tell to
|
||||
exit cleanly before it returns. With the patch in question these
|
||||
children are never allowed to complete, which in turn causes lingering
|
||||
169.254 link-local addresses on interfaces.
|
||||
|
||||
* Fix bootmisc plugin: octal permission on `/run/lock` and `/var/lock`
|
||||
|
||||
* Ensure `initctl cond get` support the flux state (exit code 255)
|
||||
* Fix potential socket leak at bootstrap and shutdown
|
||||
* Fix potential NULL pointer deref in kernel command line parser
|
||||
* Fix lingering condition in service after reload of service with new
|
||||
config that has no condition
|
||||
* Fix wrong path to command in service after reload, may have changed
|
||||
* `logit`: fall back to package name if `$LOGNAME` and `$USER` are
|
||||
unset. **Note:** you should probably not use `logit` in your own
|
||||
scripts, it is only meant for internal use by Finit. We recommend
|
||||
using `logger` from the `bsdutils` or `sysklogd` packages instead
|
||||
* Fix issue where `env:`/`pre:`/`post:`/etc. is removed from a service
|
||||
|
||||
The trick is when reloading a service like this:
|
||||
|
||||
service env:/etc/env serv -np -e foo:bar
|
||||
|
||||
into this:
|
||||
|
||||
service pre:/bin/pre.sh serv -np
|
||||
|
||||
In the second the `env:` has been removed and `pre:` added. Prior to
|
||||
this patch, `env:` was kept leading to unintended behavior.
|
||||
* Fix parse/diff of command line args, e.g., `nginx -g 'daemon off;'`
|
||||
|
||||
Starting a service like this works fine:
|
||||
|
||||
service [2345789] env:-/etc/default/nginx nginx -g 'daemon off;'
|
||||
|
||||
However, on `initctl reload` (and no change to .conf files) the arg
|
||||
list was lost while parsing the .conf files. Leading to a false
|
||||
positive 'diff' in args causing nginx to be unnecessarily restarted.
|
||||
* Fix issue with disabled "linewrap" on the console TTY after login.
|
||||
The root cause is `qemu-system-x86_64 -nographics` disabling it when
|
||||
starting up. The correct `\e[?7h` escape code is now used.
|
||||
|
||||
|
||||
[4.3][] - 2022-05-15
|
||||
--------------------
|
||||
|
||||
Critical bug fix release. If you run a 32-bit target with GLIBC 2.34
|
||||
you *need* to upgrade!
|
||||
|
||||
> **Note:** system verbosity on console at start and shutdown has been
|
||||
> increased. Now the output of all commands is logged to the
|
||||
> system logger, for early services `/dev/kmsg` is used.
|
||||
>
|
||||
> **Also:** please notice the updated support for enabling and disabling
|
||||
> kernel and Finit debug messages on the system console. Very
|
||||
> useful when debugging either of them, e.g., a kernel module.
|
||||
> For details, see [cmdline.md](doc/cmdline.md).
|
||||
|
||||
### Changes
|
||||
* Support for overriding default runlevel from kernel command line. Any
|
||||
runlevel `[1-9]` may be selected, except 6 (reboot). Issue #261
|
||||
* New command line option: `finit.fstab=/etc/fstab.custom`, with full
|
||||
support for mounting, mount helpers, fsck, and swapon/off, issue #224
|
||||
* Support for special device `/dev/root`, which may not exist in `/dev`.
|
||||
Finit now looks up the matching block device for `/` in `/sys/block/`
|
||||
* Loading `module`s no longer shows arguments in progress output
|
||||
* Warning messages in progress output now in yellow, not red, issue #214
|
||||
* `initctl`, new command line option `-V,--version` for ease of use
|
||||
* New condition `done` for run task, issue #207 by Ming Liu, Atlas Copco
|
||||
* Refactor parts of shutdown and reboot sequence for PREEMPT-RT kernels,
|
||||
by Robert Andersson, Mathias Thore, and Ming Liu, Atlas Copco
|
||||
* Conditions for run/task/sysv status, e.g. `run/foo/success` and
|
||||
`task/bar/failure`. Issue #232, by Ming Liu, Atlas Copco
|
||||
* Conditions for services, can be used to synchronize other stanzas:
|
||||
- `service/foo/running`
|
||||
- `service/foo/halted`
|
||||
- `service/foo/missing`
|
||||
- `service/foo/crashed`
|
||||
- `service/foo/stopped`
|
||||
- `service/foo/busy`
|
||||
- `service/foo/restart`
|
||||
* `initctl signal` support, by Jörgen Sigvardsson, issue #225
|
||||
* `initctl cond get` support to match `cond [set | clear]`, issue #255
|
||||
* `[WARN]` messages on console now printed in yellow, issue #214
|
||||
* Network services now also stopped when going to runlevel 6 (reboot),
|
||||
not just runlevel 0 (shutdown) or 1 (single-user)
|
||||
* When `ifup` is missing on the system, bring at least `lo` up at boot
|
||||
* Log output from `ifup -a` (and `ifdown -a`), to syslog
|
||||
* Avoid blocking PID 1 when starting SysV init scripts
|
||||
* Allow custom `pid:` for SysV init scripts
|
||||
* Document supported types of forking/non-forking services
|
||||
* Auto-detect running in some common forms of containers
|
||||
* Simplify shutdown/reboot when running in a container
|
||||
* Log to `stderr` when running in a container w/o syslog daemon
|
||||
* Add support for `type:forking` to services, already supported but
|
||||
with a very difficult `pid:` syntax, issue #223. Docs updated
|
||||
* Support for setting global environment variables in `finit.conf`,
|
||||
please note: this also affects Finit itself, be careful!
|
||||
* Extended environment variables for pre/post scripts, issue #189
|
||||
* 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
|
||||
`sulogin` recovery mode, issue #247
|
||||
* The bundled `sulogin` is no longer enabled by default, in favor of
|
||||
distribution versions. Enable with `./configure --with-sulogin`
|
||||
* Support args to sysv-like scripts, e.g. `bridge-stp br0 start`
|
||||
* The `modules-load` plugin now skips all lines starting with `#` and
|
||||
`;`. Furthermore, files in `/etc/modules-load.d/*.conf` are now read
|
||||
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
|
||||
|
||||
### Fixes
|
||||
* Fix nasty 32/64-bit alignment issue between finit and its plugins,
|
||||
applicable to 32-bit targets with GLIBC 2.34 and later. External
|
||||
plugins must make sure to use, at least: `-D_TIME_BITS=64`
|
||||
* Fix #215: disable cgroup support at runtime if kernel lacks support
|
||||
or does not have the required controllers (cpu)
|
||||
* Fix #217: iwatcher initialization issue, by Ming Liu, Atlas Copco
|
||||
* Fix #218: initctl matches too many services, by Ming Liu, Atlas Copco
|
||||
* Fix #219: not all filesystems unmounted at shutdown, by Ming Liu,
|
||||
Mathias Thore, and Robert Andersson, Atlas Copco
|
||||
* Fix #226: initctl shows wrong PID for crashing services
|
||||
* Fix #227: reboot stalls if process stopped with `[WARN]`
|
||||
* 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 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
|
||||
* Fix suspend to RAM issue. Previously `reboot(RB_SW_SUSPEND)` was
|
||||
used, now the modern `/sys/power/state` API is used instead.
|
||||
* Fix nasty run/task/service matcher bug, triggered by stanzas using the
|
||||
same basename of a command but different `:ID`. Caused Finit to match
|
||||
with already registered but different run/task/service
|
||||
|
||||
|
||||
[4.2][] - 2022-01-16
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Support for non-root users to use `initctl`, e.g. group wheel
|
||||
* Support for new libite (-lite) header namespace
|
||||
* RTC plugin now reset an invalid RTC time to the kernel default
|
||||
time, 2000-01-01 00:00, prevents errors and is less crazy than
|
||||
some systems coming with with <= Jan 1, 1970
|
||||
* urandom plugin now use RNDADDENTROPY ioctl to seed kernel rng,
|
||||
incrementing entropy count. Also, 32 kiB instead of 512 bytes are
|
||||
now saved (and restored) on reboot. This should greatly improve
|
||||
reliability of systems with none or poor HWRNG
|
||||
* Kernel logging to console (loglevel >= 7, debug, when quiet mode is
|
||||
not used) is now honored by Finit, regardless of the finit.debug
|
||||
command line option
|
||||
* Reduced default log level from `LOG_NOTICE` to `LOG_INFO`
|
||||
* Wrapped all calls to mount(2) to add logging in case of failure
|
||||
* New configure options to control fastboot (no fsck) and fsck fix
|
||||
options, by Ming Liu, Atlas Copco
|
||||
* Support for overriding default `/etc/nologin` file with an external
|
||||
`#define`, by Ming Liu, Atlas Copco
|
||||
* Support for overriding default `/var/run/dbus/pid` file with an
|
||||
external `#define`, by Ming Liu, Atlas Copco
|
||||
* Support for more service options to control respawn behavior of
|
||||
crashing services, by Robert Andersson and Ming Liu, Atlas Copco
|
||||
* Support for `initctl ident [NAME]` which lists all instances of
|
||||
`NAME`, or all enabled system run/tasks and services
|
||||
* Show number of total restarts and current respawn count for a
|
||||
service in `initctl status foo`
|
||||
* Crashing services no longer have the crash/restart counter reset as
|
||||
soon as they have stabilized. Instead, a background timer will
|
||||
slowly (every 300 sec) age (decrement) the counter. This will still
|
||||
catch services that "rage quit", but also those that crash after a
|
||||
longer period of activity
|
||||
|
||||
### Fixes
|
||||
* Fix #180: user managed (`manual:yes`) services accidentally started
|
||||
by `initctl reload`, regression introduced in Finit v4.0
|
||||
* Fix #181: lots of typos all over the tree, by David Yang, Debian
|
||||
* Fix #187: fix typos, incl. small cleanup, in doc/bootstrap.md
|
||||
* Fix #188: support running on kernels that do not have cgroups v2.
|
||||
When this is detect, all functions related to cgroups support in
|
||||
Finit are disabled, *except* the .conf file parser. Hence, you
|
||||
may get parse error if you have invalid cgroup configuration in
|
||||
your Finit .conf files
|
||||
* Fix #197: `initctl status foo` now shows a focused overview of all
|
||||
matching instances; foo:1, foo:2 -- if only one instance matches the
|
||||
command line argument, or if onle one instance exists, the detailed
|
||||
view is shown, as before
|
||||
* Fix #198: a few typos found by Tim Gates
|
||||
* Fix #199: avoid using C++ reserved keywords
|
||||
* Fix #201: memory leak in usr condition plugin, by Ming Liu, Atlas Copco
|
||||
* Fix #203: ensure all filesystems listed in /proc/mounts are properly
|
||||
unumounted on shutdown/reboot, by Robert Andersson, Atlas Copco
|
||||
* Fix #210: resizing terminal (smaller) after boot causes empty lines
|
||||
to be inserted between boot progress
|
||||
* Fix #211: drop hard-coded 32 character limit in getty, now reads
|
||||
`_SC_LOGIN_NAME_MAX` from `sysconf(3)`
|
||||
* Fix #212: service PID file lost after inictl reload, visible from
|
||||
the output from `initctl status foo`
|
||||
|
||||
|
||||
[4.1][] - 2021-06-06
|
||||
--------------------
|
||||
|
||||
Bug fix release. Also disables handlers for `SIGINT` and `SIGPWR`, a
|
||||
new set of `sys` conditions are instead generated which can be used to
|
||||
trigger external programs.
|
||||
|
||||
### Changes
|
||||
* Change behavior on SIGUSR1 to be compatible with sysvinit and systemd.
|
||||
@@ -16,17 +592,90 @@ Bug fix release.
|
||||
sysvinit (upgrading/reinstalling causes scripts to `kill -USR1 1`)
|
||||
* Change how `contrib/debian/install.sh` sets up a Grub boot entry for
|
||||
finit. We now modify the $SUPPORTED_INITS variable in `10_linux`
|
||||
* Disable default kernel ctrl-alt-delete handler and let Finit instead
|
||||
catch `SIGINT` from kernel to be able to perform a proper reboot.
|
||||
There is no default command for this, you need to set up a task that
|
||||
triggers on `<sys/key/ctrlaltdel>` to issue `initctl reboot`
|
||||
* Added keventd to provide `<sys/pwr/ac>` condition to Finit. keventd
|
||||
is currently only responsible for monitoring `/sys/class/power_supply`
|
||||
for changes to active AC mains power online status. Enable keventd
|
||||
with `configure --with-keventd`
|
||||
* For handling power fail events (from UPS and similar) a process may
|
||||
send `SIGPWR` to PID 1. Finit no longer redirects this to `SIGUSR1`
|
||||
(poweroff). There is no default command for this, you need to set up
|
||||
a task that triggers on `<sys/pwr/fail>` to issue `initctl poweroff`
|
||||
* Built-in Finit getty is now a standalone program
|
||||
* Default termios for TTYs now enable `IUTF8` on input
|
||||
* If `/bin/login` is not found, Finit now tries `sulogin` before it
|
||||
falls back to an unauthenticated `/bin/sh`
|
||||
* Dropped (broken) support for multiple consoles. Finit now follows
|
||||
the default console selected by the kernel, `/dev/console`
|
||||
* Dropped signal handlers for SIGSTOP/TSTP and SIGCONT
|
||||
* Added support for `\n`, in addition to `\r`, in "Please press Enter"
|
||||
prompt before starting getty
|
||||
* Finit no longer parses `/proc/cmdline` for its options. Instead all
|
||||
options are by default now read from `argv[]`, like a normal program,
|
||||
this is also what the kernel does by default. Please note, this may
|
||||
not work if your systems boots with an initramfs (ymmv), for such
|
||||
cases, see `configure --enable-kernel-cmdline`
|
||||
* The following plugins are now possible to disable (for containers):
|
||||
`rtc.so`, `urandom.so`, you may also want to disable `hotplug.so`.
|
||||
They are all enabled by default, as in Finit 4.0, but may be moved
|
||||
to external tools or entries in `finit.conf` in Finit 5.0
|
||||
* Added support for reading `PRETTY_NAME` from `/etc/os-release` to use
|
||||
as heading in progress output, unless `--with-heading=GREET` is used.
|
||||
* Added manual pages for finit(8), initctl(8), and finit.conf(5)
|
||||
|
||||
### Fixes
|
||||
* Stricter interface name validation in netlink plugin, modeled after
|
||||
the kernel. Suggested by Coverity Scan
|
||||
* Fix problem of re-registering a service as a task. Previously, if a
|
||||
fundamental change, like type, was made to an active service/run/task
|
||||
it did not take. Only possible workaround was to remove from config
|
||||
* initctl: drop warning when removing a non-existing usr condition
|
||||
* initctl: drop confusing `errno 0` when timing out waiting for reply
|
||||
* Ensure services in plugins and from Finit main belong to a cgroup
|
||||
* Ensure init top-level cgroup remains a leaf group
|
||||
* Fix tty parse error for detecting use of external getty
|
||||
* Fix default `name:` and `:ID` for tty's, e.g. `ttyS0` now gives
|
||||
`tty:S0` as expected. This was default for built-in getty already
|
||||
* Fix max username (32 chars) in bundled Finit getty
|
||||
* The `contrib/*/install.sh` scripts failed to run from tarball
|
||||
* Finit no longer forcibly mounts; `/dev`, `/proc`, or `/sys`, instead
|
||||
it checks first if they are already mounted (devtmpfs or container)
|
||||
* Fix `/etc/fstab` parser to properly check for 'ro' to not remount the
|
||||
root filesystem at boot. The wrong field was read, so a root mounted
|
||||
by an initramfs, or by lxc for a container, had their root remounted
|
||||
* Fix SIGCHLD handler, `waitpd()` may be interrupted by a signal
|
||||
* Reset `starting` flag of services being stopped. When a service
|
||||
is started and then stopped before it has created its pid file,
|
||||
it could be left forever in the stopping state, unless we reset
|
||||
the starting flag.
|
||||
* Fix #170: detect loss of default route when interfaces go down. This
|
||||
emulates the missing kernel netlink message to remove the condition
|
||||
net/default/route to allow stopping dependent services
|
||||
* Fix #171: restore automatic mount of `/dev/shm`, `/dev/pts`, `/run`
|
||||
and `/tmp`, unless mounted already by `/etc/fstab`. This is what most
|
||||
desktop systems expect PID 1 to do
|
||||
desktop systems expect PID 1 to do. Here we also make sure to mount
|
||||
`/run/lock` as a tmpfs as well, with write perms for regular users,
|
||||
this prevents regular users from filling up `/run` and causing DoS.
|
||||
* Fix #173: netlink plugin runs out of socket buffer space;
|
||||
|
||||
finit[1]: nl_callback():recv(): No buffer space available
|
||||
|
||||
Fixed by adding support for resync with kernel on `ENOBUFS`. See
|
||||
netlink(7) for details. As a spin-off the plugin now supports any
|
||||
number of interfaces and routes on a system. On resync, the following
|
||||
message is now logged, as a warning:
|
||||
|
||||
finit[1]: nl_callback():busy system, resynchronizing with kernel.
|
||||
|
||||
* Fix #174: loss of log messages using combo of prio and facility, e.g.,
|
||||
`logit(LOG_CONSOLE | LOG_NOTICE, ...)`, by Jacques de Laval, Westermo
|
||||
* Fix #175: ensure Finit does not acquire a controlling TTY when checking
|
||||
if a device is a TTY before starting a getty. This fixes an old bug
|
||||
where Ctrl-C after logout from a shell could cause PID 1 to get SIGINT,
|
||||
which in turn could lead to a system reboot
|
||||
|
||||
|
||||
[4.0][] - 2021-04-26
|
||||
@@ -109,7 +758,7 @@ make sure to read the whole change log when upgrading.
|
||||
- As long as a stepped service changes state we queue another step all
|
||||
event, because services may depend on each other
|
||||
* Require new libuEv API: `uev_init1()` to reduce event cache so that
|
||||
the kernel can invalidate deleted events before enqueing to userspace
|
||||
the kernel can invalidate deleted events before enqueuing to userspace
|
||||
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
|
||||
from the `hwclock` tool. Note: the kernel can also be set to load
|
||||
and store RTC to/from system clock at boot/halt as well, issue #110
|
||||
@@ -431,7 +1080,7 @@ Bug fix release.
|
||||
|
||||
* Add support for status/show service by `name:id`
|
||||
* Enforce terse mode after boot, if verbose mode is disabled
|
||||
* Reenable verbose mode at reboot, if disabled at boot
|
||||
* Re-enable verbose mode at reboot, if disabled at boot
|
||||
* Update section mentioning BusyBox getty
|
||||
* Update debugging documentation
|
||||
* Allow debug to override terse mode
|
||||
@@ -486,7 +1135,7 @@ handling and reconfiguration at runtime.
|
||||
* Clarify how to select different plugins with the configure script
|
||||
* Improve urandom plugin for embedded systems w/o random seed
|
||||
* Add `--debug` flag to `initctl`
|
||||
* The runlevels listed for services in `initctl show` now hightlight the
|
||||
* The runlevels listed for services in `initctl show` now highlight the
|
||||
active runlevel.
|
||||
* Clarify in the README and in `initctl help` that the GW event to
|
||||
listen for in service declarations is `GW:UP`
|
||||
@@ -518,7 +1167,7 @@ handling and reconfiguration at runtime.
|
||||
* Do not use `-Os` use `-O2` as default optimization level. Many cross
|
||||
compiler toolchains are known to have problems with `-Os`
|
||||
* Do not allow build VERSION to be overloaded by an environment variable
|
||||
* Fix too small MAX arguments and too few argments in `svc_t` for
|
||||
* Fix too small MAX arguments and too few arguments in `svc_t` for
|
||||
reading currently running services with `initctl show`
|
||||
* Unblock blocked signals after forking off a child
|
||||
|
||||
@@ -618,7 +1267,7 @@ The inetd release.
|
||||
### Changes
|
||||
* Add support for built-in inetd super server -- launch services on
|
||||
demand. Supports filtering per interface and custom Inet ports.
|
||||
* Upgrade to [libuEv] v1.1.0 to better handle error conditions.
|
||||
* Upgrade to [libuEv][] v1.1.0 to better handle error conditions.
|
||||
* Allow mixed case config directives in `finit.conf`
|
||||
* Add support for RFC 868 (rdate) time plugin, start as inetd service.
|
||||
* Load plugins before parsing `finit.conf`, this makes it possible to
|
||||
@@ -636,7 +1285,7 @@ The inetd release.
|
||||
turned out to not work so well after all. For instance, launching
|
||||
TTYs in a background process completely blocked inetd services from
|
||||
even starting up listening sockets ... proper fork seems to work fine
|
||||
though. This is the casue for *yanking* the [1.11] release, below.
|
||||
though. This is the cause for *yanking* the [1.11] release, below.
|
||||
* Trap segfaults caused by external plugins/callbacks in a sub-process.
|
||||
This prevents a single programming mistake in by a 3rd party developer
|
||||
from taking down the entire system.
|
||||
@@ -649,7 +1298,7 @@ The inetd release.
|
||||
[1.11][] - 2015-01-24 [YANKED]
|
||||
------------------------------
|
||||
|
||||
The [libuEv] release.
|
||||
The [libuEv][] release.
|
||||
|
||||
**Note:** This release has been *yanked* from distribution due to a
|
||||
regression in launching background processes and TTY's. Fixed in
|
||||
@@ -732,7 +1381,7 @@ Major bug fix release.
|
||||
|
||||
### Changes
|
||||
* Show `__FILE__` in `_d()` debug messages, useful for plugins with
|
||||
similarily named callbacks. Also, only in debug mode anyway
|
||||
similarly named callbacks. Also, only in debug mode anyway
|
||||
* Make sure to cleanup recorded PID when a service is lost. Required by
|
||||
service plugins for their callbacks to work.
|
||||
* Only clear screen when in verbose mode. Maybe this should be removed
|
||||
@@ -900,7 +1549,12 @@ Major bug fix release.
|
||||
|
||||
* Initial release
|
||||
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.0...HEAD
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.5...HEAD
|
||||
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
|
||||
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
|
||||
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
|
||||
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
|
||||
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
|
||||
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
|
||||
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
|
||||
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
SUBDIRS = 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
|
||||
|
||||
@@ -29,3 +29,7 @@ release: distcheck
|
||||
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
|
||||
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
|
||||
done
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-testserv-plugin --enable-x11-common-plugin \
|
||||
--with-watchdog --with-keventd --with-fstab=/etc/fstab
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
|
||||
[![License Badge][]][License] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan]
|
||||
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -22,22 +22,26 @@
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
|
||||
was reverse engineered from the [EeePC fastinit][] over ten years ago
|
||||
by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
|
||||
Fast init for Linux systems. Reverse engineered from the [EeePC
|
||||
fastinit][] over ten years ago by [Claudio Matsuoka][] — "gaps filled
|
||||
with frog DNA …"
|
||||
|
||||

|
||||
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
|
||||
|
||||
Features include:
|
||||
|
||||
* Runlevels
|
||||
* One-shot tasks, daemons, or SysV init scripts
|
||||
* Runparts and rc.local
|
||||
* Process supervision
|
||||
* Runlevels, defined per service
|
||||
* One-shot tasks, services (daemons), or [SysV init][4] start/stop scripts
|
||||
* Runparts and `/etc/rc.local` support
|
||||
* Process supervision similar to [systemd][]
|
||||
* Sourcing environment files
|
||||
* Conditions for network/process/custom dependencies
|
||||
* 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,14 +49,24 @@ 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 amd64 (x86_64) systems.
|
||||
> Custom setups, for embedded systems, can be found in [myLinux][].
|
||||
|
||||
|
||||
Example
|
||||
@@ -61,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
|
||||
@@ -97,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
|
||||
|
||||
@@ -119,10 +149,10 @@ some of the most common components needed to start a UNIX daemon:
|
||||
|
||||
```
|
||||
service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon
|
||||
^ ^ ^ ^ ^ ^ ^ ^
|
||||
| | | | | | | `-- Optional description
|
||||
| | | | | | `----------- Daemon arguments
|
||||
| | | | | `-------------------------- Path to daemon
|
||||
^ ^ ^ ^ ^ ^ ^ ^
|
||||
| | | | | | | `---------- Optional description
|
||||
| | | | | | `------------------ Daemon arguments
|
||||
| | | | | `------------------------- Path to daemon
|
||||
| | | | `---------------------------------------------------- Optional env. file
|
||||
| | | `-------------------------------------------------------- Redirect output to log
|
||||
| | `--------------------------------------------------------------- Optional conditions
|
||||
@@ -134,13 +164,13 @@ Some components are optional: runlevel(s), condition(s) and description,
|
||||
making it easy to create simple start scripts and still possible for more
|
||||
advanced uses as well:
|
||||
|
||||
service sshd -D
|
||||
service /usr/sbin/sshd -D
|
||||
|
||||
Dependencies are handled using [conditions](doc/conditions.md). One of
|
||||
the most common conditions is to wait for basic networking to become
|
||||
available:
|
||||
|
||||
service <net/route/default> nginx -- High performace HTTP server
|
||||
service <net/route/default> nginx -- High performance HTTP server
|
||||
|
||||
Here is another example where we instruct Finit to not start BusyBox
|
||||
`ntpd` until `syslogd` has started properly. Finit waits for `syslogd`
|
||||
@@ -215,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
|
||||
@@ -263,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
|
||||
@@ -300,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
|
||||
------------------------
|
||||
@@ -324,6 +395,33 @@ in [doc/plugins.md](doc/plugins.md#hooks), and about the system bootstrap
|
||||
in [doc/bootstrap.md](doc/bootstrap.md).
|
||||
|
||||
|
||||
### Limitations
|
||||
|
||||
It is not possible to call Finit via signals or use `initctl` in any
|
||||
runparts or `/etc/rc.local` script. This because Finit is single
|
||||
threaded and is calling these scripts in a blocking fashion at the end
|
||||
of runlevel S, at which point the event loop has not yet been started.
|
||||
|
||||
The event loop is the whole thing which Finit is built around, except
|
||||
for runlevel S, which remains a slow procession through a lot of set up,
|
||||
with a few hooks and blocking call outs to external scripts.
|
||||
|
||||
However, not all `initctl` commands are prohibited, supported commands:
|
||||
are:
|
||||
|
||||
- `inictl cond`: only operate of files in `/run/finit/cond`
|
||||
- `initctl enable/disable`: enabled run/task/service is activated on
|
||||
the runlevel change from S to 2
|
||||
- `initctl touch/show/create/delete/list`: `create`, provided the
|
||||
non-interactive mode is used, again changes take effect in the
|
||||
runlevel change directly after bootstrap
|
||||
- `initctl -f reboot/poweroff/halt`: provided the `-f` flag is used to
|
||||
force direct kernel commands
|
||||
|
||||
**Example:** you can set a `usr/` condition in `/etc/rc.local` and have
|
||||
a service/task in runlevel 2 depend on it to execute.
|
||||
|
||||
|
||||
Runlevels
|
||||
---------
|
||||
|
||||
@@ -409,24 +507,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
|
||||
@@ -438,16 +537,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
|
||||
|
||||
@@ -455,14 +559,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
|
||||
|
||||
Bug report address: https://github.com/troglobit/finit/issues
|
||||
Project homepage: https://troglobit.com/finit.html
|
||||
utmp show Raw dump of UTMP/WTMP db
|
||||
```
|
||||
|
||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||
@@ -481,24 +586,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,
|
||||
@@ -523,7 +628,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
|
||||
@@ -574,36 +679,41 @@ This project is based on the [original finit][] by [Claudio Matsuoka][]
|
||||
which was reverse engineered from syscalls of the [EeePC fastinit][] —
|
||||
"gaps filled with frog DNA …"
|
||||
|
||||
Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][].
|
||||
Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][6].
|
||||
Please file bug reports, clone it, or send pull requests for bug fixes
|
||||
and proposed extensions.
|
||||
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Process_supervision
|
||||
[2]: http://cr.yp.to/daemontools.html
|
||||
[3]: http://smarden.org/runit/
|
||||
[4]: http://en.wikipedia.org/wiki/Init
|
||||
[5]: http://en.wikipedia.org/wiki/Runlevel
|
||||
[6]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||
[9]: https://github.com/troglobit/troglos
|
||||
[1]: https://en.wikipedia.org/wiki/Process_supervision
|
||||
[2]: https://cr.yp.to/daemontools.html
|
||||
[3]: https://smarden.org/runit/
|
||||
[4]: https://en.wikipedia.org/wiki/Init
|
||||
[5]: https://en.wikipedia.org/wiki/Runlevel
|
||||
[6]: https://github.com/troglobit/finit
|
||||
[RFC862]: https://tools.ietf.org/html/rfc862
|
||||
[RFC863]: https://tools.ietf.org/html/rfc863
|
||||
[RFC864]: https://tools.ietf.org/html/rfc864
|
||||
[RFC867]: https://tools.ietf.org/html/rfc867
|
||||
[RFC868]: https://tools.ietf.org/html/rfc868
|
||||
[init]: http://en.wikipedia.org/wiki/Init
|
||||
[upstart]: http://upstart.ubuntu.com/
|
||||
[systemd]: http://www.freedesktop.org/wiki/Software/systemd/
|
||||
[openrc]: http://www.gentoo.org/proj/en/base/openrc/
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
[init]: https://en.wikipedia.org/wiki/Init
|
||||
[upstart]: https://upstart.ubuntu.com/
|
||||
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||
[openrc]: https://www.gentoo.org/proj/en/base/openrc/
|
||||
[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]: http://troglobit.com
|
||||
[GitHub]: https://github.com/troglobit/finit
|
||||
[Joachim Wiberg]: https://troglobit.com
|
||||
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
|
||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||
[Travis]: https://travis-ci.org/troglobit/finit
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
|
||||
[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
|
||||
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
|
||||
|
||||
@@ -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
|
||||
+146
-19
@@ -1,20 +1,23 @@
|
||||
AC_INIT(Finit, 4.1-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_INIT([Finit],[4.5-rc1],[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
|
||||
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile
|
||||
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
|
||||
doc/Makefile
|
||||
man/Makefile
|
||||
plugins/Makefile
|
||||
src/Makefile
|
||||
test/test.env
|
||||
test/Makefile])
|
||||
system/Makefile
|
||||
tmpfiles.d/Makefile
|
||||
test/test.env test/Makefile
|
||||
test/lib/Makefile test/src/Makefile])
|
||||
|
||||
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
||||
#m4_include([m4/plugin.m4])
|
||||
@@ -31,9 +34,8 @@ AC_PROG_INSTALL
|
||||
AC_PROG_MKDIR_P
|
||||
|
||||
# Configuration.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h])
|
||||
AC_CHECK_FUNCS([strstr getopt getfsenty])
|
||||
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h])
|
||||
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r])
|
||||
|
||||
# Check for uint[8,16,32]_t
|
||||
AC_TYPE_UINT8_T
|
||||
@@ -52,12 +54,24 @@ AC_ARG_ENABLE(auto_reload,
|
||||
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
|
||||
enable_auto_reload=no])
|
||||
|
||||
AC_ARG_ENABLE(kernel_cmdline,
|
||||
AS_HELP_STRING([--enable-kernel-cmdline], [Parse init args from /proc/cmdline (don't use!)]),,[
|
||||
enable_kernel_cmdline=no])
|
||||
|
||||
AC_ARG_ENABLE(fastboot,
|
||||
AS_HELP_STRING([--enable-fastboot], [Skip fsck check on filesystems listed in /etc/fstab]),,[
|
||||
enable_fastboot=no])
|
||||
|
||||
AC_ARG_ENABLE(fsckfix,
|
||||
AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[
|
||||
enable_fsckfix=no])
|
||||
|
||||
AC_ARG_ENABLE(redirect,
|
||||
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
|
||||
enable_redirect=yes])
|
||||
|
||||
AC_ARG_ENABLE(logrotate,
|
||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp]),,[
|
||||
enable_logrotate=yes])
|
||||
|
||||
AC_ARG_ENABLE(doc,
|
||||
@@ -68,19 +82,35 @@ 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([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([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(fstab,
|
||||
AS_HELP_STRING([--with-fstab=FILE], [System default fstab, default $sysconfdir/fstab]),
|
||||
[fstab=$withval], [fstab=yes])
|
||||
|
||||
AC_ARG_WITH(heading,
|
||||
AS_HELP_STRING([--with-heading=GREET], [Display GREET instead of Finit version at boot]),
|
||||
AS_HELP_STRING([--with-heading=GREET], [Heading for boot progress. Default: PRETTY_NAME from /etc/os-release, fallback to "Finit vX.YY"]),
|
||||
[heading=$withval], [heading=yes])
|
||||
|
||||
AC_ARG_WITH(config,
|
||||
@@ -99,28 +129,59 @@ AC_ARG_WITH(sysconfig,
|
||||
AS_HELP_STRING([--with-sysconfig=DIR], [System environment directory, sourced by .conf files, default /etc/default & /etc/conf.d]),
|
||||
[sysconfig=$withval], [sysconfig=default])
|
||||
|
||||
AC_ARG_WITH(group,
|
||||
AS_HELP_STRING([--with-group=NAME], [Group for /run/finit/socket (initctl), default: root]),
|
||||
[group=$withval], [group=root])
|
||||
|
||||
AC_ARG_WITH(hostname,
|
||||
AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]),
|
||||
[hostname=$withval], [hostname=yes])
|
||||
|
||||
AC_ARG_WITH(runlevel,
|
||||
AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap, default: 2]),
|
||||
AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap [1-9], default: 2]),
|
||||
[runlevel=$withval], [runlevel=yes])
|
||||
|
||||
AC_ARG_WITH(random-seed,
|
||||
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
|
||||
[random_seed=$withval], [random_seed=yes])
|
||||
|
||||
AC_ARG_WITH(keventd,
|
||||
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
|
||||
|
||||
AC_ARG_WITH(sulogin,
|
||||
AS_HELP_STRING([--with-sulogin@<:@=USER@:>@], [Enable built-in sulogin, optional USER to request password for (default root), default: no.]),[sulogin=$withval],[with_sulogin=no])
|
||||
|
||||
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 ###########################################################################
|
||||
|
||||
# Create config.h from selected features and fallback defaults
|
||||
AS_IF([test "x$enable_auto_reload" = "xyes"], [
|
||||
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
|
||||
|
||||
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [
|
||||
AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])])
|
||||
|
||||
AS_IF([test "x$enable_fastboot" = "xyes"], [
|
||||
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
|
||||
|
||||
AS_IF([test "x$enable_fsckfix" = "xyes"], [
|
||||
AC_DEFINE(FSCK_FIX, 1, [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab])])
|
||||
|
||||
AS_IF([test "x$enable_redirect" = "xyes"], [
|
||||
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
||||
|
||||
@@ -128,9 +189,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$with_fstab" != "xno"], [
|
||||
AS_IF([test "x$fstab" = "xyes"], [fstab=$sysconfdir/fstab])
|
||||
AC_EXPAND_DIR(fstab_path, "$fstab")
|
||||
AC_DEFINE_UNQUOTED(FINIT_FSTAB, "$fstab_path", [Built-in default: /etc/fstab])],[
|
||||
AC_DEFINE_UNQUOTED(FINIT_FSTAB, NULL, [No built-in default fstab])])
|
||||
|
||||
AS_IF([test "x$with_config" != "xno"], [
|
||||
AS_IF([test "x$conf" = "xyes"], [
|
||||
conf=$sysconfdir/finit.conf])])
|
||||
@@ -158,9 +228,13 @@ AS_IF([test "x$with_sysconfig" != "xno"], [
|
||||
[System environment, default /etc/default & /etc/conf.d])])])
|
||||
|
||||
AS_IF([test "x$with_heading" != "xno"], [
|
||||
AS_IF([test "x$heading" = "xyes"], [
|
||||
heading="$PACKAGE_NAME v$PACKAGE_VERSION"])])
|
||||
AC_DEFINE_UNQUOTED(INIT_HEADING, "$heading", [Displayed by Finit at boot])
|
||||
AS_IF([test "x$heading" = "xyes"], [heading=""])
|
||||
AC_DEFINE_UNQUOTED(INIT_OSHEADING, "$heading", [Displayed by Finit at boot, defaults to /etc/os-release PRETTY_NAME])])
|
||||
|
||||
AS_IF([test "x$with_group" != "xno"], [
|
||||
AS_IF([test "x$group" = "xyes"], [
|
||||
group=root])])
|
||||
AC_DEFINE_UNQUOTED(DEFGROUP, "$group", [For /run/finit/socket])
|
||||
|
||||
AS_IF([test "x$with_hostname" != "xno"], [
|
||||
AS_IF([test "x$hostname" = "xyes"], [
|
||||
@@ -179,6 +253,19 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
||||
AC_EXPAND_DIR(random_path, "$random_seed")
|
||||
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
|
||||
|
||||
AS_IF([test "x$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"], [
|
||||
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"], [
|
||||
watchdog=/dev/watchdog])
|
||||
@@ -186,27 +273,59 @@ 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" != "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(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(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"])
|
||||
|
||||
# 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(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])
|
||||
|
||||
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
|
||||
|
||||
@@ -218,6 +337,7 @@ cat <<EOF
|
||||
Exec prefix...........: $eprefix
|
||||
Sysconfdir............: `eval echo $sysconfdir`
|
||||
Localstatedir.........: `eval echo $localstatedir`
|
||||
Default fstab.........: `eval echo $fstab`
|
||||
Finit config file.....: $conf_path
|
||||
Finit config.d path...: $rcsd_path
|
||||
Finit plugin path.....: $plugin_path
|
||||
@@ -230,14 +350,21 @@ cat <<EOF
|
||||
Behavior:
|
||||
Boot heading..........: $heading
|
||||
Plugins...............: $plugins
|
||||
RTC restore date......: $RTC_DATE
|
||||
|
||||
Optional features:
|
||||
Install doc/..........: $enable_doc
|
||||
Install contrib/......: $enable_contrib
|
||||
Built-in keventd......: $with_keventd
|
||||
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
|
||||
|
||||
------------- Compiler version --------------
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/alpine
|
||||
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
|
||||
docs_DATA = README.md finit.conf build.sh install.sh rc.local
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
3.6.2
|
||||
@@ -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-heading="Alpine Linux 3.13"
|
||||
--with-keventd --with-sulogin
|
||||
|
||||
echo
|
||||
echo "*** Building ..."
|
||||
@@ -35,4 +35,3 @@ fi
|
||||
echo
|
||||
echo "*** Done"
|
||||
echo
|
||||
|
||||
|
||||
@@ -5,6 +5,22 @@
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
#cgroup init cpu.weight:100
|
||||
#cgroup user cpu.weight:100
|
||||
#cgroup system cpu.weight:9700
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ if [ "x`id -u`" != "x0" ]; then
|
||||
fi
|
||||
|
||||
# Adjust base directory
|
||||
if [ -e alpine-release ]; then
|
||||
grep -q alpine Makefile 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
cd ../..
|
||||
elif [ ! -e configure.ac ]; then
|
||||
echo "*** Please run this script from the Finit base directory."
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
#
|
||||
# Each line is run in sequence.
|
||||
#
|
||||
# NOTE: Remeber to set the executable bit!
|
||||
# NOTE: Remember to set the executable bit!
|
||||
#
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/debian
|
||||
docs_DATA = README.md finit.conf os-release build.sh install.sh
|
||||
docs_DATA = README.md finit.conf build.sh install.sh
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -47,12 +47,13 @@ comands to `initctl`, followed by `reload` to activate your changes.
|
||||
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
|
||||
and initialization like keyboard language etc.
|
||||
|
||||
If you want to use the X Window system, `sudo apt install elogind`
|
||||
(Bullseye and later). Followed by `initctl reload` (enabled by default)
|
||||
and logout/login again. The elogind daemon ensures a regular non-root
|
||||
user can start and interact with an X session, otherwise keyboard and
|
||||
mouse won't work. When you're happy, you can enable the lightdm.conf,
|
||||
change the default runlevel to 3, and presto! you have a desktop again.
|
||||
> **NOTE:** X Window system, you may need to `sudo apt install elogind`
|
||||
> (Bullseye and later), followed by `initctl reload` to activate it (it
|
||||
> is enabled by default), and logout/login again. The elogind daemon
|
||||
> ensures a regular non-root user can start and interact with an X
|
||||
> session, otherwise keyboard and mouse won't work. When you're happy,
|
||||
> you can enable the lightdm.conf, change the default runlevel to 3, and
|
||||
> presto! you have a desktop again.
|
||||
|
||||
|
||||
Have fun!
|
||||
|
||||
@@ -25,9 +25,8 @@ echo
|
||||
--prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin \
|
||||
--with-random-seed=/var/lib/urandom/random-seed \
|
||||
--with-heading="Debian GNU/Linux"
|
||||
--enable-alsa-utils-plugin --with-keventd \
|
||||
--with-random-seed=/var/lib/urandom/random-seed
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo
|
||||
@@ -60,6 +59,3 @@ else
|
||||
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
|
||||
echo
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,22 @@
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
#cgroup init cpu.weight:100
|
||||
#cgroup user cpu.weight:100
|
||||
#cgroup system cpu.weight:9700
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
SUBDIRS = available
|
||||
docsdir := @docdir@/contrib/debian/finit.d
|
||||
d_LINKS = console-setup.conf elogind.conf getty.conf \
|
||||
keyboard-setup.conf modules.conf rsyslogd.conf \
|
||||
sshd.conf
|
||||
d_LINKS = acpid.conf apparmor.conf avahi-daemon.conf \
|
||||
bluetooth.conf console-setup.conf elogind.conf \
|
||||
getty.conf keyboard-setup.conf modules.conf \
|
||||
plymouth-quit.conf rsyslogd.conf sshd.conf
|
||||
|
||||
install-exec-hook:
|
||||
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
docsdir := @docdir@/contrib/debian/finit.d/available
|
||||
docs_DATA = acpid.conf console-setup.conf elogind.conf getty.conf \
|
||||
docs_DATA = acpid.conf apparmor.conf avahi-daemon.conf bluetooth.conf \
|
||||
console-setup.conf elogind.conf getty.conf \
|
||||
keyboard-setup.conf lightdm.conf modules.conf sntpd.conf \
|
||||
rsyslogd.conf sshd.conf syslogd.conf xdm.conf
|
||||
plymouth-quit.conf rsyslogd.conf sshd.conf syslogd.conf \
|
||||
xdm.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
|
||||
@@ -0,0 +1,2 @@
|
||||
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
|
||||
@@ -0,0 +1,3 @@
|
||||
# To ignore errors like "org.freedesktop.hostname1", update the env file with
|
||||
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
|
||||
service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
|
||||
@@ -0,0 +1 @@
|
||||
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
|
||||
@@ -1,4 +1,7 @@
|
||||
# Consoles to start getty on when system is up
|
||||
tty [12345] /dev/tty1 linux noclear nowait
|
||||
tty [2345] /dev/tty2 linux noclear nowait
|
||||
tty [2345] /dev/tty3 linux noclear nowait
|
||||
tty [2345] /dev/tty2 linux nowait
|
||||
tty [2345] /dev/tty3 linux nowait
|
||||
tty [2345] /dev/tty4 linux nowait
|
||||
tty [2345] /dev/tty5 linux nowait
|
||||
tty [2345] /dev/tty6 linux nowait
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Need to stop plymouth, or it'll block stdin on tty1
|
||||
task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen
|
||||
@@ -8,7 +8,7 @@ if [ "x`id -u`" != "x0" ]; then
|
||||
fi
|
||||
|
||||
# Adjust base directory
|
||||
grep -q Debian os-release 2>/dev/null
|
||||
grep -q debian Makefile 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
cd ../..
|
||||
elif [ ! -e configure.ac ]; then
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
|
||||
NAME="Debian GNU/Linux"
|
||||
VERSION_ID="8"
|
||||
VERSION="8 (jessie)"
|
||||
ID=debian
|
||||
HOME_URL="http://www.debian.org/"
|
||||
SUPPORT_URL="http://www.debian.org/support"
|
||||
BUG_REPORT_URL="https://bugs.debian.org/"
|
||||
@@ -35,6 +35,14 @@ tty /dev/tty1
|
||||
tty /dev/tty2
|
||||
tty /dev/tty3
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
# Alternative method instead of runparts
|
||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
In Finit issue #196 we were reminded [1] of a missing inotify flag in
|
||||
Linux older kernels, IN_MASK_CREATE. Finit makes use of this internally
|
||||
for monitoring PID conditions, and more. This is a central mechanism to
|
||||
synchronize services at boot and reload at runtime.
|
||||
|
||||
The following patch for Linux 4.9.0, was provided by Andy Savage for
|
||||
other people that may want to run Finit on slightly older kernels.
|
||||
|
||||
[1]: https://github.com/troglobit/finit/issues/196
|
||||
|
||||
Index: fs/notify/inotify/inotify_user.c
|
||||
===================================================================
|
||||
--- fs.orig/notify/inotify/inotify_user.c
|
||||
+++ fs/notify/inotify/inotify_user.c
|
||||
@@ -522,6 +522,7 @@ static int inotify_update_existing_watch
|
||||
__u32 old_mask, new_mask;
|
||||
__u32 mask;
|
||||
int add = (arg & IN_MASK_ADD);
|
||||
+ int create = (arg & IN_MASK_CREATE);
|
||||
int ret;
|
||||
|
||||
mask = inotify_arg_to_mask(arg);
|
||||
@@ -529,6 +530,8 @@ static int inotify_update_existing_watch
|
||||
fsn_mark = fsnotify_find_inode_mark(group, inode);
|
||||
if (!fsn_mark)
|
||||
return -ENOENT;
|
||||
+ else if (create)
|
||||
+ return -EEXIST;
|
||||
|
||||
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
|
||||
|
||||
@@ -726,6 +729,10 @@ SYSCALL_DEFINE3(inotify_add_watch, int,
|
||||
if (unlikely(!f.file))
|
||||
return -EBADF;
|
||||
|
||||
+ /* IN_MASK_ADD and IN_MASK_CREATE don't make sense together */
|
||||
+ if (unlikely((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
/* verify that this is indeed an inotify instance */
|
||||
if (unlikely(f.file->f_op != &inotify_fops)) {
|
||||
ret = -EINVAL;
|
||||
@@ -814,7 +821,7 @@ static int __init inotify_user_setup(voi
|
||||
BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
|
||||
BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
|
||||
|
||||
- BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
|
||||
+ BUG_ON(hweight32(ALL_INOTIFY_BITS) != 22);
|
||||
|
||||
inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC);
|
||||
|
||||
Index: include/linux/inotify.h
|
||||
===================================================================
|
||||
--- include.orig/linux/inotify.h
|
||||
+++ include/linux/inotify.h
|
||||
@@ -17,6 +17,6 @@ extern struct ctl_table inotify_table[];
|
||||
IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | \
|
||||
IN_Q_OVERFLOW | IN_IGNORED | IN_ONLYDIR | \
|
||||
IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_MASK_ADD | \
|
||||
- IN_ISDIR | IN_ONESHOT)
|
||||
+ IN_MASK_CREATE | IN_ISDIR | IN_ONESHOT)
|
||||
|
||||
#endif /* _LINUX_INOTIFY_H */
|
||||
Index: include/uapi/linux/inotify.h
|
||||
===================================================================
|
||||
--- include.orig/uapi/linux/inotify.h
|
||||
+++ include/uapi/linux/inotify.h
|
||||
@@ -52,6 +52,7 @@ struct inotify_event {
|
||||
#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */
|
||||
#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */
|
||||
#define IN_EXCL_UNLINK 0x04000000 /* exclude events on unlinked objects */
|
||||
+#define IN_MASK_CREATE 0x10000000 /* only create watches */
|
||||
#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */
|
||||
#define IN_ISDIR 0x40000000 /* event occurred against dir */
|
||||
#define IN_ONESHOT 0x80000000 /* only send event once */
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/void
|
||||
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
|
||||
docs_DATA = README.md finit.conf build.sh install.sh rc.local
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -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-heading="Void Linux"
|
||||
--with-keventd --with-sulogin
|
||||
|
||||
echo
|
||||
echo "*** Building ..."
|
||||
@@ -35,4 +35,3 @@ fi
|
||||
echo
|
||||
echo "*** Done"
|
||||
echo
|
||||
|
||||
|
||||
@@ -5,5 +5,22 @@
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
#cgroup init cpu.weight:100
|
||||
#cgroup user cpu.weight:100
|
||||
#cgroup system cpu.weight:9700
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
@@ -8,7 +8,8 @@ if [ "x`id -u`" != "x0" ]; then
|
||||
fi
|
||||
|
||||
# Adjust base directory
|
||||
if [ -e xbps ]; then
|
||||
grep -q void Makefile 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
cd ../..
|
||||
elif [ ! -e configure.ac ]; then
|
||||
echo "*** Please run this script from the Finit base directory."
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
#
|
||||
# Each line is run in sequence.
|
||||
#
|
||||
# NOTE: Remeber to set the executable bit!
|
||||
# NOTE: Remember to set the executable bit!
|
||||
|
||||
+24
-2
@@ -9,6 +9,16 @@ value in the project turned out to be:
|
||||
> systems, container, and dedicated server applications.
|
||||
|
||||
|
||||
Before Next Release
|
||||
-------------------
|
||||
|
||||
* When did runparts +arg break?
|
||||
* Should we update the =runparts DIR= syntax to =runparts [progress] [debug] DIR=?
|
||||
* Add SNN-only option top runparts, for Buildroot
|
||||
* Calling `initctl runlevel` at bootstrap => N 10, what should it be?
|
||||
* Reindent all shell scripts in true emacs shell-script-mode (4 chars)
|
||||
|
||||
|
||||
Misc TODOs
|
||||
----------
|
||||
|
||||
@@ -21,17 +31,29 @@ Some of these are also registered in the issue tracker on GitHub.
|
||||
* Allow running as non-pid1 => read .conf and RCSD form cmdline
|
||||
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
|
||||
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
|
||||
|
||||
As of Finit v4.1 SIGPWR generates <sys/power/fail> condition and it
|
||||
is up to a task to check why and take appropriate action.
|
||||
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
|
||||
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
|
||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||
`pimd` man pages ...
|
||||
|
||||
As of Finit v4.1 SIGINT generates <sys/key/ctrlaltdel>, but there is
|
||||
no check if the signal actually originates from the kernel. To get
|
||||
this verification, a new libuEv release is needed. As of June 6 -21
|
||||
that functionality is not yet released.
|
||||
|
||||
|
||||
Initctl
|
||||
-------
|
||||
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown:
|
||||
|
||||
shutdown -h 03:35 "foobar was here"
|
||||
|
||||
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
||||
`initctl show --json`
|
||||
* Add support for `-s` to redirect all output to syslog, for scripting
|
||||
|
||||
|
||||
Init
|
||||
|
||||
+11
-10
@@ -1,6 +1,8 @@
|
||||
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
|
||||
@@ -16,7 +18,9 @@ Bootstrap
|
||||
11. Load all `/etc/finit.d/*.conf` files and set hostname
|
||||
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
|
||||
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
|
||||
14. Mount all file systems listed in `/etc/fstab` and swap, if available
|
||||
14. Mount all file systems listed in `/etc/fstab` and swap, if available.
|
||||
On mount error `HOOK_MOUNT_ERROR` is called. After mount, regardless
|
||||
of error, `HOOK_MOUNT_POST` is called
|
||||
15. Enable SysV init signals
|
||||
16. Call 2nd level hooks, `HOOK_BASEFS_UP`
|
||||
17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin
|
||||
@@ -38,16 +42,13 @@ Bootstrap
|
||||
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||
27. Start all configured TTYs
|
||||
|
||||
In (19) and (22) tasks and services defined in `/etc/finit.conf` and
|
||||
`/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task`
|
||||
stanzas are started in parallel and `run` in sequence, and in the order
|
||||
listed. Hence, to emulate a SysV `/etc/init.d/rcS` one could write a
|
||||
long `finit.conf` with only `run` statements.
|
||||
In (19) and (23) tasks and services defined in `/etc/finit.conf` and
|
||||
`/etc/finit.d/*.conf` are started.
|
||||
|
||||
Notice the five hook points that are called at various point in the
|
||||
bootstrap process. This is where plugins can extend the boot in any way
|
||||
they please. There are other hook points, e.g. `HOOK_MOUNT_ERROR`, for
|
||||
more on this see [plugins.md](plugins.md).
|
||||
Notice the seven hook points that are called at various point in the
|
||||
bootstrap process. This is where plugins can extend the boot in any
|
||||
way they please. There are other hook points available, for more on
|
||||
this, see [plugins.md](plugins.md).
|
||||
|
||||
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
|
||||
USB stick, convert/copy its contents to the system's `/etc/` directory,
|
||||
|
||||
+33
-13
@@ -27,10 +27,10 @@ optional plugins to enable. It depends on two external libraries:
|
||||
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
|
||||
|
||||
The configure script checks for all dependencies, including the correct
|
||||
version of the above mentioned libraries. Currently requried versions:
|
||||
version of the above mentioned libraries. Currently required versions:
|
||||
|
||||
- libite v2.2.0
|
||||
- libuEv v2.2.0
|
||||
- libite v2.2.0, or later
|
||||
- libuEv v2.2.0, or later
|
||||
|
||||
|
||||
Configure
|
||||
@@ -53,6 +53,10 @@ Below are a few of the main switches to configure:
|
||||
built-ins (.o files) and all external libraries, except the C library
|
||||
will be linked statically.
|
||||
|
||||
* `--enable-kernel-cmdline`: Enable Finit pre-4.1 parsing of init args from
|
||||
`/proc/cmdline`, this is *not recommended* since Finit may be running as the
|
||||
init for container apps that can see the host's `/proc` filesystem
|
||||
|
||||
* `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin.
|
||||
|
||||
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
|
||||
@@ -61,8 +65,19 @@ Below are a few of the main switches to configure:
|
||||
|
||||
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
|
||||
|
||||
* `--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.
|
||||
|
||||
For more configure flags, see <kbd>./configure --help</kbd>
|
||||
|
||||
> **Note:** the configure script is not available in the GIT sources. It is
|
||||
> however included in (officially supported) released tarballs. The
|
||||
> idea is that you should not need GNU autotools to build, only the
|
||||
> above mentioned dependencies, a POSIX shell, a C compiler and make.
|
||||
> Any contributing to Finit can generate it from `configure.ac` using
|
||||
> the `autogen.sh` script.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
@@ -70,16 +85,16 @@ Example
|
||||
First, unpack the archive:
|
||||
|
||||
```shell
|
||||
$ tar xf finit-3.0.tar.xz
|
||||
$ cd finit-3.0/
|
||||
$ tar xf finit-4.3.tar.gz
|
||||
$ cd finit-4.3/
|
||||
```
|
||||
|
||||
Then configure, build and install:
|
||||
|
||||
```shell
|
||||
$ ./configure --prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--with-heading="Alpine Linux 3.13" --with-hostname=alpine
|
||||
$ ./configure --prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--with-keventd --with-watchdog
|
||||
$ make
|
||||
.
|
||||
.
|
||||
@@ -87,13 +102,13 @@ $ make
|
||||
$ DESTDIR=/tmp/finit make install
|
||||
```
|
||||
|
||||
In this example the [finit-3.0.tar.xz][1] archive is unpacked to the
|
||||
In this example the [finit-4.3.tar.gz][1] archive is unpacked to the
|
||||
user's home directory, configured, built and installed to a temporary
|
||||
staging directory. The environment variable `DESTDIR` controls the
|
||||
destination directory when installing, very useful for building binary
|
||||
standalone packages.
|
||||
|
||||
Finit 3.0 and later can detect if it runs on an embedded system, or a
|
||||
Finit 4.1 and later can detect if it runs on an embedded system, or a
|
||||
system that use BusyBox tools instead of udev & C:o. On such systems
|
||||
`mdev` instead of `udev` is used. However, remember to also change the
|
||||
Linux config to:
|
||||
@@ -142,17 +157,22 @@ enabled. The default Finit rescue mode configuration is installed into
|
||||
|
||||
By default the a root shell, without login, is started.
|
||||
|
||||
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to
|
||||
> force `reboot`, `shutdown`, or `poweroff`.
|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Edit, or append to, the kernel command line: remove `quiet` to enable
|
||||
kernel messages and add `debug` to enable Finit debug messages.
|
||||
kernel messages and add `finit.debug` to enable Finit debug messages.
|
||||
|
||||
```shell
|
||||
append="init=/sbin/finit debug"
|
||||
append="init=/sbin/finit -- finit.debug"
|
||||
```
|
||||
|
||||
Notice the `--` separator.
|
||||
|
||||
To debug startup issues, in particular issues with getty/login, add
|
||||
the following to your Finit .conf file:
|
||||
|
||||
@@ -171,6 +191,6 @@ it only for debugging start up issues when Finit crashes.
|
||||
systems since they can potentially give a user root access.
|
||||
|
||||
|
||||
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
|
||||
[1]: ftp://troglobit.com/finit/finit-4.3.tar.gz
|
||||
[libuEv]: https://github.com/troglobit/libuev
|
||||
[libite]: https://github.com/troglobit/libite
|
||||
|
||||
+73
-12
@@ -6,15 +6,57 @@ passed on the Linux kernel command line. Not limited to Finit.
|
||||
|
||||
The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
|
||||
> **NOTE:** remember to use `--` to separate kernel parameters from
|
||||
> parameters to init. E.g., `init=/sbin/finit -- finit.debug rescue`
|
||||
|
||||
* `debug`: Enable kernel debug. Debug messages are printed to the
|
||||
console until Finit starts up.
|
||||
console until Finit starts up, unless `loglevel=7` (below) is used.
|
||||
|
||||
* `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.
|
||||
independently of the kernel `debug` setting. New as of Finit v4.
|
||||
|
||||
* `finit.show_status[=bool]`: Control finit boot progress, including
|
||||
banner.
|
||||
* `finit.fstab=</path/to/etc/fstab.alternative>`: Tell Finit to use an
|
||||
alternate `fstab` to mount the file system from. Remember, this file
|
||||
must be on the `root=...` file system provided to Finit from the
|
||||
kernel. By default the built-in fstab is used, which itself defaults
|
||||
to `/etc/fstab`, but can be changed at build time with:
|
||||
|
||||
./configure --with-fstab=/path/to/fstab
|
||||
|
||||
It is even possible to disable a built-in default using:
|
||||
|
||||
./configure --without-fstab
|
||||
|
||||
Making `finit.fstab=/path/to/fstab` a *mandatory* command line option.
|
||||
Note, if the command line fstab is missing, Finit falls back to the
|
||||
built-in fstab, and if both are missing, the system treats this as a
|
||||
bad `fsck` and thus calls `sulogin`. If, in turn, `sulogin` is not
|
||||
available on the system, Finit calls reboot, which is also what will
|
||||
happen when a user exits from `sulogin`.
|
||||
|
||||
* `finit.status[=bool]`: Control finit boot progress, including banner.
|
||||
(Used to be `finit.show_status`, which works but is deprecated.)
|
||||
|
||||
* `finit.status_style=<classic,modern>`: Set Finit boot progress style,
|
||||
when enabled.
|
||||
@@ -24,25 +66,44 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
signals and has no job control. Recommend using, and modifying,
|
||||
`rescue` mode instead.
|
||||
|
||||
* `loglevel=<0-7>`, sets the kernel's log level, which is more granular
|
||||
than `debug`. Also, when `loglevel=7`, Finit will *not disable*
|
||||
kernel logs to the console. This is very useful when debugging the
|
||||
kernel at system bring-up. Since `loglevel=7` is the same as `debug`
|
||||
this means you have to use `quiet` for a quiet boot, until sysklogd
|
||||
takes over logging of kernel events.
|
||||
|
||||
* `panic=SEC`: By default the kernel does not reboot after a kernel
|
||||
panic. This setting will cause a kernel reboot after SEC seconds.
|
||||
|
||||
* `quiet`: Suppress kernel logging to console, except for warnings and
|
||||
errors.
|
||||
errors. Also, see `loglevel` and `quiet` above.
|
||||
|
||||
* `rescue`: Start rescue/maintenance mode. If your system comes with
|
||||
the bundled `sulogin` program (Finit, or from util-linux/Busybox),
|
||||
you will be given a root login to a maintenance shell. However, if
|
||||
`sulogin` is missing, the file `/lib/finit/rescue.conf` is read and
|
||||
the system booted in a limited fallback mode. See [config.md][]
|
||||
for more information.
|
||||
for more information.
|
||||
|
||||
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
|
||||
debug problems with the regular runlevel. All services and TTYs in
|
||||
`[1]` will be started, so a `tty [1] @console nologin` configuration
|
||||
presents you with a root console without login.
|
||||
This option can be disabled with `configure --without-rescue`
|
||||
|
||||
For more on kernel boot paramaters, see the man page [bootparam(7)][].
|
||||
**Note:** in this mode `initctl` will not work. Correct the problem
|
||||
and use `reboot -f` to force reboot.
|
||||
|
||||
[config.md][]: config.md#rescue-mode
|
||||
* `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
|
||||
Finit to ignore any `runlevel` in `/etc/finit.conf` as well as the
|
||||
configure fallback `--with-runlevel=N` setting. Remember, `6` is the
|
||||
reboot runlevel and is not permitted. Any other values are ignored.
|
||||
|
||||
For more on kernel boot parameters, see the man page [bootparam(7)][].
|
||||
|
||||
[config.md]: config.md#rescue-mode
|
||||
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
|
||||
|
||||
+53
-17
@@ -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"*
|
||||
@@ -35,7 +35,7 @@ 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
|
||||
@@ -46,11 +46,16 @@ 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,12 +92,35 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||
Built-in Conditions
|
||||
-------------------
|
||||
|
||||
Finit comes with a `pidfile` and `netlink` plugin. When enabled, the
|
||||
`pidfile` plugin watches `/var/run/` (recursively) for PID files created
|
||||
by the monitored services, and sets a corresponding condition in the
|
||||
`pid/` namespace. Similarily, the `netlink` plugin provides basic
|
||||
conditions for when an interface is brought up/down and when a default
|
||||
route (gateway) is set, in the `net/` namespace.
|
||||
Finit comes with a set of plugins for conditions:
|
||||
|
||||
- `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
|
||||
@@ -111,6 +139,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
|
||||
@@ -156,7 +192,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.
|
||||
@@ -173,7 +209,7 @@ PID IDENT STATUS CONDITION (+ ON, ~ FLUX, - OFF)
|
||||
|
||||
Here we can see that `netd` is allowed to run since both its conditions
|
||||
are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however
|
||||
is not allowed to run since `net/vlan1/exist` condition is not satsifed.
|
||||
is not allowed to run since `net/vlan1/exist` condition is not satisfied.
|
||||
As indicated by the `-`-prefix.
|
||||
|
||||
To fake interface `vlan1` suddenly appearing, and test what happens to
|
||||
|
||||
+638
-74
@@ -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,44 +28,178 @@ 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)
|
||||
* [/etc/finit.d](#etcfinitd)
|
||||
* [Watchdog](#watchdog)
|
||||
* [keventd](#keventd)
|
||||
|
||||
|
||||
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, simply drop a `.conf` file in `/etc/finit.d` and
|
||||
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
|
||||
call `finit q`, but that has been deprecated with the `initctl` tool).
|
||||
Finit monitors all known active `.conf` files, so if you want to force
|
||||
a restart of any service you can simply touch its corresponding `.conf`
|
||||
file in `/etc/finit.d` and call `initctl reload`. Finit handle any
|
||||
and all conditions and dependencies between services automatically.
|
||||
### Configuration Files
|
||||
|
||||
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.
|
||||
Originally Finit was configured using a single file, `/etc/finit.conf`.
|
||||
Although still possible to use this single configuration file, today the
|
||||
following familiar layout is recommended:
|
||||
|
||||
/etc/
|
||||
|- finit.d/
|
||||
| |- available/
|
||||
| | `- my-service.conf
|
||||
: |- enabled/
|
||||
: | `- my-service.conf -> ../available/my-service.conf
|
||||
: :
|
||||
: |- static-service.conf
|
||||
: `- another-static.conf
|
||||
:
|
||||
`- finit.conf
|
||||
|
||||
Services in the `available/` and `enabled/` sub-directories are called
|
||||
dynamic services, in contrast to static services -- the only difference
|
||||
being where they are installed and if the `initctl` tool can manage them
|
||||
with the `enable` and `disable` commands. An administrator can always
|
||||
create files and symlinks manually.
|
||||
|
||||
At bootstrap, and `initctl reload`, all .conf files are read, starting
|
||||
with `finit.conf`, then, in alphabetical order, all `finit.d/*.conf`
|
||||
files, and finally (still in alphabetical order) all `enabled/*.conf`
|
||||
files.
|
||||
|
||||
> **Note:** the `finit.conf` and `finit.d/` names are only defaults.
|
||||
> They can be changed at compile-time with two `configure` options:
|
||||
> `--with-config=/etc/foo.conf` and `--with-rcsd=/var/foo.d`.
|
||||
>
|
||||
> They can also be overridden from the [kernel command line](cmdline.md)
|
||||
> using: `-- finit.config=/etc/bar.conf` and in that file use the
|
||||
> top-level configuration directive `rcsd /path/to/finit.d`.
|
||||
|
||||
|
||||
### Filesystem Layout
|
||||
|
||||
Finit is most comfortable with a traditional style Linux filesystem
|
||||
layout, as specified in the [FHS][]:
|
||||
|
||||
/.
|
||||
|- bin/
|
||||
|- dev/ # Mounted automatically if devtmpfs is available
|
||||
| |- pts/ # Mounted automatically by Finit if it exists
|
||||
| `- shm/ # Mounted automatically by Finit if it exists
|
||||
|- etc/
|
||||
| |- finit.d/
|
||||
| | |- available/
|
||||
| | `- enabled/
|
||||
| `- finit.conf
|
||||
|- home/
|
||||
|- lib/
|
||||
|- libexec/
|
||||
|- mnt/
|
||||
|- proc/ # Mounted automatically by Finit if it exists
|
||||
|- root/
|
||||
|- run/ # Mounted automatically by Finit if it exists
|
||||
| `- lock/ # Created automatically if Finit mounts /run
|
||||
|- sbin/
|
||||
|- sys/ # Mounted automatically by Finit if it exists
|
||||
|- tmp/ # Mounted automatically by Finit if it exists
|
||||
|- usr/
|
||||
`- var/
|
||||
|- cache/
|
||||
|- db/
|
||||
|- lib/
|
||||
| `- misc/
|
||||
|- lock/
|
||||
|- log/
|
||||
|- run -> ../run
|
||||
|- spool/
|
||||
`- tmp/
|
||||
|
||||
Finit starts (very early) by mounting `/dev`, `/proc/`, and `/sys`,
|
||||
unless they (i.e., `/dev`) are already mounted. When all plugins and
|
||||
other, core Finit functions, have been set up, all relevant filesystems
|
||||
(`PASS > 0`) are checked and mounted from the selected `fstab`, either
|
||||
the default `/etc/fstab`, or any custom one selected from the command
|
||||
line, or at build time.
|
||||
|
||||
Any file system not listed `fstab` are automatically mounted by Finit,
|
||||
as listed above, provided their respective mount point exists.
|
||||
|
||||
With all filesystems mounted, Finit calls `swapon`.
|
||||
|
||||
At shutdown, and after having stopped all services and other lingering
|
||||
processes have been killed, filesystems are unmounted in the reverse
|
||||
order, and `swapoff` is called.
|
||||
|
||||
[FHS]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
|
||||
|
||||
### Runlevels
|
||||
|
||||
- ` S`: bootStrap
|
||||
- ` 1`: Single user mode
|
||||
- `2-5`: traditional multi-user mode
|
||||
- ` 6`: reboot
|
||||
- `7-9`: multi-user mode (extra)
|
||||
- ` 0`: shutdown
|
||||
|
||||
Runlevels are declared per service/run/task/sysv command. Starting in
|
||||
runlevel S ([bootStrap](bootstrap.md)), usually only for tasks supposed
|
||||
to run once at boot, and services like `syslogd`, which you need to
|
||||
start and run throughout the whole time your system is up. Before `S`
|
||||
is started, however, Finit performs a lot of housekeeping tasks like
|
||||
mounting all filesystems, calling `fsck` if needed, and making sure the
|
||||
everything is OK.
|
||||
|
||||
task [S] /lib/console-setup/console-setup.sh
|
||||
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
|
||||
|
||||
After runlevel S, Finit proceeds to runlevel 2. (This can be changed in
|
||||
`/etc/finit.conf` using the `runlevel N` directive.) Before starting
|
||||
the services in runlevel 2, Finit first stops everything that is not
|
||||
allowed to run in 2, and then brings up networking. Networking is
|
||||
expected to be available in all runlevels except: S, 1 (single user
|
||||
level), 6, and 0. Networking is enabled either by the `network script`
|
||||
directive, or if you have an `/etc/network/interfaces` file, Finit calls
|
||||
`ifup -a` -- at the very least the loopback interface is brought up.
|
||||
|
||||
> **Note:** when moving from runlevel S to 2, all run/task/services that
|
||||
> were constrained to runlevel S only are dropped from bookkeeping. So
|
||||
> when reaching the prompt, `initctl` will not show these run/tasks.
|
||||
> This is a safety mechanism to prevent bootstrap-only tasks from
|
||||
> accidentally being run again. E.g., `console-setup.sh` above.
|
||||
|
||||
|
||||
### Managing Services
|
||||
|
||||
Using `initctl disable my-service` the symlink (above) is removed and
|
||||
the service is queued for removal. Several changes can be made to the
|
||||
system, but it is not until `initctl reload` is called that the changes
|
||||
are activated.
|
||||
|
||||
To add a new static service, drop a `.conf` file in `/etc/finit.d/` and
|
||||
run `initctl reload`. (It is also possible to `SIGHUP` PID 1, or call
|
||||
`finit q`, but that has been deprecated with the `initctl` tool). Finit
|
||||
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 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:
|
||||
|
||||
- If a service's `.conf` file has been removed, or its conditions are no
|
||||
longer satisifed, the service is stopped.
|
||||
longer satisfied, the service is stopped.
|
||||
- If the file is modified, or a service it depends on has been reloaded,
|
||||
the service is reloaded (stopped and started).
|
||||
- If a new service is added it is automatically started — respecting
|
||||
@@ -67,9 +208,37 @@ 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
|
||||
|
||||
In Finit v4.3 support for setting environment variables in `finit.conf`,
|
||||
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. In
|
||||
Finit v4.4 the `set` keyword was added for completeness, but the old
|
||||
syntax (without the `set ` prefix) is still honored:
|
||||
|
||||
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`
|
||||
file, it will no longer be used by Finit or any new (!) started
|
||||
run/tasks or services. The environment of already started processes can
|
||||
not be changed.
|
||||
|
||||
The only variables reset to sane defaults on .conf reload are:
|
||||
|
||||
PATH=_PATH_STDPATH
|
||||
SHELL=_PATH_BSHELL
|
||||
LOGNAME=root
|
||||
USER=root
|
||||
|
||||
It is entirely possible to override these as well from the .conf files,
|
||||
but be careful. Changing SHELL changes the behavior of `system()` and a
|
||||
lot of other commands as well.
|
||||
|
||||
|
||||
Service Environment
|
||||
@@ -100,6 +269,67 @@ the `ps` command we can see that the process is started with:
|
||||
> `-` is used, a missing environment file does *not* block the start.
|
||||
|
||||
|
||||
Service Synchronization
|
||||
-----------------------
|
||||
|
||||
Finit was created for fast booting systems. Faster than a regular SysV
|
||||
Init based system at the time. Early on the need for a guaranteed start
|
||||
order of services (daemons) arose. I.e., service `A` must be guaranteed
|
||||
to have started (and be ready!) before `B`. The model that was chosen
|
||||
to determine this was very simple: PID files.
|
||||
|
||||
Early on in UNIX daemons were controlled with basic IPC like signals,
|
||||
and the way for a user to know that a daemon was ready to respond to
|
||||
signals (minimally having set up its signal handler), was to tell the
|
||||
user;
|
||||
|
||||
> "Hey, you can send signals to me using the PID in this file:
|
||||
> `/var/run/daemon.pid`".
|
||||
|
||||
Since most systems run fairly unchanged after boot, Finit could rely on
|
||||
the PID file for `A` being created before launching `B`. This method
|
||||
has worked well for a long time, and for systems based on Open Source it
|
||||
was easy to either add PID file support to a daemon without support for
|
||||
it, or fix ordering issues (PID file created before signal handler is
|
||||
set up) in existing daemons.
|
||||
|
||||
However, with the advent of other Init systems (Finit is rather old),
|
||||
most notably systemd and s6, other methods for signaling "readiness"
|
||||
arrived and daemons were adapted to these new schemes to a larger
|
||||
extent.
|
||||
|
||||
As of Finit v4.4 partial support for systemd and s6 is available. The
|
||||
native PID file mode of operation is still default, the other's can be
|
||||
defined per service, like this:
|
||||
|
||||
service notify:systemd foo
|
||||
service notify:s6 bar
|
||||
|
||||
To synchronize two services the following condition can be used:
|
||||
|
||||
service notify:systemd A
|
||||
service <service/A/ready> B
|
||||
|
||||
For details on the syntax and options, see below.
|
||||
|
||||
> **Note:** on `initctl reload` conditions are normally set in "flux",
|
||||
> while figuring out which to stop, start or restart. Services that
|
||||
> need to be restarted have their `ready` condition removed prior to
|
||||
> Finit sending them SIGHUP (if they support that), or stop-starting
|
||||
> them. A daemon is expected to reassert its readiness, e.g. systemd
|
||||
> style daemons to write `READY=1\n`.
|
||||
>
|
||||
> However, the s6 notify mode does not support this because in s6 you
|
||||
> are expected to close your notify descriptor after having written
|
||||
> `\n`. This means s6 style daemons currently must be stop-started.
|
||||
> (Declare the service with `<!>` in its condition statement.)
|
||||
>
|
||||
> For native PID file style readiness notification a daemon is expected
|
||||
> to either create its PID file, if it does not exist yet, or touch it
|
||||
> using `utimensat()` or similar. This triggers both the `<pid/>` and
|
||||
> `<.../ready>` conditions.
|
||||
|
||||
|
||||
Service Wrapper Scripts
|
||||
-----------------------
|
||||
|
||||
@@ -132,6 +362,26 @@ 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
|
||||
-------
|
||||
|
||||
@@ -153,7 +403,7 @@ Top-level group configuration.
|
||||
cgroup system cpu.weight:9800
|
||||
|
||||
Adding an extra cgroup `maint/` will require you to adjust the weight of
|
||||
the above three. We leave `init/` and `user /` as-is reducing weight of
|
||||
the above three. We leave `init/` and `user/` as-is reducing weight of
|
||||
`system/` to 9700.
|
||||
|
||||
cgroup system cpu.weight:9700
|
||||
@@ -183,7 +433,7 @@ The latter form also allows per-stanza limits on the form:
|
||||
|
||||
Notice the comma separation and the `mem.` exception to the rule: every
|
||||
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
|
||||
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is not filtering,
|
||||
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no filtering,
|
||||
except for expanding the shorthand `mem.` to `memory.`, if the file is
|
||||
not available, either the cgroup controller is not available in your
|
||||
Linux kernel, or the name is misspelled.
|
||||
@@ -196,6 +446,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>`
|
||||
@@ -239,6 +514,27 @@ Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
||||
> **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>`
|
||||
@@ -274,7 +570,10 @@ The system runlevel to go to after [bootstrap][] (S) has completed. `N`
|
||||
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
||||
halt.
|
||||
|
||||
Default: 2
|
||||
It is recommended to keep runlevel 1 as single-user mode, because
|
||||
Finit disables networking in this mode.
|
||||
|
||||
*Default:* 2
|
||||
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
|
||||
@@ -283,19 +582,37 @@ Default: 2
|
||||
|
||||
**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
|
||||
@@ -303,13 +620,13 @@ redirects can be freely used:
|
||||
|
||||
task [s] echo "foo" | cat >/tmp/bar
|
||||
|
||||
> `<COND>` is described in the [Services](#services) section.
|
||||
|
||||
|
||||
### 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.
|
||||
@@ -322,8 +639,12 @@ calls `init-script restart` on `initctl reload`. Similar to how
|
||||
|
||||
Forking services started with `sysv` scripts can be monitored by Finit
|
||||
by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
|
||||
Notice the leading `!`, it signifies Finit should not try to create the
|
||||
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
|
||||
@@ -331,8 +652,13 @@ by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
|
||||
**Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
||||
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely. Finit tries to restart services that die 10 times
|
||||
before giving up, then you have to `initctl restart NAME` it manually.
|
||||
exits prematurely. Finit tries to restart services that die, by
|
||||
default 10 times before giving up and marking them as *crashed*.
|
||||
After which they have to be manually restarted with `initctl restart
|
||||
NAME`. The limits controling this are configurable, see the options
|
||||
below.
|
||||
|
||||
> **Tip:** to allow endless restarts, see below option `respawn`
|
||||
|
||||
For daemons that support it, we recommend appending `--foreground`,
|
||||
`--no-background`, `-n`, `-F`, or similar command line argument to
|
||||
@@ -340,9 +666,19 @@ prevent them from forking off a sub-process in the background. This is
|
||||
the most reliable way to monitor a service.
|
||||
|
||||
However, not all daemons support running in the foreground, or they may
|
||||
start logging to the foreground as well, these are called forking
|
||||
services and are supported using the same syntax as forking `sysv`
|
||||
services, using the `pid:!/path/to/pidfile.pid` syntax.
|
||||
start logging to the foreground as well, these are forking daemons and
|
||||
are supported using the same syntax as forking `sysv` services, using
|
||||
the `pid:!/path/to/pidfile.pid` syntax. There is an alternative syntax
|
||||
that may be more intuitive, where Finit can also guess the PID file
|
||||
based on the daemon's command name:
|
||||
|
||||
service type:forking ntpd -- NTP daemon
|
||||
|
||||
This example lets BusyBox `ntpd` daemonize itself. Finit uses the
|
||||
basename of the binary to guess the PID file to watch for the PID:
|
||||
`/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the
|
||||
full `pid:!/path/to/file.pid`.
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -369,10 +705,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.
|
||||
@@ -389,6 +771,46 @@ 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:
|
||||
|
||||
* `restart:NUM` -- number of times Finit tries to restart a crashing
|
||||
service, default: 10, max: 255. When this limit is reached the
|
||||
service is marked *crashed* and must be restarted manually with
|
||||
`initctl restart NAME`
|
||||
* `restart_sec:SEC` -- number of seconds before Finit tries to restart
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
* `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.
|
||||
* `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
|
||||
allow the process to shut down gracefully. If the process has not
|
||||
@@ -413,12 +835,9 @@ 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.
|
||||
|
||||
> **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.
|
||||
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.
|
||||
|
||||
|
||||
### Run-parts Scripts
|
||||
@@ -430,15 +849,48 @@ files, or scripts, in the directory are called, in alphabetic order.
|
||||
The scripts in this directory are executed at the very end of runlevel
|
||||
`S`, [bootstrap][].
|
||||
|
||||
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.
|
||||
**Limitations:**
|
||||
|
||||
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||
Scripts called from `runparts`, or hook scripts (see below), are limited
|
||||
in what they can do and expect from Finit:
|
||||
|
||||
1. Finit is single threaded and all scripts executed from these two
|
||||
mechanisms are limited to file system operations (early or late
|
||||
hooks may not have write access!) and standard UNIX programs.
|
||||
2. Thus, it is *not possible* to call `initctl` to query status of
|
||||
services since that calls Finit over the `/run/finit/socket`.
|
||||
3. Confusingly enough, there are some commands to `initctl` that are
|
||||
available; `ls`, `enable`, `disable`, `touch`, `create`, `delete`,
|
||||
and all the `cond` commands. Because all these are basically just
|
||||
wrappers for manipulating files (again, write access for early/late
|
||||
hooks may not be available).
|
||||
|
||||
Also, similar to the `/etc/rc.local` shell script, make sure that all
|
||||
your services and programs either terminate or start in the background
|
||||
or you will block Finit.
|
||||
|
||||
> **Note:** `runparts` scrips are only read and executed in runlevel S
|
||||
> ([bootstrap][]). See [hook scripts](plugins.md#hooks) for other ways
|
||||
> to run scripts at certain points during the complete lifetime of the
|
||||
> system.
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
It can be beneficial to use `01-name`, `02-othername`, etc., to ensure
|
||||
the scripts are started in that order, e.g., if there is a dependency
|
||||
order between scripts. Symlinks to existing daemons can talso be used,
|
||||
but make sure they daemonize (background) themselves properly, otherwise
|
||||
Finit will lock up.
|
||||
|
||||
If `S[0-9]foo` and `K[0-9]bar` style naming is used, the executable will
|
||||
be called with an extra argument, `start` and `stop`, respectively.
|
||||
E.g., `S01foo` will be called as `S01foo start`. Of course, `S01foo`
|
||||
and `K01foo` may be a symlink to to `another/directory/foo`.
|
||||
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
|
||||
@@ -472,8 +924,9 @@ size limit is reached.
|
||||
`tty [LVLS] <COND> [notty] [rescue]`
|
||||
|
||||
The first variant of this option uses the built-in getty on the given
|
||||
TTY device DEV, in the given runlevels. The DEV may be the special
|
||||
keyword `@console`, or `console`, useful on embedded systems.
|
||||
TTY device DEV, in the given runlevels. DEV may be the special keyword
|
||||
`@console`, which is expanded from `/sys/class/tty/console/active`,
|
||||
useful on embedded systems.
|
||||
|
||||
The default baud rate is 0, i.e., keep kernel default.
|
||||
|
||||
@@ -554,8 +1007,8 @@ One can also use the `service` stanza to start a stand-alone shell:
|
||||
### Non-privileged Services
|
||||
|
||||
Every `run`, `task`, or `service` can also list the privileges the
|
||||
`/path/to/cmd` should be executed with. Simply prefix the path with
|
||||
`[@USR[:GRP]]` like this:
|
||||
`/path/to/cmd` should be executed with. Prefix the command with
|
||||
`@USR[:GRP]`, group is optional, like this:
|
||||
|
||||
run [2345] @joe:users logger "Hello world"
|
||||
|
||||
@@ -596,6 +1049,91 @@ Log rotation is controlled using the global `log` setting.
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
|
||||
|
||||
### Misc Settings
|
||||
|
||||
**Syntax:** `reboot-delay <0-60>`
|
||||
|
||||
Optional delay at reboot (or shutdown or halt) to allow kernel
|
||||
filesystem threads to complete after calling `sync(2)` before
|
||||
rebooting. This applies primarily to filesystems that do not
|
||||
have a reboot notifier implemented. At the point of writing,
|
||||
the only known filesystems affected are: ubifs, jffs2.
|
||||
|
||||
*Default:* 0 (disabled)
|
||||
|
||||
When enabled (non-zero), this delay runs after file systems have been
|
||||
unmounted and the root filesystem has been remounted read-only, and
|
||||
sync(2) has been called, twice.
|
||||
|
||||
> "On Linux, sync is only guaranteed to schedule the dirty blocks for
|
||||
> writing; it can actually take a short time before all the blocks are
|
||||
> finally written.
|
||||
|
||||
|
||||
SysV Init Compatibility
|
||||
-----------------------
|
||||
|
||||
Finit comes with a few SysV Init compatibility features to ease the
|
||||
transition from a serialized boot process.
|
||||
|
||||
### `runparts DIRECTORY`
|
||||
|
||||
For a directory with traditional start/stop scripts that should run, in
|
||||
order, at bootstrap, Finit provides the `runparts` directive. It runs
|
||||
in runlevel S, at the very end of it (before calling `/etc/rc.local`)
|
||||
making it perfect for most scenarios.
|
||||
|
||||
For syntax details, see [Run-parts Scripts](#run-parts-scripts) above.
|
||||
Here is an example take from a Debian installation:
|
||||
|
||||
runparts /etc/rc2.d
|
||||
|
||||
Files in these directories are usually named `SNNfoo` and `KNNfoo`,
|
||||
which Finit knows about and automatically appends the correct argument:
|
||||
|
||||
/bin/sh -c /etc/rc2.d/S01openbsd-inetd start
|
||||
|
||||
or
|
||||
|
||||
/bin/sh -c /etc/rc0.d/K01openbsd-inetd stop
|
||||
|
||||
Files that do not match this pattern are started similarly but without
|
||||
the extra command line argument.
|
||||
|
||||
|
||||
### Start/Stop Scripts
|
||||
|
||||
For syntax details, see [SysV Init Scripts](#sysv-init-scripts), above.
|
||||
Here follows an example taken from a Debian installation:
|
||||
|
||||
sysv [2345] <pid/syslogd> /etc/init.d/openbsd-inetd -- OpenBSD inet daemon
|
||||
|
||||
The init script header could be parsed to extract `Default-Start:` and
|
||||
other parameters for the `sysv` command to Finit. There is currently no
|
||||
way to detail a generic syslogd dependency in Finit, so `Should-Start:`
|
||||
in the header must be mapped to the condition system in Finit using an
|
||||
absolute reference, here we depend on the sysklogd project's syslogd.
|
||||
|
||||
### `/etc/rc.local`
|
||||
|
||||
One often requested feature, early on, was a way to run a site specific
|
||||
script to set up, e.g., static routes or firewall rules. A user can add
|
||||
a `task` or `run` command in the Finit configuration for this, but for
|
||||
compatibility reasons the more widely know `/etc/rc.local` is used if
|
||||
it exists, and is executable. It is called very late in the boot process
|
||||
when the system has left runlevel S, stopped all old and started all new
|
||||
services in the target runlevel (default 2).
|
||||
|
||||
> In Finit releases before v4.5 this script blocked Finit execution and
|
||||
> made it as good as impossible to call `initctl` during that time.
|
||||
|
||||
### `init q`
|
||||
|
||||
When `/sbin/finit` is installed as `/sbin/init`, it is possible to use
|
||||
`init q` to reload the configuration. This is the same as calling
|
||||
`initctl reload`.
|
||||
|
||||
|
||||
Rescue Mode
|
||||
-----------
|
||||
|
||||
@@ -603,21 +1141,30 @@ 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
|
||||
> force `reboot`, `shutdown`, or `poweroff`.
|
||||
|
||||
|
||||
### 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
|
||||
@@ -653,8 +1200,9 @@ Limitations
|
||||
-----------
|
||||
|
||||
As of Finit v4 there are no limitations to where `.conf` settings can be
|
||||
placed. Except for the system/global `rlimit`, which can only be set
|
||||
from `/etc/finit.conf`, since it is the first `.conf` file Finit reads.
|
||||
placed. Except for the system/global `rlimit` and `cgroup` top-level
|
||||
group declarations, which can only be set from `/etc/finit.conf`, since
|
||||
it is the first `.conf` file Finit reads.
|
||||
|
||||
Originally, `/etc/finit.conf` was the only way to set up a Finit system.
|
||||
Today it is mainly used for bootstrap settings like system hostname,
|
||||
@@ -665,9 +1213,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
|
||||
@@ -704,3 +1252,19 @@ the only option is to remove `/libexec/finit/watchdogd` or build without
|
||||
it at configure time.
|
||||
|
||||
[bootstrap]: bootstrap.md
|
||||
|
||||
|
||||
keventd
|
||||
-------
|
||||
|
||||
The kernel event daemon bundled with Finit is a simple uevent monitor
|
||||
for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition,
|
||||
which can be useful to prevent power hungry services like anacron to run
|
||||
when a laptop is only running on battery, for instance.
|
||||
|
||||
Since keventd is not an integral part of Finit yet it is not enabled by
|
||||
default. Enable it using `./configuure --with-keventd`. The bundled
|
||||
contrib build scripts for Debian, Alpine, and Void have this enabled.
|
||||
|
||||
This daemon is planned to be extended with monitoring of other uevents,
|
||||
patches and ideas are welcome in the issue tracker.
|
||||
|
||||
+138
-25
@@ -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.
|
||||
@@ -34,26 +35,75 @@ For your convenience a set of *optional* plugins are available:
|
||||
* *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)). _Optional plugin._
|
||||
|
||||
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*: Setup and start either udev or mdev hotplug daemon, if
|
||||
available.
|
||||
available. Enabled by default.
|
||||
|
||||
> See the [Services](config.md#services) section in the configuration
|
||||
> guide for an example how to run `mdevd`, alternative to plain mdev.
|
||||
|
||||
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
|
||||
Enabled by default.
|
||||
|
||||
* *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
|
||||
* *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, `#`, or `;`, is skipped.
|
||||
|
||||
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 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
|
||||
for services that may want to be SIGHUP'ed on new default route or
|
||||
interfaces going up/down.
|
||||
interfaces going up/down. Enabled by default.
|
||||
|
||||
* *resolvconf.so*: Setup necessary files for `resolvconf` at startup.
|
||||
_Optional plugin._
|
||||
|
||||
* *tty.so*: Watches `/dev`, using inotify, for new device nodes (TTY's)
|
||||
to start/stop getty consoles on them on demand. Useful when plugging
|
||||
in a usb2serial converter to login to your embedded device.
|
||||
in a usb2serial converter to login to your embedded device. Enabled
|
||||
by default.
|
||||
|
||||
* *urandom.so*: Setup random seed at startup.
|
||||
* *urandom.so*: Setup random seed at startup. Enabled by default.
|
||||
|
||||
* *x11-common.so*: Setup necessary files for X-Window. _Optional plugin._
|
||||
|
||||
@@ -67,38 +117,101 @@ 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_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
|
||||
mounted — very early
|
||||
* `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_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
|
||||
and default init signals are setup
|
||||
* `HOOK_ROOTFS_UP`, `hook/mount/root`: When `finit.conf` has been read
|
||||
and `/` has is mounted — very early
|
||||
|
||||
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
|
||||
networking is up (`lo` is up and the `network` script has run)
|
||||
* `HOOK_MOUNT_ERROR`, `hook/mount/error`: executed if `mount -a` fails
|
||||
|
||||
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
|
||||
* `HOOK_MOUNT_POST`, `hook/mount/post`: always executed after `mount -a`
|
||||
|
||||
* `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d`
|
||||
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_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()`
|
||||
|
||||
+55
-1
@@ -1,7 +1,59 @@
|
||||
Finit Services
|
||||
==============
|
||||
|
||||

|
||||
Starting & Monitoring
|
||||
---------------------
|
||||
|
||||
Finit can start and monitor the following types of daemons:
|
||||
|
||||
* Forks to background, creates a PID file
|
||||
* Runs in foreground:
|
||||
- creates a PID file
|
||||
- does not create a PID file -- Finit can create it for you (optional)
|
||||
|
||||
Finit can *not* start and monitor a daemon that:
|
||||
|
||||
* Forks to background and does *not* create a PID file
|
||||
|
||||
| | Forking | Creates PID File | Finit creates PID File |
|
||||
|---|---------|------------------|------------------------|
|
||||
| ✔ | Yes | Yes | No |
|
||||
| ✔ | No | Yes | No |
|
||||
| ✔ | No | No | Yes, optionally |
|
||||
| ✘ | Yes | No | No |
|
||||
|
||||
> **Note:** PID files is one mechanism used to assert conditions to
|
||||
> synchronize the start and stop of other, dependent, services.
|
||||
|
||||
### Forks to Background w/ PID File
|
||||
|
||||
There are two syntax variants, type 1 and type 2. The former is the
|
||||
traditional one used also for `sysv` start/stop scripts, and the latter
|
||||
is inspired by systemd, with a twist -- it lets Finit guess the pifdile
|
||||
to look for based on the standard path and the basename of the command.
|
||||
|
||||
service pid:!/run/serv.pid serv -- Forking service, type 1
|
||||
service type:forking serv -- Forking service, type 2
|
||||
|
||||
In this example the resulting files to watch for are `/run/serv.pid` and
|
||||
`/var/run/serv.pid`, respectively. On most modern Linux systems this is
|
||||
the same directory (`/var/run` is a symlink to `../run`).
|
||||
|
||||
### Runs in Foreground w/ PID File
|
||||
|
||||
service serv -n -p -- Foreground service w/ PID file
|
||||
|
||||
### Runs in Foreground w/o PID File
|
||||
|
||||
Same as previous, but we tell Finit to create the PID file, because we
|
||||
need it to synchronize start/stop of a dependent service.
|
||||
|
||||
service pid:/run/serv.pid serv -n -- Foreground service w/o PID file
|
||||
|
||||
### Runs in Foreground w/ Custom PID File
|
||||
|
||||
service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file
|
||||
|
||||
|
||||
State Machine
|
||||
-------------
|
||||
@@ -11,6 +63,8 @@ For `run`s and `task`s there is an additional end state called `DONE`,
|
||||
not shown in the diagram for brevity. Services start in the `HALTED`
|
||||
state.
|
||||
|
||||

|
||||
|
||||
The current state depends on the two following conditions:
|
||||
|
||||
* `E`: Service enabled. In order for `E` to be satisfied, the service
|
||||
|
||||
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 |
@@ -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")])
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
@@ -0,0 +1,2 @@
|
||||
dist_man8_MANS = finit.8 initctl.8
|
||||
dist_man5_MANS = finit.conf.5
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd Apr 22, 2023
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.Dt FINIT 8 SMM
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
.Nm finit
|
||||
.Nd Fast init for Linux systems
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/finit
|
||||
.Op --
|
||||
.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
|
||||
.Op Fl hv
|
||||
.Op q | Q | 0-9
|
||||
.Sh DESCRIPTION
|
||||
Fast init for Linux systems. Reverse engineered from the EeePC
|
||||
fastinit, 10+ years ago by Claudio Matsuoka, "gaps filled with frog
|
||||
DNA ...". Focus is on small and embedded systems, although Finit is
|
||||
fully usable on server and desktop systems as well.
|
||||
.Pp
|
||||
Features include:
|
||||
.Bl -bullet -width 1n -compact
|
||||
.It
|
||||
Runlevels, defined per service
|
||||
.It
|
||||
One-shot tasks, services (daemons), or SysV init start/stop scripts
|
||||
.It
|
||||
Runparts and
|
||||
.Pa /etc/rc.local
|
||||
support
|
||||
.It
|
||||
Process supervision similar to
|
||||
.Xr systemd 8
|
||||
.It
|
||||
Sourcing environment files
|
||||
.It
|
||||
Conditions for network/process/custom dependencies
|
||||
.It
|
||||
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
|
||||
.It
|
||||
Built-in getty
|
||||
.It
|
||||
Built-in watchdog, with support for hand-over to
|
||||
.Lk https://troglobit.com/watchdogd.html watchdogd
|
||||
.It
|
||||
Built-in support for Debian/BusyBox
|
||||
.Pa /etc/network/interfaces
|
||||
.It
|
||||
Cgroups v2, both configuration and monitoring in `initctl top`
|
||||
.It
|
||||
Plugin support for customization
|
||||
.It
|
||||
Proper rescue mode with bundled `sulogin` for protected maintenance shell
|
||||
.El
|
||||
.Sh BOOTPARAM
|
||||
.Nm
|
||||
support the following command line parameters to alter the behavior
|
||||
at boot. Notice the
|
||||
.Cm --
|
||||
separator needed to tell the kernel which paramters should be given to
|
||||
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,
|
||||
Finit use the kernel ring buffer,
|
||||
.Pa /dev/kmsg .
|
||||
.It Cm finit.fstab=/path/to/fstab.alt
|
||||
Tell
|
||||
.Nm
|
||||
to use an alternate
|
||||
.Cm fstab
|
||||
to mount the file system from. Remember, this file must be on the
|
||||
.Cm root=...
|
||||
file system provided to Finit from the kernel. If this file is missing,
|
||||
.Nm
|
||||
falls back to its built-in default, which can be changed at build time
|
||||
using the
|
||||
.Cm --with-fstab=/path/to/footab
|
||||
configure option. It is even possible to completely disable the
|
||||
built-in using
|
||||
.Cm --without-fstab ,
|
||||
meaning
|
||||
.Nm
|
||||
.Sy must
|
||||
be started with the
|
||||
.Cm finit.fstab
|
||||
option.
|
||||
.Pp
|
||||
Should
|
||||
.Nm
|
||||
discover that there is no fstab file available, including the fallback,
|
||||
it triggers the same failure mode as when
|
||||
.Cm fsck
|
||||
fails, starting
|
||||
.Xr sulogin 8 ,
|
||||
if that is availble. When
|
||||
.Cm sulogin
|
||||
is missing, or when the user exits from
|
||||
.Cm sulogin ,
|
||||
the system is rebooted.
|
||||
.Pp
|
||||
Default fstab, unless changed at build-time:
|
||||
.Pa /etc/fstab
|
||||
.It Cm finit.status=<on,off>
|
||||
Control finit boot progress, including banner. This used to be called
|
||||
.Cm finit.show_status ,
|
||||
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
|
||||
program (Finit, or from util-linux/Busybox), you will be given a root
|
||||
login to a maintenance shell. However, if
|
||||
.Cm sulogin
|
||||
is missing, the file
|
||||
.Pa /lib/finit/rescue.conf
|
||||
is read and the system booted in a limited fallback mode. See
|
||||
.Xr finit.conf 5
|
||||
for more information.
|
||||
.It Cm single | S
|
||||
Single user mode, runlevel 1, in Finit. Useful to debug problems with
|
||||
the regular runlevel. All services and TTYs in
|
||||
.Cm [1]
|
||||
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
|
||||
Before laucnhing services, the system needs to be bootstrapped. This
|
||||
involves mounting all filesystems (not already mounted by an initramfs)
|
||||
in
|
||||
.Pa /etc/fstab ,
|
||||
or any alternate
|
||||
.Pa fstab
|
||||
file provided on the Finit command line using
|
||||
.Cm finit.fstab ,
|
||||
ensuring that necessary filesystems like:
|
||||
.Pa /dev ,
|
||||
.Pa /proc ,
|
||||
.Pa /sys ,
|
||||
and
|
||||
.Pa /run
|
||||
are available and can provide the system with the services required.
|
||||
.Pp
|
||||
With that out of the way, a set of plugins take over:
|
||||
.Pp
|
||||
.Bl -tag -width bootmisc.so -offset indent
|
||||
.It Cm bootmisc.so
|
||||
Cleans up stale files from previous boots and default directories are
|
||||
created in tmpfs filesystems
|
||||
.It Cm modprobe.so
|
||||
Probes kernel modules required by the system
|
||||
.It Cm procps.so
|
||||
Set kernel variables from
|
||||
.Pa /etc/sysctl.conf
|
||||
et al
|
||||
.El
|
||||
.Pp
|
||||
When the plugins are done, finit reads
|
||||
.Pa /etc/finit.conf
|
||||
and all other enabled services in
|
||||
.Pa /etc/finit.d/* .
|
||||
First all services, run/tasks in runlevel S (bootStrap) are started,
|
||||
When all run/tasks have completed, Finit changes to the configured
|
||||
runlevel (default 2), Processes are started in parallel, in dependency
|
||||
order according to their conditions.
|
||||
.Pp
|
||||
When all services and run/tasks have been started, the console progress
|
||||
is disabled and all configured getty services are started.
|
||||
.Sh CONDITIONS
|
||||
The condition subysystem in Finit was initially created to track PID
|
||||
files of daemons. If service/task B requires service A to have started,
|
||||
Finit waits for the PID file of service A to be created/updated before
|
||||
it starts service B. Conversely, if service A is stopped and its PID
|
||||
file removed, service B is also stopped.
|
||||
.Pp
|
||||
The following condition families are available today:
|
||||
.Pp
|
||||
.Bl -tag -width 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
|
||||
.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
|
||||
User conditions, assert and deassert using
|
||||
.Cm initctl cond [set|clr] foo
|
||||
.El
|
||||
.Sh SIGNALS
|
||||
PID 1 is the only process that by default blocks all signals. Finit
|
||||
allows for the following signals to control operation, but recommends
|
||||
instead using the
|
||||
.Xr initctl 8
|
||||
tool.
|
||||
.Pp
|
||||
.Bl -tag -width TERM
|
||||
.It HUP
|
||||
Tell
|
||||
.Nm
|
||||
to reload its configuration file(s), same as
|
||||
.Cm initctl reload
|
||||
.It USR1
|
||||
Restart API
|
||||
.Cm ( initctl )
|
||||
socket.
|
||||
.It USR2
|
||||
Tell
|
||||
.Nm
|
||||
to perform a system shutdown, same as
|
||||
.Cm initctl poweroff
|
||||
.It TERM
|
||||
Like SIGUSR2 but performs a reboot, same as
|
||||
.Cm initctl reboot
|
||||
.It INT
|
||||
Sent from kernel when Ctrl-Alt-Del is pressed (on the console), this
|
||||
asserts the sys/key/ctrlaltdel condition, which can be used to start
|
||||
tasks or services
|
||||
.It PWR
|
||||
Sent from a a power daemon on changes to UPS status, Finit asserts
|
||||
the sys/pwr/fail condition
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width /etc/finit.d/available/*.conf -compact
|
||||
.It Pa /etc/finit.conf
|
||||
Main configuration file
|
||||
.It Pa /etc/finit.d/*.conf
|
||||
Static service definitions
|
||||
.It Pa /etc/finit.d/available/*.conf
|
||||
Available services
|
||||
.It Pa /etc/finit.d/enabled/*.conf
|
||||
Symlinks from available
|
||||
.It Pa /run/finit/
|
||||
Runtime files, including the condition subsystem
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr finit.conf 5
|
||||
.Xr initctl 8
|
||||
.Xr tmpfiles.d 5
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
maintained by Joachim Wiberg, with contributions by many others.
|
||||
@@ -0,0 +1,989 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd July 11, 2023
|
||||
.Dt FINIT.CONF 5 SMM
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
.Nm finit.conf
|
||||
.Nd Finit configuration file format
|
||||
.Sh SYNOPSIS
|
||||
.Nm /etc/finit.conf
|
||||
.Nm /etc/finit.d/*.conf
|
||||
.Nm /etc/finit.d/available/*.conf
|
||||
.Nm /etc/finit.d/enabled/*.conf
|
||||
.Sh DESCRIPTION
|
||||
.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
|
||||
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
|
||||
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, optional
|
||||
.It Pa /etc/finit.d/*.conf
|
||||
Static (system) service definitions
|
||||
.It Pa /etc/finit.d/available/*.conf
|
||||
Available (installed) services
|
||||
.It Pa /etc/finit.d/enabled/*.conf
|
||||
Enabled services (symlink back)
|
||||
.El
|
||||
.Pp
|
||||
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.
|
||||
.Pp
|
||||
.Bl -tag -width 1n
|
||||
.It Cm rlimit Oo hard|soft Oc Ar RESOURCE Aq LIMIT | unlimited
|
||||
.Pp
|
||||
Set the hard or soft limit for a resource, or both if that argument is
|
||||
omitted.
|
||||
.Ar RESOURCE
|
||||
is the lower-case
|
||||
.Cm RLIMIT_
|
||||
string constants from
|
||||
.Xr setrlimit 2 ,
|
||||
without prefix. E.g. to set
|
||||
.Cm RLIMIT_CPU ,
|
||||
use
|
||||
.Cm cpu .
|
||||
.Pp
|
||||
.Ar LIMIT
|
||||
is an integer that depends on the resource being modified, see
|
||||
the man page, or the kernel
|
||||
.Pa /proc/PID/limits
|
||||
file, for details.
|
||||
Finit versions before v3.1 used
|
||||
.Cm infinity
|
||||
for
|
||||
.Cm unlimited ,
|
||||
which is still supported, albeit deprecated.
|
||||
.Bd -unfilled -offset indent
|
||||
# No process is allowed more than 8MB of address space
|
||||
rlimit hard as 8388608
|
||||
|
||||
# Core dumps may be arbitrarily large
|
||||
rlimit soft core infinity
|
||||
|
||||
# CPU limit for all services, soft & hard = 10 sec
|
||||
rlimit cpu 10
|
||||
.Ed
|
||||
.Pp
|
||||
.Cm rlimit
|
||||
can be set globally, in
|
||||
.Pa /etc/finit.conf ,
|
||||
or locally per each
|
||||
.Pa /etc/finit.d/*.conf
|
||||
read. I.e., a set of task/run/service stanzas can share the same
|
||||
rlimits if they are in the same .conf.
|
||||
.It Cm runlevel Aq N
|
||||
The system runlevel to go to after bootstrap (S) has completed.
|
||||
.Cm N
|
||||
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
||||
halt. All other can be used by operating system administrators.
|
||||
Default: 2
|
||||
.Pp
|
||||
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
|
||||
One-shot command to run in sequence when entering a runlevel, with
|
||||
optional arguments and description.
|
||||
.Pp
|
||||
.Cm run
|
||||
commands are guaranteed to be completed before running the next
|
||||
command. Highly useful if true serialization is needed. Usually
|
||||
only used in the bootstrap (S) runlevel.
|
||||
.Pp
|
||||
.Cm Aq COND
|
||||
conditions are described in
|
||||
.Xr finit 8 ,
|
||||
see also the
|
||||
.Sx Examples
|
||||
section below.
|
||||
.It Cm task Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
|
||||
One-shot like
|
||||
.Cm run ,
|
||||
but starts in parallel with the next command.
|
||||
.Pp
|
||||
Both
|
||||
.Cm run
|
||||
and
|
||||
.Cm task
|
||||
commands are run in a shell, so pipes and redirects can be freely used:
|
||||
.Bd -unfilled -offset indent
|
||||
task [s] echo "foo" | cat >/tmp/bar
|
||||
.Ed
|
||||
.It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description
|
||||
Similar to
|
||||
.Cm task
|
||||
is the
|
||||
.Cm sysv
|
||||
stanza, which can be used to call SysV style start/stop scripts. The
|
||||
primary intention for this command is to be able to re-use much of
|
||||
existing setup and init scripts in Linux distributions.
|
||||
.Pp
|
||||
When entering an allowed runlevel, Finit calls
|
||||
.Cm init-script start ,
|
||||
when entering a disallowed runlevel, Finit calls
|
||||
.Cm init-script stop ,
|
||||
and if the Finit .conf, where the
|
||||
.Cm sysv
|
||||
stanza is declared, is modified, Finit calls
|
||||
.Cm init-script restart
|
||||
on
|
||||
.Cm initctl reload .
|
||||
Similar to how
|
||||
.Cm service
|
||||
stanzas work.
|
||||
.Pp
|
||||
Forking services started with
|
||||
.Cm sysv
|
||||
scripts can be monitored by Finit by declaring the PID file to look for:
|
||||
.Bd -unfilled -offset indent
|
||||
sysv pid:!/path/to/pidfile.pid /path/to/script ...
|
||||
.Ed
|
||||
.Pp
|
||||
The leading '!' is to prevent Finit from managing the PID file, which is
|
||||
the default behavior for the
|
||||
.Cm pid:
|
||||
command modifier.
|
||||
.It Cm service Oo LVLS Oc Ao COND Ac Ar /path/to/daemon ARGS Op -- Optional description
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely. Finit tries to restart services that die, by
|
||||
default 10 times, before giving up and marking them as
|
||||
.Em crashed .
|
||||
After which they have to be restarted manually using
|
||||
.Cm initctl restart NAME .
|
||||
The number of restarts, the delay between each restart, and more is
|
||||
configurable, see the options below.
|
||||
.Pp
|
||||
.Bd -filled -offset indent
|
||||
.Sy Tip:
|
||||
to allow endless restarts, see below option
|
||||
.Cm respawn
|
||||
.Ed
|
||||
.Pp
|
||||
For daemons that support it, we recommend appending
|
||||
.Cm --foreground , --no-background , -n , -F ,
|
||||
or similar command line argument to prevent them from forking off a
|
||||
sub-process in the background. This is the most reliable way to monitor
|
||||
a service.
|
||||
.Pp
|
||||
However, not all daemons support running in the foreground, or they may
|
||||
start logging to the foreground as well, these are called forking
|
||||
services and are supported using the same syntax as forking
|
||||
.Cm sysv
|
||||
services, using the
|
||||
.Cm pid:!/path/to/pidfile.pid
|
||||
command modifier syntax. There is an alternative syntax that may be
|
||||
more intuitive, where Finit can also guess the PID file based on the
|
||||
daemon's command name:
|
||||
.Bd -unfilled -offset indent
|
||||
service type:forking ntpd -- NTP daemon
|
||||
.Ed
|
||||
.Pp
|
||||
Here we let BusyBox
|
||||
.Nm ntpd
|
||||
daemonize itself. Finit uses the basename of the binary to guess the
|
||||
PID file to watch for the PID:
|
||||
.Pa /var/run/ntpd.pid .
|
||||
If Finit guesses wrong, you have to submit the full
|
||||
.Cm pid:!/path/to/file.pid
|
||||
option to your service stanza.
|
||||
.Pp
|
||||
.Sy Example: in the case of
|
||||
.Cm ospfd
|
||||
(below), we omit the
|
||||
.Cm -d
|
||||
flag (daemonize) to prevent it from forking to the background:
|
||||
.Bd -unfilled -offset indent
|
||||
service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
|
||||
.Ed
|
||||
.Pp
|
||||
.Cm [2345]
|
||||
denote the runlevels
|
||||
.Cm ospfd
|
||||
is allowed to run in, they are optional and default to runlevel 2-5 if
|
||||
omitted.
|
||||
.Pp
|
||||
.Cm <pid/zebra>
|
||||
is the condition for starting
|
||||
.Cm ospfd .
|
||||
In this example Finit waits for another service,
|
||||
.Cm zebra ,
|
||||
to have created its PID file in
|
||||
.Pa /var/run/quagga/zebra.pid
|
||||
before starting
|
||||
.Cm ospfd .
|
||||
Finit watches *all* files in
|
||||
.Pa /var/run ,
|
||||
for each file named
|
||||
.Cm *.pid ,
|
||||
.Cm */pid ,
|
||||
Finit opens it and find the matching
|
||||
.Cm NAME:ID
|
||||
using the PID.
|
||||
.Pp
|
||||
Some services do not maintain a PID file and rather than patching each
|
||||
application Finit provides a workaround. A
|
||||
.Cm pid
|
||||
modifier keyword can be set to have Finit automatically create (when
|
||||
starting) and later remove (when stopping) the PID file. The file is
|
||||
created in the
|
||||
.Pa /var/run
|
||||
directory using the
|
||||
.Xr basename 3
|
||||
of the service. The full syntax of the
|
||||
.Cm pid
|
||||
modifier is:
|
||||
.Bd -unfilled -offset indent
|
||||
pid[:[!][/path/to/]filename[.pid]]
|
||||
.Ed
|
||||
.Pp
|
||||
For example, by adding
|
||||
.Cm pid:/run/foo.pid
|
||||
to the service
|
||||
.Cm /sbin/bar ,
|
||||
that PID file will, not only be created and removed automatically, but
|
||||
also be used by the Finit condition subsystem. So a service/run/task
|
||||
can depend on the
|
||||
.Cm <pid/bar>
|
||||
condition.
|
||||
.Pp
|
||||
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
|
||||
command modifier. The service can then be started at any time by
|
||||
running
|
||||
.Cm initctl start NAME
|
||||
.Pp
|
||||
The name of a service, shown by the
|
||||
.Cm initctl
|
||||
tool, defaults to the basename of the service executable. It can be
|
||||
changed with the
|
||||
.Cm name:foo
|
||||
command modifier.
|
||||
.Pp
|
||||
As mentioned previously, services are automatically restarted should
|
||||
they crash, this is configurable with the following options:
|
||||
.Bl -tag -width 1n
|
||||
.It Cm restart:NUM
|
||||
number of times Finit tries to restart a crashing
|
||||
service, default: 10. When this limit is reached the service is
|
||||
marked
|
||||
.Em crashed
|
||||
and must be restarted manually with
|
||||
.Xr initctl 8 .
|
||||
.It Cm restart_sec:SEC
|
||||
number of seconds before Finit tries to restart
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
.It Cm 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
|
||||
.It Cm respawn
|
||||
bypasses the
|
||||
.Cm restart
|
||||
mechanism completely, allows endless restarts. Useful in many
|
||||
use-cases, but not what
|
||||
.Cm service
|
||||
was originally designed for so not the default behavior.
|
||||
.It Cm oncrash:reboot
|
||||
when all retries have failed, and the service
|
||||
has
|
||||
.Em crashed ,
|
||||
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
|
||||
moving to another runlevel, Finit starts by sending SIGTERM, to allow
|
||||
the process to shut down gracefully. If the process has not been
|
||||
collected within 3 seconds, Finit sends SIGKILL. To halt the process
|
||||
using a different signal, use the command modifier
|
||||
.Cm halt:SIGNAL ,
|
||||
e.g.,
|
||||
.Cm halt:SIGPWR .
|
||||
To change the delay between your halt signal and KILL, use the command
|
||||
modifier
|
||||
.Cm kill:SEC ,
|
||||
e.g.,
|
||||
.Cm kill:10
|
||||
to wait 10 seconds before sending SIGKILL.
|
||||
.Pp
|
||||
Services support the
|
||||
.Cm pre:script
|
||||
and
|
||||
.Cm post:script
|
||||
command actions as well. These run as the same
|
||||
.Cm @USER:GROUP
|
||||
as the service itself, with any
|
||||
.Cm env:file
|
||||
sourced. The scripts must use an absolute path, but are executed from
|
||||
the
|
||||
.Cm $HOME
|
||||
of the given user. The scripts are not called with any argument
|
||||
(currently), but both get the
|
||||
.Cm SERVICE_IDENT=foo
|
||||
environment variable set. Here
|
||||
.Cm foo
|
||||
denotes the identity of the service, which if there are multiple
|
||||
services named
|
||||
.Cm foo ,
|
||||
may be
|
||||
.Cm foo:1 ,
|
||||
or any unique identifier specified in the .conf file. The
|
||||
.Cm post:script
|
||||
is called with an additional set of environment variables:
|
||||
.Bl -tag -offset indent -width 1n
|
||||
.It Cm EXIT_CODE=[exited,signal,crashed]
|
||||
set to one of
|
||||
.Cm exited ,
|
||||
.Cm signal ,
|
||||
or
|
||||
.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
|
||||
.El
|
||||
.Pp
|
||||
The scripts have a default execution time of 3 seconds before they are
|
||||
SIGKILLed, this can be adjusted using the
|
||||
.Cm kill:SEC
|
||||
modifier syntax.
|
||||
.It Cm runparts Aq DIR
|
||||
Call
|
||||
.Xr run-parts 8
|
||||
on
|
||||
.Cm DIR
|
||||
to run start scripts. All executable files, or scripts, in the
|
||||
directory are called, in alphabetic order. The scripts in this
|
||||
directory are executed at the very end of bootstrap, runlevel S.
|
||||
.Pp
|
||||
It can be beneficial to use
|
||||
.Cm S01name ,
|
||||
.Cm S02othername ,
|
||||
etc. if there is a dependency order between the scripts. Symlinks to
|
||||
existing daemons can talso be used, but make sure they daemonize by
|
||||
default.
|
||||
.Pp
|
||||
Similar to the
|
||||
.Pa /etc/rc.local
|
||||
shell script, make sure that all your services and programs either
|
||||
terminate or start in the background or you will block Finit.
|
||||
.Sy Note:
|
||||
only read and executed in runlevel S (bootstrap).
|
||||
.It Cm include Aq CONF
|
||||
Include another configuration file. Absolute path required.
|
||||
.It Cm log size:BYTES count:NUM
|
||||
Log rotation for run/task/services using the
|
||||
.Cm log
|
||||
command modifier with redirection to a log file. Global setting,
|
||||
applies to all services.
|
||||
.Pp
|
||||
The size can be given as bytes, without a specifier, or in `k`, `M`,
|
||||
or `G`, e.g.
|
||||
.Cm size:10M ,
|
||||
or
|
||||
.Cm size:3G .
|
||||
A value of
|
||||
.Cm size:0
|
||||
disables log rotation. The default is
|
||||
.Cm size:200k .
|
||||
.Pp
|
||||
The count value is recommended to be between 1-5, with a default 5.
|
||||
Setting count to 0 means the logfile will be truncated when the MAX
|
||||
size limit is reached.
|
||||
.It Cm tty Oo LVLS Oc Ao COND Ac Ar DEV Oo BAUD Oc Oo noclear Oc Oo nowait Oc Oo nologin Oc Oo TERM Oc
|
||||
This form of the
|
||||
.Cm tty
|
||||
stanza uses the built-in getty on the given TTY device
|
||||
.Ar DEV ,
|
||||
in the given runlevels.
|
||||
.Ar DEV may be the special keyword
|
||||
.Cm @console ,
|
||||
or `console`, which is expanded from `/sys/class/tty/console/active`,
|
||||
useful on embedded systems.
|
||||
.Pp
|
||||
The default baud rate is 0, i.e., keep kernel default.
|
||||
.Pp
|
||||
The `tty` stanza inherits runlevel, condition (and other feature)
|
||||
parsing from the `service` stanza. So TTYs can run in one or many
|
||||
runlevels and depend on any condition supported by Finit. This is
|
||||
useful e.g. to depend on `<pid/elogind>` before starting a TTY.
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||
.Ed
|
||||
.It Cm tty Oo LVLS Oc Ao COND Ac Ar CMD DEV Oo noclear Oc Oo nowait Oc
|
||||
This form of the
|
||||
.Cm tty
|
||||
stanza is for using an external getty, like agetty or the BusyBox getty.
|
||||
.Pp
|
||||
By default, these first two syntax variants
|
||||
.Em clear
|
||||
the TTY and
|
||||
.Em wait
|
||||
for the user to press enter before starting getty.
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
||||
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm noclear
|
||||
option disables clearing the TTY after each session. Clearing the TTY
|
||||
when a user logs out is usually preferable.
|
||||
.Pp
|
||||
The
|
||||
.Cm nowait
|
||||
option disables the
|
||||
.Cm Please press Enter to activate console
|
||||
message before actually starting the getty program. On small and
|
||||
embedded systems running multiple unused getty wastes both memory and
|
||||
CPU cycles, so `wait` is the preferred default.
|
||||
.Pp
|
||||
The
|
||||
.Cm nologin
|
||||
option disables getty and
|
||||
.Pa /bin/login ,
|
||||
and gives the user a root (login) shell on the given TTY
|
||||
.Cm DEV
|
||||
immediately. Needless to say, this is a rather insecure option, but can
|
||||
be very useful for developer builds, during board bringup, or similar.
|
||||
.Pp
|
||||
Notice the ordering, the
|
||||
.Cm TERM
|
||||
option to the built-in getty must be the last argument.
|
||||
.Pp
|
||||
Embedded systems may want to enable automatic `DEV` by supplying the
|
||||
special
|
||||
.Cm @console
|
||||
device. This works regardless weather the system uses
|
||||
.Cm ttyS0 , ttyAMA0 , ttyMXC0 ,
|
||||
or anything else. Finit figures it out by querying sysfs:
|
||||
.Pa /sys/class/tty/console/active .
|
||||
The speed can be omitted to keep the kernel default.
|
||||
.Pp
|
||||
Most systems get by fine by just using `console`, which will evaluate
|
||||
to
|
||||
.Pa /dev/console .
|
||||
If you have to use
|
||||
.Cm @console
|
||||
to get any output, you may have some issue with your kernel config.
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] @console noclear vt220
|
||||
.Ed
|
||||
.Pp
|
||||
On really bare bones systems, or for board bringup, Finit can give you a
|
||||
shell prompt as soon as bootstrap is done, without opening any device
|
||||
node:
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345789] notty
|
||||
.Ed
|
||||
.Pp
|
||||
This should of course not be enabled on production systems. Because it
|
||||
may give a user root access without having to log in. However, for
|
||||
board bringup and system debugging it can come in handy.
|
||||
.Pp
|
||||
One can also use the
|
||||
.Cm service
|
||||
stanza to start a stand-alone shell:
|
||||
.Bd -unfilled -offset indent
|
||||
service [12345] /bin/sh -l
|
||||
.Ed
|
||||
.Pp
|
||||
.It Cm tty Oo LVLS Oc Ao COND Ac Oo notty Oc Oo rescue Oc
|
||||
The third
|
||||
.Cm tty
|
||||
form is for board bringup and the
|
||||
.Cm rescue
|
||||
boot mode. No device node is required in this variant, the same output
|
||||
that the kernel uses is reused for stdio. If the
|
||||
.Cm rescue
|
||||
option is omitted, a shell is started. The flags
|
||||
.Cm nologin , noclear ,
|
||||
and
|
||||
.Cm nowait
|
||||
are implied. If the
|
||||
.Cm rescue
|
||||
option is set the bundled
|
||||
.Pa /libexec/finit/sulogin
|
||||
is started to present a bare-bones root login prompt. If the root
|
||||
(uid:0, gid:0) user does not have a password set, no rescue is possible.
|
||||
.El
|
||||
.Sh COMMAND MODIFIERS
|
||||
The run/task/tty/service/sysv stanzas take modifiers, or options, to
|
||||
control their behavior. This section lists them with their limitations.
|
||||
All modifiers must be placed between the stanza and its command.
|
||||
.Bl -tag -width 1n
|
||||
.It Cm @user:group
|
||||
Every
|
||||
.Cm run , task ,
|
||||
or
|
||||
.Cm service
|
||||
can also list the privileges the
|
||||
.Cm /path/to/cmd
|
||||
should be executed with. Prefix the command with
|
||||
.Cm @USR[:GRP] ,
|
||||
group is optional, like this:
|
||||
.Bd -unfilled -offset indent
|
||||
run [2345] @joe:users logger "Hello world"
|
||||
.Ed
|
||||
.Pp
|
||||
For multiple instances of the same command, e.g. a DHCP client or
|
||||
multiple web servers, add
|
||||
.Cm :ID
|
||||
somewhere between the
|
||||
.Cm run , task , service
|
||||
keyword and the command, like this:
|
||||
.Bd -unfilled -offset indent
|
||||
service :80 [2345] httpd -f -h /http -p 80 -- Web server
|
||||
service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
|
||||
.Ed
|
||||
.Pp
|
||||
Without the
|
||||
.Cm :ID
|
||||
to the service the latter will overwrite the former and only the old web
|
||||
server would be started and supervised.
|
||||
.It Cm log:/path/to/file
|
||||
Redirect stdout/stderr of a command to the given log file. See the
|
||||
global log directive, above, for details on log rotation.
|
||||
.It Cm log:console
|
||||
Redirect stdout/stderr of a command to
|
||||
.Pa /dev/console ,
|
||||
only use this for debugging or bringup.
|
||||
.It Cm log:null
|
||||
Redirect stdout/stderr of a command to
|
||||
.Pa /dev/null .
|
||||
.It Cm log:prio:facility.level,tag:ident
|
||||
Redirect stdout/stderr of a command to syslog using the given priority
|
||||
and tag identity.
|
||||
.Bd -unfilled -offset indent
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
.Ed
|
||||
.It Cm log
|
||||
Default
|
||||
.Cm prio
|
||||
is
|
||||
.Cm daemon.info
|
||||
and the default
|
||||
.Cm tag
|
||||
identity is the basename of the service or run/task command.
|
||||
.El
|
||||
.Sh RESCUE MODE
|
||||
Finit supports a rescue mode which is activated by the
|
||||
.Cm rescue
|
||||
option on the kernel command line. The rescue mode comes in two
|
||||
flavors:
|
||||
.Em traditional
|
||||
and
|
||||
.Em fallback .
|
||||
.Ss Traditional
|
||||
This is what most users expect. A very early maintenance login prompt,
|
||||
served by the bundled
|
||||
.Pa /libexec/finit/sulogin
|
||||
program, or the standard
|
||||
.Cm sulogin
|
||||
from util-linux or BusyBox is searched for in the UNIX default
|
||||
.Cm $PATH .
|
||||
If a successful login is made, or the user exits (Ctrl-D), the rescue
|
||||
mode is ended and the system boots up normally.
|
||||
.Pp
|
||||
.Sy Note:
|
||||
if the user (UID 0 and GID 0) does not have a password, or
|
||||
.Em the account is locked ,
|
||||
the user is presented with a password-less prompt:
|
||||
.Cm "Press enter to enter maintenance mode." ,
|
||||
which opens up a root shell.
|
||||
.Ss Fallback
|
||||
If no
|
||||
.Cm sulogin
|
||||
program is found, Finit tries to bring up as much of its own
|
||||
functionality as possible, yet limiting many aspects, meaning; no
|
||||
network, no`fsck` of file systems in
|
||||
.Pa /etc/fstab ,
|
||||
no
|
||||
.Pa /etc/rc.local ,
|
||||
no
|
||||
.Cm runparts ,
|
||||
and most plugins are skipped (except those that provide functionality
|
||||
for the condition subsystem).
|
||||
.Pp
|
||||
Instead of reading
|
||||
.Pa /etc/finit.conf
|
||||
et al, system configuration is read from
|
||||
.Pa /lib/finit/rescue.conf ,
|
||||
which can be freely modified by the system administrator.
|
||||
.Pp
|
||||
The bundled default `rescue.conf` contains nothing more than:
|
||||
.Bd -unfilled -offset indent
|
||||
runlevel 1
|
||||
tty [12345] rescue
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm tty
|
||||
has the
|
||||
.Cm rescue
|
||||
option set, which works similar to the board bring-up tty option
|
||||
.Cm notty .
|
||||
The major difference being that `sulogin` is started to query for
|
||||
root/admin password. If
|
||||
.Cm sulogin
|
||||
is not found,
|
||||
.Cm rescue
|
||||
behaves like
|
||||
.Cm notty
|
||||
and gives a plain root shell prompt.
|
||||
.Pp
|
||||
If Finit cannot find
|
||||
.Pa /lib/finit/rescue.conf
|
||||
it defaults to:
|
||||
.Bd -unfilled -offset indent
|
||||
tty [12345] rescue
|
||||
.Ed
|
||||
.Pp
|
||||
There is no way to exit the
|
||||
.Em fallback
|
||||
rescue mode.
|
||||
.Sh SERVICE ENVIRONMENT
|
||||
Finit supports sourcing environment variables from
|
||||
.Pa /etc/default/* ,
|
||||
or similar. This is a common pattern from SysV init scripts, where the
|
||||
start/stop script is a generic script for the given service,
|
||||
.Cm foo ,
|
||||
and the options for the service are sourced from the file
|
||||
.Pa /etc/default/foo .
|
||||
Like this:
|
||||
.Bd -unfilled -offset indent
|
||||
/etc/default/foo:
|
||||
|
||||
FOO_OPTIONS=--extra-arg="bar" -s -x
|
||||
|
||||
/etc/finit.conf:
|
||||
|
||||
service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon
|
||||
.Ed
|
||||
.Pp
|
||||
Here the service
|
||||
.Cm foo
|
||||
is started with
|
||||
.Op Fl -n ,
|
||||
to make sure it runs in the foreground, and the with the options found
|
||||
in the environment file. With the
|
||||
.Cm ps
|
||||
command we can see that the process is started with:
|
||||
.Bd -unfilled -offset indent
|
||||
foo -n --extra-arg=bar -s -x
|
||||
.Ed
|
||||
.Pp
|
||||
.Sy Note:
|
||||
the leading `-` determines if Finit should treat a missing environment
|
||||
file as blocking the start of the service or not. When `-` is used, a
|
||||
missing environment file does
|
||||
.Em not
|
||||
block the start.
|
||||
.Sh SERVICE WRAPPER SCRIPTS
|
||||
If your service requires to run additional commands, executed before the
|
||||
service is actually started, like the systemd `ExecStartPre`, you can
|
||||
use a wrapper shell script to start your service.
|
||||
.Pp
|
||||
The Finit service
|
||||
.Cm .conf
|
||||
file can be put into
|
||||
.Pa /etc/finit.d/available ,
|
||||
so you can control the service using
|
||||
.Cm initctl .
|
||||
Then use the path to the wrapper script in the Finit
|
||||
.Cm .conf
|
||||
service stanza. The following example employs a wrapper script in
|
||||
.Pa /etc/start.d .
|
||||
.Bd -unfilled
|
||||
/etc/finit.d/available/program.conf:
|
||||
|
||||
service [235] <!> /etc/start.d/program -- Example Program
|
||||
|
||||
/etc/start.d/program:
|
||||
|
||||
#!/bin/sh
|
||||
# Prepare the command line options
|
||||
OPTIONS="-u $(cat /etc/username)"
|
||||
|
||||
# Execute the program
|
||||
exec /usr/bin/program $OPTIONS
|
||||
.Ed
|
||||
.Pp
|
||||
.Sy Note:
|
||||
the example sets
|
||||
.Cm <!>
|
||||
to denote that it doesn't support SIGHUP. That way Finit will
|
||||
stop/start the service instead of sending SIGHUP at restart/reload
|
||||
events.
|
||||
.Sh 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
|
||||
.Bl -enum -offset indent -compact
|
||||
.It
|
||||
Global top-level group: init, system, user, or a custom group
|
||||
.It
|
||||
Selecting a top-level group for a set of run/task/services
|
||||
.It
|
||||
Per run/task/service limits
|
||||
.El
|
||||
.Pp
|
||||
Top-level group configuration.
|
||||
.Bd -unfilled -offset indent
|
||||
# Top-level cgroups and their default settings. All groups mandatory
|
||||
# but more can be added, max 8 groups in total currently. The cgroup
|
||||
# 'root' is also available, reserved for RT processes. Settings are
|
||||
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||
# cgroup v2 controller default names.
|
||||
cgroup init cpu.weight:100
|
||||
cgroup user cpu.weight:100
|
||||
cgroup system cpu.weight:9800
|
||||
.Ed
|
||||
.Pp
|
||||
Adding an extra cgroup
|
||||
.Cm maint/
|
||||
will require you to adjust the weight of the above three. We leave
|
||||
.Cm init/
|
||||
and
|
||||
.Cm user/
|
||||
as-is reducing weight of
|
||||
.Cm system/
|
||||
to 9700.
|
||||
.Bd -unfilled -offset indent
|
||||
cgroup system cpu.weight:9700
|
||||
|
||||
# Example extra cgroup 'maint'
|
||||
cgroup maint cpu.weight:100
|
||||
.Ed
|
||||
.Pp
|
||||
By default, the
|
||||
.Cm system/
|
||||
cgroup is selected for almost everything. The
|
||||
.Cm init/
|
||||
cgroup is reserved for PID 1 itself and its closest relatives. The
|
||||
.Cm user/
|
||||
cgroup is for local TTY logins spawned by getty.
|
||||
.Pp
|
||||
To select a different top-level cgroup, e.g.
|
||||
.Cm maint/ ,
|
||||
one can either define it for a group of run/task/service directives in a
|
||||
.Cm .conf
|
||||
or per each stanza:
|
||||
.Bd -unfilled -offset indent
|
||||
cgroup.maint
|
||||
service [...] <...> /path/to/foo args -- description
|
||||
service [...] <...> /path/to/bar args -- description
|
||||
.Ed
|
||||
.Pp
|
||||
or
|
||||
.Bd -unfilled -offset indent
|
||||
service [...] <...> cgroup.maint /path/to/foo args -- description
|
||||
.Ed
|
||||
.Pp
|
||||
The latter form also allows per-stanza limits on the form:
|
||||
.Bd -unfilled -offset indent
|
||||
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||
.Ed
|
||||
.Pp
|
||||
Notice the comma separation and the
|
||||
.Cm mem.
|
||||
exception to the rule: every cgroup setting maps directly to cgroup v2
|
||||
syntax. I.e.,
|
||||
.Cm cpu.max
|
||||
maps to the file
|
||||
.Pm /sys/fs/cgroup/maint/foo/cpu.max .
|
||||
There is no filtering, except for expanding the shorthand
|
||||
.Cm mem.
|
||||
to
|
||||
.Cm memory. ,
|
||||
if the file is not available, either the cgroup controller is not
|
||||
available in your Linux kernel, or the name is misspelled.
|
||||
.Pp
|
||||
Linux cgroups and details surrounding values are not explained in the
|
||||
Finit documentation. The Linux admin-guide cover this well:
|
||||
.Lk https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
|
||||
.Sh LIMITATIONS
|
||||
As of Finit v4 there are no limitations to where
|
||||
.Cm .conf
|
||||
settings can be placed. Except for the system/global
|
||||
.Cm rlimit
|
||||
and
|
||||
.Cm cgroup
|
||||
top-level group declarations, which can only be set from
|
||||
.Pa /etc/finit.conf ,
|
||||
since it is the first
|
||||
.Cm .conf
|
||||
file Finit reads.
|
||||
.Pp
|
||||
Originally,
|
||||
.Pp /etc/finit.conf
|
||||
was the only way to set up a Finit system. Today it is mainly used for
|
||||
bootstrap settings like system hostname, early module loading for
|
||||
watchdogd, network bringup and system shutdown. These can now also be
|
||||
set in any
|
||||
.Cm .conf
|
||||
file in
|
||||
.Pa /etc/finit.d .
|
||||
.Pp
|
||||
There is, however, nothing preventing you from having all configuration
|
||||
settings in
|
||||
.Pa /etc/finit.conf .
|
||||
.Sh SEE ALSO
|
||||
.Xr finit 8 ,
|
||||
.Xr initctl 8
|
||||
.Sh AUTHORS
|
||||
.Nm Finit
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
maintained by Joachim Wiberg, with contributions by many others.
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.Dd June 6, 2021
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.Dt INITCTL 8 SMM
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
.Nm initctl
|
||||
.Nd Control tool for Finit
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/initctl
|
||||
.Op Fl bcfhjlpqtvV
|
||||
.Op COMMAND
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the official tool for interacting with
|
||||
.Xr finit 8 .
|
||||
It comes with functions for showing status, querying health of services,
|
||||
as well as editing, enabling and disabling services.
|
||||
.Sh OPTIONS
|
||||
This program follows the usual UNIX command line syntax, with long
|
||||
options starting with two dashes (`-'). The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl b, -batch
|
||||
Batch mode, no screen size probing. Useful for scripting.
|
||||
.It Fl c, -create
|
||||
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
|
||||
Use plain table headings, no ANSI control characters.
|
||||
.It Fl q, -quiet
|
||||
Silent, only return status of command.
|
||||
.It Fl t, -no-heading
|
||||
Skip table headings.
|
||||
.It Fl v, -verbose
|
||||
Verbose output, where applicable.
|
||||
.It Fl V, -version
|
||||
Show program version.
|
||||
.El
|
||||
.Sh COMMANDS
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ar debug
|
||||
Toggle
|
||||
.Nm finit
|
||||
(daemon) debug to
|
||||
.Pa /dev/console
|
||||
.It Nm Ar help
|
||||
Show built-in help text.
|
||||
.It Nm Ar version
|
||||
Show program version.
|
||||
.It Nm Ar ls | list
|
||||
List all
|
||||
.Cm *.conf
|
||||
files in
|
||||
.Pa /etc/finit.d
|
||||
.It Nm Ar create Ar CONF
|
||||
Create
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available .
|
||||
.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
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.It Nm Ar show Ar CONF
|
||||
Show
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.It Nm Ar edit Ar CONF
|
||||
Edit
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available
|
||||
.It Nm Ar touch Ar CONF
|
||||
Change (mark as modified)
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available ,
|
||||
like
|
||||
.Ar edit
|
||||
this tells
|
||||
.Nm finit
|
||||
that a configuration has been changed and is a candidate to be restarted
|
||||
(or SIGHUP'ed) on
|
||||
.Cm initctl reload
|
||||
.It Nm Ar enable Ar CONF
|
||||
Enable
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/available ,
|
||||
i.e., add symlink in
|
||||
.Pa /etc/finit.d/enabled
|
||||
.It Nm Ar disable Ar CONF
|
||||
Disable
|
||||
.Cm .conf
|
||||
in
|
||||
.Pa /etc/finit.d/enabled ,
|
||||
i.e., removes symlink.
|
||||
.It Nm Ar reload
|
||||
Reload
|
||||
.Cm *.conf in
|
||||
.Pa /etc/finit.d ,
|
||||
i.e., activates changes.
|
||||
.It Nm Ar cond set Ar COND Op COND ...
|
||||
Set (assert) user-defined condition,
|
||||
.Cm +usr/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. 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.
|
||||
.It Nm Ar log Op Cm NAME
|
||||
Show ten last Finit, or
|
||||
.Cm NAME ,
|
||||
messages from syslog.
|
||||
.It Nm Ar start Cm NAME[:ID]
|
||||
Start service by name, with optional ID, e.g.,
|
||||
.Cm initctl start tty:1
|
||||
.It Nm Ar stop Cm NAME[:ID]
|
||||
Stop/Pause a running service by name.
|
||||
.It Nm Ar reload Cm NAME[:ID]
|
||||
Reload service 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.
|
||||
.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 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. Also supports the
|
||||
.Fl j
|
||||
option for detailed JSON output.
|
||||
.It Nm Ar cgroup
|
||||
List cgroup config overview.
|
||||
.It Nm Ar ps
|
||||
List processes based on cgroups.
|
||||
.It Nm Ar top
|
||||
Show top-like listing based on cgroups.
|
||||
.It Nm Ar plugins
|
||||
List installed plugins.
|
||||
.It Nm Ar runlevel Op Ar 0-9
|
||||
Show or set runlevel: 0 halt, 6 reboot.
|
||||
.It Nm Ar reboot
|
||||
Reboot system, default if
|
||||
.Cm reboot
|
||||
is symlinked to
|
||||
.Nm .
|
||||
.It Nm Ar halt
|
||||
Halt system, default if
|
||||
.Cm halt
|
||||
is symlinked to
|
||||
.Nm .
|
||||
.It Nm Ar poweroff
|
||||
Power-off system, default if
|
||||
.Cm poweroff
|
||||
is symlinked to
|
||||
.Nm .
|
||||
.It Nm Ar suspend
|
||||
Suspend system, default if
|
||||
.Cm suspend
|
||||
is symlinked to
|
||||
.Nm .
|
||||
.It Nm Ar utmp show
|
||||
Raw dump of UTMP/WTMP db.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr finit.conf 5
|
||||
.Xr finit 8
|
||||
.Sh AUTHORS
|
||||
.Nm finit
|
||||
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
|
||||
maintained by Joachim Wiberg, with contributions by many others.
|
||||
+42
-10
@@ -1,13 +1,13 @@
|
||||
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)
|
||||
AM_CPPFLAGS += -D__FINIT__ $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
|
||||
libplug_la_SOURCES = bootmisc.c pidfile.c procps.c sys.c usr.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
@@ -17,14 +17,14 @@ 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
|
||||
|
||||
if BUILD_MODPROBE_PLUGIN
|
||||
libplug_la_SOURCES += modprobe.c
|
||||
endif
|
||||
|
||||
if BUILD_NETLINK_PLUGIN
|
||||
libplug_la_SOURCES += netlink.c
|
||||
endif
|
||||
@@ -33,8 +33,24 @@ if BUILD_RESOLVCONF_PLUGIN
|
||||
libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
if BUILD_RTC_PLUGIN
|
||||
libplug_la_SOURCES += rtc.c
|
||||
endif
|
||||
|
||||
if BUILD_TTY_PLUGIN
|
||||
libplug_la_SOURCES += tty.c
|
||||
endif
|
||||
|
||||
if BUILD_URANDOM_PLUGIN
|
||||
libplug_la_SOURCES += urandom.c
|
||||
endif
|
||||
|
||||
if BUILD_X11_COMMON_PLUGIN
|
||||
libplug_la_SOURCES += x11-common.c
|
||||
endif
|
||||
|
||||
else
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la
|
||||
pkglib_LTLIBRARIES = bootmisc.la pidfile.la procps.la sys.la usr.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
@@ -44,14 +60,18 @@ if BUILD_DBUS_PLUGIN
|
||||
pkglib_LTLIBRARIES += dbus.la
|
||||
endif
|
||||
|
||||
if BUILD_HOTPLUG_PLUGIN
|
||||
pkglib_LTLIBRARIES += hotplug.la
|
||||
if BUILD_HOOK_SCRIPTS_PLUGIN
|
||||
pkglib_LTLIBRARIES += hook-scripts.la
|
||||
endif
|
||||
|
||||
if BUILD_MODULES_LOAD_PLUGIN
|
||||
pkglib_LTLIBRARIES += modules-load.la
|
||||
endif
|
||||
|
||||
if BUILD_MODPROBE_PLUGIN
|
||||
pkglib_LTLIBRARIES += modprobe.la
|
||||
endif
|
||||
|
||||
if BUILD_NETLINK_PLUGIN
|
||||
pkglib_LTLIBRARIES += netlink.la
|
||||
endif
|
||||
@@ -60,6 +80,18 @@ if BUILD_RESOLVCONF_PLUGIN
|
||||
pkglib_LTLIBRARIES += resolvconf.la
|
||||
endif
|
||||
|
||||
if BUILD_RTC_PLUGIN
|
||||
pkglib_LTLIBRARIES += rtc.la
|
||||
endif
|
||||
|
||||
if BUILD_TTY_PLUGIN
|
||||
pkglib_LTLIBRARIES += tty.la
|
||||
endif
|
||||
|
||||
if BUILD_URANDOM_PLUGIN
|
||||
pkglib_LTLIBRARIES += urandom.la
|
||||
endif
|
||||
|
||||
if BUILD_X11_COMMON_PLUGIN
|
||||
pkglib_LTLIBRARIES += x11-common.la
|
||||
endif
|
||||
|
||||
+11
-6
@@ -1,6 +1,6 @@
|
||||
/* Save and restore ALSA sound settings using alsactl
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,7 +21,12 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
@@ -31,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");
|
||||
}
|
||||
}
|
||||
@@ -44,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");
|
||||
}
|
||||
}
|
||||
|
||||
+59
-74
@@ -1,6 +1,6 @@
|
||||
/* Setup necessary system files for, e.g. UTMP (tracking logins)
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,24 +21,35 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ftw.h>
|
||||
#ifdef HAVE_MNTENT_H
|
||||
#include <mntent.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "tmpfiles.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
static int is_tmpfs(char *path)
|
||||
{
|
||||
struct mntent mount;
|
||||
struct mntent *mnt;
|
||||
char buf[256];
|
||||
int tmpfs = 0;
|
||||
FILE *fp;
|
||||
char *dir;
|
||||
struct mntent *mnt;
|
||||
|
||||
/* If path is a symlink, check what it resolves to */
|
||||
dir = realpath(path, NULL);
|
||||
@@ -51,7 +62,7 @@ static int is_tmpfs(char *path)
|
||||
return 0; /* Dunno, maybe not */
|
||||
}
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) {
|
||||
if (!strcmp(dir, mnt->mnt_dir))
|
||||
break;
|
||||
}
|
||||
@@ -70,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;
|
||||
@@ -100,6 +111,45 @@ static void clean(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP
|
||||
*/
|
||||
@@ -109,80 +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 ...");
|
||||
makedir("/run/lock", 1777);
|
||||
symlink("/run/lock", "/var/lock");
|
||||
symlink("/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 */
|
||||
symlink("/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"))
|
||||
symlink("../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");
|
||||
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
|
||||
+110
-24
@@ -1,6 +1,6 @@
|
||||
/* Setup and start system message bus, D-Bus
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -22,7 +22,11 @@
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "config.h"
|
||||
@@ -31,54 +35,136 @@
|
||||
#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"
|
||||
|
||||
#define DAEMONUSER "messagebus"
|
||||
#ifndef DBUS_DAEMONUSER
|
||||
#define DBUS_DAEMONUSER "messagebus"
|
||||
#endif
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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 (getgroup(group) == -1) {
|
||||
if (getgroup("dbus") == -1)
|
||||
group = "root"; /* fallback */
|
||||
else
|
||||
group = "dbus"; /* e.g., Buildroot */
|
||||
}
|
||||
|
||||
/* Clean up from any previous pre-bootstrap run */
|
||||
pidfn = dbus_pidfn();
|
||||
if (pidfn)
|
||||
remove(pidfn);
|
||||
|
||||
dbg("Creating D-Bus Required Directories ...");
|
||||
prev = umask(0);
|
||||
mksubsys("/var/run/dbus", 0755, user, group);
|
||||
mksubsys("/var/run/lock/subsys", 0755, user, group);
|
||||
mksubsys("/var/lib/dbus", 0755, user, group);
|
||||
mksubsys("/tmp/dbus", 0755, user, group);
|
||||
umask(prev);
|
||||
|
||||
/* Generate machine id for dbus */
|
||||
if (whichp("dbus-uuidgen"))
|
||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||
|
||||
/* Clean up from any previous pre-bootstrap run */
|
||||
remove("/var/run/dbus/pid");
|
||||
/*
|
||||
* Register service with Finit
|
||||
* Note: dbus drops privs after starting up.
|
||||
*/
|
||||
if (pidfn) {
|
||||
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus pid:!%s %s %s -- %s",
|
||||
pidfn, cmd, DBUS_ARGS, DBUS_DESC);
|
||||
free(pidfn);
|
||||
} else
|
||||
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus %s %s -- %s",
|
||||
cmd, DBUS_ARGS, DBUS_DESC);
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345789] @%s:%s %s %s -- %s",
|
||||
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
err(1, "Failed registering %s", DBUS_DAEMON);
|
||||
|
||||
umask(prev);
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/* Trigger scripts to run from finit's hooks
|
||||
*
|
||||
* Copyright (c) 2022 Tobias Waldekranz <tobias@waldekranz.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 "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "config.h"
|
||||
|
||||
extern void plugin_script_run(hook_point_t no);
|
||||
|
||||
static void hscript_run_parts(hook_point_t no)
|
||||
{
|
||||
plugin_script_run(no);
|
||||
}
|
||||
|
||||
static void hscript_banner(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_BANNER);
|
||||
}
|
||||
|
||||
static void hscript_rootfs_up(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_ROOTFS_UP);
|
||||
}
|
||||
|
||||
static void hscript_mount_error(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_MOUNT_ERROR);
|
||||
}
|
||||
|
||||
static void hscript_mount_post(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_MOUNT_POST);
|
||||
}
|
||||
|
||||
static void hscript_basefs_up(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_BASEFS_UP);
|
||||
}
|
||||
|
||||
static void hscript_network_up(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_NETWORK_UP);
|
||||
}
|
||||
|
||||
static void hscript_svc_up(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_SVC_UP);
|
||||
}
|
||||
|
||||
static void hscript_system_up(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_SYSTEM_UP);
|
||||
}
|
||||
|
||||
static void hscript_shutdown(void *arg)
|
||||
{
|
||||
hscript_run_parts(HOOK_SHUTDOWN);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BANNER] = { .cb = hscript_banner },
|
||||
.hook[HOOK_ROOTFS_UP] = { .cb = hscript_rootfs_up },
|
||||
.hook[HOOK_MOUNT_ERROR] = { .cb = hscript_mount_error },
|
||||
.hook[HOOK_MOUNT_POST] = { .cb = hscript_mount_post },
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = hscript_basefs_up },
|
||||
.hook[HOOK_NETWORK_UP] = { .cb = hscript_network_up },
|
||||
.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)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -1,116 +0,0 @@
|
||||
/* Heuristically find and initialize a suitable hotplug daemon
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <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] pid:udevd name:udevd log %s "
|
||||
"-- Device event managing daemon", path);
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
||||
_pe("Failed registering %s", path);
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), ":1 [S] <pid/udevd> log "
|
||||
"udevadm trigger -c add -t devices "
|
||||
"-- Requesting device events");
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
||||
|
||||
snprintf(cmd, sizeof(cmd), ":2 [S] <pid/udevd> log "
|
||||
"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:
|
||||
*/
|
||||
+79
-15
@@ -1,6 +1,6 @@
|
||||
/* Coldplug modules using modalias magic
|
||||
*
|
||||
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -40,11 +40,15 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h> /* gettimeofday() */
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
#include "plugin.h"
|
||||
|
||||
struct module {
|
||||
@@ -67,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;
|
||||
}
|
||||
|
||||
@@ -102,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)
|
||||
{
|
||||
@@ -116,27 +120,87 @@ 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 = rindex(path, '/');
|
||||
if (!basename)
|
||||
return NULL;
|
||||
|
||||
basename++;
|
||||
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;
|
||||
|
||||
+169
-44
@@ -21,10 +21,37 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
/*
|
||||
* This plugin scans files in /etc/modules-load.d/, for each line in the
|
||||
* file it assumes the name of a kernel module, with optional arguments.
|
||||
* Each module is loaded when entering runlevel 2, 3, 4, or 5, using the
|
||||
* modprobe tool.
|
||||
*
|
||||
* Loading is done by inserting a `task` stanza in Finit, ensuring all
|
||||
* modules are loaded in parallel, with other modules and programs.
|
||||
* Each `task` stanza is by default given the name:modprobe.module and
|
||||
* indexed with `:ID`, starting with 1.
|
||||
*
|
||||
* Indexing can be disabled per file in /etc/modules-load.d/, anywwhere
|
||||
* in a file, by putting the keyword `noindex` on a line of its own. A
|
||||
* noindex command can appear anywhere in the file, and up to the point
|
||||
* it is read, tasks will be indexed with an incrementing `:ID`. When
|
||||
* `noindex` is read indexing is disabled and all subseequent tasks will
|
||||
* not have any `:ID` at all.
|
||||
*
|
||||
* Please note, the :ID is there for your benefit, it ensures that tasks
|
||||
* in Finit are unique. If you have two tasks with the same name and ID
|
||||
* (or no ID), the last one read replaces any preceeding one!
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -35,60 +62,158 @@
|
||||
#ifndef MODULES_LOAD_PATH
|
||||
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||
#endif
|
||||
#define MODPROBE_PATH "/sbin/modprobe"
|
||||
#define SERVICE_LINE \
|
||||
":%d name:modprobe.%s [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 [%s] %s %s %s --"
|
||||
|
||||
static int modules_load(const char *file, int index)
|
||||
{
|
||||
char module_path[PATH_MAX];
|
||||
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) {
|
||||
warnx("failed opening %s for reading, skipping ...", module_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
modprobe_path = strdup(MODPROBE_PATH);
|
||||
if (!modprobe_path) {
|
||||
fail:
|
||||
warnx("failed allocating memory in modules-load plugin.");
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lvl = strdup("S");
|
||||
if (!lvl) {
|
||||
free(modprobe_path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
|
||||
char cmd[CMD_SIZE * 2], *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])
|
||||
goto next;
|
||||
|
||||
/* Finit extension 'set foo bar' */
|
||||
if ((set = fgetval(mod, "set", "= \t"))) {
|
||||
char *val = mod;
|
||||
|
||||
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)
|
||||
goto next;
|
||||
|
||||
if (!index)
|
||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, lvl, modprobe_path, mod, args);
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
/* XXX: check here for .conf only? */
|
||||
static int module_filter(const struct dirent *d)
|
||||
{
|
||||
if (d->d_type == DT_DIR)
|
||||
return 0;
|
||||
if (d->d_name[0] == '.')
|
||||
return 0; /* skip dot files */
|
||||
if (d->d_name[strlen(d->d_name) - 1] == '~')
|
||||
return 0; /* skip backup files */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void load(void *arg)
|
||||
{
|
||||
FILE *fp;
|
||||
DIR *dirp;
|
||||
struct dirent *direntp;
|
||||
char line[256];
|
||||
struct dirent **dentry;
|
||||
int index = 1;
|
||||
int i, num;
|
||||
|
||||
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
|
||||
|
||||
dirp = opendir(MODULES_LOAD_PATH);
|
||||
if (!dirp) return;
|
||||
|
||||
while ((direntp = readdir(dirp)))
|
||||
{
|
||||
char module_path[PATH_MAX];
|
||||
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
||||
strlcat(module_path, direntp->d_name, sizeof(module_path));
|
||||
|
||||
fp = fopen(module_path, "r");
|
||||
if (!fp) continue;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
char cmd[CMD_SIZE], *mod, *args;
|
||||
|
||||
mod = strip_line(line);
|
||||
if (!*mod) continue;
|
||||
|
||||
mod = chomp(mod);
|
||||
if (!mod || !*mod) continue;
|
||||
|
||||
mod = strtok_r(mod, " ", &args);
|
||||
|
||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
|
||||
index, mod, mod, args, mod);
|
||||
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
||||
|
||||
index++;
|
||||
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 += modules_load(dentry[i]->d_name, index);
|
||||
free(dentry[i]);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(dentry);
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = load
|
||||
},
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_SVC_PLUGIN] = { .cb = load },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+215
-125
@@ -1,7 +1,8 @@
|
||||
|
||||
/* Netlink plugin for IFUP/IFDN and GW events
|
||||
*
|
||||
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
|
||||
* Copyright (C) 2009-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (C) 2009-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -30,27 +31,22 @@
|
||||
#include "cond.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
|
||||
static int ifdown = 0;
|
||||
#define NL_BUFSZ 4096
|
||||
|
||||
struct nl_request {
|
||||
struct nlmsghdr nh;
|
||||
union {
|
||||
struct rtmsg rtm;
|
||||
struct ifinfomsg ifi;
|
||||
};
|
||||
};
|
||||
|
||||
static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
|
||||
{
|
||||
if (!NLMSG_OK(nh, len))
|
||||
return 1;
|
||||
static int nl_defidx;
|
||||
static int nl_ifdown;
|
||||
static char *nl_buf;
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_DONE) {
|
||||
_d("Done with netlink messages.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_ERROR) {
|
||||
_d("Netlink reports error.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
@@ -66,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;
|
||||
}
|
||||
|
||||
@@ -74,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;
|
||||
}
|
||||
|
||||
@@ -84,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;
|
||||
}
|
||||
|
||||
@@ -106,44 +102,17 @@ 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)
|
||||
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
|
||||
cond_clear("net/route/default");
|
||||
else
|
||||
nl_defidx = 0;
|
||||
} else {
|
||||
cond_set("net/route/default");
|
||||
}
|
||||
}
|
||||
|
||||
static int nl_default(struct nlmsghdr *nlh, struct in_addr *dst, struct in_addr *gw)
|
||||
{
|
||||
struct in_addr nil = { 0 };
|
||||
struct rtattr *a;
|
||||
struct rtmsg *r;
|
||||
int len;
|
||||
|
||||
r = (struct rtmsg *)NLMSG_DATA(nlh);
|
||||
if ((r->rtm_family != AF_INET) || (r->rtm_table != RT_TABLE_MAIN))
|
||||
return -1;
|
||||
|
||||
len = RTM_PAYLOAD(nlh);
|
||||
for (a = RTM_RTA(r); RTA_OK(a, len); a = RTA_NEXT(a, len)) {
|
||||
switch (a->rta_type) {
|
||||
case RTA_GATEWAY:
|
||||
memcpy(gw, RTA_DATA(a), sizeof(*gw));
|
||||
break;
|
||||
|
||||
case RTA_DST:
|
||||
memcpy(dst, RTA_DATA(a), sizeof(*dst));
|
||||
break;
|
||||
nl_defidx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (!memcmp(dst, &nil, sizeof(nil)) && memcmp(gw, &nil, sizeof(nil)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void net_cond_set(char *ifname, char *cond, int set)
|
||||
@@ -177,6 +146,19 @@ static int validate_ifname(const char *ifname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if this interface was associated with the default route
|
||||
* previously, or if it's been removed. If so, trigger a recheck
|
||||
* of the system default route.
|
||||
*/
|
||||
static void nl_check_default(char *ifname)
|
||||
{
|
||||
int idx = (int)if_nametoindex(ifname);
|
||||
|
||||
if ((nl_defidx > 0 && nl_defidx == idx) || (idx == 0 && errno == ENODEV))
|
||||
nl_ifdown = 1;
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
@@ -185,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;
|
||||
}
|
||||
|
||||
@@ -193,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;
|
||||
}
|
||||
|
||||
@@ -203,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;
|
||||
}
|
||||
|
||||
@@ -213,108 +195,208 @@ 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);
|
||||
if (!(i->ifi_flags & IFF_UP) || !(i->ifi_flags & IFF_RUNNING))
|
||||
ifdown = 1;
|
||||
nl_check_default(ifname);
|
||||
break;
|
||||
|
||||
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);
|
||||
ifdown = 1;
|
||||
nl_check_default(ifname);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nl_callback(void *arg, int sd, int events)
|
||||
static int nl_parse(int sd)
|
||||
{
|
||||
static char buf[4096];
|
||||
struct nlmsghdr *nh;
|
||||
ssize_t len;
|
||||
while (1) {
|
||||
struct nlmsghdr *nh;
|
||||
ssize_t len;
|
||||
size_t l;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
len = recv(sd, buf, sizeof(buf), 0);
|
||||
if (len < 0) {
|
||||
if (errno != EINTR) /* Signal */
|
||||
_pe("recv()");
|
||||
while ((len = recv(sd, nl_buf, NL_BUFSZ, 0)) < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN: /* Nothing more right now. */
|
||||
return 0;
|
||||
|
||||
case EINTR: /* Signal */
|
||||
continue;
|
||||
|
||||
case ENOBUFS: /* netlink(7) */
|
||||
break;
|
||||
|
||||
default:
|
||||
err(1, "recv()");
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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:
|
||||
// dbg("Done with netlink messages.");
|
||||
return 0;
|
||||
|
||||
case NLMSG_ERROR:
|
||||
// dbg("Kernel netlink comm. error.");
|
||||
nle = NLMSG_DATA(nh);
|
||||
if (nle) {
|
||||
errno = -nle->error;
|
||||
err(1, "Kernel netlink error %d", errno);
|
||||
}
|
||||
return -1;
|
||||
|
||||
case RTM_NEWROUTE:
|
||||
case RTM_DELROUTE:
|
||||
// dbg("Netlink route ...");
|
||||
nl_route(nh, len);
|
||||
break;
|
||||
|
||||
case RTM_NEWLINK:
|
||||
case RTM_DELLINK:
|
||||
// dbg("Netlink link ...");
|
||||
nl_link(nh, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
warnx("unhandled netlink message, type %d", nh->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int nl_request(int sd, unsigned int seq, int type)
|
||||
{
|
||||
struct nl_request *nlr = (struct nl_request *)nl_buf;
|
||||
|
||||
memset(nlr, 0, sizeof(struct nl_request));
|
||||
nlr->nh.nlmsg_type = type;
|
||||
nlr->nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||||
nlr->nh.nlmsg_seq = seq;
|
||||
nlr->nh.nlmsg_pid = 1;
|
||||
|
||||
switch (type) {
|
||||
case RTM_GETROUTE:
|
||||
// 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:
|
||||
// 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:
|
||||
warnx("Cannot resync, unhandled message type %d", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (send(sd, nlr, nlr->nh.nlmsg_len, 0) < 0)
|
||||
return 1;
|
||||
|
||||
return nl_parse(sd);
|
||||
}
|
||||
|
||||
static void nl_resync_routes(int sd, unsigned int seq)
|
||||
{
|
||||
if (nl_request(sd, seq, RTM_GETROUTE))
|
||||
err(1, "Failed netlink route request");
|
||||
}
|
||||
|
||||
static void nl_resync_ifaces(int sd, unsigned int seq)
|
||||
{
|
||||
if (nl_request(sd, seq, RTM_GETLINK))
|
||||
err(1, "Failed netlink link request");
|
||||
}
|
||||
|
||||
/*
|
||||
* We've potentially lost netlink events, let's resync with kernel.
|
||||
*/
|
||||
static void nl_resync(int all)
|
||||
{
|
||||
unsigned int seq = 0;
|
||||
int sd;
|
||||
|
||||
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||||
if (sd < 0) {
|
||||
err(1, "netlink socket");
|
||||
return;
|
||||
}
|
||||
|
||||
/* check for interface changes -> loss of default route */
|
||||
ifdown = 0;
|
||||
if (all) {
|
||||
dbg("============================ RESYNC =================================");
|
||||
/* this doesn't update conditions, and thus does not stop services */
|
||||
cond_deassert("net/");
|
||||
|
||||
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
|
||||
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
|
||||
nl_route(nh, len);
|
||||
else
|
||||
nl_link(nh, len);
|
||||
nl_resync_ifaces(sd, seq++);
|
||||
nl_resync_routes(sd, seq++);
|
||||
|
||||
/* delayed update after we've corrected things */
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
dbg("=========================== RESYNCED ================================");
|
||||
} else
|
||||
nl_resync_routes(sd, seq++);
|
||||
|
||||
close(sd);
|
||||
}
|
||||
|
||||
static void nl_callback(void *arg, int sd, int events)
|
||||
{
|
||||
if (nl_parse(sd) < 0) {
|
||||
if (errno == ENOBUFS) { /* netlink(7) */
|
||||
warnx("busy system, resynchronizing with kernel.");
|
||||
nl_resync(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux doesn't send route changes, when interfaces go down, so
|
||||
* Linux doesn't send route changes when interfaces go down, so
|
||||
* we need to check ourselves, e.g. for loss of default route.
|
||||
*/
|
||||
if (ifdown) {
|
||||
unsigned int seq = 0;
|
||||
int found = 0;
|
||||
|
||||
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||||
if (sd < 0) {
|
||||
_pe("netlink socket");
|
||||
return;
|
||||
if (nl_ifdown) {
|
||||
dbg("interface down, checking default route.");
|
||||
if (nl_defidx > 0) {
|
||||
nl_defidx = 0;
|
||||
nl_resync(0);
|
||||
if (nl_defidx <= 0) {
|
||||
cond_clear("net/route/default");
|
||||
nl_defidx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
nh = (struct nlmsghdr *)buf;
|
||||
nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
nh->nlmsg_type = RTM_GETROUTE;
|
||||
nh->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||||
nh->nlmsg_seq = seq++;
|
||||
nh->nlmsg_pid = 1;
|
||||
|
||||
if (send(sd, nh, nh->nlmsg_len, 0) < 0) {
|
||||
_pe("Failed netlink route request");
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
|
||||
len = recv(sd, buf, sizeof(buf), 0);
|
||||
close(sd);
|
||||
|
||||
for (; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
struct in_addr dst = { 0 };
|
||||
struct in_addr gw = { 0 };
|
||||
|
||||
if (nl_default(nh, &dst, &gw))
|
||||
continue;
|
||||
|
||||
_d("default via %s\n", inet_ntoa(gw));
|
||||
found = 1;
|
||||
}
|
||||
|
||||
/* We already get notification when adding default rt */
|
||||
if (!found)
|
||||
cond_clear("net/route/default");
|
||||
nl_ifdown = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,17 +421,24 @@ 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;
|
||||
}
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.nl_family = AF_NETLINK;
|
||||
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; // | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR;
|
||||
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK;
|
||||
sa.nl_pid = getpid();
|
||||
|
||||
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
_pe("bind()");
|
||||
err(1, "bind()");
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
|
||||
nl_buf = malloc(NL_BUFSZ);
|
||||
if (!nl_buf) {
|
||||
err(1, "malloc()");
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
@@ -361,6 +450,7 @@ PLUGIN_INIT(plugin_init)
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
close(plugin.io.fd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+52
-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-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* 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,41 +71,63 @@ 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->name, fn, svc->pid);
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
|
||||
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
if (!svc_has_pidfile(svc))
|
||||
/*
|
||||
* only mark native services as started, the s6 and
|
||||
* systemd style services rely on their respective
|
||||
* readiness notification. Issue #343
|
||||
*/
|
||||
if (!svc->notify)
|
||||
svc_started(svc);
|
||||
|
||||
if (!svc_has_pidfile(svc)) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Service is not crashing, let's tell state machine! */
|
||||
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 */
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Started %s[%d]", svc_ident(svc, NULL, 0), pid);
|
||||
}
|
||||
}
|
||||
|
||||
cond_set(cond);
|
||||
} else if (mask & IN_DELETE)
|
||||
if (!svc->notify)
|
||||
service_ready(svc);
|
||||
} else if (mask & IN_DELETE) {
|
||||
cond_clear(cond);
|
||||
if (!svc->notify)
|
||||
service_ready(svc);
|
||||
}
|
||||
}
|
||||
|
||||
/* synthesize events in case of new run dirs */
|
||||
@@ -127,7 +149,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);
|
||||
@@ -142,7 +164,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);
|
||||
|
||||
@@ -166,7 +188,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;
|
||||
@@ -218,6 +240,9 @@ static void pidfile_reconf(void *arg)
|
||||
if (svc_is_changed(svc) || svc_is_starting(svc))
|
||||
continue;
|
||||
|
||||
if (!svc->notify)
|
||||
service_ready(svc);
|
||||
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
if (cond_get(cond) == COND_ON)
|
||||
continue;
|
||||
@@ -226,9 +251,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);
|
||||
@@ -240,7 +265,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;
|
||||
}
|
||||
|
||||
@@ -251,10 +276,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;
|
||||
}
|
||||
}
|
||||
@@ -275,15 +300,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)
|
||||
@@ -303,8 +328,8 @@ PLUGIN_INIT(plugin_init)
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
iwatch_exit(&iw_pidfile);
|
||||
plugin_unregister(&plugin);
|
||||
iwatch_exit(&iw_pidfile);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+13
-7
@@ -1,6 +1,6 @@
|
||||
/* Set kernel variables from /etc/sysctl.conf et al
|
||||
*
|
||||
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -22,7 +22,11 @@
|
||||
*/
|
||||
|
||||
#include <glob.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -30,12 +34,10 @@
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
size_t i;
|
||||
glob_t gl;
|
||||
char buf[160];
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -47,9 +49,13 @@ static void setup(void *arg)
|
||||
glob("/mnt/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
glob("/etc/sysctl.conf", GLOB_APPEND, NULL, &gl);
|
||||
if (gl.gl_pathc > 0) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
snprintf(buf, sizeof(buf), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]);
|
||||
run(buf);
|
||||
char cmd[160];
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]);
|
||||
run(cmd, "sysctl");
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Setup necessary files for resolvconf
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,7 +21,11 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -33,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);
|
||||
@@ -41,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);
|
||||
chdir("/");
|
||||
}
|
||||
|
||||
|
||||
+42
-11
@@ -1,6 +1,6 @@
|
||||
/* Save and restore RTC using hwclock
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,17 +21,29 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#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)
|
||||
{
|
||||
@@ -85,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;
|
||||
}
|
||||
|
||||
@@ -95,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;
|
||||
}
|
||||
@@ -127,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) {
|
||||
@@ -147,19 +161,29 @@ 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
|
||||
logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno));
|
||||
|
||||
/* Been here already? */
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Attempt to set RTC to a sane value ... */
|
||||
tv.tv_sec = rtc_date_fallback;
|
||||
if (!gmtime_r(&tv.tv_sec, &tm))
|
||||
goto out;
|
||||
|
||||
logit(LOG_NOTICE, "Resetting RTC to kernel default, %s.", rtc_timestamp);
|
||||
rc = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -185,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);
|
||||
}
|
||||
|
||||
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
/* System condition event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fnmatch.h>
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "iwatch.h"
|
||||
|
||||
static struct iwatch iw_sys;
|
||||
|
||||
|
||||
static int sys_add_path(struct iwatch *iw, char *path)
|
||||
{
|
||||
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* unlink conditions with no active task/service, otherwise they may
|
||||
* trigger inadvertently when calling `initctl reload` to activate
|
||||
* a new configuration.
|
||||
*/
|
||||
static void sys_update_conds(char *dir, char *name, uint32_t mask)
|
||||
{
|
||||
char path[256];
|
||||
char *cond;
|
||||
char *ptr;
|
||||
|
||||
ptr = strrchr(name, '/');
|
||||
if (ptr)
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, ++ptr);
|
||||
else
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
|
||||
cond = strstr(path, COND_BASE);
|
||||
if (!cond)
|
||||
return;
|
||||
|
||||
cond += strlen(COND_BASE) + 1;
|
||||
dbg("cond: %s set: %d", cond, (mask & IN_CREATE) ? 1 : 0);
|
||||
if (!cond_update(cond))
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
/* synthesize events in case of new run dirs */
|
||||
static void sys_scandir(struct iwatch *iw, char *dir, int len)
|
||||
{
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
char path[len + 6];
|
||||
int rc;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/*", dir);
|
||||
rc = glob(path, GLOB_NOSORT, NULL, &gl);
|
||||
if (rc && rc != GLOB_NOMATCH)
|
||||
return;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
dbg("scan found %s", gl.gl_pathv[i]);
|
||||
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
|
||||
/*
|
||||
* create/remove sub-directory in monitored directory
|
||||
*/
|
||||
static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||
{
|
||||
char path[strlen(dir) + strlen(name) + 2];
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
dbg("path: %s", path);
|
||||
|
||||
iwp = iwatch_find_by_path(iw, path);
|
||||
|
||||
if (mask & IN_CREATE) {
|
||||
if (!iwp) {
|
||||
if (!sys_add_path(iw, path))
|
||||
sys_scandir(iw, path, sizeof(path));
|
||||
}
|
||||
} else if (mask & IN_DELETE) {
|
||||
if (iwp)
|
||||
iwatch_del(&iw_sys, iwp);
|
||||
}
|
||||
}
|
||||
|
||||
static void sys_callback(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
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;
|
||||
|
||||
dbg("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
/* Find base path for this event */
|
||||
iwp = iwatch_find_by_wd(&iw_sys, ev->wd);
|
||||
if (!iwp)
|
||||
continue;
|
||||
|
||||
if (ev->mask & IN_ISDIR) {
|
||||
sys_handle_dir(&iw_sys, iwp->path, ev->name, ev->mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & (IN_CREATE | IN_DELETE))
|
||||
sys_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void sys_init(void *arg)
|
||||
{
|
||||
char sysdir[MAX_ARG_LEN];
|
||||
char *path;
|
||||
|
||||
mkpath(_PATH_COND, 0755);
|
||||
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
|
||||
err(1, "Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
|
||||
return;
|
||||
}
|
||||
|
||||
path = realpath(_PATH_CONDSYS, NULL);
|
||||
if (!path) {
|
||||
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)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = iwatch_init(&iw_sys);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
plugin.io.fd = fd;
|
||||
plugin.io.cb = sys_callback;
|
||||
plugin.io.flags = PLUGIN_IO_READ;
|
||||
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
iwatch_exit(&iw_sys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+12
-9
@@ -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-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2013-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,7 +27,11 @@
|
||||
#include <unistd.h> /* read() */
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -51,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)
|
||||
@@ -63,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);
|
||||
}
|
||||
|
||||
@@ -83,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;
|
||||
@@ -99,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);
|
||||
}
|
||||
}
|
||||
@@ -112,10 +116,9 @@ PLUGIN_INIT(plugin_init)
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
if (plugin.io.fd)
|
||||
close(plugin.io.fd);
|
||||
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+75
-8
@@ -1,6 +1,6 @@
|
||||
/* Setup random seed
|
||||
/* Setup and save random seed at boot/shutdown
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -22,21 +22,36 @@
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h> /* gettimeofday() */
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <linux/random.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#ifndef RANDOM_BYTES
|
||||
#define RANDOM_BYTES (32*1024)
|
||||
#endif
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
struct rand_pool_info *rpi;
|
||||
ssize_t len = 0;
|
||||
int rc = -1;
|
||||
int fd, err;
|
||||
|
||||
if (rescue) {
|
||||
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
dbg("Skipping %s plugin in rescue mode.", __FILE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,8 +81,60 @@ static void setup(void *arg)
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
print_desc("Initializing random number generator", NULL);
|
||||
print_result(512 != copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
|
||||
print_desc("Seeding random number generator", NULL);
|
||||
|
||||
/*
|
||||
* Simply copying our saved entropy to /dev/urandom doesn't
|
||||
* increment the kernel "entropy count", instead we must use
|
||||
* the ioctl below.
|
||||
*/
|
||||
fd = open(RANDOMSEED, O_RDONLY);
|
||||
if (fd < 0)
|
||||
goto fallback;
|
||||
|
||||
rpi = malloc(sizeof(*rpi) + RANDOM_BYTES);
|
||||
if (!rpi) {
|
||||
close(fd);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
do {
|
||||
ssize_t num;
|
||||
|
||||
num = read(fd, &rpi->buf[len], RANDOM_BYTES - len);
|
||||
if (num <= 0) {
|
||||
if (num == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (len > 0)
|
||||
break;
|
||||
|
||||
close(fd);
|
||||
free(rpi);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
len += num;
|
||||
} while (len < RANDOM_BYTES);
|
||||
close(fd);
|
||||
|
||||
fd = open("/dev/urandom", O_WRONLY);
|
||||
if (fd < 0) {
|
||||
free(rpi);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
rpi->buf_size = len;
|
||||
rpi->entropy_count = len * 8;
|
||||
rc = ioctl(fd, RNDADDENTROPY, rpi);
|
||||
err = errno;
|
||||
close(fd);
|
||||
free(rpi);
|
||||
if (rc < 0)
|
||||
logit(LOG_ERR, "Failed adding entropy to kernel random pool: %s", strerror(err));
|
||||
print_result(rc < 0);
|
||||
return;
|
||||
fallback:
|
||||
print_result(512 > copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -77,14 +144,14 @@ 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;
|
||||
}
|
||||
|
||||
prev = umask(077);
|
||||
|
||||
print_desc("Saving random seed", NULL);
|
||||
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
||||
print_result(RANDOM_BYTES != copyfile("/dev/urandom", RANDOMSEED, RANDOM_BYTES, 0));
|
||||
|
||||
umask(prev);
|
||||
#endif
|
||||
|
||||
+11
-7
@@ -1,6 +1,6 @@
|
||||
/* User-defined condtion event monitor for the Finit condition engine
|
||||
/* User-defined condition event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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,24 +84,28 @@ 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;
|
||||
}
|
||||
|
||||
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
|
||||
iwatch_exit(&iw_usr);
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
|
||||
.depends = { "bootmisc", },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
@@ -121,8 +125,8 @@ PLUGIN_INIT(plugin_init)
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
iwatch_exit(&iw_usr);
|
||||
plugin_unregister(&plugin);
|
||||
iwatch_exit(&iw_usr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
-35
@@ -1,6 +1,6 @@
|
||||
/* Console setup (for X)
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -24,7 +24,11 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
@@ -32,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);
|
||||
run("pam_console_apply");
|
||||
}
|
||||
#endif
|
||||
|
||||
makedir("/tmp/.X11-unix", 01777);
|
||||
makedir("/tmp/.ICE-unix", 01777);
|
||||
|
||||
if (whichp("restorecon"))
|
||||
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
|
||||
|
||||
umask(prev);
|
||||
if (fexist("/var/run/console/console.lock") && which("pam_console_apply"))
|
||||
run("pam_console_apply", "pam-console");
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_NETWORK_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", }
|
||||
.hook[HOOK_SVC_PLUGIN] = { .cb = setup },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
.deps/*
|
||||
finit
|
||||
getty
|
||||
initctl
|
||||
keventd
|
||||
logit
|
||||
reboot
|
||||
runparts
|
||||
sulogin
|
||||
watchdogd
|
||||
Makefile
|
||||
|
||||
+35
-9
@@ -8,39 +8,63 @@ AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
sbin_PROGRAMS = finit initctl
|
||||
pkglibexec_PROGRAMS = logit sulogin
|
||||
pkglibexec_PROGRAMS = getty logit runparts
|
||||
if SULOGIN
|
||||
pkglibexec_PROGRAMS += sulogin
|
||||
endif
|
||||
if KEVENTD
|
||||
pkglibexec_PROGRAMS += keventd
|
||||
endif
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS += watchdogd
|
||||
endif
|
||||
|
||||
finit_pkglibdir = $(pkglibdir)
|
||||
finit_pkglib_SCRIPTS = rescue.conf sample.conf
|
||||
|
||||
getty_SOURCES = finit.h getty.c helpers.h logrotate.c stty.c utmp-api.c utmp-api.h
|
||||
getty_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
getty_CFLAGS += $(lite_CFLAGS)
|
||||
getty_LDADD = $(lite_LIBS)
|
||||
|
||||
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h
|
||||
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
keventd_CFLAGS += $(lite_CFLAGS)
|
||||
keventd_LDADD = $(lite_LIBS)
|
||||
|
||||
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
|
||||
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS += watchdogd
|
||||
endif
|
||||
|
||||
logit_SOURCES = logit.c logrotate.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
logit_CFLAGS += $(lite_CFLAGS)
|
||||
logit_LDADD = $(lite_LIBS)
|
||||
|
||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
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 \
|
||||
getty.c stty.c \
|
||||
stty.c \
|
||||
helpers.c helpers.h \
|
||||
iwatch.c iwatch.h \
|
||||
log.c log.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
|
||||
@@ -48,8 +72,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)
|
||||
@@ -61,7 +87,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-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,6 +21,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -29,10 +31,13 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <uev/uev.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
@@ -40,36 +45,40 @@
|
||||
#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 *), char *buf, size_t len)
|
||||
static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
|
||||
{
|
||||
return svc_parse_jobstr(buf, len, action, NULL);
|
||||
return svc_parse_jobstr(buf, len, NULL, action, NULL);
|
||||
}
|
||||
|
||||
static int stop(svc_t *svc)
|
||||
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;
|
||||
}
|
||||
|
||||
static int start(svc_t *svc)
|
||||
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;
|
||||
}
|
||||
@@ -79,25 +88,30 @@ static int start(svc_t *svc)
|
||||
* responsibility of initctl to do. Otherwise we'd block PID 1,
|
||||
* or introduce some nasty race conditions.
|
||||
*/
|
||||
static int restart(svc_t *svc)
|
||||
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;
|
||||
}
|
||||
|
||||
static int reload(svc_t *svc)
|
||||
static int reload(svc_t *svc, void *user_data)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
if (svc_is_blocked(svc))
|
||||
svc_start(svc);
|
||||
else
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
service_step(svc);
|
||||
@@ -110,8 +124,28 @@ static int do_start (char *buf, size_t len) { return call(start, buf, len); }
|
||||
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
|
||||
static int do_reload (char *buf, size_t len) { return call(reload, buf, len); }
|
||||
|
||||
static int do_signal_svc(svc_t *svc, void *user_data)
|
||||
{
|
||||
int signo;
|
||||
|
||||
if (!svc || !user_data || !svc_is_running(svc))
|
||||
return 1;
|
||||
|
||||
signo = *(int *)user_data;
|
||||
return !!kill(svc->pid, signo);
|
||||
}
|
||||
|
||||
static int do_signal(char *buf, size_t len, int sig)
|
||||
{
|
||||
/* Sanity check: do we know this signal? */
|
||||
if (!*sig2str(sig))
|
||||
return 1;
|
||||
|
||||
return svc_parse_jobstr(buf, len, &sig, do_signal_svc, NULL);
|
||||
}
|
||||
|
||||
static char query_buf[368];
|
||||
static int missing(char *job, char *id)
|
||||
static int missing(char *job, char *id, void *user_data)
|
||||
{
|
||||
char buf[20];
|
||||
|
||||
@@ -129,7 +163,7 @@ static int missing(char *job, char *id)
|
||||
static int do_query(char *buf, size_t len)
|
||||
{
|
||||
query_buf[0] = 0;
|
||||
if (svc_parse_jobstr(buf, len, NULL, missing)) {
|
||||
if (svc_parse_jobstr(buf, len, NULL, NULL, missing)) {
|
||||
memcpy(buf, query_buf, len);
|
||||
return 1;
|
||||
}
|
||||
@@ -173,70 +207,93 @@ static svc_t *do_find(char *buf, size_t len)
|
||||
if (!id)
|
||||
return svc_named_iterator(&iter, 1, input);
|
||||
|
||||
return svc_find_by_nameid(input, id);
|
||||
return svc_find(input, id);
|
||||
}
|
||||
|
||||
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 = reboot(RB_SW_SUSPEND);
|
||||
rc = suspend();
|
||||
if (rc) {
|
||||
if (errno == EINVAL)
|
||||
snprintf(buf, len, "Kernel does not support suspend.");
|
||||
snprintf(buf, len, "Kernel does not support suspend to RAM.");
|
||||
else
|
||||
snprintf(buf, len, "Failed: %s", strerror(errno));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = 255;
|
||||
/* Handled previously */
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -265,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)
|
||||
@@ -277,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;
|
||||
}
|
||||
|
||||
@@ -294,14 +351,37 @@ 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_RUNLVL:
|
||||
case INIT_CMD_RELOAD:
|
||||
case INIT_CMD_START_SVC:
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
case INIT_CMD_STOP_SVC:
|
||||
case INIT_CMD_RELOAD_SVC:
|
||||
case INIT_CMD_REBOOT:
|
||||
case INIT_CMD_HALT:
|
||||
case INIT_CMD_POWEROFF:
|
||||
case INIT_CMD_SUSPEND:
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||
warnx("Unsupported command in runlevel S and 6.");
|
||||
goto leave;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -314,7 +394,7 @@ 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;
|
||||
@@ -324,47 +404,55 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
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;
|
||||
@@ -373,21 +461,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;
|
||||
}
|
||||
|
||||
_e("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);
|
||||
@@ -403,7 +491,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
if (wdog->protect) {
|
||||
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog.");
|
||||
stop(wdog);
|
||||
stop(wdog, NULL);
|
||||
svc_del(wdog);
|
||||
}
|
||||
}
|
||||
@@ -411,9 +499,9 @@ 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 severly limits the number of
|
||||
* XXX: This severely limits the number of
|
||||
* simultaneous client connections, but will
|
||||
* have to do for now.
|
||||
*/
|
||||
@@ -422,25 +510,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 */
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -450,7 +559,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:
|
||||
@@ -461,39 +570,46 @@ 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)
|
||||
{
|
||||
int sd;
|
||||
mode_t oldmask;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
mode_t oldmask;
|
||||
int uid, gid;
|
||||
int sd;
|
||||
|
||||
_d("Setting up external API socket ...");
|
||||
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;
|
||||
}
|
||||
|
||||
uid = geteuid();
|
||||
gid = getgroup(DEFGROUP);
|
||||
|
||||
erase(INIT_SOCKET);
|
||||
oldmask = umask(0077);
|
||||
oldmask = umask(0117);
|
||||
if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun)))
|
||||
goto error;
|
||||
|
||||
if (-1 == listen(sd, 10))
|
||||
goto error;
|
||||
|
||||
if (chown(INIT_SOCKET, uid, gid))
|
||||
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 intializing API socket");
|
||||
err(1, "Failed initializing API socket");
|
||||
umask(oldmask);
|
||||
close(sd);
|
||||
return 1;
|
||||
|
||||
+99
-41
@@ -1,6 +1,6 @@
|
||||
/* Finit control group support functions
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -24,8 +24,13 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
# include <libite/queue.h> /* BSD sys/queue.h API */
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
# include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#endif
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysinfo.h> /* get_nprocs_conf() */
|
||||
|
||||
@@ -42,7 +47,7 @@ struct cg {
|
||||
char *cfg; /* kernel settings */
|
||||
|
||||
int active; /* for mark & sweep */
|
||||
int protected; /* for init/, user/, & system/ */
|
||||
int is_protected; /* for init/, user/, & system/ */
|
||||
};
|
||||
|
||||
static TAILQ_HEAD(, cg) cgroups = TAILQ_HEAD_INITIALIZER(cgroups);
|
||||
@@ -51,22 +56,23 @@ static char controllers[256];
|
||||
|
||||
static struct iwatch iw_cgroup;
|
||||
static uev_t cgw;
|
||||
static int avail;
|
||||
|
||||
|
||||
static void cgset(const char *path, char *ctrl, char *prop)
|
||||
{
|
||||
char *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;
|
||||
}
|
||||
|
||||
@@ -75,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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -100,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])
|
||||
@@ -117,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
|
||||
@@ -138,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;
|
||||
@@ -150,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));
|
||||
|
||||
@@ -159,6 +165,9 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
|
||||
|
||||
int cgroup_user(char *name, int pid)
|
||||
{
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
return cgroup_leaf_init("user", name, pid, NULL);
|
||||
}
|
||||
|
||||
@@ -166,12 +175,18 @@ int cgroup_service(char *name, int pid, struct cgroup *cg)
|
||||
{
|
||||
char *group = "system";
|
||||
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
if (cg && cg->name[0]) {
|
||||
char path[256];
|
||||
|
||||
if (!strcmp(cg->name, "root"))
|
||||
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
|
||||
|
||||
if (!strcmp(cg->name, "init"))
|
||||
return fnwrite(str("%d", pid), FINIT_CGPATH "/init/cgroup.procs");
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
|
||||
if (fisdir(path))
|
||||
group = cg->name;
|
||||
@@ -196,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;
|
||||
}
|
||||
|
||||
@@ -246,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;
|
||||
@@ -272,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
|
||||
@@ -299,8 +314,11 @@ void cgroup_mark_all(void)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
if (!avail)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||
if (cg->protected)
|
||||
if (cg->is_protected)
|
||||
continue;
|
||||
|
||||
cg->active = 0;
|
||||
@@ -315,6 +333,9 @@ void cgroup_cleanup(void)
|
||||
struct cg *cg, *tmp;
|
||||
char path[256];
|
||||
|
||||
if (!avail)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) {
|
||||
if (cg->active)
|
||||
continue;
|
||||
@@ -327,10 +348,13 @@ void cgroup_cleanup(void)
|
||||
/*
|
||||
* Add, or update, settings for top-level cgroup
|
||||
*/
|
||||
int cgroup_add(char *name, char *cfg, int protected)
|
||||
int cgroup_add(char *name, char *cfg, int is_protected)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
if (!name)
|
||||
return -1;
|
||||
if (!cfg)
|
||||
@@ -340,12 +364,12 @@ int cgroup_add(char *name, char *cfg, int 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;
|
||||
}
|
||||
@@ -355,13 +379,13 @@ int cgroup_add(char *name, char *cfg, int 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);
|
||||
return -1;
|
||||
}
|
||||
cg->protected = protected;
|
||||
cg->is_protected = is_protected;
|
||||
cg->active = 1;
|
||||
|
||||
return 0;
|
||||
@@ -375,6 +399,9 @@ int cgroup_del(char *dir)
|
||||
struct cg *cg;
|
||||
char path[256];
|
||||
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
|
||||
if (strcmp(path, dir))
|
||||
@@ -387,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;
|
||||
}
|
||||
|
||||
@@ -406,6 +433,9 @@ void cgroup_config(void)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
if (!avail)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||
char path[256];
|
||||
int leaf = 0;
|
||||
@@ -434,14 +464,24 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
int fd;
|
||||
|
||||
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
|
||||
_pe("Failed mounting cgroup v2");
|
||||
if (errno == ENOENT)
|
||||
logit(LOG_INFO, "Kernel does not support cgroups v2, disabling.");
|
||||
else if (errno == EPERM) /* Probably inside an unpriviliged container */
|
||||
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
|
||||
else
|
||||
err(1, "Failed mounting cgroup v2");
|
||||
avail = 0;
|
||||
return;
|
||||
}
|
||||
avail = 1;
|
||||
|
||||
/* 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;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -457,9 +497,29 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/* Check for cpu controller, abort if missing */
|
||||
if (!strstr(controllers, "+cpu")) {
|
||||
logit(LOG_NOTICE, "Missing CPU controller, disabling cgroup support.");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Enable all controllers */
|
||||
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control"))
|
||||
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
|
||||
if (fnwrite(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)) {
|
||||
err(1, "Failed setting up cgroup.events watcher");
|
||||
iwatch_exit(&iw_cgroup);
|
||||
close(fd);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Default (protected) groups, PID 1, services, and user/login processes */
|
||||
cgroup_add("init", "cpu.weight:100", 1);
|
||||
@@ -468,14 +528,12 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
cgroup_config();
|
||||
|
||||
/* Move ourselves to init (best effort, otherwise run in 'root' group */
|
||||
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs"))
|
||||
_pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init");
|
||||
|
||||
/* prepare cgroup.events watcher */
|
||||
fd = iwatch_init(&iw_cgroup);
|
||||
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
||||
_pe("Failed setting up cgroup.events watcher");
|
||||
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) {
|
||||
err(1, "Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init");
|
||||
uev_io_stop(&cgw);
|
||||
iwatch_exit(&iw_cgroup);
|
||||
close(fd);
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* Finit control group support functions
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,7 +34,7 @@ struct cgroup {
|
||||
void cgroup_mark_all(void);
|
||||
void cgroup_cleanup (void);
|
||||
|
||||
int cgroup_add (char *name, char *cfg, int protected);
|
||||
int cgroup_add (char *name, char *cfg, int is_protected);
|
||||
int cgroup_del (char *dir);
|
||||
void cgroup_config (void);
|
||||
|
||||
|
||||
+20
-10
@@ -1,6 +1,6 @@
|
||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,18 +23,22 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <search.h>
|
||||
#include <inttypes.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
|
||||
#include "cgutil.h"
|
||||
#include "initctl.h"
|
||||
#include "log.h"
|
||||
|
||||
#define CDIM plain ? "" : "\e[2m"
|
||||
#define CRST plain ? "" : "\e[0m"
|
||||
@@ -49,6 +53,10 @@ uint64_t total_ram; /* From sysinfo() */
|
||||
struct cg dummy; /* empty result "NULL" */
|
||||
struct cg *list;
|
||||
|
||||
int cgroup_avail(void)
|
||||
{
|
||||
return fismnt(FINIT_CGPATH);
|
||||
}
|
||||
|
||||
char *pid_cmdline(int pid, char *buf, size_t len)
|
||||
{
|
||||
@@ -236,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;
|
||||
@@ -272,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)
|
||||
@@ -288,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;
|
||||
}
|
||||
@@ -426,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:
|
||||
@@ -448,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);
|
||||
|
||||
@@ -592,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;
|
||||
@@ -602,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;
|
||||
@@ -611,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();
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
/* Cgroup utility functions for initctl
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -52,6 +52,8 @@ struct cg {
|
||||
} cg_cpu;
|
||||
};
|
||||
|
||||
int cgroup_avail(void);
|
||||
|
||||
char *pid_cmdline (int pid, char *buf, size_t len);
|
||||
char *pid_comm (int pid, char *buf, size_t len);
|
||||
char *pid_cgroup (int pid, char *buf, size_t len);
|
||||
|
||||
+74
-34
@@ -1,6 +1,6 @@
|
||||
/* External client API, using UNIX domain socket.
|
||||
*
|
||||
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* 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;
|
||||
|
||||
@@ -39,11 +39,17 @@ int client_connect(void)
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (-1 == sd)
|
||||
err(1, "Failed creating UNIX domain socket");
|
||||
if (-1 == sd) {
|
||||
warn("Failed creating UNIX domain socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
err(1, "Failed connecting to finit");
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||
if (errno != ENOENT)
|
||||
warnx("Failed connecting to finit");
|
||||
close(sd);
|
||||
sd = -1;
|
||||
}
|
||||
|
||||
return sd;
|
||||
}
|
||||
@@ -63,60 +69,97 @@ 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;
|
||||
pfd.events = POLLIN;
|
||||
if (poll(&pfd, 1, 2000) <= 0) {
|
||||
warn("Timed out waiting for reply from Finit, errno %d", errno);
|
||||
goto exit;
|
||||
pfd.events = POLLIN | POLLERR | POLLHUP;
|
||||
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
|
||||
if (rc) {
|
||||
if (errno == EINTR) /* shutdown/reboot */
|
||||
return -1;
|
||||
warn("poll(), errno %d", errno);
|
||||
} else
|
||||
warnx("Timed out waiting for reply from Finit.");
|
||||
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)
|
||||
@@ -135,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));
|
||||
@@ -168,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-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* 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);
|
||||
|
||||
+227
-43
@@ -21,16 +21,85 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <ftw.h>
|
||||
#include <libgen.h>
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#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
|
||||
@@ -106,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)
|
||||
@@ -116,105 +185,183 @@ 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
nftw(path, do_delete, 20, FTW_DEPTH);
|
||||
if (remove(path) && errno != ENOENT)
|
||||
err(1, "Failed removing condition path %s", path);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case COND_OFF:
|
||||
if (unlink(path) && errno != ENOENT)
|
||||
_pe("Failed removing condition '%s'", path);
|
||||
if (unlink(path)) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
break;
|
||||
case EISDIR:
|
||||
cond_delpath(path);
|
||||
break;
|
||||
default:
|
||||
err(1, "Failed removing condition '%s'", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
_e("Invalid condition state");
|
||||
errx(1, "Invalid condition state");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return next != prev;
|
||||
}
|
||||
|
||||
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
|
||||
void cond_update(const char *name)
|
||||
/* Should only be used by cond_set*(), cond_clear(), and usr/sys plugins! */
|
||||
int cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
int affects = 0;
|
||||
|
||||
_d("%s", name);
|
||||
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;
|
||||
|
||||
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
|
||||
affects++;
|
||||
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);
|
||||
}
|
||||
|
||||
return affects;
|
||||
}
|
||||
|
||||
int cond_set_noupdate(const char *name)
|
||||
{
|
||||
dbg("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return 1;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_ON))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cond_set(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_ON))
|
||||
dbg("%s", name);
|
||||
if (cond_set_noupdate(name))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_set_oneshot(const char *name)
|
||||
int cond_set_oneshot_noupdate(const char *name)
|
||||
{
|
||||
const char *path;
|
||||
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
return 1;
|
||||
|
||||
path = cond_path(name);
|
||||
_d("%s => %s", name, path);
|
||||
dbg("%s => %s", name, path);
|
||||
|
||||
if (cond_checkpath(path))
|
||||
return 1;
|
||||
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
|
||||
err(1, "Failed creating onshot cond %s", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cond_set_oneshot(const char *name)
|
||||
{
|
||||
dbg("%s", name);
|
||||
if (cond_set_oneshot_noupdate(name))
|
||||
return;
|
||||
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||
_pe("Failed creating onshot cond %s", name);
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
int cond_clear_noupdate(const char *name)
|
||||
{
|
||||
dbg("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return 1;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cond_clear(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||
dbg("%s", name);
|
||||
if (cond_clear_noupdate(name))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
@@ -222,12 +369,12 @@ void cond_clear(const char *name)
|
||||
|
||||
void cond_reload(void)
|
||||
{
|
||||
_d("");
|
||||
dbg("");
|
||||
|
||||
cond_bump_reconf();
|
||||
}
|
||||
|
||||
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||
static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw, int set)
|
||||
{
|
||||
char *nm;
|
||||
|
||||
@@ -239,25 +386,56 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
|
||||
nm = strstr((char *)fpath, COND_BASE);
|
||||
if (!nm) {
|
||||
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
||||
errx(1, "Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de");
|
||||
return 1;
|
||||
}
|
||||
|
||||
nm += strlen(COND_BASE);
|
||||
_d("Reasserting %s => %s", fpath, nm);
|
||||
cond_set(nm);
|
||||
dbg("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
|
||||
if (set)
|
||||
cond_set(nm);
|
||||
else
|
||||
cond_clear_noupdate(nm); /* important, see netlink plugin! */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||
{
|
||||
return do_assert(fpath, sb, tflg, ftw, 1);
|
||||
}
|
||||
|
||||
static int deassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||
{
|
||||
return do_assert(fpath, sb, tflg, ftw, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used only by netlink plugin atm.
|
||||
* type: is a one of pid/, net/, etc.
|
||||
*/
|
||||
void cond_reassert(const char *type)
|
||||
void cond_reassert(const char *pat)
|
||||
{
|
||||
_d("%s", type);
|
||||
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
|
||||
dbg("%s", pat);
|
||||
nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used only by netlink plugin atm.
|
||||
*/
|
||||
void cond_deassert(const char *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)
|
||||
@@ -265,11 +443,17 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-2
@@ -21,8 +21,12 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
@@ -108,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)
|
||||
|
||||
+20
-4
@@ -2,15 +2,20 @@
|
||||
#define FINIT_COND_H_
|
||||
|
||||
#include <paths.h>
|
||||
#include <svc.h>
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
#define COND_BASE "finit/cond"
|
||||
#define COND_USR "usr/"
|
||||
#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_CONDUSR _PATH_COND COND_USR
|
||||
#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
|
||||
#define _PATH_RECONF _PATH_COND "reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
@@ -28,14 +33,25 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
void cond_update (const char *name);
|
||||
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);
|
||||
void cond_set_oneshot (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
|
||||
int cond_set_noupdate(const char *name);
|
||||
int cond_set_oneshot_noupdate(const char *name);
|
||||
int cond_clear_noupdate(const char *name);
|
||||
|
||||
void cond_reassert (const char *pat);
|
||||
void cond_deassert (const char *pat);
|
||||
|
||||
int cond_is_available(void);
|
||||
|
||||
void cond_init (void);
|
||||
void cond_exit (void);
|
||||
|
||||
#endif /* FINIT_COND_H_ */
|
||||
|
||||
|
||||
+664
-164
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -1,6 +1,6 @@
|
||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||
*
|
||||
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -42,6 +42,8 @@ int conf_any_change (void);
|
||||
int conf_changed (char *file);
|
||||
int conf_monitor (void);
|
||||
|
||||
void conf_reset_env (void);
|
||||
void conf_saverc (void);
|
||||
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-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "helpers.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "iwatch.h"
|
||||
|
||||
static struct iwatch iw_devmon;
|
||||
static uev_t devw;
|
||||
static int fd;
|
||||
|
||||
struct dev_node {
|
||||
TAILQ_ENTRY(dev_node) link;
|
||||
char *name;
|
||||
size_t refcnt;
|
||||
};
|
||||
|
||||
static TAILQ_HEAD(, dev_node) dev_node_list = TAILQ_HEAD_INITIALIZER(dev_node_list);
|
||||
|
||||
static struct dev_node *find_node(const char *cond)
|
||||
{
|
||||
struct dev_node *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &dev_node_list, link, tmp) {
|
||||
if (string_compare(node->name, cond))
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drop_node(struct dev_node *node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
node->refcnt--;
|
||||
if (node->refcnt)
|
||||
return;
|
||||
|
||||
TAILQ_REMOVE(&dev_node_list, node, link);
|
||||
free(node->name);
|
||||
free(node);
|
||||
}
|
||||
|
||||
void devmon_add_cond(const char *cond)
|
||||
{
|
||||
struct dev_node *node;
|
||||
|
||||
if (!cond || strncmp(cond, "dev/", 4)) {
|
||||
dbg("no match");
|
||||
return;
|
||||
}
|
||||
|
||||
// dbg("%s", cond);
|
||||
node = find_node(cond);
|
||||
if (node) {
|
||||
node->refcnt++;
|
||||
return;
|
||||
}
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node) {
|
||||
fail:
|
||||
warn("failed allocating node %s", cond);
|
||||
return;
|
||||
}
|
||||
|
||||
node->name = strdup(cond);
|
||||
if (!node->name) {
|
||||
free(node);
|
||||
goto fail;
|
||||
}
|
||||
node->refcnt = 1;
|
||||
|
||||
TAILQ_INSERT_TAIL(&dev_node_list, node, link);
|
||||
}
|
||||
|
||||
void devmon_del_cond(const char *cond)
|
||||
{
|
||||
if (!cond || strcmp(cond, "dev/"))
|
||||
return;
|
||||
|
||||
drop_node(find_node(cond));
|
||||
}
|
||||
|
||||
static int devmon_add_path(struct iwatch *iw, char *path)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
ptr = strstr(path, "dev/");
|
||||
if (ptr) {
|
||||
char *slash;
|
||||
|
||||
ptr += 4;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
ptr = slash++;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
dbg("Path too deep, skipping.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iwatch_add1(iw, path, IN_CREATE | IN_DELETE);
|
||||
}
|
||||
|
||||
static void devmon_update_conds(char *dir, char *name, uint32_t mask)
|
||||
{
|
||||
char fn[PATH_MAX];
|
||||
char *cond;
|
||||
|
||||
paste(fn, sizeof(fn), dir, name);
|
||||
cond = &fn[1];
|
||||
|
||||
// dbg("path: %s, mask: %08x, cond: %s", fn, mask, cond);
|
||||
if (!find_node(cond)) {
|
||||
// dbg("unregistered, skipping %s", cond);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mask & IN_CREATE)
|
||||
cond_set(cond);
|
||||
else if (mask & IN_DELETE)
|
||||
cond_clear(cond);
|
||||
}
|
||||
|
||||
/* synthesize events in case of new run dirs */
|
||||
static void devmon_scandir(struct iwatch *iw, char *dir, int len)
|
||||
{
|
||||
char path[len + 6];
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
int rc;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/*", dir);
|
||||
rc = glob(path, GLOB_NOSORT, NULL, &gl);
|
||||
if (rc && rc != GLOB_NOMATCH)
|
||||
return;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
// dbg("scan found %s", gl.gl_pathv[i]);
|
||||
devmon_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
|
||||
/*
|
||||
* create/remove sub-directory in monitored directory
|
||||
*/
|
||||
static void devmon_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||
{
|
||||
char path[strlen(dir) + strlen(name) + 2];
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
paste(path, sizeof(path), dir, name);
|
||||
dbg("path: %s", path);
|
||||
|
||||
iwp = iwatch_find_by_path(iw, path);
|
||||
|
||||
if (mask & IN_CREATE) {
|
||||
if (!iwp) {
|
||||
if (!devmon_add_path(iw, path))
|
||||
devmon_scandir(iw, path, sizeof(path));
|
||||
}
|
||||
} else if (mask & IN_DELETE) {
|
||||
if (iwp)
|
||||
iwatch_del(&iw_devmon, iwp);
|
||||
}
|
||||
}
|
||||
|
||||
static void devmon_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
err(1, "invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
/* Find base path for this event */
|
||||
iwp = iwatch_find_by_wd(&iw_devmon, ev->wd);
|
||||
if (!iwp)
|
||||
continue;
|
||||
|
||||
if (ev->mask & IN_ISDIR) {
|
||||
devmon_handle_dir(&iw_devmon, iwp->path, ev->name, ev->mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
dbg("Got %s", ev->name);
|
||||
if (ev->mask & (IN_CREATE | IN_DELETE))
|
||||
devmon_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
|
||||
void devmon_init(uev_ctx_t *ctx)
|
||||
{
|
||||
char dir[MAX_ARG_LEN];
|
||||
|
||||
fd = iwatch_init(&iw_devmon);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDDEV, dir, sizeof(dir)), 0755) && errno != EEXIST) {
|
||||
err(1, "Failed creating %s condition directory, %s", COND_DEV, _PATH_CONDDEV);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uev_io_init(ctx, &devw, devmon_cb, NULL, fd, UEV_READ)) {
|
||||
err(1, "Failed setting up I/O callback for /dev watcher");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(dir, _PATH_DEV, sizeof(dir));
|
||||
if (devmon_add_path(&iw_devmon, dir))
|
||||
iwatch_exit(&iw_devmon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Device node monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2022-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_DEVMON_H_
|
||||
#define FINIT_DEVMON_H_
|
||||
|
||||
void devmon_add_cond(const char *cond);
|
||||
void devmon_del_cond(const char *cond);
|
||||
|
||||
void devmon_init(uev_ctx_t *ctx);
|
||||
|
||||
#endif /* FINIT_DEVMON_H_ */
|
||||
+167
-206
@@ -1,7 +1,7 @@
|
||||
/* Functions for exec'ing processes
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -24,15 +24,20 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <linux/vt.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
@@ -52,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;
|
||||
}
|
||||
@@ -64,91 +69,128 @@ int complete(char *cmd, int pid)
|
||||
return status;
|
||||
}
|
||||
|
||||
int run(char *cmd)
|
||||
static int do_redirect(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/dev/null", "w");
|
||||
if (fp) {
|
||||
int fd = fileno(fp);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run 'cmd' and wait for completion. If 'log' is not NULL any output
|
||||
* from the command is logged with the given string as prefix, use "" to
|
||||
* skip. If 'log' is NULL all output is redirected to /dev/null, which
|
||||
* is what this function originally did.
|
||||
*/
|
||||
int run(char *cmd, char *log)
|
||||
{
|
||||
char *args[NUM_ARGS + 1] = { 0 };
|
||||
int status, result, i = 0;
|
||||
char *args[NUM_ARGS + 1], *arg, *backup;
|
||||
char *backup = NULL;
|
||||
pid_t pid;
|
||||
|
||||
/* We must create a copy that is possible to modify. */
|
||||
backup = arg = strdup(cmd);
|
||||
if (!arg)
|
||||
return 1; /* Failed allocating a string to be modified. */
|
||||
if (!log) {
|
||||
char *arg;
|
||||
|
||||
/* Split command line into tokens of an argv[] array. */
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
while (arg && i < NUM_ARGS) {
|
||||
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
|
||||
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
|
||||
if (*arg == '\'' || *arg == '"') {
|
||||
char *p, delim[2] = " ";
|
||||
/* We must create a copy that is possible to modify. */
|
||||
backup = arg = strdup(cmd);
|
||||
if (!arg)
|
||||
return 1;
|
||||
|
||||
delim[0] = arg[0];
|
||||
args[i++] = arg++;
|
||||
strsep(&arg, delim);
|
||||
p = arg - 1;
|
||||
*p = *delim;
|
||||
*arg++ = 0;
|
||||
} else {
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
/* Split command line into tokens of an argv[] array. */
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
while (arg && i < NUM_ARGS) {
|
||||
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
|
||||
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
|
||||
if (*arg == '\'' || *arg == '"') {
|
||||
char *p, delim[2] = " ";
|
||||
|
||||
delim[0] = arg[0];
|
||||
args[i++] = arg++;
|
||||
strsep(&arg, delim);
|
||||
p = arg - 1;
|
||||
*p = *delim;
|
||||
*arg++ = 0;
|
||||
} else {
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
}
|
||||
}
|
||||
}
|
||||
args[i] = NULL;
|
||||
args[i] = NULL;
|
||||
|
||||
if (i == NUM_ARGS && arg) {
|
||||
_e("Command too long: %s", cmd);
|
||||
free(backup);
|
||||
errno = EOVERFLOW;
|
||||
return 1;
|
||||
if (i == NUM_ARGS && arg) {
|
||||
errx(1, "Command too long: %s", cmd);
|
||||
free(backup);
|
||||
errno = EOVERFLOW;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (0 == pid) {
|
||||
FILE *fp;
|
||||
int rc = EX_OSERR;
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
setsid();
|
||||
sig_unblock();
|
||||
if (!log) {
|
||||
do_redirect();
|
||||
execvp(args[0], args);
|
||||
} else {
|
||||
char *pfx = *log ? ": " : "";
|
||||
FILE *pp;
|
||||
|
||||
/* Always redirect stdio for run() */
|
||||
fp = fopen("/dev/null", "w");
|
||||
if (fp) {
|
||||
int fd = fileno(fp);
|
||||
pp = popen(cmd, "r");
|
||||
if (pp) {
|
||||
char buf[256];
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
while (fgets(buf, sizeof(buf), pp)) {
|
||||
chomp(buf);
|
||||
logit(LOG_NOTICE, "%s%s%s", log, pfx, buf);
|
||||
}
|
||||
|
||||
rc = pclose(pp);
|
||||
}
|
||||
}
|
||||
|
||||
sig_unblock();
|
||||
execvp(args[0], args);
|
||||
|
||||
_exit(1); /* Only if execv() fails. */
|
||||
_exit(rc);
|
||||
} else if (-1 == pid) {
|
||||
_pe("%s", args[0]);
|
||||
free(backup);
|
||||
err(1, "%s", !log ? args[0] : cmd);
|
||||
if (backup)
|
||||
free(backup);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = complete(args[0], pid);
|
||||
if (-1 == status) {
|
||||
free(backup);
|
||||
if (backup)
|
||||
free(backup);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = WEXITSTATUS(status);
|
||||
if (WIFEXITED(status)) {
|
||||
_d("Started %s and ended OK: %d", args[0], result);
|
||||
dbg("Started '%s' and exit without signal, status: %d", cmd, result);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
_d("Process %s terminated by signal %d", args[0], 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 */
|
||||
}
|
||||
|
||||
free(backup);
|
||||
if (backup)
|
||||
free(backup);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -182,7 +224,7 @@ int run_interactive(char *cmd, char *fmt, ...)
|
||||
}
|
||||
|
||||
/* Run cmd ... */
|
||||
status = run(cmd);
|
||||
status = run(cmd, "");
|
||||
|
||||
/* Restore stderr/stdout */
|
||||
if (fp && !debug) {
|
||||
@@ -218,7 +260,6 @@ int run_interactive(char *cmd, char *fmt, ...)
|
||||
|
||||
int exec_runtask(char *cmd, char *args[])
|
||||
{
|
||||
size_t i;
|
||||
char buf[1024] = "";
|
||||
char *argv[4] = {
|
||||
"sh",
|
||||
@@ -226,24 +267,23 @@ int exec_runtask(char *cmd, char *args[])
|
||||
buf,
|
||||
NULL
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct termios term;
|
||||
char name[80];
|
||||
int fd;
|
||||
int fd, dummy;
|
||||
|
||||
fd = open(tty, O_RDWR);
|
||||
if (fd < 0) {
|
||||
@@ -254,44 +294,18 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
/*
|
||||
* Become session leader and set controlling TTY
|
||||
* to enable Ctrl-C and job control in shell.
|
||||
*/
|
||||
setsid();
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
|
||||
/* Set default TERM, run_getty() below allows override */
|
||||
if (ioctl(fd, VT_OPENQRY, &dummy) == -1)
|
||||
setenv("TERM", "vt102", 1); /* likely a serial line */
|
||||
else
|
||||
setenv("TERM", "linux", 1);
|
||||
|
||||
/*
|
||||
* Reset to sane defaults in case of messup from prev. session
|
||||
*/
|
||||
stty(STDIN_FILENO, speed);
|
||||
|
||||
/*
|
||||
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
|
||||
* It is up to the getty process to allow them again.
|
||||
*/
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_lflag &= ~ISIG;
|
||||
term.c_cc[VEOF] = _POSIX_VDISABLE;
|
||||
term.c_cc[VINTR] = _POSIX_VDISABLE;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
|
||||
}
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
|
||||
/*
|
||||
* Ignore a few signals, needed to prevent Ctrl-C at login:
|
||||
* prompt and to prevent QUIT from dumping core.
|
||||
*/
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
|
||||
/* Set configured limits */
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
@@ -309,8 +323,12 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
||||
|
||||
static int activate_console(int noclear, int nowait)
|
||||
{
|
||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||
static const char clr[] = "\r\e[K";
|
||||
static const char cup[] = "\e[A";
|
||||
struct termios orig;
|
||||
int ret = 0;
|
||||
char ch;
|
||||
int rc;
|
||||
|
||||
if (nowait || rescue)
|
||||
return 1;
|
||||
@@ -329,36 +347,24 @@ static int activate_console(int noclear, int nowait)
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &c);
|
||||
}
|
||||
|
||||
while (!fexist(SYNC_SHUTDOWN)) {
|
||||
char c;
|
||||
static const char clr[] = "\r\e[2K";
|
||||
static const char cup[] = "\e[A";
|
||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, msg, strlen(msg));
|
||||
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED)) {
|
||||
sleep(5);
|
||||
continue;
|
||||
}
|
||||
/* On any error (likely EINTR), we avoid starting getty */
|
||||
if (rc == -1)
|
||||
rc = 0;
|
||||
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, msg, strlen(msg));
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, cup, strlen(cup));
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
/* Clear msg and move cursor up for next message/login: */
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, cup, strlen(cup));
|
||||
|
||||
/* Restore TTY */
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig) == -1)
|
||||
ret = 0;
|
||||
rc = 0; /* TTY in bad shape, restart */
|
||||
|
||||
return ret;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -380,32 +386,14 @@ static int activate_console(int noclear, int nowait)
|
||||
* since /bin/login usually only disables ECHO until a password line has
|
||||
* been entered. Upon starting the user's $SHELL the ISIG flag is reset
|
||||
*/
|
||||
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
speed_t speed;
|
||||
int rc = 1;
|
||||
|
||||
speed = stty_parse_speed(baud);
|
||||
prepare_tty(tty, speed, "tty", rlimit);
|
||||
if (activate_console(noclear, nowait)) {
|
||||
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
|
||||
rc = getty(tty, speed, term, NULL);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start external getty as defined by user in .conf file
|
||||
*/
|
||||
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
|
||||
pid_t run_getty(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
/* Dunno speed, tell stty() to not mess with it */
|
||||
prepare_tty(tty, B0, "getty", rlimit);
|
||||
if (activate_console(noclear, nowait)) {
|
||||
logit(LOG_INFO, "Starting external getty on %s", tty);
|
||||
logit(LOG_INFO, "Starting getty on %s", tty);
|
||||
rc = execv(cmd, args);
|
||||
}
|
||||
|
||||
@@ -414,6 +402,34 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, st
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sh(char *tty)
|
||||
{
|
||||
char *args[2] = {
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
char *arg0;
|
||||
size_t len;
|
||||
|
||||
/* The getty process is usually responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
|
||||
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||
len = strlen(_PATH_BSHELL) + 2;
|
||||
arg0 = malloc(len);
|
||||
if (!arg0) {
|
||||
err(1, "Failed allocating memory");
|
||||
return 1;
|
||||
}
|
||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||
args[0] = arg0;
|
||||
|
||||
/* Unblock signals inherited from parent */
|
||||
sig_unblock();
|
||||
|
||||
return execv(_PATH_BSHELL, args);
|
||||
}
|
||||
|
||||
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
int rc = 1;
|
||||
@@ -425,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+354
-216
@@ -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-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,22 +26,29 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#ifdef HAVE_FSTAB_H
|
||||
#include <fstab.h>
|
||||
#endif
|
||||
#include <getopt.h>
|
||||
#ifdef HAVE_MNTENT_H
|
||||
#include <mntent.h>
|
||||
#endif
|
||||
#include <time.h> /* tzet() */
|
||||
#include <sys/klog.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h> /* umask(), mkdir() */
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef HAVE_SYS_SYSMACROS_H
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "devmon.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "plugin.h"
|
||||
@@ -51,21 +58,25 @@
|
||||
#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;
|
||||
int debug = 0; /* debug mode from kernel cmdline */
|
||||
int rescue = 0; /* rescue mode from kernel cmdline */
|
||||
int single = 0; /* single user mode from kernel cmdline */
|
||||
int bootstrap = 1; /* set while bootrapping (for TTYs) */
|
||||
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
|
||||
int syncsec = 0; /* reboot delay */
|
||||
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 */
|
||||
svc_t *wdog = NULL; /* No watchdog by default */
|
||||
@@ -81,7 +92,7 @@ static void banner(void)
|
||||
* similar enabled to start emptying /dev/kmsg, but for
|
||||
* our progress we want to own the console.
|
||||
*/
|
||||
if (!debug)
|
||||
if (!debug && !kerndebug)
|
||||
klogctl(6, NULL, 0);
|
||||
|
||||
/*
|
||||
@@ -90,7 +101,94 @@ static void banner(void)
|
||||
*/
|
||||
plugin_run_hooks(HOOK_BANNER);
|
||||
|
||||
print_banner(INIT_HEADING);
|
||||
#ifdef INIT_OSHEADING
|
||||
osheading = INIT_OSHEADING;
|
||||
if (osheading) {
|
||||
if (!osheading[0])
|
||||
osheading = release_heading();
|
||||
print_banner(osheading);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sulogin(int do_reboot)
|
||||
{
|
||||
int rc = EX_OSFILE;
|
||||
char *cmd[] = {
|
||||
"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;
|
||||
}
|
||||
|
||||
rc = systemf("%s", path);
|
||||
free(path);
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_reboot) {
|
||||
do_shutdown(SHUT_REBOOT);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
char *fs_root_dev(char *real, size_t len)
|
||||
{
|
||||
struct dirent *d;
|
||||
struct stat st;
|
||||
int found = 0;
|
||||
dev_t dev;
|
||||
DIR *dir;
|
||||
|
||||
if (stat("/", &st))
|
||||
return NULL;
|
||||
|
||||
if (S_ISBLK(st.st_mode))
|
||||
dev = st.st_rdev;
|
||||
else
|
||||
dev = st.st_dev;
|
||||
|
||||
dir = opendir("/sys/block");
|
||||
if (!dir)
|
||||
return NULL;
|
||||
|
||||
while ((d = readdir(dir))) {
|
||||
char buf[10];
|
||||
char *ptr;
|
||||
|
||||
if (fnread(buf, sizeof(buf), "/sys/block/%s/dev", d->d_name) == -1)
|
||||
continue;
|
||||
|
||||
ptr = strchr(buf, ':');
|
||||
if (!ptr)
|
||||
continue;
|
||||
*ptr++ = 0;
|
||||
|
||||
if (atoi(buf) == (int)major(dev) && atoi(ptr) == (int)minor(dev)) {
|
||||
/* Guess name, assume no renaming */
|
||||
snprintf(real, len, "/dev/%s", d->d_name);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
if (!found)
|
||||
return NULL;
|
||||
|
||||
return real;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -98,71 +196,136 @@ static void banner(void)
|
||||
*/
|
||||
static int fsck(int pass)
|
||||
{
|
||||
struct fstab *fs;
|
||||
struct mntent mount;
|
||||
struct mntent *mnt;
|
||||
char real[192];
|
||||
char buf[256];
|
||||
int rc = 0;
|
||||
FILE *fp;
|
||||
|
||||
if (!setfsent()) {
|
||||
_pe("Failed opening fstab");
|
||||
return 1;
|
||||
fp = setmntent(fstab, "r");
|
||||
if (!fp) {
|
||||
err(1, "Failed opening fstab: %s", fstab);
|
||||
sulogin(1);
|
||||
}
|
||||
|
||||
while ((fs = getfsent())) {
|
||||
char cmd[80];
|
||||
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;
|
||||
|
||||
if (fs->fs_passno != pass)
|
||||
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);
|
||||
|
||||
if (mnt->mnt_passno == 0 || mnt->mnt_passno != pass)
|
||||
continue;
|
||||
|
||||
/* Device to maybe fsck, */
|
||||
dev = mnt->mnt_fsname;
|
||||
|
||||
errno = 0;
|
||||
if (stat(fs->fs_spec, &st) || !S_ISBLK(st.st_mode)) {
|
||||
if (!string_match(fs->fs_spec, "UUID=") && !string_match(fs->fs_spec, "LABEL=")) {
|
||||
_d("Cannot fsck %s, not a block device: %s", fs->fs_spec, strerror(errno));
|
||||
if (stat(dev, &st) || !S_ISBLK(st.st_mode)) {
|
||||
int skip = 1;
|
||||
|
||||
if (string_match(dev, "UUID=") || string_match(dev, "LABEL="))
|
||||
skip = 0;
|
||||
|
||||
/*
|
||||
* Kernel short form for root= device, figure out
|
||||
* actual device since we cannot rely on symlinks
|
||||
* https://bugs.busybox.net/show_bug.cgi?id=8891
|
||||
*/
|
||||
else if (string_compare(dev, "/dev/root")) {
|
||||
if (fs_root_dev(real, sizeof(real))) {
|
||||
dev = real;
|
||||
skip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
dbg("Cannot fsck %s, not a block device: %s", dev, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ismnt("/proc/mounts", fs->fs_file, "rw")) {
|
||||
_d("Skipping fsck of %s, already mounted rw on %s.", fs->fs_spec, fs->fs_file);
|
||||
if (ismnt("/proc/mounts", mnt->mnt_dir, "rw")) {
|
||||
dbg("Skipping fsck of %s, already mounted rw on %s.", dev, mnt->mnt_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec);
|
||||
rc += run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
||||
#ifdef FSCK_FIX
|
||||
snprintf(cmd, sizeof(cmd), "fsck -yf %s", dev);
|
||||
#else
|
||||
snprintf(cmd, sizeof(cmd), "fsck %s %s %s", fsck_mode, fsck_repair, dev);
|
||||
#endif
|
||||
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
|
||||
* 2 or larger. A return code of 1 indicates that filesystem
|
||||
* errors were corrected but that the boot may proceed.
|
||||
*/
|
||||
if (fsck_rc > 1) {
|
||||
logit(LOG_CONSOLE | LOG_ALERT, "Failed fsck %s, attempting sulogin ...", dev);
|
||||
sulogin(1);
|
||||
}
|
||||
rc += fsck_rc;
|
||||
}
|
||||
|
||||
endfsent();
|
||||
endmntent(fp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsck_all(void)
|
||||
{
|
||||
int pass, rc = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Wrapper for mount(2), logs any errors to stderr */
|
||||
static void fs_mount(const char *src, const char *tgt, const char *fstype,
|
||||
unsigned long flags, const void *data)
|
||||
{
|
||||
const char *msg = !fstype ? "MS_MOVE" : "mounting";
|
||||
int rc;
|
||||
|
||||
rc = mount(src, tgt, fstype, flags, data);
|
||||
if (rc && errno != EBUSY)
|
||||
err(1, "Failed %s %s on %s", msg, src, tgt);
|
||||
}
|
||||
|
||||
#ifndef SYSROOT
|
||||
static void fs_remount_root(int fsckerr)
|
||||
{
|
||||
struct fstab *fs;
|
||||
struct mntent *mnt;
|
||||
FILE *fp;
|
||||
|
||||
if (!setfsent())
|
||||
fp = setmntent(fstab, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
while ((fs = getfsent())) {
|
||||
if (!strcmp(fs->fs_file, "/"))
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(mnt->mnt_dir, "/"))
|
||||
break;
|
||||
}
|
||||
|
||||
/* If / is not listed in fstab, or listed as 'ro', leave it alone */
|
||||
if (!fs || !strcmp(fs->fs_type, "ro"))
|
||||
if (!mnt || hasmntopt(mnt, "ro"))
|
||||
goto out;
|
||||
|
||||
if (fsckerr)
|
||||
@@ -172,7 +335,7 @@ static void fs_remount_root(int fsckerr)
|
||||
"Remounting / as read-write");
|
||||
|
||||
out:
|
||||
endfsent();
|
||||
endmntent(fp);
|
||||
}
|
||||
#else
|
||||
static void fs_remount_root(int fsckerr)
|
||||
@@ -181,8 +344,7 @@ static void fs_remount_root(int fsckerr)
|
||||
* XXX: Untested, in the initramfs age we should
|
||||
* probably use switch_root instead.
|
||||
*/
|
||||
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL))
|
||||
_pe("Failed %s / MS_MOVE");
|
||||
fs_mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
}
|
||||
#endif /* SYSROOT */
|
||||
|
||||
@@ -206,51 +368,127 @@ static void fs_finalize(void)
|
||||
*/
|
||||
if (!fismnt("/dev/shm")) {
|
||||
makedir("/dev/shm", 0777);
|
||||
mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
|
||||
fs_mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
|
||||
}
|
||||
|
||||
/* Modern systems use /dev/pts */
|
||||
if (!fismnt("/dev/pts")) {
|
||||
char opts[32];
|
||||
int mode;
|
||||
int gid;
|
||||
|
||||
gid = getgroup("tty");
|
||||
if (gid == -1)
|
||||
gid = 0;
|
||||
|
||||
/* 0600 is default on Debian, use 0620 to get mesg y by default */
|
||||
mode = 0620;
|
||||
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
|
||||
|
||||
makedir("/dev/pts", 0755);
|
||||
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620,ptmxmode=0666");
|
||||
fs_mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
|
||||
}
|
||||
|
||||
/* Modern systems use tmpfs for /run */
|
||||
if (!fismnt("/run"))
|
||||
mount("tmpfs", "/run", "tmpfs", 0, "mode=0755,nosuid,nodev,noexec,relatime");
|
||||
/*
|
||||
* Modern systems use tmpfs for /run. Fallback to /var/run if
|
||||
* /run doesn't exist is handled by the bootmisc plugin. It
|
||||
* also sets up compat symlinks.
|
||||
*
|
||||
* The unconditional mount of /run/lock is for DoS prevention.
|
||||
* To override any of this behavior, add entries to /etc/fstab
|
||||
* for /run (and optionally /run/lock).
|
||||
*/
|
||||
if (fisdir("/run") && !fismnt("/run")) {
|
||||
fs_mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
|
||||
|
||||
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
|
||||
makedir("/run/lock", 1777);
|
||||
fs_mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880");
|
||||
}
|
||||
|
||||
/* Modern systems use tmpfs for /tmp */
|
||||
if (!fismnt("/tmp"))
|
||||
mount("tmpfs", "/tmp", "tmpfs", 0, "mode=0755,nosuid,nodev");
|
||||
fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
|
||||
}
|
||||
|
||||
static void fs_mount(void)
|
||||
static void fs_swapon(char *cmd, size_t len)
|
||||
{
|
||||
struct mntent *mnt;
|
||||
FILE *fp;
|
||||
|
||||
if (!whichp("swapon"))
|
||||
return;
|
||||
|
||||
fp = setmntent(fstab, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (strcmp(mnt->mnt_type, MNTTYPE_SWAP))
|
||||
continue;
|
||||
|
||||
snprintf(cmd, len, "swapon %s", mnt->mnt_fsname);
|
||||
run_interactive(cmd, "Enabling swap %s", mnt->mnt_fsname);
|
||||
}
|
||||
|
||||
endmntent(fp);
|
||||
}
|
||||
|
||||
static void fs_mount_all(void)
|
||||
{
|
||||
char cmd[256] = "mount -na";
|
||||
|
||||
if (!fstab || !fexist(fstab)) {
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "%s system fstab %s, trying fallback ...",
|
||||
!fstab ? "Missing" : "Cannot find", fstab ?: "\b");
|
||||
fstab = FINIT_FSTAB;
|
||||
}
|
||||
if (!fstab || !fexist(fstab)) {
|
||||
logit(LOG_CONSOLE | LOG_EMERG, "%s system fstab %s, attempting sulogin ...",
|
||||
!fstab ? "Missing" : "Cannot find", fstab ?: "\b");
|
||||
sulogin(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Needed by fsck, both BusyBox and util-linux support this.
|
||||
* We leave it set in the env. for the benefit of any mount
|
||||
* helpers and other system tools that the user expects to
|
||||
* behave even if we've booted with a different fstab.
|
||||
*/
|
||||
setenv("FSTAB_FILE", fstab, 1);
|
||||
|
||||
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 (run_interactive("mount -na", "Mounting filesystems"))
|
||||
if (fstab && strcmp(fstab, "/etc/fstab"))
|
||||
snprintf(cmd, sizeof(cmd), "mount -na -T %s", fstab);
|
||||
|
||||
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);
|
||||
|
||||
run("swapon -ea");
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* We need /proc for rs_remount_root() and conf_parse_cmdline(), /dev
|
||||
* for early multi-console, and /sys for the cgroups support. Any
|
||||
* occurrence of these file systems in /etc/fstab will replace these
|
||||
* mounts later in fs_mount()
|
||||
* mounts later in fs_mount_all()
|
||||
*
|
||||
* Ignore any mount errors with EBUSY, kernel likely alread mounted
|
||||
* Ignore any mount errors with EBUSY, kernel likely already mounted
|
||||
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
|
||||
*/
|
||||
static void fs_init(void)
|
||||
@@ -268,111 +506,20 @@ static void fs_init(void)
|
||||
umask(022);
|
||||
|
||||
for (i = 0; i < NELEMS(fs); i++) {
|
||||
int rc;
|
||||
/*
|
||||
* Check if already mounted, we may be running in a
|
||||
* container, or an initramfs ran before us. The
|
||||
* function fismnt() reliles on /proc/mounts being
|
||||
* unique for each chroot/container.
|
||||
*/
|
||||
if (fismnt(fs[i].file))
|
||||
continue;
|
||||
|
||||
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
if (rc && errno != EBUSY)
|
||||
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
|
||||
fs_mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
/*
|
||||
* Initalize 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 int cnt = 120;
|
||||
static struct wq final = {
|
||||
.cb = finalize,
|
||||
.delay = 10
|
||||
};
|
||||
|
||||
_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
|
||||
*/
|
||||
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
|
||||
service_runlevel(cfglevel);
|
||||
}
|
||||
|
||||
static int version(int rc)
|
||||
{
|
||||
puts(PACKAGE_STRING);
|
||||
@@ -403,7 +550,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;
|
||||
@@ -448,30 +595,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 = {
|
||||
.cb = crank_worker,
|
||||
.delay = 10
|
||||
};
|
||||
struct wq bootstrap = {
|
||||
.cb = bootstrap_worker,
|
||||
.delay = 100
|
||||
};
|
||||
uev_ctx_t loop;
|
||||
|
||||
/* telinit or stand-alone process monitor */
|
||||
if (getpid() != 1)
|
||||
return telinit(argc, argv);
|
||||
/* user calling telinit or init */
|
||||
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
|
||||
@@ -480,17 +623,17 @@ int main(int argc, char *argv[])
|
||||
|
||||
/*
|
||||
* Parse /proc/cmdline (debug, rescue, console=, etc.)
|
||||
* Also calls log_init() to set correct log level
|
||||
*/
|
||||
conf_parse_cmdline(argc, argv);
|
||||
|
||||
/*
|
||||
* Figure out system console(s)
|
||||
* Figure out system console(s) and call log_init() to set
|
||||
* correct log level, possibly finit.debug enabled.
|
||||
*/
|
||||
console_init();
|
||||
|
||||
/*
|
||||
* Initalize event context.
|
||||
* Initialize event context.
|
||||
*/
|
||||
uev_init1(&loop, 1);
|
||||
ctx = &loop;
|
||||
@@ -498,30 +641,18 @@ int main(int argc, char *argv[])
|
||||
/*
|
||||
* Set PATH, SHELL, and PWD early to something sane
|
||||
*/
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
setenv("SHELL", _PATH_BSHELL, 1);
|
||||
conf_reset_env();
|
||||
|
||||
if (chdir("/"))
|
||||
_pe("Failed cd /");
|
||||
err(1, "Failed cd /");
|
||||
|
||||
/*
|
||||
* In case of emergency.
|
||||
*/
|
||||
if (rescue) {
|
||||
char *sulogin[] = {
|
||||
_PATH_SULOGIN,
|
||||
"sulogin",
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(sulogin); i++) {
|
||||
if (systemf(sulogin[i]))
|
||||
continue;
|
||||
|
||||
rescue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef RESCUE_MODE
|
||||
if (rescue)
|
||||
rescue = sulogin(0);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Load plugins early, the first hook is in banner(), so we
|
||||
@@ -535,6 +666,11 @@ int main(int argc, char *argv[])
|
||||
enable_progress(1); /* Allow progress, if enabled */
|
||||
banner();
|
||||
|
||||
if (osheading)
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel S", osheading);
|
||||
else
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel S");
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
@@ -545,35 +681,33 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
cgroup_init(&loop);
|
||||
|
||||
/* Check and mount filesystems. */
|
||||
fs_mount();
|
||||
/*
|
||||
* Check custom fstab from cmdline, including fallback, then run
|
||||
* fsck before mounting all filesystems, on error call sulogin.
|
||||
*/
|
||||
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();
|
||||
|
||||
/* Emit conditions for early hooks that ran before the
|
||||
* condition system was initialized in case anyone . */
|
||||
/*
|
||||
* Emit conditions for early hooks that ran before the condition
|
||||
* system was initialized in case anyone.
|
||||
*/
|
||||
cond_set_oneshot(plugin_hook_str(HOOK_BANNER));
|
||||
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
|
||||
|
||||
/*
|
||||
* 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] name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
|
||||
wdog = svc_find_by_nameid("watchdog", "finit");
|
||||
}
|
||||
|
||||
/* 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
|
||||
@@ -581,19 +715,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);
|
||||
dbg("Starting service interval monitor ...");
|
||||
service_init(&loop);
|
||||
|
||||
_d("Starting bootstrap finalize timer ...");
|
||||
schedule_work(&bootstrap);
|
||||
/*
|
||||
* Initialize state machine and start all bootstrap tasks
|
||||
* NOTE: no network available!
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+22
-8
@@ -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-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,7 +34,11 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include <uev/uev.h>
|
||||
|
||||
/* just in case */
|
||||
@@ -50,17 +54,21 @@
|
||||
#define FINIT_CGPATH "/sys/fs/cgroup"
|
||||
#endif
|
||||
|
||||
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
|
||||
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
|
||||
/* 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 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
|
||||
@@ -77,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
|
||||
@@ -89,6 +97,8 @@
|
||||
#define INIT_CMD_SVC_QUERY 130
|
||||
#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
|
||||
|
||||
@@ -103,16 +113,20 @@ struct init_request {
|
||||
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int cmdlevel;
|
||||
extern int prevlevel;
|
||||
extern int debug;
|
||||
extern int rescue;
|
||||
extern int single;
|
||||
extern int bootstrap;
|
||||
extern char *rcsd;
|
||||
extern int kerndebug;
|
||||
extern int syncsec;
|
||||
extern char *fstab;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
extern char *runparts;
|
||||
extern char *osheading;
|
||||
extern uev_ctx_t *ctx;
|
||||
|
||||
#endif /* FINIT_H_ */
|
||||
|
||||
+225
-83
@@ -1,6 +1,6 @@
|
||||
/* Initialize and serve a login terminal
|
||||
*
|
||||
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,52 +21,139 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h> /* LOGIN_NAME_MAX */
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h> /* sysconf() */
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#ifndef _PATH_LOGIN
|
||||
#define _PATH_LOGIN "/bin/login"
|
||||
#endif
|
||||
|
||||
#ifndef LOGIN_NAME_MIN
|
||||
#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.
|
||||
*/
|
||||
static int readch(char *tty)
|
||||
static int readch(void)
|
||||
{
|
||||
int st;
|
||||
char ch1;
|
||||
int st;
|
||||
|
||||
st = read(STDIN_FILENO, &ch1, 1);
|
||||
if (st == 0) {
|
||||
dprint(STDOUT_FILENO, "\n", 0);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (st < 0)
|
||||
errx(1, "getty: %s: read error", tty);
|
||||
if (st <= 0)
|
||||
return -1;
|
||||
|
||||
return ch1 & 0xFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++) {
|
||||
@@ -75,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;
|
||||
@@ -90,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;
|
||||
@@ -114,37 +239,40 @@ 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the process of a GETTY.
|
||||
*/
|
||||
static void do_getty(char *tty, char *name, size_t len)
|
||||
static int get_logname(char *tty, char *name, size_t len)
|
||||
{
|
||||
int ch;
|
||||
char *np;
|
||||
int ch;
|
||||
|
||||
/*
|
||||
* Display prompt.
|
||||
@@ -152,17 +280,12 @@ static void do_getty(char *tty, char *name, size_t len)
|
||||
ch = ' ';
|
||||
*name = '\0';
|
||||
while (ch != '\n') {
|
||||
do_issue(tty);
|
||||
issue(tty);
|
||||
|
||||
np = name;
|
||||
while ((ch = readch(tty)) != '\n') {
|
||||
if (ch == CTRL('U')) {
|
||||
while (np > name) {
|
||||
dprint(STDOUT_FILENO, "\b \b", 3);
|
||||
np--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
while ((ch = readch()) != '\n') {
|
||||
if (ch < 0)
|
||||
return 1;
|
||||
|
||||
if (np < name + len)
|
||||
*np++ = ch;
|
||||
@@ -174,37 +297,42 @@ static void do_getty(char *tty, char *name, size_t len)
|
||||
}
|
||||
|
||||
name[len - 1] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
cgroup_user(name, 0);
|
||||
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.
|
||||
* Try a starting a rescue shell instead.
|
||||
*
|
||||
* Note: Add /etc/securetty handling.
|
||||
*/
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_BSHELL);
|
||||
if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode))
|
||||
if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode)) {
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_SULOGIN);
|
||||
execl(_PATH_SULOGIN, _PATH_SULOGIN, NULL);
|
||||
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_SULOGIN, _PATH_BSHELL);
|
||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
||||
}
|
||||
|
||||
return 1; /* We shouldn't get here ... */
|
||||
}
|
||||
|
||||
int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
static int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
{
|
||||
const char cln[] = "\r\e[2K\n";
|
||||
char name[30];
|
||||
const char cln[] = "\r\e[K\n";
|
||||
char name[logname_len + 1]; /* +1 for NUL termination */
|
||||
pid_t sid;
|
||||
|
||||
/*
|
||||
* Clean up tty name.
|
||||
@@ -212,59 +340,73 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
if (!strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)))
|
||||
tty += 5;
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(STDIN_FILENO, speed);
|
||||
dprint(STDERR_FILENO, cln, strlen(cln));
|
||||
|
||||
/* The getty process is responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
if (!user)
|
||||
do_getty(tty, name, sizeof(name));
|
||||
else
|
||||
|
||||
/* Replace "Please press enter ..." with login: */
|
||||
dprint(STDERR_FILENO, cln, strlen(cln));
|
||||
|
||||
restart:
|
||||
stty(STDIN_FILENO, speed);
|
||||
if (!user) {
|
||||
if (get_logname(tty, name, sizeof(name)))
|
||||
goto restart;
|
||||
} else
|
||||
strlcpy(name, user, sizeof(name));
|
||||
|
||||
/* check current session associated with our tty */
|
||||
sid = tcgetsid(0);
|
||||
if (sid < 0 || getpid() != sid) {
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) == -1)
|
||||
err(1, "failed stealing controlling TTY");
|
||||
}
|
||||
|
||||
if (term && term[0])
|
||||
setenv("TERM", term, 1);
|
||||
|
||||
return do_login(name);
|
||||
return exec_login(name);
|
||||
}
|
||||
|
||||
int sh(char *tty)
|
||||
static int usage(int rc)
|
||||
{
|
||||
struct termios term;
|
||||
char *args[2] = {
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
char *arg0;
|
||||
size_t len;
|
||||
warnx("usage: getty [-h?p] tty [speed [term]]");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The getty process is usually responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *tty, *speed = "0", *term = NULL;
|
||||
int c;
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(STDIN_FILENO, B0);
|
||||
|
||||
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||
len = strlen(_PATH_BSHELL) + 2;
|
||||
arg0 = malloc(len);
|
||||
if (!arg0)
|
||||
err(1, "Failed allocating memory");
|
||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||
args[0] = arg0;
|
||||
|
||||
/* Reenable Ctrl-D and Ctrl-C, and ... */
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_lflag |= ISIG;
|
||||
term.c_cc[VEOF] = CEOF;
|
||||
term.c_cc[VINTR] = CINTR;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
|
||||
while ((c = getopt(argc, argv, "h?p")) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
case 'p':
|
||||
passenv = 1;
|
||||
break;
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ... unblock signals in general */
|
||||
sig_unblock();
|
||||
if (optind >= argc)
|
||||
return usage(1);
|
||||
|
||||
return execv(_PATH_BSHELL, args);
|
||||
tty = argv[optind++];
|
||||
if (optind < argc)
|
||||
speed = argv[optind++];
|
||||
if (optind < argc)
|
||||
term = argv[optind++];
|
||||
|
||||
logname_len = sysconf(_SC_LOGIN_NAME_MAX);
|
||||
if (logname_len == -1i)
|
||||
logname_len = LOGIN_NAME_MAX;
|
||||
if (logname_len < LOGIN_NAME_MIN)
|
||||
logname_len = LOGIN_NAME_MIN;
|
||||
|
||||
return getty(tty, atoi(speed), term, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+146
-183
@@ -1,7 +1,7 @@
|
||||
/* Misc. utility functions for finit and its plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -22,6 +22,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h> /* isblank() */
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
@@ -30,12 +32,19 @@
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdarg.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "log.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
@@ -53,7 +62,7 @@ static pstyle_t progress_style = PROGRESS_DEFAULT;
|
||||
#define STATUS_CLASS { \
|
||||
CHOOSE(" OK ", " OK ", "\e[1;32m"), /* Green */ \
|
||||
CHOOSE("FAIL", "FAIL", "\e[1;31m"), /* Red */ \
|
||||
CHOOSE("WARN", "WARN", "\e[1;31m"), /* Red */ \
|
||||
CHOOSE("WARN", "WARN", "\e[1;33m"), /* Yellow */ \
|
||||
CHOOSE(" \\/ ", " ⋯ ", "\e[1;33m"), /* Yellow */ \
|
||||
}
|
||||
|
||||
@@ -68,93 +77,17 @@ static const char *status2[] = STATUS_CLASS;
|
||||
#define CHOOSE(x,y,z) z
|
||||
static const char *color[] = STATUS_CLASS;
|
||||
|
||||
/*
|
||||
* System consoles, can be given multiple times on the kernel cmdline.
|
||||
* We support only three currently. If none are given on the cmdline
|
||||
* we probe /sys/class/tty/console/active and use the first (default).
|
||||
*/
|
||||
#define MAX_CONS 3
|
||||
|
||||
static char *consoles[MAX_CONS];
|
||||
static int fds[MAX_CONS];
|
||||
static int num_cons = 0;
|
||||
|
||||
/*
|
||||
* System default console is always the first
|
||||
*/
|
||||
char *console(void)
|
||||
{
|
||||
return consoles[0];
|
||||
}
|
||||
|
||||
static void add_console(char *cons)
|
||||
{
|
||||
char path[80];
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (num_cons == NELEMS(fds)) {
|
||||
_e("too many consoles, max %d supported", NELEMS(fds));
|
||||
return;
|
||||
}
|
||||
|
||||
/* drop any options */
|
||||
ptr = strchr(cons, ',');
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
if (!strncmp(cons, "/dev/", 5))
|
||||
cons += 5;
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/%s", cons);
|
||||
|
||||
/* check for and skip duplicates */
|
||||
for (i = 0; i < num_cons; i++) {
|
||||
if (string_compare(path, consoles[i]))
|
||||
return;
|
||||
}
|
||||
|
||||
/* this requires we have /dev mounted && a device there */
|
||||
fds[num_cons] = open(path, O_WRONLY);
|
||||
if (fds[num_cons] < 0) {
|
||||
_pe("Failed opening console %s", path);
|
||||
return;
|
||||
}
|
||||
consoles[num_cons] = strdup(path);
|
||||
num_cons++;
|
||||
}
|
||||
|
||||
static void find_active_consoles(void)
|
||||
{
|
||||
char *cons, *ptr, *tok;
|
||||
char buf[512];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/sys/class/tty/console/active", "r");
|
||||
if (!fp) {
|
||||
fallback:
|
||||
_d("Using fallback /dev/console");
|
||||
add_console("console");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = fgets(buf, sizeof(buf), fp);
|
||||
fclose(fp);
|
||||
if (!ptr)
|
||||
goto fallback;
|
||||
|
||||
cons = chomp(ptr);
|
||||
_d("Using these system consoles: %s", cons);
|
||||
while ((tok = strtok(cons, " \t"))) {
|
||||
cons = NULL;
|
||||
add_console(tok);
|
||||
}
|
||||
return _PATH_CONSOLE;
|
||||
}
|
||||
|
||||
void console_init(void)
|
||||
{
|
||||
find_active_consoles();
|
||||
ttinit();
|
||||
/* Enable line wrap, if disabled previously, e.g., qemu */
|
||||
dprint(STDOUT_FILENO, "\033[?7h", 5);
|
||||
|
||||
log_init();
|
||||
}
|
||||
|
||||
ssize_t cprintf(const char *fmt, ...)
|
||||
@@ -163,18 +96,27 @@ ssize_t cprintf(const char *fmt, ...)
|
||||
char buf[len < 256 ? 256 : len];
|
||||
size_t size;
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
va_start(ap, fmt);
|
||||
size = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
for (i = 0; i < num_cons; i++)
|
||||
dprint(fds[i], buf, size);
|
||||
dprint(STDERR_FILENO, buf, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void tabstospaces(char *line)
|
||||
{
|
||||
if (!line)
|
||||
return;
|
||||
|
||||
for (int i = 0; line[i]; i++) {
|
||||
if (line[i] == '\t')
|
||||
line[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
char *strip_line(char *line)
|
||||
{
|
||||
char *ptr;
|
||||
@@ -205,6 +147,49 @@ void show_progress(pstyle_t style)
|
||||
progress_onoff = style;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find PRETTY_NAME in /etc/os-release to use as heading at boot
|
||||
* and on runlevel changes. Fallback: "Finit vX.YY"
|
||||
*/
|
||||
char *release_heading(void)
|
||||
{
|
||||
char *name = NULL;
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/etc/os-release", "r");
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *ptr = &buf[12];
|
||||
|
||||
if (strncmp(buf, "PRETTY_NAME=", 12))
|
||||
continue;
|
||||
|
||||
if (*ptr == '"' || *ptr == '\'') {
|
||||
char *end = &ptr[strlen(ptr) - 1];
|
||||
char q = *ptr;
|
||||
|
||||
while (end > ptr && isspace(*end))
|
||||
*end-- = 0;
|
||||
|
||||
if (*end == q) {
|
||||
*end = 0;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
name = strdup(ptr);
|
||||
break;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!name)
|
||||
name = strdup(PACKAGE_NAME " v" PACKAGE_VERSION);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return screen length of string, not counting escape chars, and
|
||||
* accounting for unicode characters as only one screen byte wide
|
||||
@@ -261,7 +246,7 @@ void print_banner(const char *heading)
|
||||
return;
|
||||
|
||||
buf[0] = 0;
|
||||
strlcat(buf, "\r\e[2K", sizeof(buf));
|
||||
strlcat(buf, "\r\e[K", sizeof(buf));
|
||||
|
||||
if (progress_style == PROGRESS_CLASSIC) {
|
||||
strlcat(buf, "\e[1m", sizeof(buf));
|
||||
@@ -346,7 +331,7 @@ void printv(const char *fmt, va_list ap)
|
||||
if (progress_style == PROGRESS_CLASSIC)
|
||||
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
|
||||
else
|
||||
cprintf("\r\e[2K%s%s", status(3), buf);
|
||||
cprintf("\r\e[K%s%s", status(3), buf);
|
||||
}
|
||||
|
||||
void print(int rc, const char *fmt, ...)
|
||||
@@ -385,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;
|
||||
@@ -462,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);
|
||||
@@ -501,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,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) {
|
||||
@@ -527,126 +515,101 @@ void networking(int updown)
|
||||
|
||||
/* Debian/Ubuntu/Busybox/RH/Suse */
|
||||
if (!whichp("ifup"))
|
||||
goto done;
|
||||
goto fallback;
|
||||
|
||||
if (fexist("/etc/network/interfaces")) {
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
int rc = EX_OSERR;
|
||||
const char *cmd;
|
||||
FILE *pp;
|
||||
|
||||
setsid();
|
||||
sig_unblock();
|
||||
|
||||
if (updown)
|
||||
run("ifup -a");
|
||||
cmd = "ifup -a 2>&1";
|
||||
else if (whichp("ifquery"))
|
||||
/* Regular ifodwn supports --force but not -f */
|
||||
cmd = "ifdown -a --force 2>&1";
|
||||
else
|
||||
run("ifdown -a");
|
||||
_exit(0);
|
||||
/* Busybox ifdown support -f, but not --force */
|
||||
cmd = "ifdown -a -f 2>&1";
|
||||
|
||||
pp = popen(cmd, "r");
|
||||
if (pp) {
|
||||
char buf[256];
|
||||
|
||||
while (fgets(buf, sizeof(buf), pp))
|
||||
logit(LOG_NOTICE, "network: %s", chomp(buf));
|
||||
|
||||
rc = pclose(pp);
|
||||
}
|
||||
|
||||
_exit(rc);
|
||||
}
|
||||
cgroup_service("network", pid, NULL);
|
||||
print(pid > 0 ? 0 : 1, "%s network interfaces ...",
|
||||
updown ? "Bringing up" : "Taking down");
|
||||
|
||||
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
fallback:
|
||||
/* Fall back to bring up at least loopback */
|
||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", updown);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_GETFSENT
|
||||
static lfile_t *fstab = NULL;
|
||||
|
||||
int setfsent(void)
|
||||
/*
|
||||
* Very basic, and incomplete, check if we're running in a container.
|
||||
*/
|
||||
int in_container(void)
|
||||
{
|
||||
if (fstab)
|
||||
lfclose(fstab);
|
||||
const char *containers[] = {
|
||||
"lxc",
|
||||
"docker",
|
||||
"kubepod",
|
||||
"unshare"
|
||||
};
|
||||
const char *files[] = {
|
||||
"/run/.containerenv",
|
||||
"/.dockerenv"
|
||||
};
|
||||
static int incont = -1;
|
||||
size_t i;
|
||||
char *c;
|
||||
|
||||
fstab = lfopen("/etc/fstab", " \t\n");
|
||||
if (!fstab)
|
||||
return 0;
|
||||
if (incont > 0)
|
||||
return incont;
|
||||
|
||||
return 1;
|
||||
}
|
||||
c = getenv("container");
|
||||
if (c) {
|
||||
for (i = 0; i < NELEMS(containers); i++) {
|
||||
if (!strcmp(containers[i], c))
|
||||
return incont = 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct fstab *getfsent(void)
|
||||
{
|
||||
static struct fstab fs;
|
||||
|
||||
fs.fs_spec = lftok(fstab);
|
||||
if (fs.fs_spec == NULL)
|
||||
return NULL;
|
||||
|
||||
fs.fs_file = lftok(fstab);
|
||||
fs.fs_vfstype = lftok(fstab);
|
||||
fs.fs_mntops = lftok(fstab);
|
||||
fs.fs_type = "rw";
|
||||
fs.fs_freq = atoi(lftok(fstab) ?: "0");
|
||||
fs.fs_passno = atoi(lftok(fstab) ?: "0");
|
||||
|
||||
return &fs;
|
||||
}
|
||||
|
||||
void endfsent(void)
|
||||
{
|
||||
if (fstab)
|
||||
lfclose(fstab);
|
||||
|
||||
fstab = NULL;
|
||||
}
|
||||
#endif /* HAVE_GETFSENT */
|
||||
|
||||
static int hasopt(char *opts, char *opt)
|
||||
{
|
||||
char buf[strlen(opts) + 1];
|
||||
char *ptr;
|
||||
|
||||
memcpy(buf, opts, sizeof(buf));
|
||||
ptr = strtok(buf, ",");
|
||||
while (ptr) {
|
||||
if (!strcmp(ptr, opt))
|
||||
return 1;
|
||||
|
||||
ptr = strtok(NULL, ",");
|
||||
for (i = 0; i < NELEMS(files); i++) {
|
||||
if (!access(files[i], F_OK))
|
||||
return incont = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ismnt(char *file, char *dir, char *mode)
|
||||
{
|
||||
struct mntent *mnt;
|
||||
int found = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = setmntent(file, "r");
|
||||
if (!fp)
|
||||
return 0; /* Dunno, maybe not */
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(mnt->mnt_dir, dir)) {
|
||||
if (mode) {
|
||||
if (hasopt(mnt->mnt_opts, mode))
|
||||
found = 1;
|
||||
} else
|
||||
found = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Requires /proc to be mounted */
|
||||
int fismnt(char *dir)
|
||||
{
|
||||
return ismnt("/proc/mounts", dir, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
+109
-35
@@ -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-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,14 +25,20 @@
|
||||
#ifndef FINIT_HELPERS_H_
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <mntent.h>
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#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>
|
||||
#include <lite/lite.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
#include "log.h"
|
||||
|
||||
#define PROGRESS_DEFAULT PROGRESS_MODERN
|
||||
@@ -43,40 +49,24 @@ typedef enum {
|
||||
PROGRESS_MODERN,
|
||||
} pstyle_t;
|
||||
|
||||
#ifndef HAVE_GETFSENT
|
||||
struct fstab {
|
||||
char *fs_spec; /* block device name */
|
||||
char *fs_file; /* mount point */
|
||||
char *fs_vfstype; /* file-system type */
|
||||
char *fs_mntops; /* mount options */
|
||||
const char *fs_type; /* rw/rq/ro/sw/xx option */
|
||||
int fs_freq; /* dump frequency, in days */
|
||||
int fs_passno; /* pass number on parallel dump */
|
||||
};
|
||||
|
||||
int setfsent (void);
|
||||
void endfsent (void);
|
||||
struct fstab *getfsent (void);
|
||||
#endif /* HAVE_GETFSENT */
|
||||
|
||||
char *console (void);
|
||||
void console_init (void);
|
||||
ssize_t cprintf (const char *fmt, ...);
|
||||
ssize_t cprintf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
void tabstospaces (char *line);
|
||||
char *strip_line (char *line);
|
||||
|
||||
void enable_progress (int onoff);
|
||||
void show_progress (pstyle_t style);
|
||||
|
||||
int getty (char *tty, speed_t speed, char *term, char *user);
|
||||
int sh (char *tty);
|
||||
char *release_heading (void);
|
||||
|
||||
void stty (int fd, speed_t speed);
|
||||
speed_t stty_parse_speed(char *baud);
|
||||
void stty (int fd, speed_t speed);
|
||||
|
||||
void print_banner (const char *heading);
|
||||
void printv (const char *fmt, va_list ap);
|
||||
void print (int action, const char *fmt, ...);
|
||||
void print (int action, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
void print_desc (char *action, char *desc);
|
||||
int print_result (int fail);
|
||||
|
||||
@@ -90,26 +80,67 @@ int mksubsys (const char *dir, mode_t mode, char *user, char *group);
|
||||
|
||||
void set_hostname (char **hostname);
|
||||
void networking (int updown);
|
||||
int in_container (void);
|
||||
|
||||
int complete (char *cmd, int pid);
|
||||
int run (char *cmd);
|
||||
int run_interactive (char *cmd, char *fmt, ...);
|
||||
int run (char *cmd, char *log);
|
||||
int run_interactive (char *cmd, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
int exec_runtask (char *cmd, char *args[]);
|
||||
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
pid_t run_bg (char *cmd, char *args[]);
|
||||
int run_parts (char *dir, char *cmd, const char *env[], int progress);
|
||||
|
||||
static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
/*
|
||||
* Defaults to user "root" and group "wheel" (root) if:
|
||||
* 1) the user is missing
|
||||
* 2) the group or the file /etc/group is missing
|
||||
*/
|
||||
static inline int create(char *path, mode_t mode, char *user, char *group)
|
||||
{
|
||||
if (touch(path) || chmod(path, mode) || chown(path, uid, gid)) {
|
||||
_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;
|
||||
@@ -128,8 +159,51 @@ static inline int dprint(int fd, const char *s, size_t len)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ismnt (char *file, char *dir, char *mode);
|
||||
int fismnt (char *dir);
|
||||
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_ */
|
||||
|
||||
|
||||
+763
-219
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