mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83915b42f3 | ||
|
|
babcd117b1 | ||
|
|
892ac2edb2 | ||
|
|
5b12d17b56 | ||
|
|
1b6930126e | ||
|
|
8def97a465 | ||
|
|
7bbc0847b1 | ||
|
|
88dc3f0061 | ||
|
|
02e69932ae | ||
|
|
662dfbe777 | ||
|
|
c249cbdbe1 | ||
|
|
a47171891f | ||
|
|
1dc67acd40 | ||
|
|
146685c2c2 | ||
|
|
3cc2df20f9 | ||
|
|
4f3d246100 | ||
|
|
11bfec1a23 | ||
|
|
f24a03a32c | ||
|
|
829ff0b9f7 | ||
|
|
b9ae4d04ef | ||
|
|
47060c9738 | ||
|
|
846692ca03 | ||
|
|
985d53bec4 | ||
|
|
8548e67dcd | ||
|
|
1c60e949e7 | ||
|
|
86f02e8553 | ||
|
|
2c7332583c | ||
|
|
a96b74ea5e | ||
|
|
5a1b13ff41 | ||
|
|
4a71f19c40 | ||
|
|
5c672885f1 | ||
|
|
e38eb7cbd5 | ||
|
|
ba9ae0337d | ||
|
|
b674aad802 | ||
|
|
88f9458c6e | ||
|
|
6cb3d86cd1 | ||
|
|
5cde809909 | ||
|
|
1b6931eaea | ||
|
|
c3b7530b98 | ||
|
|
0f61b0792b | ||
|
|
bab13c9cef | ||
|
|
9c9fe51eee | ||
|
|
4f8211f6a1 | ||
|
|
5fc3a0a3b9 | ||
|
|
67c55145b9 | ||
|
|
fc2489d9e1 | ||
|
|
a384a95529 | ||
|
|
a013e48ab5 | ||
|
|
24a0ddfc59 | ||
|
|
3da4c2abe9 | ||
|
|
2950820d34 | ||
|
|
75b1ac8199 | ||
|
|
8f05b36551 | ||
|
|
0f840e6576 | ||
|
|
451f63ebe0 | ||
|
|
2dc9daf118 | ||
|
|
3c67a10347 | ||
|
|
6885a9f868 | ||
|
|
38142baa93 | ||
|
|
34bdbdb3b1 | ||
|
|
83af7e8e9d | ||
|
|
4eb5f8a0dc | ||
|
|
e03b358ff0 | ||
|
|
d2846b805a | ||
|
|
07d767e1af | ||
|
|
d423e6b774 | ||
|
|
bb89bdd8a9 | ||
|
|
5cc98f6e53 | ||
|
|
a6300f6ac6 | ||
|
|
aeabb4d2ea | ||
|
|
dcbb429863 | ||
|
|
077d52d209 | ||
|
|
44f5de188d | ||
|
|
191e5440b2 | ||
|
|
a2b0d00058 | ||
|
|
e8fa827026 | ||
|
|
89105090ed | ||
|
|
6ce218612b | ||
|
|
dd87c81adc | ||
|
|
37f3478873 | ||
|
|
a64259362e | ||
|
|
3554138118 | ||
|
|
f7d6d269c7 | ||
|
|
2c1e9877f9 | ||
|
|
a94c97221e | ||
|
|
f9732a6fb9 | ||
|
|
581fc956be | ||
|
|
ee901b018c | ||
|
|
d373ebfdd7 | ||
|
|
698b87dd35 | ||
|
|
dd566acfd2 | ||
|
|
5c56f5c5a7 | ||
|
|
7ab963a576 | ||
|
|
73d462c847 | ||
|
|
56e94a4e4a | ||
|
|
51830e0f5b | ||
|
|
85115eca62 | ||
|
|
659ff9962e | ||
|
|
9436c8b73c | ||
|
|
5a3397419f | ||
|
|
10b72f1d7f | ||
|
|
6b237b7b00 | ||
|
|
b4f385d2e0 | ||
|
|
4b4572de4d | ||
|
|
19b81cf434 | ||
|
|
3023433def | ||
|
|
49968c188b | ||
|
|
2499405b27 | ||
|
|
9fc1f50678 | ||
|
|
b1d709ba22 | ||
|
|
5ddc35d387 | ||
|
|
50632f941b | ||
|
|
7b215e4fd4 | ||
|
|
bb7208c591 | ||
|
|
bb0505f57c | ||
|
|
4aa569de44 | ||
|
|
512a07a25c | ||
|
|
8cfd11c909 | ||
|
|
c3adf3bf51 | ||
|
|
3ca28e765d | ||
|
|
79207dcb22 | ||
|
|
47f3e2ca04 | ||
|
|
2e7bee9f84 | ||
|
|
2681e4c01a | ||
|
|
56a3f6882b | ||
|
|
88d8ab67c9 | ||
|
|
74eb2bf4d2 | ||
|
|
b3fb9ab3ed | ||
|
|
308ae609c4 | ||
|
|
e3783e8bac | ||
|
|
002b24fbc2 | ||
|
|
3f71ed352d | ||
|
|
f7305c36b8 | ||
|
|
8bcecd474b | ||
|
|
a9a7b08021 | ||
|
|
62cb683af0 | ||
|
|
65e44dc88f | ||
|
|
54467d4467 | ||
|
|
cfa258e7f2 | ||
|
|
574760df13 | ||
|
|
c2ec25efd3 | ||
|
|
d670e7d3e8 | ||
|
|
77561b6071 | ||
|
|
cec1c64755 | ||
|
|
5bab329178 | ||
|
|
b6bf8bf518 | ||
|
|
0a21ff5236 | ||
|
|
3853061f09 | ||
|
|
078982d3ec | ||
|
|
f4327910d9 | ||
|
|
b1fb536468 | ||
|
|
95cb94cf49 | ||
|
|
e1c0e7fe58 | ||
|
|
9783e120af | ||
|
|
3ad149fb28 | ||
|
|
e6430aabc2 | ||
|
|
2743a49d91 | ||
|
|
6f99c1db05 | ||
|
|
0eafe8bcd9 | ||
|
|
b01fae03ec | ||
|
|
dbbe6b31b8 | ||
|
|
2718c709aa | ||
|
|
8c54fde36c | ||
|
|
0d1bc167ff | ||
|
|
f8def336d4 | ||
|
|
addc21bdf6 | ||
|
|
27899feea0 | ||
|
|
a016359891 | ||
|
|
7cdd42195b | ||
|
|
07f06c8b95 | ||
|
|
2067fd597f | ||
|
|
f0318b0df0 | ||
|
|
508c7b225a | ||
|
|
8a79b05a97 | ||
|
|
7b07d5559c | ||
|
|
23cdd6e438 | ||
|
|
576cb1075c | ||
|
|
fc74fb110d | ||
|
|
baa914e995 | ||
|
|
6490735ee9 | ||
|
|
13159ea99a | ||
|
|
b9fa0f85a2 | ||
|
|
dbbe3cecb3 | ||
|
|
1a8f1d9604 | ||
|
|
d76ecad517 | ||
|
|
f81750e0b1 | ||
|
|
7fc7faf59a | ||
|
|
c38b452ac4 | ||
|
|
7eac88b639 | ||
|
|
f374f443f4 | ||
|
|
d2a5e65862 | ||
|
|
501deae4ed | ||
|
|
2c29893558 | ||
|
|
a1a7f69349 | ||
|
|
8d9fbea551 | ||
|
|
1d89e1a706 | ||
|
|
596b7cc413 | ||
|
|
fbed5cd8cb | ||
|
|
0cb1ce2483 | ||
|
|
e23b62ffb7 | ||
|
|
94d41e38f8 | ||
|
|
42800a1cc8 | ||
|
|
4cf60b7491 | ||
|
|
72a9832662 | ||
|
|
eeb59eb22b | ||
|
|
0df0f019e3 | ||
|
|
bc7f6f1bc1 | ||
|
|
73b807a007 | ||
|
|
62113a0057 | ||
|
|
968f1bf991 | ||
|
|
57514130fc | ||
|
|
0710fdd2a3 | ||
|
|
08e46782e5 | ||
|
|
b96d8d3055 | ||
|
|
4ede2a93b2 | ||
|
|
dd2eb9ce04 | ||
|
|
d15633781b | ||
|
|
789354ab40 | ||
|
|
8002704a5f | ||
|
|
82853bf0ea | ||
|
|
bfe9e011bd | ||
|
|
9416e14074 | ||
|
|
46a58f058a | ||
|
|
72a484b639 | ||
|
|
82e483d6c7 | ||
|
|
dafa50ee42 | ||
|
|
2187e05e06 | ||
|
|
a4fd058607 | ||
|
|
c49eb5e10d | ||
|
|
01d1dabaae | ||
|
|
a3b0022d66 | ||
|
|
e4d882e99e | ||
|
|
5ef426d424 | ||
|
|
5ee1b7c322 | ||
|
|
0d665f3773 | ||
|
|
38619966ee | ||
|
|
6380f17d1f | ||
|
|
42181a3031 | ||
|
|
77b32ebff0 | ||
|
|
97de3ad0bb | ||
|
|
44f8a2666f | ||
|
|
4cf5b055cf | ||
|
|
744bb9dd14 | ||
|
|
1edfecdaab | ||
|
|
bc84d66018 | ||
|
|
2216923343 | ||
|
|
ac834cfa1c | ||
|
|
f191266a33 | ||
|
|
796275af62 | ||
|
|
a7e52914d2 | ||
|
|
fcfbd57ca8 | ||
|
|
5b81d14e51 | ||
|
|
329af172de | ||
|
|
33ce1592c1 | ||
|
|
7a954e3743 | ||
|
|
48c2937f4f | ||
|
|
24961eb3ba | ||
|
|
aced97c2e6 | ||
|
|
a67350ebf6 | ||
|
|
c47a0cd812 | ||
|
|
67c0d43912 | ||
|
|
7b00969f6c | ||
|
|
875080d1e1 | ||
|
|
1121965167 | ||
|
|
45bdadebb6 | ||
|
|
7dfdc2395b | ||
|
|
5723b2e75b | ||
|
|
d4fdf50636 | ||
|
|
4f7366640b | ||
|
|
90497af7bb | ||
|
|
cd0983a7f6 | ||
|
|
c7573fedad | ||
|
|
2b74fb0e93 | ||
|
|
eb57457522 | ||
|
|
f0cda83d24 | ||
|
|
fbacae7071 | ||
|
|
48e2cbd183 | ||
|
|
8f9ea0ae57 | ||
|
|
1c0776b818 | ||
|
|
6ff9cb2ff3 | ||
|
|
fbef03a4c6 | ||
|
|
66f22a0a5f | ||
|
|
171ae7dc85 | ||
|
|
ebfa8f061c | ||
|
|
2baa4f8ea9 | ||
|
|
38203ab9e8 | ||
|
|
88a4ebf9e7 | ||
|
|
51f61939da | ||
|
|
7262a25c85 | ||
|
|
8b9309ee41 | ||
|
|
affc6a97b9 | ||
|
|
ec041b3c02 | ||
|
|
267eeb9692 | ||
|
|
58e00fe52e | ||
|
|
a40301ef86 | ||
|
|
3a86db7d9f | ||
|
|
927956bf78 | ||
|
|
05938a88ce | ||
|
|
4a8a92af2e | ||
|
|
79b95daf3a | ||
|
|
ea21bfc4c9 | ||
|
|
e9da876a10 | ||
|
|
ef7f0b100b | ||
|
|
33ccff7733 | ||
|
|
00e5a8f5d9 | ||
|
|
f74a73be20 | ||
|
|
34d8848b5c | ||
|
|
662f364d09 | ||
|
|
e53acd1cdd | ||
|
|
d96d1a3023 | ||
|
|
f249210c04 | ||
|
|
969ecffc05 | ||
|
|
cc15d3f77b | ||
|
|
ad2bafbfe8 | ||
|
|
c5fad8e659 | ||
|
|
c1f725340d | ||
|
|
29315090f9 | ||
|
|
60df2043ac | ||
|
|
d48da81026 | ||
|
|
5b86c76694 | ||
|
|
364f3bbafb | ||
|
|
9a539c636a | ||
|
|
ca830b9a09 | ||
|
|
f2b0951d78 | ||
|
|
6d84fd010e | ||
|
|
110ee220e7 | ||
|
|
f29dff691f | ||
|
|
a1e6febc4f | ||
|
|
df1fbbc996 | ||
|
|
448a9e1198 | ||
|
|
80624d3ff8 | ||
|
|
4b0f78c1b5 | ||
|
|
5ea3cc0090 | ||
|
|
7f5f8ba09d | ||
|
|
efd265c9c8 | ||
|
|
7e91cb4afc | ||
|
|
9a03adce5a | ||
|
|
06d1e90bac | ||
|
|
439d9df122 | ||
|
|
f44ac5a023 | ||
|
|
84c75c4416 | ||
|
|
ee8a69f5ed | ||
|
|
acf6cb6900 | ||
|
|
f51183d5f2 | ||
|
|
ff5814e4d4 | ||
|
|
831d0c5520 | ||
|
|
d9cc21b3c8 | ||
|
|
096a3eff51 | ||
|
|
889bce1768 | ||
|
|
0ba8df5c2d | ||
|
|
ec2505191b | ||
|
|
3de9e4e5db | ||
|
|
1177c734ce | ||
|
|
f7f222c924 | ||
|
|
dd8715f3dc | ||
|
|
3e93ed2ead | ||
|
|
e50e12417e | ||
|
|
9e70a12273 | ||
|
|
b313e623df | ||
|
|
376d7a0fbb | ||
|
|
1398536194 | ||
|
|
95135ca0a7 | ||
|
|
ec31930638 | ||
|
|
ed8b21db36 | ||
|
|
b1cd3a2332 | ||
|
|
5675d068f8 | ||
|
|
4baf2611f6 | ||
|
|
13d4821abe | ||
|
|
1f1c2e0466 | ||
|
|
c4af8b7cbd | ||
|
|
426d915c08 | ||
|
|
86be6812b9 | ||
|
|
4efdc4fe19 | ||
|
|
da76cf4f75 | ||
|
|
a2177a8063 | ||
|
|
cfa155deae | ||
|
|
e7832e2ca7 | ||
|
|
d7d45b65d0 | ||
|
|
4ae5c54f45 | ||
|
|
7b8944730d | ||
|
|
87ca6574b8 | ||
|
|
35844f2d1d | ||
|
|
2e2d87ee78 | ||
|
|
ba11d218bc | ||
|
|
707c42db63 | ||
|
|
4cfe4781a3 | ||
|
|
8f415aeaf6 | ||
|
|
3f3220e806 | ||
|
|
e54c7eef65 | ||
|
|
05cc6bf5d2 | ||
|
|
4fecb8621d | ||
|
|
6a49f7ed8c | ||
|
|
8bcc34ce91 | ||
|
|
8da1505454 | ||
|
|
34e4d31219 | ||
|
|
6489dcc967 | ||
|
|
2d6769005a | ||
|
|
98cd6dffb1 | ||
|
|
eb86a76a0d | ||
|
|
4423a2b16e | ||
|
|
0467c571df | ||
|
|
cef4b5761a | ||
|
|
806d05622a | ||
|
|
57fb4738f1 | ||
|
|
0bbc69200b | ||
|
|
726f0d428d | ||
|
|
0704947c33 | ||
|
|
2555fe8e96 | ||
|
|
c41016ef32 | ||
|
|
5d86137142 | ||
|
|
bd58345b8c | ||
|
|
994b589377 | ||
|
|
caff08f58d | ||
|
|
d58c58d972 | ||
|
|
a97499999e | ||
|
|
9c7046a25d | ||
|
|
cd5d55ef51 | ||
|
|
5eb172ed46 | ||
|
|
3d99940252 | ||
|
|
fce420121c | ||
|
|
6c91c4ec12 | ||
|
|
2cb5997abe | ||
|
|
5afb47bc9b | ||
|
|
d6d92f1df7 | ||
|
|
4899bba06b | ||
|
|
97a160dd1d | ||
|
|
901bdc68ba | ||
|
|
c6055031b8 | ||
|
|
e8d699c194 | ||
|
|
80273555c5 | ||
|
|
6ece4b8fa3 | ||
|
|
0beb566e45 | ||
|
|
c641a1374e | ||
|
|
171d611b14 | ||
|
|
02eb68f46c | ||
|
|
7fe4c3355f | ||
|
|
5b28fed321 | ||
|
|
aa1da0e9f2 | ||
|
|
2232aea0f7 | ||
|
|
7a3408c173 | ||
|
|
af7b6c17cb | ||
|
|
a822a7657c | ||
|
|
e46271c1a9 | ||
|
|
1028725079 | ||
|
|
ed5ee667ae | ||
|
|
4c3e053041 | ||
|
|
a5c5a5c644 | ||
|
|
fa3a97366c | ||
|
|
5b3e3da7ff | ||
|
|
59d96f264d | ||
|
|
7027cbc439 |
+22
-2
@@ -1,8 +1,13 @@
|
||||
*~
|
||||
*.o
|
||||
*.d
|
||||
*.so
|
||||
*.gz
|
||||
*.map
|
||||
configure
|
||||
Makefile
|
||||
Makefile.in
|
||||
/.deps/*
|
||||
/.libs/*
|
||||
/finit
|
||||
/initctl
|
||||
/reboot
|
||||
@@ -12,4 +17,19 @@
|
||||
/GRTAGS
|
||||
/GSYMS
|
||||
/GTAGS
|
||||
/contrib/*
|
||||
/autom4te.cache/*
|
||||
/aclocal.m4
|
||||
/ar-lib
|
||||
/compile
|
||||
/config.guess
|
||||
/config.h.in
|
||||
/config.log
|
||||
/config.status
|
||||
/config.sub
|
||||
/depcomp
|
||||
/install-sh
|
||||
/libtool
|
||||
/ltmain.sh
|
||||
|
||||
/missing
|
||||
/stamp-h1
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
[submodule "libuev"]
|
||||
path = libuev
|
||||
url = https://github.com/troglobit/libuev.git
|
||||
[submodule "libite"]
|
||||
path = libite
|
||||
url = https://github.com/troglobit/libite.git
|
||||
|
||||
+35
-7
@@ -2,9 +2,17 @@
|
||||
# Defaults to GNU GCC and autotools: ./configure && make && make test
|
||||
language: c
|
||||
|
||||
# We don't need to install packages, use dockerized build, quicker
|
||||
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
|
||||
sudo: false
|
||||
|
||||
# Use non-docker builds atm. to install libuEv and libite from source
|
||||
#sudo: true
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
@@ -12,17 +20,37 @@ env:
|
||||
- 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: "./configure"
|
||||
build_command: "make"
|
||||
branch_pattern: dev
|
||||
build_command_prepend: "./autogen.sh && PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp"
|
||||
build_command: "make"
|
||||
branch_pattern: dev
|
||||
|
||||
install:
|
||||
- wget https://github.com/troglobit/libuev/releases/download/v1.5.2/libuev-1.5.2.tar.xz
|
||||
- wget https://github.com/troglobit/libite/releases/download/v1.8.2/libite-1.8.2.tar.xz
|
||||
- tar xf libuev-1.5.2.tar.xz
|
||||
- tar xf libite-1.8.2.tar.xz
|
||||
- (cd libuev-1.5.2 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- (cd libite-1.8.2 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
- make distclean
|
||||
- ./configure
|
||||
- make
|
||||
- ./autogen.sh
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp
|
||||
- cat config.log
|
||||
- make clean
|
||||
- make -j5 V=1
|
||||
- make install-strip
|
||||
- tree /tmp
|
||||
- ldd /tmp/sbin/finit
|
||||
- size /tmp/sbin/finit
|
||||
- size /tmp/sbin/initctl
|
||||
- size /tmp/sbin/reboot
|
||||
- LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
|
||||
|
||||
+64
-1
@@ -3,6 +3,66 @@ Change Log
|
||||
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
[3.0][UNRELEASED] - 2016-XX-YY
|
||||
------------------------------
|
||||
|
||||
### Changes
|
||||
|
||||
* Manage service states based on user defined conditions
|
||||
* Manage dependencies between services, w/ conditions (pidfile plugin)
|
||||
* Manage service dependenceis on network events (netlink plugin)
|
||||
* Support for dynamically reloading Finit configuration at runtime
|
||||
* Refactor to use GNU configure and build system
|
||||
* New hooks for for detecting lost and started services (lost plugin)
|
||||
* External libraries, libuEv and libite, now build requirements
|
||||
* Early logging support to `/dev/kmsg` instead of console
|
||||
* Support for redirecting stdout/stderr of services to syslog
|
||||
* Support for managing resource limits for Finit and its processes
|
||||
* Add optional built-in inetd services: echo server, chargen, etc.
|
||||
* Add simple built-in getty
|
||||
* Greatly improved accounting support, both UTMP and WTMP fixes+features
|
||||
* Improved udev support, on non-embedded systems
|
||||
* Improvide shutdown and file-system unmount support (Debian)
|
||||
* Added `--enable-emergency-shell` (configure) fallback debug mode
|
||||
* Support SysV init `/etc/rc.local`
|
||||
* Inetd protection against UDP looping attacks
|
||||
* Support systems with `/run` instead of `/var/run` (bootmisc plugin)
|
||||
* Adopted BusyBox init signals for halt/reboot/poweroff
|
||||
* SysV init compat support for reboot (setenv)
|
||||
* Compat support for musl libc
|
||||
* Add OpenRC-like support for sysctl.d/*.conf
|
||||
* Add support for Debian/BusyBox `/etc/network/interfaces`
|
||||
* Add support for running fsck on file systems in `/etc/fstab`
|
||||
* Added example configs + HowTos for Debian (Jessie) and Alpine Linux
|
||||
|
||||
|
||||
[2.4][] - 2015-12-04
|
||||
--------------------
|
||||
|
||||
Bug fix release.
|
||||
|
||||
### Changes
|
||||
|
||||
* 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
|
||||
* Update section mentioning BusyBox getty
|
||||
* Update debugging documentation
|
||||
* Allow debug to override terse mode
|
||||
* Revert confusing change in service state introduced in [v2.3][2.3].
|
||||
As of [v2.4][2.4] services are listed as "halted" and "stopped", when
|
||||
they have been halted due to a runlevel changed or stopped by the
|
||||
user, respectively.
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix system freeze at reconfiguration. Changed services that
|
||||
all support `SIGHUP` caused a freeze due to Finit waiting for
|
||||
them to stop.
|
||||
* Make sure to start and/or `SIGHUP` services after reconfiguration
|
||||
when there was no services to stop.
|
||||
|
||||
|
||||
[2.3][] - 2015-11-28
|
||||
--------------------
|
||||
|
||||
@@ -454,7 +514,10 @@ Major bug fix release.
|
||||
|
||||
* Initial release
|
||||
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/2.1...HEAD
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/2.4...HEAD
|
||||
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
|
||||
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
|
||||
[2.2]: https://github.com/troglobit/finit/compare/2.1...2.2
|
||||
[2.1]: https://github.com/troglobit/finit/compare/2.0...2.1
|
||||
[2.0]: https://github.com/troglobit/finit/compare/1.12...2.0
|
||||
[1.12]: https://github.com/troglobit/finit/compare/1.11...1.12
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
# Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
#
|
||||
# Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
# Copyright (c) 2008-2015 Joachim Nilsson <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.
|
||||
|
||||
.PHONY: all install clean distclean dist \
|
||||
install-exec install-data install-dev \
|
||||
uninstall-exec uninstall-data uninstall-dev
|
||||
|
||||
#VERSION = $(shell git tag -l | tail -1)
|
||||
VERSION = 2.3
|
||||
NAME = finit
|
||||
PKG = $(NAME)-$(VERSION)
|
||||
DEV = $(NAME)-dev
|
||||
ARCHTOOL = `which git-archive-all`
|
||||
ARCHIVE = $(PKG).tar
|
||||
ARCHIVEZ = ../$(ARCHIVE).xz
|
||||
EXEC = finit initctl reboot
|
||||
HEADERS = finit.h plugin.h svc.h inetd.h helpers.h queue.h
|
||||
DISTFILES = LICENSE README ChangeLog finit.conf services
|
||||
OBJS = finit.o api.o client.o conf.o exec.o helpers.o pid.o sig.o \
|
||||
svc.o service.o plugin.o tty.o inetd.o event.o
|
||||
DEPLIBS =
|
||||
TOPDIR = $(shell pwd)
|
||||
-include config.mk
|
||||
|
||||
# Figure out source and dependency files
|
||||
SRCS = $(OBJS:.o=.c)
|
||||
DEPS = $(SRCS:.c=.d)
|
||||
|
||||
CFLAGS += -W -Wall
|
||||
# Disable annoying gcc warning for "warn_unused_result", see GIT 37af997
|
||||
CPPFLAGS += -U_FORTIFY_SOURCE
|
||||
CPPFLAGS += -I$(TOPDIR)
|
||||
CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
|
||||
|
||||
include common.mk
|
||||
|
||||
|
||||
all: config.h $(EXEC)
|
||||
+$(MAKE) -C plugins $@
|
||||
|
||||
$(DEPLIBS): Makefile
|
||||
+$(MAKE) -C `dirname $@` all
|
||||
|
||||
$(OBJS): Makefile
|
||||
|
||||
config.h: configure
|
||||
@echo "Please run configure script first."
|
||||
@exit 1
|
||||
|
||||
finit: $(OBJS) $(DEPLIBS)
|
||||
|
||||
initctl: initctl.o svc.o helpers.o $(DEPLIBS)
|
||||
|
||||
reboot: reboot.o $(DEPLIBS)
|
||||
|
||||
install-exec: all
|
||||
@$(INSTALL) -d $(DESTDIR)$(FINIT_RCSD)
|
||||
@$(INSTALL) -d $(DESTDIR)$(sbindir)
|
||||
@for file in $(EXEC); do \
|
||||
printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \
|
||||
$(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \
|
||||
done
|
||||
@printf " INSTALL $(DESTDIR)$(sbindir)/service\n"
|
||||
@$(INSTALL) -m 0755 service $(DESTDIR)$(sbindir)/service
|
||||
printf " INSTALL $(DESTDIR)$(sbindir)/telinit\n"
|
||||
@ln -sf finit $(DESTDIR)$(sbindir)/telinit
|
||||
$(MAKE) -C plugins install
|
||||
ifndef LIBITE
|
||||
$(MAKE) -C libite install-exec
|
||||
endif
|
||||
|
||||
install-data:
|
||||
@$(INSTALL) -d $(DESTDIR)$(datadir)
|
||||
@$(INSTALL) -d $(DESTDIR)$(mandir)
|
||||
@for file in $(DISTFILES); do \
|
||||
printf " INSTALL $(DESTDIR)$(datadir)/$$file\n"; \
|
||||
$(INSTALL) -m 0644 $$file $(DESTDIR)$(datadir)/$$file; \
|
||||
done
|
||||
|
||||
install-dev:
|
||||
@$(INSTALL) -d $(DESTDIR)$(incdir)
|
||||
@for file in $(HEADERS); do \
|
||||
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
|
||||
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
|
||||
done
|
||||
ifndef LIBITE
|
||||
$(MAKE) -C libite install-dev
|
||||
endif
|
||||
ifndef LIBUEV
|
||||
$(MAKE) -C libuev install-dev
|
||||
endif
|
||||
|
||||
install: install-exec install-data install-dev
|
||||
|
||||
uninstall-exec:
|
||||
-@for file in $(EXEC); do \
|
||||
printf " REMOVE $(DESTDIR)$(sbindir)/$$file\n"; \
|
||||
rm $(DESTDIR)$(sbindir)/$$file 2>/dev/null; \
|
||||
done
|
||||
-@for file in service telinit; do \
|
||||
printf " REMOVE $(DESTDIR)$(sbindir)/$$file\n"; \
|
||||
rm $(DESTDIR)$(sbindir)/$$file 2>/dev/null; \
|
||||
done
|
||||
-@rmdir $(DESTDIR)$(sbindir) 2>/dev/null
|
||||
-@rmdir $(DESTDIR)$(FINIT_RCSD) 2>/dev/null
|
||||
$(MAKE) -C plugins uninstall
|
||||
ifndef LIBITE
|
||||
$(MAKE) -C libite uninstall
|
||||
endif
|
||||
|
||||
uninstall-data:
|
||||
-@for file in $(DISTFILES); do \
|
||||
printf " REMOVE $(DESTDIR)$(datadir)/$$file\n"; \
|
||||
rm $(DESTDIR)$(datadir)/$$file 2>/dev/null; \
|
||||
done
|
||||
-@rmdir $(DESTDIR)$(mandir) 2>/dev/null
|
||||
-@rmdir $(DESTDIR)$(datadir) 2>/dev/null
|
||||
|
||||
uninstall-dev:
|
||||
-@$(RM) -rf $(DESTDIR)$(incdir) 2>/dev/null
|
||||
|
||||
uninstall: uninstall-exec uninstall-data uninstall-dev
|
||||
|
||||
clean:
|
||||
+$(MAKE) -C plugins $@
|
||||
+$(MAKE) -C libite $@
|
||||
+$(MAKE) -C libuev $@
|
||||
-@$(RM) $(OBJS) $(DEPS) $(EXEC) 2>/dev/null
|
||||
|
||||
distclean: clean
|
||||
+$(MAKE) -C plugins $@
|
||||
+$(MAKE) -C libite $@
|
||||
+$(MAKE) -C libuev $@
|
||||
-@$(RM) $(JUNK) config.mk config.h unittest *.o *.html 2>/dev/null
|
||||
|
||||
check:
|
||||
$(CHECK) *.c plugins/*.c
|
||||
|
||||
dist:
|
||||
@if [ x"$(ARCHTOOL)" = x"" ]; then \
|
||||
echo "Missing git-archive-all from https://github.com/Kentzo/git-archive-all"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -e $(ARCHIVEZ) ]; then \
|
||||
echo "Distribution already exists."; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "Building xz tarball of $(PKG) in parent dir..."
|
||||
@$(ARCHTOOL) ../$(ARCHIVE)
|
||||
@xz ../$(ARCHIVE)
|
||||
@md5sum $(ARCHIVEZ) | tee $(ARCHIVEZ).md5
|
||||
|
||||
dev: distclean
|
||||
@echo "Building unstable xz $(DEV) in parent dir..."
|
||||
-@$(RM) -f ../$(DEV).tar.xz*
|
||||
@(dir=`mktemp -d`; mkdir $$dir/$(DEV); cp -a . $$dir/$(DEV); \
|
||||
cd $$dir; tar --exclude=.git --exclude=contrib \
|
||||
-c -J -f $(DEV).tar.xz $(DEV); \
|
||||
cd - >/dev/null; mv $$dir/$(DEV).tar.xz ../; cd ..; \
|
||||
rm -rf $$dir; md5sum $(DEV).tar.xz | tee $(DEV).tar.xz.md5)
|
||||
|
||||
ifneq ($(MAKECMDGOALS),distclean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
sbin_PROGRAMS = finit initctl reboot
|
||||
finit_SOURCES = api.c \
|
||||
cond.c cond-w.c cond.h \
|
||||
client.c \
|
||||
conf.c conf.h \
|
||||
exec.c finit.c finit.h \
|
||||
getty.c \
|
||||
helpers.c helpers.h \
|
||||
mdadm.c mount.c pid.c \
|
||||
plugin.c plugin.h private.h \
|
||||
service.c service.h \
|
||||
sig.c sig.h \
|
||||
sm.c sm.h \
|
||||
svc.c svc.h \
|
||||
tty.c tty.h \
|
||||
util.c util.h \
|
||||
utmp-api.c utmp-api.h
|
||||
pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h plugin.h svc.h
|
||||
if INETD
|
||||
finit_SOURCES += inetd.c inetd.h
|
||||
endif
|
||||
if WATCHDOGD
|
||||
finit_SOURCES += watchdog.c watchdog.h
|
||||
endif
|
||||
|
||||
finit_CFLAGS = -W -Wall -Wextra -std=gnu99 $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
if STATIC
|
||||
finit_LDADD += plugins/libplug.la
|
||||
else
|
||||
finit_LDADD += -ldl
|
||||
endif
|
||||
|
||||
initctl_SOURCES = initctl.c svc.c svc.h helpers.c helpers.h cond.c cond.h \
|
||||
util.c util.h utmp-api.c utmp-api.h
|
||||
initctl_CFLAGS = $(lite_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS)
|
||||
|
||||
reboot_SOURCES = reboot.c util.c util.h
|
||||
|
||||
INIT_LNKS = telinit init
|
||||
REBOOT_LNKS = shutdown halt poweroff suspend
|
||||
SUBDIRS = plugins
|
||||
doc_DATA = README.md LICENSE finit.conf
|
||||
EXTRA_DIST = README.md LICENSE ChangeLog.md finit.conf
|
||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
|
||||
if STATIC
|
||||
AM_CPPFLAGS += -DENABLE_STATIC
|
||||
AM_LDFLAGS = -static-libtool-libs
|
||||
else
|
||||
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
|
||||
AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
# Compat hook
|
||||
install-dev: install-pkgincludeHEADERS
|
||||
|
||||
# Hook in install to add finit and reboot symlink(s)
|
||||
install-exec-hook:
|
||||
@for file in $(INIT_LNKS); do \
|
||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ -e $$lnk ]; then \
|
||||
echo "Skipping $$lnk exists already."; \
|
||||
else \
|
||||
$(LN_S) finit $$lnk; \
|
||||
fi; \
|
||||
done
|
||||
@if [ -L $(DESTDIR)$(sbindir)/reboot ]; then \
|
||||
echo "Non-finit reboot, skipping install."; \
|
||||
else \
|
||||
for file in $(REBOOT_LNKS); do \
|
||||
link=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ -e $$link ]; then \
|
||||
echo "$$link exists, skipping.";\
|
||||
else \
|
||||
$(LN_S) reboot $$link; \
|
||||
fi; \
|
||||
done; \
|
||||
fi
|
||||
|
||||
uninstall-hook:
|
||||
@for file in $(INIT_LNKS); do \
|
||||
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
|
||||
if [ "$$lnk" != "finit" ]; then \
|
||||
echo "Skipping non-finit $$file."; \
|
||||
continue; \
|
||||
fi; \
|
||||
$(RM) $(DESTDIR)$(sbindir)/$$file; \
|
||||
done
|
||||
@if [ -L $(DESTDIR)$(sbindir)/reboot ]; then \
|
||||
echo "Non-finit reboot, skipping uninstall."; \
|
||||
else \
|
||||
for file in $(REBOOT_LNKS); do \
|
||||
if [ "`readlink $$file`" != "reboot" ]; \
|
||||
then continue; \
|
||||
fi; \
|
||||
$(RM) $(DESTDIR)$(sbindir)/$$file; \
|
||||
done; \
|
||||
fi
|
||||
|
||||
# Target to run when building a release
|
||||
release: dist
|
||||
@for file in $(DIST_ARCHIVES); do \
|
||||
md5sum $$file > ../$$file.md5; \
|
||||
done
|
||||
@mv $(DIST_ARCHIVES) ../
|
||||
@echo
|
||||
@echo "Resulting release files:"
|
||||
@echo "================================================================="
|
||||
@for file in $(DIST_ARCHIVES); do \
|
||||
printf "$$file \tDistribution tarball\n"; \
|
||||
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
|
||||
done
|
||||
@@ -1,25 +1,22 @@
|
||||
Finit ][ Fast & Extensible init for Linux
|
||||
=========================================
|
||||
[![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
|
||||
|
||||

|
||||
|
||||
<p align="center"><img src="images/finit3.png" alt="Finit: Fast Init" /></p>
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
[![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Features](#features)
|
||||
* [/etc/finit.conf](#etcfinitconf)
|
||||
* [/etc/finit.d](#etcfinitd)
|
||||
* [Runparts](#runparts)
|
||||
* [Bootstrap](#bootstrap)
|
||||
* [/etc/finit.conf](doc/config.md#etcfinitconf)
|
||||
* [/etc/finit.d](doc/config.md#etcfinitd)
|
||||
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
|
||||
* [Bootstrap](doc/bootstrap.md#bootstrap)
|
||||
* [Runlevels](#runlevels)
|
||||
* [Inetd](#inetd)
|
||||
* [Hooks, Callbacks & Plugins](#hooks-callbacks--plugins)
|
||||
* [Inetd](doc/inetd.md#inetd)
|
||||
* [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
|
||||
* [Rebooting & Halting](#rebooting--halting)
|
||||
* [Commands & Status](#commands--status)
|
||||
* [Building](#building)
|
||||
* [Building](doc/build.md#building)
|
||||
* [Requirements](#requirements)
|
||||
* [Running](#running)
|
||||
* [Debugging](#debugging)
|
||||
* [Origin & References](#origin--references)
|
||||
@@ -28,29 +25,75 @@ Table of Contents
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Init is the first userland process started by the UNIX kernel, therefore
|
||||
it always has PID 1 and is responsible for starting up the rest of the
|
||||
system.
|
||||
Finit is an EeePC Fastinit inspired clone with [process supervision][1]
|
||||
similar to that of D.J. Bernstein's [daemontools][2] and Gerrit Pape's
|
||||
[runit][3]. It also supports free-form conditions based on events, e.g.
|
||||
triggering on PID file creation, Netlink events like a default route or
|
||||
interfaces coming up, or even custom events. Similar to [systemd][6].
|
||||
|
||||
Finit is a plugin-based init with [process supervision][1] similar to
|
||||
that of D.J. Bernstein's [daemontools][2] and Gerrit Pape's [runit][3].
|
||||
The main focus of Finit is on small and embedded GNU/Linux systems, yet
|
||||
fully functional on standard server and desktop installations as well.
|
||||
Finit focuses on small and embedded Linux systems, although fully usable
|
||||
on servers and desktops as well. See the [contrib section](contrib/)
|
||||
for Debian and Alpine Linux examples.
|
||||
|
||||
Traditional [SysV init][4] style systems are scripted. For low-resource
|
||||
embedded systems this can be quite resource intensive and cause longer
|
||||
boot times. Finit is optimized to reduce context switches and forking
|
||||
of processes to provide a very basic bootstrap written entirely in C.
|
||||
embedded systems this is quite resource intensive and often leads to
|
||||
long boot times. Finit reduces context switches and forking of shell
|
||||
scripts to provide a swift [system bootstrap](doc/bootstrap.md) written
|
||||
entirely in C.
|
||||
|
||||
> Finit basically does everything you need from its `main()` function!
|
||||
Configuration is read from [/etc/finit.conf](doc/config.md#etcfinitconf)
|
||||
which details kernel modules to load, services and TTYs to start. When
|
||||
initial [bootstrap](doc/bootstrap.md) is done, including setting up
|
||||
networking, [/etc/finit.d/](doc/config.md#etcfinitd) and the familiar
|
||||
[/etc/rc.local](#runparts--etcrclocal) are run.
|
||||
|
||||
Hence, there is no `/etc/init.d/rcS` script, or similar, instead Finit
|
||||
reads its configuration from [/etc/finit.conf](#etcfinitconf). This
|
||||
file details what kernel modules to load, programs to run, daemons to
|
||||
supervise, and inetd services to launch on demand.
|
||||
**Example /etc/finit.conf:**
|
||||
|
||||
See [TroglOS][9] for an example of how to boot a small embedded system
|
||||
with Finit.
|
||||
```
|
||||
# Fallback if /etc/hostname is missing
|
||||
host wopr
|
||||
|
||||
# Runlevel to start after bootstrap, runlevel 'S'
|
||||
runlevel 2
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
|
||||
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
|
||||
service [S12345] /sbin/klogd -n -- Kernel log daemon
|
||||
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
||||
|
||||
# For multiple instances of the same service, add :ID somewhere between
|
||||
# the service/run/task keyword and the command.
|
||||
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
|
||||
service :2 [2345] /sbin/merecat -n -p 8080 /var/www -- Old web server
|
||||
|
||||
# Alternative method instead of below runparts, can also use /etc/rc.local
|
||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd start -- Preparing console
|
||||
|
||||
# Inetd services to start on demand, with alternate ports and filtering
|
||||
inetd ftp/tcp nowait [2345] /sbin/uftpd -i -f -- FTP daemon
|
||||
inetd tftp/udp wait [2345] /sbin/uftpd -i -y -- TFTP daemon
|
||||
inetd time/udp wait [2345] internal -- UNIX rdate service
|
||||
inetd time/tcp nowait [2345] internal -- UNIX rdate service
|
||||
inetd 3737/tcp nowait [2345] internal.time -- UNIX rdate service
|
||||
inetd telnet/tcp nowait [2345] /sbin/telnetd -i -F -- Telnet daemon
|
||||
inetd 2323/tcp nowait [2345] /sbin/telnetd -i -F -- Telnet daemon
|
||||
inetd 222/tcp@eth0 nowait [2345] /sbin/dropbear -i -R -F -- SSH service
|
||||
inetd ssh/tcp@*,!eth0 nowait [2345] /sbin/dropbear -i -R -F -- SSH service
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
|
||||
# Virtual consoles to start built-in getty on
|
||||
tty [12345] /dev/tty1 115200 linux
|
||||
tty [12345] /dev/ttyAMA0 115200 vt100
|
||||
```
|
||||
|
||||
For an example of a full blown embedded Linux, see [TroglOS][9], or take
|
||||
a look at the `contrib/` section with [Alpine Linux](contrib/alpine/),
|
||||
[Debian](contrib/debian/), support and more.
|
||||
|
||||
|
||||
Features
|
||||
@@ -58,40 +101,59 @@ Features
|
||||
|
||||
**Process Supervision**
|
||||
|
||||
Start, monitor and restart processes (daemons) if they fail.
|
||||
|
||||
Start, monitor and restart services should they fail.
|
||||
|
||||
**Inetd**
|
||||
|
||||
Finit comes with a built-in `inetd` server. No need to maintain a
|
||||
separate config file for services that you want to start on demand.
|
||||
Finit comes with a built-in [inetd server](doc/inetd.md). No need to
|
||||
maintain a separate config file for services that you want to start on
|
||||
demand.
|
||||
|
||||
All inetd services started can be filtered per port and inbound
|
||||
interface, reducing the need for a full blown firewall.
|
||||
|
||||
Built-in optional inetd services:
|
||||
|
||||
- echo RFC862
|
||||
- chargen RFC864
|
||||
- daytime RFC867
|
||||
- discard RFC863
|
||||
- time (rdate) RFC868
|
||||
|
||||
For more information, see [doc/inetd.md](doc/inetd.md).
|
||||
|
||||
**Getty**
|
||||
|
||||
Finit also comes with a built-in Getty for Linux console TTYs. It can
|
||||
parse `/etc/inittab` and set the speed. Then `/bin/login` handles all
|
||||
nasty bits with PAM etc.
|
||||
|
||||
# /etc/finit.conf
|
||||
tty [12345] /dev/tty1 38400 linux
|
||||
tty [12345] /dev/ttyAMA0 115200 vt100
|
||||
|
||||
**Runlevels**
|
||||
|
||||
Runlevels are optional in Finit, but support for [SysV runlevels][5] is
|
||||
available if needed. All services in runlevel S(1) are started first,
|
||||
followed by the desired run-time runlevel. 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>
|
||||
Support for SysV init-style [runlevels][5] is available, in the same
|
||||
minimal style as everything else in Finit. The `[2345]` syntax can be
|
||||
applied to service, task, run, inetd, and TTY stanzas.
|
||||
|
||||
All services in runlevel S(1) are started first, followed by the desired
|
||||
run-time runlevel. 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.
|
||||
|
||||
|
||||
**Plugins**
|
||||
|
||||
Finit plugins can be *callbacks*, *boot hooks* into different stages of
|
||||
the boot process, or *pure extensions*. Plugins are written in C and
|
||||
compiled into a dynamic library that is loaded automatically by finit at
|
||||
boot. A basic set of plugins that extend and modify the basic behavior
|
||||
are bundled in the `plugins/` directory.
|
||||
Plugins can *extend* the functionality of Finit and *hook into* the
|
||||
different stages of the boot process and at runtime. Plugins are
|
||||
written in C and compiled into a dynamic library loaded automatically by
|
||||
finit at boot. A basic set of plugins are bundled in the `plugins/`
|
||||
directory.
|
||||
|
||||
Capabilities:
|
||||
|
||||
- **Task/Run/Service callbacks**
|
||||
Modify service arguments, override service monitor's decisions to
|
||||
start/stop/reload a service.
|
||||
- **Hooks**
|
||||
Hook into the boot at predefined points to extend Finit
|
||||
- **I/O**
|
||||
@@ -104,217 +166,25 @@ Extensions and functionality not purely related to what an `/sbin/init`
|
||||
needs to start a system are available as a set of plugins that either
|
||||
hook into the boot process or respond to various I/O.
|
||||
|
||||
|
||||
/etc/finit.conf
|
||||
---------------
|
||||
|
||||
Contrary to most other script based init alternatives ([SysV init][4],
|
||||
[upstart][6], [systemd][7], [OpenRC][8], etc.) Finit reads its entire
|
||||
configuration from `/etc/finit.conf`.
|
||||
|
||||
The command line arguments given in `/etc/finit.conf` to each service
|
||||
provide a default. A plugin can be used to register a callback to a
|
||||
service and then modify the behavior to suit the current runlevel and
|
||||
system configuration.
|
||||
|
||||
For instance, before starting a heavily resource intensive service like
|
||||
IPsec or OpenVPN, a callback can check if the outbound interface is up
|
||||
and has an IP address, or just check if the service is disabled — much
|
||||
like what a SysV init start script usually does.
|
||||
|
||||
Syntax:
|
||||
|
||||
* `check <DEV>`
|
||||
Run fsck on a file system before mounting it
|
||||
|
||||
* `module <MODULE>`
|
||||
Load a kernel module, with optional arguments
|
||||
|
||||
* `network <PATH>`
|
||||
Script or program to bring up networking, with optional arguments
|
||||
|
||||
* `runlevel <N>`
|
||||
N is the runlevel number 1-9, where 6 is reserved for reboot.
|
||||
Default is 2.
|
||||
|
||||
* `run [LVLS] /path/to/cmd ARGS -- Optional description`
|
||||
One-shot command to run in sequence when entering a runlevel, with
|
||||
optional arguments and description. This command is guaranteed to be
|
||||
completed before running the next command.
|
||||
|
||||
* `task [LVLS] /path/to/cmd ARGS -- Optional description`
|
||||
One-shot like 'run', but starts in parallel with the next command
|
||||
|
||||
* `service [LVLS] /path/to/daemon ARGS -- Optional description`
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely. Please note that you often need to provide a
|
||||
`--foreground` or `--no-background` argument to most daemons to
|
||||
prevent them from forking off a sub-process in the background.
|
||||
|
||||
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
||||
Launch a daemon when a client initiates a connection on an Internet
|
||||
port. Available services are listed in the UNIX `/etc/services` file.
|
||||
Finit can filter access to from a list of interfaces, `@iflist`, per
|
||||
inetd service as well as listen to custom ports.
|
||||
|
||||
```shell
|
||||
inetd ftp/tcp nowait @root /usr/sbin/uftpd -i -f
|
||||
inetd tftp/udp wait @root /usr/sbin/uftpd -i -t
|
||||
```
|
||||
|
||||
The following example listens to port 2323 for telnet connections and
|
||||
only allows clients connecting from `eth0`:
|
||||
|
||||
```shell
|
||||
inetd 2323/tcp@eth0 nowait [2345] /sbin/telnetd -i -F
|
||||
```
|
||||
|
||||
The interface list, `@iflist`, is of the format `@iface,!iface,iface`,
|
||||
where a single `!` means to deny access. Notice how interfaces are
|
||||
comma separated with no spaces.
|
||||
|
||||
The `inetd` directive can also have ` -- Optional Description`, only
|
||||
Finit does not output this text on the console when launching inetd
|
||||
services. Instead this text is sent to syslog and also shown by the
|
||||
`initctl` tool. More on inetd below.
|
||||
|
||||
* `runparts <DIR>`
|
||||
Call run-parts(8) on a directory to run start scripts. All executable
|
||||
files, or scripts, in the directory are called, in alphabetic order.
|
||||
|
||||
* `include <CONF>`
|
||||
Include another configuration file. Absolute path required.
|
||||
|
||||
* `tty [LVLS] <DEV | /path/to/cmd [args]>`
|
||||
Start a getty on the given TTY device DEV, in the given runlevels. If
|
||||
no tty setting is given in `finit.conf`, or if `/bin/sh` is given as
|
||||
argument instead of a device path, a single shell is started on the
|
||||
default console. Useful for really bare-bones systems.
|
||||
|
||||
It is also possible to supply the full command line, with arguments,
|
||||
to your getty. In this case finit will simply use that command.
|
||||
|
||||
See `finit.h` for the `#define GETTY` that is called, along with the
|
||||
default baud rate.
|
||||
|
||||
* `console <DEV | /path/to/cmd [args]>`
|
||||
Some embedded systems have a dedicated serial console/service port.
|
||||
This command tells finit to not start getty directly, since there may
|
||||
not be anyone there. To save RAM and CPU finit instead displays a
|
||||
friendly message and waits for the user to activate the console with a
|
||||
key press before starting getty. Finit also does some other magic and
|
||||
changes the process name to "console".
|
||||
|
||||
Like the `tty` command, the `console` command can also be used to
|
||||
simply start a UNIX shell, e.g. `/bin/sh`.
|
||||
|
||||
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
|
||||
be installed since system requirements differ too much. Try out the
|
||||
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
|
||||
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
|
||||
|
||||
Every `run`, `task`, `service`, or `inetd` can also list the privileges
|
||||
the `/path/to/cmd` should be executed with. Simply prefix the path with
|
||||
`[@USR[:GRP]]` like this:
|
||||
|
||||
```shell
|
||||
run [2345] @joe:users /usr/bin/logger "Hello world"
|
||||
```
|
||||
|
||||
For multiple instances of the same command, e.g. a DHCP client or
|
||||
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
||||
`service` keyword and the command, like this:
|
||||
|
||||
```shell
|
||||
service :1 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
|
||||
service :2 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
|
||||
```
|
||||
|
||||
Without the `:ID` to the service the latter will overwrite the former
|
||||
and only the old web server would be started and supervised.
|
||||
For more information, see [doc/plugins.md](doc/plugins.md).
|
||||
|
||||
|
||||
/etc/finit.d
|
||||
------------
|
||||
Runparts & /etc/rc.local
|
||||
------------------------
|
||||
|
||||
Finit supports changes to the overall system configuration at runtime.
|
||||
For this purpose the (configurable) directory `/etc/finit.d` is used.
|
||||
Here you can put configuration file snippets, one per service if you
|
||||
like, which are all sourced automatically by finit at boot when loading
|
||||
the static configuration from `/etc/finit.conf`. This is the default
|
||||
behavior, so no include directives are necessary.
|
||||
|
||||
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
|
||||
send `SIGHUP` to PID 1, or call `finit q`. Any service read from this
|
||||
directory is flagged as a dynamic service, so changes to their `.conf`
|
||||
files, or even removal of the files, is detected at `SIGHUP`.
|
||||
|
||||
- If a service's `.conf` file has been removed, the service is stopped.
|
||||
- If the file is modified, the service is reloaded, stopped and started.
|
||||
- If a new service is detected, it is started — respecting runlevels
|
||||
and return values from any callbacks.
|
||||
|
||||
The `/etc/finit.d` directory was previously the default Finit `runparts`
|
||||
directory. Finit no longer has a default `runparts`, so make sure to
|
||||
update your setup, or the finit configuration, accordingly.
|
||||
|
||||
**Note:** Configurations read from `/etc/finit.d` are read *after*
|
||||
initial bootstrap, runlevel S(1). Hence, bootstrap services *must* be
|
||||
in `/etc/finit.conf`!
|
||||
|
||||
|
||||
Runparts
|
||||
--------
|
||||
|
||||
At the end of the boot, when networking and all services are up, finit
|
||||
At the end of the boot, when networking and all services are up, Finit
|
||||
calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory,
|
||||
if it exists. Similar to how the `/ec/rc.local` file works in most
|
||||
other init daemons, only finit runs a directory of scripts. This
|
||||
replaces the earlier support for a `/usr/sbin/services.sh` script in the
|
||||
original finit.
|
||||
and `/etc/rc.local`, in that order if they exist.
|
||||
|
||||
```shell
|
||||
runparts /etc/rc.d/
|
||||
```
|
||||
|
||||
Bootstrap
|
||||
---------
|
||||
|
||||
1. Populate `/dev`
|
||||
2. Parse `/etc/finit.conf`
|
||||
3. Load all `.so` plugins
|
||||
4. Remount/Pivot `/` to get R+W
|
||||
5. Call 1st level hooks, `HOOK_ROOTFS_UP`
|
||||
6. Mount `/etc/fstab` and swap, if available
|
||||
7. Cleanup stale files from `/tmp/*` et al
|
||||
8. Enable SysV init signals
|
||||
9. Call 2nd level hooks, `HOOK_BASEFS_UP`
|
||||
10. Start all 'S' runlevel tasks and services
|
||||
11. Load kernel parameters from `/etc/sysctl.conf`
|
||||
12. Set hostname and bring up loopback interface
|
||||
13. Call `network` script, if set in `/etc/finit.conf`
|
||||
14. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
||||
15. Load all `*.conf` files in `/etc/finit.d/` and switch to the active
|
||||
active runlevel, as set in `/etc/finit.conf`, default is 2. Here is
|
||||
where the rest of all tasks and inetd services are started.
|
||||
16. Call 4th level hooks, `HOOK_SVC_UP`
|
||||
17. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
||||
18. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||
19. Start TTYs defined in `/etc/finit.conf`, or rescue on `/dev/console`
|
||||
20. Enter main monitor loop
|
||||
|
||||
In (10) and (15) tasks and services defined in `/etc/finit.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 file with only
|
||||
`run` statements.
|
||||
|
||||
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.
|
||||
|
||||
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
|
||||
USB stick, convert/copy its contents to the system's `/etc/` directory,
|
||||
well before all 'S' runlevel tasks are started. This could be used with
|
||||
system images that are created read-only and all configuration is stored
|
||||
on external media.
|
||||
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
|
||||
If it exists and is an executable shell script, finit calls it at the
|
||||
very end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on
|
||||
hooks in [doc/plugins.md](doc/plugins.md#hooks), and about the system
|
||||
bootstrap in [doc/bootstrap.md](doc/bootstrap.md).
|
||||
|
||||
|
||||
Runlevels
|
||||
@@ -334,7 +204,7 @@ are used, and even more commonly, only the default runlevel is used.
|
||||
To specify an allowed set of runlevels for a `service`, `run` command,
|
||||
`task`, or `tty`, add `[NNN]` to your `/etc/finit.conf`, like this:
|
||||
|
||||
```shell
|
||||
```
|
||||
service [S12345] /sbin/syslogd -n -x -- System log daemon
|
||||
run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
task [S] /etc/init.d/kbd start -- Preparing console
|
||||
@@ -368,210 +238,39 @@ previous runlevel again — that way Finit can both stop old services and
|
||||
start any new ones for you, without rebooting the system.
|
||||
|
||||
|
||||
Inetd
|
||||
-----
|
||||
|
||||
A built-in *Internet Super Server* support was added in Finit v1.12 and
|
||||
v1.13, along with an internal `time` inetd service, RFC 868 (rdate).
|
||||
The latter is supplied as a plugin to illustrate how simple it is to
|
||||
extend finit with more internal inetd services.
|
||||
|
||||
> Please note, not all UNIX daemons are prepared to run as inetd services.
|
||||
> In the example below `sshd` also need the command line argument `-i`.
|
||||
|
||||
The inetd support in finit is quite advanced. Not only does it launch
|
||||
services on demand, it can do so on custom ports and also filter inbound
|
||||
traffic using a poor man's [TCP wrappers][]. The syntax is very similar
|
||||
to the traditional `/etc/inetd.conf`, yet keeping with the style of
|
||||
Finit:
|
||||
|
||||
```shell
|
||||
# Launch SSH on demand, in runlevels 2-5 as root
|
||||
inetd ssh/tcp nowait [2345] @root:root /usr/sbin/sshd -i
|
||||
```
|
||||
|
||||
A more advanced example is listed below, please note the *incompatible
|
||||
syntax change* that was made between Finit v1.12 and v1.13 to support
|
||||
deny filters:
|
||||
|
||||
```shell
|
||||
# Start sshd if inbound connection on eth0, port 222, or
|
||||
# inbound on eth1, port 22. Ignore on other interfaces.
|
||||
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
|
||||
inetd ssh/tcp@eth1,eth1 nowait [2345] /usr/sbin/sshd -i
|
||||
```
|
||||
|
||||
If `eth0` is your Internet interface you may want to avoid using the
|
||||
default port. To run ssh on port 222, and all others on port 22:
|
||||
|
||||
```shell
|
||||
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
|
||||
inetd ssh/tcp@*,!eth0 nowait [2345] /usr/sbin/sshd -i
|
||||
```
|
||||
|
||||
Compared to Finit v1.12 you must *explicitly deny* access from `eth0`!
|
||||
|
||||
|
||||
**Internal Services**
|
||||
|
||||
The original `inetd` had a few standard services built-in:
|
||||
|
||||
- time
|
||||
- echo
|
||||
- chargen
|
||||
- discard
|
||||
|
||||
Finit only supports the `time` service. This is realized as a plugin to
|
||||
provide a simple means of testing the inetd functionality stand-alone,
|
||||
but also as a very rudimentary time server for rdate clients.
|
||||
|
||||
The `time.so` inetd plugin is set up as follows. Notice the keyword
|
||||
`internal` which applies to all built-in inetd services:
|
||||
|
||||
```shell
|
||||
inetd time/tcp nowait [2345] internal
|
||||
```
|
||||
|
||||
Then call `rdate` from a remote machine (or use localhost):
|
||||
|
||||
```shell
|
||||
rdate -p <IP>
|
||||
```
|
||||
|
||||
If you use `time/udp` you must use the standard rdate implementation and
|
||||
then call it with `rdate -up` to connect using UDP. Without the `-p`
|
||||
argument rdate will try to set the system clock. Please note that rdate
|
||||
has been deprecated by the NTP protocol and this plugin should only be
|
||||
used for testing or environments where NTP for some reason is blocked.
|
||||
Also, remember the UNIX year 2038 bug, or in the case of RFC 868 (and
|
||||
some NTP implementations), year 2036!
|
||||
|
||||
**Note:** There is currently no verification that the same port is used
|
||||
more than once. So a standard `inetd http/tcp` service will clash
|
||||
with an ssh entry for the same port `inetd 80/tcp` …
|
||||
|
||||
|
||||
Hooks, Callbacks & Plugins
|
||||
--------------------------
|
||||
|
||||
Finit provides only the bare necessities for starting and supervising
|
||||
processes, with an emphasis on *bare* — for your convenience it does
|
||||
however come with support for hooks, service callbacks and plugins that
|
||||
can used to extend finit with.
|
||||
|
||||
### Plugins
|
||||
|
||||
For your convenience a set of *optional* plugins are available:
|
||||
|
||||
* *alsa-utils.so*: Restore and save ALSA sound settings on
|
||||
startup/shutdown.
|
||||
|
||||
* *bootmisc.so*: Setup necessary files for UTMP, tracks logins at boot.
|
||||
|
||||
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
|
||||
|
||||
* *hwclock.so*: Restore and save system clock from/to RTC on
|
||||
startup/shutdown.
|
||||
|
||||
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
|
||||
|
||||
* *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.
|
||||
|
||||
* *resolvconf.so*: Setup necessary files for `resolvconf` at startup.
|
||||
|
||||
* *time.so*: RFC 868 (rdate) plugin. Start as inetd service. Useful
|
||||
for testing inetd filtering — BusyBox has an rdate (TCP) client.
|
||||
|
||||
* *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.
|
||||
|
||||
* *urandom.so*: Setup random seed at startup.
|
||||
|
||||
* *x11-common.so*: Setup necessary files for X-Window.
|
||||
|
||||
Usually you want to hook into the boot process once, simple hook plugins
|
||||
like `bootmisc.so` are great for that purpose. They are called at each
|
||||
hook point in the boot process, useful to insert some pre-bootstrap
|
||||
mechanisms, like generating configuration files, restoring HW device
|
||||
state, etc. Available hook points are:
|
||||
|
||||
### Bootstrap Hooks
|
||||
|
||||
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
|
||||
mounted — very early
|
||||
|
||||
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
|
||||
and default init signals are setup
|
||||
|
||||
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
|
||||
networking is up (`lo` is up and the `network` script has run)
|
||||
|
||||
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
|
||||
|
||||
* `HOOK_SYSTEM_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_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.
|
||||
|
||||
### Shutdown Hooks
|
||||
|
||||
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
|
||||
services are sent `SIGTERM`
|
||||
|
||||
Plugins like `initctl.so` and `tty.so` extend finit by acting on events,
|
||||
they are called I/O plugins and are called from the finit main loop when
|
||||
`poll()` detects an event. See the source code for `plugins/*.c` for
|
||||
more help and ideas.
|
||||
|
||||
### Callbacks
|
||||
|
||||
Callback plugins are called by finit right before a process is started,
|
||||
or restarted if it exits. The callback runs as a separate process and
|
||||
receives a pointer to the `svc_t` of the service, with all command line
|
||||
parameters free to modify as needed.
|
||||
|
||||
All the callback needs to do is respond with one of: `SVC_STOP (0)`
|
||||
tells Finit to *not* start the service, `SVC_START (1)` to start the
|
||||
service, or `SVC_RELOAD (2)` to have finit signal the process with
|
||||
`SIGHUP`.
|
||||
|
||||
|
||||
Rebooting & Halting
|
||||
-------------------
|
||||
|
||||
Finit handles `SIGUSR1` and `SIGUSR2` for reboot and halt, and listens
|
||||
to `/dev/initctl` so system reboot and halt commands also work. The
|
||||
latter is implemented in the optional `initctl.so` plugin and can be
|
||||
accessed with the traditional `telinit` command line tool, symlinked to
|
||||
`finit`. Hence, if finit is your system init, then `init q` will work
|
||||
as the UNIX beards intended.
|
||||
Traditionally, rebooting and halting a UNIX system is done by changing
|
||||
its runlevel. Finit comes with its own tooling providing: `shutdown`,
|
||||
`reboot`, `poweroff`, and `suspend`, but also the traditional `init` and
|
||||
`telinit`, as well as a more modern `initctl` tool, detailed in the next
|
||||
section.
|
||||
|
||||
```shell
|
||||
~ $ telinit
|
||||
For compatibility reasons Finit listens to the same set of signals as
|
||||
BusyBox init. This is not 100% compatible with SysV init, but clearly
|
||||
the more common combination for Finit. For more details, see
|
||||
[doc/signals.md](doc/signals.md).
|
||||
|
||||
Finit also listens to the classic SysV init FIFO, used by `telinit`.
|
||||
Support for this is implemented by the `initctl.so` plugin. Hence,
|
||||
`telinit q` will work as the UNIX beards intended.
|
||||
|
||||
~ # telinit -h
|
||||
Usage: telinit [OPTIONS] [q | Q | 0-9]
|
||||
|
||||
Options:
|
||||
-h, --help This help text
|
||||
-v, --version Show Finit version
|
||||
-h, --help This help text
|
||||
-V, --version Show Finit version
|
||||
|
||||
Commands:
|
||||
q | Q Reload *.conf in /etc/finit.d/, like SIGHUP
|
||||
0 - 9 Change runlevel: 0 halt, 6 reboot
|
||||
```
|
||||
0 Power-off the system, same as initctl poweroff
|
||||
6 Reboot the system, same as initctl reboot
|
||||
2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any
|
||||
services in prev. runlevel that are not allowed in new.
|
||||
q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or
|
||||
sending SIGHUP to PID 1
|
||||
1, s, S Enter system rescue mode, runlevel 1
|
||||
|
||||
|
||||
Commands & Status
|
||||
@@ -581,7 +280,6 @@ Finit also implements a more modern API to query status, and start/stop
|
||||
services, called `initctl`. Unlike `telinit` the `initctl` tool does
|
||||
not return until the given command has fully completed.
|
||||
|
||||
```shell
|
||||
~ $ initctl -h
|
||||
Usage: initctl [OPTIONS] <COMMAND>
|
||||
|
||||
@@ -593,83 +291,29 @@ not return until the given command has fully completed.
|
||||
Commands:
|
||||
debug Toggle Finit (daemon) debug
|
||||
help This help text
|
||||
emit <EV> Emit event; a predefined event: RELOAD, STOP, START
|
||||
or a custom string matching an event in a service
|
||||
stanza, e.g: GW:UP, IFUP:IFNAME, IFDN:IFNAME. Where
|
||||
IFNAME is the interface name, e.g. eth0
|
||||
reload Reload *.conf in /etc/finit.d/ and activate changes
|
||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||
status | show Show status of services
|
||||
cond set <COND> Set (assert) condition => +COND
|
||||
cond clear <COND> Clear (deassert) condition => -COND
|
||||
cond flux <COND> Emulate flux condition => ~COND
|
||||
cond show Show condition status
|
||||
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
|
||||
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
|
||||
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
|
||||
reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name
|
||||
version Show Finit version
|
||||
```
|
||||
|
||||
The `emit <EV>` command can be used to send events to Finit. Built-in
|
||||
events are: RELOAD, STOP, START. These events act on a lower level than
|
||||
their command counterparts. The `reload` command reloads all `*.conf`
|
||||
files in `/etc/finit.d/` *and* activates the changes, with `emit RELOAD`
|
||||
only the `*.conf` files are reloaded.
|
||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||
file must be used to tell Finit to stop and start it on `reload` and
|
||||
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
|
||||
these will also affect how a service is maintained.
|
||||
|
||||
**Note:** The `emit STOP` event is more like "prepare" than "stop".
|
||||
Depending on how the service is declared, `<!>` or not, the service
|
||||
may be stopped, or skipped to be be sent `SIGHUP` later when `emit
|
||||
START` is issued.
|
||||
|
||||
To achieve the same result as `initctl reload` emit all three events:
|
||||
|
||||
```shell
|
||||
~ $ initctl emit "RELOAD,STOP,START"
|
||||
```
|
||||
|
||||
On an embedded system this can be used when changing complete system
|
||||
configuration. Between `STOP` and `START` reset/change any hardware
|
||||
or kernel settings required to be in effect before new services are
|
||||
started.
|
||||
|
||||
```shell
|
||||
# 1. Update/Change/Generate all daemon configuration files
|
||||
~ $ …
|
||||
|
||||
# 2. Update /etc/finit.d/*.conf
|
||||
~ $ …
|
||||
|
||||
# 3. Prepare finit, stop all old/previous services
|
||||
~ $ initctl emit "RELOAD,STOP"
|
||||
|
||||
# 4. Reconfigure hardware, interfaces/bridges/VLANs, etc.
|
||||
~ $ brctl …
|
||||
~ $ vconfig …
|
||||
~ $ ifconfig …
|
||||
|
||||
# 5. Complete finit reload by starting all new services and
|
||||
# SIGHUP any changed services
|
||||
~ $ initctl emit "START"
|
||||
```
|
||||
|
||||
The `emit <EV>` command can also be used to emit custom events. In
|
||||
fact, the event is a simple string. Declare a list of events in a
|
||||
service stanza: `service … <GW:UP,IFUP:eth0>` to reload (`SIGHUP`) a
|
||||
service when recieving the `"GW:UP"` or `"IFUP:eth0"` strings. If a
|
||||
service cannot handle reload and must be stopped-started, simply add an
|
||||
exclamation mark first: `service … <!GW:UP,IFUP:eth0>`.
|
||||
|
||||
The `<!>` notation to a service stanza can be used empty, then it will
|
||||
apply to `reload` and `runlevel` commands. I.e., when a service's
|
||||
`.conf` file has been changed Finit will stop and start it instead. If
|
||||
a service does *not* have `<!>` declared, the service will be reloaded
|
||||
(`SIGHUP`:ed) in the `START` phase instead. The latter is the preferred
|
||||
behaviour, but not all daemons support this, unfortunately.
|
||||
|
||||
**Note:** even though it is possible to start services not belonging to
|
||||
**Note:** even though it is possible to start services not belonging in
|
||||
the current runlevel these services will not be respawned automatically
|
||||
by Finit if they exit (crash). Hence, if the runlevel is 2, the below
|
||||
Dropbear SSH service will not be restarted if it, for some reason,
|
||||
exits.
|
||||
Dropbear SSH service will not be restarted if it is killed or exits.
|
||||
|
||||
```shell
|
||||
~ $ initctl status -v
|
||||
1 running 476 [S12345] /sbin/watchdog -T 16 -t 2 -F /dev/watchdog
|
||||
2 running 477 [S12345] /sbin/syslogd -n -b 3 -D
|
||||
@@ -681,156 +325,70 @@ exits.
|
||||
5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
|
||||
6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
|
||||
6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
|
||||
```
|
||||
|
||||
|
||||
Building
|
||||
--------
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Finit comes with a lightweight configure script to control which
|
||||
features to enable an plugins to build. Below are a few of the main
|
||||
switches to configure:
|
||||
Finit is capable of running on both desktop/server systems with udev and
|
||||
embedded systems that usually come with BusyBox mdev. Finit probes for
|
||||
them at runtime and expects `/dev/` to be writable, usually `devtmpfs`.
|
||||
It is also possible to run on a statically set up `/dev` if needed. It
|
||||
is however not a good idea to have both udev and mdev installed at the
|
||||
same time, this will lead to unpredictable results.
|
||||
|
||||
* `--prefix=`: Base prefix path for all files, except `--sbindir` and
|
||||
`--sysconfdir`. Used in concert with the `DESTDIR` variable.
|
||||
At boot Finit calls either `mdev` or `udevd` to populate `/dev`, this is
|
||||
done slightly differently and on systems with udev you might want to add
|
||||
the following one-shot task early in your `/etc/finit.conf`:
|
||||
|
||||
Defaults to `/usr`.
|
||||
run [S] /sbin/udevadm settle --timeout=120 -- Waiting for udev
|
||||
|
||||
* `--sbindir=`: Path to where resulting binaries should install to. Used
|
||||
in concert with the `DESTDIR` variable.
|
||||
Finit has a built-in Getty for TTYs, but requires a working `/bin/login`
|
||||
or `/bin/sh`, if no TTYs are configured in `/etc/finit.conf`.
|
||||
|
||||
Defaults to `/sbin`.
|
||||
For a fully operational system `/var`, `/run` and `/tmp` must be set up
|
||||
properly in `/etc/fstab` -- which is iterated over at boot.
|
||||
|
||||
* `--sysconfdir=`: Path to where finit configuration files should
|
||||
install to. Used in concert with the `DESTDIR` variable.
|
||||
|
||||
Defaults to `/etc`, but is currently unused.
|
||||
|
||||
* `--plugindir=`: Absolute path to where finit should search for
|
||||
dynamically loadable plugins at runtime. Can also be controlled
|
||||
with the `--libdir` switch.
|
||||
|
||||
Defaults to `/lib/finit/plugins`.
|
||||
|
||||
* `--with-plugins=`: List of stock finit plugins to build and install.
|
||||
The build system adds `.o` or `.so` extension to the listed plugins
|
||||
depending on the `--enable-static` switch. Hence, list the plugins
|
||||
*without* their extension.
|
||||
|
||||
* `--enable-embedded`: Target finit for BusyBox getty and mdev instead
|
||||
of a standard Linux distribution with GNU tools and udev.
|
||||
|
||||
* `--enable-debug`: Add GDB symbols and disable code optimization.
|
||||
|
||||
* `--enable-static`: Build Finit statically. The plugins will be
|
||||
built-in (.o files) instead. Note: very untested and not all plugins
|
||||
can be built static. It is recommended to use `--with-plugins` and
|
||||
select only the plugins really needed.
|
||||
|
||||
* `--disable-inetd`: Disables the built-in inetd server.
|
||||
|
||||
The following environment variables are checked by the makefiles and
|
||||
control what is built and where resulting binaries are installed.
|
||||
|
||||
* `ROOTDIR=`: Top directory for building complete system, used in pretty
|
||||
printing at build time.
|
||||
|
||||
* `VERSION=`: Defaults to the currently released version of finit,
|
||||
e.g., 1.3 but can be overridden by packagers using this variable to
|
||||
add a suffix or completely alter the version.
|
||||
|
||||
* `CFLAGS=`: Default `CFLAGS` are inherited from the environment.
|
||||
|
||||
* `CPPFLAGS=`: Default `CPPFLAGS` are inherited from the environment.
|
||||
|
||||
* `LDFLAGS=`: Default `LDFLAGS` are inherited from the environment.
|
||||
|
||||
* `LDLIBS=`: Default `LIBLIBS` are inherited from the environment.
|
||||
|
||||
* `DESTDIR=`: Used by packagers and distributions when building a
|
||||
relocatable bundle of files. Always prepended to the `prefix`
|
||||
destination directory.
|
||||
|
||||
**Example**
|
||||
|
||||
First, unpack the archive:
|
||||
|
||||
```shell
|
||||
$ tar xfJ finit-1.13.tar.xz
|
||||
$ cd finit-1.13/
|
||||
```
|
||||
|
||||
Then configure, build and install:
|
||||
|
||||
```shell
|
||||
$ ./configure --with-plugins="initctl hwclock"
|
||||
$ make
|
||||
CC finit.o
|
||||
CC conf.o
|
||||
CC helpers.o
|
||||
CC sig.o
|
||||
CC svc.o
|
||||
CC plugin.o
|
||||
CC strlcpy.o
|
||||
LINK finit
|
||||
CC plugins/initctl.o
|
||||
PLUGIN plugins/initctl.so
|
||||
CC plugins/hwclock.o
|
||||
PLUGIN plugins/hwclock.so
|
||||
$
|
||||
$ DESTDIR=/tmp/finit make install
|
||||
INSTALL /tmp/finit/sbin/finit
|
||||
INSTALL /tmp/finit/lib/finit/plugins/initctl.so
|
||||
INSTALL /tmp/finit/lib/finit/plugins/hwclock.so
|
||||
```
|
||||
|
||||
In this example the [finit-1.13.tar.xz][10] 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.
|
||||
|
||||
To target an embedded Linux system, usally a system that use BusyBox
|
||||
tools instead of udev & C:o, add <kbd>--enable-embedded</kbd> to the
|
||||
configure command above.
|
||||
|
||||
For more configure flags, see <kbd>./configure --help</kbd>
|
||||
|
||||
**Note:** If you run into problems starting Finit, take a look at
|
||||
`finit.c`. One of the most common problems is a custom Linux kernel
|
||||
build that lack `CONFIG_DEVTMPFS`. Another is too much cruft in the
|
||||
system `/etc/fstab`.
|
||||
The built-in Inetd requires `/etc/services` and `/etc/protocols` to work
|
||||
with port names rather than numbers.
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The default install does not setup finit as the system default
|
||||
`/sbin/init`, neither does it setup an initial `/etc/finit.conf`.
|
||||
Having successfully [built Finit](doc/build.md) it may now be time to
|
||||
take it for a test drive. The `make install` attempts to set up finit
|
||||
as the system system init, `/sbin/init`, but this is usually a symlink
|
||||
pointing to the current init.
|
||||
|
||||
It is assumed that users of finit are competent enough to either setup
|
||||
finit as their default `/sbin/init` or alter their respective Grub,
|
||||
LOADLIN, LILO, U-Boot/Barebox or RedBoot boot loader configuration to
|
||||
give the kernel the following extra command line:
|
||||
So either change the symlink, or change your boot loader (GRUB, LOADLIN,
|
||||
LILO, U-Boot/Barebox or RedBoot) configuration to append the following
|
||||
to the kernel command line:
|
||||
|
||||
```shell
|
||||
init=/sbin/finit
|
||||
append="init=/sbin/finit"
|
||||
```
|
||||
|
||||

|
||||
Remember to also set up an initial `/etc/finit.conf` before rebooting!
|
||||
|
||||

|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Add `finit_debug`, or `--debug`, to the kernel command line to
|
||||
enable trace messages. A console getty is always started, see the file
|
||||
`finit.h` for more useful compile-time tweaks:
|
||||
Add `finit_debug`, or `--debug`, to the kernel command line to enable
|
||||
debug messages.
|
||||
|
||||
```shell
|
||||
init=/sbin/finit --debug
|
||||
append="init=/sbin/finit --debug"
|
||||
```
|
||||
|
||||
To debug startup issues, in particular issues with getty/login, try
|
||||
`configure --enable-fallback-shell`. When no TTYs are detected, and
|
||||
Finit is configured with this option, Finit will try to start a bare
|
||||
`/bin/sh` on the boot console.
|
||||
|
||||
|
||||
Origin & References
|
||||
-------------------
|
||||
@@ -843,17 +401,14 @@ Finit is developed and maintained by [Joachim Nilsson][] at [GitHub][].
|
||||
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]: http://upstart.ubuntu.com/
|
||||
[7]: http://www.freedesktop.org/wiki/Software/systemd/
|
||||
[8]: http://www.gentoo.org/proj/en/base/openrc/
|
||||
[6]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||
[9]: https://github.com/troglobit/troglos
|
||||
[10]: ftp://troglobit.com/finit/finit-1.13.tar.xz
|
||||
[TCP Wrappers]: https://en.wikipedia.org/wiki/TCP_Wrapper
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
[original finit]: http://helllabs.org/finit/
|
||||
[EeePC fastinit]: http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||
|
||||
@@ -18,29 +18,29 @@ very low dependency on external packages.
|
||||
General
|
||||
-------
|
||||
|
||||
* Add compile-time support for running `/bin/sh` instead of `getty`
|
||||
* Add `!` or `[wait <pidfile>]` to service directive to let finit pause
|
||||
and wait for a pidfile before starting processes -- both at boot and
|
||||
when starting processes in a runlevel.
|
||||
* Allow custom reload for processes like libreSwan's pluto
|
||||
|
||||
service [2345] <!gw,if:eth0,reload:'starter reload'> /sbin/pluto
|
||||
|
||||
* Add halt/poweroff/shutdown symlinks to finit and convert reboot, but
|
||||
only install symlinks if these tools don't already exist.
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown.
|
||||
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
|
||||
API restart to SIGHUP callback for the same functionality in Finit.
|
||||
* 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
|
||||
* 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.
|
||||
* Add `IFF_RUNNING` support to netlink plugin along `IFF_IUP`
|
||||
* Allow process callbacks to be scripts.
|
||||
* Add support for a pipe/popen where we can listen for `STOP`, `START`
|
||||
and `RELOAD` on `STDOUT` from a script ... call it for example
|
||||
`.monitor=` in `svc_t` ... the script named as the basename of the
|
||||
service it monitors + `.sh`.
|
||||
* Add support for `/etc/sysctl.d`
|
||||
* Implement `initctl stop|start|restart|reload|status <SVC>` and
|
||||
`service <SVC> stop|start|restart|reload|status` on top
|
||||
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
||||
`initctl show --json`
|
||||
* Improve output of `initctl show SVC`, list service uptime, last
|
||||
ten relevant logs, etc. Similar to `systemctl show SVC`
|
||||
* Add PRE and POST hooks for when switching between runlevels
|
||||
* Make sure to install `queue.h` to `$(PREFIX)/include/finit/queue.h`
|
||||
* Move `finit.conf` "check" command to plugin which checks `/etc/fstab`
|
||||
instead. This is the de-facto practice. But keep the check command
|
||||
for really low-end systems w/o `/etc/fstab`.
|
||||
* Add support for generic events (strings or ubus events) that are passed
|
||||
verbatim (?) to the service stanza definitions.
|
||||
|
||||
|
||||
Init
|
||||
@@ -55,9 +55,14 @@ to achieve compatibility would be to add a plugin to finit which reads
|
||||
Inetd
|
||||
-----
|
||||
|
||||
Optimize interface filtering by using socket filter. The functions
|
||||
`inet_*_peek()` and `inetd_is_allowed()` used for interace filtering
|
||||
should be possible to rewrite as socket filters.
|
||||
* Add support for throttling connections.
|
||||
* Optimize interface filtering by using socket filter. The functions
|
||||
`inet_*_peek()` and `inetd_is_allowed()` used for interace filtering
|
||||
should be possible to rewrite as socket filters.
|
||||
* Optimize HTTP/HTTPS inetd connections by adding basic support for the
|
||||
`inetd` variant `redir http/tcp@eth0 nowait [2345] 127.0.0.1:8080`,
|
||||
which would reduce the overhead of spawn the web server on each HTTP
|
||||
connection, yet still provide a means of filtering access per iface.
|
||||
|
||||
|
||||
Crond
|
||||
@@ -91,59 +96,15 @@ the shell -- that way setting up one-time jobs would not entail
|
||||
re-reading `/etc/finit.conf`
|
||||
|
||||
|
||||
Watchdog
|
||||
--------
|
||||
|
||||
Support for monitoring the health of the system and its processes.
|
||||
|
||||
* Add support for a `/dev/watchdog` built-in to replace both the BusyBox
|
||||
watchdogd and my own refreshed uClinux-dist adaptation,
|
||||
[watchdogd](https://github.com/troglobit/watchdogd)
|
||||
* Add a system supervisor (pmon) to optionally reboot the system when a
|
||||
process stops responding, or when a respawn limit has been reached, as
|
||||
well as when system load gets too high. For details on UNIX loadavg,
|
||||
see http://stackoverflow.com/questions/11987495/linux-proc-loadavg
|
||||
- Default to 0.7 as MAX recommended load before warning and 0.9 reboot
|
||||
* API for processes to register with the watchdog, libwdt
|
||||
- Deeper monitoring of a process' main loop by instrumenting
|
||||
- If a process is not heard from within its subscribed period time
|
||||
reboot system or restart process.
|
||||
- Add client libwdt library, inspired by old Westermo API and the
|
||||
[libwdt] API published in the [Fritz!Box source dump], take for
|
||||
instance the `fritzbox7170-source-files-04.87-tar.gz` drop and see
|
||||
the `GPL-release_kernel.tgz` in `drivers/char/avm_new/` for the
|
||||
kernel driver, and the `LGPL-GPL-release_target_tools.tgz` in `wdt/`
|
||||
for the userland API.
|
||||
- When a client process (a standard app/daemon instrumented with
|
||||
libwdt calls in its main loop) registers with the watchdog, we raise
|
||||
the RT priority to 98 (just below the kernel watchdog in prio).
|
||||
This to ensure that system monitoring goes before anything else in
|
||||
the system.
|
||||
- Use UNIX domain sockets for communication between daemon and clients
|
||||
* Separate `/etc/watchdog.conf` configuration file, or a perhaps
|
||||
support for a more generic `/etc/finit.d/PLUGIN.conf`?
|
||||
* Support enable/disable watchdog features:
|
||||
- Supervise processes,
|
||||
- CPU loadavg,
|
||||
- Watch for file descriptor leaks,
|
||||
- etc.
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||
`pimd` man pages ...
|
||||
* Update Debian `finit.conf` example with runlevels, `tty/console` and
|
||||
dependency handling for Debian 8 (the dreaded systemd release).
|
||||
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
|
||||
how close Finit3 still is to the original easy-to-use Finit0 :)
|
||||
|
||||
|
||||
Investigation
|
||||
-------------
|
||||
|
||||
* [FHS changes](http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run)
|
||||
affecting runtime status, plugins, etc.
|
||||
|
||||
[libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so
|
||||
[Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/
|
||||
|
||||
|
||||
@@ -29,24 +29,24 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <lite/lite.h>
|
||||
#include <uev/uev.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "sig.h"
|
||||
#include "service.h"
|
||||
|
||||
#include "libite/lite.h"
|
||||
#include "libuev/uev.h"
|
||||
|
||||
uev_t api_watcher;
|
||||
|
||||
/* Allowed characters in job/id/name */
|
||||
static int isallowed(int ch)
|
||||
{
|
||||
return isalnum(ch) || isspace(ch) || ch == ':';
|
||||
return isprint(ch);
|
||||
}
|
||||
|
||||
/* Sanitize user input, make sure to NUL terminate. */
|
||||
@@ -113,14 +113,31 @@ static int call(int (*action)(svc_t *), char *buf, size_t len)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int service_pause(svc_t *svc)
|
||||
static int service_block(svc_t *svc)
|
||||
{
|
||||
return service_stop(svc, SVC_PAUSED_STATE);
|
||||
svc_block(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_start (char *buf, size_t len) { return call(service_start, buf, len); }
|
||||
static int do_pause (char *buf, size_t len) { return call(service_pause, buf, len); }
|
||||
static int do_reload (char *buf, size_t len) { return call(service_reload, buf, len); }
|
||||
static int service_unblock(svc_t *svc)
|
||||
{
|
||||
svc_unblock(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_restart(svc_t *svc)
|
||||
{
|
||||
svc_mark_dirty(svc);
|
||||
service_step(svc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_start (char *buf, size_t len) { return call(service_unblock, buf, len); }
|
||||
static int do_pause (char *buf, size_t len) { return call(service_block, buf, len); }
|
||||
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
@@ -155,9 +172,7 @@ typedef struct {
|
||||
} ev_t;
|
||||
|
||||
ev_t ev_list[] = {
|
||||
{ "RELOAD", conf_reload_dynamic },
|
||||
{ "STOP", service_stop_dynamic },
|
||||
{ "START", service_start_dynamic },
|
||||
{ "RELOAD", service_reload_dynamic },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -175,9 +190,13 @@ static int do_handle_event(char *event)
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: iterate over all services' events before failing. */
|
||||
|
||||
return -1;
|
||||
if (event[0] == '-')
|
||||
cond_clear(&event[1]);
|
||||
else if (event[0] == '+')
|
||||
cond_set(&event[1]);
|
||||
else
|
||||
cond_set(event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_handle_emit(char *buf, size_t len)
|
||||
@@ -198,9 +217,14 @@ static int do_handle_emit(char *buf, size_t len)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* In contrast to the SysV compat handling in plugins/initctl.c, when
|
||||
* `initctl runlevel 0` is issued we default to POWERDOWN the system
|
||||
* instead of just halting.
|
||||
*/
|
||||
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
int sd;
|
||||
int sd, lvl;
|
||||
struct init_request rq;
|
||||
|
||||
sd = accept(w->fd, NULL, NULL);
|
||||
@@ -229,33 +253,26 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
}
|
||||
|
||||
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||
_e("Invalid initctl request.");
|
||||
_e("Invalid initctl request");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RUNLVL:
|
||||
switch (rq.runlevel) {
|
||||
case '0':
|
||||
_d("Halting system (SIGUSR2)");
|
||||
do_shutdown(SIGUSR2);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
_d("Cannot enter bootstrap after boot ...");
|
||||
rq.runlevel = '1';
|
||||
rq.runlevel = '1'; /* Single user mode */
|
||||
/* Fall through to regular processing */
|
||||
|
||||
case '1'...'5':
|
||||
case '7'...'9':
|
||||
case '0'...'9':
|
||||
_d("Setting new runlevel %c", rq.runlevel);
|
||||
service_runlevel(rq.runlevel - '0');
|
||||
break;
|
||||
|
||||
case '6':
|
||||
_d("Rebooting system (SIGUSR1)");
|
||||
do_shutdown(SIGUSR1);
|
||||
lvl = rq.runlevel - '0';
|
||||
if (lvl == 0)
|
||||
halt = SHUT_OFF;
|
||||
if (lvl == 6)
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(lvl);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -266,6 +283,10 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
|
||||
case INIT_CMD_DEBUG:
|
||||
debug = !debug;
|
||||
if (debug)
|
||||
silent = 0;
|
||||
else
|
||||
silent = quiet ? 1 : SILENT_MODE;
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
||||
@@ -280,10 +301,6 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
result = do_pause(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD_SVC:
|
||||
result = do_reload(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
result = do_restart(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
@@ -298,10 +315,27 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
result = do_handle_emit(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_RUNLEVEL:
|
||||
rq.runlevel = runlevel;
|
||||
break;
|
||||
|
||||
case INIT_CMD_ACK:
|
||||
_d("Client failed reading ACK.");
|
||||
_d("Client failed reading ACK");
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_WDOG_HELLO:
|
||||
if (rq.runlevel <= 0) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wdogpid > 0 && wdogpid != rq.runlevel) {
|
||||
kill(wdogpid, SIGTERM);
|
||||
do_sleep(1);
|
||||
}
|
||||
wdogpid = rq.runlevel;
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
break;
|
||||
@@ -313,7 +347,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
rq.cmd = INIT_CMD_ACK;
|
||||
len = write(sd, &rq, sizeof(rq));
|
||||
if (len != sizeof(rq))
|
||||
_d("Failed sending ACK/NACK back to client.");
|
||||
_d("Failed sending ACK/NACK back to client");
|
||||
}
|
||||
|
||||
leave:
|
||||
@@ -323,36 +357,45 @@ leave:
|
||||
int api_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int sd;
|
||||
mode_t oldmask;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (-1 == sd) {
|
||||
_pe("Failed starting external API socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
erase(INIT_SOCKET);
|
||||
oldmask = umask(0077);
|
||||
if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun)))
|
||||
goto error;
|
||||
|
||||
if (-1 == listen(sd, 10))
|
||||
goto error;
|
||||
|
||||
return uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ);
|
||||
umask(oldmask);
|
||||
if (!uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ))
|
||||
return 0;
|
||||
|
||||
error:
|
||||
_pe("Failed intializing API socket");
|
||||
umask(oldmask);
|
||||
close(sd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int api_exit(void)
|
||||
{
|
||||
return shutdown(api_watcher.fd, SHUT_RDWR) ||
|
||||
close(api_watcher.fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
autoreconf -W portability -vifm
|
||||
@@ -27,38 +27,71 @@
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "libite/lite.h"
|
||||
#include "util.h"
|
||||
|
||||
static int do_send(int cmd, int runlevel)
|
||||
static int do_send(struct init_request *rq)
|
||||
{
|
||||
int fd;
|
||||
static int fd = -1;
|
||||
int result = 255;
|
||||
ssize_t len;
|
||||
|
||||
if (fd == -1) {
|
||||
if (!fexist(FINIT_FIFO)) {
|
||||
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(FINIT_FIFO, O_RDWR);
|
||||
if (-1 == fd) {
|
||||
perror("Failed opening " FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
len = write(fd, rq, sizeof(*rq));
|
||||
if (sizeof(*rq) == len)
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int do_reload(void)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = cmd,
|
||||
.cmd = INIT_CMD_RELOAD,
|
||||
};
|
||||
|
||||
return do_send(&rq);
|
||||
}
|
||||
|
||||
static int set_env(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SETENV,
|
||||
};
|
||||
|
||||
if (runlevel == 0) {
|
||||
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
|
||||
return do_send(&rq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_runlevel(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RUNLVL,
|
||||
.runlevel = runlevel,
|
||||
};
|
||||
|
||||
if (!fexist(FINIT_FIFO)) {
|
||||
fprintf(stderr, "%s does not support %s!\n", __progname, FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(FINIT_FIFO, O_RDWR);
|
||||
if (-1 == fd) {
|
||||
perror("Failed opening " FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
len = write(fd, &rq, sizeof(rq));
|
||||
if (sizeof(rq) == len)
|
||||
result = 0;
|
||||
close(fd);
|
||||
|
||||
return result;
|
||||
return set_env(runlevel) || do_send(&rq);
|
||||
}
|
||||
|
||||
/* telinit q | telinit <0-9> */
|
||||
@@ -66,11 +99,17 @@ static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help This help text\n\n"
|
||||
" -V, --version Show Finit version\n\n"
|
||||
" -h, --help This help text\n"
|
||||
" -V, --version Show Finit version\n\n"
|
||||
"Commands:\n"
|
||||
" q | Q Reload *.conf in /etc/finit.d/, like SIGHUP\n"
|
||||
" 0 - 9 Change runlevel: 0 halt, 6 reboot\n\n", __progname);
|
||||
" 0 Power-off the system, same as initctl poweroff\n"
|
||||
" 6 Reboot the system, same as initctl reboot\n"
|
||||
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
|
||||
" services in prev. runlevel that are not allowed in new.\n"
|
||||
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
|
||||
" sending SIGHUP to PID 1\n"
|
||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||
"\n", prognm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -84,7 +123,8 @@ int client(int argc, char *argv[])
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
verbose = 0;
|
||||
silent = 1;
|
||||
progname(argv[0]);
|
||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'v':
|
||||
@@ -99,12 +139,14 @@ int client(int argc, char *argv[])
|
||||
if (optind < argc) {
|
||||
int req = (int)argv[optind][0];
|
||||
|
||||
/* Compat: 'init <0-9>' */
|
||||
if (isdigit(req))
|
||||
return do_send(INIT_CMD_RUNLVL, req);
|
||||
return set_runlevel(req);
|
||||
|
||||
if (req == 'q' || req == 'Q')
|
||||
return do_send(INIT_CMD_RELOAD, 0);
|
||||
return do_reload();
|
||||
|
||||
if (req == 's' || req == 'S')
|
||||
return set_runlevel(1);
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
@@ -112,7 +154,6 @@ int client(int argc, char *argv[])
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# Top directory for building complete system, fall back to this directory
|
||||
ROOTDIR ?= $(TOPDIR)
|
||||
|
||||
# Strip binaries unless user explicitly disables it
|
||||
STRIPARGS ?= -s --strip-program=$(CROSS)strip -m 0755
|
||||
|
||||
# Some junk files we always want to be removed when doing a make clean.
|
||||
JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.*
|
||||
MAKE := @$(MAKE)
|
||||
MAKEFLAGS = --no-print-directory --silent
|
||||
CHECK := cppcheck $(CPPFLAGS) --quiet --enable=all
|
||||
INSTALL := install --backup=off
|
||||
STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755
|
||||
STRIPINST := $(INSTALL) $(STRIPARGS)
|
||||
ARFLAGS := crus
|
||||
|
||||
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC
|
||||
@@ -15,13 +18,13 @@ export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC
|
||||
|
||||
# Override default implicit rules
|
||||
%.o: %.c
|
||||
@printf " CC $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
@printf " CC $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
@$(CC) $(CFLAGS) $(CPPFLAGS) -c -MMD -MP -o $@ $<
|
||||
|
||||
%: %.o
|
||||
@printf " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
@printf " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
@$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@)))
|
||||
|
||||
%.so: %.o
|
||||
@printf " PLUGIN $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
@printf " PLUGIN $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
@$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/* Condition engine (write)
|
||||
*
|
||||
* Copyright (c) 2015-2016 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 <libgen.h>
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new)
|
||||
{
|
||||
char buf[MAX_ARG_LEN], *dir;
|
||||
enum cond_state old;
|
||||
|
||||
old = cond_get_path(path);
|
||||
|
||||
switch (new) {
|
||||
case COND_ON:
|
||||
strlcpy(buf, path, sizeof(buf));
|
||||
dir = dirname(buf);
|
||||
if (!dir) {
|
||||
_e("Invalid path '%s' for condition", path);
|
||||
return 0;
|
||||
}
|
||||
if (makepath(dir) && errno != EEXIST) {
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 0;
|
||||
}
|
||||
touch(path);
|
||||
utime(path, NULL);
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
unlink(path);
|
||||
break;
|
||||
|
||||
default:
|
||||
_e("Invalid condition state");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new != old;
|
||||
}
|
||||
|
||||
/* Has condition in configuration and cond is allowed? */
|
||||
static int svc_has_cond(svc_t *svc)
|
||||
{
|
||||
if (!svc->cond[0])
|
||||
return 0;
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
_d("%s", name);
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
||||
continue;
|
||||
|
||||
_d("%s: match <%s> %s", name ?: "nil", svc->cond, svc->cmd);
|
||||
service_step(svc);
|
||||
}
|
||||
}
|
||||
|
||||
void cond_set(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (!cond_set_path(cond_path(name), COND_ON))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_clear(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_reload(void)
|
||||
{
|
||||
_d("");
|
||||
cond_set_path(COND_RECONF, COND_ON);
|
||||
|
||||
cond_update(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,117 @@
|
||||
/* Condition engine (read)
|
||||
*
|
||||
* Copyright (c) 2015-2016 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 <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
|
||||
static inline int timespec_newer(const struct timespec *a,
|
||||
const struct timespec *b)
|
||||
{
|
||||
if (a->tv_sec != b->tv_sec)
|
||||
return a->tv_sec > b->tv_sec;
|
||||
|
||||
return a->tv_nsec > b->tv_nsec;
|
||||
}
|
||||
|
||||
const char *condstr(enum cond_state s)
|
||||
{
|
||||
static const char *strs[] = {
|
||||
[COND_OFF] = "off",
|
||||
[COND_FLUX] = "flux",
|
||||
[COND_ON] = "on",
|
||||
};
|
||||
|
||||
return strs[s];
|
||||
}
|
||||
|
||||
const char *cond_path(const char *name)
|
||||
{
|
||||
static char file[MAX_ARG_LEN];
|
||||
|
||||
snprintf(file, sizeof(file), COND_PATH "/%s", name);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
enum cond_state cond_get_path(const char *path)
|
||||
{
|
||||
struct stat st, st_reconf;
|
||||
|
||||
if (stat(path, &st))
|
||||
return COND_OFF;
|
||||
|
||||
if (stat(COND_RECONF, &st_reconf) ||
|
||||
timespec_newer(&st.st_mtim, &st_reconf.st_mtim))
|
||||
return COND_ON;
|
||||
|
||||
return COND_FLUX;
|
||||
}
|
||||
|
||||
enum cond_state cond_get(const char *name)
|
||||
{
|
||||
return cond_get_path(cond_path(name));
|
||||
}
|
||||
|
||||
enum cond_state cond_get_agg(const char *names)
|
||||
{
|
||||
char *cond;
|
||||
enum cond_state s = COND_ON;
|
||||
static char conds[MAX_ARG_LEN];
|
||||
|
||||
if (!names)
|
||||
return COND_ON;
|
||||
|
||||
strlcpy(conds, names, sizeof(conds));
|
||||
for (cond = strtok(conds, ","); s && cond; cond = strtok(NULL, ","))
|
||||
s = min(s, cond_get(cond));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int cond_affects(const char *name, const char *names)
|
||||
{
|
||||
char *cond;
|
||||
static char conds[MAX_ARG_LEN];
|
||||
|
||||
if (!name || !names)
|
||||
return 0;
|
||||
|
||||
strlcpy(conds, names, sizeof(conds));
|
||||
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
||||
if (!strcmp(cond, name))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef FINIT_COND_H_
|
||||
#define FINIT_COND_H_
|
||||
|
||||
#include <paths.h>
|
||||
|
||||
#define COND_PATH _PATH_VARRUN "finit/cond"
|
||||
#define COND_SVC_PATH COND_PATH "/svc/"
|
||||
#define COND_RECONF COND_PATH "/reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
COND_OFF = 0,
|
||||
COND_FLUX,
|
||||
COND_ON
|
||||
} cond_state_t;
|
||||
|
||||
const char *condstr (enum cond_state s);
|
||||
const char *cond_path (const char *name);
|
||||
enum cond_state cond_get_path(const char *path);
|
||||
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);
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
|
||||
#endif /* FINIT_COND_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -23,14 +23,15 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
#include "libite/lite.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
@@ -39,40 +40,22 @@
|
||||
static int parse_conf(char *file);
|
||||
|
||||
|
||||
static char *strip_line(char *line)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
/* Trim leading whitespace */
|
||||
while (*line && isblank(*line))
|
||||
line++;
|
||||
|
||||
/* Strip any comment at end of line */
|
||||
ptr = line;
|
||||
while (*ptr && *ptr != '#')
|
||||
ptr++;
|
||||
*ptr = 0;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void conf_parse_cmdline(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE];
|
||||
|
||||
if ((fp = fopen("/proc/cmdline", "r")) != NULL) {
|
||||
fp = fopen("/proc/cmdline", "r");
|
||||
if (fp) {
|
||||
fgets(line, sizeof(line), fp);
|
||||
_d("Kernel command line: %s", line);
|
||||
|
||||
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
|
||||
debug = 1;
|
||||
#ifdef KERNEL_QUIET
|
||||
|
||||
if (!debug && strstr(line, "quiet"))
|
||||
quiet = 1;
|
||||
else
|
||||
quiet = 0;
|
||||
#endif
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +96,7 @@ int conf_parse_runlevels(char *runlevels)
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
void conf_parse_events(svc_t *svc, char *events)
|
||||
void conf_parse_cond(svc_t *svc, char *cond)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *ptr;
|
||||
@@ -124,13 +107,14 @@ void conf_parse_events(svc_t *svc, char *events)
|
||||
}
|
||||
|
||||
/* By default we assume UNIX daemons support SIGHUP */
|
||||
svc->sighup = 1;
|
||||
if (svc_is_daemon(svc))
|
||||
svc->sighup = 1;
|
||||
|
||||
if (!events)
|
||||
if (!cond)
|
||||
return;
|
||||
|
||||
/* First character must be '!' if SIGHUP is not supported. */
|
||||
ptr = events;
|
||||
ptr = cond;
|
||||
if (ptr[i] == '!') {
|
||||
svc->sighup = 0;
|
||||
ptr++;
|
||||
@@ -140,13 +124,98 @@ void conf_parse_events(svc_t *svc, char *events)
|
||||
i++;
|
||||
ptr[i] = 0;
|
||||
|
||||
if (i >= sizeof(svc->events)) {
|
||||
FLOG_WARN("Too long event list in declaration of %s: %s", svc->cmd, ptr);
|
||||
if (i >= sizeof(svc->cond)) {
|
||||
logit(LOG_WARNING, "Too long event list in declaration of %s: %s", svc->cmd, ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
svc->state = SVC_CONDHALT_STATE;
|
||||
strlcpy(svc->events, ptr, sizeof(svc->events));
|
||||
strlcpy(svc->cond, ptr, sizeof(svc->cond));
|
||||
}
|
||||
|
||||
struct rlimit_name {
|
||||
char *name;
|
||||
int val;
|
||||
};
|
||||
|
||||
static const struct rlimit_name rlimit_names[] = {
|
||||
{ "as", RLIMIT_AS },
|
||||
{ "core", RLIMIT_CORE },
|
||||
{ "cpu", RLIMIT_CPU },
|
||||
{ "data", RLIMIT_DATA },
|
||||
{ "fsize", RLIMIT_FSIZE },
|
||||
{ "locks", RLIMIT_LOCKS },
|
||||
{ "memlock", RLIMIT_MEMLOCK },
|
||||
{ "msgqueue", RLIMIT_MSGQUEUE },
|
||||
{ "nice", RLIMIT_NICE },
|
||||
{ "nofile", RLIMIT_NOFILE },
|
||||
{ "nproc", RLIMIT_NPROC },
|
||||
{ "rss", RLIMIT_RSS },
|
||||
{ "rtprio", RLIMIT_RTPRIO },
|
||||
#ifdef RLIMIT_RTTIME
|
||||
{ "rttime", RLIMIT_RTTIME },
|
||||
#endif
|
||||
{ "sigpending", RLIMIT_SIGPENDING },
|
||||
{ "stack", RLIMIT_STACK },
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
void conf_parse_rlimit(char *line)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
rlim_t new, *set;
|
||||
const struct rlimit_name *name;
|
||||
int resource = -1;
|
||||
|
||||
char *tok = strtok(line, " \t");
|
||||
|
||||
if (tok && !strcmp(tok, "soft"))
|
||||
set = &rlim.rlim_cur;
|
||||
else if (tok && !strcmp(tok, "hard"))
|
||||
set = &rlim.rlim_max;
|
||||
else
|
||||
goto error;
|
||||
|
||||
tok = strtok(NULL, " \t");
|
||||
if (!tok)
|
||||
goto error;
|
||||
|
||||
for (name = rlimit_names; name->name; name++) {
|
||||
if (!strcmp(tok, name->name))
|
||||
resource = name->val;
|
||||
}
|
||||
|
||||
if (resource < 0)
|
||||
goto fail;
|
||||
|
||||
tok = strtok(NULL, " \t");
|
||||
if (!tok)
|
||||
goto fail;
|
||||
|
||||
if (!strcmp(tok, "infinity")) {
|
||||
new = RLIM_INFINITY;
|
||||
} else {
|
||||
const char *err = NULL;
|
||||
|
||||
new = strtonum(tok, 0, (long long)2 << 31, &err);
|
||||
if (err)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (getrlimit(resource, &rlim))
|
||||
goto fail;
|
||||
|
||||
*set = new;
|
||||
if (setrlimit(resource, &rlim))
|
||||
goto fail;
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
|
||||
return;
|
||||
error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
}
|
||||
|
||||
static void parse_static(char *line)
|
||||
@@ -271,16 +340,13 @@ static void parse_dynamic(char *line, time_t mtime)
|
||||
if (MATCH_CMD(line, "#", x))
|
||||
return;
|
||||
|
||||
/* Monitored daemon, will be respawned on exit, as
|
||||
* long as the (optional) service callback returns
|
||||
* non-zero */
|
||||
/* Monitored daemon, will be respawned on exit */
|
||||
if (MATCH_CMD(line, "service ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, x, mtime, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* One-shot task, will not be respawned. Only runs if
|
||||
* the (optional) service callback returns true */
|
||||
/* One-shot task, will not be respawned */
|
||||
if (MATCH_CMD(line, "task ", x)) {
|
||||
service_register(SVC_TYPE_TASK, x, mtime, NULL);
|
||||
return;
|
||||
@@ -301,6 +367,22 @@ static void parse_dynamic(char *line, time_t mtime)
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "rlimit ", x)) {
|
||||
conf_parse_rlimit(x);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void tabstospaces(char *line)
|
||||
{
|
||||
if (!line)
|
||||
return;
|
||||
|
||||
for (int i = 0; line[i]; i++) {
|
||||
if (line[i] == '\t')
|
||||
line[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_conf_dynamic(char *file, time_t mtime)
|
||||
@@ -319,6 +401,7 @@ static int parse_conf_dynamic(char *file, time_t mtime)
|
||||
continue;
|
||||
|
||||
chomp(line);
|
||||
tabstospaces(line);
|
||||
_d("dyn conf: %s", line);
|
||||
|
||||
parse_dynamic(line, mtime);
|
||||
@@ -359,14 +442,16 @@ static int parse_conf(char *file)
|
||||
strcat(line, " ");
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
if (!silent)
|
||||
print(0, "Loading %sconfiguration", line);
|
||||
while (!feof(fp)) {
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
continue;
|
||||
chomp(line);
|
||||
|
||||
chomp(line);
|
||||
tabstospaces(line);
|
||||
_d("conf: %s", line);
|
||||
|
||||
parse_static(line);
|
||||
parse_dynamic(line, 0);
|
||||
}
|
||||
@@ -425,28 +510,14 @@ void conf_reload_dynamic(void)
|
||||
|
||||
int conf_parse_config(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
username = strdup(DEFUSER);
|
||||
hostname = strdup(DEFHOST);
|
||||
|
||||
result = parse_conf(FINIT_CONF);
|
||||
if (!tty_num()) {
|
||||
char *fallback = FALLBACK_SHELL;
|
||||
|
||||
if (console)
|
||||
fallback = console;
|
||||
|
||||
tty_register(fallback);
|
||||
}
|
||||
|
||||
return result;
|
||||
return parse_conf(FINIT_CONF);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
void conf_parse_cmdline (void);
|
||||
int conf_parse_runlevels (char *runlevels);
|
||||
void conf_parse_events (svc_t *svc, char *events);
|
||||
void conf_parse_cond (svc_t *svc, char *cond);
|
||||
int conf_parse_config (void);
|
||||
void conf_reload_dynamic (void);
|
||||
|
||||
@@ -36,7 +36,6 @@ void conf_reload_dynamic (void);
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -1,451 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Simple home brew configure script, not as fancy as GNU Autotools
|
||||
#
|
||||
# --with-plugins="time initctl"
|
||||
# --enable-static
|
||||
# --disable-inetd
|
||||
# --disable-plugins
|
||||
#
|
||||
# This creates a configuration where Finit is built statically, with the
|
||||
# time and initctl plugins built-in, and no inetd support.
|
||||
#
|
||||
|
||||
# Setup defaults if undefined
|
||||
defaults()
|
||||
{
|
||||
heading=${heading:='"Finit v" VERSION'}
|
||||
prefix=${prefix:="/usr"}
|
||||
sysconfdir=${sysconfdir:="/etc"}
|
||||
sbindir=${sbindir:="/sbin"}
|
||||
libdir=${libdir:="/lib"}
|
||||
pdir="finit/plugins"
|
||||
plugindir=${plugindir:="$libdir/$pdir"}
|
||||
# The initctl FIFO, should probably be in /run, not /dev
|
||||
fifo=${fifo:="/dev/initctl"}
|
||||
random=${random:="/var/lib/urandom/random-seed"}
|
||||
conf="finit.conf"
|
||||
config=${config:="$sysconfdir/$conf"}
|
||||
confdir=${confdir:="finit.d"}
|
||||
rcsd=${rcsd:="$sysconfdir/$confdir"}
|
||||
console=${console:='_PATH_CONSOLE'}
|
||||
shell=${shell:='_PATH_BSHELL'}
|
||||
defuser=${defuser:="root"}
|
||||
defhost=${defhost:="noname"}
|
||||
runlevel=${runlevel:=2}
|
||||
debug=${debug:=0}
|
||||
embedded=${embedded:=0}
|
||||
dbus=${dbus:=0}
|
||||
remount=${remount:=0}
|
||||
inetd=${inetd:=1}
|
||||
verbose=${verbose:=1}
|
||||
kernel_quiet=${kernel_quiet:=1}
|
||||
static=${static:=0}
|
||||
disable_plugins=${disable_plugins:=0}
|
||||
}
|
||||
|
||||
result()
|
||||
{
|
||||
echo
|
||||
echo "Finit configuration"
|
||||
echo "========================================================================"
|
||||
echo "Heading : $heading"
|
||||
echo "Install prefix : $prefix"
|
||||
echo "System config dir : $sysconfdir"
|
||||
echo "Super user bindir : $sbindir"
|
||||
echo "Shared libs dir : $libdir"
|
||||
echo "Plugins dir : $plugindir"
|
||||
echo "FIFO : $fifo"
|
||||
echo "Config file : $config"
|
||||
echo "rcs.d/ directory : $rcsd"
|
||||
echo "Default console : $console"
|
||||
# echo "Fallback shell : $shell"
|
||||
if [ x"$sysroot" != x"" ]; then
|
||||
echo "Swap rootfs to : $sysroot"
|
||||
fi
|
||||
echo "Default username : $defuser"
|
||||
echo "Default hostname : $defhost"
|
||||
echo "Default runlevel : $runlevel"
|
||||
|
||||
if [ $debug -ne 0 ]; then
|
||||
echo "Debug : ENABLED"
|
||||
fi
|
||||
|
||||
if [ $embedded -ne 0 ]; then
|
||||
echo "Embedded target : YES (BusyBox getty, and mdev instead of udev)"
|
||||
fi
|
||||
|
||||
if [ $dbus -ne 0 ]; then
|
||||
echo "D-Bus : YES, remember to install dbus.so plugin as well!"
|
||||
fi
|
||||
|
||||
if [ $remount -ne 0 ]; then
|
||||
echo "Remount ROOT fs : YES, / will be remounted as read-write at bootstrap"
|
||||
fi
|
||||
|
||||
if [ x"$random" != x"" ]; then
|
||||
echo "Random seed : $random"
|
||||
fi
|
||||
|
||||
if [ $inetd -ne 0 ]; then
|
||||
echo "Inetd : Built-in"
|
||||
else
|
||||
echo "Inetd : DISABLED"
|
||||
fi
|
||||
|
||||
if [ x"$libite" = x"" ]; then
|
||||
echo "Libite (LITE) : Built-in"
|
||||
else
|
||||
echo "Libite (LITE) : $libite + {/lib, /include}"
|
||||
fi
|
||||
|
||||
if [ x"$libuev" = x"" ]; then
|
||||
echo "LibuEv : Built-in"
|
||||
else
|
||||
echo "LibuEv : $libuev + {/lib, /include}"
|
||||
fi
|
||||
|
||||
if [ $verbose -ne 1 ]; then
|
||||
echo "Quiet Finit : YES"
|
||||
else
|
||||
echo "Quiet Finit : NO, verbose output (default)"
|
||||
fi
|
||||
|
||||
if [ $kernel_quiet -eq 1 ]; then
|
||||
echo "Kernel Quiet : YES, silent after inital boot (default)"
|
||||
else
|
||||
echo "Kernel Quiet : NO"
|
||||
fi
|
||||
|
||||
if [ $static -ne 0 ]; then
|
||||
echo "Static Finit : YES"
|
||||
fi
|
||||
|
||||
if [ $disable_plugins -eq 1 ]; then
|
||||
echo "Plugins : DISABLED"
|
||||
else
|
||||
if [ x"$plugins" = x"" ]; then
|
||||
echo "Plugins : ALL"
|
||||
else
|
||||
echo "Plugins : $plugins"
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
defaults
|
||||
|
||||
echo "Run this script to configure finit for your operating system."
|
||||
echo
|
||||
echo "Usage: $0 [ARG]"
|
||||
echo
|
||||
echo " --help This help text"
|
||||
echo " --prefix=PREFIX Set installation prefix, default '$prefix'"
|
||||
echo " --sysconfdir=CONF Set conf dir, default '$sysconfdir'"
|
||||
echo " --sbindir=SBIN Set sbin dir, default '$sbindir'"
|
||||
echo " --libdir=LIB Set lib dir, default '$libdir'"
|
||||
echo " --plugindir=DIR Set plugin dir, default '\$libdir/$pdir'"
|
||||
echo
|
||||
echo " --with-fifo=FIFO FIFO for initctl plugin, default '$fifo'"
|
||||
echo " --with-config=CONFIG Finit CONFIG file, default '\$sysconfdir/$conf'"
|
||||
echo " --with-heading=GREET Display GREET instead of Finit version at boot"
|
||||
echo " --with-randomseed=SEED Seed /dev/urandom at boot from SEED. Usually found in"
|
||||
echo " standard Linux systems at $random"
|
||||
echo " which is also the default. Requires urandom plugin!"
|
||||
echo " --with-plugins=\"a b c\" Build plugins 'a', 'b' and 'c', either as dynamic .so"
|
||||
echo " files, or as built-in if --enable-static is given,"
|
||||
echo " defaults to all plugins. Exclude extension!"
|
||||
echo " --with-rcsd=RCSD Finit RCSD directory, default '\$sysconfdir/$confdir'"
|
||||
echo " --with-console=DEV Console to use, default: /dev/console"
|
||||
# echo " --with-shell=PATH Fallback shell to use, default: /bin/sh"
|
||||
echo " --with-sysroot=PATH Some distributions keep a different (writable) rootfs"
|
||||
echo " that is used at runtime, usually /sysroot or /mnt"
|
||||
echo " --with-username=USER User for 'startx' .conf option, default: root"
|
||||
echo " --with-hostname=NAME Hostname, if /etc/hostname is missing, default: noname"
|
||||
echo " --with-runlevel=LEVEL Runlevel to switch to after bootstrap (S), default: 2"
|
||||
echo " --with-libite=PATH Look for libite in PATH/include and PATH/lib"
|
||||
echo " This is for disabling the use of built-in libite."
|
||||
echo " --with-libuev=PATH Look for libuEv in PATH/include and PATH/lib"
|
||||
echo " This is for disabling the use of built-in libuEv."
|
||||
echo
|
||||
echo " --enable-embedded Embedded defaults, BusBox getty, mdev etc."
|
||||
echo " --enable-dbus Enable D-Bus plugin. Note: install plugin as well!"
|
||||
echo " --enable-rw-rootfs Remount / as read-write at bootstrap, not for embedded"
|
||||
echo " --enable-debug Enable debug flags, '-O0, -g'"
|
||||
echo " --enable-quiet Quiet mode, reduce screen output to absolute minimum"
|
||||
echo " --enable-static Link statically. This disables a few features."
|
||||
echo " --disable-inetd Disable inetd support if not needed."
|
||||
echo " --disable-kernel-quiet Disable kernel cmdline quiet, default enabled."
|
||||
echo " --disable-plugins Disable plugins"
|
||||
echo
|
||||
echo "The build system also supports external CFLAGS, CPPFLAGS, LDFLAGS, LDLIBS,"
|
||||
echo "and DESTDIR, useful for packaging or cross-compiling."
|
||||
echo
|
||||
echo "Example:"
|
||||
echo " ./configure --enable-embedded --with-plugins=\"initctl tty\""
|
||||
echo
|
||||
echo "Report bugs to https://github.com/troglobit/finit/issues"
|
||||
echo
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
while [ "$*" != "" ]; do
|
||||
opt=`expr "$1" : "--\([^=]*\)=.*"`
|
||||
arg=`expr "$1" : "--[^=]*=\(.*\)"`
|
||||
if [ -z "$opt" ]; then
|
||||
opt=`expr "$1" : "--\(.*\)"`
|
||||
fi
|
||||
shift
|
||||
|
||||
case $opt in
|
||||
prefix)
|
||||
prefix="$arg"
|
||||
;;
|
||||
|
||||
sysconfdir)
|
||||
sysconfdir="$arg"
|
||||
;;
|
||||
|
||||
sbindir)
|
||||
sbindir="$arg"
|
||||
;;
|
||||
|
||||
libdir)
|
||||
libdir="$arg"
|
||||
;;
|
||||
|
||||
plugindir)
|
||||
plugindir="$arg"
|
||||
;;
|
||||
|
||||
with-fifo)
|
||||
fifo="$arg"
|
||||
;;
|
||||
|
||||
with-config)
|
||||
config="$arg"
|
||||
;;
|
||||
|
||||
with-heading)
|
||||
heading="\"$arg\""
|
||||
;;
|
||||
|
||||
with-randomseed)
|
||||
random="$arg"
|
||||
;;
|
||||
|
||||
with-plugins)
|
||||
plugins="$arg"
|
||||
;;
|
||||
|
||||
with-rcsd)
|
||||
rcsd="$arg"
|
||||
;;
|
||||
|
||||
with-console)
|
||||
console="\"$arg\""
|
||||
;;
|
||||
|
||||
with-shell)
|
||||
shell="\"$arg\""
|
||||
;;
|
||||
|
||||
with-sysroot)
|
||||
sysroot="$arg"
|
||||
;;
|
||||
|
||||
with-usename)
|
||||
defuser="$arg"
|
||||
;;
|
||||
|
||||
with-hostname)
|
||||
defhost="$arg"
|
||||
;;
|
||||
|
||||
with-runlevel)
|
||||
runlevel="$arg"
|
||||
;;
|
||||
|
||||
with-libite)
|
||||
libite="$arg"
|
||||
;;
|
||||
|
||||
with-libuev)
|
||||
libuev="$arg"
|
||||
;;
|
||||
|
||||
enable-debug)
|
||||
debug=1
|
||||
;;
|
||||
|
||||
enable-embedded)
|
||||
embedded=1
|
||||
;;
|
||||
|
||||
enable-dbus)
|
||||
dbus=1
|
||||
;;
|
||||
|
||||
enable-rw-rootfs)
|
||||
remount=1
|
||||
;;
|
||||
|
||||
enable-quiet)
|
||||
verbose=0
|
||||
;;
|
||||
|
||||
enable-static)
|
||||
static=1
|
||||
;;
|
||||
|
||||
disable-inetd)
|
||||
inetd=0
|
||||
;;
|
||||
|
||||
disable-kernel-quiet)
|
||||
kernel_quiet=0
|
||||
;;
|
||||
|
||||
disable-plugins)
|
||||
disable_plugins=1
|
||||
;;
|
||||
|
||||
help)
|
||||
usage
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown option: $opt"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# If no arguments given to configure script, use defaults
|
||||
defaults
|
||||
result
|
||||
|
||||
##############################################################################
|
||||
echo "# Generated by finit configure script." > config.mk
|
||||
echo "prefix = $prefix" >> config.mk
|
||||
echo "sysconfdir = $sysconfdir" >> config.mk
|
||||
echo "sbindir = $sbindir" >> config.mk
|
||||
echo "libdir = $libdir" >> config.mk
|
||||
echo "plugindir = $plugindir" >> config.mk
|
||||
echo "incdir = \$(prefix)/include/finit" >> config.mk
|
||||
echo "datadir = \$(prefix)/share/doc/finit" >> config.mk
|
||||
echo "mandir = \$(prefix)/share/man/man8" >> config.mk
|
||||
echo "FINIT_RCSD = $rcsd" >> config.mk
|
||||
|
||||
if [ $inetd -eq 0 ]; then
|
||||
echo >> config.mk
|
||||
echo "OBJS := \$(filter-out inetd.o, \$(OBJS))" >> config.mk
|
||||
fi
|
||||
|
||||
echo >> config.mk
|
||||
if [ $debug -eq 1 ]; then
|
||||
echo "CFLAGS += -O0 -g" >> config.mk
|
||||
else
|
||||
echo "CFLAGS += -O2" >> config.mk
|
||||
fi
|
||||
|
||||
echo >> config.mk
|
||||
if [ $static -ne 0 ]; then
|
||||
echo "STATIC = 1" >> config.mk
|
||||
echo "LDFLAGS += -static" >> config.mk
|
||||
echo "CPPFLAGS += -DENABLE_STATIC" >> config.mk
|
||||
echo "PLUGINS = \"\"" >> config.mk
|
||||
if [ $disable_plugins -ne 1 ]; then
|
||||
if [ x"$plugins" != x"" ]; then
|
||||
echo "OBJS += \$(addsuffix .o, \$(addprefix plugins/, $plugins))" >> config.mk
|
||||
else
|
||||
echo "tmp += \$(wildcard plugins/*.c)" >> config.mk
|
||||
echo "OBJS += \$(tmp:.c=.o)" >> config.mk
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "STATIC = 0" >> config.mk
|
||||
echo "LDFLAGS += -rdynamic" >> config.mk
|
||||
echo "LDLIBS += -ldl" >> config.mk
|
||||
if [ x"$plugins" != x"" ]; then
|
||||
echo "PLUGINS = \$(addsuffix .so, $plugins)" >> config.mk
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ x"$libite" != x"" ]; then
|
||||
echo "LIBITE = $libite" >> config.mk
|
||||
echo "CPPFLAGS += -I\$(LIBITE)/include" >> config.mk
|
||||
echo "LDFLAGS += -L\$(LIBITE)/lib" >> config.mk
|
||||
echo "LDLIBS += -l:libite.so.2" >> config.mk
|
||||
else
|
||||
echo "LDFLAGS += -L\$(TOPDIR)/libite" >> config.mk
|
||||
echo "LDLIBS += -lite" >> config.mk
|
||||
echo "DEPLIBS += libite/libite.a" >> config.mk
|
||||
fi
|
||||
|
||||
if [ x"$libuev" != x"" ]; then
|
||||
echo "LIBUEV = $libuev" >> config.mk
|
||||
echo "CPPFLAGS += -I\$(LIBUEV)/include" >> config.mk
|
||||
echo "LDFLAGS += -L\$(LIBUEV)/lib" >> config.mk
|
||||
echo "LDLIBS += -l:libuev.so.1" >> config.mk
|
||||
else
|
||||
echo "LDFLAGS += -L\$(TOPDIR)/libuev" >> config.mk
|
||||
echo "LDLIBS += -luev" >> config.mk
|
||||
echo "DEPLIBS += libuev/libuev.a" >> config.mk
|
||||
fi
|
||||
|
||||
echo >> config.mk
|
||||
echo "export libdir plugindir incdir" >> config.mk
|
||||
echo "export CFLAGS CPPFLAGS LDFLAGS LDLIBS STATIC" >> config.mk
|
||||
|
||||
##############################################################################
|
||||
echo "/* Generated by finit configure script. */" > config.h
|
||||
echo >> config.h
|
||||
echo "#ifndef FINIT_CONFIG_H_" >> config.h
|
||||
echo "#define FINIT_CONFIG_H_" >> config.h
|
||||
echo >> config.h
|
||||
echo "#define INIT_HEADING $heading" >> config.h
|
||||
echo "#define VERBOSE_MODE $verbose" >> config.h
|
||||
echo "#define KERNEL_QUIET $kernel_quiet" >> config.h
|
||||
if [ $embedded -eq 1 ]; then
|
||||
echo "#define EMBEDDED_SYSTEM 1" >> config.h
|
||||
echo "#define SETUP_DEVFS \"/sbin/mdev -s\"" >> config.h
|
||||
echo "#define GETTY \"/sbin/getty -L\"" >> config.h
|
||||
echo "#define GETTY_BUSYBOX 1" >> config.h
|
||||
echo "#define BAUDRATE 115200" >> config.h
|
||||
else
|
||||
echo "#define SETUP_DEVFS \"/sbin/udevd --daemon\"" >> config.h
|
||||
echo "#define GETTY \"/sbin/getty -8\"" >> config.h
|
||||
echo "#define GETTY_AGETTY 1" >> config.h
|
||||
echo "#define BAUDRATE 38400" >> config.h
|
||||
fi
|
||||
if [ $remount -eq 1 ]; then
|
||||
echo "#define REMOUNT_ROOTFS 1" >> config.h
|
||||
fi
|
||||
if [ $dbus -eq 1 ]; then
|
||||
echo "#define HAVE_DBUS 1" >> config.h
|
||||
fi
|
||||
|
||||
if [ $inetd -eq 0 ]; then
|
||||
echo "#define INETD_DISABLED" >> config.h
|
||||
fi
|
||||
echo "#define FINIT_FIFO \"$fifo\"" >> config.h
|
||||
echo "#define FINIT_CONF \"$config\"" >> config.h
|
||||
echo "#define FINIT_RCSD \"$rcsd\"" >> config.h
|
||||
echo "#define CONSOLE $console" >> config.h
|
||||
echo "#define FALLBACK_SHELL $shell" >> config.h
|
||||
if [ x"$sysroot" != x"" ]; then
|
||||
echo "#define SYSROOT \"$sysroot\"" >> config.h
|
||||
fi
|
||||
echo "#define PLUGIN_PATH \"$plugindir\"" >> config.h
|
||||
if [ x"$random" != x"" ]; then
|
||||
echo "#define RANDOMSEED \"$random\"" >> config.h
|
||||
fi
|
||||
echo "#define DEFUSER \"$defuser\"" >> config.h
|
||||
echo "#define DEFHOST \"$defhost\"" >> config.h
|
||||
echo "#define RUNLEVEL $runlevel" >> config.h
|
||||
|
||||
echo >> config.h
|
||||
echo "#endif /* FINIT_CONFIG_H_ */" >> config.h
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
AC_INIT(Finit, 3.0-rc2, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
AC_CONFIG_SRCDIR([finit.c])
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile plugins/Makefile])
|
||||
|
||||
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
||||
#m4_include([m4/plugin.m4])
|
||||
#m4_include([m4/expand.m4])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
# Change default prefix, we're PID 1 after all ...
|
||||
AC_PREFIX_DEFAULT([])
|
||||
|
||||
# Handle building plugins either as dynamically loadable, or built-in
|
||||
LT_INIT([shared disable-static dlopen aix-soname=both disable-fast-install])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_INSTALL
|
||||
|
||||
# Configuration.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(fstaby.h)
|
||||
AC_CHECK_FUNCS(strstr getopt getfsenty)
|
||||
|
||||
# Check for uint[8,16,32]_t
|
||||
AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
|
||||
# Check for required libraries
|
||||
PKG_CHECK_MODULES([uev], [libuev >= 1.5.2])
|
||||
PKG_CHECK_MODULES([lite], [libite >= 1.8.2])
|
||||
|
||||
# Check for configured Finit features
|
||||
AC_ARG_ENABLE(emergency_shell,
|
||||
AS_HELP_STRING([--enable-emergency-shell], [Experimental emergency fallback at boot.
|
||||
Only use this for debugging very early boot problems with Finit.]))
|
||||
|
||||
AC_ARG_ENABLE(fallback_shell,
|
||||
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start.]))
|
||||
|
||||
AC_ARG_ENABLE(rw-rootfs,
|
||||
AS_HELP_STRING([--enable-rw-rootfs], [Remount / R/W at boot, usually not for embedded]))
|
||||
|
||||
AC_ARG_ENABLE(silent,
|
||||
AS_HELP_STRING([--enable-silent], [Completely silent operation, not even boot messages]))
|
||||
|
||||
AC_ARG_ENABLE(watchdog,
|
||||
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]))
|
||||
|
||||
AC_ARG_ENABLE(inetd,
|
||||
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]))
|
||||
|
||||
AC_ARG_ENABLE(quiet,
|
||||
AS_HELP_STRING([--disable-quiet], [Default Finit obeys the kernel quiet option]))
|
||||
|
||||
# Check for extra plugins to enable
|
||||
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([inetd-echo], [no], [Inetd plugin: echo server, RFC862])
|
||||
AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864])
|
||||
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
|
||||
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
|
||||
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
|
||||
AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!])
|
||||
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.])
|
||||
|
||||
# Check for extra arguments or packages
|
||||
AC_ARG_WITH(heading,
|
||||
AS_HELP_STRING([--with-heading=GREET], [Display GREET instead of Finit version at boot]),
|
||||
[heading=$withval], [heading="$PACKAGE_NAME v$PACKAGE_VERSION"])
|
||||
|
||||
AC_ARG_WITH(fifo,
|
||||
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
|
||||
[fifo=$withval], [fifo=/var/run/initctl])
|
||||
|
||||
AC_ARG_WITH(plugindir,
|
||||
AS_HELP_STRING([--with-plugindir=DIR], [Finit plugin directory, default /lib/finit/plugins]),
|
||||
[pkglibdir=$withval], [pkglibdir=$libdir/finit/plugins])
|
||||
AC_SUBST([pkglibdir])
|
||||
|
||||
AC_ARG_WITH(config,
|
||||
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
|
||||
[conf=$withval], [conf=$sysconfdir/finit.conf])
|
||||
|
||||
AC_ARG_WITH(rcsd,
|
||||
AS_HELP_STRING([--with-rcsd=DIR], [Finit rcS.d/ directory, default /etc/finit.d]),
|
||||
[rcsd=$withval], [rcsd=$sysconfdir/finit.d])
|
||||
|
||||
AC_ARG_WITH(rc-local,
|
||||
AS_HELP_STRING([--with-rc-local=FILE], [SysV init /etc/rc.local file, default /etc/rc.local]),
|
||||
[rclocal=$withval], [rclocal=$sysconfdir/rc.local])
|
||||
|
||||
AC_ARG_WITH(console,
|
||||
AS_HELP_STRING([--with-console=DEV], [Console to use, default: /dev/console]),
|
||||
[console=$withval], [console=/dev/console])
|
||||
|
||||
AC_ARG_WITH(username,
|
||||
AS_HELP_STRING([--with-username=USER], [User for 'startx' .conf option, default: root]),
|
||||
[username=$withval], [username=root])
|
||||
|
||||
AC_ARG_WITH(hostname,
|
||||
AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]),
|
||||
[hostname=$withval], [hostname=noname])
|
||||
|
||||
AC_ARG_WITH(runlevel,
|
||||
AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap, default: 2]),
|
||||
[runlevel=$withval], [runlevel=2])
|
||||
|
||||
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=/var/lib/misc/random-seed])
|
||||
|
||||
|
||||
### Enable features ###########################################################################
|
||||
|
||||
# Create config.h from selected features and fallback defautls
|
||||
AS_IF([test "x$enable_emergency_shell" = "xyes"], [
|
||||
AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
|
||||
|
||||
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
|
||||
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
|
||||
|
||||
AS_IF([test "x$enable_rw_rootfs" = "xyes"], [
|
||||
AC_DEFINE(REMOUNT_ROOTFS, 1, [Remount / as rw at boot])])
|
||||
|
||||
AS_IF([test "x$enable_silent" = "xyes"], [
|
||||
AC_DEFINE(SILENT_MODE, 1, [Completely silent operation, not even boot messages])
|
||||
], [
|
||||
AC_DEFINE(SILENT_MODE, 0, [Normal output, default])])
|
||||
|
||||
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
||||
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
||||
|
||||
### Disable features ###########################################################################
|
||||
AS_IF([test "x$enable_inetd" != "xno"],
|
||||
enable_inetd="yes", [
|
||||
AC_DEFINE(INETD_DISABLED, 1, [Disable built-in inetd])])
|
||||
|
||||
AS_IF([test "x$enable_quiet" != "xno"], [
|
||||
AC_DEFINE(QUIET_MODE, 1, [Silent, after initial boot, no stopping/starting messages. Default: 1])
|
||||
], [
|
||||
AC_DEFINE(QUIET_MODE, 0, [Silent, after initial boot, no stopping/starting messages. Default: 1])])
|
||||
|
||||
### With features ##############################################################################
|
||||
AS_IF([test "x$with_fifo" != "xno"], [
|
||||
AS_IF([test "x$fifo" = "xyes"], [
|
||||
fifo='/var/run/initctl'])])
|
||||
AC_EXPAND_DIR(lurvig, "$fifo")
|
||||
AC_DEFINE_UNQUOTED(FINIT_FIFO, "$lurvig", [FIFO for old style init/telinit client programs])
|
||||
|
||||
AS_IF([test "x$with_config" != "xno"], [
|
||||
AS_IF([test "x$config" = "xyes"], [
|
||||
conf=$sysconfdir/finit.conf])])
|
||||
AC_EXPAND_DIR(lurvig, "$conf")
|
||||
AC_DEFINE_UNQUOTED(FINIT_CONF, "$lurvig", [Main Finit configuration file, default /etc/finit.conf])
|
||||
|
||||
AS_IF([test "x$with_rcsd" != "xno"], [
|
||||
AS_IF([test "x$rcsd" = "xyes"], [
|
||||
rcsd=$sysconfdir/finit.d])])
|
||||
AC_EXPAND_DIR(lurvig, "$rcsd")
|
||||
AC_DEFINE_UNQUOTED(FINIT_RCSD, "$lurvig", [Finit rcS.d/, default /etc/finit.d])
|
||||
|
||||
AS_IF([test "x$with_rc_local" != "xno"], [
|
||||
AS_IF([test "x$rclocal" = "xyes"], [
|
||||
rclocal=$sysconfdir/rc.local])])
|
||||
AC_EXPAND_DIR(lurvig, "$rclocal")
|
||||
AC_DEFINE_UNQUOTED(FINIT_RC_LOCAL, "$lurvig", [Compat SysV /etc/rc.local file to run last at boot.])
|
||||
|
||||
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$with_console" != "xno"], [
|
||||
AS_IF([test "x$console" = "xyes"], [
|
||||
console=/dev/console])])
|
||||
AC_DEFINE_UNQUOTED(CONSOLE, "$console", [Default console to use])
|
||||
|
||||
AS_IF([test "x$with_username" != "xno"], [
|
||||
AS_IF([test "x$username" = "xyes"], [
|
||||
username=root])])
|
||||
AC_DEFINE_UNQUOTED(DEFUSER, "$username", [Used for startx .conf option])
|
||||
|
||||
AS_IF([test "x$with_hostname" != "xno"], [
|
||||
AS_IF([test "x$hostname" = "xyes"], [
|
||||
hostname=noname])])
|
||||
AC_DEFINE_UNQUOTED(DEFHOST, "$hostname", [For /etc/hostname])
|
||||
|
||||
AS_IF([test "x$with_runlevel" != "xno"], [
|
||||
AS_IF([test "x$runlevel" = "xyes"], [
|
||||
AC_DEFINE_UNQUOTED(RUNLEVEL, 2, [Default runlevel to start after S])])
|
||||
AC_DEFINE_UNQUOTED(RUNLEVEL, $runlevel, [Runlevel to start after S])])
|
||||
|
||||
AS_IF([test "x$with_random_seed" != "xno"], [
|
||||
AS_IF([test "x$random_seed" = "xyes"], [
|
||||
random_seed=/var/lib/misc/random-seed])
|
||||
AC_EXPAND_DIR(lurvig, "$random_seed")
|
||||
AC_DEFINE_UNQUOTED(RANDOMSEED, "$lurvig", [Improve random at boot by seeding it with sth from before.])])
|
||||
|
||||
# Control build with automake flags
|
||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
||||
|
||||
# Generate all files
|
||||
AC_OUTPUT
|
||||
@@ -0,0 +1,12 @@
|
||||
Finit Examples
|
||||
==============
|
||||
|
||||
This section provides configuration examples and helpful tools to
|
||||
install and try out Finit on various Linux distributions.
|
||||
|
||||
* [Alpine](alpine/)
|
||||
* [Debian](debian/)
|
||||
|
||||
If you have ideas on how to simplify, extend, or even add new example
|
||||
configurations for other distributions you are most welcome! :)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
HowTo: Finit on Alpine Linux 3.4
|
||||
================================
|
||||
|
||||
Mini HowTo using Finit to boot an Alpine Linux system. It is assumed
|
||||
that the user has already installed a compiler, C library header files,
|
||||
and other tools needed to build a GNU configure based project.
|
||||
|
||||
To start with you need to first install [libuEv][] and [libite][], by
|
||||
default they install to `/usr/local` and Alpine's `pkg-config` does not
|
||||
look for libraries and header files there. So the `PKG_CONFIG_LIBDIR`
|
||||
environment variable has to be used:
|
||||
|
||||
alpine:~/finit# PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure \
|
||||
--enable-rw-rootfs --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin --enable-inetd-echo-plugin \
|
||||
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
|
||||
--enable-inetd-discard-plugin --enable-inetd-time-plugin \
|
||||
--with-heading="Alpine Linux 3.4" --with-hostname=alpine
|
||||
|
||||
The plugins are optional, but the other configure flags are *not*.
|
||||
|
||||
alpine:~/finit# make all install
|
||||
|
||||
The installation skips `/sbin/init`, because it already exists, and
|
||||
Alpine is hard coded to use it. So you have to change the symlink
|
||||
yourself to point to `finit` instead of `/bin/busybox`.
|
||||
|
||||
alpine:~/finit# cd /sbin
|
||||
alpine:/sbin# rm init
|
||||
alpine:/sbin# ln -s finit init
|
||||
|
||||
Before rebooting, make sure to set up a [/etc/finit.conf](finit.conf),
|
||||
and possibly also an [/etc/rc.local](rc.local) for one-shot set up and
|
||||
initialization like keyboard language etc. You can use the files in
|
||||
this directory as templates -- check them both if they need to be
|
||||
tweaked for your installation, you may not run Dropbear SSH or speak
|
||||
Swedish ...
|
||||
|
||||
[libuEv]: https://github.com/troglobit/libuev
|
||||
[libite]: https://github.com/troglobit/libite
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
@@ -0,0 +1,40 @@
|
||||
# /etc/finit.conf: Example for Alpine Linux 3.4
|
||||
|
||||
# Modules to load
|
||||
#module qxl
|
||||
#module fbcon
|
||||
module button
|
||||
module evdev
|
||||
|
||||
# Runlevel to start after bootstrap, runlevel 'S'
|
||||
runlevel 2
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
service [S12345] /sbin/klogd -n -- Kernel log daemon
|
||||
service [S12345] /sbin/syslogd -n -- Syslog daemon
|
||||
|
||||
# Network bringup script, blocks boot like run stanza, use below task instead
|
||||
#network /sbin/ifup -a
|
||||
|
||||
### Every line below can be a service/task per file in /etc/finit.d/*.conf ###
|
||||
|
||||
# Network bringup not needed if /etc/network/interfaces is used.
|
||||
task [2345] log <svc/sbin/syslogd> /sbin/ifup -a -- Networking
|
||||
|
||||
# Depends on syslogd having started. Redirect any output to log.
|
||||
service [2345] log <svc/sbin/syslogd> /usr/sbin/acpid -f -- ACPI daemon
|
||||
|
||||
# Silly Busybox ntpd has no option to use syslog when running in foreground
|
||||
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
|
||||
|
||||
# Start SSH daemon as soon as basic networking comes up
|
||||
service [2345] <net/lo/up> /usr/sbin/dropbear -R -F -p 22 -- Dropbear SSH daemon
|
||||
service [2345] <svc/sbin/syslogd> /usr/sbin/crond -f -S -c /etc/crontabs -- Cron daemon
|
||||
|
||||
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
|
||||
service [2345] /sbin/acpid -f -- ACPI daemon
|
||||
|
||||
# Consoles to start getty on when system is up
|
||||
tty /dev/tty1
|
||||
tty /dev/tty2
|
||||
tty /dev/tty3
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
zcat /etc/keymap/sv-latin1.bmap.gz | loadkmap
|
||||
@@ -0,0 +1,52 @@
|
||||
HowTo: Finit on Debian Jessie
|
||||
=============================
|
||||
|
||||
Mini HowTo using Finit to boot Debian GNU/Linux 8.5 (Jessie). It is
|
||||
assumed that the user has already installed a compiler, C library header
|
||||
files, and other tools needed to build a GNU configure based project.
|
||||
|
||||
Also, Finit has only been verified to work with the Debian *Server* ed.
|
||||
I.e., during install — skip GNOME and other X stuff to get a plain and
|
||||
usable Debian server install, which we can add X-Window to later :-)
|
||||
|
||||
Now, I started off by replacing systemd from the base install with the
|
||||
old SysV init, using [this HowTo][1], you may not need to do that, but
|
||||
for me it was easier — and also a great rebuttal to all the systemd
|
||||
haters out there (yeah, even though I speak for the alternative PID 1
|
||||
movement I don't hate systemd :P) it is possible to still run Debian
|
||||
without systemd! Anyway, this text was based on that experience.
|
||||
|
||||
Like the [Alpine HowTo](../alpine/), you need to install [libuEv][] and
|
||||
[libite][], but since this is Debian — which takes infinite care of its
|
||||
users ♥ ♥ ♥ we don't need to worry about `pkg-config`, except for having
|
||||
it installed so it can locate the uEv and lite libraries.
|
||||
|
||||
> With Debian everything just works!™
|
||||
|
||||
The following build and install script can be used to configure, build,
|
||||
install and set up your system to run Finit:
|
||||
|
||||
debian:~/finit$ contrib/debian/build.sh
|
||||
|
||||
Since `/sbin/init` already exists on your system the script creates
|
||||
another entry in your GRUB config, in `/etc/grub.d/40_custom`, where
|
||||
`init=/sbin/finit` is added to your kernel line. You could of course
|
||||
also change the `/sbin/init` symlink and uninstall systemd/SysV init if
|
||||
everything works fine, but that's up to you.
|
||||
|
||||
Before rebooting, read up on [/etc/finit.conf](finit.conf) and notice
|
||||
how `/etc/start.d/` and `/etc/rc.local` can be used to extend the boot
|
||||
with simpler setup tasks on your system — see the docu for details.
|
||||
|
||||
Have fun!
|
||||
/Joachim ツ
|
||||
|
||||
[1]: http://without-systemd.org/wiki/index.php/How_to_remove_systemd_from_a_Debian_jessie/sid_installation
|
||||
[libuEv]: https://github.com/troglobit/libuev
|
||||
[libite]: https://github.com/troglobit/libite
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script configures Finit for Debian, which needs a few more plugins
|
||||
# and tweaks than an embedded system usually does.
|
||||
#
|
||||
# Usage:
|
||||
# user@jessie:~/finit$ contrib/debian/build.sh
|
||||
#
|
||||
|
||||
echo
|
||||
echo "*** Configuring Finit for Debian"
|
||||
|
||||
if [ ! -e configure ]; then
|
||||
echo " The configure script is missing, maybe you're using a GIT checkout?"
|
||||
echo " Attempting to run the autogen.sh script, you need these tools:"
|
||||
echo " autoconf, automake, libtool, pkg-config ..."
|
||||
echo
|
||||
./autogen.sh
|
||||
fi
|
||||
echo
|
||||
|
||||
./configure --enable-rw-rootfs --enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin --with-random-seed=/var/lib/urandom/random-seed \
|
||||
--with-heading="Debian GNU/Linux 8.5" --with-hostname=jessie \
|
||||
--enable-inetd-echo-plugin --enable-inetd-chargen-plugin \
|
||||
--enable-inetd-daytime-plugin --enable-inetd-discard-plugin \
|
||||
--enable-inetd-time-plugin
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo
|
||||
echo "*** Configure script failed, have you installed libuEV and libite?"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "*** Building ..."
|
||||
echo
|
||||
make
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo
|
||||
echo "*** The build failed for some reason"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "*** Done"
|
||||
echo
|
||||
|
||||
read -p "*** Run (sudo) install script (y/N)? " yorn
|
||||
if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
|
||||
sudo contrib/debian/install.sh
|
||||
else
|
||||
echo
|
||||
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
|
||||
echo
|
||||
fi
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# /etc/finit.conf: Example for Debian GNU/Linux 8.5
|
||||
# Here we use one big (?) finit.conf, but all non-bootstrap services
|
||||
# can just as easily be listed in a separate /etc/finit.d/*.conf
|
||||
|
||||
# The host directive is a fallback if /etc/hostname is missing
|
||||
host jessie
|
||||
|
||||
# The user directive is only for the startx directive, which in turn is
|
||||
# just like a service but with a different user. Services can also run
|
||||
# with a different user, using the @username syntax
|
||||
user root
|
||||
|
||||
# Linux kernel modules to load, /etc/modprobe is not used
|
||||
module button
|
||||
module evdev
|
||||
module loop
|
||||
module psmouse
|
||||
|
||||
# Runlevel to start after bootstrap, runlevel 'S'.
|
||||
# Change this to 3 to start XDM at boot.
|
||||
runlevel 2
|
||||
|
||||
# Some services may need to start very early, e.g. a watchdog daemon.
|
||||
# Runlevel [S] (bootStrap) is useful for that. It cannot be switched
|
||||
# to at runtime either, so tasks and run directives usually live here.
|
||||
# Please note, the run directive is blocking, so be careful!
|
||||
#service [S12345] /sbin/watchdogd -n -- Watchdog daemon
|
||||
#run [S] /sbin/probe -- system probe
|
||||
#run [S] /sbin/setupd --bootstrap -- system bootstrap
|
||||
service [S12345] /usr/sbin/rsyslogd -n -- Syslog daemon
|
||||
|
||||
# Network bringup script not needed if /etc/network/interfaces is used
|
||||
#network service networking start
|
||||
|
||||
# Regular services to be monitored and respawned as needed. Here we use
|
||||
# a (svc) condition, the listed service must be started (waits for the
|
||||
# PID file to be created in /run/*.pid) before starting either service,
|
||||
# (net) conditions are also available.
|
||||
service [2345] <svc/usr/sbin/rsyslogd> log /usr/sbin/acpid -f -- ACPI daemon
|
||||
service [2345] <svc/usr/sbin/rsyslogd> /usr/sbin/sshd -D -- SSH daemon
|
||||
|
||||
# Here we use XDM since it has few dependencies (relatively speaking).
|
||||
# It is also an example of a service (startx is an alias for service)
|
||||
# that does not have a -- description.
|
||||
#service [3] /usr/sbin/lightdm -- X-Window Display Manager
|
||||
startx [3] /usr/bin/xdm -nodaemon
|
||||
|
||||
# Run old-style start scripts from this directory. Very similar to how
|
||||
# SysV init works: 01first, 02second, etc. see run-parsts(8) for details
|
||||
runparts /etc/start.d
|
||||
# |-- S03keyboard-setup
|
||||
# |-- S17kbd
|
||||
# `- S18console-setup
|
||||
|
||||
# Virtual consoles to start getty on. Runlevels, speed & $TERM optional
|
||||
# TTYs are also services, sort of, but they are guaranteed to start at
|
||||
# the very end of the boot sequence. Finit comes with a built-in getty.
|
||||
tty [12345] /dev/tty1 115200 vt100
|
||||
tty [2345] /dev/tty2 38400 linux
|
||||
tty [2345] /dev/tty3 38400 linux
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
exec tail -n +3 $0
|
||||
# This file provides an easy way to add custom menu entries. Simply type the
|
||||
# menu entries you want to add after this comment. Be careful not to change
|
||||
# the 'exec tail' line above.
|
||||
|
||||
menuentry '$MENU with Finit' --class $TYPE --class gnu-linux --class gnu --class os --user {
|
||||
insmod gzio
|
||||
insmod part_msdos
|
||||
insmod ext2
|
||||
search --no-floppy --fs-uuid --set=root $BOOT
|
||||
linux /vmlinuz root=UUID=$ROOT ro quiet init=/sbin/finit
|
||||
initrd /initrd.img
|
||||
}
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/bin/sh
|
||||
|
||||
SYMLINKS="start.d/*"
|
||||
FILES="finit.conf"
|
||||
|
||||
if [ "x`id -u`" != "x0" ]; then
|
||||
echo
|
||||
echo "=> Unfortunately this script must run as root (sudo) <="
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Adjust base directory
|
||||
grep -q Debian os-release 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
cd ../..
|
||||
elif [ ! -e finit.c ]; then
|
||||
echo "Please run this script from the Finit base directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Install Finit on Debian 8 (jessie)"
|
||||
echo "========================================================================"
|
||||
echo "/sbin/finit - PID 1"
|
||||
echo "/lib/finit/plugins/* - All enabled Finit plugins"
|
||||
echo "/etc/finit.conf - Instead of units or /etc/init.d Finit can have"
|
||||
echo " one config for your system. Or you can split"
|
||||
echo " it in service(s) per file in /etc/finit.d/"
|
||||
echo "/etc/start.d/ - Simple Debian setup scripts run with run-parts(8)"
|
||||
echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader"
|
||||
echo
|
||||
read -p "Do you want to continue (y/N)? " yorn
|
||||
echo
|
||||
|
||||
if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
|
||||
echo "Installing Finit files ..."
|
||||
make install
|
||||
cd `dirname $0`
|
||||
mkdir -p /etc/start.d
|
||||
for file in $FILES; do
|
||||
install -vbD $file /etc/$file
|
||||
done
|
||||
for link in $SYMLINKS; do
|
||||
cp -va $link /etc/$link
|
||||
done
|
||||
echo "Setting up a GRUB boot entry ..."
|
||||
MENU=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/menuentry '\([^']*\).*/\1/"`
|
||||
TYPE=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/.*' --class \([a-z]*\).*/\1/"`
|
||||
BOOT=`grep set=root /boot/grub/grub.cfg |head -1 |sed 's/.* \([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
|
||||
ROOT=`grep root=UUID /boot/grub/grub.cfg |head -1 |sed 's/.*UUID=\([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
|
||||
cat grub.d/40_custom | sed "s,\$BOOT,$BOOT,;s,\$ROOT,$ROOT,;s,\$TYPE,$TYPE,;s,\$MENU,$MENU," >/etc/grub.d/40_custom
|
||||
update-grub
|
||||
else
|
||||
echo "Aborting install."
|
||||
fi
|
||||
@@ -0,0 +1,8 @@
|
||||
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/"
|
||||
@@ -0,0 +1 @@
|
||||
../init.d/keyboard-setup
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../init.d/kbd
|
||||
+1
@@ -0,0 +1 @@
|
||||
../init.d/console-setup
|
||||
@@ -1,7 +1,12 @@
|
||||
#!/bin/sh
|
||||
# Alternative boot method for other distributions than Debian 6.0
|
||||
# Place this file in a /etc/finit.d/ directory. At boot finit will
|
||||
# run all files, using its built-in run_parts(), in that directory.
|
||||
# Place this file in a /etc/start.d/ directory and add the following
|
||||
# line to /etc/finit.conf:
|
||||
#
|
||||
# runparts /etc/start.d
|
||||
#
|
||||
# At boot finit will run all files, using its built-in run_parts(),
|
||||
# in that directory.
|
||||
#
|
||||
# Here we chain load Debian /etc/rcS.d and /etc/rc2.d directories
|
||||
# in sequence using the standard run-parts tool.
|
||||
@@ -0,0 +1,53 @@
|
||||
Bootstrap
|
||||
=========
|
||||
|
||||
1. Populate `/dev`
|
||||
2. Parse `/etc/finit.conf`
|
||||
3. Load all `.so` plugins
|
||||
4. Remount/Pivot `/` to get R+W
|
||||
5. Call 1st level hooks, `HOOK_ROOTFS_UP`
|
||||
6. Mount `/etc/fstab` and swap, if available
|
||||
7. Cleanup stale files from `/tmp/*` et al
|
||||
8. Enable SysV init signals
|
||||
9. Call 2nd level hooks, `HOOK_BASEFS_UP`
|
||||
10. Start all 'S' runlevel tasks and services
|
||||
11. Load kernel params from `/etc/sysctl.d/*.conf`, `/etc/sysctl.conf`
|
||||
et al. (Supports all locations that SysV init does.)
|
||||
12. Set hostname and bring up loopback interface
|
||||
13. Call `network` script, if set in `/etc/finit.conf`
|
||||
14. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
||||
15. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
||||
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
|
||||
(re)loaded and new services, tasks, and blocking run commands are
|
||||
started. Provided they are allowed in the new runlevel and all of
|
||||
their conditions, if any, are set.
|
||||
16. Call 4th level hooks, `HOOK_SVC_UP`
|
||||
17. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
||||
18. Call `/etc/rc.local`, if it exists and is an executable shell script
|
||||
19. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||
20. Start TTYs defined in `/etc/finit.conf`, or rescue on `/dev/console`
|
||||
|
||||
In (10) and (15) tasks and services defined in `/etc/finit.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 file with only
|
||||
`run` statements.
|
||||
|
||||
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.
|
||||
|
||||
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
|
||||
USB stick, convert/copy its contents to the system's `/etc/` directory,
|
||||
well before all 'S' runlevel tasks are started. This could be used with
|
||||
system images that are created read-only and all configuration is stored
|
||||
on external media.
|
||||
|
||||
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
@@ -0,0 +1,99 @@
|
||||
Building
|
||||
========
|
||||
|
||||
Finit comes with a traditional configure script to control features and
|
||||
optional plugins to enable. It does however depend on two external
|
||||
libraries that provide some frog DNA needed:
|
||||
|
||||
- [libuEv][]
|
||||
- [libite][] (-lite)
|
||||
|
||||
Like most free/open source software that uses `configure` they deafult
|
||||
to install to `/usr/local`. However, some Linux distributions do no
|
||||
longer search that path for installed software, e.g. Fedora and Alpine
|
||||
Linux. To get finit's configure script to find its dependencies you
|
||||
have to help the `pkg-config` tool a bit if you do not change the
|
||||
default prefix path:
|
||||
|
||||
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
|
||||
|
||||
Below are a few of the main switches to
|
||||
configure:
|
||||
|
||||
* `--disable-inetd`: Disable the built-in inetd server.
|
||||
|
||||
* `--enable-embedded`: Target finit for BusyBox getty and mdev instead
|
||||
of a standard Linux distribution with GNU tools and udev.
|
||||
|
||||
* `--enable-rw-rootfs`: Most desktop and server systems boot with the
|
||||
root file stystem read-only. With this setting Finit will remount it
|
||||
as read-write early at boot so the `bootmisc.so` plugin can run.
|
||||
Usually not needed on embedded systems.
|
||||
|
||||
* `--enable-static`: Build Finit statically. The plugins will be
|
||||
built-ins (.o files) and all external libraries, except the C library
|
||||
will be linked statically.
|
||||
|
||||
* `--enable-alsa-utils`: Enable the optional `alsa-utils.so` sound plugin.
|
||||
|
||||
* `--enable-dbus`: Enable the optional D-Bus `dbus.so` plugin.
|
||||
|
||||
* `--enable-lost`: Enable noisy example plugin for `HOOK_SVC_LOST`.
|
||||
|
||||
* `--enable-resolvconf`: Enable the `resolvconf.so` optional plugin.
|
||||
|
||||
* `--enable-x11-common`: Enable the optional X Window `x11-common.so` plugin.
|
||||
|
||||
For more configure flags, see <kbd>./configure --help</kbd>
|
||||
|
||||
**Example**
|
||||
|
||||
First, unpack the archive:
|
||||
|
||||
```shell
|
||||
$ tar xf finit-3.0.tar.xz
|
||||
$ cd finit-3.0/
|
||||
```
|
||||
|
||||
Then configure, build and install:
|
||||
|
||||
```shell
|
||||
$ ./configure --enable-embedded --enable-rw-rootfs --enable-inetd-echo-plugin \
|
||||
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
|
||||
--enable-inetd-discard-plugin --enable-inetd-time-plugin \
|
||||
--with-heading="Alpine Linux 3.4" --with-hostname=alpine
|
||||
$ make
|
||||
.
|
||||
.
|
||||
.
|
||||
$ DESTDIR=/tmp/finit make install
|
||||
```
|
||||
|
||||
In this example the [finit-3.0.tar.xz][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.
|
||||
|
||||
To target an embedded Linux system, usally a system that use BusyBox
|
||||
tools instead of udev & C:o, add <kbd>--enable-embedded</kbd> to the
|
||||
configure command above. This enables `mdev` instead of `udev` and the
|
||||
BusyBox `getty` syntax. Remember to also change the Linux config to:
|
||||
|
||||
CONFIG_UEVENT_HELPER_PATH="/sbin/mdev"
|
||||
|
||||
**Note:** If you run into problems starting Finit, take a look at
|
||||
`finit.c`. One of the most common problems is a custom Linux kernel
|
||||
build that lack `CONFIG_DEVTMPFS`. Another is too much cruft in the
|
||||
system `/etc/fstab`.
|
||||
|
||||
|
||||
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
|
||||
[libuEv]: https://github.com/troglobit/libuev
|
||||
[libite]: https://github.com/troglobit/libite
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
@@ -0,0 +1,162 @@
|
||||
Finit Conditions
|
||||
================
|
||||
|
||||

|
||||
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Triggering](#triggering)
|
||||
* [Built-in Conditions](#built-in--conditions)
|
||||
* [Debugging](#debugging)
|
||||
* [Internals](#internals)
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
In addition to declaring allowed runlevels per service, it is also
|
||||
possible to declare user-defined conditions as dependencies. Conditions
|
||||
are specified within angle brackets (<>) in the service stanza.
|
||||
Multiple conditions may be specified separated by comma. Conditions are
|
||||
AND'ed during evaluation, i.e. all conditions must be satisfied in order
|
||||
for a service to run.
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
```shell
|
||||
service [2345] <svc/sbin/setupd,svc/sbin/zebra> /sbin/netd -- Network monitor
|
||||
```
|
||||
|
||||
In this example the Network monitor daemon `netd` is not started until
|
||||
both the `svc/sbin/setupd` *and* `svc/sbin/zebra` conditions are
|
||||
satisfied. An `svc` condition is satisfied by the corresponding
|
||||
service's pidfile being created.
|
||||
|
||||
|
||||
Triggering
|
||||
----------
|
||||
|
||||
Conditions are triggered either by plugins or by using the `cond`
|
||||
command of the `initctl` control tool.
|
||||
|
||||
* `initctl cond set your/cond/here`
|
||||
|
||||
To set a condition
|
||||
|
||||
* `initctl cond clear your/cond/here`
|
||||
|
||||
To clear a condition
|
||||
|
||||
Conditions retain their current state until the next reconfiguration or
|
||||
runlevel change. At that point all set conditions transition into the
|
||||
`flux` state, meaning the condition's state is unknown. (See
|
||||
[Internals](#internals) for the rationale behind this.) Thus, after a
|
||||
reconfiguration it is up to the "owner" of the condition to convey the
|
||||
new (or possibly unchanged) state of it.
|
||||
|
||||
|
||||
Built-in Conditions
|
||||
-------------------
|
||||
|
||||
Finit is distributed with a `pidfile` and `netlink` plugin. If enabled,
|
||||
the `pidfile` plugin watches `/var/run/` for PID files created by
|
||||
monitored services, and sets a corresponding condition in the `svc/`
|
||||
namespace. Similarily, the `netlink` plugin provides basic conditions
|
||||
for when an interface is brought up/down and when a default route
|
||||
(gateway) is set.
|
||||
|
||||
With the example listed above, finit does not start the `/sbin/netd`
|
||||
daemon until `setupd` and `zebra` has started *and* created their PID
|
||||
files. Which they do when they completed their main tasks of setting up
|
||||
VLANs, bridge, interfaces, etc. When `netd` in turn starts up it
|
||||
creates the file `/var/run/netd.pid`, and the condition `svc/sbin/netd`
|
||||
is satisfied. When the file is removed, the condition is cleared.
|
||||
|
||||
The full path to the dependency is needed by finit to match the PID file
|
||||
to a monitored process.
|
||||
|
||||
Built-in conditions:
|
||||
|
||||
- `svc/<PATH>`
|
||||
- `net/route/default`
|
||||
- `net/<IFNAME>/exist`
|
||||
- `net/<IFNAME>/up`
|
||||
|
||||
Note: `up` is administratively up, `IFF_UP`, not link up, `IFF_RUNNING`.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
In that situation, running `initctl cond show` reveals which of the
|
||||
conditions that are not satisfied. Listed as `off` below.
|
||||
|
||||
**Example:**
|
||||
|
||||
```shell
|
||||
~ # initctl cond show
|
||||
PID Service Status Condition (+ on, ~ flux, - off)
|
||||
===============================================================================
|
||||
1419 /sbin/netd on <+svc/sbin/setupd,+svc/sbin/zebra>
|
||||
0 /sbin/udhcpc off <-net/vlan1/exist>
|
||||
```
|
||||
|
||||
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.
|
||||
As indicated by the `-`-prefix.
|
||||
|
||||
To fake interface `vlan1` suddenly appearing, and test what happens to
|
||||
`udhcpc` we can enable debug mode and assert the condition, like this:
|
||||
|
||||
```shell
|
||||
~ # initctl debug
|
||||
~ # initctl cond set net/vlan1/exist
|
||||
```
|
||||
|
||||
Then watch the console for the debug messages and then check the output
|
||||
from `initctl cond show` again. (The client will likely have failed to
|
||||
start, but at least the condition is now satisfied.)
|
||||
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
A condition is always in one of three states:
|
||||
|
||||
* `on` (+): The condition is asserted.
|
||||
* `off` (-): The condition is deasserted.
|
||||
* `flux` (~): The conditions state is unknown.
|
||||
|
||||
All conditions that have not explicitly been set are interpreted as
|
||||
being in the `off` state.
|
||||
|
||||
When a reconfiguration is requested, Finit transitions all conditions to
|
||||
the `flux` state. As a result, services that depend on a condition are
|
||||
sent `SIGSTOP`. Once the new state of the condition is asserted, the
|
||||
service receives `SIGCONT`. If the condition is no longer satisfied the
|
||||
service will then be stopped, otherwise no further action is taken.
|
||||
|
||||
This STOP/CONT handling minimizes the number of unnecessary service
|
||||
restarts that would otherwise occur because a depending service was sent
|
||||
`SIGHUP` for example.
|
||||
|
||||
Therefore, any plugin that supplies Finit with conditions must ensure
|
||||
that their state is updated after each reconfiguration. This can be
|
||||
done by binding to the `HOOK_SVC_RECONF` hook. For an example of how
|
||||
to do this, see `plugins/pidfile.c`.
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
* [/etc/finit.conf](#etcfinitconf)
|
||||
* [/etc/finit.d](#etcfinitd)
|
||||
|
||||
/etc/finit.conf
|
||||
---------------
|
||||
|
||||
Contrary to most other script based init alternatives (SysV [init][],
|
||||
[upstart][], [systemd][], [OpenRC][], etc.) Finit relies on its main
|
||||
configuration file `/etc/finit.conf` and `/etc/finit.d/`.
|
||||
|
||||
The command line arguments given in `/etc/finit.conf` to each service
|
||||
provide a default. A plugin can be used to register a callback to a
|
||||
service and then modify the behavior to suit the current runlevel and
|
||||
system configuration.
|
||||
|
||||
For instance, before starting a heavily resource intensive service like
|
||||
IPsec or OpenVPN, a callback can check if the outbound interface is up
|
||||
and has an IP address, or just check if the service is disabled — much
|
||||
like what a SysV init start script usually does.
|
||||
|
||||
Syntax:
|
||||
|
||||
* `check <DEV>`
|
||||
Run fsck on a file system. Only needed if not listed in `/etc/fstab`,
|
||||
Finit 3.0 and later check all file systems automatically at boot.
|
||||
|
||||
* `module <MODULE>`
|
||||
Load a kernel module, with optional arguments
|
||||
|
||||
* `network <PATH>`
|
||||
Script or program to bring up networking, with optional arguments
|
||||
|
||||
* `rlimit <hard|soft> RESOURCE <LIMIT|infinity>`
|
||||
Modify the specified resource's hard or soft limit to be the
|
||||
specifed limit. `RESOURCE` is a lower-case string matching the
|
||||
constants in `setrlimit(2)` with the `RLIMIT_` prefix
|
||||
removed. E.g. to select `RLIMIT_CPU`, `RESOURCE` would be
|
||||
`cpu`. `LIMIT` is an integer whose unit depends on the resource
|
||||
being modified, see `setrlimit(2)` for more information. The special
|
||||
limit `infinity` means the there should be no limit on the resource,
|
||||
i.e. `RLIM_INFINITY` is passed to `setrlimit(2)`.
|
||||
|
||||
```shell
|
||||
# No process is allowed more than 8MB of address space
|
||||
rlimit hard as 8388608
|
||||
|
||||
# Core dumps may be arbitrarily large
|
||||
rlimit soft core infinity
|
||||
```
|
||||
|
||||
* `runlevel <N>`
|
||||
N is the runlevel number 1-9, where 6 is reserved for reboot.
|
||||
Default is 2.
|
||||
|
||||
* `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. This command is guaranteed to be
|
||||
completed before running the next command.
|
||||
|
||||
* `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
||||
One-shot like 'run', but starts in parallel with the next command
|
||||
|
||||
* `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely. Please note that you often need to provide a
|
||||
`--foreground` or `--no-background` argument to most daemons to
|
||||
prevent them from forking off a sub-process in the background.
|
||||
|
||||
```shell
|
||||
service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon
|
||||
```
|
||||
|
||||
For a detailed description of conditions, and how to debug them,
|
||||
see the [Finit Conditions](conditions.md) document.
|
||||
|
||||
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
||||
Launch a daemon when a client initiates a connection on an Internet
|
||||
port. Available services are listed in the UNIX `/etc/services` file.
|
||||
Finit can filter access to from a list of interfaces, `@iflist`, per
|
||||
inetd service as well as listen to custom ports.
|
||||
|
||||
```shell
|
||||
inetd ftp/tcp nowait @root /usr/sbin/uftpd -i -f
|
||||
inetd tftp/udp wait @root /usr/sbin/uftpd -i -t
|
||||
```
|
||||
|
||||
The following example listens to port 2323 for telnet connections and
|
||||
only allows clients connecting from `eth0`:
|
||||
|
||||
```shell
|
||||
inetd 2323/tcp@eth0 nowait [2345] /sbin/telnetd -i -F
|
||||
```
|
||||
|
||||
The interface list, `@iflist`, is of the format `@iface,!iface,iface`,
|
||||
where a single `!` means to deny access. Notice how interfaces are
|
||||
comma separated with no spaces.
|
||||
|
||||
The `inetd` directive can also have ` -- Optional Description`, only
|
||||
Finit does not output this text on the console when launching inetd
|
||||
services. Instead this text is sent to syslog and also shown by the
|
||||
`initctl` tool. More on inetd below.
|
||||
|
||||
* `runparts <DIR>`
|
||||
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
||||
files, or scripts, in the directory are called, in alphabetic order.
|
||||
|
||||
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.
|
||||
|
||||
As the optional `/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.
|
||||
|
||||
* `include <CONF>`
|
||||
Include another configuration file. Absolute path required.
|
||||
|
||||
* `tty [LVLS] <DEV> [BAUD] [TERM]`
|
||||
Start a getty on the given TTY device DEV, in the given runlevels. If
|
||||
no tty setting is given in `finit.conf` no login is possible. Use the
|
||||
`service` stanza to start a stand-alone shell for really bare bones
|
||||
systems.
|
||||
|
||||
Finit comes with a lightweight built-in getty, which after it has
|
||||
opened a TTY and the user has input a username, calls `/bin/login`
|
||||
like any other getty.
|
||||
|
||||
* `console <DEV>`
|
||||
Use this, along with a matching `tty DEV` line, to mark this TTY as a
|
||||
special "console" port with with `prctl()`. Useful in use-cases when
|
||||
all logins except the console must be stopped, e.g. when performing a
|
||||
flash upgrade on an embedded system.
|
||||
|
||||
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
|
||||
be installed since system requirements differ too much. Try out the
|
||||
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
|
||||
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
|
||||
|
||||
Every `run`, `task`, `service`, or `inetd` can also list the privileges
|
||||
the `/path/to/cmd` should be executed with. Simply prefix the path with
|
||||
`[@USR[:GRP]]` like this:
|
||||
|
||||
```shell
|
||||
run [2345] @joe:users /usr/bin/logger "Hello world"
|
||||
```
|
||||
|
||||
For multiple instances of the same command, e.g. a DHCP client or
|
||||
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
||||
`service` keyword and the command, like this:
|
||||
|
||||
```shell
|
||||
service :1 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
|
||||
service :2 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
|
||||
```
|
||||
|
||||
Without the `:ID` to the service the latter will overwrite the former
|
||||
and only the old web server would be started and supervised.
|
||||
|
||||
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
|
||||
`log` to redirect `stderr` and `stdout` of the application to syslog,
|
||||
using `logger`.
|
||||
|
||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
||||
detailed description, see the [Conditions](conditions.md) document.
|
||||
|
||||
|
||||
/etc/finit.d
|
||||
------------
|
||||
|
||||
Finit supports changes to the overall system configuration at runtime.
|
||||
For this purpose the (configurable) directory `/etc/finit.d` is used.
|
||||
Here you can put configuration file snippets, one per service if you
|
||||
like, which are all sourced automatically by finit at boot when loading
|
||||
the static configuration from `/etc/finit.conf`. This is the default
|
||||
behavior, so no include directives are necessary.
|
||||
|
||||
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).
|
||||
Any service read from this directory is flagged as a dynamic service, so
|
||||
changes to or removal of `/etc/finit.d/*.conf` files, is detected.
|
||||
|
||||
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.
|
||||
- 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
|
||||
runlevels and return values from any callbacks.
|
||||
|
||||
For more info on the different states of a service, see the separate
|
||||
document [Finit Services](service.md).
|
||||
|
||||
The `/etc/finit.d` directory was previously the default Finit `runparts`
|
||||
directory. Finit no longer has a default `runparts`, so make sure to
|
||||
update your setup, or the finit configuration, accordingly.
|
||||
|
||||
**Note:** Configurations read from `/etc/finit.d` are read *after*
|
||||
initial bootstrap, runlevel S(1). Hence, bootstrap services *must* be
|
||||
in `/etc/finit.conf`!
|
||||
|
||||
[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
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
Internet Super Server
|
||||
=====================
|
||||
|
||||
Inetd
|
||||
-----
|
||||
|
||||
A built-in *Internet Super Server* support was added in Finit v1.12 and
|
||||
v1.13, along with an internal `time` inetd service, RFC 868 (rdate).
|
||||
The latter is supplied as a plugin to illustrate how simple it is to
|
||||
extend finit with more internal inetd services. Today more built-in
|
||||
services are available.
|
||||
|
||||
> Please note, not all UNIX daemons are prepared to run as inetd services.
|
||||
> In the example below `sshd` also need the command line argument `-i`.
|
||||
|
||||
The inetd support in finit is quite advanced. Not only does it launch
|
||||
services on demand, it can do so on custom ports and also filter inbound
|
||||
traffic using a poor man's [TCP wrappers][]. The syntax is very similar
|
||||
to the traditional `/etc/inetd.conf`, yet keeping with the style of
|
||||
Finit:
|
||||
|
||||
```shell
|
||||
# Launch SSH on demand, in runlevels 2-5 as root
|
||||
inetd ssh/tcp nowait [2345] @root:root /usr/sbin/sshd -i
|
||||
```
|
||||
|
||||
A more advanced example is listed below, please note the *incompatible
|
||||
syntax change* that was made between Finit v1.12 and v1.13 to support
|
||||
deny filters:
|
||||
|
||||
```shell
|
||||
# Start sshd if inbound connection on eth0, port 222, or
|
||||
# inbound on eth1, port 22. Ignore on other interfaces.
|
||||
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
|
||||
inetd ssh/tcp@eth1,eth1 nowait [2345] /usr/sbin/sshd -i
|
||||
```
|
||||
|
||||
If `eth0` is your Internet interface you may want to avoid using the
|
||||
default port. To run ssh on port 222, and all others on port 22:
|
||||
|
||||
```shell
|
||||
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
|
||||
inetd ssh/tcp@*,!eth0 nowait [2345] /usr/sbin/sshd -i
|
||||
```
|
||||
|
||||
Compared to Finit v1.12 you must *explicitly deny* access from `eth0`!
|
||||
|
||||
To protect against looping attacks, the inetd server will refuse UDP
|
||||
service if the reply port corresponds to any internal service. Similar
|
||||
to how the FreeBSD inetd operates.
|
||||
|
||||
|
||||
**Internal Services**
|
||||
|
||||
Like the original `inetd`, Finit has a few standard services built-in.
|
||||
They are realized as plugins to provide a simple means of testing the
|
||||
inetd functionality stand-alone. But this also provides both a useful
|
||||
network testing/availability, as well as a rudimentary time server for
|
||||
`rdate` clients.
|
||||
|
||||
- echo
|
||||
- chargen
|
||||
- daytime
|
||||
- discard
|
||||
- time
|
||||
|
||||
For security reasons they are all disabled by default and have to be
|
||||
enabled with both the `configure` script and a special `inetd` stanza in
|
||||
the `finit.conf` or `finit.d/*.conf` like this:
|
||||
|
||||
```shell
|
||||
inetd echo/udp wait [2345] internal
|
||||
inetd echo/tcp nowait [2345] internal
|
||||
inetd chargen/udp wait [2345] internal
|
||||
inetd chargen/tcp nowait [2345] internal
|
||||
inetd daytime/udp wait [2345] internal
|
||||
inetd daytime/tcp nowait [2345] internal
|
||||
inetd discard/udp wait [2345] internal
|
||||
inetd discard/tcp nowait [2345] internal
|
||||
inetd time/udp wait [2345] internal
|
||||
inetd time/tcp nowait [2345] internal
|
||||
```
|
||||
|
||||
Then call `rdate` from a remote machine (or use localhost):
|
||||
|
||||
```shell
|
||||
rdate -p <IP>
|
||||
rdate -up <IP>
|
||||
```
|
||||
|
||||
Or `echoping` to reach the echo service:
|
||||
|
||||
```shell
|
||||
echoping -v <IP>
|
||||
echoping -uv <IP>
|
||||
```
|
||||
|
||||
Or `echoping -d` to reach the discard service:
|
||||
|
||||
```shell
|
||||
echoping -dv <IP>
|
||||
echoping -duv <IP>
|
||||
```
|
||||
|
||||
Or `echoping -c` to reach the chargen service:
|
||||
|
||||
```shell
|
||||
echoping -cv <IP>
|
||||
echoping -cuv <IP>
|
||||
```
|
||||
|
||||
If you use `time/udp` you must use the standard rdate implementation and
|
||||
then call it with `rdate -up` to connect using UDP. Without the `-p`
|
||||
argument rdate will try to set the system clock. Please note that rdate
|
||||
has been deprecated by the NTP protocol and this plugin should only be
|
||||
used for testing or environments where NTP for some reason is blocked.
|
||||
Also, remember the UNIX year 2038 bug, or in the case of RFC 868 (and
|
||||
some NTP implementations), year 2036!
|
||||
|
||||
**Note:** There is currently no verification that the same port is used
|
||||
more than once. So a standard `inetd http/tcp` service will clash
|
||||
with an ssh entry for the same port `inetd 80/tcp` …
|
||||
|
||||
|
||||
[TCP Wrappers]: https://en.wikipedia.org/wiki/TCP_Wrapper
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
Hooks & Plugins
|
||||
===============
|
||||
|
||||
Finit can be extended to add general functionality in the form of I/O
|
||||
monitors, built-in inetd services, or hook plugins.
|
||||
|
||||
The following sections detail existing plugins and hook points. For
|
||||
more information, see the plugins listed below.
|
||||
|
||||
|
||||
Plugins
|
||||
-------
|
||||
|
||||
For your convenience a set of *optional* plugins are available:
|
||||
|
||||
* *alsa-utils.so*: Restore and save ALSA sound settings on
|
||||
startup/shutdown. _Optional plugin._
|
||||
|
||||
* *bootmisc.so*: Setup necessary files and system directories for, e.g.,
|
||||
UTMP (tracks logins at boot). This plugin is central to get a working
|
||||
system and runs at `HOOK_BASEFS_UP`. The `/var`, `/run`, and `/dev`
|
||||
file systems must be writable for this plugin to work.
|
||||
|
||||
Note: On an embedded system both `/var` and `/run` can be `tmpfs` RAM
|
||||
disks and `/dev` is usually a `devtmpfs`. This must be defined in the
|
||||
`/etc/fstab` file and in the Linux kernel config.
|
||||
|
||||
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
|
||||
_Optional plugin._
|
||||
|
||||
* *echo.so*: RFC 862 plugin. Start as inetd service, like time below.
|
||||
|
||||
* *chargen.so*: RFC 864 plugin. Start as inetd service, like time below.
|
||||
|
||||
* *daytime.so*: RFC 867 plugin. Start as inetd service, like time below.
|
||||
|
||||
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
|
||||
|
||||
* *hwclock.so*: Restore and save system clock from/to RTC on
|
||||
startup/shutdown.
|
||||
|
||||
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
|
||||
|
||||
* *lost.so*: Very simple `HOOK_SVC_LOST` example. Logs process ID and
|
||||
name to syslog. _Optional plugin._
|
||||
|
||||
* *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.
|
||||
|
||||
* *resolvconf.so*: Setup necessary files for `resolvconf` at startup.
|
||||
_Optional plugin._
|
||||
|
||||
* *time.so*: RFC 868 (rdate) plugin. Start as inetd service. Useful
|
||||
for testing inetd filtering — BusyBox has an rdate (TCP) client.
|
||||
|
||||
* *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.
|
||||
|
||||
* *urandom.so*: Setup random seed at startup.
|
||||
|
||||
* *x11-common.so*: Setup necessary files for X-Window. _Optional plugin._
|
||||
|
||||
Usually you want to hook into the boot process once, simple hook plugins
|
||||
like `bootmisc.so` are great for that purpose. They are called at each
|
||||
hook point in the boot process, useful to insert some pre-bootstrap
|
||||
mechanisms, like generating configuration files, restoring HW device
|
||||
state, etc. Available hook points are:
|
||||
|
||||
|
||||
Hooks
|
||||
-----
|
||||
|
||||
### Bootstrap Hooks
|
||||
|
||||
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
|
||||
mounted — very early
|
||||
|
||||
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
|
||||
and default init signals are setup
|
||||
|
||||
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
|
||||
networking is up (`lo` is up and the `network` script has run)
|
||||
|
||||
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
|
||||
|
||||
* `HOOK_SYSTEM_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_LOST`: Called when a process is lost. When reconfiguring
|
||||
services at runtime this hook may be called a lot. However, it may be
|
||||
a quite useful hook to monitor a system post bootstrap when no, or
|
||||
few, services are expected to exit. A default plugin `lost.so` is
|
||||
available in the `plugins/` subdirectory as an example.
|
||||
|
||||
**NOTE:** This hook callback gets the lost PID as argument.
|
||||
|
||||
* `HOOK_SVC_START`: Like `HOOK_SVC_LOST`, but called when a process is
|
||||
started. Same caveats apply.
|
||||
|
||||
**NOTE:** This hook callback gets the new PID as argument.
|
||||
|
||||
* `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.
|
||||
|
||||
### Shutdown Hooks
|
||||
|
||||
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
|
||||
services are sent `SIGTERM`
|
||||
|
||||
Plugins like `initctl.so` and `tty.so` extend finit by acting on events,
|
||||
they are called I/O plugins and are called from the finit main loop when
|
||||
`poll()` detects an event. See the source code for `plugins/*.c` for
|
||||
more help and ideas.
|
||||
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
@@ -0,0 +1,38 @@
|
||||
Finit Services
|
||||
==============
|
||||
|
||||

|
||||
|
||||
State Machine
|
||||
-------------
|
||||
|
||||
A service is bound to a state machine that is in one of six (6) states.
|
||||
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
|
||||
must be allowed to run in the current runlevel and must not be
|
||||
blocked. A service may be blocked for several reasons:
|
||||
|
||||
- The user has manually stopped the service using `initctl stop JOB`
|
||||
- The program exits immediately. I.e. keeps crashing (make sure to use
|
||||
the 'run this service in the foreground' command line option)
|
||||
- The binary is missing in the filesystem
|
||||
|
||||
* `C`: Service conditions are satisfied:
|
||||
|
||||
- `on` (+): The condition is asserted.
|
||||
- `off` (-): The condition is deasserted.
|
||||
- `flux` (~): The conditions state is unknown.
|
||||
|
||||
For a detailed description of conditions, and how to debug them,
|
||||
see the [Finit Conditions](conditions.md) document.
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
@@ -0,0 +1,41 @@
|
||||
Finit Signals
|
||||
=============
|
||||
|
||||
Finit is often used on embedded and small Linux systems with BusyBox.
|
||||
Though Finit comes with its own tools for (poweroff, halt, reboot), for
|
||||
compatibility with the existing BusyBox toolset the following signals
|
||||
have been adopted:
|
||||
|
||||
* `SIGUSR1`
|
||||
Same effect as `finit q`, reloads all *.conf files in `/etc/finit.d/`
|
||||
* `SIGUSR1`
|
||||
Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
||||
processes, and unmounts all file systems. Then tells kernel to
|
||||
halt. Most people these days want SIGUSR2 though.
|
||||
|
||||
SysV init and systemd use this to re-open their FIFO/D-Bus.
|
||||
* `SIGUSR2`
|
||||
Like SIGUSR1, but tell kernel to power-off the system, if ACPI
|
||||
or similar exists to actually do this. If the kernel fails
|
||||
power-off, Finit falls back to halt.
|
||||
|
||||
SysV init N/A, systemd dumps its internal state to log.
|
||||
* `SIGTERM`
|
||||
Like `SIGUSR1`, but tell kernel to reboot the system when done.
|
||||
|
||||
SysV init N/A, systemd rexecutes itself.
|
||||
* `SIGINT`
|
||||
Sent from kernel when the CTRL-ALT-DEL key combo is pressed.
|
||||
SysV init and systemd default to reboot with `shutdown -r`.
|
||||
Finit currently forwards this to `SIGTERM`.
|
||||
* `SIGPWR`
|
||||
Sent from a power daemon, like `powstatd(8)`, on changes to the
|
||||
UPS status. Traditionally SysV init read /etc/powerstatus and
|
||||
acted on "OK", "FAIL", or "LOW" and then removed the file.
|
||||
Finit currently forwards this to `SIGUSR2`.
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -1,241 +0,0 @@
|
||||
/* Event aggregator, also serves as event cache, remembering GW and IFUP states
|
||||
*
|
||||
* Copyright (c) 2015 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
#include "libite/lite.h"
|
||||
|
||||
#include "finit.h"
|
||||
#include "service.h"
|
||||
|
||||
struct ifc {
|
||||
LIST_ENTRY(ifc) link;
|
||||
|
||||
int updown;
|
||||
char ifname[IFNAMSIZ];
|
||||
};
|
||||
|
||||
static int gwup = 0;
|
||||
static LIST_HEAD(, ifc) ifcache = LIST_HEAD_INITIALIZER();
|
||||
|
||||
|
||||
static int cache_gw(char *updown)
|
||||
{
|
||||
int oldgw = gwup;
|
||||
|
||||
if (!strncasecmp(updown, "UP", 2))
|
||||
gwup = 1;
|
||||
else
|
||||
gwup = 0;
|
||||
|
||||
return (oldgw == gwup) ? 0 : gwup == 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
static struct ifc *find_if(char *ifname)
|
||||
{
|
||||
struct ifc *entry;
|
||||
|
||||
LIST_FOREACH(entry, &ifcache, link) {
|
||||
if (!strncmp(entry->ifname, ifname, sizeof(entry->ifname)))
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int alloc_if(char *ifname, int updown)
|
||||
{
|
||||
struct ifc *entry = calloc(1, sizeof(struct ifc));
|
||||
|
||||
if (!entry) {
|
||||
_pe("Failed recording interface event");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_d("Creating new node for %s", ifname);
|
||||
entry->updown = updown;
|
||||
strlcpy(entry->ifname, ifname, sizeof(entry->ifname));
|
||||
LIST_INSERT_HEAD(&ifcache, entry, link);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int free_if(char *ifname)
|
||||
{
|
||||
struct ifc *entry = find_if(ifname);
|
||||
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
LIST_REMOVE(entry, link);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cache_if(char *ifname, int updown)
|
||||
{
|
||||
struct ifc *entry = find_if(ifname);
|
||||
|
||||
if (entry) {
|
||||
int old = entry->updown;
|
||||
|
||||
entry->updown = updown;
|
||||
return old == updown ? 0 : updown ? 1 : -1;
|
||||
}
|
||||
|
||||
return alloc_if(ifname, updown);
|
||||
}
|
||||
|
||||
/*
|
||||
* System events like GW/IF are cached, this function caters to that
|
||||
* Returns: 0 if no change, -1 on condition low, +1 on condition high
|
||||
*/
|
||||
static int event_cache(char *msg)
|
||||
{
|
||||
if (!strncmp(msg, "GW:", 3))
|
||||
return cache_gw(&msg[3]);
|
||||
|
||||
if (!strncmp(msg, "IFUP:", 5))
|
||||
return cache_if(&msg[5], 1);
|
||||
|
||||
if (!strncmp(msg, "IFDN:", 5))
|
||||
return cache_if(&msg[5], 0);
|
||||
|
||||
if (!strncmp(msg, "IFDEL:", 6))
|
||||
return free_if(&msg[6]);
|
||||
|
||||
return 0; /* No chnage, unknown event. */
|
||||
}
|
||||
|
||||
int event_cache_gw(void)
|
||||
{
|
||||
return gwup;
|
||||
}
|
||||
|
||||
int event_cache_if(char *ifname)
|
||||
{
|
||||
struct ifc *entry = find_if(ifname);
|
||||
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
return entry->updown;
|
||||
}
|
||||
|
||||
static int has_events(char *events)
|
||||
{
|
||||
return events && events[0];
|
||||
}
|
||||
|
||||
static int matches_event(char *events, char *event)
|
||||
{
|
||||
return strcasestr(events, event) ? 1 : 0;
|
||||
}
|
||||
|
||||
int event_service_cond(char *events)
|
||||
{
|
||||
int cond = 1;
|
||||
char *msg;
|
||||
char temp[MAX_ARG_LEN];
|
||||
|
||||
/* No required events, condition satisfied */
|
||||
if (!has_events(events))
|
||||
return cond;
|
||||
|
||||
strlcpy(temp, events, sizeof(temp));
|
||||
msg = strtok(temp, ",");
|
||||
while (msg) {
|
||||
_d("Looking for %s (in %s), cond %d", msg, events, cond);
|
||||
|
||||
if (!strncmp(msg, "GW", 2))
|
||||
cond &= event_cache_gw();
|
||||
else if (!strncmp(msg, "IFUP:", 5))
|
||||
cond &= event_cache_if(&msg[5]);
|
||||
else if (!strncmp(msg, "IFDN:", 5))
|
||||
cond &= event_cache_if(&msg[5]);
|
||||
else
|
||||
_e("Unknown event %s, discarding.", msg);
|
||||
|
||||
_d("=> cond %d", cond);
|
||||
msg = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
return cond;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispatch an event
|
||||
*
|
||||
* Called by initctl (via api.c) or plugins/netlink.so
|
||||
* May also cache for future reference.
|
||||
*/
|
||||
void event_dispatch(char *msg)
|
||||
{
|
||||
int change;
|
||||
svc_t *svc;
|
||||
|
||||
if (!msg) {
|
||||
_e("Invalid message received.");
|
||||
return;
|
||||
}
|
||||
|
||||
_d("%s", msg);
|
||||
change = event_cache(msg);
|
||||
if (!change) {
|
||||
_d("Nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Iterate over svc_t and call service_restart() for event matches */
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->type != SVC_TYPE_SERVICE ||
|
||||
!service_enabled(svc, 1, NULL) ||
|
||||
!has_events(svc->events) ||
|
||||
!matches_event(svc->events, msg)) {
|
||||
_d("No match for %s in service %s", msg, svc->cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (change == 1) {
|
||||
_d("%s matches <%s> %s (re)starting ...", msg, svc->events, svc->cmd);
|
||||
if (!svc->pid) {
|
||||
service_start(svc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (svc->sighup)
|
||||
service_reload(svc);
|
||||
else
|
||||
service_restart(svc);
|
||||
} else { /* change == -1 */
|
||||
if (svc->pid && !event_service_cond(svc->events))
|
||||
service_stop(svc, SVC_CONDHALT_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -26,25 +26,20 @@
|
||||
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "sig.h"
|
||||
#include "helpers.h"
|
||||
#include "libite/lite.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#define NUM_ARGS 16
|
||||
|
||||
int getty(char *tty, char *baud, char *term, char *user);
|
||||
|
||||
|
||||
/* Wait for process completion, returns status of waitpid(2) syscall */
|
||||
int complete(char *cmd, int pid)
|
||||
@@ -53,9 +48,9 @@ int complete(char *cmd, int pid)
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
if (errno == EINTR)
|
||||
_e("Caught unblocked signal waiting for %s, aborting.", cmd);
|
||||
_e("Caught unblocked signal waiting for %s, aborting", cmd);
|
||||
else if (errno == ECHILD)
|
||||
_e("Caught SIGCHLD waiting for %s, aborting.", cmd);
|
||||
_e("Caught SIGCHLD waiting for %s, aborting", cmd);
|
||||
else
|
||||
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
|
||||
|
||||
@@ -95,11 +90,7 @@ int run(char *cmd)
|
||||
}
|
||||
}
|
||||
args[i] = NULL;
|
||||
#if 0
|
||||
_e("Splitting: '%s' =>", cmd);
|
||||
for (i = 0; args[i]; i++)
|
||||
_e("\t%s", args[i]);
|
||||
#endif
|
||||
|
||||
if (i == NUM_ARGS && args[i]) {
|
||||
_e("Command too long: %s", cmd);
|
||||
free(backup);
|
||||
@@ -109,13 +100,11 @@ int run(char *cmd)
|
||||
|
||||
pid = fork();
|
||||
if (0 == pid) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
struct sigaction sa;
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
sig_unblock();
|
||||
setsid();
|
||||
|
||||
/* Always redirect stdio for run() */
|
||||
fp = fopen("/dev/null", "w");
|
||||
@@ -172,7 +161,7 @@ int run_interactive(char *cmd, char *fmt, ...)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose && fmt) {
|
||||
if (!silent && fmt) {
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(line, sizeof(line), fmt, ap);
|
||||
va_end(ap);
|
||||
@@ -204,7 +193,7 @@ int run_interactive(char *cmd, char *fmt, ...)
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose && fmt)
|
||||
if (!silent && fmt)
|
||||
print_result(status);
|
||||
|
||||
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
|
||||
@@ -224,64 +213,25 @@ int run_interactive(char *cmd, char *fmt, ...)
|
||||
return status;
|
||||
}
|
||||
|
||||
pid_t run_getty(char *cmd, char *args[], int console)
|
||||
pid_t run_getty(char *tty, char *speed, char *term, int console)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
int fd;
|
||||
char c;
|
||||
|
||||
if (console) {
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
|
||||
close(STDERR_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDIN_FILENO);
|
||||
|
||||
/* Attach TTY to console */
|
||||
fd = open(CONSOLE, O_RDWR);
|
||||
if (fd != STDIN_FILENO)
|
||||
exit(1);
|
||||
|
||||
dup2(0, STDIN_FILENO);
|
||||
dup2(0, STDOUT_FILENO);
|
||||
dup2(0, STDERR_FILENO);
|
||||
|
||||
prctl(PR_SET_NAME, "console", 0, 0, 0);
|
||||
}
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
|
||||
setsid();
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
_pe("Failed TIOCSCTTY");
|
||||
|
||||
while (!fexist(SYNC_SHUTDOWN)) {
|
||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||
|
||||
if (fexist(SYNC_STOPPED)) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (console) {
|
||||
(void)write(STDERR_FILENO, msg, sizeof(msg));
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
||||
continue;
|
||||
}
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
execv(cmd, args);
|
||||
}
|
||||
/* Set INIT_PROCESS UTMP entry */
|
||||
utmp_set_init(tty, 0);
|
||||
|
||||
if (console)
|
||||
close(fd);
|
||||
prctl(PR_SET_NAME, "console", 0, 0, 0);
|
||||
else
|
||||
prctl(PR_SET_NAME, "finit-getty", 0, 0, 0);
|
||||
|
||||
exit(0);
|
||||
_exit(getty(tty, speed, term, NULL));
|
||||
}
|
||||
|
||||
return pid;
|
||||
@@ -351,7 +301,6 @@ int run_parts(char *dir, char *cmd)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -24,10 +24,16 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#ifdef HAVE_FSTAB_H
|
||||
#include <fstab.h>
|
||||
#endif
|
||||
#include <mntent.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h> /* umask(), mkdir() */
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
@@ -36,13 +42,15 @@
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "sm.h"
|
||||
#include "tty.h"
|
||||
#include "libite/lite.h"
|
||||
#include "inetd.h"
|
||||
#include "utmp-api.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
int debug = 0;
|
||||
int quiet = KERNEL_QUIET; /* Delayed disable of verbose mode. */
|
||||
int verbose = VERBOSE_MODE;
|
||||
int quiet = QUIET_MODE; /* Delayed disable of silent mode. */
|
||||
int silent = SILENT_MODE; /* Completely silent, including boot */
|
||||
int wdogpid = 0; /* No watchdog by default */
|
||||
int runlevel = 0; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
int prevlevel = -1;
|
||||
@@ -61,7 +69,7 @@ static int banner(void)
|
||||
char buf[42] = INIT_HEADING;
|
||||
const char separator[] = "========================================================================";
|
||||
|
||||
if (!verbose)
|
||||
if (silent)
|
||||
return 0;
|
||||
|
||||
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, 66 - (int)strlen(buf), separator);
|
||||
@@ -69,9 +77,145 @@ static int banner(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check all filesystems in /etc/fstab with a fs_passno > 0
|
||||
*/
|
||||
static int fsck(int pass)
|
||||
{
|
||||
int save;
|
||||
struct fstab *fs;
|
||||
|
||||
if (!setfsent()) {
|
||||
_pe("Failed opening fstab");
|
||||
return 1;
|
||||
}
|
||||
|
||||
save = debug;
|
||||
debug = 0;
|
||||
|
||||
while ((fs = getfsent())) {
|
||||
char cmd[80];
|
||||
struct stat st;
|
||||
|
||||
if (fs->fs_passno != pass)
|
||||
continue;
|
||||
|
||||
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));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "/sbin/fsck -a %s", fs->fs_spec);
|
||||
run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
||||
}
|
||||
|
||||
debug = save;
|
||||
endfsent();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void networking(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
/* Run user network start script if enabled */
|
||||
if (network) {
|
||||
run_interactive(network, "Starting networking: %s", network);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Debian/Ubuntu/Busybox interfaces file */
|
||||
fp = fopen("/etc/network/interfaces", "r");
|
||||
if (fp) {
|
||||
int i = 0;
|
||||
char buf[160];
|
||||
|
||||
/* Bring up all 'auto' interfaces */
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char cmd[80];
|
||||
char *line, *ifname = NULL;
|
||||
|
||||
chomp(buf);
|
||||
line = strip_line(buf);
|
||||
|
||||
if (!strncmp(line, "auto", 4))
|
||||
ifname = &line[5];
|
||||
if (!strncmp(line, "allow-hotplug", 13))
|
||||
ifname = &line[14];
|
||||
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
snprintf(cmd, 80, "/sbin/ifup %s", ifname);
|
||||
run_interactive(cmd, "Bringing up interface %s", ifname);
|
||||
i++;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
if (i)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fall back to bring up at least loopback */
|
||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
|
||||
}
|
||||
|
||||
/* Requires /proc to be mounted */
|
||||
static int fismnt(char *dir)
|
||||
{
|
||||
FILE *fp;
|
||||
int found = 0;
|
||||
struct mntent *mnt;
|
||||
|
||||
fp = setmntent("/proc/mounts", "r");
|
||||
if (!fp)
|
||||
return 0; /* Dunno, maybe not */
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(mnt->mnt_dir, dir)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* If everything goes south we can use this to give the operator an
|
||||
* emergency shell to debug the problem -- Finit should not crash!
|
||||
*/
|
||||
static void emergency_shell(void)
|
||||
{
|
||||
#ifdef EMERGENCY_SHELL
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid) {
|
||||
waitpid(pid, NULL, 0);
|
||||
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
||||
fprintf(stderr, "=> For more, remove 'quiet' and add '--debug' to the kernel command line.\n\n");
|
||||
|
||||
/*
|
||||
* Become session leader and set controlling TTY
|
||||
* to enable Ctrl-C and job control in shell.
|
||||
*/
|
||||
setsid();
|
||||
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
||||
|
||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
||||
}
|
||||
#endif /* EMERGENCY_SHELL */
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int err;
|
||||
char *devfsd;
|
||||
uev_ctx_t loop;
|
||||
|
||||
/*
|
||||
@@ -81,6 +225,16 @@ int main(int argc, char* argv[])
|
||||
if (getpid() != 1)
|
||||
return client(argc, argv);
|
||||
|
||||
/*
|
||||
* In case of emergency.
|
||||
*/
|
||||
emergency_shell();
|
||||
|
||||
/*
|
||||
* Hello world.
|
||||
*/
|
||||
banner();
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
@@ -92,6 +246,14 @@ int main(int argc, char* argv[])
|
||||
uev_init(&loop);
|
||||
ctx = &loop;
|
||||
|
||||
/*
|
||||
* Check file filesystems in /etc/fstab
|
||||
*/
|
||||
for (int pass = 1; pass < 10; pass++) {
|
||||
if (fsck(pass))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
||||
*/
|
||||
@@ -100,10 +262,30 @@ int main(int argc, char* argv[])
|
||||
mount("none", "/proc", "proc", 0, NULL);
|
||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||
mount("none", "/sys", "sysfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* Some non-embedded systems without an initramfs may not have /dev mounted yet
|
||||
* If they do, check if system has udevadm and perform cleanup from initramfs
|
||||
*/
|
||||
if (!fismnt("/dev"))
|
||||
mount("udev", "/dev", "devtmpfs", MS_RELATIME, "size=10%,nr_inodes=61156,mode=755");
|
||||
else if (fexist("/sbin/udevadm"))
|
||||
run_interactive("/sbin/udevadm info --cleanup-db", "Cleaning up udev db");
|
||||
|
||||
/* Some systems use /dev/pts */
|
||||
makedir("/dev/pts", 0755);
|
||||
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
||||
|
||||
makedir("/dev/shm", 0755);
|
||||
mount("none", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
||||
mount("none", "/dev/shm", "tmpfs", 0, NULL);
|
||||
if (!fismnt("/dev/shm"))
|
||||
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* New tmpfs based /run for volatile runtime data
|
||||
* For details, see http://lwn.net/Articles/436012/
|
||||
*/
|
||||
if (fisdir("/run") && !fismnt("/run"))
|
||||
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
|
||||
umask(022);
|
||||
|
||||
/*
|
||||
@@ -111,31 +293,44 @@ int main(int argc, char* argv[])
|
||||
*/
|
||||
conf_parse_cmdline();
|
||||
|
||||
/*
|
||||
* Hello world.
|
||||
*/
|
||||
banner();
|
||||
|
||||
/*
|
||||
* Populate /dev and prepare for runtime events from kernel.
|
||||
*/
|
||||
#ifdef EMBEDDED_SYSTEM
|
||||
if (debug)
|
||||
touch("/dev/mdev.log");
|
||||
#endif
|
||||
run_interactive(SETUP_DEVFS, "Populating device tree");
|
||||
if (fexist("/sbin/mdev")) {
|
||||
/* Embedded Linux systems usually have BusyBox mdev */
|
||||
if (debug)
|
||||
touch("/dev/mdev.log");
|
||||
devfsd = "/sbin/mdev -s";
|
||||
} else {
|
||||
if (fexist("/sbin/udevd"))
|
||||
devfsd = "/sbin/udevd --daemon";
|
||||
else
|
||||
devfsd = "/lib/systemd/systemd-udevd --daemon";
|
||||
}
|
||||
|
||||
run_interactive(devfsd, "Populating device tree");
|
||||
if (fexist("/sbin/udevadm")) {
|
||||
run("/sbin/udevadm trigger --action=add --type=subsystems");
|
||||
run("/sbin/udevadm trigger --action=add --type=devices");
|
||||
run("/sbin/udevadm settle --timeout=120");
|
||||
}
|
||||
|
||||
/*
|
||||
* Load plugins first, finit.conf may contain references to
|
||||
* features implemented by plugins.
|
||||
*/
|
||||
plugin_load_all(&loop, PLUGIN_PATH);
|
||||
plugin_init(&loop, PLUGIN_PATH);
|
||||
|
||||
/*
|
||||
* Parse /etc/finit.conf, main configuration file
|
||||
*/
|
||||
conf_parse_config();
|
||||
|
||||
/*
|
||||
* Start built-in watchdog as soon as possible, if enabled
|
||||
*/
|
||||
watchdog(argv[0]);
|
||||
|
||||
/* Set hostname as soon as possible, for syslog et al. */
|
||||
set_hostname(&hostname);
|
||||
|
||||
@@ -152,23 +347,20 @@ int main(int argc, char* argv[])
|
||||
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
#endif
|
||||
|
||||
/* Debian has this little script to copy generated rules while the system was read-only */
|
||||
if (fexist("/lib/udev/udev-finish"))
|
||||
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
||||
|
||||
_d("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
|
||||
umask(0);
|
||||
print_desc("Mounting filesystems", NULL);
|
||||
|
||||
err = run("/bin/mount -na");
|
||||
print_result(err);
|
||||
if (err)
|
||||
if (run_interactive("/bin/mount -na", "Mounting filesystems"))
|
||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||
|
||||
run("/sbin/swapon -ea");
|
||||
umask(0022);
|
||||
|
||||
/* Cleanup stale files, if any still linger on. */
|
||||
run_interactive("rm -rf /tmp/* /var/run/* /var/lock/*", "Cleanup temporary directories");
|
||||
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
@@ -176,20 +368,15 @@ int main(int argc, char* argv[])
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
|
||||
/*
|
||||
* Start all bootstrap tasks, no network available!
|
||||
* Initalize finit state machine and start all bootstrap tasks, no network available!
|
||||
*/
|
||||
service_bootstrap();
|
||||
sm_init(&sm);
|
||||
sm_step(&sm);
|
||||
|
||||
/*
|
||||
* Network stuff
|
||||
*/
|
||||
|
||||
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
|
||||
run("/sbin/sysctl -e -p /etc/sysctl.conf >/dev/null");
|
||||
|
||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
|
||||
if (network)
|
||||
run_interactive(network, "Starting networking: %s", network);
|
||||
networking();
|
||||
umask(022);
|
||||
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
@@ -209,6 +396,13 @@ int main(int argc, char* argv[])
|
||||
if (runparts && fisdir(runparts)) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
run_parts(runparts, NULL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Convenient SysV compat for when you just don't care ... */
|
||||
if (!access(FINIT_RC_LOCAL, X_OK)) {
|
||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
@@ -217,9 +411,9 @@ int main(int argc, char* argv[])
|
||||
/* Start TTYs */
|
||||
tty_runlevel(runlevel);
|
||||
|
||||
/* Disable verbose mode, if selected */
|
||||
if (quiet)
|
||||
verbose = 0;
|
||||
/* Enable silent mode, if selected */
|
||||
if (quiet && !debug)
|
||||
silent = 1;
|
||||
|
||||
/* Start new initctl API responder */
|
||||
api_init(&loop);
|
||||
@@ -232,7 +426,6 @@ int main(int argc, char* argv[])
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -33,26 +33,41 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libite/lite.h"
|
||||
#include <lite/lite.h>
|
||||
#include <uev/uev.h>
|
||||
|
||||
#define CMD_SIZE 256
|
||||
#define LINE_SIZE 1024
|
||||
#define BUF_SIZE 4096
|
||||
|
||||
/* We reuse the INIT_CMD_ for the new initctl tool, but telinit only
|
||||
* supports changing runlevel and reloading the configuration. */
|
||||
/*
|
||||
* We extend the INIT_CMD_ range for the new initctl tool, Finit only
|
||||
* support changing runlevel, reloading the configuration and setenv
|
||||
* with the old-style /dev/initctl FIFO.
|
||||
*/
|
||||
#define INIT_SOCKET _PATH_VARRUN "finit.sock"
|
||||
#define INIT_MAGIC 0x03091969
|
||||
|
||||
#define INIT_CMD_START 0
|
||||
#define INIT_CMD_RUNLVL 1
|
||||
#define INIT_CMD_DEBUG 2 /* Toggle Finit debug */
|
||||
#define INIT_CMD_RELOAD 3 /* Reload *.conf in /etc/finit.d/ */
|
||||
#define INIT_CMD_START_SVC 4
|
||||
#define INIT_CMD_STOP_SVC 5
|
||||
#define INIT_CMD_RELOAD_SVC 6 /* SIGHUP service */
|
||||
#define INIT_CMD_RESTART_SVC 7 /* STOP + START service */
|
||||
#define INIT_CMD_QUERY_INETD 8
|
||||
#define INIT_CMD_EMIT 9
|
||||
#define INIT_CMD_POWERFAIL 2
|
||||
#define INIT_CMD_POWERFAILNOW 3
|
||||
#define INIT_CMD_POWEROK 4
|
||||
#define INIT_CMD_BSD 5
|
||||
#define INIT_CMD_SETENV 6
|
||||
#define INIT_CMD_UNSETENV 7
|
||||
|
||||
/* Finit extensions over std SysV */
|
||||
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
|
||||
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
|
||||
#define INIT_CMD_START_SVC 10
|
||||
#define INIT_CMD_STOP_SVC 11
|
||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
|
||||
#define INIT_CMD_QUERY_INETD 14
|
||||
#define INIT_CMD_EMIT 15
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
|
||||
#define INIT_CMD_NACK 254
|
||||
#define INIT_CMD_ACK 255
|
||||
|
||||
@@ -66,7 +81,8 @@ struct init_request {
|
||||
|
||||
extern int debug;
|
||||
extern int quiet;
|
||||
extern int verbose;
|
||||
extern int silent;
|
||||
extern int wdogpid;
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int prevlevel;
|
||||
@@ -77,13 +93,12 @@ extern char *hostname;
|
||||
extern char *username;
|
||||
extern char *runparts;
|
||||
extern char *console;
|
||||
extern char *__progname;
|
||||
extern uev_ctx_t *ctx;
|
||||
|
||||
#endif /* FINIT_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
/* Initialize and serve a login terminal
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#ifndef _PATH_LOGIN
|
||||
#define _PATH_LOGIN "/bin/login"
|
||||
#endif
|
||||
|
||||
#define print(s) (void)write(STDOUT_FILENO, s, strlen(s))
|
||||
|
||||
|
||||
/*
|
||||
* Read one character from stdin.
|
||||
*/
|
||||
static int readch(char *tty)
|
||||
{
|
||||
int st;
|
||||
char ch1;
|
||||
|
||||
st = read(STDIN_FILENO, &ch1, 1);
|
||||
if (st == 0) {
|
||||
print("\n");
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (st < 0)
|
||||
errx(1, "getty: %s: read error", tty);
|
||||
|
||||
return ch1 & 0xFF;
|
||||
}
|
||||
|
||||
static void stty(int fd, speed_t speed)
|
||||
{
|
||||
struct termios term;
|
||||
|
||||
tcdrain(fd);
|
||||
if (tcgetattr(fd, &term))
|
||||
return;
|
||||
|
||||
cfsetispeed(&term, speed);
|
||||
cfsetospeed(&term, speed);
|
||||
tcsetattr(fd, TCSAFLUSH, &term);
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
|
||||
/* Disable modem specific flags */
|
||||
term.c_cflag &= (0|CSTOPB|PARENB|PARODD);
|
||||
term.c_cflag &= ~CRTSCTS;
|
||||
term.c_cflag |= CLOCAL;
|
||||
|
||||
/* Timeouts, minimum chars and default flags */
|
||||
term.c_cc[VTIME] = 0;
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_iflag = ICRNL|IXON|IXOFF;
|
||||
term.c_oflag = OPOST|ONLCR;
|
||||
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
|
||||
term.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE;
|
||||
|
||||
/* Reset special characters to defaults */
|
||||
term.c_cc[VINTR] = CTRL('C');
|
||||
term.c_cc[VQUIT] = CTRL('\\');
|
||||
term.c_cc[VEOF] = CTRL('D');
|
||||
term.c_cc[VEOL] = '\n';
|
||||
term.c_cc[VKILL] = CTRL('U');
|
||||
term.c_cc[VERASE] = CERASE;
|
||||
tcsetattr(fd, TCSANOW, &term);
|
||||
|
||||
/* Clear screen in silent mode */
|
||||
if (silent)
|
||||
print("\033[r\033[H\033[J");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse and display a line from /etc/issue
|
||||
*/
|
||||
static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
{
|
||||
char *s, *s0;
|
||||
|
||||
s0 = line;
|
||||
for (s = line; *s != 0; s++) {
|
||||
if (*s == '\\') {
|
||||
(void)write(1, s0, s - s0);
|
||||
s0 = s + 2;
|
||||
switch (*++s) {
|
||||
case 'l':
|
||||
print(tty);
|
||||
break;
|
||||
case 'm':
|
||||
print(uts->machine);
|
||||
break;
|
||||
case 'n':
|
||||
print(uts->nodename);
|
||||
break;
|
||||
#ifdef _GNU_SOURCE
|
||||
case 'o':
|
||||
print(uts->domainname);
|
||||
break;
|
||||
#endif
|
||||
case 'r':
|
||||
print(uts->release);
|
||||
break;
|
||||
case 's':
|
||||
print(uts->sysname);
|
||||
break;
|
||||
case 'v':
|
||||
print(uts->version);
|
||||
break;
|
||||
case 0:
|
||||
goto leave;
|
||||
default:
|
||||
s0 = s - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
(void)write(1, s0, s - s0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse and display /etc/issue
|
||||
*/
|
||||
static void do_issue(char *tty)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n";
|
||||
struct utsname uts;
|
||||
|
||||
/*
|
||||
* Get data about this machine.
|
||||
*/
|
||||
uname(&uts);
|
||||
|
||||
print("\n");
|
||||
fp = fopen("/etc/issue", "r");
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
do_parse(buf, &uts, tty);
|
||||
|
||||
fclose(fp);
|
||||
} else {
|
||||
do_parse(buf, &uts, tty);
|
||||
}
|
||||
|
||||
do_parse("\\n login: ", &uts, tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the process of a GETTY.
|
||||
*/
|
||||
static void do_getty(char *tty, char *name, size_t len)
|
||||
{
|
||||
int ch;
|
||||
char *np;
|
||||
|
||||
/*
|
||||
* Clean up tty name.
|
||||
*/
|
||||
if (!strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)))
|
||||
tty += 5;
|
||||
|
||||
/*
|
||||
* Display prompt.
|
||||
*/
|
||||
ch = ' ';
|
||||
*name = '\0';
|
||||
while (ch != '\n') {
|
||||
do_issue(tty);
|
||||
|
||||
np = name;
|
||||
while ((ch = readch(tty)) != '\n') {
|
||||
if (np < name + len)
|
||||
*np++ = ch;
|
||||
}
|
||||
|
||||
*np = '\0';
|
||||
if (*name == '\0')
|
||||
ch = ' '; /* blank line typed! */
|
||||
}
|
||||
|
||||
name[len - 1] = 0;
|
||||
}
|
||||
|
||||
/* Execute the login(1) command with the current
|
||||
* username as its argument. It will reply to the
|
||||
* calling user by typing "Password: "...
|
||||
*/
|
||||
static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
* Failed to exec login, should be impossible. Try a starting a
|
||||
* fallback shell instead.
|
||||
*
|
||||
* Note: Add /etc/securetty handling.
|
||||
*/
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_BSHELL);
|
||||
if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode))
|
||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
||||
|
||||
return 1; /* We shouldn't get here ... */
|
||||
}
|
||||
|
||||
static speed_t do_parse_speed(char *baud)
|
||||
{
|
||||
char *ptr;
|
||||
size_t i;
|
||||
unsigned long val;
|
||||
struct { unsigned long val; speed_t speed; } v2s[] = {
|
||||
{ 0, B0 },
|
||||
{ 50, B50 },
|
||||
{ 75, B75 },
|
||||
{ 110, B110 },
|
||||
{ 134, B134 },
|
||||
{ 150, B150 },
|
||||
{ 200, B200 },
|
||||
{ 300, B300 },
|
||||
{ 600, B600 },
|
||||
{ 1200, B1200 },
|
||||
{ 1800, B1800 },
|
||||
{ 2400, B2400 },
|
||||
{ 4800, B4800 },
|
||||
{ 9600, B9600 },
|
||||
{ 19200, B19200 },
|
||||
{ 38400, B38400 },
|
||||
{ 57600, B57600 },
|
||||
{ 115200, B115200 },
|
||||
{ 230400, B230400 },
|
||||
{ 460800, B460800 },
|
||||
{ 500000, B500000 },
|
||||
{ 576000, B576000 },
|
||||
{ 921600, B921600 },
|
||||
{ 1000000, B1000000 },
|
||||
{ 1152000, B1152000 },
|
||||
{ 1500000, B1500000 },
|
||||
{ 2000000, B2000000 },
|
||||
{ 2500000, B2500000 },
|
||||
{ 3000000, B3000000 },
|
||||
{ 3500000, B3500000 },
|
||||
{ 4000000, B4000000 },
|
||||
};
|
||||
|
||||
errno = 0;
|
||||
val = strtoul(baud, &ptr, 10);
|
||||
if (errno || ptr == baud)
|
||||
return B0;
|
||||
|
||||
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
|
||||
if (v2s[i].val == val)
|
||||
return v2s[i].speed;
|
||||
}
|
||||
|
||||
return B0;
|
||||
}
|
||||
|
||||
int getty(char *tty, char *baud, char *UNUSED(term), char *user)
|
||||
{
|
||||
int fd;
|
||||
char name[30];
|
||||
speed_t speed = B38400;
|
||||
struct sigaction sa;
|
||||
|
||||
if (baud) {
|
||||
speed = do_parse_speed(baud);
|
||||
if (speed == B0) {
|
||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
|
||||
fd = open(tty, O_RDWR);
|
||||
if (fd < 0)
|
||||
err(1, "Failed opening %s", tty);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
warn("Failed TIOCSCTTY");
|
||||
|
||||
/*
|
||||
* Don't let QUIT dump core.
|
||||
*/
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = _exit;
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
utmp_set_login(tty, NULL);
|
||||
|
||||
if (!user)
|
||||
do_getty(tty, name, sizeof(name));
|
||||
else
|
||||
strlcpy(name, user, sizeof(name));
|
||||
|
||||
return do_login(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -22,57 +22,45 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <ctype.h> /* isblank() */
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <limits.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <utmp.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/conio.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "libite/lite.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
static int encode(int lvl)
|
||||
int _slup = 0; /* INTERNAL: Is syslog up yet? */
|
||||
|
||||
char *strip_line(char *line)
|
||||
{
|
||||
if (!lvl) return 0;
|
||||
return lvl + '0';
|
||||
char *ptr;
|
||||
|
||||
/* Trim leading whitespace */
|
||||
while (*line && isblank(*line))
|
||||
line++;
|
||||
|
||||
/* Strip any comment at end of line */
|
||||
ptr = line;
|
||||
while (*ptr && *ptr != '#')
|
||||
ptr++;
|
||||
*ptr = 0;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void runlevel_set(int pre, int now)
|
||||
{
|
||||
struct utmp utent;
|
||||
|
||||
utent.ut_type = RUN_LVL;
|
||||
utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF);
|
||||
strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user));
|
||||
|
||||
setutent();
|
||||
pututline(&utent);
|
||||
endutent();
|
||||
}
|
||||
|
||||
int runlevel_get(void)
|
||||
{
|
||||
int lvl = '?'; /* Non-existing runlevel */
|
||||
struct utmp *utent;
|
||||
|
||||
setutent();
|
||||
while ((utent = getutent())) {
|
||||
if (utent->ut_type == RUN_LVL) {
|
||||
lvl = utent->ut_pid & 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
endutent();
|
||||
|
||||
return lvl - '0';
|
||||
utmp_set_runlevel(pre, now);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,9 +127,13 @@ void print(int action, const char *fmt, ...)
|
||||
va_list ap;
|
||||
const char success[] = " \e[1m[ OK ]\e[0m\n";
|
||||
const char failure[] = " \e[7m[FAIL]\e[0m\n";
|
||||
const char warning[] = " \e[7m[WARN]\e[0m\n";
|
||||
const char pending[] = " \e[1m[ \\/ ]\e[0m\n";
|
||||
const char dots[] = " .....................................................................";
|
||||
|
||||
if (silent)
|
||||
return;
|
||||
|
||||
if (fmt) {
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
@@ -167,6 +159,10 @@ void print(int action, const char *fmt, ...)
|
||||
write(STDERR_FILENO, failure, sizeof(failure));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
write(STDERR_FILENO, warning, sizeof(warning));
|
||||
break;
|
||||
|
||||
default:
|
||||
write(STDERR_FILENO, pending, sizeof(pending));
|
||||
break;
|
||||
@@ -184,9 +180,48 @@ int print_result(int fail)
|
||||
return fail;
|
||||
}
|
||||
|
||||
int getuser(char *username)
|
||||
/*
|
||||
* Log to /dev/kmsg until syslogd has started, then openlog()
|
||||
* and continue logging as a regular daemon.
|
||||
*/
|
||||
void logit(int prio, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (!_slup && !fexist("/dev/log")) {
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/dev/kmsg", "w");
|
||||
if (fp) {
|
||||
if (debug)
|
||||
prio = LOG_ERR;
|
||||
|
||||
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
|
||||
vfprintf(fp, fmt, ap);
|
||||
fclose(fp);
|
||||
} else {
|
||||
fprintf(stderr, "Failed opening /dev/kmsg for appending ...\n");
|
||||
if (debug || prio <= LOG_ERR)
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_slup) {
|
||||
openlog("finit", LOG_PID, LOG_DAEMON);
|
||||
_slup = 1;
|
||||
}
|
||||
|
||||
vsyslog(prio, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int getuser(char *username, char **home)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
*home = "/"; /* XXX: Fixme */
|
||||
return fgetint("/etc/passwd", "x:\n", username);
|
||||
#else
|
||||
struct passwd *usr;
|
||||
@@ -194,7 +229,8 @@ int getuser(char *username)
|
||||
if (!username || (usr = getpwnam(username)) == NULL)
|
||||
return -1;
|
||||
|
||||
return usr->pw_uid;
|
||||
*home = usr->pw_dir;
|
||||
return usr->pw_uid;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -223,7 +259,6 @@ void set_hostname(char **hostname)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
_d("Set hostname: %s", *hostname);
|
||||
fp = fopen("/etc/hostname", "r");
|
||||
if (fp) {
|
||||
struct stat st;
|
||||
@@ -248,9 +283,50 @@ void set_hostname(char **hostname)
|
||||
sethostname(*hostname, strlen(*hostname));
|
||||
}
|
||||
|
||||
#ifndef HAVE_GETFSENT
|
||||
static lfile_t *fstab = NULL;
|
||||
|
||||
int setfsent(void)
|
||||
{
|
||||
if (fstab)
|
||||
lfclose(fstab);
|
||||
|
||||
fstab = lfopen("/etc/fstab", " \t\n");
|
||||
if (!fstab)
|
||||
return 0;
|
||||
|
||||
return 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 */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -25,44 +25,30 @@
|
||||
#ifndef FINIT_HELPERS_H_
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* strerror() */
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define DO_LOG(level, fmt, args...) \
|
||||
do { \
|
||||
openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \
|
||||
syslog(level, fmt, ##args); \
|
||||
closelog(); \
|
||||
} while (0)
|
||||
/*
|
||||
* Error and debug messages
|
||||
*
|
||||
* Use of logit() is preferred, but these are guaranteed to display on
|
||||
* /dev/console as well. Useful for critical errors and early debug.
|
||||
*/
|
||||
#define _l(prio, fmt, args...) do { if (_slup) warnx(fmt, ##args); logit(prio, fmt "\n", ##args); } while (0)
|
||||
#define _m(prio, fmt, args...) do { _l(prio, "%s() - " fmt, __func__, ##args); } while (0)
|
||||
#define _d(fmt, args...) do { if (debug) { _m(LOG_DEBUG, fmt, ##args); } } while (0)
|
||||
#define _w(fmt, args...) do { _m(LOG_WARNING, fmt, ##args); } while (0)
|
||||
#define _e(fmt, args...) do { _m(LOG_ERR, fmt, ##args); } while (0)
|
||||
#define _pe(fmt, args...) do { _m(LOG_ERR, fmt ". Error %d: %s", ##args, errno, strerror(errno)); } while (0)
|
||||
|
||||
#define FLOG_DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args)
|
||||
#define FLOG_INFO(fmt, args...) DO_LOG(LOG_INFO, fmt, ##args)
|
||||
#define FLOG_WARN(fmt, args...) DO_LOG(LOG_WARNING, fmt, ##args)
|
||||
#define FLOG_ERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt, ##args)
|
||||
#define FLOG_PERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt ". Error %d: %s", ##args, errno, strerror(errno))
|
||||
extern int _slup; /* INTERNAL: Is syslog up yet? */
|
||||
|
||||
|
||||
/* Esc[2JEsc[1;1H Clear screen and move cursor to 1,1 (upper left) pos. */
|
||||
#define clrscr() fputs("\e[2J\e[1;1H", stderr)
|
||||
/* Esc[K Erases from the current cursor position to the end of the current line. */
|
||||
#define clreol() fputs("\e[K", stderr)
|
||||
/* Esc[2K Erases the entire current line. */
|
||||
#define delline() fputs("\e[2K", stderr)
|
||||
/* Esc[Line;ColumnH Moves the cursor to the specified position (coordinates) */
|
||||
#define gotoxy(x,y) fprintf(stderr, "\e[%d;%dH", y, x)
|
||||
/* Esc[?25l (lower case L) Hide Cursor */
|
||||
#define hidecursor() fputs("\e[?25l", stderr)
|
||||
/* Esc[?25h (lower case H) Show Cursor */
|
||||
#define showcursor() fputs("\e[?25h", stderr)
|
||||
|
||||
#define echo(fmt, args...) do { fprintf(stderr, fmt "\n", ##args); } while (0)
|
||||
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s:%s() - " fmt "\n", __FILE__, __func__, ##args); } } while (0)
|
||||
#define _e(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt "\n", __FILE__, __func__, ##args); } while (0)
|
||||
#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt ". Error %d: %s\n", __FILE__, __func__, ##args, errno, strerror(errno)); } while (0)
|
||||
char *strip_line (char *line);
|
||||
|
||||
void runlevel_set (int pre, int now);
|
||||
int runlevel_get (void);
|
||||
char *runlevel_string (int levels);
|
||||
|
||||
int pid_alive (pid_t pid);
|
||||
@@ -73,23 +59,41 @@ void procname_set (char *name, char *args[]);
|
||||
void print (int action, const char *fmt, ...);
|
||||
void print_desc (char *action, char *desc);
|
||||
int print_result (int fail);
|
||||
|
||||
void logit (int prio, const char *fmt, ...);
|
||||
|
||||
int start_process (char *cmd, char *args[], int console);
|
||||
void do_sleep (unsigned int sec);
|
||||
int getuser (char *username);
|
||||
int getuser (char *username, char **home);
|
||||
int getgroup (char *group);
|
||||
void set_hostname (char **hostname);
|
||||
|
||||
int complete (char *cmd, int pid);
|
||||
int run (char *cmd);
|
||||
int run_interactive (char *cmd, char *fmt, ...);
|
||||
pid_t run_getty (char *cmd, char *args[], int console);
|
||||
pid_t run_getty (char *tty, char *speed, char *term, int console);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
|
||||
#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 */
|
||||
|
||||
#endif /* FINIT_HELPERS_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
_______ __ _____ ___ __ ___________ _______
|
||||
/" "||" \ (\" \|" \ |" \(" _ ")/" __ )
|
||||
(: ______)|| | |.\\ \ | || |)__/ \\__/(__/ _) ./
|
||||
\/ | |: | |: \. \\ | |: | \\_ / / //
|
||||
// ___) |. | |. \ \. | |. | |. | __ \_ \\
|
||||
(: ( /\ |\| \ \ | /\ |\ \: | (: \__) :\
|
||||
\__/ (__\_|_)\___|\____\)(__\_|_) \__| \_______)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -25,9 +25,8 @@
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "libuev/uev.h"
|
||||
#include "libite/lite.h"
|
||||
#include <uev/uev.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "inetd.h"
|
||||
@@ -35,83 +34,16 @@
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
|
||||
#define ENABLE_SOCKOPT(sd, level, opt) \
|
||||
do { \
|
||||
int val = 1; \
|
||||
if (setsockopt(sd, level, opt, &val, sizeof(val)) < 0) \
|
||||
FLOG_PERROR("Failed setting %s on %s service.", \
|
||||
#opt, inetd->name); \
|
||||
#define ENABLE_SOCKOPT(sd, level, opt) \
|
||||
do { \
|
||||
int val = 1; \
|
||||
if (setsockopt(sd, level, opt, &val, sizeof(val)) < 0) \
|
||||
logit(LOG_CRIT, "Failed enabling %s on %s service", \
|
||||
#opt, inetd->name); \
|
||||
} while (0);
|
||||
|
||||
/* Socket callback, looks up correct svc and starts it as an inetd service */
|
||||
static void socket_cb(uev_t *w, void *arg, int UNUSED(events))
|
||||
{
|
||||
svc_t *svc = (svc_t *)arg;
|
||||
|
||||
if (SVC_START != service_enabled(svc, -1, NULL))
|
||||
return;
|
||||
|
||||
if (!svc->inetd.forking)
|
||||
uev_io_stop(w);
|
||||
|
||||
service_start(svc);
|
||||
}
|
||||
|
||||
/* Launch Inet socket for service.
|
||||
* TODO: Add filtering ALLOW/DENY per interface.
|
||||
*/
|
||||
static void spawn_socket(inetd_t *inetd)
|
||||
{
|
||||
int sd;
|
||||
socklen_t len = sizeof(struct sockaddr);
|
||||
struct sockaddr_in s;
|
||||
|
||||
if (!inetd->type) {
|
||||
FLOG_ERROR("Skipping invalid inetd service %s", inetd->name);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("Spawning server socket for inetd %s ...", inetd->name);
|
||||
sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto);
|
||||
if (-1 == sd) {
|
||||
FLOG_PERROR("Failed opening inetd socket type %d proto %d", inetd->type, inetd->proto);
|
||||
return;
|
||||
}
|
||||
|
||||
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEADDR);
|
||||
#ifdef SO_REUSEPORT
|
||||
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEPORT);
|
||||
#endif
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sin_family = AF_INET;
|
||||
s.sin_addr.s_addr = INADDR_ANY;
|
||||
s.sin_port = htons(inetd->port);
|
||||
if (bind(sd, (struct sockaddr *)&s, len) < 0) {
|
||||
FLOG_PERROR("Failed binding to port %d, maybe another %s server is already running",
|
||||
inetd->port, inetd->name);
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inetd->port) {
|
||||
if (inetd->type == SOCK_STREAM) {
|
||||
if (-1 == listen(sd, 20)) {
|
||||
FLOG_PERROR("Failed listening to inetd service %s", inetd->name);
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
} else { /* SOCK_DGRAM */
|
||||
/* Set extra sockopt to get ifindex from inbound packets */
|
||||
ENABLE_SOCKOPT(sd, SOL_IP, IP_PKTINFO);
|
||||
}
|
||||
}
|
||||
|
||||
uev_io_init(ctx, &inetd->watcher, socket_cb, inetd->arg, sd, UEV_READ);
|
||||
}
|
||||
|
||||
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||
int inetd_dgram_peek(int sd, char *ifname)
|
||||
static int inetd_dgram_peek(int sd, char *ifname)
|
||||
{
|
||||
char cmbuf[0x100];
|
||||
struct msghdr msgh;
|
||||
@@ -139,7 +71,7 @@ int inetd_dgram_peek(int sd, char *ifname)
|
||||
}
|
||||
|
||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||
int inetd_stream_peek(int sd, char *ifname)
|
||||
static int inetd_stream_peek(int sd, char *ifname)
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
struct sockaddr_in sin;
|
||||
@@ -173,51 +105,224 @@ int inetd_stream_peek(int sd, char *ifname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inetd monitor, called by service_monitor() */
|
||||
int inetd_respawn(pid_t pid)
|
||||
static int get_stdin(svc_t *svc)
|
||||
{
|
||||
svc_t *svc = svc_find_by_pid(pid);
|
||||
int stdin = svc->inetd.watcher.fd;
|
||||
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
||||
|
||||
if (svc_is_inetd(svc)) {
|
||||
inetd_t *inetd = &svc->inetd;
|
||||
if (svc->inetd.type == SOCK_STREAM) {
|
||||
/* Open new client socket from server socket */
|
||||
stdin = accept(stdin, NULL, NULL);
|
||||
if (stdin < 0) {
|
||||
logit(LOG_CRIT, "Failed accepting inetd service %d/tcp", svc->inetd.port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
svc->pid = 0;
|
||||
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
|
||||
|
||||
if (svc_in_runlevel(svc, runlevel) && !inetd->forking)
|
||||
uev_io_set(&inetd->watcher, inetd->watcher.fd, UEV_READ);
|
||||
|
||||
return 1; /* It was us! */
|
||||
inetd_stream_peek(stdin, ifname);
|
||||
} else { /* SOCK_DGRAM */
|
||||
inetd_dgram_peek(stdin, ifname);
|
||||
}
|
||||
|
||||
return 0; /* Not an inetd service */
|
||||
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
||||
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
|
||||
if (svc->inetd.type == SOCK_STREAM)
|
||||
close(stdin);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return stdin;
|
||||
}
|
||||
|
||||
|
||||
void inetd_start(inetd_t *inetd)
|
||||
/* Socket callback, looks up correct svc and starts it as an inetd service */
|
||||
static void socket_cb(uev_t *UNUSED(w), void *arg, int UNUSED(events))
|
||||
{
|
||||
if (inetd->watcher.fd == -1)
|
||||
spawn_socket(inetd);
|
||||
svc_t *svc = (svc_t *)arg, *task;
|
||||
int stdin;
|
||||
|
||||
_d("Got event on %s socket ...", svc->cmd);
|
||||
stdin = get_stdin(svc);
|
||||
if (stdin < 0) {
|
||||
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure to disable O_NONBLOCK on the descriptor before
|
||||
* passing it to the inetd service, that's what is expected.
|
||||
*/
|
||||
if (fcntl(stdin, F_SETFL, fcntl(stdin, F_GETFL, 0) & ~O_NONBLOCK) < 0) {
|
||||
logit(LOG_CRIT, "Failed disabling non-blocking on %s socket", svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
task = svc_new(svc->cmd, svc->inetd.next_id++, SVC_TYPE_INETD_CONN);
|
||||
if (!task) {
|
||||
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client",
|
||||
svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!svc->inetd.forking) {
|
||||
svc_busy(svc);
|
||||
service_step(svc);
|
||||
}
|
||||
|
||||
/* Copy inherited attributes from inetd service's svc */
|
||||
task->runlevels = svc->runlevels;
|
||||
|
||||
/* Only copy the most relevant parts of inetd, in particular we
|
||||
* must *not* copy the watcher data to the clone! */
|
||||
task->inetd.svc = svc;
|
||||
task->inetd.cmd = svc->inetd.cmd;
|
||||
task->inetd.type = svc->inetd.type;
|
||||
|
||||
memcpy(task->cond, svc->cond, sizeof(task->cond));
|
||||
memcpy(task->username, svc->username, sizeof(task->username));
|
||||
memcpy(task->group, svc->group, sizeof(task->group));
|
||||
memcpy(task->args, svc->args, sizeof(task->args));
|
||||
snprintf(task->desc, sizeof(task->desc), "%s connection", svc->desc);
|
||||
|
||||
task->stdin_fd = stdin;
|
||||
service_step(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Refuse service if the request specifies a reply port corresponding to any internal service.
|
||||
* This is done as a defense against looping attacks; the remote IP address is logged.
|
||||
* http://www.freebsd.org/cgi/man.cgi?inetd(8)
|
||||
* https://svnweb.freebsd.org/base/head/usr.sbin/inetd/inetd.c?revision=298909&view=markup#l2094
|
||||
*/
|
||||
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
|
||||
{
|
||||
svc_t *svc;
|
||||
char pname[NI_MAXHOST];
|
||||
|
||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||
inetd_t *i = &svc->inetd;
|
||||
|
||||
if (!i->builtin || i->type != SOCK_DGRAM)
|
||||
continue;
|
||||
|
||||
if (((const struct sockaddr_in *)sa)->sin_port == i->port) {
|
||||
getnameinfo(sa, len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST);
|
||||
logit(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", i->name, "UDP", name, "UDP", pname);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Launch Inet socket for service.
|
||||
* TODO: Add filtering ALLOW/DENY per interface.
|
||||
*/
|
||||
static int spawn_socket(inetd_t *inetd)
|
||||
{
|
||||
int sd;
|
||||
socklen_t len = sizeof(struct sockaddr);
|
||||
struct sockaddr_in s;
|
||||
|
||||
if (!inetd->type) {
|
||||
logit(LOG_CRIT, "Invalid inetd service %s, skipping ...", inetd->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
_d("Spawning server socket for inetd %s, type %s ...", inetd->name, inetd->type == SOCK_STREAM ? "stream" : "dgram");
|
||||
sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto);
|
||||
if (-1 == sd) {
|
||||
logit(LOG_CRIT, "Failed opening inetd socket type %d proto %d", inetd->type, inetd->proto);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEADDR);
|
||||
#ifdef SO_REUSEPORT
|
||||
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEPORT);
|
||||
#endif
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sin_family = AF_INET;
|
||||
s.sin_addr.s_addr = INADDR_ANY;
|
||||
s.sin_port = htons(inetd->port);
|
||||
if (bind(sd, (struct sockaddr *)&s, len) < 0) {
|
||||
logit(LOG_CRIT, "Failed binding to port %d, maybe another %s server is already running?",
|
||||
inetd->port, inetd->name);
|
||||
close(sd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (inetd->port) {
|
||||
if (inetd->type == SOCK_STREAM) {
|
||||
if (-1 == listen(sd, 10)) {
|
||||
logit(LOG_CRIT, "Failed listening to inetd service %s", inetd->name);
|
||||
close(sd);
|
||||
return -errno;
|
||||
}
|
||||
} else { /* SOCK_DGRAM */
|
||||
/* Set extra sockopt to get ifindex from inbound packets */
|
||||
ENABLE_SOCKOPT(sd, SOL_IP, IP_PKTINFO);
|
||||
}
|
||||
}
|
||||
|
||||
if (uev_io_init(ctx, &inetd->watcher, socket_cb, inetd->svc, sd, UEV_READ)) {
|
||||
logit(LOG_CRIT, "Failed setting up inetd watcher for %s", inetd->name);
|
||||
close(sd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_start(inetd_t *inetd)
|
||||
{
|
||||
int sd;
|
||||
char buf[BUFSIZ];
|
||||
ssize_t len;
|
||||
|
||||
sd = inetd->watcher.fd;
|
||||
if (sd == -1)
|
||||
return spawn_socket(inetd);
|
||||
|
||||
/* Read anything lingering, or clean up socket after failure */
|
||||
len = recv(sd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (len > 0)
|
||||
_d("Read %zd lingering bytes from socket before (re)starting %s ...", len, inetd->svc->cmd);
|
||||
|
||||
/* Restore O_NONBLOCK for socket */
|
||||
if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK)) {
|
||||
logit(LOG_CRIT, "Cannot safely (re)start %s inetd service", inetd->svc->cmd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
_d("Re-starting %s socket watcher ...", inetd->svc->cmd);
|
||||
uev_io_start(&inetd->watcher);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inetd_stop(inetd_t *inetd)
|
||||
{
|
||||
svc_t *svc = (svc_t *)inetd->arg;
|
||||
|
||||
if (inetd->watcher.fd != -1) {
|
||||
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
|
||||
uev_io_stop(&inetd->watcher);
|
||||
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
||||
close(inetd->watcher.fd);
|
||||
inetd->watcher.fd = -1;
|
||||
}
|
||||
|
||||
/* Stop any running service, not allowed anymore. */
|
||||
if (svc->pid)
|
||||
service_stop(svc, SVC_WAITING_STATE);
|
||||
/* For dgram inetd services we block the parent SVC
|
||||
* and halt the watcher, so don't close the socket! */
|
||||
if (!svc_is_busy(inetd->svc)) {
|
||||
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
|
||||
if (inetd->type == SOCK_STREAM)
|
||||
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
||||
close(inetd->watcher.fd);
|
||||
inetd->watcher.fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
||||
static struct servent *fallback_service(char *service, char *proto)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
int service_num;
|
||||
static struct servent lfs;
|
||||
|
||||
@@ -228,11 +333,65 @@ static int getent(char *service, char *proto, struct servent **sv, struct protoe
|
||||
lfs.s_proto = NULL;
|
||||
if (!strcmp("tcp", proto) || !strcmp("udp", proto))
|
||||
lfs.s_proto = proto;
|
||||
*sv = &lfs;
|
||||
|
||||
return &lfs;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct servent *getent_service(char *service, char *proto)
|
||||
{
|
||||
struct servent *ent;
|
||||
|
||||
#ifdef ENABLE_STATIC
|
||||
ent = fallback_service(service, proto);
|
||||
#else
|
||||
*sv = getservbyname(service, proto);
|
||||
ent = getservbyname(service, proto);
|
||||
if (!ent)
|
||||
ent = fallback_service(service, proto);
|
||||
#endif
|
||||
return ent;
|
||||
}
|
||||
|
||||
static struct protoent *fallback_proto(char *proto)
|
||||
{
|
||||
int proto_num;
|
||||
static struct protoent lfp;
|
||||
|
||||
proto_num = fgetint("/etc/protocols", " \n\t", proto);
|
||||
if (proto_num > 0) {
|
||||
lfp.p_name = proto;
|
||||
lfp.p_proto = proto_num;
|
||||
|
||||
return &lfp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct protoent *getent_proto(char *proto)
|
||||
{
|
||||
struct protoent *ent;
|
||||
|
||||
#ifdef ENABLE_STATIC
|
||||
ent = fallback_proto(proto);
|
||||
#else
|
||||
ent = getprotobyname(proto);
|
||||
if (!ent)
|
||||
ent = fallback_proto(proto);
|
||||
#endif
|
||||
return ent;
|
||||
}
|
||||
|
||||
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
||||
{
|
||||
if (!fexist("/etc/services") || !fexist("/etc/protocols")) {
|
||||
_w("Cannot register inetd %s/%s, system missing /etc/services or /etc/protocols", service, proto);
|
||||
return errno = ECANCELED;
|
||||
}
|
||||
|
||||
*sv = getent_service(service, proto);
|
||||
if (!*sv) {
|
||||
const char *errstr;
|
||||
static struct servent s;
|
||||
@@ -244,31 +403,19 @@ static int getent(char *service, char *proto, struct servent **sv, struct protoe
|
||||
s.s_proto = proto;
|
||||
|
||||
if (errstr || !s.s_proto) {
|
||||
_e("Invalid/unknown inetd service, cannot create custom entry.");
|
||||
_e("Invalid/unknown inetd service, cannot create custom entry");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
_d("Creating cutom inetd service %s/%s.", service, proto);
|
||||
_d("Creating cutom inetd service %s/%s", service, proto);
|
||||
s.s_port = htons(s.s_port);
|
||||
*sv = &s;
|
||||
}
|
||||
|
||||
if (pv && (*sv)->s_proto) {
|
||||
#ifdef ENABLE_STATIC
|
||||
int proto_num;
|
||||
static struct protoent lfp;
|
||||
|
||||
proto_num = fgetint("/etc/protocols", " \n\t", (*sv)->s_proto);
|
||||
if (proto_num > 0) {
|
||||
lfp.p_name = proto;
|
||||
lfp.p_proto = proto_num;
|
||||
}
|
||||
*pv = &lfp;
|
||||
#else
|
||||
*pv = getprotobyname((*sv)->s_proto);
|
||||
#endif
|
||||
*pv = getent_proto((*sv)->s_proto);
|
||||
if (!*pv) {
|
||||
_e("Cannot find proto %s, skipping.", (*sv)->s_proto);
|
||||
_e("Cannot find proto %s, skipping ...", (*sv)->s_proto);
|
||||
return errno = EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -318,6 +465,18 @@ inetd_filter_t *inetd_filter_match(inetd_t *inetd, char *ifname)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int inetd_flush(inetd_t *inetd)
|
||||
{
|
||||
inetd_filter_t *filter, *next;
|
||||
|
||||
TAILQ_FOREACH_SAFE(filter, &inetd->filters, link, next) {
|
||||
TAILQ_REMOVE(&inetd->filters, filter, link);
|
||||
free(filter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Poor man's tcpwrappers filtering */
|
||||
int inetd_allow(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
@@ -331,7 +490,7 @@ int inetd_allow(inetd_t *inetd, char *ifname)
|
||||
|
||||
filter = inetd_filter_match(inetd, ifname);
|
||||
if (filter) {
|
||||
_d("Filter %s for inetd %s already exists, skipping.", ifname, inetd->name);
|
||||
_d("Filter %s for inetd %s already exists, skipping ...", ifname, inetd->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -361,7 +520,7 @@ int inetd_deny(inetd_t *inetd, char *ifname)
|
||||
|
||||
filter = find_filter(inetd, ifname);
|
||||
if (filter) {
|
||||
_d("%s filter %s for inetd %s already exists, cannot set deny filter for same, skipping.",
|
||||
_d("%s filter %s for inetd %s already exists, cannot set deny filter for same, skipping ...",
|
||||
filter->deny ? "Deny" : "Allow", ifname, inetd->name);
|
||||
return 1;
|
||||
}
|
||||
@@ -476,6 +635,23 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
svc_t *inetd_find_svc(char *path, char *service, char *proto)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
|
||||
continue;
|
||||
|
||||
if (inetd_match(&svc->inetd, service, proto)) {
|
||||
_d("Found a matching inetd svc for %s %s %s", path, service, proto);
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called to add a new, unique, inetd service. When an
|
||||
* ifname is given as argument this means *only* run service on this
|
||||
@@ -497,7 +673,7 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
|
||||
* If equivalent service exists already service_register() will instead call
|
||||
* inetd_allow().
|
||||
*/
|
||||
int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forking, void *arg)
|
||||
int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc)
|
||||
{
|
||||
int result;
|
||||
struct servent *sv = NULL;
|
||||
@@ -515,6 +691,7 @@ int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forkin
|
||||
inetd->port = ntohs(sv->s_port);
|
||||
inetd->proto = pv->p_proto;
|
||||
inetd->forking = !!forking;
|
||||
inetd->next_id = 2;
|
||||
if (!name)
|
||||
name = service;
|
||||
strlcpy(inetd->name, name, sizeof(inetd->name));
|
||||
@@ -526,11 +703,16 @@ int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forkin
|
||||
else
|
||||
inetd->type = SOCK_DGRAM;
|
||||
|
||||
if (inetd->type == SOCK_DGRAM && inetd->forking) {
|
||||
logit(LOG_WARNING, "%s: 'nowait' is not applicable on UDP services, ignoring", svc->cmd);
|
||||
inetd->forking = 0;
|
||||
}
|
||||
|
||||
/* Reset descriptor, used internally */
|
||||
inetd->watcher.fd = -1;
|
||||
|
||||
/* Setup socket callback argument */
|
||||
inetd->arg = arg;
|
||||
inetd->svc = svc;
|
||||
|
||||
_d("New service %s (default port %d proto %s:%d)", name, inetd->port, sv->s_proto, pv->p_proto);
|
||||
|
||||
@@ -539,17 +721,14 @@ int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forkin
|
||||
|
||||
int inetd_del(inetd_t *inetd)
|
||||
{
|
||||
inetd_filter_t *filter, *tmp;
|
||||
svc_unblock(inetd->svc);
|
||||
inetd_stop(inetd);
|
||||
|
||||
TAILQ_FOREACH_SAFE(filter, &inetd->filters, link, tmp)
|
||||
free(filter);
|
||||
|
||||
return 0;
|
||||
return inetd_flush(inetd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#include <uev/uev.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "queue.h"
|
||||
#include "libuev/uev.h"
|
||||
|
||||
typedef struct svc svc_t;
|
||||
|
||||
typedef struct inetd_filter {
|
||||
TAILQ_ENTRY(inetd_filter) link;
|
||||
@@ -39,42 +39,43 @@ typedef struct inetd_filter {
|
||||
|
||||
typedef struct {
|
||||
uev_t watcher;
|
||||
void *arg; /* svc_t pointer for the socket callback */
|
||||
svc_t *svc; /* svc_t pointer for the socket callback */
|
||||
|
||||
int type; /* Socket type: SOCK_STREAM/SOCK_DGRAM */
|
||||
int std; /* Standard proto/port from /etc/services */
|
||||
int proto;
|
||||
int port;
|
||||
int forking;
|
||||
int builtin; /* Set by built-in inetd services only */
|
||||
int next_id; /* Next child job's id */
|
||||
char name[10];
|
||||
int (*cmd)(int type); /* internal inetd service, like 'time' */
|
||||
|
||||
TAILQ_HEAD(, inetd_filter) filters;
|
||||
} inetd_t;
|
||||
|
||||
int inetd_dgram_peek (int sd, char *ifname);
|
||||
int inetd_stream_peek (int sd, char *ifname);
|
||||
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name);
|
||||
|
||||
void inetd_start (inetd_t *inetd);
|
||||
void inetd_stop (inetd_t *inetd);
|
||||
int inetd_start (inetd_t *inetd);
|
||||
void inetd_stop (inetd_t *inetd);
|
||||
|
||||
int inetd_respawn (pid_t pid);
|
||||
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
|
||||
int inetd_del (inetd_t *inetd);
|
||||
|
||||
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, void *arg);
|
||||
int inetd_del (inetd_t *inetd);
|
||||
svc_t *inetd_find_svc (char *path, char *service, char *proto);
|
||||
|
||||
int inetd_match (inetd_t *inetd, char *service, char *proto);
|
||||
int inetd_filter_str (inetd_t *inetd, char *str, size_t len);
|
||||
int inetd_match (inetd_t *inetd, char *service, char *proto);
|
||||
int inetd_filter_str(inetd_t *inetd, char *str, size_t len);
|
||||
|
||||
int inetd_allow (inetd_t *inetd, char *ifname);
|
||||
int inetd_deny (inetd_t *inetd, char *ifname);
|
||||
int inetd_is_allowed (inetd_t *inetd, char *ifname);
|
||||
int inetd_flush (inetd_t *inetd);
|
||||
int inetd_allow (inetd_t *inetd, char *ifname);
|
||||
int inetd_deny (inetd_t *inetd, char *ifname);
|
||||
int inetd_is_allowed(inetd_t *inetd, char *ifname);
|
||||
|
||||
#endif /* FINIT_INETD_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* New client tool, complements old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2015 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-2016 Joachim Nilsson <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,27 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <glob.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "helpers.h"
|
||||
#include "service.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#include "libite/lite.h"
|
||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||
|
||||
typedef struct {
|
||||
char *cmd;
|
||||
@@ -40,6 +50,7 @@ typedef struct {
|
||||
|
||||
int debug = 0;
|
||||
int verbose = 0;
|
||||
int silent = 1; /* For helpers.c */
|
||||
int runlevel = 0;
|
||||
|
||||
static int do_send(struct init_request *rq, ssize_t len)
|
||||
@@ -72,6 +83,42 @@ exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int runlevel_get(void)
|
||||
{
|
||||
int sd, result = 255;
|
||||
struct init_request rq;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
rq.cmd = INIT_CMD_GET_RUNLEVEL;
|
||||
rq.magic = INIT_MAGIC;
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
goto error;
|
||||
|
||||
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
|
||||
if (read(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
|
||||
result = rq.runlevel;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
result = -1;
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int toggle_debug(char *UNUSED(arg))
|
||||
{
|
||||
struct init_request rq = {
|
||||
@@ -107,45 +154,195 @@ static int do_svc(int cmd, char *arg)
|
||||
.cmd = cmd,
|
||||
};
|
||||
|
||||
if (!arg || !arg[0]) {
|
||||
if (cmd == INIT_CMD_RELOAD_SVC) {
|
||||
rq.cmd = INIT_CMD_RELOAD;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||
|
||||
exit:
|
||||
return do_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
|
||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
|
||||
static int do_start (char *arg) { return do_svc(INIT_CMD_START_SVC, arg); }
|
||||
static int do_stop (char *arg) { return do_svc(INIT_CMD_STOP_SVC, arg); }
|
||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD_SVC, arg); }
|
||||
static int do_restart(char *arg) { return do_svc(INIT_CMD_RESTART_SVC, arg); }
|
||||
|
||||
static void show_cond_one(const char *_conds)
|
||||
{
|
||||
static char conds[MAX_ARG_LEN];
|
||||
char *cond;
|
||||
|
||||
strlcpy(conds, _conds, sizeof(conds));
|
||||
|
||||
putchar('<');
|
||||
|
||||
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
||||
if (cond != conds)
|
||||
putchar(',');
|
||||
|
||||
switch (cond_get(cond)) {
|
||||
case COND_ON:
|
||||
printf("+%s", cond);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
printf("\e[1m~%s\e[0m", cond);
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
printf("\e[1m-%s\e[0m", cond);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
putchar('>');
|
||||
}
|
||||
|
||||
static int do_cond_magic(char op, char *cond)
|
||||
{
|
||||
cond[0] = op;
|
||||
return do_emit(cond);
|
||||
}
|
||||
|
||||
static int do_cond_set (char *cond) { return do_cond_magic('+', cond); }
|
||||
static int do_cond_clear(char *cond) { return do_cond_magic('-', cond); }
|
||||
|
||||
static int do_cond_show(char *UNUSED(arg))
|
||||
{
|
||||
svc_t *svc;
|
||||
enum cond_state cond;
|
||||
|
||||
if (verbose) {
|
||||
glob_t gl;
|
||||
|
||||
printf("Asserted conditions (taken from %s)\n", _PATH_COND);
|
||||
printf("===============================================================================\n");
|
||||
|
||||
if (!glob(_PATH_COND "*/*/*", 0, NULL, &gl)) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
char *cond, *name = gl.gl_pathv[i];
|
||||
struct stat st;
|
||||
|
||||
if (stat(name, &st) || S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
|
||||
cond = name + strlen(_PATH_COND);
|
||||
printf("\t%s\n", cond);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
puts("");
|
||||
}
|
||||
|
||||
printf("PID Service Status Condition (+ on, ~ flux, - off)\n");
|
||||
printf("===============================================================================\n");
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!svc->cond[0])
|
||||
continue;
|
||||
|
||||
cond = cond_get_agg(svc->cond);
|
||||
|
||||
printf("%-6d %-20.20s ", svc->pid, svc->cmd);
|
||||
|
||||
if (cond == COND_ON)
|
||||
printf("%-6.6s ", condstr(cond));
|
||||
else
|
||||
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
|
||||
|
||||
show_cond_one(svc->cond);
|
||||
puts("");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_cond(char *cmd)
|
||||
{
|
||||
int c;
|
||||
char *arg;
|
||||
command_t command[] = {
|
||||
{ "set", do_cond_set },
|
||||
{ "clear", do_cond_clear },
|
||||
{ "show", do_cond_show },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
arg = strpbrk(cmd, " ");
|
||||
if (arg) {
|
||||
*arg = 0;
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!strcmp(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return do_cond_show(NULL);
|
||||
}
|
||||
|
||||
static int do_signal(int signo, const char *msg)
|
||||
{
|
||||
if (kill(1, signo))
|
||||
err(1, "Failed signalling init to %s", msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_halt (char *UNUSED(arg)) { return do_signal(SIGUSR1, "halt"); }
|
||||
static int do_poweroff(char *UNUSED(arg)) { return do_signal(SIGUSR2, "power off"); }
|
||||
static int do_reboot (char *UNUSED(arg)) { return do_signal(SIGTERM, "reboot"); }
|
||||
|
||||
static int do_utmp(char *UNUSED(cmd))
|
||||
{
|
||||
return utmp_show(_PATH_WTMP) ||
|
||||
utmp_show(_PATH_UTMP);
|
||||
}
|
||||
|
||||
static int show_version(char *UNUSED(arg))
|
||||
{
|
||||
puts("v" VERSION);
|
||||
puts("v" PACKAGE_VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In verbose mode we skip the header and each service description.
|
||||
* This in favor of having all info on one line so a machine can more
|
||||
* easily parse it. */
|
||||
static int show_status(char *UNUSED(arg))
|
||||
static int show_status(char *arg)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
/* Fetch UTMP runlevel, needed for svc_status() call below */
|
||||
runlevel = runlevel_get();
|
||||
|
||||
if (arg && arg[0]) {
|
||||
int id = 1;
|
||||
char *ptr, *next;
|
||||
|
||||
ptr = strchr(arg, ':');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
next = strchr(ptr, ' ');
|
||||
if (next)
|
||||
*next = 0;
|
||||
id = atonum(ptr);
|
||||
}
|
||||
|
||||
if (isdigit(arg[0]))
|
||||
svc = svc_find_by_jobid(atonum(arg), id);
|
||||
else
|
||||
svc = svc_find_by_nameid(arg, id);
|
||||
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
printf("%s\n", svc_status(svc));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!verbose) {
|
||||
printf("# Status PID Runlevels Service Description\n");
|
||||
printf("====================================================================================\n");
|
||||
printf("===============================================================================\n");
|
||||
}
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
@@ -156,7 +353,12 @@ static int show_status(char *UNUSED(arg))
|
||||
else
|
||||
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
||||
|
||||
printf("%-5s %7s %-6d ", jobid, svc_status(svc), svc->pid);
|
||||
printf("%-5s %7s ", jobid, svc_status(svc));
|
||||
if (svc_is_inetd(svc))
|
||||
printf("inetd ");
|
||||
else
|
||||
printf("%-6d ", svc->pid);
|
||||
|
||||
lvls = runlevel_string(svc->runlevels);
|
||||
if (strchr(lvls, '\e'))
|
||||
printf("%-18.18s ", lvls);
|
||||
@@ -164,7 +366,14 @@ static int show_status(char *UNUSED(arg))
|
||||
printf("%-10.10s ", lvls);
|
||||
|
||||
if (!verbose) {
|
||||
printf("%-20s %s\n", svc->cmd, svc->desc);
|
||||
char *name = strrchr(svc->cmd, '/');
|
||||
|
||||
if (!name)
|
||||
name = svc->cmd;
|
||||
else
|
||||
name++;
|
||||
|
||||
printf("%-20.20s %-21.21s\n", name, svc->desc);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -218,18 +427,20 @@ static int usage(int rc)
|
||||
"Commands:\n"
|
||||
" debug Toggle Finit (daemon) debug\n"
|
||||
" help This help text\n"
|
||||
" emit <EV> Emit event; a predefined event: RELOAD, STOP, START\n"
|
||||
" or a custom string matching an event in a service\n"
|
||||
" stanza, e.g: GW:UP, IFUP:IFNAME, IFDN:IFNAME. Where\n"
|
||||
" IFNAME is the interface name, e.g. eth0\n"
|
||||
" reload Reload *.conf in /etc/finit.d/ and activate changes\n"
|
||||
" reload Reload *.conf in /etc/finit.d and activate changes\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" status | show Show status of services\n"
|
||||
" cond set <COND> Set (assert) condition => +COND\n"
|
||||
" cond clear <COND> Clear (deassert) condition => -COND\n"
|
||||
" cond show Show condition status\n"
|
||||
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
|
||||
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
|
||||
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
|
||||
" reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
|
||||
" version Show Finit version\n\n", __progname);
|
||||
" reboot Reboot system\n"
|
||||
" halt Halt system\n"
|
||||
" poweroff Halt and power off system\n"
|
||||
" utmp show Raw dump of UTMP/WTMP db\n"
|
||||
" version Show Finit version\n\n", prognm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -239,14 +450,18 @@ int main(int argc, char *argv[])
|
||||
int c;
|
||||
command_t command[] = {
|
||||
{ "debug", toggle_debug },
|
||||
{ "emit", do_emit },
|
||||
{ "reload", do_reload },
|
||||
{ "runlevel", do_runlevel },
|
||||
{ "status", show_status },
|
||||
{ "show", show_status }, /* Convenience alias */
|
||||
{ "cond", do_cond },
|
||||
{ "start", do_start },
|
||||
{ "stop", do_stop },
|
||||
{ "restart", do_restart },
|
||||
{ "reboot", do_reboot },
|
||||
{ "halt", do_halt },
|
||||
{ "poweroff", do_poweroff },
|
||||
{ "utmp", do_utmp },
|
||||
{ "version", show_version },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -257,7 +472,7 @@ int main(int argc, char *argv[])
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
verbose = 0;
|
||||
progname(argv[0]);
|
||||
while ((c = getopt_long(argc, argv, "dh?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
@@ -273,6 +488,7 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
silent = !verbose;
|
||||
|
||||
if (optind < argc) {
|
||||
char *cmd = argv[optind++];
|
||||
@@ -294,7 +510,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
-1
Submodule libite deleted from 12ed342d55
-1
Submodule libuev deleted from cf5e729dd3
@@ -0,0 +1,2 @@
|
||||
*.m4
|
||||
!plugin.m4
|
||||
@@ -0,0 +1,17 @@
|
||||
# From https://github.com/kakaroto/e17/blob/master/BINDINGS/javascript/elixir/m4/ac_expand_dir.m4
|
||||
# By Alexandre Oliva <oliva@dcc.unicamp.br>
|
||||
|
||||
# AC_EXPAND_DIR(VARNAME, DIR)
|
||||
# ------------------------------------------------------------
|
||||
# Expands occurrences of ${prefix} and ${exec_prefix} in the
|
||||
# given DIR, and assigns the resulting string to VARNAME
|
||||
#
|
||||
# Example: AC_DEFINE_DIR(DATADIR, "$datadir")
|
||||
AC_DEFUN([AC_EXPAND_DIR], [
|
||||
$1=$2
|
||||
$1=`(
|
||||
test "x$prefix" = xNONE && prefix="$ac_default_prefix"
|
||||
test "x$exec_prefix" = xNONE && exec_prefix="${prefix}"
|
||||
eval echo \""[$]$1"\"
|
||||
)`
|
||||
])
|
||||
@@ -0,0 +1,35 @@
|
||||
# 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)
|
||||
# ------------------------------------------------------------
|
||||
AC_DEFUN([AC_PLUGIN],[
|
||||
m4_divert_once([HELP_ENABLE], [
|
||||
Optional Plugins:])
|
||||
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"
|
||||
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.])
|
||||
fi
|
||||
AM_CONDITIONAL([BUILD_]PLUGGY[_PLUGIN], test "x$enable_plugin" = "xyes")
|
||||
enable_pluggy="$enable_plugin"])
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Handle MD arrays
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <glob.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
static glob_t *get_arrays(void)
|
||||
{
|
||||
static glob_t arrays;
|
||||
|
||||
glob("/sys/block/md*", 0, NULL, &arrays);
|
||||
if (arrays.gl_pathc > 0)
|
||||
return &arrays;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If system has an MD raid, we must tell it to stop before continuing
|
||||
* with the shutdown. Some controller cards, in particular the Intel(R)
|
||||
* Matrix Storage Manager, must be properly notified.
|
||||
*/
|
||||
void mdadm_wait(void)
|
||||
{
|
||||
size_t i;
|
||||
glob_t *gl;
|
||||
char cmd[160];
|
||||
|
||||
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
|
||||
gl = get_arrays();
|
||||
if (gl) {
|
||||
for (i = 0; i < gl->gl_pathc; i++) {
|
||||
char *array;
|
||||
|
||||
array = basename(gl->gl_pathv[i]);
|
||||
snprintf(cmd, sizeof(cmd), "mdadm --wait-clean /dev/%s >/dev/null", array);
|
||||
run_interactive(cmd, "Marking MD array %s as clean", array);
|
||||
}
|
||||
globfree(gl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,110 @@
|
||||
/* Mount and unmount helpers
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <string.h>
|
||||
#include <mntent.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
/*
|
||||
* SysV init on Debian/Ubuntu skips these protected mount points
|
||||
*
|
||||
* It also skips anything below /proc, /sys and /run for good meausre so
|
||||
* we do the same here. What we want is a list of non-protected tmpfs
|
||||
* and regular filesystems that can be safely unmounted before we do
|
||||
* swapoff, and remount / as read-only, respectively.
|
||||
*/
|
||||
static int is_protected(char *dir)
|
||||
{
|
||||
size_t i;
|
||||
char *protected[] = {
|
||||
"/sys", "/proc",
|
||||
"/.dev", "/dev", "/dev/pts", "/dev/shm", "dev/.static/dev", "/dev/vcs",
|
||||
"/run", "/var/run",
|
||||
"/", NULL
|
||||
};
|
||||
char *any[] = {
|
||||
"/proc/", "/sys/", "/run/", NULL
|
||||
};
|
||||
|
||||
for (i = 0; protected[i]; i++) {
|
||||
if (!strcmp(dir, protected[i]))
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; any[i]; i++) {
|
||||
if (!strncmp(dir, any[i], strlen(any[i])))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mntent *iterator(char *fstab)
|
||||
{
|
||||
static FILE *fp = NULL;
|
||||
static struct mntent *mnt;
|
||||
|
||||
if (!fp && fstab) {
|
||||
fp = setmntent(fstab, "r");
|
||||
if (!fp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (is_protected(mnt->mnt_dir))
|
||||
continue;
|
||||
|
||||
return mnt;
|
||||
}
|
||||
|
||||
endmntent(fp);
|
||||
fp = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void unmount_tmpfs(void)
|
||||
{
|
||||
struct mntent *mnt;
|
||||
|
||||
while ((mnt = iterator("/proc/mounts"))) {
|
||||
if (!strcmp("tmpfs", mnt->mnt_fsname))
|
||||
umount(mnt->mnt_dir);
|
||||
}
|
||||
}
|
||||
|
||||
void unmount_regular(void)
|
||||
{
|
||||
struct mntent *mnt;
|
||||
|
||||
while ((mnt = iterator("/proc/mounts"))) {
|
||||
umount(mnt->mnt_dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -26,9 +26,9 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
|
||||
/**
|
||||
@@ -92,7 +92,6 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -26,15 +26,21 @@
|
||||
#include <dirent.h> /* readdir() et al */
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "private.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "queue.h" /* BSD sys/queue.h API */
|
||||
#include "libite/lite.h"
|
||||
|
||||
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd >= 0)
|
||||
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
||||
#define SEARCH_PLUGIN(str) \
|
||||
PLUGIN_ITERATOR(p, tmp) { \
|
||||
if (!strcmp(p->name, str)) \
|
||||
return p; \
|
||||
}
|
||||
|
||||
static char *plugpath = NULL; /* Set by first load. */
|
||||
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
|
||||
@@ -43,6 +49,22 @@ static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins
|
||||
static void check_plugin_depends(plugin_t *plugin);
|
||||
#endif
|
||||
|
||||
|
||||
static char *trim_ext(char *name)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (name) {
|
||||
ptr = strstr(name, ".so");
|
||||
if (!ptr)
|
||||
ptr = strstr(name, ".c");
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
int plugin_register(plugin_t *plugin)
|
||||
{
|
||||
if (!plugin) {
|
||||
@@ -55,18 +77,19 @@ int plugin_register(plugin_t *plugin)
|
||||
#ifndef ENABLE_STATIC
|
||||
Dl_info info;
|
||||
|
||||
if (!dladdr(plugin, &info) || !info.dli_fname)
|
||||
plugin->name = "unknown";
|
||||
else
|
||||
plugin->name = (char *)info.dli_fname;
|
||||
#else
|
||||
plugin->name = "unknown";
|
||||
if (dladdr(plugin, &info) && info.dli_fname)
|
||||
plugin->name = basename(info.dli_fname);
|
||||
#endif
|
||||
if (!plugin->name)
|
||||
plugin->name = "unknown";
|
||||
}
|
||||
plugin->name = trim_ext(strdup(plugin->name));
|
||||
|
||||
/* Already registered? */
|
||||
if (plugin_find(plugin->name)) {
|
||||
_d("... %s already loaded.", plugin->name);
|
||||
_d("... %s already loaded", plugin->name);
|
||||
free(plugin->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -86,25 +109,8 @@ int plugin_unregister(plugin_t *plugin)
|
||||
#ifndef ENABLE_STATIC
|
||||
TAILQ_REMOVE(&plugins, plugin, link);
|
||||
|
||||
if (plugin->svc.cb) {
|
||||
svc_t *svc;
|
||||
|
||||
/* Unregister plugin callback for all matching services */
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (strcmp(svc->cmd, plugin->name))
|
||||
continue;
|
||||
|
||||
svc->cb = NULL;
|
||||
svc->dynamic = 0;
|
||||
svc->dynamic_stop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unload plugin */
|
||||
if (dlclose(plugin->handle)) {
|
||||
_e("Failed unloading %s ...", plugin->name);
|
||||
return 1;
|
||||
}
|
||||
_d("%s exiting ...", plugin->name);
|
||||
free(plugin->name);
|
||||
#else
|
||||
_d("Finit built statically, cannot unload %s ...", plugin->name);
|
||||
#endif
|
||||
@@ -112,14 +118,6 @@ int plugin_unregister(plugin_t *plugin)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// _d("Comparing %s against plugin %s", str, p->name);
|
||||
#define SEARCH_PLUGIN(str) \
|
||||
PLUGIN_ITERATOR(p, tmp) { \
|
||||
if (!strcmp(p->name, str)) \
|
||||
return p; \
|
||||
}
|
||||
|
||||
/**
|
||||
* plugin_find - Find a plugin by name
|
||||
* @name: With or without path, or .so extension
|
||||
@@ -153,9 +151,9 @@ plugin_t *plugin_find(char *name)
|
||||
char path[CMD_SIZE];
|
||||
|
||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||
snprintf (path, sizeof(path), "%s%s%s%s", plugpath,
|
||||
fisslashdir(plugpath) ? "" : "/",
|
||||
name, noext ? ".so" : "");
|
||||
snprintf(path, sizeof(path), "%s%s%s%s", plugpath,
|
||||
fisslashdir(plugpath) ? "" : "/",
|
||||
name, noext ? ".so" : "");
|
||||
|
||||
SEARCH_PLUGIN(path);
|
||||
}
|
||||
@@ -165,18 +163,26 @@ plugin_t *plugin_find(char *name)
|
||||
}
|
||||
|
||||
/* Private daemon API *******************************************************/
|
||||
void plugin_run_hooks(hook_point_t no)
|
||||
|
||||
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
|
||||
void plugin_run_hook(hook_point_t no, void *arg)
|
||||
{
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (p->hook[no].cb) {
|
||||
_d("Calling %s hook n:o %d from runloop...", basename(p->name), no);
|
||||
p->hook[no].cb(p->hook[no].arg);
|
||||
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
|
||||
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Regular hooks are called with the registered plugin's argument */
|
||||
void plugin_run_hooks(hook_point_t no)
|
||||
{
|
||||
plugin_run_hook(no, NULL);
|
||||
}
|
||||
|
||||
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
|
||||
static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
@@ -194,17 +200,32 @@ static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup any I/O callbacks for plugins that use them */
|
||||
static void init_plugins(uev_ctx_t *ctx)
|
||||
int plugin_io_init(plugin_t *p)
|
||||
{
|
||||
if (!is_io_plugin(p))
|
||||
return 0;
|
||||
|
||||
_d("Initializing plugin %s for I/O", basename(p->name));
|
||||
if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) {
|
||||
_e("Failed setting up I/O plugin %s", basename(p->name));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Setup any I/O callbacks for plugins that use them */
|
||||
static int init_plugins(uev_ctx_t *UNUSED(ctx))
|
||||
{
|
||||
int fail = 0;
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (is_io_plugin(p)) {
|
||||
_d("Initializing plugin %s for I/O", basename(p->name));
|
||||
uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags);
|
||||
}
|
||||
if (plugin_io_init(p))
|
||||
fail++;
|
||||
}
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
@@ -239,10 +260,7 @@ static int load_one(char *path, char *name)
|
||||
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
|
||||
|
||||
_d("Loading plugin %s ...", basename(sofile));
|
||||
/* Ignore leak, we do not support unloading of plugins atm.
|
||||
* TODO: Add handle to list of loaded plugins, dlclose() on
|
||||
* plugin_unregister() */
|
||||
handle = dlopen(sofile, RTLD_LAZY | RTLD_GLOBAL);
|
||||
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle) {
|
||||
_e("Failed loading plugin %s: %s", sofile, dlerror());
|
||||
return 1;
|
||||
@@ -250,7 +268,7 @@ static int load_one(char *path, char *name)
|
||||
|
||||
plugin = TAILQ_LAST(&plugins, plugin_head);
|
||||
if (!plugin) {
|
||||
_e("Plugin %s failed to register, unloading from memory.", sofile);
|
||||
_e("Plugin %s failed to register, unloading from memory", sofile);
|
||||
dlclose(handle);
|
||||
return 1;
|
||||
}
|
||||
@@ -286,21 +304,20 @@ static void check_plugin_depends(plugin_t *plugin)
|
||||
load_one(plugpath, plugin->depends[i]);
|
||||
}
|
||||
}
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
int plugin_load_all(uev_ctx_t *ctx, char *path)
|
||||
static int load_plugins(char *path)
|
||||
{
|
||||
int fail = 0;
|
||||
#ifndef ENABLE_STATIC
|
||||
DIR *dp = opendir(path);
|
||||
DIR *dp;
|
||||
struct dirent *entry;
|
||||
|
||||
if (verbose)
|
||||
if (!silent)
|
||||
print_desc("Loading plugins", NULL);
|
||||
|
||||
dp = opendir(path);
|
||||
if (!dp) {
|
||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||
fail = 1;
|
||||
goto exit;
|
||||
return 1;
|
||||
}
|
||||
plugpath = path;
|
||||
|
||||
@@ -308,34 +325,51 @@ int plugin_load_all(uev_ctx_t *ctx, char *path)
|
||||
if (entry->d_name[0] == '.')
|
||||
continue; /* Skip . and .. directories */
|
||||
|
||||
// print_desc(" Loading plugin ", basename(plugin));
|
||||
if (load_one(path, entry->d_name))
|
||||
fail++;
|
||||
// print_result(result);
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
return fail;
|
||||
}
|
||||
#else
|
||||
_d("Finit built statically, not loading plugins from %s ...", path);
|
||||
if (verbose)
|
||||
static int load_plugins(char *UNUSED(path))
|
||||
{
|
||||
if (!silent)
|
||||
print_desc("Initializing plugins", NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
/* Always initialize plugins */
|
||||
init_plugins(ctx);
|
||||
int plugin_init(uev_ctx_t *ctx, char *path)
|
||||
{
|
||||
int fail = 1;
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
exit:
|
||||
#endif
|
||||
if (verbose)
|
||||
if (!load_plugins(path))
|
||||
fail = init_plugins(ctx);
|
||||
|
||||
if (!silent)
|
||||
print_result(fail);
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
void plugin_exit(void)
|
||||
{
|
||||
#ifndef ENABLE_STATIC
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (dlclose(p->handle))
|
||||
_e("Failed: unloading plugin %s: %s", p->name, dlerror());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -24,9 +24,14 @@
|
||||
#ifndef FINIT_PLUGIN_H_
|
||||
#define FINIT_PLUGIN_H_
|
||||
|
||||
#include "queue.h" /* BSD sys/queue.h API */
|
||||
#include <uev/uev.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "svc.h"
|
||||
#include "libuev/uev.h"
|
||||
|
||||
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
|
||||
#define PLUGIN_PATH "/dev/null"
|
||||
#endif
|
||||
|
||||
#define PLUGIN_DEP_MAX 10
|
||||
#define PLUGIN_IO_READ UEV_READ
|
||||
@@ -51,6 +56,8 @@ typedef enum {
|
||||
|
||||
/* Runtime hooks */
|
||||
HOOK_SVC_RECONF,
|
||||
HOOK_SVC_LOST,
|
||||
HOOK_SVC_START,
|
||||
HOOK_RUNLEVEL_CHANGE,
|
||||
|
||||
/* Shutdown hooks */
|
||||
@@ -97,13 +104,6 @@ typedef struct plugin {
|
||||
* NOTE: Must match cmd for services or inetd plugins! */
|
||||
char *name;
|
||||
|
||||
/* Service callback to be called once per lap of runloop. */
|
||||
struct {
|
||||
int dynamic; /* Callback wants external events as well. */
|
||||
int dynamic_stop; /* Callback allows dynamic events to stop. */
|
||||
svc_cmd_t (*cb)(svc_t *svc, int event, void *event_arg);
|
||||
} svc;
|
||||
|
||||
/* List of hook callbacks. */
|
||||
struct {
|
||||
void *arg; /* Optional argument to callback func. */
|
||||
@@ -127,6 +127,8 @@ typedef struct plugin {
|
||||
} plugin_t;
|
||||
|
||||
/* Public plugin API */
|
||||
int plugin_io_init (plugin_t *plugin);
|
||||
|
||||
int plugin_register (plugin_t *plugin);
|
||||
int plugin_unregister (plugin_t *plugin);
|
||||
|
||||
@@ -137,7 +139,6 @@ plugin_t *plugin_find (char *name);
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/*.la
|
||||
/*.lo
|
||||
/.deps/*
|
||||
/.libs/*
|
||||
@@ -1,53 +0,0 @@
|
||||
# Plugin based services architecture for finit
|
||||
-include ../config.mk
|
||||
|
||||
TOPDIR ?= $(shell pwd)
|
||||
CFLAGS += -fPIC
|
||||
LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS))
|
||||
LDFLAGS += -shared
|
||||
ifneq ($(STATIC), 1)
|
||||
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
|
||||
resolvconf.so urandom.so x11-common.so tty.so time.so \
|
||||
netlink.so
|
||||
DEPS := $(PLUGINS:.so=.d)
|
||||
endif
|
||||
|
||||
include ../common.mk
|
||||
|
||||
ifneq ($(STATIC), 1)
|
||||
all: $(PLUGINS)
|
||||
else
|
||||
all:
|
||||
@true
|
||||
endif
|
||||
|
||||
clean:
|
||||
-@$(RM) $(PLUGINS)
|
||||
|
||||
distclean:
|
||||
-@$(RM) *.o *.so $(JUNK) $(DEPS)
|
||||
|
||||
ifneq ($(STATIC), 1)
|
||||
install: all
|
||||
@install -d $(DESTDIR)$(plugindir)
|
||||
@for plugin in $(PLUGINS); do \
|
||||
echo " INSTALL $(DESTDIR)$(plugindir)/$$plugin"; \
|
||||
$(STRIPINST) $$plugin $(DESTDIR)$(plugindir)/$$plugin; \
|
||||
done
|
||||
|
||||
uninstall:
|
||||
-@for plugin in $(PLUGINS); do \
|
||||
echo " RM $(DESTDIR)$(plugindir)/$$plugin"; \
|
||||
rm $(DESTDIR)$(plugindir)/$$plugin 2>/dev/null; \
|
||||
done
|
||||
-@rmdir $(DESTDIR)$(plugindir) 2>/dev/null
|
||||
else
|
||||
install:
|
||||
@true
|
||||
uninstall:
|
||||
@true
|
||||
endif
|
||||
|
||||
ifneq ($(MAKECMDGOALS),distclean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
@@ -0,0 +1,108 @@
|
||||
AM_LDFLAGS = -module -avoid-version -shared
|
||||
AM_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
AM_CPPFLAGS += $(lite_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
libplug_la_SOURCES = bootmisc.c hwclock.c initctl.c pidfile.c procps.c tty.c urandom.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
endif
|
||||
|
||||
if BUILD_DBUS_PLUGIN
|
||||
libplug_la_SOURCES += dbus.c
|
||||
endif
|
||||
|
||||
if INETD
|
||||
if BUILD_INETD_ECHO_PLUGIN
|
||||
libplug_la_SOURCES += echo.c
|
||||
endif
|
||||
|
||||
if BUILD_INETD_CHARGEN_PLUGIN
|
||||
libplug_la_SOURCES += chargen.c
|
||||
endif
|
||||
|
||||
if BUILD_INETD_DAYTIME_PLUGIN
|
||||
libplug_la_SOURCES += daytime.c
|
||||
endif
|
||||
|
||||
if BUILD_INETD_DISCARD_PLUGIN
|
||||
libplug_la_SOURCES += discard.c
|
||||
endif
|
||||
|
||||
if BUILD_INETD_TIME_PLUGIN
|
||||
libplug_la_SOURCES += time.c
|
||||
endif
|
||||
endif
|
||||
|
||||
if BUILD_LOST_PLUGIN
|
||||
libplug_la_SOURCES += lost.c
|
||||
endif
|
||||
|
||||
if BUILD_NETLINK_PLUGIN
|
||||
libplug_la_SOURCES += netlink.c
|
||||
endif
|
||||
|
||||
if BUILD_RESOLVCONF_PLUGIN
|
||||
libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
else
|
||||
pkglib_LTLIBRARIES = bootmisc.la hwclock.la initctl.la pidfile.la procps.la tty.la urandom.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
endif
|
||||
|
||||
if BUILD_DBUS_PLUGIN
|
||||
pkglib_LTLIBRARIES += dbus.la
|
||||
endif
|
||||
|
||||
if INETD
|
||||
if BUILD_INETD_ECHO_PLUGIN
|
||||
pkglib_LTLIBRARIES += echo.la
|
||||
endif
|
||||
|
||||
if BUILD_INETD_CHARGEN_PLUGIN
|
||||
pkglib_LTLIBRARIES += chargen.la
|
||||
endif
|
||||
|
||||
if BUILD_INETD_DAYTIME_PLUGIN
|
||||
pkglib_LTLIBRARIES += daytime.la
|
||||
endif
|
||||
|
||||
if BUILD_INETD_DISCARD_PLUGIN
|
||||
pkglib_LTLIBRARIES += discard.la
|
||||
endif
|
||||
|
||||
if BUILD_INETD_TIME_PLUGIN
|
||||
pkglib_LTLIBRARIES += time.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if BUILD_LOST_PLUGIN
|
||||
pkglib_LTLIBRARIES += lost.la
|
||||
endif
|
||||
|
||||
if BUILD_NETLINK_PLUGIN
|
||||
pkglib_LTLIBRARIES += netlink.la
|
||||
endif
|
||||
|
||||
if BUILD_RESOLVCONF_PLUGIN
|
||||
pkglib_LTLIBRARIES += resolvconf.la
|
||||
endif
|
||||
|
||||
if BUILD_X11_COMMON_PLUGIN
|
||||
pkglib_LTLIBRARIES += x11-common.la
|
||||
endif
|
||||
|
||||
# Clean up .la files installed by libtool despite being in -module mode
|
||||
install-exec-hook:
|
||||
@(cd $(DESTDIR)$(pkglibdir) && $(RM) $(pkglib_LTLIBRARIES))
|
||||
|
||||
uninstall-hook:
|
||||
@(cd $(DESTDIR)$(pkglibdir) && for file in $(pkglib_LTLIBRARIES); do $(RM) `basename $$file .la`.so; done)
|
||||
endif
|
||||
+14
-13
@@ -1,6 +1,6 @@
|
||||
/* Save and restore ALSA sound settings using alsactl
|
||||
*
|
||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2016 Joachim Nilsson <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,31 +21,33 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
#define ALSACTL "/usr/sbin/alsactl"
|
||||
|
||||
static void save(void *UNUSED(arg))
|
||||
{
|
||||
_d("Saving sound settings ...");
|
||||
run_interactive("/usr/sbin/alsactl -g store", "Saving sound settings");
|
||||
if (fexist(ALSACTL)) {
|
||||
_d("Saving sound settings ...");
|
||||
run_interactive(ALSACTL " -g store", "Saving sound settings");
|
||||
}
|
||||
}
|
||||
|
||||
static void restore(void *UNUSED(arg))
|
||||
{
|
||||
_d("Restoring sound settings ...");
|
||||
run_interactive("/usr/sbin/alsactl -g restore", "Restoring sound settings");
|
||||
if (fexist(ALSACTL)) {
|
||||
_d("Restoring sound settings ...");
|
||||
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
|
||||
}
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = restore
|
||||
},
|
||||
.hook[HOOK_SHUTDOWN] = {
|
||||
.cb = save
|
||||
}
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = restore },
|
||||
.hook[HOOK_SHUTDOWN] = { .cb = save }
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
@@ -61,7 +63,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
+111
-24
@@ -1,6 +1,6 @@
|
||||
/* Setup necessary files for UTMP, tracking logins
|
||||
/* Setup necessary system files for, e.g. UTMP (tracking logins)
|
||||
*
|
||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-2016 Joachim Nilsson <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,54 +21,139 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <ftw.h>
|
||||
#include <mntent.h>
|
||||
#include <string.h>
|
||||
#include <utmp.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
#include "../utmp-api.h"
|
||||
|
||||
static int is_tmpfs(char *path)
|
||||
{
|
||||
int tmpfs = 0;
|
||||
FILE *fp;
|
||||
char *dir;
|
||||
struct mntent *mnt;
|
||||
|
||||
/* If path is a symlink, check what it resolves to */
|
||||
dir = realpath(path, NULL);
|
||||
if (!dir)
|
||||
return 0; /* Outlook not so good */
|
||||
|
||||
fp = setmntent("/proc/mounts", "r");
|
||||
if (!fp) {
|
||||
free(dir);
|
||||
return 0; /* Dunno, maybe not */
|
||||
}
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(dir, mnt->mnt_dir))
|
||||
break;
|
||||
}
|
||||
|
||||
if (mnt && !strcmp("tmpfs", mnt->mnt_type))
|
||||
tmpfs = 1;
|
||||
|
||||
endmntent(fp);
|
||||
free(dir);
|
||||
|
||||
return tmpfs;
|
||||
}
|
||||
|
||||
static void create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
if (touch(path) || chmod(path, mode) || chown(path, uid, gid))
|
||||
_w("Failed creating %s properl.", path);
|
||||
}
|
||||
|
||||
static int do_clean(const char *fpath, const struct stat *UNUSED(sb), int UNUSED(tflag), struct FTW *ftwbuf)
|
||||
{
|
||||
if (ftwbuf->level == 0)
|
||||
return 1;
|
||||
|
||||
_d("Removing %s ...", fpath);
|
||||
(void)remove(fpath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We can safely skip tmpfs, nothing to clean from previous boot there */
|
||||
static void bootclean(void)
|
||||
{
|
||||
char *dir[] = {
|
||||
"/tmp/",
|
||||
"/var/run/",
|
||||
"/var/lock/",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int i = 0; dir[i]; i++) {
|
||||
if (is_tmpfs(dir[i]))
|
||||
continue;
|
||||
|
||||
nftw(dir[i], do_clean, 20, FTW_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup standard FHS 2.3 structure in /var, and
|
||||
* write runlevel to UTMP, needed by, e.g., printerdrake.
|
||||
* Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP
|
||||
*/
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
#ifdef RUNLEVEL
|
||||
struct utmp entry;
|
||||
#endif
|
||||
int gid;
|
||||
|
||||
/* Cleanup stale files, if any still linger on. */
|
||||
bootclean();
|
||||
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
umask(0);
|
||||
makedir("/var/cache", 0755);
|
||||
makedir("/var/games", 0755);
|
||||
makedir("/var/lib", 0755);
|
||||
makedir("/var/lib/misc", 0755);
|
||||
makedir("/var/lib/alarm", 0755);
|
||||
makedir("/var/lib/urandom",0755);
|
||||
makedir("/var/lock", 0755);
|
||||
if (fisdir("/run")) {
|
||||
_d("System with new /run tmpfs ...");
|
||||
makedir("/run/lock", 1777);
|
||||
symlink("/run/lock", "/var/lock");
|
||||
symlink("/run", "/var/run");
|
||||
symlink("/dev/shm", "/run/shm");
|
||||
} else {
|
||||
makedir("/var/lock", 1777);
|
||||
makedir("/var/run", 0755);
|
||||
}
|
||||
makedir("/var/log", 0755);
|
||||
makedir("/var/mail", 0755);
|
||||
makedir("/var/opt", 0755);
|
||||
makedir("/var/run", 0755);
|
||||
makedir("/var/spool", 0755);
|
||||
makedir("/var/spool/cron", 0755);
|
||||
makedir("/var/tmp", 0755);
|
||||
makedir("/var/empty", 0755);
|
||||
|
||||
/*
|
||||
* If /etc/group or "utmp" group is missing,
|
||||
* default to "root"/"wheel" group
|
||||
*/
|
||||
_d("Setting up necessary UTMP files ...");
|
||||
touch("/var/run/utmp");
|
||||
chown("/var/run/utmp", 0, getgroup("utmp"));
|
||||
gid = getgroup("utmp");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
#ifdef RUNLEVEL
|
||||
memset(&entry, 0, sizeof(struct utmp));
|
||||
entry.ut_type = RUN_LVL;
|
||||
entry.ut_pid = '0' + RUNLEVEL;
|
||||
setutent();
|
||||
pututline(&entry);
|
||||
endutent();
|
||||
#endif
|
||||
/*
|
||||
* UTMP actually needs multiple db files
|
||||
*/
|
||||
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
|
||||
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
|
||||
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
||||
create("/var/log/lastlog", 0644, 0, gid);
|
||||
|
||||
/* Set BOOT_TIME UTMP entry */
|
||||
utmp_set_boot();
|
||||
|
||||
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
|
||||
touch("/etc/network/run/ifstate");
|
||||
@@ -77,13 +162,16 @@ static void setup(void *UNUSED(arg))
|
||||
#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 */
|
||||
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
||||
chown("/dev/xconsole", 0, getgroup("tty"));
|
||||
|
||||
umask(022);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
@@ -105,7 +193,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/* Optional inetd plugin for the Character Generator Protocol, RFC 864
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <arpa/inet.h>
|
||||
#include <unistd.h> /* STDIN_FILENO */
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
#define NAME "chargen"
|
||||
#define PATTERN "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "
|
||||
|
||||
static char *generator(char *buf, size_t buflen)
|
||||
{
|
||||
size_t num, len, width = 72;
|
||||
static size_t pos = 0;
|
||||
const char pattern[] = PATTERN;
|
||||
|
||||
if (buflen < width)
|
||||
width = buflen;
|
||||
|
||||
len = width;
|
||||
if (len + 3 > buflen)
|
||||
len = buflen - 3;
|
||||
if (pos + len > sizeof(pattern)) {
|
||||
num = sizeof(pattern) - pos;
|
||||
len -= num;
|
||||
} else {
|
||||
num = width;
|
||||
if (num + 3 > buflen)
|
||||
num = buflen - 3;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
strncpy(buf, &pattern[pos], num--);
|
||||
if (len++)
|
||||
strncpy(&buf[num], pattern, len);
|
||||
strlcat(buf, "\r\n", width);
|
||||
|
||||
if (++pos >= sizeof(pattern) - 1)
|
||||
pos = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len)
|
||||
{
|
||||
len = recvfrom(sd, buf, len, MSG_DONTWAIT, sa, sa_len);
|
||||
if (-1 == len)
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
if (inetd_check_loop(sa, *sa_len, NAME))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t sa_len)
|
||||
{
|
||||
char *pattern = generator(buf, len);
|
||||
|
||||
return sendto(sd, pattern, strlen(pattern), MSG_DONTWAIT, sa, sa_len);
|
||||
}
|
||||
|
||||
static int cb(int UNUSED(type))
|
||||
{
|
||||
int sd = STDIN_FILENO;
|
||||
char buf[BUFSIZ];
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len))
|
||||
return -1;
|
||||
|
||||
return send_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, sa_len);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = NAME, /* Must match the inetd /etc/services entry */
|
||||
.inetd = {
|
||||
.cmd = cb
|
||||
},
|
||||
};
|
||||
|
||||
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:
|
||||
*/
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Optional inetd plugin for Daytime Protocol, RFC 867
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <arpa/inet.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h> /* STDIN_FILENO */
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
#define NAME "daytime"
|
||||
|
||||
|
||||
static char *daytime(char *buf, size_t len)
|
||||
{
|
||||
time_t t;
|
||||
struct tm *tmp;
|
||||
|
||||
t = time(NULL);
|
||||
tmp = localtime(&t);
|
||||
|
||||
memset(buf, 0, len);
|
||||
strftime(buf, len, "%a, %B %d, %Y %T-%Z", tmp);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len)
|
||||
{
|
||||
len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, sa, sa_len);
|
||||
if (-1 == len)
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
if (inetd_check_loop(sa, *sa_len, NAME))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t sa_len)
|
||||
{
|
||||
char *now = daytime(buf, len);
|
||||
|
||||
return sendto(sd, now, strlen(now), MSG_DONTWAIT, sa, sa_len);
|
||||
}
|
||||
|
||||
static int cb(int UNUSED(type))
|
||||
{
|
||||
int sd = STDIN_FILENO;
|
||||
char buf[BUFSIZ];
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len))
|
||||
return -1;
|
||||
|
||||
return send_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, sa_len);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = NAME, /* Must match the inetd /etc/services entry */
|
||||
.inetd = {
|
||||
.cmd = cb
|
||||
}
|
||||
};
|
||||
|
||||
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:
|
||||
*/
|
||||
+6
-4
@@ -22,23 +22,26 @@
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../config.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
#ifdef HAVE_DBUS
|
||||
umask(0);
|
||||
|
||||
_d("Starting D-Bus ...");
|
||||
makedir("/var/run/dbus", 0755);
|
||||
makedir("/var/lock/subsys", 0755);
|
||||
makedir("/var/lock/subsys/messagebus", 0755);
|
||||
run("dbus-uuidgen --ensure");
|
||||
erase("/var/run/dbus/pid");
|
||||
run_interactive("dbus-daemon --system", "Starting D-Bus");
|
||||
#endif
|
||||
|
||||
umask(022);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
@@ -61,7 +64,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/* Optional inetd plugin for the Discard Protocol, RFC 863
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <arpa/inet.h>
|
||||
#include <unistd.h> /* STDIN_FILENO */
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static int cb(int UNUSED(type))
|
||||
{
|
||||
int sd = STDIN_FILENO;
|
||||
char buf[BUFSIZ];
|
||||
ssize_t len;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len);
|
||||
if (-1 == len)
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = "discard", /* Must match the inetd /etc/services entry */
|
||||
.inetd = {
|
||||
.cmd = cb
|
||||
},
|
||||
};
|
||||
|
||||
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:
|
||||
*/
|
||||
@@ -0,0 +1,81 @@
|
||||
/* Optional inetd plugin for the Echo Protocol, RFC 862
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <arpa/inet.h>
|
||||
#include <unistd.h> /* STDIN_FILENO */
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
#define NAME "echo"
|
||||
|
||||
static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len)
|
||||
{
|
||||
len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, sa, sa_len);
|
||||
if (-1 == len)
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
if (inetd_check_loop(sa, *sa_len, NAME))
|
||||
return -1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int cb(int UNUSED(type))
|
||||
{
|
||||
int sd = STDIN_FILENO;
|
||||
char buf[BUFSIZ];
|
||||
ssize_t len;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
len = recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len);
|
||||
if (-1 == len)
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
return sendto(sd, buf, len, MSG_DONTWAIT, (struct sockaddr *)&sa, sa_len);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = NAME, /* Must match the inetd /etc/services entry */
|
||||
.inetd = {
|
||||
.cmd = cb
|
||||
},
|
||||
};
|
||||
|
||||
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:
|
||||
*/
|
||||
+2
-2
@@ -21,10 +21,11 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void save(void *UNUSED(arg))
|
||||
{
|
||||
@@ -62,7 +63,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
+56
-18
@@ -30,6 +30,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h> /* read() */
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../conf.h"
|
||||
@@ -37,9 +38,9 @@
|
||||
#include "../plugin.h"
|
||||
#include "../sig.h"
|
||||
#include "../service.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void parse(void *arg, int fd, int events);
|
||||
static void setup(void *arg);
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
@@ -47,6 +48,10 @@ static plugin_t plugin = {
|
||||
.cb = parse,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
};
|
||||
|
||||
static void fifo_open(void)
|
||||
@@ -58,11 +63,44 @@ static void fifo_open(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Standard reboot/shutdown utilities talk to init using /dev/initctl.
|
||||
/*
|
||||
* We only handle INIT_HALT for differentiating between halt/poweroff
|
||||
*/
|
||||
static void set_env(char *data)
|
||||
{
|
||||
const char *var = "INIT_HALT";
|
||||
|
||||
if (strncmp(data, var, strlen(var)))
|
||||
return;
|
||||
data += strlen(var);
|
||||
|
||||
/*
|
||||
* No value means unset env, in this case cancel shutdown.
|
||||
*/
|
||||
if (data[0] == 0) {
|
||||
halt = SHUT_DEFAULT;
|
||||
/* XXX: Cancel timer here, when we get support for timed shutdown */
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_match(data, "=POWERDOWN"))
|
||||
halt = SHUT_OFF;
|
||||
if (string_match(data, "=HALT"))
|
||||
halt = SHUT_HALT;
|
||||
|
||||
/* Now we wait for runlevel change, or cancelled shutdown */
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard reboot/shutdown utilities talk to init using /run/initctl.
|
||||
* We should check if the fifo was recreated and reopen it.
|
||||
*
|
||||
* For SysV compatibility the default is to halt the system when issuing
|
||||
* `init 0`, unless INIT_HALT=POWERDOWN, as performed by the SysV utils.
|
||||
*/
|
||||
static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
{
|
||||
int lvl;
|
||||
struct init_request rq;
|
||||
|
||||
while (1) {
|
||||
@@ -91,26 +129,17 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RUNLVL:
|
||||
switch (rq.runlevel) {
|
||||
case '0':
|
||||
_d("Halting system (SIGUSR2)");
|
||||
do_shutdown(SIGUSR2);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
_d("Cannot enter bootstrap after boot ...");
|
||||
rq.runlevel = '1';
|
||||
/* Fall through to regular processing */
|
||||
|
||||
case '1'...'5':
|
||||
case '7'...'9':
|
||||
case '0'...'9':
|
||||
_d("Setting new runlevel %c", rq.runlevel);
|
||||
service_runlevel(rq.runlevel - '0');
|
||||
break;
|
||||
|
||||
case '6':
|
||||
_d("Rebooting system (SIGUSR1)");
|
||||
do_shutdown(SIGUSR1);
|
||||
lvl = rq.runlevel - '0';
|
||||
if (lvl == 6)
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(lvl);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -123,6 +152,10 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
service_reload_dynamic();
|
||||
break;
|
||||
|
||||
case INIT_CMD_SETENV:
|
||||
set_env(rq.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
break;
|
||||
@@ -133,12 +166,18 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
fifo_open();
|
||||
}
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
/* Must run after the base FS is up, needs /run, or /var/run */
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
_d("Setting up %s", FINIT_FIFO);
|
||||
makefifo(FINIT_FIFO, 0600);
|
||||
|
||||
fifo_open();
|
||||
plugin_io_init(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
@@ -149,7 +188,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Example HOOK_SVC_LOST plugin, very noisy do not use as-is!
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 "../finit.h"
|
||||
#include "../svc.h"
|
||||
#include "../plugin.h"
|
||||
|
||||
static void lost_pid(void *arg)
|
||||
{
|
||||
pid_t pid = (uintptr_t)arg;
|
||||
svc_t *svc;
|
||||
|
||||
svc = svc_find_by_pid(pid);
|
||||
if (!svc)
|
||||
return;
|
||||
|
||||
logit(LOG_INFO, "Lost PID %u, svc %s", pid, svc->cmd);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_SVC_LOST] = { .cb = lost_pid },
|
||||
};
|
||||
|
||||
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:
|
||||
*/
|
||||
+22
-16
@@ -16,6 +16,7 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h> /* IFNAMSIZ */
|
||||
#include <sys/socket.h>
|
||||
@@ -25,7 +26,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../event.h"
|
||||
#include "../cond.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
|
||||
@@ -86,13 +87,10 @@ static void nl_route(struct nlmsghdr *nlmsg)
|
||||
}
|
||||
|
||||
if ((!dst && !mask) && (gw || idx)) {
|
||||
char msg[MAX_ARG_LEN];
|
||||
|
||||
if (nlmsg->nlmsg_type == RTM_DELROUTE)
|
||||
snprintf(msg, sizeof(msg), "GW:DN");
|
||||
cond_clear("net/route/default");
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "GW:UP");
|
||||
event_dispatch(msg);
|
||||
cond_set("net/route/default");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,19 +121,28 @@ static void nl_link(struct nlmsghdr *nlmsg)
|
||||
* New interface has appearad, or interface flags has changed.
|
||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||
*/
|
||||
if (i->ifi_change & IFF_UP)
|
||||
snprintf(msg, sizeof(msg), "IF%s:%s",
|
||||
(i->ifi_flags & IFF_UP) ? "UP" : "DN",
|
||||
ifname);
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "IFADD:%s", ifname);
|
||||
event_dispatch(msg);
|
||||
if (i->ifi_change & IFF_UP) {
|
||||
snprintf(msg, sizeof(msg), "net/%s/up", ifname);
|
||||
|
||||
if (i->ifi_flags & IFF_UP)
|
||||
cond_set(msg);
|
||||
else
|
||||
cond_clear(msg);
|
||||
|
||||
if (string_compare("lo", ifname)) {
|
||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||
cond_set(msg);
|
||||
}
|
||||
} else {
|
||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||
cond_set(msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
/* NOTE: Interface has dissapeared, not link down ... */
|
||||
snprintf(msg, sizeof(msg), "IFDEL:%s", ifname);
|
||||
event_dispatch(msg);
|
||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||
cond_clear(msg);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
@@ -220,7 +227,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/* Simple pidfile event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2015-2016 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 <limits.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../cond.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
|
||||
struct context {
|
||||
int fd;
|
||||
int wd;
|
||||
};
|
||||
|
||||
static void pidfile_callback(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
static char cond[MAX_ARG_LEN];
|
||||
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz, len;
|
||||
char *basename;
|
||||
svc_t *svc;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
|
||||
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||
/* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
|
||||
if (!ev->mask || !strstr(ev->name, ".pid"))
|
||||
continue;
|
||||
|
||||
basename = strtok(ev->name, ".");
|
||||
if (!basename)
|
||||
continue;
|
||||
|
||||
svc = svc_find_by_nameid(basename, 1);
|
||||
if (!svc)
|
||||
continue;
|
||||
|
||||
/* TODO FIXME XXX WKZ check that pid is controlled by finit */
|
||||
|
||||
_d("%s: match %s", basename, svc->cmd);
|
||||
snprintf(cond, sizeof(cond), "svc%s", svc->cmd);
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
|
||||
svc_started(svc);
|
||||
cond_set(cond);
|
||||
} else if (ev->mask & IN_DELETE)
|
||||
cond_clear(cond);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Assert condition only if the service is running, but not if it's
|
||||
* recently been changed or while it's starting up.
|
||||
*
|
||||
* We must wait for the service to create/touch its pidfile.
|
||||
*/
|
||||
static void pidfile_reconf(void *_null)
|
||||
{
|
||||
static char name[MAX_ARG_LEN];
|
||||
|
||||
svc_t *svc;
|
||||
(void)(_null);
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc) && !svc_is_starting(svc)) {
|
||||
snprintf(name, MAX_ARG_LEN, "svc%s", svc->cmd);
|
||||
cond_set_path(cond_path(name), COND_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pidfile_init(void *arg)
|
||||
{
|
||||
struct context *ctx = arg;
|
||||
|
||||
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
|
||||
IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
||||
if (ctx->wd < 0) {
|
||||
_pe("inotify_add_watch()");
|
||||
close(ctx->fd);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("pidfile monitor active");
|
||||
}
|
||||
|
||||
static struct context pidfile_ctx;
|
||||
|
||||
/*
|
||||
* We require /var/run to be set up before calling pidfile_init(),
|
||||
* so the bootmisc plugin must run first.
|
||||
*/
|
||||
static plugin_t plugin = {
|
||||
.hook[HOOK_BASEFS_UP] = { .arg = &pidfile_ctx, .cb = pidfile_init },
|
||||
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
||||
.io = {
|
||||
.cb = pidfile_callback,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
pidfile_ctx.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (pidfile_ctx.fd < 0) {
|
||||
_pe("inotify_init()");
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.io.fd = pidfile_ctx.fd;
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
inotify_rm_watch(pidfile_ctx.fd, pidfile_ctx.wd);
|
||||
close(pidfile_ctx.fd);
|
||||
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,74 @@
|
||||
/* Set kernel variables from /etc/sysctl.conf et al
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <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 <glob.h>
|
||||
#include <lite/lite.h>
|
||||
#include "../plugin.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
size_t i;
|
||||
glob_t gl;
|
||||
char buf[160];
|
||||
|
||||
glob("/run/sysctl.d/*.conf", 0, NULL, &gl);
|
||||
glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
glob("/usr/local/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
glob("/usr/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
glob("/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
glob("/mnt/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
glob("/etc/sysctl.conf", GLOB_APPEND, NULL, &gl);
|
||||
if (gl.gl_pathc > 0) {
|
||||
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);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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:
|
||||
*/
|
||||
+10
-31
@@ -21,48 +21,28 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
/*
|
||||
* No need to clean up files in /etc/resolvconf/run/interface/
|
||||
* this is already taken care of by bootclean
|
||||
*/
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
#ifdef USE_ETC_RESOLVCONF_RUN
|
||||
DIR *dir;
|
||||
struct dirent *d;
|
||||
#endif
|
||||
|
||||
_d("Setting up the resolver ...");
|
||||
#ifdef USE_ETC_RESOLVCONF_RUN
|
||||
makepath("/dev/shm/network");
|
||||
makepath("/dev/shm/resolvconf/interface");
|
||||
mkpath("/etc/resolvconf", 0755);
|
||||
mkpath("/etc/resolvconf/run", 0755);
|
||||
mkpath("/var/run/resolvconf", 0755);
|
||||
mkpath("/var/run/resolvconf/interface", 0755);
|
||||
|
||||
if ((dir = opendir("/etc/resolvconf/run/interface")) != NULL) {
|
||||
while ((d = readdir(dir)) != NULL) {
|
||||
if (isalnum(d->d_name[0]))
|
||||
continue;
|
||||
snprintf(line, sizeof(line),
|
||||
"/etc/resolvconf/run/interface/%s", d->d_name);
|
||||
unlink(line);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_VAR_RUN_RESOLVCONF
|
||||
makepath("/var/run/resolvconf/interface");
|
||||
symlink("../../../etc/resolv.conf", "/var/run/resolvconf/resolv.conf");
|
||||
#endif
|
||||
|
||||
#ifdef USE_ETC_RESOLVCONF_RUN
|
||||
touch("/etc/resolvconf/run/enable-updates");
|
||||
|
||||
chdir("/etc/resolvconf/run/interface");
|
||||
run_parts("/etc/resolvconf/update.d", "-i");
|
||||
chdir("/");
|
||||
#endif /* USE_ETC_RESOLVCONF_RUN */
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
@@ -85,7 +65,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
+51
-22
@@ -1,4 +1,4 @@
|
||||
/* Optional inetd plugin for time (rdate) protocol RFC 868
|
||||
/* Optional inetd plugin for Time Protocol (rdate), RFC 868
|
||||
*
|
||||
* Copyright (c) 2015 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
@@ -28,43 +28,73 @@
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
#define NAME "time"
|
||||
|
||||
/* UNIX epoch starts midnight, 1st Jan, 1970 */
|
||||
#define EPOCH_OFFSET 2208988800ULL
|
||||
|
||||
/* Return number of seconds since midnight, 1st Jan, 1900 */
|
||||
static int rfc868(int type)
|
||||
static char *rfctime(char *buf, size_t *len)
|
||||
{
|
||||
int sd = STDIN_FILENO;
|
||||
time_t now;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (SOCK_DGRAM == type) {
|
||||
int dummy;
|
||||
|
||||
/* Read empty datagram in UDP mode before sending reply. */
|
||||
if (-1 == recvfrom(sd, &dummy, sizeof(dummy), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len))
|
||||
return -1; /* On error, close connection. */
|
||||
}
|
||||
|
||||
now = time(NULL);
|
||||
if ((time_t)-1 == now)
|
||||
return -1; /* On error, close connection. */
|
||||
return NULL;
|
||||
|
||||
/* Account for UNIX epoch offset */
|
||||
/*
|
||||
* Account for UNIX epoch offset, and
|
||||
* convert to network byte order
|
||||
*/
|
||||
now += EPOCH_OFFSET;
|
||||
now = htonl(now);
|
||||
|
||||
/* Convert to network byte order, not explicitly stated in RFC */
|
||||
now = htonl(now);
|
||||
*len = sizeof(now);
|
||||
memcpy(buf, &now, *len);
|
||||
|
||||
/* Send reply, ignore error, simply close connection. */
|
||||
return sendto(sd, &now, sizeof(now), MSG_DONTWAIT, (struct sockaddr *)&sa, sa_len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len)
|
||||
{
|
||||
len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, sa, sa_len);
|
||||
if (-1 == len)
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
if (inetd_check_loop(sa, *sa_len, NAME))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t sa_len)
|
||||
{
|
||||
char *now;
|
||||
|
||||
now = rfctime(buf, (size_t *)&len);
|
||||
if (!now)
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
return sendto(sd, now, len, MSG_DONTWAIT, sa, sa_len);
|
||||
}
|
||||
|
||||
static int cb(int UNUSED(type))
|
||||
{
|
||||
int sd = STDIN_FILENO;
|
||||
char buf[BUFSIZ];
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len))
|
||||
return -1; /* On error, close connection. */
|
||||
|
||||
return send_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, sa_len);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = "time", /* Must match the inetd /etc/services entry */
|
||||
.name = NAME, /* Must match the inetd /etc/services entry */
|
||||
.inetd = {
|
||||
.cmd = rfc868
|
||||
.cmd = cb
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,7 +110,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
+5
-4
@@ -27,17 +27,16 @@
|
||||
#include <unistd.h> /* read() */
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "../tty.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void watcher(void *arg, int fd, int events);
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.io = {
|
||||
.cb = watcher,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
@@ -51,7 +50,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)
|
||||
_e("Failed starting TTY watcher: %s", strerror(errno));
|
||||
_pe("Failed starting TTY watcher");
|
||||
}
|
||||
|
||||
static void watcher(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
@@ -90,12 +89,14 @@ PLUGIN_INIT(plugin_init)
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
if (plugin.io.fd)
|
||||
close(plugin.io.fd);
|
||||
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
+1
-2
@@ -25,12 +25,12 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h> /* gettimeofday() */
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
@@ -94,7 +94,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
@@ -37,6 +37,8 @@ static void setup(void *UNUSED(arg))
|
||||
#endif
|
||||
char line[LINE_SIZE];
|
||||
|
||||
umask(0);
|
||||
|
||||
makedir("/var/run/console", 01777);
|
||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
||||
touch(line);
|
||||
@@ -51,6 +53,11 @@ static void setup(void *UNUSED(arg))
|
||||
|
||||
makedir("/tmp/.X11-unix", 01777);
|
||||
makedir("/tmp/.ICE-unix", 01777);
|
||||
|
||||
if (fexist("/sbin/restorecon"))
|
||||
run("/sbin/restorecon /tmp/.ICE-unix /tmp/.X11-unix");
|
||||
|
||||
umask(022);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
@@ -73,7 +80,6 @@ PLUGIN_EXIT(plugin_exit)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -28,22 +28,28 @@
|
||||
#include "svc.h"
|
||||
#include "plugin.h"
|
||||
|
||||
uev_ctx_t *ctx; /* Main loop context */
|
||||
/*
|
||||
* Main loop context
|
||||
*/
|
||||
uev_ctx_t *ctx;
|
||||
|
||||
int api_init (uev_ctx_t *ctx);
|
||||
int api_exit (void);
|
||||
|
||||
int client (int argc, char *argv[]);
|
||||
|
||||
void service_bootstrap(void);
|
||||
void service_monitor (pid_t lost);
|
||||
|
||||
void plugin_run_hook (hook_point_t no, void *arg);
|
||||
void plugin_run_hooks (hook_point_t no);
|
||||
int plugin_load_all (uev_ctx_t *ctx, char *path);
|
||||
|
||||
int plugin_init (uev_ctx_t *ctx, char *path);
|
||||
void plugin_exit (void);
|
||||
|
||||
#endif /* FINIT_PRIVATE_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -1,648 +0,0 @@
|
||||
/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */
|
||||
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues, and circular queues.
|
||||
*
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
|
||||
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
|
||||
#else
|
||||
#define _Q_INVALIDATE(a)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_END(head) NULL
|
||||
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = SLIST_FIRST(head); \
|
||||
(var) != SLIST_END(head); \
|
||||
(var) = SLIST_NEXT(var, field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST(head); \
|
||||
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
\
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List access methods
|
||||
*/
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_END(head) NULL
|
||||
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for((var) = LIST_FIRST(head); \
|
||||
(var)!= LIST_END(head); \
|
||||
(var) = LIST_NEXT(var, field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST(head); \
|
||||
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_END(head) NULL
|
||||
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) != SIMPLEQ_END(head); \
|
||||
(var) = SIMPLEQ_NEXT(var, field))
|
||||
|
||||
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
|
||||
== NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* XOR Simple queue definitions.
|
||||
*/
|
||||
#define XSIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqx_first; /* first element */ \
|
||||
struct type **sqx_last; /* addr of last next element */ \
|
||||
unsigned long sqx_cookie; \
|
||||
}
|
||||
|
||||
#define XSIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqx_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* XOR Simple queue access methods.
|
||||
*/
|
||||
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
|
||||
(unsigned long)(ptr)))
|
||||
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
|
||||
#define XSIMPLEQ_END(head) NULL
|
||||
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
|
||||
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
|
||||
|
||||
|
||||
#define XSIMPLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) != XSIMPLEQ_END(head); \
|
||||
(var) = XSIMPLEQ_NEXT(head, var, field))
|
||||
|
||||
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* XOR Simple queue functions.
|
||||
*/
|
||||
#define XSIMPLEQ_INIT(head) do { \
|
||||
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (head)->sqx_first) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
|
||||
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
|
||||
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
|
||||
(elm)->field.sqx_next)->field.sqx_next) \
|
||||
== XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = \
|
||||
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* tail queue access methods
|
||||
*/
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
/* XXX */
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
#define TAILQ_EMPTY(head) \
|
||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_PREV(var, headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
*(elm2)->field.tqe_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue access methods
|
||||
*/
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
#define CIRCLEQ_END(head) ((void *)(head))
|
||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||
#define CIRCLEQ_EMPTY(head) \
|
||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for((var) = CIRCLEQ_FIRST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_NEXT(var, field))
|
||||
|
||||
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = CIRCLEQ_FIRST(head); \
|
||||
(var) != CIRCLEQ_END(head) && \
|
||||
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||
for((var) = CIRCLEQ_LAST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_PREV(var, field))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = CIRCLEQ_LAST(head, headname); \
|
||||
(var) != CIRCLEQ_END(head) && \
|
||||
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
@@ -1,6 +1,6 @@
|
||||
/* reboot for finit
|
||||
/* reboot, halt, poweroff, shutdown, suspend for finit
|
||||
*
|
||||
* Copyright (c) 2009-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2009-2016 Joachim Nilsson <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,16 +21,185 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/reboot.h>
|
||||
#include "util.h"
|
||||
|
||||
int main (void)
|
||||
#ifndef RB_SW_SUSPEND /* Since Linux 2.5.18, but not in GLIBC until 2.19 */
|
||||
#define RB_SW_SUSPEND 0xd000fce2
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CMD_UNKNOWN = -1,
|
||||
CMD_REBOOT = 0,
|
||||
CMD_HALT,
|
||||
CMD_POWEROFF,
|
||||
CMD_SUSPEND,
|
||||
} cmd_t;
|
||||
|
||||
static cmd_t cmd = CMD_UNKNOWN;
|
||||
static char *msg = NULL;
|
||||
|
||||
static void translate(void)
|
||||
{
|
||||
return !!kill(1, SIGINT);
|
||||
if (cmd == CMD_UNKNOWN) {
|
||||
if (!strcmp(prognm, "halt") || !strcmp(prognm, "shutdown"))
|
||||
cmd = CMD_HALT;
|
||||
else if (!strcmp(prognm, "poweroff"))
|
||||
cmd = CMD_POWEROFF;
|
||||
else if (!strcmp(prognm, "suspend"))
|
||||
cmd = CMD_SUSPEND;
|
||||
else
|
||||
cmd = CMD_REBOOT;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_REBOOT:
|
||||
msg = "reboot";
|
||||
break;
|
||||
|
||||
case CMD_HALT:
|
||||
msg = "halt";
|
||||
break;
|
||||
|
||||
case CMD_POWEROFF:
|
||||
msg = "power-off";
|
||||
break;
|
||||
|
||||
case CMD_SUSPEND:
|
||||
msg = "suspend";
|
||||
break;
|
||||
|
||||
case CMD_UNKNOWN:
|
||||
cmd = CMD_REBOOT;
|
||||
msg = "reboot";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help This help text\n"
|
||||
" -f, --force Force unsafe %s now, do not contact the init system.\n"
|
||||
" --halt Halt system, regardless of how the command is called.\n"
|
||||
" -p, --poweroff Power-off system, regardless of how the command is called.\n"
|
||||
" --reboot Reboot system, regardless of how the command is called.\n"
|
||||
"\n", prognm, msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c, force = 0;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"force", 0, NULL, 'f'},
|
||||
{"halt", 0, NULL, 'H'},
|
||||
{"poweroff", 0, NULL, 'p'},
|
||||
{"reboot", 0, NULL, 'r'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
/* Initial command taken from program name */
|
||||
progname(argv[0]);
|
||||
translate();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h?fHpr", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
cmd = CMD_HALT;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
cmd = CMD_POWEROFF;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
cmd = CMD_REBOOT;
|
||||
break;
|
||||
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for any overrides */
|
||||
translate();
|
||||
|
||||
if (force) {
|
||||
switch (cmd) {
|
||||
case CMD_REBOOT:
|
||||
c = reboot(RB_AUTOBOOT);
|
||||
break;
|
||||
|
||||
case CMD_HALT:
|
||||
c = reboot(RB_HALT_SYSTEM);
|
||||
break;
|
||||
|
||||
case CMD_POWEROFF:
|
||||
c = reboot(RB_POWER_OFF);
|
||||
break;
|
||||
|
||||
case CMD_SUSPEND:
|
||||
c = reboot(RB_SW_SUSPEND);
|
||||
break;
|
||||
|
||||
case CMD_UNKNOWN:
|
||||
errx(1, "Invalid command");
|
||||
break;
|
||||
}
|
||||
|
||||
if (c)
|
||||
warn("Failed forced %s", msg);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_REBOOT:
|
||||
c = kill(1, SIGTERM);
|
||||
break;
|
||||
|
||||
case CMD_HALT:
|
||||
c = kill(1, SIGUSR1);
|
||||
break;
|
||||
|
||||
case CMD_POWEROFF:
|
||||
c = kill(1, SIGUSR2);
|
||||
break;
|
||||
|
||||
case CMD_SUSPEND:
|
||||
case CMD_UNKNOWN:
|
||||
errx(1, "Invalid command");
|
||||
break;
|
||||
}
|
||||
|
||||
if (c)
|
||||
err(1, "Failed signalling init to %s", msg);
|
||||
else
|
||||
sleep(2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -30,21 +30,17 @@
|
||||
void service_runlevel (int newlevel);
|
||||
int service_register (int type, char *line, time_t mtime, char *username);
|
||||
void service_unregister (svc_t *svc);
|
||||
svc_cmd_t service_enabled (svc_t *svc, int event, void *arg);
|
||||
int service_enabled (svc_t *svc);
|
||||
|
||||
int service_start (svc_t *svc);
|
||||
int service_stop (svc_t *svc, int state);
|
||||
void service_start_dynamic (void);
|
||||
void service_stop_dynamic (void);
|
||||
int service_restart (svc_t *svc);
|
||||
int service_reload (svc_t *svc);
|
||||
void service_reload_dynamic (void);
|
||||
|
||||
void service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
|
||||
#endif /* FINIT_SERVICE_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -21,74 +21,197 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* The standard SysV init only responds to the following signals:
|
||||
* SIGHUP
|
||||
* Has the same effect as telinit q.
|
||||
* Finit is often used on embedded and small Linux systems with BusyBox.
|
||||
* For compatibility with its existing toolset (halt, poweroff, reboot),
|
||||
* the following signals have been adopted:
|
||||
*
|
||||
* SIGUSR1
|
||||
* On receipt of this signals, init closes and re-opens its control
|
||||
* fifo, /dev/initctl. Useful for bootscripts when /dev is remounted.
|
||||
* SIGHUP
|
||||
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
|
||||
*
|
||||
* SIGINT
|
||||
* Normally the kernel sends this signal to init when CTRL-ALT-DEL is
|
||||
* pressed. It activates the ctrlaltdel action.
|
||||
* SIGUSR1
|
||||
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
||||
* processes, and unmounts all file systems. Then tells kernel to
|
||||
* halt. Most people these days want SIGUSR2 though.
|
||||
*
|
||||
* SIGWINCH
|
||||
* The kernel sends this signal when the KeyboardSignal key is hit.
|
||||
* It activates the kbrequest action.
|
||||
* (SysV init and systemd use this to re-open their FIFO/D-Bus.)
|
||||
*
|
||||
* SIGUSR2
|
||||
* Like SIGUSR1, but tell kernel to power-off the system, if ACPI
|
||||
* or similar exists to actually do this. If the kernel fails
|
||||
* power-off, Finit falls back to halt.
|
||||
*
|
||||
* (SysV init N/A, systemd dumps its internal state to log.)
|
||||
*
|
||||
* SIGTERM
|
||||
* Like SIGUSR1, but tell kernel to reboot the system when done.
|
||||
*
|
||||
* (SysV init N/A, systemd rexecutes itself.)
|
||||
*
|
||||
* SIGINT
|
||||
* Sent from kernel when the CTRL-ALT-DEL key combo is pressed.
|
||||
* SysV init and systemd default to reboot with `shutdown -r`.
|
||||
* Finit currently forwards this to SIGTERM.
|
||||
*
|
||||
* SIGWINCH
|
||||
* Sent from kernel when the KeyboardSignal key is pressed. Some
|
||||
* users bind keys, see loadkeys(1), to create consoles on demand.
|
||||
* Finit currently ignores this signal.
|
||||
*
|
||||
* SIGPWR
|
||||
* Sent from a power daemon, like powstatd(8), on changes to the
|
||||
* UPS status. Traditionally SysV init read /etc/powerstatus and
|
||||
* acted on "OK", "FAIL", or "LOW" and then removed the file.
|
||||
* Finit currently forwards this to SIGUSR2.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <string.h> /* strerror() */
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "config.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "service.h"
|
||||
#include "libite/lite.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
/*
|
||||
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
|
||||
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
|
||||
* runlevel 6 over the /dev/initctl FIFO. See plugins/initctl.c
|
||||
*/
|
||||
shutop_t halt = SHUT_DEFAULT;
|
||||
|
||||
static int stopped = 0;
|
||||
static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
|
||||
static uev_t sigchld_watcher, sigsegv_watcher;
|
||||
static uev_t sigterm_watcher, sigusr1_watcher, sigusr2_watcher;
|
||||
static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
|
||||
static uev_t sigchld_watcher;
|
||||
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
|
||||
|
||||
void mdadm_wait(void);
|
||||
void unmount_tmpfs(void);
|
||||
void unmount_regular(void);
|
||||
|
||||
void do_shutdown(int sig)
|
||||
/*
|
||||
* Kernel threads have no cmdline so fgets() returns NULL for them. We
|
||||
* also skip "special" processes, e.g. mdadm/mdmon or watchdogd that must
|
||||
* not be stopped here, for various reasons.
|
||||
*
|
||||
* https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
|
||||
*/
|
||||
void do_kill(int signo)
|
||||
{
|
||||
DIR *dirp;
|
||||
|
||||
dirp = opendir("/proc");
|
||||
if (dirp) {
|
||||
struct dirent *d;
|
||||
|
||||
while ((d = readdir(dirp))) {
|
||||
int pid;
|
||||
FILE *fp;
|
||||
char file[80] = "";
|
||||
|
||||
if (d->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
pid = atoi(d->d_name);
|
||||
if (!pid)
|
||||
continue;
|
||||
|
||||
snprintf(file, sizeof(file), "/proc/%s/cmdline", d->d_name);
|
||||
fp = fopen(file, "r");
|
||||
if (!fp)
|
||||
continue;
|
||||
|
||||
if (fgets(file, sizeof(file), fp)) {
|
||||
if (file[0] != '@')
|
||||
kill(pid, signo);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
closedir(dirp);
|
||||
}
|
||||
}
|
||||
|
||||
void do_shutdown(shutop_t op)
|
||||
{
|
||||
touch(SYNC_SHUTDOWN);
|
||||
|
||||
if (sdown)
|
||||
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
|
||||
|
||||
/* Call all shutdown hooks before rebooting... */
|
||||
plugin_run_hooks(HOOK_SHUTDOWN);
|
||||
/* Update UTMP db */
|
||||
utmp_set_halt();
|
||||
|
||||
/* Here is where we signal watchdogd to do a forced reset for us */
|
||||
_d("Sending SIGTERM to all processes.");
|
||||
kill(-1, SIGTERM);
|
||||
|
||||
/* Wait for WDT to timeout, should be no more than ~1 sec. */
|
||||
/*
|
||||
* Tell all remaining non-monitored processes to exit, give them
|
||||
* some time to exit gracefully, 2 sec is customary.
|
||||
*/
|
||||
do_kill(SIGTERM);
|
||||
do_sleep(2);
|
||||
do_kill(SIGKILL);
|
||||
|
||||
_d("Sending SIGKILL to remaining processes.");
|
||||
kill(-1, SIGKILL);
|
||||
/* Exit plugins and API gracefully */
|
||||
plugin_exit();
|
||||
api_exit();
|
||||
|
||||
/* Reap 'em */
|
||||
while (waitpid(-1, NULL, WNOHANG) > 0)
|
||||
;
|
||||
|
||||
/* Close all local non-console descriptors */
|
||||
for (int fd = 3; fd < 128; fd++)
|
||||
close(fd);
|
||||
|
||||
if (vfork()) {
|
||||
/*
|
||||
* Put PID 1 aside and let child perform reboot/halt
|
||||
* kernel may exit child and we don't want to exit PID 1
|
||||
* ... causing "aiii killing init" during reboot ...
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unmount any tmpfs before unmounting swap ... */
|
||||
unmount_tmpfs();
|
||||
run("/sbin/swapoff -e -a");
|
||||
|
||||
/* ... unmount remaining regular file systems. */
|
||||
unmount_regular();
|
||||
|
||||
/* We sit on / so we must remount it ro, try all the things! */
|
||||
sync();
|
||||
sync();
|
||||
_d("Unmounting file systems, remounting / read-only.");
|
||||
run("/bin/umount -fa 2>/dev/null");
|
||||
run("/bin/mount -n -o remount,ro / 2>/dev/null");
|
||||
run("/sbin/swapoff -ea");
|
||||
run("/bin/mount -n -o remount,ro -t dummytype dummydev /");
|
||||
run("/bin/mount -n -o remount,ro dummydev /");
|
||||
run("/bin/mount -n -o remount,ro /");
|
||||
|
||||
_d("%s.", sig == SIGINT || sig == SIGUSR1 ? "Rebooting" : "Halting");
|
||||
if (sig == SIGINT || sig == SIGUSR1)
|
||||
/* Call mdadm to mark any RAID array(s) as clean before halting. */
|
||||
mdadm_wait();
|
||||
|
||||
/* Wait here until the WDT reboots, or timeout */
|
||||
if (wdogpid) {
|
||||
int timeout = 10;
|
||||
|
||||
print(kill(wdogpid, SIGPWR) == 1, "Pending watchdog reboot");
|
||||
while (timeout--)
|
||||
do_sleep(1);
|
||||
}
|
||||
|
||||
_d("%s", op == SHUT_REBOOT ? "Rebooting" : "Halting");
|
||||
if (op == SHUT_REBOOT)
|
||||
reboot(RB_AUTOBOOT);
|
||||
|
||||
reboot(RB_POWER_OFF);
|
||||
if (op == SHUT_OFF)
|
||||
reboot(RB_POWER_OFF);
|
||||
|
||||
/* Also fallback if any of the other two fails */
|
||||
reboot(RB_HALT_SYSTEM);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -96,16 +219,49 @@ void do_shutdown(int sig)
|
||||
*/
|
||||
static void sighup_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/*
|
||||
* Shut down on INT PWR
|
||||
* SIGINT: Should generate <sys/key/ctrlaltdel> condition, for now reboot
|
||||
*/
|
||||
static void sigint_cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
static void sigint_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
do_shutdown(w->signo);
|
||||
_d("...");
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(6);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGUSR1: BusyBox style halt
|
||||
*/
|
||||
static void sigusr1_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
halt = SHUT_HALT;
|
||||
service_runlevel(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGUSR2: BusyBox style poweroff
|
||||
*/
|
||||
static void sigusr2_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
halt = SHUT_OFF;
|
||||
service_runlevel(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGTERM: BusyBox style reboot
|
||||
*/
|
||||
static void sigterm_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(6);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -125,15 +281,6 @@ static void sigchld_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
} while (pid > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGSEGV: mostly if service callbacks segfault
|
||||
*/
|
||||
static void sigsegv_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_e("PID %d caused a segfault!\n", getpid());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGSTOP/SIGTSTP: Paused by user or netflash
|
||||
*/
|
||||
@@ -196,6 +343,9 @@ void sig_unblock(void)
|
||||
sigaddset(&nmask, SIGSTOP);
|
||||
sigaddset(&nmask, SIGTSTP);
|
||||
sigaddset(&nmask, SIGCONT);
|
||||
sigaddset(&nmask, SIGTERM);
|
||||
sigaddset(&nmask, SIGUSR1);
|
||||
sigaddset(&nmask, SIGUSR2);
|
||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
@@ -220,12 +370,10 @@ void sig_setup(uev_ctx_t *ctx)
|
||||
uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT);
|
||||
uev_signal_init(ctx, &sigpwr_watcher, sigint_cb, NULL, SIGPWR);
|
||||
|
||||
/* Ignore SIGUSR1/2 for now, only BusyBox init implements them as reboot+halt. */
|
||||
// uev_signal_init(&ctx, &sigusr1_watcher, reopen_initctl_cb, NULL, SIGUSR1);
|
||||
// uev_signal_init(&ctx, &sigusr2_watcher, pwrdwn_cb, NULL, SIGUSR2);
|
||||
|
||||
/* Init must ignore SIGTERM. May otherwise get false SIGTERM in forked children! */
|
||||
IGNSIG(sa, SIGTERM, 0);
|
||||
/* BusyBox init style signals for halt, power-off and reboot. */
|
||||
uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1);
|
||||
uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2);
|
||||
uev_signal_init(ctx, &sigterm_watcher, sigterm_cb, NULL, SIGTERM);
|
||||
|
||||
/* Some C APIs may need SIGALRM for implementing timers. */
|
||||
IGNSIG(sa, SIGALRM, 0);
|
||||
@@ -236,22 +384,16 @@ void sig_setup(uev_ctx_t *ctx)
|
||||
/* After initial bootstrap of Finit we call the service monitor to reap children */
|
||||
uev_signal_init(ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD);
|
||||
|
||||
/* Trap SIGSEGV in case service callbacks crash */
|
||||
uev_signal_init(ctx, &sigsegv_watcher, sigsegv_cb, NULL, SIGSEGV);
|
||||
|
||||
/* Stopping init is a bit tricky. */
|
||||
uev_signal_init(ctx, &sigstop_watcher, sigstop_cb, NULL, SIGSTOP);
|
||||
uev_signal_init(ctx, &sigtstp_watcher, sigstop_cb, NULL, SIGTSTP);
|
||||
uev_signal_init(ctx, &sigcont_watcher, sigcont_cb, NULL, SIGCONT);
|
||||
|
||||
/* Disable CTRL-ALT-DELETE from kernel, we handle shutdown gracefully with SIGINT, above */
|
||||
reboot(RB_DISABLE_CAD);
|
||||
setsid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define FINIT_SIG_H_
|
||||
|
||||
#include <signal.h>
|
||||
#include "libuev/uev.h"
|
||||
#include <uev/uev.h>
|
||||
|
||||
#define SYNC_SHUTDOWN "/var/lock/finit.shutdown"
|
||||
#define SYNC_STOPPED "/var/lock/finit.stopped"
|
||||
@@ -55,7 +55,20 @@
|
||||
sigaction(sig, &sa, NULL); \
|
||||
} while (0)
|
||||
|
||||
void do_shutdown (int sig);
|
||||
/*
|
||||
* For old-style /dev/initctl shutdown we default to halt
|
||||
*/
|
||||
#define SHUT_DEFAULT SHUT_HALT
|
||||
|
||||
typedef enum {
|
||||
SHUT_OFF,
|
||||
SHUT_HALT,
|
||||
SHUT_REBOOT
|
||||
} shutop_t;
|
||||
|
||||
extern shutop_t halt;
|
||||
|
||||
void do_shutdown (shutop_t op);
|
||||
int sig_stopped (void);
|
||||
void sig_init (void);
|
||||
void sig_unblock (void);
|
||||
@@ -65,7 +78,6 @@ void sig_setup (uev_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
/* Finit state machine
|
||||
*
|
||||
* Copyright (c) 2016 Jonas Johansson <jonas.johansson@westermo.se>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "tty.h"
|
||||
#include "sm.h"
|
||||
|
||||
sm_t sm;
|
||||
|
||||
void sm_init(sm_t *sm)
|
||||
{
|
||||
sm->state = SM_BOOTSTRAP_STATE;
|
||||
sm->newlevel = -1;
|
||||
sm->reload = 0;
|
||||
sm->in_teardown = 0;
|
||||
}
|
||||
|
||||
static char *sm_status(sm_t *sm)
|
||||
{
|
||||
switch (sm->state) {
|
||||
case SM_BOOTSTRAP_STATE:
|
||||
return "bootstrap";
|
||||
|
||||
case SM_RUNNING_STATE:
|
||||
return "running";
|
||||
|
||||
case SM_RUNLEVEL_CHANGE_STATE:
|
||||
return "runlevel/change";
|
||||
|
||||
case SM_RUNLEVEL_WAIT_STATE:
|
||||
return "runlevel/wait";
|
||||
|
||||
case SM_RELOAD_CHANGE_STATE:
|
||||
return "reload/change";
|
||||
|
||||
case SM_RELOAD_WAIT_STATE:
|
||||
return "reload/wait";
|
||||
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void sm_set_runlevel(sm_t *sm, int newlevel)
|
||||
{
|
||||
sm->newlevel = newlevel;
|
||||
}
|
||||
|
||||
void sm_set_reload(sm_t *sm)
|
||||
{
|
||||
sm->reload = 1;
|
||||
}
|
||||
|
||||
int sm_is_in_teardown(sm_t *sm)
|
||||
{
|
||||
return sm->in_teardown;
|
||||
}
|
||||
|
||||
void sm_step(sm_t *sm)
|
||||
{
|
||||
sm_state_t old_state;
|
||||
|
||||
restart:
|
||||
old_state = sm->state;
|
||||
|
||||
_d("state: %s", sm_status(sm));
|
||||
|
||||
switch (sm->state) {
|
||||
case SM_BOOTSTRAP_STATE:
|
||||
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
||||
service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE);
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
|
||||
case SM_RUNNING_STATE:
|
||||
/* runlevel changed? */
|
||||
if (sm->newlevel >= 0 && sm->newlevel <= 9) {
|
||||
if (runlevel == sm->newlevel) {
|
||||
sm->newlevel = -1;
|
||||
break;
|
||||
}
|
||||
sm->state = SM_RUNLEVEL_CHANGE_STATE;
|
||||
break;
|
||||
}
|
||||
/* reload ? */
|
||||
if (sm->reload) {
|
||||
sm->reload = 0;
|
||||
sm->state = SM_RELOAD_CHANGE_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case SM_RUNLEVEL_CHANGE_STATE:
|
||||
prevlevel = runlevel;
|
||||
runlevel = sm->newlevel;
|
||||
sm->newlevel = -1;
|
||||
|
||||
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
|
||||
runlevel_set(prevlevel, runlevel);
|
||||
|
||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
|
||||
touch("/etc/nologin");
|
||||
else
|
||||
erase("/etc/nologin");
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
conf_reload_dynamic();
|
||||
|
||||
_d("Stopping services services not allowed in new runlevel ...");
|
||||
sm->in_teardown = 1;
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
sm->state = SM_RUNLEVEL_WAIT_STATE;
|
||||
break;
|
||||
|
||||
case SM_RUNLEVEL_WAIT_STATE:
|
||||
/* Need to wait for any services to stop? If so, exit early
|
||||
* and perform second stage from service_monitor later. */
|
||||
if (!svc_stop_completed())
|
||||
break;
|
||||
|
||||
/* Prev runlevel services stopped, call hooks before starting new runlevel ... */
|
||||
_d("All services have been stoppped, calling runlevel change hooks ...");
|
||||
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
||||
|
||||
_d("Starting services services new to this runlevel ...");
|
||||
sm->in_teardown = 0;
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
|
||||
/*
|
||||
* "I've seen things you people wouldn't believe. Attack ships on fire off
|
||||
* the shoulder of Orion. I watched C-beams glitter in the dark near the
|
||||
* Tannhäuser Gate. All those .. moments .. will be lost in time, like
|
||||
* tears ... in ... rain."
|
||||
*/
|
||||
if (runlevel == 0 || runlevel == 6) {
|
||||
do_shutdown(halt);
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* No TTYs run at bootstrap, they have a delayed start. */
|
||||
if (prevlevel > 0)
|
||||
tty_runlevel(runlevel);
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
|
||||
case SM_RELOAD_CHANGE_STATE:
|
||||
/* First reload all *.conf in /etc/finit.d/ */
|
||||
conf_reload_dynamic();
|
||||
|
||||
/* Then, mark all affected service conditions as in-flux and
|
||||
* let all affected services move to WAITING/HALTED */
|
||||
_d("Stopping services services not allowed after reconf ...");
|
||||
sm->in_teardown = 1;
|
||||
cond_reload();
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
|
||||
sm->state = SM_RELOAD_WAIT_STATE;
|
||||
break;
|
||||
|
||||
case SM_RELOAD_WAIT_STATE:
|
||||
/* Need to wait for any services to stop? If so, exit early
|
||||
* and perform second stage from service_monitor later. */
|
||||
if (!svc_stop_completed())
|
||||
break;
|
||||
|
||||
sm->in_teardown = 0;
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
|
||||
_d("Starting services after reconf ...");
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
|
||||
_d("Calling reconf hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_RECONF);
|
||||
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
_d("Reconfiguration done");
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sm->state != old_state) {
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Finit state machine
|
||||
*
|
||||
* Copyright (c) 2016 Jonas Johansson <jonas.johansson@westermo.se>
|
||||
*
|
||||
* 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_SM_H_
|
||||
#define FINIT_SM_H_
|
||||
|
||||
typedef enum {
|
||||
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
|
||||
SM_RUNNING_STATE, /* Normal state, services running */
|
||||
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occured */
|
||||
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */
|
||||
SM_RELOAD_CHANGE_STATE, /* A reload event has occured */
|
||||
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */
|
||||
} sm_state_t;
|
||||
|
||||
typedef struct sm {
|
||||
sm_state_t state; /* Running, Changed, Waiting, ... */
|
||||
int newlevel; /* Set on runlevel change to new runlevel, -1 if not change */
|
||||
int reload; /* Set on reload event, else 0 */
|
||||
int in_teardown; /* Set when waiting for all processes to be halted */
|
||||
} sm_t;
|
||||
|
||||
extern sm_t sm;
|
||||
|
||||
void sm_init(sm_t *sm);
|
||||
void sm_step(sm_t *sm);
|
||||
void sm_set_runlevel(sm_t *sm, int newlevel);
|
||||
void sm_set_reload(sm_t *sm);
|
||||
int sm_is_in_teardown(sm_t *sm);
|
||||
|
||||
#endif /* FINIT_SM_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "libite/lite.h"
|
||||
#include <strings.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
@@ -74,12 +74,22 @@ svc_t *svc_new(char *cmd, int id, int type)
|
||||
svc_t *svc = &list[i];
|
||||
|
||||
if (svc->type == SVC_TYPE_FREE) {
|
||||
char *desc;
|
||||
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
svc->type = type;
|
||||
svc->job = job;
|
||||
svc->id = id;
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
|
||||
/* Default description, if missing */
|
||||
desc = rindex(cmd, '/');
|
||||
if (desc)
|
||||
desc++;
|
||||
else
|
||||
desc = cmd;
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
@@ -250,6 +260,44 @@ void svc_foreach_dynamic(void (*cb)(svc_t *))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_foreach_type - Run a callback for each matching type
|
||||
* @types: Mask of service types
|
||||
* @cb: Callback to run for each matching type
|
||||
*/
|
||||
void svc_foreach_type(int types, void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!(svc->type & types))
|
||||
continue;
|
||||
|
||||
cb(svc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_stop_completed - Have all stopped services been collected?
|
||||
*
|
||||
* Returns:
|
||||
* 1, if all stopped services have been collected. 0 otherwise.
|
||||
*/
|
||||
int svc_stop_completed(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
|
||||
if (svc->state == SVC_STOPPING_STATE)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_find - Find a service object by its full path name
|
||||
* @cmd: Full path name, e.g., /sbin/syslogd
|
||||
@@ -352,17 +400,27 @@ void svc_mark_dynamic(void)
|
||||
svc_t *svc = svc_dynamic_iterator(1);
|
||||
|
||||
while (svc) {
|
||||
svc->dirty = -1;
|
||||
*((int *)&svc->dirty) = -1;
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
|
||||
void svc_mark_dirty(svc_t *svc)
|
||||
{
|
||||
*((int *)&svc->dirty) = 1;
|
||||
}
|
||||
|
||||
void svc_mark_clean(svc_t *svc)
|
||||
{
|
||||
*((int *)&svc->dirty) = 0;
|
||||
}
|
||||
|
||||
void svc_check_dirty(svc_t *svc, time_t mtime)
|
||||
{
|
||||
if (svc->mtime != mtime)
|
||||
svc->dirty = 1;
|
||||
svc_mark_dirty(svc);
|
||||
else
|
||||
svc->dirty = 0;
|
||||
svc_mark_clean(svc);
|
||||
svc->mtime = mtime;
|
||||
}
|
||||
|
||||
@@ -381,7 +439,6 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
|
||||
if (svc->dirty == -1 && cb)
|
||||
cb(svc);
|
||||
|
||||
svc->dirty = 0;
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
@@ -406,36 +463,66 @@ int svc_clean_bootstrap(svc_t *svc)
|
||||
|
||||
char *svc_status(svc_t *svc)
|
||||
{
|
||||
if (!svc_in_runlevel(svc, runlevel))
|
||||
return "stopped";
|
||||
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
switch (svc->block) {
|
||||
case SVC_BLOCK_NONE:
|
||||
return "halted";
|
||||
|
||||
case SVC_BLOCK_MISSING:
|
||||
return "missing";
|
||||
|
||||
case SVC_BLOCK_CRASHING:
|
||||
return "crashed";
|
||||
|
||||
case SVC_BLOCK_USER:
|
||||
return "blocked";
|
||||
|
||||
case SVC_BLOCK_BUSY:
|
||||
return "busy";
|
||||
|
||||
case SVC_BLOCK_RESTARTING:
|
||||
return "restart";
|
||||
}
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
return "done";
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_INETD_CONN:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_TASK:
|
||||
return "active";
|
||||
|
||||
default:
|
||||
return "stopping";
|
||||
}
|
||||
|
||||
case SVC_WAITING_STATE:
|
||||
return "waiting";
|
||||
|
||||
case SVC_PAUSED_STATE:
|
||||
return "paused";
|
||||
|
||||
case SVC_CONDHALT_STATE:
|
||||
return "nocond";
|
||||
|
||||
case SVC_RESTART_STATE:
|
||||
return "restart";
|
||||
|
||||
case SVC_RELOAD_STATE:
|
||||
return "reload";
|
||||
case SVC_READY_STATE:
|
||||
return "ready";
|
||||
|
||||
case SVC_RUNNING_STATE:
|
||||
if (svc->pid)
|
||||
return "running";
|
||||
/* Fall through */
|
||||
return "running";
|
||||
|
||||
case SVC_HALTED_STATE:
|
||||
default:
|
||||
break;
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
return "halted";
|
||||
const char *svc_dirtystr(svc_t *svc)
|
||||
{
|
||||
if (svc_is_removed(svc))
|
||||
return "removed";
|
||||
if (svc_is_updated(svc))
|
||||
return "updated";
|
||||
if (svc_is_changed(svc))
|
||||
return "UNKNOWN";
|
||||
|
||||
return "clean";
|
||||
}
|
||||
|
||||
/* Same base service, return unique ID */
|
||||
@@ -478,7 +565,6 @@ int svc_is_unique(svc_t *svc)
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
@@ -28,34 +28,42 @@
|
||||
#include <sys/ipc.h> /* IPC_CREAT */
|
||||
#include <sys/shm.h> /* shmat() */
|
||||
#include <sys/types.h> /* pid_t */
|
||||
#include "libite/lite.h"
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "inetd.h"
|
||||
#include "helpers.h"
|
||||
|
||||
typedef enum {
|
||||
SVC_STOP = 0, /* Disabled */
|
||||
SVC_START, /* Enabled */
|
||||
SVC_RELOAD /* Enabled, needs restart */
|
||||
} svc_cmd_t;
|
||||
typedef int svc_cmd_t;
|
||||
|
||||
typedef enum {
|
||||
SVC_TYPE_FREE = 0, /* Free to allocate */
|
||||
SVC_TYPE_SERVICE, /* Monitored, will be respawned */
|
||||
SVC_TYPE_TASK, /* One-shot, runs in parallell */
|
||||
SVC_TYPE_RUN, /* Like task, but wait for completion */
|
||||
SVC_TYPE_INETD /* Classic inetd service */
|
||||
SVC_TYPE_FREE = 0, /* Free to allocate */
|
||||
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
|
||||
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */
|
||||
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
|
||||
SVC_TYPE_INETD = 8, /* Classic inetd service */
|
||||
SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */
|
||||
} svc_type_t;
|
||||
|
||||
#define SVC_TYPE_ANY (-1)
|
||||
|
||||
typedef enum {
|
||||
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
||||
SVC_WAITING_STATE, /* Waiting for connection (inetd service) */
|
||||
SVC_PAUSED_STATE, /* Stopped/Paused by user started on reload */
|
||||
SVC_CONDHALT_STATE, /* Not allowed to run atm. event/state lost */
|
||||
SVC_RESTART_STATE, /* Restarting service waiting to be stopped */
|
||||
SVC_RELOAD_STATE, /* Reloading services, after .conf changed */
|
||||
SVC_RUNNING_STATE, /* Currently running service, see svc->pid */
|
||||
SVC_DONE_STATE, /* Task/Run job has been run */
|
||||
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
||||
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
||||
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
||||
SVC_RUNNING_STATE, /* Process running */
|
||||
} svc_state_t;
|
||||
|
||||
typedef enum {
|
||||
SVC_BLOCK_NONE = 0,
|
||||
SVC_BLOCK_MISSING,
|
||||
SVC_BLOCK_CRASHING,
|
||||
SVC_BLOCK_USER,
|
||||
SVC_BLOCK_BUSY,
|
||||
SVC_BLOCK_RESTARTING,
|
||||
} svc_block_t;
|
||||
|
||||
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
||||
#define MAX_ARG_LEN 64
|
||||
#define MAX_STR_LEN 64
|
||||
@@ -64,29 +72,38 @@ typedef enum {
|
||||
#define MAX_NUM_SVC 64 /* Enough? */
|
||||
#define MAX_NUM_SVC_ARGS 32
|
||||
|
||||
/* Default enable for all services, can be stopped by means
|
||||
/*
|
||||
* Default enable for all services, can be stopped by means
|
||||
* of issuing an initctl call. E.g.
|
||||
* initctl <stop|start|restart> service */
|
||||
*
|
||||
* initctl <stop|start|restart> service
|
||||
*/
|
||||
typedef struct svc {
|
||||
/* Instance specifics */
|
||||
int job, id; /* JOB:ID */
|
||||
|
||||
/* Service details */
|
||||
pid_t pid;
|
||||
svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
svc_type_t type;
|
||||
time_t mtime; /* Modification time for .conf from /etc/finit.d/ */
|
||||
int dirty; /* Set if old mtime != new mtime => reloaded,
|
||||
const int dirty; /* Set if old mtime != new mtime => reloaded,
|
||||
* or -1 when marked for removal */
|
||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||
int runlevels;
|
||||
int sighup; /* This service supports SIGHUP :) */
|
||||
char events[MAX_ARG_LEN];
|
||||
svc_block_t block; /* Reason that this service is currently blocked */
|
||||
char cond[MAX_ARG_LEN];
|
||||
|
||||
/* Incremented for each restart by service monitor. */
|
||||
unsigned int restart_counter;
|
||||
const unsigned int restart_counter;
|
||||
|
||||
/* For inetd services */
|
||||
inetd_t inetd;
|
||||
int stdin_fd;
|
||||
|
||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
||||
int log;
|
||||
|
||||
/* Identity */
|
||||
char username[MAX_USER_LEN];
|
||||
@@ -97,15 +114,12 @@ typedef struct svc {
|
||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
||||
char desc[MAX_STR_LEN];
|
||||
|
||||
/* For external plugins. If @cb is set, a plugin is loaded.
|
||||
* @dynamic: Set by plugins that want dynamic events.
|
||||
* @dynamic_stop: Set by plugins that allow dyn. events to stop it as well.
|
||||
* @private: Can be used freely by plugin, e.g., to store "states".
|
||||
/*
|
||||
* Used to forcefully kill services that won't shutdown on
|
||||
* termination and to delay restarts of crashing services.
|
||||
*/
|
||||
int dynamic;
|
||||
int dynamic_stop;
|
||||
int private;
|
||||
svc_cmd_t (*cb)(struct svc *svc, int event, void *event_arg);
|
||||
uev_t timer;
|
||||
void (*timer_cb)(struct svc *svc);
|
||||
} svc_t;
|
||||
|
||||
typedef struct svc_map svc_map_t;
|
||||
@@ -141,31 +155,51 @@ svc_t *svc_job_iterator (int first, int job);
|
||||
|
||||
void svc_foreach (void (*cb)(svc_t *));
|
||||
void svc_foreach_dynamic (void (*cb)(svc_t *));
|
||||
void svc_foreach_type (int types, void (*cb)(svc_t *));
|
||||
|
||||
int svc_stop_completed (void);
|
||||
|
||||
void svc_mark_dynamic (void);
|
||||
void svc_check_dirty (svc_t *svc, time_t mtime);
|
||||
void svc_mark_dirty (svc_t *svc);
|
||||
void svc_mark_clean (svc_t *svc);
|
||||
void svc_clean_dynamic (void (*cb)(svc_t *));
|
||||
int svc_clean_bootstrap (svc_t *svc);
|
||||
|
||||
char *svc_status (svc_t *svc);
|
||||
int svc_next_id (char *cmd);
|
||||
int svc_next_id (char *cmd);
|
||||
int svc_is_unique (svc_t *svc);
|
||||
|
||||
static inline int svc_in_runlevel(svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||
|
||||
static inline int svc_is_dynamic(svc_t *svc) { return svc && 0 != svc->mtime; }
|
||||
static inline int svc_is_removed(svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed(svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated(svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
|
||||
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
|
||||
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
|
||||
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||
static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime; }
|
||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
const char *svc_dirtystr (svc_t *svc);
|
||||
|
||||
#endif /* FINIT_SVC_H_ */
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
|
||||
static inline void svc_block (svc_t *svc) { svc->block = SVC_BLOCK_USER; }
|
||||
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; }
|
||||
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; }
|
||||
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
|
||||
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
|
||||
|
||||
#endif /* FINIT_SVC_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user