mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a822a7657c | ||
|
|
e46271c1a9 | ||
|
|
1028725079 | ||
|
|
ed5ee667ae | ||
|
|
4c3e053041 | ||
|
|
a5c5a5c644 | ||
|
|
fa3a97366c | ||
|
|
5b3e3da7ff | ||
|
|
59d96f264d | ||
|
|
7027cbc439 | ||
|
|
8218fff7e5 | ||
|
|
263e23a0df | ||
|
|
1f88e1ff8a | ||
|
|
b4ccc8b008 | ||
|
|
8df2b7b6e9 | ||
|
|
8073f7cf27 | ||
|
|
e303e1f833 | ||
|
|
3ea4be9343 | ||
|
|
d8188428ed | ||
|
|
d9f1882204 | ||
|
|
4d899851cc | ||
|
|
ad8feca3e7 | ||
|
|
7569c479b8 | ||
|
|
e7db6c6ac6 | ||
|
|
f12535dc19 | ||
|
|
b19ea16e9d | ||
|
|
eb59155dad | ||
|
|
f38dfaf2dc | ||
|
|
4ada42f380 | ||
|
|
924bf041bc | ||
|
|
b1d57d6828 | ||
|
|
87f9b94b42 | ||
|
|
c8f0b97d0f | ||
|
|
f902505b5d | ||
|
|
a6b78d57c0 | ||
|
|
a33478d563 | ||
|
|
369fcf2100 | ||
|
|
b2c5e97417 | ||
|
|
8725200260 | ||
|
|
58980c72fa | ||
|
|
bcc70ec774 | ||
|
|
44579cc089 | ||
|
|
da361f460d | ||
|
|
bc2cb21005 | ||
|
|
e1169628ce | ||
|
|
48b420b757 | ||
|
|
9796809907 | ||
|
|
a3193b34a6 | ||
|
|
c0af01db6d | ||
|
|
3c1c7df420 | ||
|
|
258efd4101 | ||
|
|
5e33647f80 | ||
|
|
d2f23e281f | ||
|
|
b5e3e69553 | ||
|
|
8d85e59214 | ||
|
|
a1c8ada320 | ||
|
|
ae15988eb8 | ||
|
|
9491f18972 | ||
|
|
545f3105e2 | ||
|
|
eec166a9eb | ||
|
|
125fc661a3 | ||
|
|
6531b2f20a | ||
|
|
1f84edfb8c | ||
|
|
c354d0e7e6 | ||
|
|
98eb7cfea1 | ||
|
|
ec80ce1c9b | ||
|
|
ef05983dc8 | ||
|
|
1c04097dca | ||
|
|
36aba8952f | ||
|
|
6e07dd8dec | ||
|
|
ffa32c6674 | ||
|
|
c30039facf | ||
|
|
b13c6421b3 | ||
|
|
fcaf5d7b76 | ||
|
|
f9ddb1bdfc | ||
|
|
5701a00558 | ||
|
|
9a81bbccbb | ||
|
|
2fcd0fd899 | ||
|
|
5126017c7e | ||
|
|
d2ed03e74c | ||
|
|
525aef10dd | ||
|
|
57031071fb | ||
|
|
0bd57a7826 | ||
|
|
814ffd9259 | ||
|
|
f94db7bceb | ||
|
|
98ba5955c8 | ||
|
|
52c51f16ed | ||
|
|
3b1e652d40 | ||
|
|
d6490df9a3 | ||
|
|
0fd9aa6497 | ||
|
|
b30678bbe9 | ||
|
|
1db144c677 | ||
|
|
8b8ddc43b5 | ||
|
|
838ec0423d | ||
|
|
05a8ace030 | ||
|
|
a05f920292 | ||
|
|
d8b331d737 | ||
|
|
381a32bf8a | ||
|
|
ee9e49f412 | ||
|
|
ff2df8ce5c | ||
|
|
e595e9da5f | ||
|
|
f1807a32ca | ||
|
|
9a6ab331a6 | ||
|
|
70af5d7f8d | ||
|
|
36c59e77b4 | ||
|
|
57b167f9aa | ||
|
|
155ae15390 | ||
|
|
06a1a3c839 | ||
|
|
42f3d4dc79 | ||
|
|
8e12dba8d9 | ||
|
|
589a7ea8a5 | ||
|
|
e5732f24d3 | ||
|
|
3d0e58ebcd | ||
|
|
162b394726 | ||
|
|
c6a9fa3b98 | ||
|
|
e04c73bab8 | ||
|
|
ac0edc9b37 | ||
|
|
59731988fd | ||
|
|
d0da3b8b98 | ||
|
|
be074abce8 | ||
|
|
577622c945 | ||
|
|
5d2172eac8 | ||
|
|
276cda9a4d | ||
|
|
bafdf0007f | ||
|
|
064e94e030 | ||
|
|
c00ac21fe2 | ||
|
|
164fe12685 | ||
|
|
e7dad9a770 | ||
|
|
02e5c868ba | ||
|
|
66f472bb05 | ||
|
|
7b894fcf52 | ||
|
|
0de4fb0cbf | ||
|
|
231539cd7d | ||
|
|
195f17ae08 | ||
|
|
54ace1a6cf | ||
|
|
1f19b50846 | ||
|
|
6c58a7d774 | ||
|
|
dbcece81ea | ||
|
|
70f4d99922 | ||
|
|
ee6d524bc8 | ||
|
|
d9055c3965 | ||
|
|
03573b41ca | ||
|
|
b0c759e518 | ||
|
|
1a1f24721f | ||
|
|
66edb42e06 | ||
|
|
4e904142cb | ||
|
|
7b5d56df98 | ||
|
|
1d58f4abbe | ||
|
|
24e1d284ad | ||
|
|
720324b256 | ||
|
|
534979e6c7 | ||
|
|
6a27ad8997 | ||
|
|
6d3972cca8 | ||
|
|
f8d608107c | ||
|
|
0dd97de046 | ||
|
|
2026486fda | ||
|
|
811bd75eab | ||
|
|
97a48ec5fb | ||
|
|
8e9a5ee678 | ||
|
|
7236e8c6a3 | ||
|
|
2c1f3b1f7a | ||
|
|
f5584ecc3c | ||
|
|
e83f4ab4bf | ||
|
|
fa89370517 | ||
|
|
2cba0c73be | ||
|
|
cb075560ed | ||
|
|
687f52b0b5 | ||
|
|
24051ab2fb | ||
|
|
9139afd318 | ||
|
|
71ecead5b7 | ||
|
|
56292f27c2 | ||
|
|
14c2f612cf | ||
|
|
e9506aec1c | ||
|
|
d2eb46c173 | ||
|
|
0d733ad234 | ||
|
|
4955e05882 | ||
|
|
c3fcfe7e4e | ||
|
|
32587d341c | ||
|
|
a74b640507 | ||
|
|
52664791f5 | ||
|
|
cf651df744 | ||
|
|
807df44769 | ||
|
|
f97bc83ac1 | ||
|
|
daa299721d | ||
|
|
ec90e3dac3 | ||
|
|
5ddd5c15a5 | ||
|
|
957586db09 | ||
|
|
e0c8a7df66 | ||
|
|
c769fbda0f | ||
|
|
1f860164f4 | ||
|
|
e29c5c069b | ||
|
|
872d0d24e7 | ||
|
|
b644f6f635 | ||
|
|
510d15195c | ||
|
|
7ad4bce487 | ||
|
|
703b7537cf | ||
|
|
9cb6c88f34 | ||
|
|
97068223ff | ||
|
|
1de5777f7e | ||
|
|
aff11d83d8 | ||
|
|
335f09726f | ||
|
|
12c470667f | ||
|
|
71ac8bb802 | ||
|
|
fc9758da8e | ||
|
|
216701ce3c | ||
|
|
5d5075954e | ||
|
|
ded4b3926e | ||
|
|
365932510f | ||
|
|
888128e56b | ||
|
|
2adec264af | ||
|
|
ccdab464c3 | ||
|
|
a7353678a3 | ||
|
|
ad8ae4af91 | ||
|
|
dd7f0ce090 | ||
|
|
8c955836dd | ||
|
|
183022ebe4 | ||
|
|
33be077f92 | ||
|
|
6a137fa73c | ||
|
|
25c85dfc88 | ||
|
|
58cc96115c | ||
|
|
b4b63d3722 | ||
|
|
d6efe85a57 | ||
|
|
51b27e8372 | ||
|
|
cac612c485 | ||
|
|
a5b9f566b3 | ||
|
|
0d098616e8 | ||
|
|
63722a20ac | ||
|
|
6f6ab6bc33 | ||
|
|
7f0a85c106 | ||
|
|
46ddf4bcc6 | ||
|
|
f5ab075f9d | ||
|
|
9b361864fa | ||
|
|
41b0dc41a6 | ||
|
|
cab339c857 | ||
|
|
dcb42602c9 | ||
|
|
9e446557b4 | ||
|
|
a3d76c58a4 | ||
|
|
e0e0581d10 | ||
|
|
ef75d5ec31 | ||
|
|
dfeabd8bae | ||
|
|
7aa687bad9 | ||
|
|
c2ea2c1193 | ||
|
|
291b22e987 | ||
|
|
a091b69dd6 | ||
|
|
887c84a0d1 | ||
|
|
5009d54ab8 | ||
|
|
840ade6da7 | ||
|
|
030aa70842 | ||
|
|
dea3ae857b | ||
|
|
9f5172b0d1 | ||
|
|
e0529a5597 | ||
|
|
425cd17e57 | ||
|
|
0669a9cb81 | ||
|
|
5e040449c4 | ||
|
|
074686b520 | ||
|
|
d40f52357e | ||
|
|
076f77a31b | ||
|
|
8f1c16a8a5 | ||
|
|
b149dbc73e | ||
|
|
aa8d6a7294 | ||
|
|
a94dde403f | ||
|
|
9bb7e7b014 | ||
|
|
02a97e8d7d | ||
|
|
def759fe41 | ||
|
|
939cfc2476 | ||
|
|
7f2c73468f | ||
|
|
81a368f710 | ||
|
|
4e975e4974 | ||
|
|
a296c2dd00 | ||
|
|
5ff7994852 | ||
|
|
1bdcef9a2c | ||
|
|
f621117eb9 | ||
|
|
83cec7129a | ||
|
|
06b33c58fd | ||
|
|
8ce39f592c | ||
|
|
d37c451e81 | ||
|
|
a7ff1a61d5 | ||
|
|
8349909f60 | ||
|
|
3a2cf30f47 | ||
|
|
9a3cab3645 | ||
|
|
7dbc02f879 | ||
|
|
a39f75744c | ||
|
|
5742c6591a | ||
|
|
588901ee4c | ||
|
|
e30d2aae19 | ||
|
|
463e4f12f8 | ||
|
|
01c2817005 | ||
|
|
1a16aab970 | ||
|
|
8247344f0d | ||
|
|
2fdd64a2d8 | ||
|
|
22ed4e7f73 | ||
|
|
327ac9f931 | ||
|
|
5ad0c38fe8 | ||
|
|
757911f149 | ||
|
|
35b771fb42 | ||
|
|
720f1b0e5b | ||
|
|
292a3be515 | ||
|
|
5ed407e493 | ||
|
|
40988b901c | ||
|
|
26d49b2027 | ||
|
|
10386ee4de | ||
|
|
773d2e65d6 | ||
|
|
3e29d883fa | ||
|
|
f6f2895524 | ||
|
|
62287f8fcc | ||
|
|
be3257f805 | ||
|
|
1bf3aa528a | ||
|
|
056236c9f8 | ||
|
|
71fff76111 | ||
|
|
1095724e5c | ||
|
|
e81c2f2f90 | ||
|
|
9f91b4c050 | ||
|
|
7ada824e3e | ||
|
|
7cf7591f0a | ||
|
|
143a257c25 | ||
|
|
1d98647c54 | ||
|
|
7cd52b36a1 | ||
|
|
b033052647 | ||
|
|
4fcf2533a6 | ||
|
|
a8a103a521 | ||
|
|
12b09bb80b | ||
|
|
49aaffcf9a | ||
|
|
afbe628f17 | ||
|
|
1d6cbe31fe | ||
|
|
cb874d7a94 | ||
|
|
d7598ca672 | ||
|
|
ff8e494abd | ||
|
|
dfaa6dd3f8 | ||
|
|
849f3fbb28 | ||
|
|
329acf24bb | ||
|
|
bf40278131 | ||
|
|
4f2cf52eff | ||
|
|
f96fb855ce | ||
|
|
65f0be8a55 | ||
|
|
bcfcafdb7c | ||
|
|
c57bd066d1 | ||
|
|
4171fbdfd3 | ||
|
|
0f60aa2289 | ||
|
|
f97aab4699 | ||
|
|
740d447442 | ||
|
|
6448d6ccfa | ||
|
|
25cc9f3b9b | ||
|
|
e776838de9 | ||
|
|
bf0cfe0693 | ||
|
|
f690767507 | ||
|
|
4d57b14b5b | ||
|
|
263036045a | ||
|
|
56ef3f75d3 | ||
|
|
d0c902ab78 | ||
|
|
5ba7bc1fdf | ||
|
|
7e9ac7ba67 | ||
|
|
e2eaee9620 | ||
|
|
b23ce7ab8e | ||
|
|
53127a7ce6 | ||
|
|
011b73ba39 | ||
|
|
5d04a72a61 | ||
|
|
c3b76fdccf | ||
|
|
ec95243dad | ||
|
|
ae5d4a0bb2 | ||
|
|
0231f858e9 | ||
|
|
5d6db9f76f | ||
|
|
3bcefd2160 | ||
|
|
89675403d8 | ||
|
|
f277166354 | ||
|
|
b212babc87 | ||
|
|
6518c2d246 | ||
|
|
c551228079 | ||
|
|
c1198087af | ||
|
|
990ccc6d8b | ||
|
|
56a16066b7 | ||
|
|
407a47dd12 | ||
|
|
73b43b4f2b | ||
|
|
5961748b2a | ||
|
|
a18c5f3441 | ||
|
|
03c47a4020 | ||
|
|
77b0df3d07 | ||
|
|
7e3465a022 | ||
|
|
04fd3f7a1b | ||
|
|
685618691d | ||
|
|
f29d2733c7 | ||
|
|
af59174e49 | ||
|
|
785c21d27b | ||
|
|
40c7a44d4f | ||
|
|
cbb50937cd | ||
|
|
83a4bf7947 | ||
|
|
0d7d5a9820 |
+11
-2
@@ -1,6 +1,15 @@
|
||||
*.o
|
||||
.*.d
|
||||
*.d
|
||||
*.so
|
||||
*.gz
|
||||
*.map
|
||||
finit
|
||||
/finit
|
||||
/initctl
|
||||
/reboot
|
||||
/config.h
|
||||
/config.mk
|
||||
/GPATH
|
||||
/GRTAGS
|
||||
/GSYMS
|
||||
/GTAGS
|
||||
/contrib/*
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "libuev"]
|
||||
path = libuev
|
||||
url = https://github.com/troglobit/libuev.git
|
||||
[submodule "libite"]
|
||||
path = libite
|
||||
url = https://github.com/troglobit/libite.git
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Travis CI integration -- https://travis-ci.org/troglobit/finit
|
||||
# Defaults to GNU GCC and autotools: ./configure && make && make test
|
||||
language: c
|
||||
|
||||
# We don't need to install packages, use dockerized build, quicker
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
|
||||
|
||||
addons:
|
||||
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
|
||||
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
- make distclean
|
||||
- ./configure
|
||||
- make
|
||||
@@ -1,90 +0,0 @@
|
||||
1.4 (20121002)
|
||||
- Fix I/O plugin watcher and load plugins earlier for a new hook
|
||||
- Start refactoring helpers.c into a libite.so (-lite). This
|
||||
means other user space applications/daemons can make use of
|
||||
the neat toolbox available in finit
|
||||
- Use short-form -s/-w -u to work with BusyBox hwclock as well
|
||||
- Use RTLD_GLOBAL flag to tell dynamic loader to load dependent
|
||||
.so files as well. Lets other plugins use global symbols.
|
||||
- Greatly simplify svc hook for external plugins and cleanup
|
||||
the plugin API.
|
||||
- And more... see the GIT log for more details.
|
||||
|
||||
1.3 (20120928)
|
||||
- Cleanup public plugin API a bit and add new pid/pidfile funcs
|
||||
- Add plugin hook to end of service startup
|
||||
- Remove finit.h from svc.h, plugins should not need this.
|
||||
- Move utility macros etc. to helpers.h
|
||||
- Make finit.h daemon internal, only
|
||||
- Move defines of FIFO, conf and rcS.d to Makefile => correct paths
|
||||
- Add support for installing required headers in system include dir
|
||||
- Better support for distributions and packagers with install-exec,
|
||||
install-data, and install-dev targets in Makefile. Useful if you
|
||||
want to call targets with different $DESTDIR!
|
||||
- Makefile fixes for installation, paths encoded wrong
|
||||
- Strip binaries + .so files, support for $(CROSS) toolchain strip
|
||||
- Default install is now to /sbin/finit and /usr/
|
||||
- Note change in $PLUGIN_DIR environemnt variable to $plugindir
|
||||
|
||||
1.2 (20120927)
|
||||
- Fix installation paths encoded in finit binary
|
||||
- Update README with section on building and enviroment variables
|
||||
|
||||
1.1 (20120927)
|
||||
- Build fixes for ARM eabi/uClibc
|
||||
- Rename signal.[ch]-->sig.[ch] to avoid name clash w/ system headers
|
||||
|
||||
1.0 (20120926)
|
||||
- New plugin based system for all odd extensions
|
||||
- New service monitor that restarts services if they die
|
||||
- New maintainer at GitHub http://github.com/troglobit/finit
|
||||
- Add standard LICENSE and AUTHORS files
|
||||
- New focus: embedded systems and small headless servers
|
||||
|
||||
0.6 (20100614)
|
||||
- don't start consolekit manually, dbus starts it (rtp)
|
||||
- unmount all filesystems before rebooting
|
||||
- disable USE_VAR_RUN_RESOLVCONF for Mandriva
|
||||
- unset terminal type in Mandriva before running X
|
||||
- remove extra sleep in finit-alt before calling services.sh (caio)
|
||||
|
||||
0.5 (20080821)
|
||||
- add option to start dbus and consolekit before the X server
|
||||
- finit-alt listens to /dev/initctl to work with reboot(8) (smurfy)
|
||||
- write runlevel to utmp, needed by Printerdrake (Pascal Terjan)
|
||||
- fix ownership of /var/run/utmp (reported by Pascal Terjan)
|
||||
- remove obsolete code to load AGP drivers
|
||||
- conditional build of /etc/resolveconf/run support
|
||||
- add support to /var/run/resolvconf in Mandriva (blino)
|
||||
|
||||
O.4 (20080516)
|
||||
- default username for finit-alt configurable in Makefile
|
||||
- create loopback device node in finit-alt (for squashfs)
|
||||
- add option to use built-in run-parts instead of /bin/run-parts
|
||||
- ignore signal instead of setting to an empty handler (Metalshark)
|
||||
- handle pam_console permissions in finit-alt for Mandriva
|
||||
- add services.sh example and nash-hotplug patch for Mandriva
|
||||
- mount /proc/bus/usb in Mandriva
|
||||
- add runtime debug to finit-alt if finit_debug parameter is specified
|
||||
- read configuration from /etc/finit.conf
|
||||
- run getty with openvt on the virtual terminal
|
||||
|
||||
0.3 (20080223)
|
||||
- Change poweroff method to reboot(RB_POWER_OFF) (Metalshark)
|
||||
- Remove duplicate unionctl() reimplementation error
|
||||
- Fix string termination in path creation
|
||||
- Mount /var/lock and /var/run as tmpfs
|
||||
|
||||
0.2 (20080219)
|
||||
- replace system("touch") with touch() in finit-mod (Metalshark)
|
||||
- disable NO_HCTOSYS by default to match stock Eeepc kernel
|
||||
- drop system("rm -f") to clean /tmp, its a fresh mounted tmpfs
|
||||
- write ACPI sleep state to /sys/power/state instead of
|
||||
/proc/acpi/sleep (Metalshark)
|
||||
- use direct calls to set loopback instead of system("ifconfig")
|
||||
- replace system("cat") and system("dd") with C implementation
|
||||
- moved finit-mod and finit-alt helpers to helpers.c
|
||||
- replace system("echo;cat") to draw shutdown splash with C calls
|
||||
|
||||
0.1 (20080214)
|
||||
- initial release
|
||||
+519
@@ -0,0 +1,519 @@
|
||||
Change Log
|
||||
==========
|
||||
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[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][].
|
||||
As of v2.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
|
||||
--------------------
|
||||
|
||||
Bug fix release.
|
||||
|
||||
### Changes
|
||||
|
||||
* Add support for stop/start/restart/reload service by `name:id`
|
||||
* Refactor service status listed in `initctl show`, show actual status
|
||||
|
||||
### Fixes
|
||||
|
||||
* Remove bootstrap-only tasks/services when leaving runlevel 'S'
|
||||
* Fix reference counting issue with already stopped and removed services
|
||||
when the user performs `initctl reload` to change system configuration
|
||||
* Revert semantic change in behavior of `initctl restart`: users expect
|
||||
service to be stopped/started, not reloaded with `SIGHUP` even if the
|
||||
service supports it
|
||||
* Fix `NULL` pointer dereference causing kernel panic when user calls
|
||||
`initctl reload` after change of system configuration
|
||||
* Fix column alignment in output of `initctl show` for services not in
|
||||
current runlevel
|
||||
|
||||
|
||||
[2.2][] - 2015-11-23
|
||||
--------------------
|
||||
|
||||
Lots of fixes to handle static builds, but also fixes for dynamic event
|
||||
handling and reconfiguration at runtime.
|
||||
|
||||
### Changes
|
||||
|
||||
* Upgrade to libuEv v1.2.4, to handle static builds
|
||||
* Upgrade to libite (LITE) v1.2.0, to handle static builds
|
||||
* Clarify how to select different plugins with the configure script
|
||||
* Improve urandom plugin for embedded systems w/o random seed
|
||||
* Add `--debug` flag to `initctl`
|
||||
* The runlevels listed for services in `initctl show` now hightlight the
|
||||
active runlevel.
|
||||
* Clarify in the README and in `initctl help` that the GW event to
|
||||
listen for in service declarations is `GW:UP`
|
||||
|
||||
### Fixes
|
||||
|
||||
* Build fixes for `configure --disable-inetd`
|
||||
* Fixed issue #14: Improved support for static Finit builds
|
||||
* Misc. fixes to silence warnings when building a static Finit
|
||||
* Default to register services as `SIGHUP`'able, regression in v2.0
|
||||
* Call `HOOK_SVC_RECONF` only when all processes have been stopped
|
||||
* On reload/reconf we must wait for all services to stop first
|
||||
* Only trigger on events that matches the service's specification,
|
||||
fix by Tobias Waldekranz
|
||||
|
||||
|
||||
[2.1][] - 2015-10-16
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
|
||||
* Add hook point for fstab mount failure
|
||||
* Set hostname on dynamic reload
|
||||
* Upgrade to libite v1.1.1
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix service callback coredump checks and simplify callback exit
|
||||
* Do not use `-Os` use `-O2` as default optimization level. Many cross
|
||||
compiler toolchains are known to have problems with `-Os`
|
||||
* Do not allow build VERSION to be overloaded by an environment variable
|
||||
* Fix too small MAX arguments and too few argments in `svc_t` for
|
||||
reading currently running services with `initctl show`
|
||||
* Unblock blocked signals after forking off a child
|
||||
|
||||
|
||||
[2.0][] - 2015-09-20
|
||||
--------------------
|
||||
|
||||
Support for multiple instances and event based services, as well as the
|
||||
introduction of an `initctl` tool.
|
||||
|
||||
**Note:** Incompatible change to syntax for custom `inetd` services,
|
||||
c.f. Finit v[1.12][].
|
||||
|
||||
### Changes
|
||||
* The most notable change is the support for multiple instances. A must
|
||||
have when running multiple DHCP clients, OpenVPN tunnels, or anything
|
||||
that means using the same command only with different arguments. Now
|
||||
simply add a `:ID` after the `service` keyword, where `ID` is a unique
|
||||
instance number for that service.
|
||||
|
||||
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
|
||||
|
||||
* Another noteworthy new feature is support for starting/stopping
|
||||
services on Netlink events:
|
||||
|
||||
service :1 [2345] <!IFUP:eth0,GW> /sbin/dropbear -R -F -p 22 -- SSH daemon
|
||||
|
||||
Here the first instance `:1` of the SSH daemon is declared to run in
|
||||
runlevels 2-5, but only if eth0 `IFUP:eth0` is up and a gateway `GW`
|
||||
is set. When the configuration changes, a new gateway is set, or if
|
||||
somehow a new `IFUP` event for eth0 is received, then dropbear is not
|
||||
SIGHUP'ed, but instead stop-started `<!>`. The latter trick applies
|
||||
to all services, even those that do not define any events.
|
||||
* Support for reloading `*.conf` files in `/etc/finit.d/` on SIGHUP.
|
||||
All `task`, `service` and `run` statements can be used in these .conf
|
||||
files. Use the `telinit q` command, `initctl reload` or simply send
|
||||
`SIGHUP` to PID 1 to reload them. Finit automatically does reload of
|
||||
these `*.conf` files when changing runlevel.
|
||||
* Support for a modern `initctl` tool which can stop/start/reload and
|
||||
list status of all system services. Also, the old client tool used
|
||||
to change runlevel is now also available as a symlink: `telinit`.
|
||||
|
||||
initctl [-v] <status|stop|start|reload|restart> [JOB]
|
||||
|
||||
* Add concept of "jobs". This is a unique identifier, composed of a
|
||||
service and instance number, `SVC:ID`
|
||||
|
||||
initctl <stop|start|reload|restart> JOB
|
||||
|
||||
* Support for *deny filters* in `inetd` services.
|
||||
|
||||
inetd service/proto[@iface,!iface,...] </path/to/cmd | internal[.service]>
|
||||
|
||||
Internal services on a custom port must use the `internal.service`
|
||||
syntax so Finit can properly bind the inetd service to the correct
|
||||
plugin. Here follows a few examples:
|
||||
|
||||
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@*,!eth1,!eth0, nowait [2345] /sbin/telnetd -i -F -- Telnet service
|
||||
inetd 2323/tcp@eth1,eth2,eth0 nowait [2345] /sbin/telnetd -i -F -- Telnet service
|
||||
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
|
||||
|
||||
Access to telnet on port `2323` is only possible from interfaces
|
||||
`eth0`, `eth1` and `eth2`. The standard telnet port (`23`) is
|
||||
available from all other interfaces, but also `eth2`. The `*`
|
||||
notation used in the ssh stanza means *any* interface, however, here
|
||||
`eth0` is not allowed.
|
||||
|
||||
**NOTE:** This change breaks syntax compatibility with Finit v[1.12].
|
||||
* Support for a more user-friendly configure script rather than editing
|
||||
the top Makefile, or setting environment variables at build time.
|
||||
* Support for building Finit statically, no external libraries. This
|
||||
unfortunately means that some plugins cannot be built, at all.
|
||||
Big thanks goes to James Mills for all help testing this out!
|
||||
* Support for disabling the built-in inetd server with `configure`.
|
||||
* Support for two new hook points: `HOOK_SVC_RECONF` and
|
||||
`HOOK_RUNLEVEL_CHANGE`. See the source for the exact location.
|
||||
* The `include <FILE>` option now needs an absolute path to `FILE`.
|
||||
|
||||
### Fixes
|
||||
* Rename `patches/` to `contrib/` to simplify integration in 3rd party
|
||||
build systems.
|
||||
* Fix for unwanted zombies ... when receiving SIGCHLD we must reap *all*
|
||||
children. We only receive one signal, but multiple processes may have
|
||||
exited and need to be collected.
|
||||
|
||||
|
||||
[1.12][] - 2015-03-04
|
||||
---------------------
|
||||
|
||||
The inetd release.
|
||||
|
||||
### Changes
|
||||
* Add support for built-in inetd super server -- launch services on
|
||||
demand. Supports filtering per interface and custom Inet ports.
|
||||
* Upgrade to [libuEv] v1.1.0 to better handle error conditions.
|
||||
* Allow mixed case config directives in `finit.conf`
|
||||
* Add support for RFC 868 (rdate) time plugin, start as inetd service.
|
||||
* Load plugins before parsing `finit.conf`, this makes it possible to
|
||||
extend finit even with configuration commands. E.g., the `time.so`
|
||||
plugin must be loaded for the `inetd time/tcp internal` service to be
|
||||
accepted when parsing `finit.conf`.
|
||||
* Slight change in TTY fallback behavior, if no TTY is listed in the
|
||||
system `finit.conf` first inspect the `console` setting and only if
|
||||
that too is unset fall back to `/bin/sh`
|
||||
* When falling back to the `console` TTY or `/bin/sh`, finit now marks
|
||||
this fallback as console. Should improve usability in some use cases.
|
||||
|
||||
### Fixes
|
||||
* Revert "Use vfork() instead of fork() before exec()" from v1.11. It
|
||||
turned out to not work so well after all. For instance, launching
|
||||
TTYs in a background process completely blocked inetd services from
|
||||
even starting up listening sockets ... proper fork seems to work fine
|
||||
though. This is the casue for *yanking* the [1.11] release, below.
|
||||
* Trap segfaults caused by external plugins/callbacks in a sub-process.
|
||||
This prevents a single programming mistake in by a 3rd party developer
|
||||
from taking down the entire system.
|
||||
* Fix Coverity CID 56281: dlopen() resource leak by storing the pointer.
|
||||
For the time being we do not support unloading plugins.
|
||||
* Set hostname early, so bootstrap processes like syslog can use it.
|
||||
* Only restart *lost daemons* when recovering from a SIGSTOP/norespawn.
|
||||
|
||||
|
||||
[1.11][] - 2015-01-24 [YANKED]
|
||||
------------------------------
|
||||
|
||||
The [libuEv] release.
|
||||
|
||||
**Note:** This release has been *yanked* from distribution due to a
|
||||
regression in launching background processes and TTY's. Fixed in
|
||||
Finit v[1.12].
|
||||
|
||||
### Changes
|
||||
* Now using the asynchronous libuEv library for handling all events:
|
||||
signals, timers and listening to sockets or file descriptors.
|
||||
* Rename NEWS.md --> CHANGELOG.md, with symlinks for `make install`
|
||||
* Attempt to align with http://keepachangelog.com/ for this file.
|
||||
* Travis CI now only invokes Coverity Scan from the 'dev' branch. This
|
||||
means that all development, except documentation updates, must go into
|
||||
that branch.
|
||||
|
||||
### Fixes
|
||||
* Fix bug with finit dying when no `tty` is defined in `finit.conf`, now
|
||||
even the fallback shell has control over its TTY, see fix in GIT
|
||||
commit [dea3ae8] for this.
|
||||
|
||||
|
||||
[1.10][] - 2014-11-27
|
||||
---------------------
|
||||
|
||||
Major bug fix release.
|
||||
|
||||
### Changes
|
||||
* Project now relies on static code analysis from Coverity, so this
|
||||
release contains many serious bug fixes.
|
||||
* Convert to use Markdown for README, NEWS and TODO.
|
||||
* Serious update to README and slight pruning of finished TODO items.
|
||||
|
||||
### Fixes
|
||||
* Fix serious file descriptor and memory leaks in the following
|
||||
functions. In particular the leaks in `run_interactive()` are very
|
||||
serious since that function is called every time a service is started
|
||||
and/or restarted! For details, see the GIT log:
|
||||
* `helpers.c:run()`
|
||||
* `helpers.c:run_interactive()`
|
||||
* `helpers.c:set_hostname()`
|
||||
* `helpers.c:procname_kill()`
|
||||
* `svc.c:svc_start()`: Fix swapped arguments to dup2() and add close(fd)
|
||||
to prevent descriptor leak.
|
||||
* `svc.c:svc_start()`: Fix out of bounds write to local stack variable,
|
||||
wrote off-by-one outside array.
|
||||
* Several added checks for return values to `mknod()`, `mkdir()`,
|
||||
`remove()`, etc.
|
||||
|
||||
|
||||
[1.9][] - 2014-04-21
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Add support for an include directive to `.conf` files
|
||||
* Fallback to `/bin/sh` if user forgets tty setting
|
||||
* Initial support for restarting lost services during `norespwan`
|
||||
|
||||
### Fixes
|
||||
* Bug fixes, code cleanup
|
||||
* Handle `SIGHUP` from service callback properly when switching runlevel
|
||||
* Misc. major (memleak) and minor fixes and additions to `libite/lite.h`
|
||||
|
||||
|
||||
[1.8][] - 2013-06-07
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Support for runlevels, with a bootstrap runlevel 'S'
|
||||
* Support for saving previous and current runlevel to UTMP
|
||||
* Support for new `finit.conf` commands: run, task, and runlevel
|
||||
* Support for tty and console commands in `finit.conf`, like services
|
||||
but for launching multiple getty logins
|
||||
* New tty plugin to monitor TTYs coming and going, like USB TTYs
|
||||
|
||||
### Fixes
|
||||
* Bugfixes to libite
|
||||
|
||||
|
||||
[1.7][] - 2012-10-08
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Show `__FILE__` in `_d()` debug messages, useful for plugins with
|
||||
similarily named callbacks. Also, only in debug mode anyway
|
||||
* Make sure to cleanup recorded PID when a service is lost. Required by
|
||||
service plugins for their callbacks to work.
|
||||
* Only clear screen when in verbose mode. Maybe this should be removed
|
||||
altogether?
|
||||
|
||||
### Fixes
|
||||
* Bugfix: Do not `free()` static string in `finit.conf` parser
|
||||
|
||||
|
||||
[1.6][] - 2012-10-06
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Skip `.` and `..` in plugin loader and display error when failing to
|
||||
load plugins
|
||||
* Support for overriding `/etc/finit.d` with `runparts DIR` in
|
||||
`finit.conf`
|
||||
* Revoke support for starting services not starting with a slash.
|
||||
* Prevent endless restart of non-existing services in `finit.conf`
|
||||
* Support for sysvinit style startstop scripts in `/etc/finit.d`
|
||||
|
||||
### Fixes
|
||||
* Minor fix to alsa-utils plugin to silence on non-existing cards
|
||||
|
||||
|
||||
[1.5][] - 2012-10-03
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Use bootmisc plugin to setup standard FHS 2.3 structure in `/var`
|
||||
* Added `FLOG_WARN()` syslog macro, for plugins
|
||||
* Add plugin dependency resolver. Checks `plugin_t` for `.depends`
|
||||
|
||||
|
||||
[1.4][] - 2012-10-02
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Start refactoring helpers.c into a libite.so (-lite). This means
|
||||
other user space applications/daemons can make use of the neat toolbox
|
||||
available in finit
|
||||
* Use short-form -s/-w -u to work with BusyBox hwclock as well
|
||||
* Use RTLD_GLOBAL flag to tell dynamic loader to load dependent .so
|
||||
files as well. Lets other plugins use global symbols.
|
||||
* Greatly simplify svc hook for external plugins and cleanup plugin API.
|
||||
* And more ... see the GIT log for more details.
|
||||
|
||||
### Fixes
|
||||
* Fix I/O plugin watcher and load plugins earlier for a new hook
|
||||
|
||||
|
||||
[1.3][] - 2012-09-28
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Cleanup public plugin API a bit and add new pid/pidfile funcs
|
||||
* Add plugin hook to end of service startup
|
||||
* Remove finit.h from svc.h, plugins should not need this.
|
||||
* Move utility macros etc. to helpers.h
|
||||
* Make `finit.h` daemon internal, only
|
||||
* Move defines of FIFO, conf and rcS.d to Makefile => correct paths
|
||||
* Add support for installing required headers in system include dir
|
||||
* Better support for distributions and packagers with install-exec,
|
||||
install-data, and install-dev targets in Makefile. Useful if you want
|
||||
to call targets with different `$DESTDIR`!
|
||||
* Makefile fixes for installation, paths encoded wrong
|
||||
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip
|
||||
* Default install is now to `/sbin/finit` and `/usr/`
|
||||
* Note change in `$PLUGIN_DIR` environemnt variable to `$plugindir`
|
||||
|
||||
|
||||
[1.2][] - 2012-09-27
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Update README with section on building and enviroment variables
|
||||
|
||||
### Fixes
|
||||
* Fix installation paths encoded in finit binary
|
||||
|
||||
|
||||
[1.1][] - 2012-09-27
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* Rename signal.[ch]-->sig.[ch] to avoid name clash w/ system headers
|
||||
|
||||
### Fixes
|
||||
* Build fixes for ARM eabi/uClibc
|
||||
|
||||
|
||||
[1.0][] - 2012-09-26
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
* New plugin based system for all odd extensions
|
||||
* New service monitor that restarts services if they die
|
||||
* New maintainer at GitHub http://github.com/troglobit/finit
|
||||
* Add standard LICENSE and AUTHORS files
|
||||
* New focus: embedded systems and small headless servers
|
||||
|
||||
|
||||
[0.6][] - 2010-06-14
|
||||
--------------------
|
||||
|
||||
* Don't start consolekit manually, dbus starts it (rtp)
|
||||
* Unmount all filesystems before rebooting
|
||||
* Disable `USE_VAR_RUN_RESOLVCONF` for Mandriva
|
||||
* Unset terminal type in Mandriva before running X
|
||||
* Remove extra sleep in finit-alt before calling services.sh (caio)
|
||||
|
||||
|
||||
[0.5][] - 2008-08-21
|
||||
--------------------
|
||||
|
||||
* Add option to start dbus and consolekit before the X server
|
||||
* finit-alt listens to `/dev/initctl` to work with `reboot(8)` (smurfy)
|
||||
* Write runlevel to utmp, needed by Printerdrake (Pascal Terjan)
|
||||
* Fix ownership of `/var/run/utmp` (reported by Pascal Terjan)
|
||||
* Remove obsolete code to load AGP drivers
|
||||
* Conditional build of `/etc/resolveconf/run` support
|
||||
* Add support to `/var/run/resolvconf` in Mandriva (blino)
|
||||
|
||||
|
||||
[0.4][] - 2008-05-16
|
||||
--------------------
|
||||
|
||||
* Default username for finit-alt configurable in Makefile
|
||||
* Create loopback device node in finit-alt (for squashfs)
|
||||
* Add option to use built-in run-parts instead of `/bin/run-parts`
|
||||
* Ignore signal instead of setting to an empty handler (Metalshark)
|
||||
* Handle pam_console permissions in finit-alt for Mandriva
|
||||
* Add services.sh example and nash-hotplug patch for Mandriva
|
||||
* Mount `/proc/bus/usb` in Mandriva
|
||||
* Add runtime debug to finit-alt if finit_debug parameter is specified
|
||||
* Read configuration from `/etc/finit.conf`
|
||||
* Run getty with openvt on the virtual terminal
|
||||
|
||||
|
||||
[0.3][] - 2008-02-23
|
||||
--------------------
|
||||
|
||||
* Change poweroff method to `reboot(RB_POWER_OFF)` (Metalshark)
|
||||
* Remove duplicate `unionctl()` reimplementation error
|
||||
* Fix string termination in path creation
|
||||
* Mount `/var/lock` and `/var/run` as tmpfs
|
||||
|
||||
|
||||
[0.2][] - 2008-02-19
|
||||
--------------------
|
||||
|
||||
* Replace `system("touch")` with `touch()` in finit-mod (Metalshark)
|
||||
* Disable `NO_HCTOSYS` by default to match stock Eeepc kernel
|
||||
* Drop `system("rm -f")` to clean `/tmp`, its a fresh mounted tmpfs
|
||||
* Write ACPI sleep state to `/sys/power/state` instead of
|
||||
`/proc/acpi/sleep` (Metalshark)
|
||||
* Use direct calls to set loopback instead of `system("ifconfig")`
|
||||
* Replace `system("cat")` and `system("dd")` with C implementation
|
||||
* Moved finit-mod and finit-alt helpers to `helpers.c`
|
||||
* Replace `system("echo;cat")` to draw shutdown splash with C calls
|
||||
|
||||
|
||||
0.1 - 2008-02-14
|
||||
----------------
|
||||
|
||||
* Initial release
|
||||
|
||||
[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.11]: https://github.com/troglobit/finit/compare/1.10...1.11
|
||||
[1.10]: https://github.com/troglobit/finit/compare/1.9...1.10
|
||||
[1.9]: https://github.com/troglobit/finit/compare/1.8...1.9
|
||||
[1.8]: https://github.com/troglobit/finit/compare/1.7...1.8
|
||||
[1.7]: https://github.com/troglobit/finit/compare/1.6...1.7
|
||||
[1.6]: https://github.com/troglobit/finit/compare/1.5...1.6
|
||||
[1.5]: https://github.com/troglobit/finit/compare/1.4...1.5
|
||||
[1.4]: https://github.com/troglobit/finit/compare/1.3...1.4
|
||||
[1.3]: https://github.com/troglobit/finit/compare/1.2...1.3
|
||||
[1.2]: https://github.com/troglobit/finit/compare/1.1...1.2
|
||||
[1.1]: https://github.com/troglobit/finit/compare/1.0...1.1
|
||||
[1.0]: https://github.com/troglobit/finit/compare/0.9...1.0
|
||||
[0.9]: https://github.com/troglobit/finit/compare/0.8...0.9
|
||||
[0.8]: https://github.com/troglobit/finit/compare/0.7...0.8
|
||||
[0.7]: https://github.com/troglobit/finit/compare/0.6...0.7
|
||||
[0.6]: https://github.com/troglobit/finit/compare/0.5...0.6
|
||||
[0.5]: https://github.com/troglobit/finit/compare/0.4...0.5
|
||||
[0.4]: https://github.com/troglobit/finit/compare/0.3...0.4
|
||||
[0.3]: https://github.com/troglobit/finit/compare/0.2...0.3
|
||||
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
|
||||
[libuEv]: https://github.com/troglobit/libuev
|
||||
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
@@ -1,5 +1,5 @@
|
||||
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
Copyright (c) 2008-2014 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
# Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
#
|
||||
# Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
# Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -25,73 +25,70 @@
|
||||
install-exec install-data install-dev \
|
||||
uninstall-exec uninstall-data uninstall-dev
|
||||
|
||||
# Top directory for building complete system, fall back to this directory
|
||||
ROOTDIR = $(shell pwd)
|
||||
|
||||
#VERSION ?= $(shell git tag -l | tail -1)
|
||||
VERSION ?= 1.4
|
||||
#VERSION = $(shell git tag -l | tail -1)
|
||||
VERSION = 2.4
|
||||
NAME = finit
|
||||
PKG = $(NAME)-$(VERSION)
|
||||
DEV = $(NAME)-dev
|
||||
ARCHIVE = $(PKG).tar.xz
|
||||
EXEC = finit reboot
|
||||
HEADERS = plugin.h svc.h helpers.h
|
||||
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 conf.o helpers.o sig.o svc.o plugin.o
|
||||
OBJS += strlcpy.o
|
||||
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 = $(addprefix .,$(SRCS:.c=.d))
|
||||
DEPS = $(SRCS:.c=.d)
|
||||
|
||||
# Installation paths, always prepended with DESTDIR if set
|
||||
prefix ?= /usr
|
||||
sysconfdir ?= /etc
|
||||
sbindir ?= /sbin
|
||||
libdir ?= /lib
|
||||
plugindir ?= $(libdir)/finit/plugins
|
||||
incdir = $(prefix)/include/finit
|
||||
datadir = $(prefix)/share/doc/finit
|
||||
mandir = $(prefix)/share/man/man8
|
||||
|
||||
# The initctl FIFO, should probably be in /run, not /dev
|
||||
FINIT_FIFO ?= /dev/initctl
|
||||
FINIT_CONF ?= $(sysconfdir)/finit.conf
|
||||
FINIT_RCSD ?= $(sysconfdir)/finit.d
|
||||
|
||||
CFLAGS += -W -Wall -Werror -Os
|
||||
CFLAGS += -W -Wall
|
||||
# Disable annoying gcc warning for "warn_unused_result", see GIT 37af997
|
||||
CPPFLAGS += -U_FORTIFY_SOURCE
|
||||
CPPFLAGS += -Ilibite -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE
|
||||
CPPFLAGS += -I$(TOPDIR)
|
||||
CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
|
||||
CPPFLAGS += -DFINIT_FIFO=\"$(FINIT_FIFO)\" -DFINIT_CONF=\"$(FINIT_CONF)\"
|
||||
CPPFLAGS += -DFINIT_RCSD=\"$(FINIT_RCSD)\" -DPLUGIN_PATH=\"$(plugindir)\"
|
||||
LDFLAGS += -rdynamic -L$(ROOTDIR)/libite
|
||||
DEPLIBS = libite/libite.so
|
||||
LDLIBS += -ldl -lite
|
||||
|
||||
include common.mk
|
||||
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS
|
||||
|
||||
all: $(DEPLIBS) $(EXEC)
|
||||
$(MAKE) -C plugins $@
|
||||
|
||||
all: config.h $(EXEC)
|
||||
+$(MAKE) -C plugins $@
|
||||
|
||||
$(DEPLIBS): Makefile
|
||||
$(MAKE) -C libite all
|
||||
+$(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 \
|
||||
@for file in $(EXEC); do \
|
||||
printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \
|
||||
$(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \
|
||||
done
|
||||
$(MAKE) -C libite install-exec
|
||||
@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)
|
||||
@@ -107,7 +104,12 @@ install-dev:
|
||||
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
|
||||
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
|
||||
done
|
||||
$(MAKE) -C libite install-dev install-exec
|
||||
ifndef LIBITE
|
||||
$(MAKE) -C libite install-dev
|
||||
endif
|
||||
ifndef LIBUEV
|
||||
$(MAKE) -C libuev install-dev
|
||||
endif
|
||||
|
||||
install: install-exec install-data install-dev
|
||||
|
||||
@@ -116,38 +118,58 @@ uninstall-exec:
|
||||
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 libite uninstall
|
||||
$(MAKE) -C plugins uninstall
|
||||
ifndef LIBITE
|
||||
$(MAKE) -C libite uninstall
|
||||
endif
|
||||
|
||||
uninstall-data:
|
||||
@for file in $(DISTFILES); do \
|
||||
-@for file in $(DISTFILES); do \
|
||||
printf " REMOVE $(DESTDIR)$(datadir)/$$file\n"; \
|
||||
rm $(DESTDIR)$(datadir)/$$file 2>/dev/null; \
|
||||
done
|
||||
-@rmdir $(DESTDIR)$(mandir)
|
||||
-@rmdir $(DESTDIR)$(datadir)
|
||||
-@rmdir $(DESTDIR)$(mandir) 2>/dev/null
|
||||
-@rmdir $(DESTDIR)$(datadir) 2>/dev/null
|
||||
|
||||
uninstall-dev:
|
||||
-@$(RM) -rf $(DESTDIR)$(incdir)
|
||||
-@$(RM) -rf $(DESTDIR)$(incdir) 2>/dev/null
|
||||
|
||||
uninstall: uninstall-exec uninstall-data uninstall-dev
|
||||
|
||||
clean:
|
||||
-@$(RM) $(OBJS) $(DEPS) $(EXEC)
|
||||
$(MAKE) -C libite $@
|
||||
$(MAKE) -C plugins $@
|
||||
+$(MAKE) -C plugins $@
|
||||
+$(MAKE) -C libite $@
|
||||
+$(MAKE) -C libuev $@
|
||||
-@$(RM) $(OBJS) $(DEPS) $(EXEC) 2>/dev/null
|
||||
|
||||
distclean: clean
|
||||
-@$(RM) $(JUNK) unittest *.o .*.d
|
||||
$(MAKE) -C libite $@
|
||||
$(MAKE) -C plugins $@
|
||||
+$(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..."
|
||||
git archive --format=tar --prefix=$(PKG)/ $(VERSION) | xz >../$(ARCHIVE)
|
||||
@(cd ..; md5sum $(ARCHIVE) | tee $(ARCHIVE).md5)
|
||||
@$(ARCHTOOL) ../$(ARCHIVE)
|
||||
@xz ../$(ARCHIVE)
|
||||
@md5sum $(ARCHIVEZ) | tee $(ARCHIVEZ).md5
|
||||
|
||||
dev: distclean
|
||||
@echo "Building unstable xz $(DEV) in parent dir..."
|
||||
@@ -158,11 +180,6 @@ dev: distclean
|
||||
cd - >/dev/null; mv $$dir/$(DEV).tar.xz ../; cd ..; \
|
||||
rm -rf $$dir; md5sum $(DEV).tar.xz | tee $(DEV).tar.xz.md5)
|
||||
|
||||
# Include automatically generated rules, such as:
|
||||
# uncgi.o: .../some/dir/uncgi.c /usr/include/stdio.h
|
||||
# but don't bother during clean!
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),distclean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,877 @@
|
||||
Finit ][ Fast & Extensible init for Linux
|
||||
=========================================
|
||||
[![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
|
||||
|
||||

|
||||
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Features](#features)
|
||||
* [/etc/finit.conf](#etcfinitconf)
|
||||
* [/etc/finit.d](#etcfinitd)
|
||||
* [Runparts](#runparts)
|
||||
* [Bootstrap](#bootstrap)
|
||||
* [Runlevels](#runlevels)
|
||||
* [Inetd](#inetd)
|
||||
* [Hooks, Callbacks & Plugins](#hooks-callbacks--plugins)
|
||||
* [Rebooting & Halting](#rebooting--halting)
|
||||
* [Commands & Status](#commands--status)
|
||||
* [Building](#building)
|
||||
* [Running](#running)
|
||||
* [Debugging](#debugging)
|
||||
* [Origin & References](#origin--references)
|
||||
|
||||
|
||||
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 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.
|
||||
|
||||
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.
|
||||
|
||||
> Finit basically does everything you need from its `main()` function!
|
||||
|
||||
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.
|
||||
|
||||
See [TroglOS][9] for an example of how to boot a small embedded system
|
||||
with Finit.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
**Process Supervision**
|
||||
|
||||
Start, monitor and restart processes (daemons) if 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.
|
||||
|
||||
All inetd services started can be filtered per port and inbound
|
||||
interface, reducing the need for a full blown firewall.
|
||||
|
||||
|
||||
**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>
|
||||
|
||||
|
||||
**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.
|
||||
|
||||
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**
|
||||
Listen to external events and control Finit behavior/services
|
||||
- **Inetd**
|
||||
Extend Finit with internal inetd services, for an example, see
|
||||
`plugins/time.c`
|
||||
|
||||
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.
|
||||
|
||||
**Note:** BusyBox getty, used when Finit is built for embedded
|
||||
systems, can take a `DEV` with *or* without `/dev/` prefix. Make
|
||||
sure to call the configure script with `--enable-embedded` if this
|
||||
is what you want.
|
||||
|
||||
* `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.
|
||||
|
||||
|
||||
/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
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Runlevels
|
||||
---------
|
||||
|
||||
Basic support for [runlevels][5] is included in Finit from v1.8. By
|
||||
default all services, tasks, run commands and TTYs listed without a set
|
||||
of runlevels get a default set `[234]` assigned. The default runlevel
|
||||
after boot is 2.
|
||||
|
||||
Finit supports runlevels 0-9, and S, with 0 reserved for halt, 6 reboot
|
||||
and S for services to only run at bootstrap. Runlevel 1 is the single
|
||||
user level, where usually no networking is enabled. In Finit this is
|
||||
more of a policy for the user to define. Normally only runlevels 1-6
|
||||
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
|
||||
service [S12345] /sbin/klogd -n -x -- Kernel log daemon
|
||||
tty [12345] /dev/tty1
|
||||
tty [2] /dev/tty2
|
||||
tty [2] /dev/tty3
|
||||
tty [2] /dev/tty4
|
||||
tty [2] /dev/tty5
|
||||
tty [2] /dev/tty6
|
||||
```
|
||||
|
||||
In this example syslogd is first started, in parallel, and then acpid is
|
||||
called using a conventional SysV init script. It is called with the run
|
||||
command, meaning the following task command to start the kbd script is
|
||||
not called until the acpid init script has fully completed. Then the
|
||||
keyboard setup script is called in parallel with klogd as a monitored
|
||||
service.
|
||||
|
||||
Again, tasks and services are started in parallel, while run commands
|
||||
are called in the order listed and subsequent commands are not started
|
||||
until a run command has completed.
|
||||
|
||||
Switching between runlevels can be done by calling init with a single
|
||||
argument, e.g. <kbd>init 5</kbd> switches to runlevel 5. When changing
|
||||
runlevels Finit also automatically reloads all `.conf` files in the
|
||||
`/etc/finit.d/` directory. So if you want to set a new system config,
|
||||
switch to runlevel 1, change all config files in the system, and touch
|
||||
all `.conf` files in `/etc/finit.d` before switching back to the
|
||||
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.
|
||||
|
||||
```shell
|
||||
~ $ telinit
|
||||
Usage: telinit [OPTIONS] [q | Q | 0-9]
|
||||
|
||||
Options:
|
||||
-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
|
||||
```
|
||||
|
||||
|
||||
Commands & Status
|
||||
-----------------
|
||||
|
||||
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>
|
||||
|
||||
Options:
|
||||
-d, --debug Debug initctl (client)
|
||||
-v, --verbose Verbose output
|
||||
-h, --help This help text
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
**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
|
||||
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.
|
||||
|
||||
```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
|
||||
3 running 478 [S12345] /sbin/klogd -n
|
||||
4:1 inetd 0 [2345] internal time allow *:37
|
||||
4:2 inetd 0 [2345] internal time allow *:37
|
||||
4:3 inetd 0 [2345] internal 3737 allow *:3737
|
||||
5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
|
||||
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
|
||||
--------
|
||||
|
||||
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:
|
||||
|
||||
* `--prefix=`: Base prefix path for all files, except `--sbindir` and
|
||||
`--sysconfdir`. Used in concert with the `DESTDIR` variable.
|
||||
|
||||
Defaults to `/usr`.
|
||||
|
||||
* `--sbindir=`: Path to where resulting binaries should install to. Used
|
||||
in concert with the `DESTDIR` variable.
|
||||
|
||||
Defaults to `/sbin`.
|
||||
|
||||
* `--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`.
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The default install does not setup finit as the system default
|
||||
`/sbin/init`, neither does it setup an initial `/etc/finit.conf`.
|
||||
|
||||
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:
|
||||
|
||||
```shell
|
||||
init=/sbin/finit
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Add `finit_debug`, or `--debug`, to the kernel command line to enable
|
||||
debug messages. See the output from `configure --help` for more on
|
||||
how to assist debugging.
|
||||
|
||||
```shell
|
||||
init=/sbin/finit --debug
|
||||
```
|
||||
|
||||
|
||||
Origin & References
|
||||
-------------------
|
||||
|
||||
This project is based on the [original finit][] by [Claudio Matsuoka][]
|
||||
which was reverse engineered from syscalls of the [EeePC fastinit][] —
|
||||
"gaps filled with frog DNA …"
|
||||
|
||||
Finit is developed and maintained by [Joachim 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/
|
||||
[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
|
||||
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
||||
[Joachim Nilsson]: http://troglobit.com
|
||||
[GitHub]: https://github.com/troglobit/finit
|
||||
[Travis]: https://travis-ci.org/troglobit/finit
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
|
||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
==============================================================================
|
||||
Finit — A fast plugin-based /sbin/init with service monitoring
|
||||
==============================================================================
|
||||
|
||||
Welcome to finit!
|
||||
|
||||
This is an extremely fast /sbin/init replacement with focus on small
|
||||
embedded GNU/Linux systems. Based on Claudio Matsuoka's original finit_
|
||||
with I/O, service and hook plugin extensions. The original was a
|
||||
reimplementation of the EeePC fastinit_ daemon based on its system
|
||||
calls with gaps filled with frog DNA.
|
||||
|
||||
In the world of finit we write C plugins that hook into and extend the
|
||||
``main()`` loop of ``/sbin/init``, this is what gives the system its raw
|
||||
performance and the user full control.
|
||||
|
||||
If you chose finit you know what you want, a fast no hassles boot!
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
**Service monitoring**
|
||||
Restarting a service when it exits
|
||||
|
||||
**Plugins**
|
||||
Extend and modify finit behavior. See examples in plugins/ directory.
|
||||
Plugin capabilities:
|
||||
|
||||
* Service callbacks — should the service run/restart/stop?
|
||||
* Hooks — hook into the boot at predefined points to extend finit
|
||||
* I/O — listen to external events and control finit behaviour/services
|
||||
|
||||
Most extensions and functionality not purely related to what an /sbin/init
|
||||
needs to start a system from the original finit_ has been reactored into
|
||||
plugins with either hooks or I/O demands.
|
||||
|
||||
|
||||
/etc/finit.conf
|
||||
---------------
|
||||
|
||||
Contrary to most script based init alternatives (`SysV init`_, upstart_,
|
||||
OpenRC_ and the likes) finit instead reads its configuration from
|
||||
``/etc/finit.conf``, see the source code for available options.
|
||||
|
||||
When running ``make install`` no default ``/etc/finit.conf`` will be
|
||||
provided since the 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!
|
||||
|
||||
|
||||
/etc/finit.d
|
||||
------------
|
||||
|
||||
At the end of the boot, when networking and all services are up, finit
|
||||
calls its built-in run-parts(8) on the ``/etc/finit.d/`` directory, akin
|
||||
to how the ``/ec/rc.local`` file works in most other inits, only finit
|
||||
runs a directory of scripts. This replaces the earlier
|
||||
``/usr/sbin/services.sh`` support of the original finit_.
|
||||
|
||||
|
||||
Rebooting and Halting
|
||||
---------------------
|
||||
|
||||
Finit handles SIGUSR1 and SIGUSR2 for reboot and halt, and listens to
|
||||
``/dev/initctl`` so standard Linux reboot and halt commands should also
|
||||
work.
|
||||
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
The finit build system does not employ the GNU Configure and Build System,
|
||||
instead standard makefiles are used. The user is encouraged to make source
|
||||
code changes, using defines and conditionally building plugins instead to
|
||||
alter the behavior of finit.
|
||||
|
||||
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
|
||||
|
||||
**VERSION=**
|
||||
Defaults to the currently released version of finit, e.g., 1.3 but can
|
||||
be overridden by packages to add a suffix or completely alter the version.
|
||||
|
||||
**CFLAGS=**
|
||||
Standard CFLAGS are inherited from the build enviornmant.
|
||||
|
||||
**CPPFLAGS=**
|
||||
Standard CPPFLAGS are inherited from the build enviornmant.
|
||||
|
||||
**LDFLAGS=**
|
||||
Standard LDFLAGS are inherited from the build enviornmant.
|
||||
|
||||
**LDLIBS=**
|
||||
Standard LIBLIBS are inherited from the build enviornmant.
|
||||
|
||||
**prefix=**
|
||||
Base prefix path for all files, except ``sbinbdir`` and ``sysconfdir``.
|
||||
Used in concert with the ``DESTDIR`` variable. Defaults to ``/usr``
|
||||
|
||||
**sbindir=**
|
||||
Path to where resulting binaries should install to. Used in concert
|
||||
with the ``DESTDIR`` variable. Defaults to ``/sbin``
|
||||
|
||||
**sysconfdir=**
|
||||
Path to where finit configuration files should install to. Used in
|
||||
concert with the ``DESTDIR`` variable. Defaults to ``/etc``, but is
|
||||
currently unused.
|
||||
|
||||
**PLUGINS=**
|
||||
List of stock finit plugins to build and install.
|
||||
|
||||
**plugindir=**
|
||||
Absolute path to where finit should look for dynamically loadable plugins
|
||||
at runtime. At installation prepended by ``DESTDIR`` and ``prefix``.
|
||||
Defaults to ``/lib/finit/plugins``
|
||||
|
||||
**DESTDIR=**
|
||||
Used by packagers and distributions when building a relocatable
|
||||
bundle of files. Alwawys prepended to the ``prefix`` destination
|
||||
directory.
|
||||
|
||||
**Example**::
|
||||
|
||||
$ tar xfJ finit-1.3.tar.xz
|
||||
$ PLUGINS="initctl.so hwclock.so" DESTDIR=/tmp/finit/dst \
|
||||
make -C finit-1.3/ clean install
|
||||
make: Entering directory `/home/troglobit/finit-1.3'
|
||||
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
|
||||
INSTALL /tmp/finit/dst/sbin/finit
|
||||
INSTALL /tmp/finit/dst/lib/finit/plugins/initctl.so
|
||||
INSTALL /tmp/finit/dst/lib/finit/plugins/hwclock.so
|
||||
make: Leaving directory `/home/troglobit/finit-1.3'
|
||||
|
||||
In this example the `finit-1.3.tar.xz`_ archive is unpacked to the
|
||||
user's home directory, built and installed to a temporary staging
|
||||
directory. The enviroment variables ``DESTDIR`` and ``PLUGINS`` are
|
||||
changed to suit this particular build.
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The default install does not setup finit as the system default
|
||||
``/sbin/init``, neither does it setup an initial ``/etc/finit.conf``.
|
||||
|
||||
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::
|
||||
|
||||
init=/sbin/finit
|
||||
|
||||
|
||||
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 comile-time tweaks::
|
||||
|
||||
init=/sbin/finit --debug
|
||||
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
Finit is maintained collaborativly at http://github.com/troglobit/finit —
|
||||
please file a bug report, clone it, or send pull requests for bug fixes and
|
||||
proposed extensions, or become a co-maintainer by contacting the main author.
|
||||
|
||||
Regards
|
||||
/Joachim Nilsson <troglobit@gmail.com>
|
||||
|
||||
.. _finit: http://helllabs.org/finit/
|
||||
.. _fastinit: http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||
.. _`SysV init`: http://savannah.nongnu.org/projects/sysvinit
|
||||
.. _upstart: http://upstart.ubuntu.com/
|
||||
.. _openrc: http://www.gentoo.org/proj/en/base/openrc/
|
||||
.. _`finit-1.3.tar.xz`: http://github.com/downloads/troglobit/finit/finit-1.3.tar.xz
|
||||
@@ -1,6 +0,0 @@
|
||||
* FHS changes, see http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run
|
||||
* Look at Erik Troan's nash linuxrc parser for more inspiration
|
||||
* Look at how to support a standard SysV init runlevel mechanism
|
||||
* Arguments to finit, like to SysV init "init q" to reload finit.conf at runtime, etc.
|
||||
* Implement initctl stop|start|reload <SVC> and service <SVC> stop|start|reload on top
|
||||
* Write man pages for finit and finit.conf, steal from pimd man pages...
|
||||
@@ -0,0 +1,154 @@
|
||||
Goals of Finit
|
||||
==============
|
||||
|
||||
The intent of finit is to monitor not just processes, but to supervise
|
||||
an entire system and the behavior of certain processes that declare
|
||||
themselves to be finit compliant. Similar to launchd, the goal of
|
||||
finit is to be a replacement for:
|
||||
|
||||
* init
|
||||
* inetd
|
||||
* crond
|
||||
* watchdogd
|
||||
|
||||
A small and simple replacement, primarily for embedded systems, with a
|
||||
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
|
||||
|
||||
* 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 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
|
||||
----
|
||||
|
||||
Finit in itself is not SysV Init compatible, it doesn't use `/etc/rc.d`
|
||||
and `/etc/inet.d` scripts to boot the system. However, a plausible way
|
||||
to achieve compatibility would be to add a plugin to finit which reads
|
||||
`/etc/inittab` to be able to start standard Linux systems.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Crond
|
||||
-----
|
||||
|
||||
Requirements are quite rudimentary, basic cron functionality, mainly
|
||||
system-level timed periodic tasks:
|
||||
|
||||
* Periodically run a task, in a matching runlevel
|
||||
* Optionally run as non-root (`crontab -e` as operator)
|
||||
|
||||
Proposed syntax:
|
||||
|
||||
# Crontab
|
||||
cron @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
||||
|
||||
# One-shot 'at' command
|
||||
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
||||
|
||||
Notice the ISO date in notation. The runlevels is extra filtering
|
||||
sugar. E.g., if cron service is only allowed in runlevels two or three
|
||||
it will not start if system currently is in runlevel four.
|
||||
|
||||
To run a command as another user the `.conf` file must have a that
|
||||
owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
|
||||
only hold `cron ...` lines.
|
||||
|
||||
What would be extremely useful is if `initctl`, or a homegrown `at`,
|
||||
could support the basic functionality of the `at` command directly from
|
||||
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).
|
||||
|
||||
|
||||
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/
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
@@ -0,0 +1,363 @@
|
||||
/* External client API, using UNIX domain socket.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.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 == ':';
|
||||
}
|
||||
|
||||
/* Sanitize user input, make sure to NUL terminate. */
|
||||
static char *sanitize(char *arg, size_t len)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
while (isallowed(arg[i]) && i < len)
|
||||
i++;
|
||||
|
||||
if (i + 1 < len) {
|
||||
arg[i + 1] = 0;
|
||||
return arg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int call(int (*action)(svc_t *), char *buf, size_t len)
|
||||
{
|
||||
int result = 0;
|
||||
char *input, *token, *pos;
|
||||
|
||||
input = sanitize(buf, len);
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
token = strtok_r(input, " ", &pos);
|
||||
while (token) {
|
||||
svc_t *svc;
|
||||
char *ptr = strchr(token, ':');
|
||||
|
||||
if (isdigit(token[0])) {
|
||||
int job = atonum(token);
|
||||
|
||||
if (!ptr) {
|
||||
svc = svc_job_iterator(1, job);
|
||||
while (svc) {
|
||||
result += action(svc);
|
||||
svc = svc_job_iterator(0, job);
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
job = atonum(token);
|
||||
result += action(svc_find_by_jobid(job, atonum(ptr)));
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!ptr) {
|
||||
svc = svc_named_iterator(1, token);
|
||||
while (svc) {
|
||||
result += action(svc);
|
||||
svc = svc_named_iterator(0, token);
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
result += action(svc_find_by_nameid(token, atonum(ptr)));
|
||||
}
|
||||
}
|
||||
|
||||
token = strtok_r(NULL, " ", &pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int service_pause(svc_t *svc)
|
||||
{
|
||||
return service_stop(svc, SVC_PAUSED_STATE);
|
||||
}
|
||||
|
||||
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 do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
static int do_query_inetd(char *buf, size_t len)
|
||||
{
|
||||
int id = 1;
|
||||
char *ptr, *input = sanitize(buf, len);
|
||||
svc_t *svc;
|
||||
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
ptr = strchr(input, ':');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
id = atonum(ptr);
|
||||
}
|
||||
|
||||
svc = svc_find_by_jobid(atonum(input), id);
|
||||
if (!svc || !svc_is_inetd(svc)) {
|
||||
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return inetd_filter_str(&svc->inetd, buf, len);
|
||||
}
|
||||
#endif /* !INETD_DISABLED */
|
||||
|
||||
typedef struct {
|
||||
char *event;
|
||||
void (*cb)(void);
|
||||
} ev_t;
|
||||
|
||||
ev_t ev_list[] = {
|
||||
{ "RELOAD", conf_reload_dynamic },
|
||||
{ "STOP", service_stop_dynamic },
|
||||
{ "START", service_start_dynamic },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int do_handle_event(char *event)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ev_list[i].event; i++) {
|
||||
ev_t *e = &ev_list[i];
|
||||
size_t len = MAX(strlen(e->event), strlen(event));
|
||||
|
||||
if (!strncasecmp(e->event, event, len)) {
|
||||
e->cb();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: iterate over all services' events before failing. */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int do_handle_emit(char *buf, size_t len)
|
||||
{
|
||||
int result = 0;
|
||||
char *input, *event, *pos;
|
||||
|
||||
input = sanitize(buf, len);
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
event = strtok_r(input, " ", &pos);
|
||||
while (event) {
|
||||
result += do_handle_event(event);
|
||||
event = strtok_r(NULL, " ", &pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
int sd;
|
||||
struct init_request rq;
|
||||
|
||||
sd = accept(w->fd, NULL, NULL);
|
||||
if (sd < 0) {
|
||||
_pe("Failed serving API request");
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int result = 0;
|
||||
ssize_t len;
|
||||
|
||||
len = read(sd, &rq, sizeof(rq));
|
||||
if (len <= 0) {
|
||||
if (-1 == len) {
|
||||
if (EINTR == errno)
|
||||
continue;
|
||||
|
||||
if (EAGAIN == errno)
|
||||
break;
|
||||
|
||||
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||
_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';
|
||||
/* Fall through to regular processing */
|
||||
|
||||
case '1'...'5':
|
||||
case '7'...'9':
|
||||
_d("Setting new runlevel %c", rq.runlevel);
|
||||
service_runlevel(rq.runlevel - '0');
|
||||
break;
|
||||
|
||||
case '6':
|
||||
_d("Rebooting system (SIGUSR1)");
|
||||
do_shutdown(SIGUSR1);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported runlevel: %d", rq.runlevel);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case INIT_CMD_DEBUG:
|
||||
debug = !debug;
|
||||
if (debug)
|
||||
verbose = 1;
|
||||
else
|
||||
verbose = quiet ? 0 : VERBOSE_MODE;
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
||||
service_reload_dynamic();
|
||||
break;
|
||||
|
||||
case INIT_CMD_START_SVC:
|
||||
result = do_start(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_STOP_SVC:
|
||||
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;
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
case INIT_CMD_QUERY_INETD:
|
||||
result = do_query_inetd(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case INIT_CMD_EMIT:
|
||||
result = do_handle_emit(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_ACK:
|
||||
_d("Client failed reading ACK.");
|
||||
goto leave;
|
||||
|
||||
default:
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
rq.cmd = INIT_CMD_NACK;
|
||||
else
|
||||
rq.cmd = INIT_CMD_ACK;
|
||||
len = write(sd, &rq, sizeof(rq));
|
||||
if (len != sizeof(rq))
|
||||
_d("Failed sending ACK/NACK back to client.");
|
||||
}
|
||||
|
||||
leave:
|
||||
close(sd);
|
||||
}
|
||||
|
||||
int api_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int sd;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd) {
|
||||
_pe("Failed starting external API socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
erase(INIT_SOCKET);
|
||||
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);
|
||||
|
||||
error:
|
||||
_pe("Failed intializing API socket");
|
||||
close(sd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,119 @@
|
||||
/* Simple telinit client
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static int do_send(int cmd, int runlevel)
|
||||
{
|
||||
int fd;
|
||||
int result = 255;
|
||||
ssize_t len;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = cmd,
|
||||
.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;
|
||||
}
|
||||
|
||||
/* telinit q | telinit <0-9> */
|
||||
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"
|
||||
"Commands:\n"
|
||||
" q | Q Reload *.conf in /etc/finit.d/, like SIGHUP\n"
|
||||
" 0 - 9 Change runlevel: 0 halt, 6 reboot\n\n", __progname);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int client(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"version", 0, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
verbose = 0;
|
||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'v':
|
||||
return puts("v" VERSION) == EOF;
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
int req = (int)argv[optind][0];
|
||||
|
||||
/* Compat: 'init <0-9>' */
|
||||
if (isdigit(req))
|
||||
return do_send(INIT_CMD_RUNLVL, req);
|
||||
|
||||
if (req == 'q' || req == 'Q')
|
||||
return do_send(INIT_CMD_RELOAD, 0);
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -1,20 +1,22 @@
|
||||
# Top directory for building complete system, fall back to this directory
|
||||
ROOTDIR ?= $(TOPDIR)
|
||||
|
||||
# Some junk files we always want to be removed when doing a make clean.
|
||||
JUNK = *~ *.bak *.aux *.dvi *.idx *.ind *.log *.ps *.map .*.d DEADJOE semantic.cache *.gdb *.elf core core.*
|
||||
MAKE := @$(MAKE)
|
||||
MAKEFLAGS = --no-print-directory --silent
|
||||
INSTALL := install --backup=off
|
||||
STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755
|
||||
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
|
||||
ARFLAGS := crus
|
||||
|
||||
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC
|
||||
|
||||
# Smart autodependecy generation via GCC -M.
|
||||
.%.d: %.c
|
||||
@$(SHELL) -ec "$(CC) -MM $(CFLAGS) $(CPPFLAGS) $< \
|
||||
| sed 's,.*: ,$*.o $@ : ,g' > $@; \
|
||||
[ -s $@ ] || rm -f $@"
|
||||
|
||||
# Override default implicit rules
|
||||
%.o: %.c
|
||||
@printf " CC $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
@$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
@$(CC) $(CFLAGS) $(CPPFLAGS) -c -MMD -MP -o $@ $<
|
||||
|
||||
%: %.o
|
||||
@printf " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Parser for finit.conf
|
||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||
*
|
||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-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
|
||||
@@ -21,118 +21,426 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
#include "libite/lite.h"
|
||||
#include "helpers.h"
|
||||
|
||||
/* Match one command. */
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
|
||||
static int parse_conf(char *file);
|
||||
|
||||
|
||||
static char *build_cmd(char *cmd, char *line, int len)
|
||||
static char *strip_line(char *line)
|
||||
{
|
||||
int l;
|
||||
char *c;
|
||||
char *ptr;
|
||||
|
||||
/* Trim leading whitespace */
|
||||
while (*line && (*line == ' ' || *line == '\t'))
|
||||
while (*line && isblank(*line))
|
||||
line++;
|
||||
|
||||
if (!cmd) {
|
||||
cmd = malloc (strlen(line) + 1);
|
||||
if (!cmd) {
|
||||
_e("No memory left for '%s'", line);
|
||||
return NULL;
|
||||
}
|
||||
*cmd = 0;
|
||||
}
|
||||
c = cmd + strlen(cmd);
|
||||
for (l = 0; *line && *line != '#' && *line != '\t' && l < len; l++)
|
||||
*c++ = *line++;
|
||||
*c = 0;
|
||||
/* Strip any comment at end of line */
|
||||
ptr = line;
|
||||
while (*ptr && *ptr != '#')
|
||||
ptr++;
|
||||
*ptr = 0;
|
||||
|
||||
_d("cmd = %s", cmd);
|
||||
return cmd;
|
||||
return line;
|
||||
}
|
||||
|
||||
void parse_finit_conf(char *file)
|
||||
void conf_parse_cmdline(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE];
|
||||
|
||||
if ((fp = fopen("/proc/cmdline", "r")) != NULL) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||
int conf_parse_runlevels(char *runlevels)
|
||||
{
|
||||
int i, not = 0, bitmask = 0;
|
||||
|
||||
if (!runlevels)
|
||||
runlevels = "[234]";
|
||||
i = 1;
|
||||
while (i) {
|
||||
int level;
|
||||
char lvl = runlevels[i++];
|
||||
|
||||
if (']' == lvl || 0 == lvl)
|
||||
break;
|
||||
if ('!' == lvl) {
|
||||
not = 1;
|
||||
bitmask = 0x3FE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('s' == lvl || 'S' == lvl)
|
||||
lvl = '0';
|
||||
|
||||
level = lvl - '0';
|
||||
if (level > 9 || level < 0)
|
||||
continue;
|
||||
|
||||
if (not)
|
||||
CLRBIT(bitmask, level);
|
||||
else
|
||||
SETBIT(bitmask, level);
|
||||
}
|
||||
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
void conf_parse_events(svc_t *svc, char *events)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *ptr;
|
||||
|
||||
if (!svc) {
|
||||
_e("Invalid service pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default we assume UNIX daemons support SIGHUP */
|
||||
svc->sighup = 1;
|
||||
|
||||
if (!events)
|
||||
return;
|
||||
|
||||
/* First character must be '!' if SIGHUP is not supported. */
|
||||
ptr = events;
|
||||
if (ptr[i] == '!') {
|
||||
svc->sighup = 0;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
while (ptr[i] != '>' && ptr[i] != 0)
|
||||
i++;
|
||||
ptr[i] = 0;
|
||||
|
||||
if (i >= sizeof(svc->events)) {
|
||||
FLOG_WARN("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));
|
||||
}
|
||||
|
||||
static void parse_static(char *line)
|
||||
{
|
||||
char *x;
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
/* Default username and hostname */
|
||||
/* Do this before mounting / read-write
|
||||
* XXX: Move to plugin which checks /etc/fstab instead */
|
||||
if (MATCH_CMD(line, "check ", x)) {
|
||||
char *dev = strip_line(x);
|
||||
|
||||
strcpy(cmd, "/sbin/fsck -C -a ");
|
||||
strlcat(cmd, dev, sizeof(cmd));
|
||||
run_interactive(cmd, "Checking file system %s", dev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "user ", x)) {
|
||||
if (username) free(username);
|
||||
username = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "host ", x)) {
|
||||
if (hostname) free(hostname);
|
||||
hostname = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "module ", x)) {
|
||||
char *mod = strip_line(x);
|
||||
|
||||
strcpy(cmd, "/sbin/modprobe ");
|
||||
strlcat(cmd, mod, sizeof(cmd));
|
||||
run_interactive(cmd, "Loading kernel module %s", mod);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "mknod ", x)) {
|
||||
char *dev = strip_line(x);
|
||||
|
||||
strcpy(cmd, "/bin/mknod ");
|
||||
strlcat(cmd, dev, sizeof(cmd));
|
||||
run_interactive(cmd, "Creating device node %s", dev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "network ", x)) {
|
||||
if (network) free(network);
|
||||
network = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "runparts ", x)) {
|
||||
if (runparts) free(runparts);
|
||||
runparts = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "include ", x)) {
|
||||
char *file = strip_line(x);
|
||||
|
||||
strlcpy(cmd, file, sizeof(cmd));
|
||||
if (!fexist(cmd)) {
|
||||
_e("Cannot find include file %s, absolute path required!", x);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_conf(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "startx ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, strip_line(x), 0, username);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "shutdown ", x)) {
|
||||
if (sdown) free(sdown);
|
||||
sdown = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
/* The desired runlevel to start when leaving bootstrap (S).
|
||||
* Finit supports 1-9, but most systems only use 1-6, where
|
||||
* 6 is reserved for reboot */
|
||||
if (MATCH_CMD(line, "runlevel ", x)) {
|
||||
char *token = strip_line(x);
|
||||
const char *err = NULL;
|
||||
|
||||
cfglevel = strtonum(token, 1, 9, &err);
|
||||
if (err)
|
||||
cfglevel = RUNLEVEL;
|
||||
if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6)
|
||||
cfglevel = 2; /* Fallback */
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Make console & tty dynamically loadable from /etc/finit.d */
|
||||
if (MATCH_CMD(line, "console ", x)) {
|
||||
if (console) free(console);
|
||||
console = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Make console & tty dynamically loadable from /etc/finit.d */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
tty_register(strip_line(x));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_dynamic(char *line, time_t mtime)
|
||||
{
|
||||
char *x;
|
||||
|
||||
/* Skip comments, i.e. lines beginning with # */
|
||||
if (MATCH_CMD(line, "#", x))
|
||||
return;
|
||||
|
||||
/* Monitored daemon, will be respawned on exit, as
|
||||
* long as the (optional) service callback returns
|
||||
* non-zero */
|
||||
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 */
|
||||
if (MATCH_CMD(line, "task ", x)) {
|
||||
service_register(SVC_TYPE_TASK, x, mtime, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Like task but waits for completion, useful w/ [S] */
|
||||
if (MATCH_CMD(line, "run ", x)) {
|
||||
service_register(SVC_TYPE_RUN, x, mtime, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Classic inetd service */
|
||||
if (MATCH_CMD(line, "inetd ", x)) {
|
||||
#ifndef INETD_DISABLED
|
||||
service_register(SVC_TYPE_INETD, x, mtime, NULL);
|
||||
#else
|
||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_conf_dynamic(char *file, time_t mtime)
|
||||
{
|
||||
FILE *fp = fopen(file, "r");
|
||||
|
||||
if (!fp) {
|
||||
_pe("Failed opening %s", file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!feof(fp)) {
|
||||
char line[LINE_SIZE] = "";
|
||||
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
continue;
|
||||
|
||||
chomp(line);
|
||||
_d("dyn conf: %s", line);
|
||||
|
||||
parse_dynamic(line, mtime);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_conf(char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE] = "";
|
||||
char *x;
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If not standard finit.conf, then we want to show just the base name
|
||||
* Loading configuration ............. vs
|
||||
* Loading services configuration ....
|
||||
*/
|
||||
if (!string_match (file, FINIT_CONF)) {
|
||||
/* Remove leading path */
|
||||
x = strrchr(file, '/');
|
||||
if (!x) x = file;
|
||||
else x++;
|
||||
|
||||
/* Remove ending .conf */
|
||||
strlcpy(line, x, sizeof(line));
|
||||
x = strstr(line, ".conf");
|
||||
if (x) *x = 0;
|
||||
|
||||
/* Add empty space. */
|
||||
strcat(line, " ");
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
print(0, "Loading %sconfiguration", line);
|
||||
while (!feof(fp)) {
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
continue;
|
||||
chomp(line);
|
||||
|
||||
_d("conf: %s", line);
|
||||
parse_static(line);
|
||||
parse_dynamic(line, 0);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reload all *.conf in /etc/finit.d/ */
|
||||
void conf_reload_dynamic(void)
|
||||
{
|
||||
int i, num;
|
||||
char *dir = rcsd;
|
||||
struct dirent **e;
|
||||
|
||||
/* Mark and sweep */
|
||||
svc_mark_dynamic();
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("No files found in %s, skipping ...", dir);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char *name = e[i]->d_name;
|
||||
char path[CMD_SIZE];
|
||||
struct stat st;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
|
||||
/* Check that it's an actual file ... */
|
||||
if (stat(path, &st)) {
|
||||
_d("Cannot even read .conf file %s, skipping ...", path);
|
||||
continue;
|
||||
}
|
||||
if (S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
|
||||
/* Check that file ends with '.conf' */
|
||||
if (strcmp(&path[strlen(path) - 5], ".conf")) {
|
||||
_d("File %s is not a .conf, skipping ... ", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
parse_conf_dynamic(path, st.st_mtime);
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
set_hostname(&hostname);
|
||||
}
|
||||
|
||||
int conf_parse_config(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
username = strdup(DEFUSER);
|
||||
hostname = strdup(DEFHOST);
|
||||
|
||||
if ((fp = fopen(file, "r")) != NULL) {
|
||||
char *x;
|
||||
result = parse_conf(FINIT_CONF);
|
||||
if (!tty_num()) {
|
||||
char *fallback = FALLBACK_SHELL;
|
||||
|
||||
_d("Parse %s ...", file);
|
||||
while (!feof(fp)) {
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
continue;
|
||||
chomp(line);
|
||||
if (console)
|
||||
fallback = console;
|
||||
|
||||
_d("conf: %s", line);
|
||||
|
||||
/* Skip comments. */
|
||||
if (MATCH_CMD(line, "#", x)) {
|
||||
continue;
|
||||
}
|
||||
/* Do this before mounting / read-write */
|
||||
if (MATCH_CMD(line, "check ", x)) {
|
||||
strcpy(cmd, "/sbin/fsck -C -a ");
|
||||
build_cmd(cmd, x, CMD_SIZE);
|
||||
run_interactive(cmd, "Checking file system integrity on %s", x);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "user ", x)) {
|
||||
if (username) free(username);
|
||||
username = build_cmd(NULL, x, USERNAME_SIZE);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "host ", x)) {
|
||||
if (hostname) free(hostname);
|
||||
hostname = build_cmd(NULL, x, HOSTNAME_SIZE);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "shutdown ", x)) {
|
||||
if (sdown) free(sdown);
|
||||
sdown = build_cmd(NULL, x, CMD_SIZE);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "module ", x)) {
|
||||
strcpy(cmd, "/sbin/modprobe ");
|
||||
build_cmd(cmd, x, CMD_SIZE);
|
||||
run_interactive(cmd, " Loading module %s", x);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "mknod ", x)) {
|
||||
strcpy(cmd, "/bin/mknod ");
|
||||
build_cmd(cmd, x, CMD_SIZE);
|
||||
run_interactive(cmd, " Creating device node %s", x);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "network ", x)) {
|
||||
if (network) free(network);
|
||||
network = build_cmd(NULL, x, CMD_SIZE);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "startx ", x)) {
|
||||
svc_register(x, username);
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "service ", x)) {
|
||||
svc_register(x, NULL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
tty_register(fallback);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+11
-21
@@ -1,6 +1,6 @@
|
||||
/* Check if file exists
|
||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||
*
|
||||
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
|
||||
* Copyright (c) 2012-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
|
||||
@@ -21,28 +21,18 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#ifndef FINIT_CONF_H_
|
||||
#define FINIT_CONF_H_
|
||||
|
||||
/**
|
||||
* fexist - Check if a file exists in the file system.
|
||||
* @file: File to look for, with full path.
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) if the file exists, otherwise %FALSE(0).
|
||||
*/
|
||||
int fexist(char *file)
|
||||
{
|
||||
if (!file) {
|
||||
errno = EINVAL;
|
||||
return 0; /* Doesn't exist ... */
|
||||
}
|
||||
#include "svc.h"
|
||||
|
||||
if (-1 == access(file, F_OK))
|
||||
return 0;
|
||||
void conf_parse_cmdline (void);
|
||||
int conf_parse_runlevels (char *runlevels);
|
||||
void conf_parse_events (svc_t *svc, char *events);
|
||||
int conf_parse_config (void);
|
||||
void conf_reload_dynamic (void);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* FINIT_CONF_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
@@ -0,0 +1,451 @@
|
||||
#!/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
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: keyboard-setup
|
||||
# Required-Start: mountkernfs
|
||||
# Required-Stop:
|
||||
# Should-Start: keymap udev
|
||||
# X-Start-Before: checkroot
|
||||
# Default-Start: S
|
||||
# Default-Stop:
|
||||
# X-Interactive: true
|
||||
# Short-Description: Set preliminary keymap
|
||||
# Description: Set the console keyboard as early as possible
|
||||
# so during the file systems checks the administrator
|
||||
# can interact. At this stage of the boot process
|
||||
# only the ASCII symbols are supported.
|
||||
### END INIT INFO
|
||||
|
||||
set -e
|
||||
|
||||
# This script is not used by keyboard-configuration. It is used
|
||||
# jointly by console-setup and console-setup-mini
|
||||
|
||||
test -f /bin/setupcon || exit 0
|
||||
|
||||
if [ -f /lib/lsb/init-functions ]; then
|
||||
. /lib/lsb/init-functions
|
||||
else
|
||||
log_action_begin_msg () {
|
||||
echo -n "$@... "
|
||||
}
|
||||
|
||||
log_action_end_msg () {
|
||||
if [ "$1" -eq 0 ]; then
|
||||
echo done.
|
||||
else
|
||||
echo failed.
|
||||
fi
|
||||
}
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
stop)
|
||||
# keyboard-setup isn't a daemon
|
||||
;;
|
||||
start|force-reload|restart|reload)
|
||||
case `readlink /proc/self/fd/2` in
|
||||
/dev/tty[0-9]*|/dev/vc/[0-9]*|/dev/console)
|
||||
log_action_begin_msg "Setting preliminary keymap"
|
||||
if setupcon -k; then
|
||||
log_action_end_msg 0
|
||||
else
|
||||
log_action_end_msg $?
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo 'Usage: /etc/init.d/keyboard-setup {start|reload|restart|force-reload|stop}'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: acpid
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# X-Start-Before: kdm gdm xdm hal
|
||||
# X-Stop-After: kdm gdm xdm hal
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop:
|
||||
# Short-Description: Start the Advanced Configuration and Power Interface daemon
|
||||
# Description: Provide a socket for X11, hald and others to multiplex
|
||||
# kernel ACPI events.
|
||||
### END INIT INFO
|
||||
|
||||
set -e
|
||||
|
||||
ACPID="/usr/sbin/acpid"
|
||||
DEFAULTS="/etc/default/acpid"
|
||||
|
||||
# Check for daemon presence
|
||||
[ -x "$ACPID" ] || exit 0
|
||||
|
||||
OPTIONS=""
|
||||
MODULES=""
|
||||
# Include acpid defaults if available
|
||||
[ -r "$DEFAULTS" ] && . "$DEFAULTS"
|
||||
|
||||
# Get lsb functions
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
# As the name says. If the kernel supports modules, it'll try to load
|
||||
# the ones listed in "MODULES".
|
||||
load_modules() {
|
||||
[ -f /proc/modules ] || return 0
|
||||
if [ "$MODULES" = "all" ]; then
|
||||
MODULES="$(sed -rn 's#^(/lib/modules/[^/]+/)?kernel/(drivers|ubuntu)/acpi/([^/]+/)*(.*)\.ko:.*#\4#p' "/lib/modules/$(uname -r)/modules.dep")"
|
||||
fi
|
||||
|
||||
if [ -z "$MODULES" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
log_begin_msg "Loading ACPI kernel modules..."
|
||||
# work around a bug in initramfs which leaks this env var Launchpad #291619
|
||||
# and a bug in modprobe --all --quiet which doesn't load all modules and
|
||||
# exits with non-zero exit status Debian #504088
|
||||
unset MODPROBE_OPTIONS
|
||||
modprobe --all --use-blacklist $MODULES 2>/dev/null
|
||||
log_end_msg $?
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
load_modules || true
|
||||
log_begin_msg "Starting ACPI services..."
|
||||
start-stop-daemon --start --quiet --oknodo --exec "$ACPID" -- $OPTIONS
|
||||
log_end_msg $?
|
||||
;;
|
||||
stop)
|
||||
log_begin_msg "Stopping ACPI services..."
|
||||
start-stop-daemon --stop --quiet --oknodo --retry 2 --exec "$ACPID"
|
||||
log_end_msg $?
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
sleep 1
|
||||
$0 start
|
||||
;;
|
||||
reload|force-reload)
|
||||
log_begin_msg "Reloading ACPI services..."
|
||||
start-stop-daemon --stop --signal 1 --exec "$ACPID"
|
||||
log_end_msg $?
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$ACPID" acpid
|
||||
;;
|
||||
*)
|
||||
log_success_msg "Usage: /etc/init.d/acpid {start|stop|restart|reload|force-reload|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
Executable
+259
@@ -0,0 +1,259 @@
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: kbd
|
||||
# Required-Start: $remote_fs
|
||||
# Required-Stop:
|
||||
# Default-Start: S
|
||||
# Default-Stop:
|
||||
# X-Interactive: true
|
||||
# Description: Set console screen modes and fonts
|
||||
# Short-Description: Prepare console
|
||||
### END INIT INFO
|
||||
|
||||
# This is the boot script for the `kbd' package.
|
||||
# It loads parameters from /etc/kbd/config and maybe loads
|
||||
# default font and map.
|
||||
# (c) 1997 Yann Dirson
|
||||
|
||||
PKG=kbd
|
||||
if [ -r /etc/$PKG/config ]; then
|
||||
. /etc/$PKG/config
|
||||
fi
|
||||
|
||||
if [ -d /etc/$PKG/config.d ]; then
|
||||
for i in `run-parts --list /etc/$PKG/config.d `; do
|
||||
. $i
|
||||
done
|
||||
fi
|
||||
|
||||
# do some magic with the variables for compatibility with the config
|
||||
# file of console-tools
|
||||
for vc in '' `set | grep "^.*_vc[0-9][0-9]*=" | sed 's/^.*\(_vc[0-9][0-9]*\)=.*/\1/'`
|
||||
do
|
||||
eval [ '"'\${SCREEN_FONT$vc}'"' ] && eval CONSOLE_FONT$vc=\${CONSOLE_FONT$vc:-\${SCREEN_FONT$vc}}
|
||||
eval [ '"'\${SCREEN_FONT_MAP$vc}'"' ] && eval FONT_MAP$vc=\${FONT_MAP$vc:-\${SCREEN_FONT_MAP$vc}}
|
||||
eval [ '"'\${APP_CHARSET_MAP$vc}'"' ] && eval CONSOLE_MAP$vc=\${CONSOLE_MAP$vc:-\${APP_CHARSET_MAP$vc}}
|
||||
done
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||
SETFONT_OPT="-v"
|
||||
|
||||
if which setupcon >/dev/null
|
||||
then
|
||||
HAVE_SETUPCON=yes
|
||||
fi
|
||||
|
||||
# set DEVICE_PREFIX depending on devfs/udev
|
||||
if [ -d /dev/vc ]; then
|
||||
DEVICE_PREFIX="/dev/vc/"
|
||||
else
|
||||
DEVICE_PREFIX="/dev/tty"
|
||||
fi
|
||||
|
||||
# determine the system charmap
|
||||
ENV_FILE=''
|
||||
[ -r /etc/environment ] && ENV_FILE="/etc/environment"
|
||||
[ -r /etc/default/locale ] && ENV_FILE="/etc/default/locale"
|
||||
[ "$ENV_FILE" ] && CHARMAP=$(set -a && . "$ENV_FILE" && locale charmap)
|
||||
if [ "$CHARMAP" = "UTF-8" -a -z "$CONSOLE_MAP" ]
|
||||
then
|
||||
UNICODE_MODE=yes
|
||||
fi
|
||||
|
||||
reset_vga_palette ()
|
||||
{
|
||||
if [ -f /proc/fb ]; then
|
||||
# They have a framebuffer device.
|
||||
# That means we have work to do...
|
||||
echo -n "]R"
|
||||
fi
|
||||
}
|
||||
|
||||
unicode_start_stop ()
|
||||
{
|
||||
vc=$1
|
||||
if [ -n "$UNICODE_MODE" -a -z "`eval echo \\$CONSOLE_MAP_vc$vc`" ]; then
|
||||
action=unicode_start
|
||||
else
|
||||
action=unicode_stop
|
||||
fi
|
||||
if [ "${CONSOLE_FONT}" ]; then
|
||||
$action "${CONSOLE_FONT}" < ${DEVICE_PREFIX}$vc > ${DEVICE_PREFIX}$vc 2> /dev/null || true
|
||||
else
|
||||
$action < ${DEVICE_PREFIX}$vc > ${DEVICE_PREFIX}$vc 2> /dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
setup ()
|
||||
{
|
||||
# be sure the main program is installed
|
||||
which setfont >/dev/null || return
|
||||
|
||||
VT="no"
|
||||
# If we can't access the console, quit
|
||||
CONSOLE_TYPE=`fgconsole 2>/dev/null` || return
|
||||
|
||||
if [ ! $CONSOLE_TYPE = "serial" ]; then
|
||||
readlink /proc/self/fd/0 | grep -q -e /dev/vc -e '/dev/tty[^p]' -e /dev/console
|
||||
if [ $? -eq 0 ]; then
|
||||
VT="yes"
|
||||
reset_vga_palette
|
||||
fi
|
||||
fi
|
||||
|
||||
[ $VT = "no" ] && return
|
||||
|
||||
# start vcstime
|
||||
if [ "${DO_VCSTIME}" = "yes" ] && which vcstime >/dev/null; then
|
||||
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Starting clock on text console"
|
||||
vcstime &
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||
fi
|
||||
|
||||
if [ -d /etc/init ] && which initctl >/dev/null; then
|
||||
# Upstart
|
||||
LIST_CONSOLES=`cd /etc/init; find -name 'tty*.conf' -printf '%f ' | sed -e 's/[^0-9 ]//g'`
|
||||
else
|
||||
# traditional SysV init
|
||||
LIST_CONSOLES=`sed -e '/^ *#/d' /etc/inittab | grep -e '\<tty[0-9]*\>' | awk -F: '{printf "%s ", $1}'`
|
||||
fi
|
||||
|
||||
# Global default font+map
|
||||
if [ -z "${HAVE_SETUPCON}" -a "${CONSOLE_FONT}" ]; then
|
||||
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting up general console font"
|
||||
sfm="${FONT_MAP}" && [ "$sfm" ] && sfm="-u $sfm"
|
||||
acm="${CONSOLE_MAP}" && [ "$acm" ] && acm="-m $acm"
|
||||
|
||||
# Set for the first 6 VCs (as they are allocated in /etc/inittab)
|
||||
for vc in $LIST_CONSOLES
|
||||
do
|
||||
if ! ( unicode_start_stop $vc \
|
||||
&& setfont -C ${DEVICE_PREFIX}$vc ${SETFONT_OPT} $sfm ${CONSOLE_FONT} $acm )
|
||||
then
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 1
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||
fi
|
||||
|
||||
# Default to Unicode mode for new VTs?
|
||||
if [ -f /sys/module/vt/parameters/default_utf8 ]; then
|
||||
if [ -n "$UNICODE_MODE" ]; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi > /sys/module/vt/parameters/default_utf8
|
||||
fi
|
||||
|
||||
# Per-VC font+sfm
|
||||
PERVC_FONTS="`set | grep "^CONSOLE_FONT_vc[0-9]*=" | tr -d \' `"
|
||||
if [ -z "${HAVE_SETUPCON}" -a "${PERVC_FONTS}" ]; then
|
||||
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting up per-VC fonts"
|
||||
for font in ${PERVC_FONTS}
|
||||
do
|
||||
# extract VC and FONTNAME info from variable setting
|
||||
vc=`echo $font | cut -b16- | cut -d= -f1`
|
||||
eval font=\$CONSOLE_FONT_vc$vc
|
||||
# eventually find an associated SFM
|
||||
eval sfm=\${FONT_MAP_vc${vc}}
|
||||
[ "$sfm" ] && sfm="-u $sfm"
|
||||
if ! ( unicode_start_stop $vc \
|
||||
&& setfont -C ${DEVICE_PREFIX}$vc ${SETFONT_OPT} $sfm $font )
|
||||
then
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 1
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||
fi
|
||||
|
||||
|
||||
# Per-VC ACMs
|
||||
PERVC_ACMS="`set | grep "^CONSOLE_MAP_vc[0-9]*=" | tr -d \' `"
|
||||
if [ -z "${HAVE_SETUPCON}" -a "${PERVC_ACMS}" ]; then
|
||||
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting up per-VC ACM's"
|
||||
for acm in ${PERVC_ACMS}
|
||||
do
|
||||
# extract VC and ACM_FONTNAME info from variable setting
|
||||
vc=`echo $acm | cut -b15- | cut -d= -f1`
|
||||
eval acm=\$CONSOLE_MAP_vc$vc
|
||||
if ! setfont -C "${DEVICE_PREFIX}$vc" ${SETFONT_OPT} -m "$acm"; then
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 1
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||
fi
|
||||
|
||||
|
||||
# screensaver stuff
|
||||
setterm_args=""
|
||||
if [ "$BLANK_TIME" ]; then
|
||||
setterm_args="$setterm_args -blank $BLANK_TIME"
|
||||
fi
|
||||
if [ "$BLANK_DPMS" ]; then
|
||||
setterm_args="$setterm_args -powersave $BLANK_DPMS"
|
||||
fi
|
||||
if [ "$POWERDOWN_TIME" ]; then
|
||||
setterm_args="$setterm_args -powerdown $POWERDOWN_TIME"
|
||||
fi
|
||||
if [ "$setterm_args" ]; then
|
||||
setterm $setterm_args
|
||||
fi
|
||||
|
||||
# Keyboard rate and delay
|
||||
KBDRATE_ARGS=""
|
||||
if [ -n "$KEYBOARD_RATE" ]; then
|
||||
KBDRATE_ARGS="-r $KEYBOARD_RATE"
|
||||
fi
|
||||
if [ -n "$KEYBOARD_DELAY" ]; then
|
||||
KBDRATE_ARGS="$KBDRATE_ARGS -d $KEYBOARD_DELAY"
|
||||
fi
|
||||
if [ -n "$KBDRATE_ARGS" ]; then
|
||||
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting keyboard rate and delay"
|
||||
kbdrate -s $KBDRATE_ARGS
|
||||
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||
fi
|
||||
|
||||
# Inform gpm if present, of potential changes.
|
||||
if [ -f /var/run/gpm.pid ]; then
|
||||
kill -s WINCH `cat /var/run/gpm.pid` 2> /dev/null
|
||||
fi
|
||||
|
||||
# Allow user to remap keys on the console
|
||||
if [ -z "${HAVE_SETUPCON}" -a -r /etc/$PKG/remap ]; then
|
||||
dumpkeys < ${DEVICE_PREFIX}1 | sed -f /etc/$PKG/remap | loadkeys --quiet
|
||||
fi
|
||||
|
||||
# Set LEDS here
|
||||
if [ -n "$LEDS" ]; then
|
||||
for i in $LIST_CONSOLES
|
||||
do
|
||||
setleds -D $LEDS < $DEVICE_PREFIX$i
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start|reload|restart|force-reload)
|
||||
if [ -n "$HAVE_SETUPCON" ]
|
||||
then
|
||||
log_action_msg "Setting console screen modes"
|
||||
else
|
||||
log_action_msg "Setting console screen modes and fonts"
|
||||
fi
|
||||
setup
|
||||
[ -z "$HAVE_SETUPCON" ] || log_action_msg "Skipping font and keymap setup (handled by console-setup)"
|
||||
;;
|
||||
stop)
|
||||
;;
|
||||
*)
|
||||
setup
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
/* 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:
|
||||
*/
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Check if directory exists
|
||||
/* Event aggregator, also serves as event cache, remembering GW and IFUP states
|
||||
*
|
||||
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
|
||||
* 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
|
||||
@@ -21,26 +21,15 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#ifndef FINIT_EVENT_H_
|
||||
#define FINIT_EVENT_H_
|
||||
|
||||
/**
|
||||
* fexist - Check if a file exists and is a directory.
|
||||
* @file: File to look for, with full path.
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) if the file exists and is a directory, otherwise %FALSE(0).
|
||||
*/
|
||||
int fisdir(char *file)
|
||||
{
|
||||
struct stat sb;
|
||||
int event_cache_gw (void);
|
||||
int event_cache_if (char *ifname);
|
||||
int event_service_cond (char *events);
|
||||
void event_dispatch (char *msg);
|
||||
|
||||
if (!stat(file, &sb) && S_ISDIR(sb.st_mode))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* FINIT_EVENT_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
@@ -0,0 +1,358 @@
|
||||
/* Functions for exec'ing processes
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#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 "finit.h"
|
||||
#include "sig.h"
|
||||
#include "helpers.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
#define NUM_ARGS 16
|
||||
|
||||
|
||||
/* Wait for process completion, returns status of waitpid(2) syscall */
|
||||
int complete(char *cmd, int pid)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
if (errno == EINTR)
|
||||
_e("Caught unblocked signal waiting for %s, aborting.", cmd);
|
||||
else if (errno == ECHILD)
|
||||
_e("Caught SIGCHLD waiting for %s, aborting.", cmd);
|
||||
else
|
||||
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int run(char *cmd)
|
||||
{
|
||||
int status, result, i = 0;
|
||||
char *args[NUM_ARGS + 1], *arg, *backup;
|
||||
pid_t pid;
|
||||
|
||||
/* We must create a copy that is possible to modify. */
|
||||
backup = arg = strdup(cmd);
|
||||
if (!arg)
|
||||
return 1; /* Failed allocating a string to be modified. */
|
||||
|
||||
/* Split command line into tokens of an argv[] array. */
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
while (arg && i < NUM_ARGS) {
|
||||
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
|
||||
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
|
||||
if (*arg == '\'' || *arg == '"') {
|
||||
char *p, delim[2] = " ";
|
||||
|
||||
delim[0] = arg[0];
|
||||
args[i++] = arg++;
|
||||
strsep(&arg, delim);
|
||||
p = arg - 1;
|
||||
*p = *delim;
|
||||
*arg++ = 0;
|
||||
} else {
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
}
|
||||
}
|
||||
args[i] = NULL;
|
||||
#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);
|
||||
errno = EOVERFLOW;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Always redirect stdio for run() */
|
||||
fp = fopen("/dev/null", "w");
|
||||
if (fp) {
|
||||
int fd = fileno(fp);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
sig_unblock();
|
||||
execvp(args[0], args);
|
||||
|
||||
_exit(1); /* Only if execv() fails. */
|
||||
} else if (-1 == pid) {
|
||||
_pe("%s", args[0]);
|
||||
free(backup);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = complete(args[0], pid);
|
||||
if (-1 == status) {
|
||||
free(backup);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = WEXITSTATUS(status);
|
||||
if (WIFEXITED(status)) {
|
||||
_d("Started %s and ended OK: %d", args[0], result);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
|
||||
if (!result)
|
||||
result = 1; /* Must alert callee that the command did complete successfully.
|
||||
* This is necessary since not all programs trap signals and
|
||||
* change their return code accordingly. --Jocke */
|
||||
}
|
||||
|
||||
free(backup);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int run_interactive(char *cmd, char *fmt, ...)
|
||||
{
|
||||
int status, oldout = 1, olderr = 2;
|
||||
char line[LINE_SIZE];
|
||||
va_list ap;
|
||||
FILE *fp;
|
||||
|
||||
if (!cmd) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose && fmt) {
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(line, sizeof(line), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
print_desc("", line);
|
||||
}
|
||||
|
||||
/* Redirect output from cmd to a tempfile */
|
||||
fp = tempfile();
|
||||
if (fp && !debug) {
|
||||
oldout = dup(STDOUT_FILENO);
|
||||
olderr = dup(STDERR_FILENO);
|
||||
dup2(fileno(fp), STDOUT_FILENO);
|
||||
dup2(fileno(fp), STDERR_FILENO);
|
||||
}
|
||||
|
||||
/* Run cmd ... */
|
||||
status = run(cmd);
|
||||
|
||||
/* Restore stderr/stdout */
|
||||
if (fp && !debug) {
|
||||
if (oldout >= 0) {
|
||||
dup2(oldout, STDOUT_FILENO);
|
||||
close(oldout);
|
||||
}
|
||||
if (olderr >= 0) {
|
||||
dup2(olderr, STDERR_FILENO);
|
||||
close(olderr);
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose && fmt)
|
||||
print_result(status);
|
||||
|
||||
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
|
||||
if (fp && !debug) {
|
||||
size_t len, written;
|
||||
|
||||
rewind(fp);
|
||||
do {
|
||||
len = fread(line, 1, sizeof(line), fp);
|
||||
written = fwrite(line, len, sizeof(char), stderr);
|
||||
} while (len > 0 && written == len);
|
||||
}
|
||||
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
pid_t run_getty(char *cmd, char *args[], int console)
|
||||
{
|
||||
pid_t 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (console)
|
||||
close(fd);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
int run_parts(char *dir, char *cmd)
|
||||
{
|
||||
struct dirent **e;
|
||||
int i, num;
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("No files found in %s, skipping ...", dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
int j = 0;
|
||||
pid_t pid = 0;
|
||||
mode_t mode;
|
||||
char *args[NUM_ARGS];
|
||||
char *name = e[i]->d_name;
|
||||
char path[CMD_SIZE];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
mode = fmode(path);
|
||||
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
|
||||
_d("Skipping %s ...", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill in args[], starting with full path to executable */
|
||||
args[j++] = path;
|
||||
|
||||
/* If the callee didn't supply a run_parts() argument */
|
||||
if (!cmd) {
|
||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||
_d("Checking if %s is a sysvinit startstop script ...", name);
|
||||
if (name[0] == 'S' && isdigit(name[1])) {
|
||||
args[j++] = "start";
|
||||
} else if (name[0] == 'K' && isdigit(name[1])) {
|
||||
args[j++] = "stop";
|
||||
}
|
||||
} else {
|
||||
args[j++] = cmd;
|
||||
}
|
||||
args[j++] = NULL;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
_d("Calling %s ...", path);
|
||||
sig_unblock();
|
||||
execv(path, args);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
complete(path, pid);
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -22,62 +22,76 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h> /* umask(), mkdir() */
|
||||
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "plugin.h"
|
||||
#include "svc.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "tty.h"
|
||||
#include "libite/lite.h"
|
||||
#include "inetd.h"
|
||||
|
||||
int debug = 0;
|
||||
int verbose = 1;
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *username = NULL;
|
||||
char *hostname = NULL;
|
||||
int debug = 0;
|
||||
int quiet = KERNEL_QUIET; /* Delayed disable of verbose mode. */
|
||||
int verbose = VERBOSE_MODE;
|
||||
int runlevel = 0; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
int prevlevel = -1;
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *username = NULL;
|
||||
char *hostname = NULL;
|
||||
char *rcsd = FINIT_RCSD;
|
||||
char *runparts = NULL;
|
||||
char *console = NULL;
|
||||
|
||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||
|
||||
static void parse_kernel_cmdline(void)
|
||||
static int banner(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE];
|
||||
char buf[42] = INIT_HEADING;
|
||||
const char separator[] = "========================================================================";
|
||||
|
||||
if ((fp = fopen("/proc/cmdline", "r")) != NULL) {
|
||||
fgets(line, sizeof(line), fp);
|
||||
_d("Kernel command line: %s", line);
|
||||
if (!verbose)
|
||||
return 0;
|
||||
|
||||
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
|
||||
debug = 1;
|
||||
|
||||
if (!debug && strstr(line, "quiet"))
|
||||
verbose = 0;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
static int run_loop(void)
|
||||
{
|
||||
while (1) {
|
||||
svc_monitor();
|
||||
plugin_monitor();
|
||||
}
|
||||
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, 66 - (int)strlen(buf), separator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int UNUSED(args), char *argv[])
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int err;
|
||||
uev_ctx_t loop;
|
||||
|
||||
/*
|
||||
* finit/init/telinit client tool uses /dev/initctl pipe
|
||||
* for compatibility but initctl client tool uses socket
|
||||
*/
|
||||
if (getpid() != 1)
|
||||
return client(argc, argv);
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
sig_init();
|
||||
|
||||
/*
|
||||
* Initalize event context.
|
||||
*/
|
||||
uev_init(&loop);
|
||||
ctx = &loop;
|
||||
|
||||
/*
|
||||
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
||||
*/
|
||||
@@ -86,8 +100,8 @@ int main(int UNUSED(args), char *argv[])
|
||||
mount("none", "/proc", "proc", 0, NULL);
|
||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||
mount("none", "/sys", "sysfs", 0, NULL);
|
||||
mkdir("/dev/pts", 0755);
|
||||
mkdir("/dev/shm", 0755);
|
||||
makedir("/dev/pts", 0755);
|
||||
makedir("/dev/shm", 0755);
|
||||
mount("none", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
||||
mount("none", "/dev/shm", "tmpfs", 0, NULL);
|
||||
umask(022);
|
||||
@@ -95,63 +109,77 @@ int main(int UNUSED(args), char *argv[])
|
||||
/*
|
||||
* Parse kernel parameters
|
||||
*/
|
||||
parse_kernel_cmdline();
|
||||
conf_parse_cmdline();
|
||||
|
||||
cls();
|
||||
echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")");
|
||||
/*
|
||||
* Hello world.
|
||||
*/
|
||||
banner();
|
||||
|
||||
/*
|
||||
* Populate /dev and prepare for runtime events from kernel.
|
||||
*/
|
||||
#if defined(USE_UDEV)
|
||||
run_interactive("udevd --daemon", "Populating device tree");
|
||||
#elif defined (MDEV)
|
||||
run_interactive(MDEV " -s", "Populating device tree");
|
||||
#ifdef EMBEDDED_SYSTEM
|
||||
if (debug)
|
||||
touch("/dev/mdev.log");
|
||||
#endif
|
||||
run_interactive(SETUP_DEVFS, "Populating device tree");
|
||||
|
||||
/*
|
||||
* Parse configuration file
|
||||
* Load plugins first, finit.conf may contain references to
|
||||
* features implemented by plugins.
|
||||
*/
|
||||
parse_finit_conf(FINIT_CONF);
|
||||
plugin_load_all(&loop, PLUGIN_PATH);
|
||||
|
||||
/*
|
||||
* Load plugins. Must run after finit.conf has registered
|
||||
* all services, or service plugins won't have anything to
|
||||
* hook on to.
|
||||
* Parse /etc/finit.conf, main configuration file
|
||||
*/
|
||||
print_desc("", "Loading plugins");
|
||||
print_result(plugin_load_all(PLUGIN_PATH));
|
||||
conf_parse_config();
|
||||
|
||||
/* Set hostname as soon as possible, for syslog et al. */
|
||||
set_hostname(&hostname);
|
||||
|
||||
/* Set default PATH, for uid 0 */
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
|
||||
/*
|
||||
* Mount filesystems
|
||||
*/
|
||||
_d("Mount filesystems in /etc/fstab ...");
|
||||
|
||||
#ifdef REMOUNT_ROOTFS_RW
|
||||
#ifdef REMOUNT_ROOTFS
|
||||
run("/bin/mount -n -o remount,rw /");
|
||||
#endif
|
||||
#ifdef SYSROOT
|
||||
run(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
#endif
|
||||
|
||||
_d("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
|
||||
umask(0);
|
||||
run("/bin/mount -na");
|
||||
print_desc("Mounting filesystems", NULL);
|
||||
|
||||
err = run("/bin/mount -na");
|
||||
print_result(err);
|
||||
if (err)
|
||||
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();
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
_d("Base FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
|
||||
/*
|
||||
* Start all bootstrap tasks, no network available!
|
||||
*/
|
||||
service_bootstrap();
|
||||
|
||||
/*
|
||||
* Network stuff
|
||||
*/
|
||||
@@ -159,45 +187,47 @@ int main(int UNUSED(args), char *argv[])
|
||||
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
|
||||
run("/sbin/sysctl -e -p /etc/sysctl.conf >/dev/null");
|
||||
|
||||
/* Set initial hostname. */
|
||||
set_hostname(hostname);
|
||||
|
||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
|
||||
if (network)
|
||||
run_interactive(network, "Starting networking: %s", network);
|
||||
umask(022);
|
||||
|
||||
/*
|
||||
* Hooks that rely on loopback, or basic networking being up.
|
||||
*/
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||
|
||||
/*
|
||||
* Start service monitor framework
|
||||
* Start all tasks/services in the configured runlevel
|
||||
*/
|
||||
_d("Starting all static services from %s", FINIT_CONF);
|
||||
svc_start_all();
|
||||
service_runlevel(cfglevel);
|
||||
|
||||
_d("Running svc up hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
|
||||
/*
|
||||
* Run startup scripts in /etc/finit.d/, if any.
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
_d("Running startup scripts in /etc/finit.d/ ...");
|
||||
run_parts(FINIT_RCSD);
|
||||
if (runparts && fisdir(runparts)) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
run_parts(runparts, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hooks that should run at the very end
|
||||
*/
|
||||
/* Hooks that should run at the very end */
|
||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||
|
||||
/* Start GETTY on console */
|
||||
_d("Starting getty on console ...");
|
||||
run_getty(GETTY, argv);
|
||||
/* Start TTYs */
|
||||
tty_runlevel(runlevel);
|
||||
|
||||
/* Disable verbose mode, if selected */
|
||||
if (quiet && !debug)
|
||||
verbose = 0;
|
||||
|
||||
/* Start new initctl API responder */
|
||||
api_init(&loop);
|
||||
|
||||
/*
|
||||
* Enter main loop to monior /dev/initctl and services
|
||||
*/
|
||||
_d("Entering main loop ...");
|
||||
return run_loop();
|
||||
return uev_run(&loop, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+45
-7
@@ -1,12 +1,50 @@
|
||||
# Simple Debian finit.conf
|
||||
# Sample finit.conf to demonstrate the syntax
|
||||
#
|
||||
user admin
|
||||
host testbed
|
||||
|
||||
# Devices to fsck at boot
|
||||
check /dev/vda1
|
||||
|
||||
# Linux kernel modules to load
|
||||
module button
|
||||
module evdev
|
||||
module loop
|
||||
module psmouse
|
||||
user admin
|
||||
host testbed
|
||||
|
||||
# Runlevel to start after bootstrap, runlevel 'S'
|
||||
runlevel 2
|
||||
|
||||
# Network bringup script
|
||||
network service networking start
|
||||
service /sbin/klogd -n -- Kernel logging server
|
||||
service /sbin/syslogd -n -- Syslog server
|
||||
service /usr/sbin/sshd -D -- OpenSSH server
|
||||
service /usr/sbin/gdm -- GNOME Display Manager
|
||||
|
||||
# Virtual consoles to start getty on
|
||||
tty /dev/tty1
|
||||
tty /dev/tty2
|
||||
tty /dev/tty3
|
||||
|
||||
# Alternative method instead of runparts
|
||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd start -- Preparing console
|
||||
#run [2] /etc/init.d/networking start -- Start networking
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
service [2345] /sbin/klogd -n -- Kernel logging server
|
||||
service [2345] /sbin/syslogd -n -- Syslog server
|
||||
service [3] /usr/sbin/gdm -- GNOME Display Manager
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
|
||||
# Inetd services
|
||||
inetd time/udp wait [2345] internal -- UNIX rdate service
|
||||
inetd time/tcp nowait [2345] internal -- UNIX rdate service
|
||||
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i -- SSH service
|
||||
inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i -- SSH service
|
||||
|
||||
# For multiple instances of the same service, add :ID somewhere between
|
||||
# the service/run/task keyword and the command.
|
||||
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
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -27,48 +27,57 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Distribution configuration */
|
||||
#include "libite/lite.h"
|
||||
|
||||
#if defined EMBEDDED_SYSTEM
|
||||
# define CONSOLE "/dev/console"
|
||||
# define MDEV "/sbin/mdev"
|
||||
# define GETTY "/sbin/getty -L 115200 ttyS0 vt100"
|
||||
#else /* Debian/Ubuntu based distributions */
|
||||
# define RANDOMSEED "/var/lib/urandom/random-seed"
|
||||
# define CONSOLE "/dev/tty1"
|
||||
# define GETTY "/sbin/getty -8 38400 tty1"
|
||||
# define REMOUNT_ROOTFS_RW
|
||||
# define RUNLEVEL 2
|
||||
# define USE_UDEV
|
||||
# define USE_MESSAGE_BUS
|
||||
#endif
|
||||
#define CMD_SIZE 256
|
||||
#define LINE_SIZE 1024
|
||||
#define BUF_SIZE 4096
|
||||
|
||||
#ifndef DEFUSER
|
||||
# define DEFUSER "root"
|
||||
#endif
|
||||
#ifndef DEFHOST
|
||||
# define DEFHOST "noname"
|
||||
#endif
|
||||
/* We reuse the INIT_CMD_ for the new initctl tool, but telinit only
|
||||
* supports changing runlevel and reloading the configuration. */
|
||||
#define INIT_SOCKET _PATH_VARRUN "finit.sock"
|
||||
#define INIT_MAGIC 0x03091969
|
||||
#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_NACK 254
|
||||
#define INIT_CMD_ACK 255
|
||||
|
||||
#define CMD_SIZE 256
|
||||
#define LINE_SIZE 1024
|
||||
#define BUF_SIZE 4096
|
||||
#define USERNAME_SIZE 16
|
||||
#define HOSTNAME_SIZE 32
|
||||
struct init_request {
|
||||
int magic; /* Magic number */
|
||||
int cmd; /* What kind of request */
|
||||
int runlevel; /* Runlevel to change to */
|
||||
int sleeptime; /* Time between TERM and KILL */
|
||||
char data[368];
|
||||
};
|
||||
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
extern char *username;
|
||||
|
||||
/* conf.c */
|
||||
void parse_finit_conf(char *file);
|
||||
extern int debug;
|
||||
extern int quiet;
|
||||
extern int verbose;
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int prevlevel;
|
||||
extern char *rcsd;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
extern char *username;
|
||||
extern char *runparts;
|
||||
extern char *console;
|
||||
extern char *__progname;
|
||||
|
||||
#endif /* FINIT_H_ */
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Misc. utility functions and C-library extensions for finit and its plugins
|
||||
/* Misc. utility functions for finit and its plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -22,309 +22,96 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#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 "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "lite.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
#define NUM_ARGS 16
|
||||
#define NUM_SCRIPTS 128 /* ought to be enough for anyone */
|
||||
|
||||
/*
|
||||
* Helpers to replace system() calls
|
||||
*/
|
||||
|
||||
int makepath(char *path)
|
||||
static int encode(int lvl)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char *x = buf;
|
||||
int ret;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
do {
|
||||
*x++ = *path++;
|
||||
} while (*path && *path != '/');
|
||||
|
||||
*x = 0;
|
||||
ret = mkdir(buf, 0777);
|
||||
} while (*path && (*path != '/' || *(path + 1))); /* ignore trailing slash */
|
||||
|
||||
return ret;
|
||||
if (!lvl) return 0;
|
||||
return lvl + '0';
|
||||
}
|
||||
|
||||
void ifconfig(char *ifname, char *addr, char *mask, int up)
|
||||
void runlevel_set(int pre, int now)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_in *a = (struct sockaddr_in *)&ifr.ifr_addr;
|
||||
int sock;
|
||||
struct utmp utent;
|
||||
|
||||
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
|
||||
return;
|
||||
utent.ut_type = RUN_LVL;
|
||||
utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF);
|
||||
strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user));
|
||||
|
||||
memset(&ifr, 0, sizeof (ifr));
|
||||
strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
setutent();
|
||||
pututline(&utent);
|
||||
endutent();
|
||||
}
|
||||
|
||||
if (up) {
|
||||
if (addr) {
|
||||
inet_aton(addr, &a->sin_addr);
|
||||
ioctl(sock, SIOCSIFADDR, &ifr);
|
||||
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;
|
||||
}
|
||||
if (mask) {
|
||||
inet_aton(mask, &a->sin_addr);
|
||||
ioctl(sock, SIOCSIFNETMASK, &ifr);
|
||||
}
|
||||
endutent();
|
||||
|
||||
return lvl - '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* runlevel_string - Convert a bit encoded runlevel to .conf syntax
|
||||
* @levels: Bit encoded runlevels
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to string on the form [2345]
|
||||
*/
|
||||
char *runlevel_string(int levels)
|
||||
{
|
||||
int i, pos = 1;
|
||||
static char lvl[20];
|
||||
|
||||
memset(lvl, 0, sizeof(lvl));
|
||||
lvl[0] = '[';
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ISSET(levels, i)) {
|
||||
if (runlevel == i)
|
||||
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
|
||||
|
||||
if (i == 0)
|
||||
lvl[pos++] = 'S';
|
||||
else
|
||||
lvl[pos++] = '0' + i;
|
||||
|
||||
if (runlevel == i)
|
||||
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
|
||||
}
|
||||
}
|
||||
|
||||
ioctl(sock, SIOCGIFFLAGS, &ifr);
|
||||
lvl[pos++] = ']';
|
||||
lvl[pos] = 0;
|
||||
|
||||
if (up)
|
||||
ifr.ifr_flags |= IFF_UP;
|
||||
else
|
||||
ifr.ifr_flags &= ~IFF_UP;
|
||||
|
||||
ioctl(sock, SIOCSIFFLAGS, &ifr);
|
||||
|
||||
close(sock);
|
||||
return lvl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pidfile_read - Reads a PID value from a pidfile.
|
||||
* @pidfile: File containing PID, usually in /var/run/<PROC>.pid
|
||||
*
|
||||
* This function takes a @pidfile and returns the PID found therein.
|
||||
*
|
||||
* Returns:
|
||||
* On invalid @pidfile -1 and @errno set to %EINVAL, when @pidfile does not exist -1
|
||||
* and errno set to %ENOENT. When the pidfile is empty or when its contents cannot
|
||||
* be translated this function returns zero (0), on success this function returns
|
||||
* a PID value greater than one. PID 1 is reserved for the system init process.
|
||||
*/
|
||||
pid_t pidfile_read(char *pidfile)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
char buf[16];
|
||||
FILE *fp;
|
||||
|
||||
if (!pidfile) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!fexist(pidfile))
|
||||
return -1;
|
||||
|
||||
fp = fopen(pidfile, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
errno = 0;
|
||||
pid = strtoul(buf, NULL, 0);
|
||||
if (errno)
|
||||
pid = 0; /* Failed conversion. */
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* pidfile_poll - Poll for the existence of a pidfile and return PID
|
||||
* @cmd: Process name, or command, called to expect a pidfile
|
||||
* @path: Path to pidfile to poll for
|
||||
*
|
||||
* This function polls for the pidfile at @path for at most 5 seconds
|
||||
* before timing out. If the file is created within that time span the
|
||||
* file is read and its PID contents returned.
|
||||
*
|
||||
* Returns:
|
||||
* The PID read from @path, or zero on timeout.
|
||||
*/
|
||||
pid_t pidfile_poll(char *cmd, char *path)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
int tries = 0;
|
||||
|
||||
/* Timeout = 100 * 50ms = 5s */
|
||||
while (!fexist(path) && tries++ < 100) {
|
||||
/* Wait 50ms between retries */
|
||||
usleep(50000);
|
||||
}
|
||||
|
||||
if (!fexist(path)) {
|
||||
_e("Timeout! No PID found for %s, pidfile %s does not exist?", cmd, path);
|
||||
pid = 0;
|
||||
} else {
|
||||
pid = pidfile_read(path);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pid_alive - Check if a given process ID is running
|
||||
* @pid: Process ID to check for.
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) if pid is alive (/proc/pid exists), otherwise %FALSE(0)
|
||||
*/
|
||||
int pid_alive(pid_t pid)
|
||||
{
|
||||
char name[24]; /* Enough for max pid_t */
|
||||
|
||||
snprintf(name, sizeof(name), "/proc/%d", pid);
|
||||
|
||||
return fexist(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pid_get_name - Find name of a process
|
||||
* @pid: PID of process to find.
|
||||
* @name: Pointer to buffer where to return process name, may be %NULL
|
||||
* @len: Length in bytes of @name buffer.
|
||||
*
|
||||
* If @name is %NULL, or @len is zero the function will return
|
||||
* a static string. This may be useful to one-off calls.
|
||||
*
|
||||
* Returns:
|
||||
* %NULL on error, otherwise a va
|
||||
*/
|
||||
char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
{
|
||||
int ret = 1;
|
||||
FILE *fp;
|
||||
char path[32];
|
||||
static char line[64];
|
||||
|
||||
if (!name || len == 0)
|
||||
name = line;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/status", pid);
|
||||
if ((fp = fopen(path, "r")) != NULL) {
|
||||
if (fgets(line, sizeof (line), fp)) {
|
||||
char *pname = line + 6; /* Skip first part of line --> "Name:\t" */
|
||||
|
||||
chomp(pname);
|
||||
strlcpy(name, pname, len);
|
||||
ret = 0; /* Found it! */
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* procname_set - Change process name, as seen in process listnings
|
||||
* @name: New name of process
|
||||
* @args: The process' argv[] vector.
|
||||
*/
|
||||
void procname_set(char *name, char *args[])
|
||||
{
|
||||
size_t len = strlen(args[0]) + 1; /* Include terminating '\0' */
|
||||
|
||||
prctl(PR_SET_NAME, name, 0, 0, 0);
|
||||
memset(args[0], 0, len);
|
||||
strlcpy(args[0], name, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* procname_kill - Send a signal to a process group by name.
|
||||
* @name: Name of process to send signal to.
|
||||
* @signo: Signal to send.
|
||||
*
|
||||
* Send a signal to a running process (group). This function searches
|
||||
* for a given process @name and sends a signal, @signo, to each
|
||||
* process ID matching that @name.
|
||||
*
|
||||
* Returns:
|
||||
* Number of signals sent, i.e. number of processes who have received the signal.
|
||||
*/
|
||||
int procname_kill (char *name, int signo)
|
||||
{
|
||||
int result = 0;
|
||||
char path[32], line[64];
|
||||
FILE *fp;
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
dir = opendir("/proc");
|
||||
if (!dir || !name) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((entry = readdir (dir)) != NULL) {
|
||||
/* Skip non-process entries in /proc */
|
||||
if (!isdigit (*entry->d_name))
|
||||
continue;
|
||||
|
||||
snprintf (path, sizeof(path), "/proc/%s/status", entry->d_name);
|
||||
/* Skip non-readable files (protected?) */
|
||||
if ((fp = fopen (path, "r")) == NULL)
|
||||
continue;
|
||||
|
||||
if (fgets (line, sizeof (line), fp)) {
|
||||
char *pname = line + 6; /* Skip first part of line --> "Name:\t" */
|
||||
|
||||
if (strncmp(pname, name, strlen(pname) - 1) == 0) {
|
||||
int error = errno;
|
||||
|
||||
if (kill(atoi(entry->d_name), signo)) {
|
||||
FLOG_ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error));
|
||||
} else {
|
||||
result++; /* Track number of processes we deliver the signal to. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static int print_uptime(void)
|
||||
static int print_timestamp(void)
|
||||
{
|
||||
#if defined(CONFIG_PRINTK_TIME)
|
||||
FILE * uptimefile;
|
||||
@@ -345,352 +132,123 @@ static int print_uptime(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_desc(char *action, char *desc)
|
||||
void print(int action, const char *fmt, ...)
|
||||
{
|
||||
const char home[] = "\r\e[K";
|
||||
char buf[80];
|
||||
size_t len;
|
||||
va_list ap;
|
||||
const char success[] = " \e[1m[ OK ]\e[0m\n";
|
||||
const char failure[] = " \e[7m[FAIL]\e[0m\n";
|
||||
const char pending[] = " \e[1m[ \\/ ]\e[0m\n";
|
||||
const char dots[] = " .....................................................................";
|
||||
|
||||
write(STDERR_FILENO, home, strlen(home));
|
||||
if (!verbose)
|
||||
return;
|
||||
|
||||
print_uptime();
|
||||
if (fmt) {
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
write(STDERR_FILENO, action, strlen(action));
|
||||
write(STDERR_FILENO, desc, strlen(desc));
|
||||
write(STDERR_FILENO, dots, 60 - strlen(desc) - strlen(action)); /* pad with dots. */
|
||||
delline();
|
||||
print_timestamp();
|
||||
|
||||
write(STDERR_FILENO, "\r", 1);
|
||||
write(STDERR_FILENO, buf, len);
|
||||
write(STDERR_FILENO, dots, 60 - len); /* pad with dots. */
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case -1:
|
||||
break;
|
||||
|
||||
case 0:
|
||||
write(STDERR_FILENO, success, sizeof(success));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
write(STDERR_FILENO, failure, sizeof(failure));
|
||||
break;
|
||||
|
||||
default:
|
||||
write(STDERR_FILENO, pending, sizeof(pending));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void print_desc(char *action, char *desc)
|
||||
{
|
||||
print(-1, "%s%s", action ?: "", desc ?: "");
|
||||
}
|
||||
|
||||
int print_result(int fail)
|
||||
{
|
||||
if (fail)
|
||||
fprintf(stderr, " \e[7m[FAIL]\e[0m\n");
|
||||
else
|
||||
fprintf(stderr, " \e[1m[ OK ]\e[0m\n");
|
||||
|
||||
print(!!fail, NULL);
|
||||
return fail;
|
||||
}
|
||||
|
||||
int run(char *cmd)
|
||||
{
|
||||
int status, result, i = 0;
|
||||
FILE *fp;
|
||||
char *args[NUM_ARGS + 1], *arg, *backup;
|
||||
pid_t pid;
|
||||
|
||||
/* We must create a copy that is possible to modify. */
|
||||
backup = arg = strdup(cmd);
|
||||
if (!arg)
|
||||
return 1; /* Failed allocating a string to be modified. */
|
||||
|
||||
/* Split command line into tokens of an argv[] array. */
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
do {
|
||||
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
|
||||
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
|
||||
if (*arg == '\'' || *arg == '"') {
|
||||
char *p, delim[2] = " ";
|
||||
|
||||
delim[0] = arg[0];
|
||||
args[i++] = arg++;
|
||||
strsep(&arg, delim);
|
||||
p = arg - 1;
|
||||
*p = *delim;
|
||||
*arg++ = 0;
|
||||
} else {
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
}
|
||||
} while (arg && i < NUM_ARGS);
|
||||
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);
|
||||
errno = EOVERFLOW;
|
||||
return 1;
|
||||
}
|
||||
|
||||
fp = fopen("/dev/null", "w");
|
||||
pid = fork();
|
||||
if (0 == pid) {
|
||||
int i;
|
||||
struct sigaction sa;
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
|
||||
/* Redirect stdio if the caller requested so. */
|
||||
if (fp) {
|
||||
int fd = fileno(fp);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
execvp(args[0], args);
|
||||
_exit(1); /* Only if execv() fails. */
|
||||
} else if (-1 == pid) {
|
||||
_pe("%s", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
if (errno == EINTR)
|
||||
_e("Caught unblocked signal waiting for %s, aborting.", args[0]);
|
||||
else if (errno == ECHILD)
|
||||
_e("Caught SIGCHLD waiting for %s, aborting.", args[0]);
|
||||
else
|
||||
_e("Failed starting %s, error %d: %s", args[0], errno, strerror (errno));
|
||||
|
||||
if (fp) fclose(fp);
|
||||
free(backup);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = WEXITSTATUS(status);
|
||||
if (WIFEXITED(status)) {
|
||||
_d("Started %s and ended OK: %d", args[0], result);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
|
||||
if (!result)
|
||||
result = 1; /* Must alert callee that the command did complete successfully.
|
||||
* This is necessary since not all programs trap signals and
|
||||
* change their return code accordingly. --Jocke */
|
||||
}
|
||||
|
||||
if (fp) fclose(fp);
|
||||
free(backup);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int run_interactive(char *cmd, char *fmt, ...)
|
||||
{
|
||||
int status, oldout = 1, olderr = 2;
|
||||
char line[LINE_SIZE];
|
||||
va_list ap;
|
||||
FILE *fp = tmpfile();
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(line, sizeof(line), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
print_desc("", line);
|
||||
if (fp && !debug) {
|
||||
oldout = dup(STDOUT_FILENO);
|
||||
olderr = dup(STDERR_FILENO);
|
||||
dup2(fileno(fp), STDOUT_FILENO);
|
||||
dup2(fileno(fp), STDERR_FILENO);
|
||||
}
|
||||
status = run(cmd);
|
||||
if (fp && !debug) {
|
||||
dup2(oldout, STDOUT_FILENO);
|
||||
dup2(olderr, STDERR_FILENO);
|
||||
}
|
||||
print_result(status);
|
||||
if (fp && !debug) {
|
||||
size_t len, written;
|
||||
|
||||
rewind(fp);
|
||||
do {
|
||||
len = fread(line, 1, sizeof(line), fp);
|
||||
written = fwrite(line, len, sizeof(char), stderr);
|
||||
} while (len > 0 && written == len);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
pid_t run_getty(char *cmd, char *argv[])
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if (!pid) {
|
||||
int i;
|
||||
char c;
|
||||
sigset_t nmask;
|
||||
struct sigaction sa;
|
||||
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
|
||||
close(2);
|
||||
close(1);
|
||||
close(0);
|
||||
|
||||
/* Attach TTY to console */
|
||||
if (open(CONSOLE, O_RDWR) != 0)
|
||||
exit(1);
|
||||
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
|
||||
dup2(0, STDIN_FILENO);
|
||||
dup2(0, STDOUT_FILENO);
|
||||
dup2(0, STDERR_FILENO);
|
||||
|
||||
procname_set("console", argv);
|
||||
|
||||
while (!fexist(SYNC_SHUTDOWN)) {
|
||||
static const char msg[] = "\nPlease press Enter to activate this console. ";
|
||||
|
||||
if (fexist(SYNC_STOPPED)) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
i = write(STDERR_FILENO, msg, sizeof(msg) - 1);
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
run(cmd);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static int cmp(const void *s1, const void *s2)
|
||||
{
|
||||
return strcmp(*(char **)s1, *(char **)s2);
|
||||
}
|
||||
|
||||
int run_parts(char *dir, ...)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *e;
|
||||
struct stat st;
|
||||
char *oldpwd = NULL;
|
||||
char *ent[NUM_SCRIPTS];
|
||||
int i, num = 0, argnum = 1;
|
||||
char *args[NUM_ARGS];
|
||||
va_list ap;
|
||||
|
||||
oldpwd = getcwd (NULL, 0);
|
||||
if (chdir(dir)) {
|
||||
if (oldpwd) free(oldpwd);
|
||||
return -1;
|
||||
}
|
||||
if ((d = opendir(dir)) == NULL) {
|
||||
if (oldpwd) free(oldpwd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_start(ap, dir);
|
||||
while (argnum < NUM_ARGS && (args[argnum++] = va_arg(ap, char *)));
|
||||
va_end(ap);
|
||||
|
||||
while ((e = readdir(d))) {
|
||||
if (e->d_type == DT_REG && stat(e->d_name, &st) == 0) {
|
||||
_d("Found %s/%s ...", dir, e->d_name);
|
||||
if (st.st_mode & S_IXUSR) {
|
||||
ent[num++] = strdup(e->d_name);
|
||||
if (num >= NUM_SCRIPTS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
if (num == 0) {
|
||||
if (oldpwd) free(oldpwd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
qsort(ent, num, sizeof(char *), cmp);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
pid_t pid = 0;
|
||||
int status;
|
||||
|
||||
args[0] = ent[i];
|
||||
args[1] = NULL;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
_d("Calling %s ...", ent[i]);
|
||||
execv(ent[i], args);
|
||||
exit(0);
|
||||
}
|
||||
waitpid(pid, &status, 0);
|
||||
free(ent[i]);
|
||||
}
|
||||
|
||||
chdir(oldpwd);
|
||||
free(oldpwd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getuser(char *username)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
return fgetint("/etc/passwd", "x:\n", username);
|
||||
#else
|
||||
struct passwd *usr;
|
||||
|
||||
if (!username || (usr = getpwnam(username)) == NULL)
|
||||
return -1;
|
||||
|
||||
return usr->pw_uid;
|
||||
#endif
|
||||
}
|
||||
|
||||
int getgroup(char *group)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
return fgetint("/etc/group", "x:\n", group);
|
||||
#else
|
||||
struct group *grp;
|
||||
|
||||
if ((grp = getgrnam(group)) == NULL)
|
||||
return -1;
|
||||
|
||||
return grp->gr_gid;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Other convenience functions
|
||||
*/
|
||||
|
||||
void cls(void)
|
||||
/* Signal safe sleep ... we get a lot of SIGCHLD at reboot */
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
static const char cls[] = "\e[2J\e[1;1H";
|
||||
|
||||
if (!debug)
|
||||
fprintf (stderr, "%s", cls);
|
||||
while ((sec = sleep(sec)))
|
||||
;
|
||||
}
|
||||
|
||||
void chomp(char *str)
|
||||
{
|
||||
char *x;
|
||||
|
||||
if ((x = strchr((str), 0x0a)) != NULL)
|
||||
*x = 0;
|
||||
}
|
||||
|
||||
void set_hostname(char *hostname)
|
||||
void set_hostname(char **hostname)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
_d("Set hostname: %s", hostname);
|
||||
if ((fp = fopen("/etc/hostname", "r")) != NULL) {
|
||||
fgets(hostname, HOSTNAME_SIZE, fp);
|
||||
chomp(hostname);
|
||||
_d("Set hostname: %s", *hostname);
|
||||
fp = fopen("/etc/hostname", "r");
|
||||
if (fp) {
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fileno(fp), &st)) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
*hostname = realloc(*hostname, st.st_size);
|
||||
if (!*hostname) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
fgets(*hostname, st.st_size, fp);
|
||||
chomp(*hostname);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
sethostname(hostname, strlen(hostname));
|
||||
if (*hostname)
|
||||
sethostname(*hostname, strlen(*hostname));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Misc. utility functions and C-library extensions for finit and its plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -26,70 +26,64 @@
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* strerror() */
|
||||
#include <syslog.h>
|
||||
|
||||
#define DO_LOG(level, fmt, args...) \
|
||||
{ \
|
||||
do { \
|
||||
openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \
|
||||
syslog(level, fmt, ##args); \
|
||||
closelog(); \
|
||||
}
|
||||
} 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))
|
||||
|
||||
|
||||
#ifndef touch
|
||||
# define touch(x) mknod((x), S_IFREG|0644, 0)
|
||||
#endif
|
||||
#ifndef chardev
|
||||
# define chardev(x,m,maj,min) mknod((x), S_IFCHR|(m), makedev((maj),(min)))
|
||||
#endif
|
||||
#ifndef blkdev
|
||||
# define blkdev(x,m,maj,min) mknod((x), S_IFBLK|(m), makedev((maj),(min)))
|
||||
#endif
|
||||
/* 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)
|
||||
|
||||
#ifndef UNUSED
|
||||
#define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
|
||||
#endif
|
||||
#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)
|
||||
|
||||
#define echo(fmt, args...) do { if (verbose) { fprintf(stderr, fmt "\n", ##args); } } while (0)
|
||||
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __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)
|
||||
|
||||
extern int debug;
|
||||
extern int verbose;
|
||||
|
||||
int makepath (char *path);
|
||||
void ifconfig (char *ifname, char *addr, char *mask, int up);
|
||||
|
||||
pid_t pidfile_read (char *pidfile);
|
||||
pid_t pidfile_poll (char *cmd, char *path);
|
||||
void runlevel_set (int pre, int now);
|
||||
int runlevel_get (void);
|
||||
char *runlevel_string (int levels);
|
||||
|
||||
int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
void procname_set (char *name, char *args[]);
|
||||
int procname_kill (char *name, int signo);
|
||||
|
||||
void print_desc (char *action, char *desc);
|
||||
void print (int action, const char *fmt, ...);
|
||||
void print_desc (char *action, char *desc);
|
||||
int print_result (int fail);
|
||||
int start_process (char *cmd, char *args[], int console);
|
||||
void cls (void);
|
||||
void chomp (char *str);
|
||||
void do_sleep (unsigned int sec);
|
||||
int getuser (char *username);
|
||||
int getgroup (char *group);
|
||||
void set_hostname (char *hostname);
|
||||
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 *argv[]);
|
||||
int run_parts (char *dir, ...);
|
||||
|
||||
/* strlcpy.c */
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
pid_t run_getty (char *cmd, char *args[], int console);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
|
||||
#endif /* FINIT_HELPERS_H_ */
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,556 @@
|
||||
/* Classic inetd services launcher for Finit
|
||||
*
|
||||
* 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 <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "libuev/uev.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
#include "finit.h"
|
||||
#include "inetd.h"
|
||||
#include "helpers.h"
|
||||
#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); \
|
||||
} 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)
|
||||
{
|
||||
char cmbuf[0x100];
|
||||
struct msghdr msgh;
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
memset(&msgh, 0, sizeof(msgh));
|
||||
msgh.msg_control = cmbuf;
|
||||
msgh.msg_controllen = sizeof(cmbuf);
|
||||
|
||||
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
|
||||
return -1;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
|
||||
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||
|
||||
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
|
||||
continue;
|
||||
|
||||
if_indextoname(ipi->ipi_ifindex, ifname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||
int inetd_stream_peek(int sd, char *ifname)
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t len = sizeof(sin);
|
||||
|
||||
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
|
||||
return -1;
|
||||
|
||||
if (-1 == getifaddrs(&ifaddr))
|
||||
return -1;
|
||||
|
||||
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
|
||||
size_t len = sizeof(struct in_addr);
|
||||
struct sockaddr_in *iin;
|
||||
|
||||
if (!ifa->ifa_addr)
|
||||
continue;
|
||||
|
||||
if (ifa->ifa_addr->sa_family != AF_INET)
|
||||
continue;
|
||||
|
||||
iin = (struct sockaddr_in *)ifa->ifa_addr;
|
||||
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
|
||||
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inetd monitor, called by service_monitor() */
|
||||
int inetd_respawn(pid_t pid)
|
||||
{
|
||||
svc_t *svc = svc_find_by_pid(pid);
|
||||
|
||||
if (svc_is_inetd(svc)) {
|
||||
inetd_t *inetd = &svc->inetd;
|
||||
|
||||
svc->pid = 0;
|
||||
|
||||
if (svc_in_runlevel(svc, runlevel) && !inetd->forking)
|
||||
uev_io_set(&inetd->watcher, inetd->watcher.fd, UEV_READ);
|
||||
|
||||
return 1; /* It was us! */
|
||||
}
|
||||
|
||||
return 0; /* Not an inetd service */
|
||||
}
|
||||
|
||||
|
||||
void inetd_start(inetd_t *inetd)
|
||||
{
|
||||
if (inetd->watcher.fd == -1)
|
||||
spawn_socket(inetd);
|
||||
}
|
||||
|
||||
void inetd_stop(inetd_t *inetd)
|
||||
{
|
||||
svc_t *svc = (svc_t *)inetd->arg;
|
||||
|
||||
if (inetd->watcher.fd != -1) {
|
||||
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);
|
||||
}
|
||||
|
||||
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
int service_num;
|
||||
static struct servent lfs;
|
||||
|
||||
service_num = fgetint("/etc/services", " \n\t", service);
|
||||
if (service_num > 0) {
|
||||
lfs.s_name = service;
|
||||
lfs.s_port = htons(service_num);
|
||||
lfs.s_proto = NULL;
|
||||
if (!strcmp("tcp", proto) || !strcmp("udp", proto))
|
||||
lfs.s_proto = proto;
|
||||
*sv = &lfs;
|
||||
}
|
||||
#else
|
||||
*sv = getservbyname(service, proto);
|
||||
#endif
|
||||
if (!*sv) {
|
||||
const char *errstr;
|
||||
static struct servent s;
|
||||
|
||||
s.s_name = service;
|
||||
s.s_port = strtonum(service, 1, UINT16_MAX, &errstr);
|
||||
s.s_proto = NULL;
|
||||
if (!strcmp("tcp", proto) || !strcmp("udp", proto))
|
||||
s.s_proto = proto;
|
||||
|
||||
if (errstr || !s.s_proto) {
|
||||
_e("Invalid/unknown inetd service, cannot create custom entry.");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
_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
|
||||
if (!*pv) {
|
||||
_e("Cannot find proto %s, skipping.", (*sv)->s_proto);
|
||||
return errno = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find exact match.
|
||||
*/
|
||||
static inetd_filter_t *find_filter(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!ifname)
|
||||
ifname = "*";
|
||||
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
_d("Checking filters for %s: '%s' vs '%s' (exact match) ...",
|
||||
inetd->name, filter->ifname, ifname);
|
||||
|
||||
if (!strcmp(filter->ifname, ifname))
|
||||
return filter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* First try exact match, then fall back to any match.
|
||||
*/
|
||||
inetd_filter_t *inetd_filter_match(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter = find_filter(inetd, ifname);
|
||||
|
||||
if (filter)
|
||||
return filter;
|
||||
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
_d("Checking filters for %s: '%s' vs '%s' (any match) ...",
|
||||
inetd->name, filter->ifname, ifname);
|
||||
|
||||
if (!strcmp(filter->ifname, "*"))
|
||||
return filter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Poor man's tcpwrappers filtering */
|
||||
int inetd_allow(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd)
|
||||
return errno = EINVAL;
|
||||
|
||||
if (!ifname)
|
||||
ifname = "*";
|
||||
|
||||
filter = inetd_filter_match(inetd, ifname);
|
||||
if (filter) {
|
||||
_d("Filter %s for inetd %s already exists, skipping.", ifname, inetd->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_d("Allow iface %s for service %s (port %d)", ifname, inetd->name, inetd->port);
|
||||
filter = calloc(1, sizeof(*filter));
|
||||
if (!filter) {
|
||||
_e("Out of memory, cannot add filter to service %s", inetd->name);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
filter->deny = 0;
|
||||
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
|
||||
TAILQ_INSERT_TAIL(&inetd->filters, filter, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_deny(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd)
|
||||
return errno = EINVAL;
|
||||
|
||||
if (!ifname)
|
||||
ifname = "*";
|
||||
|
||||
filter = find_filter(inetd, ifname);
|
||||
if (filter) {
|
||||
_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;
|
||||
}
|
||||
|
||||
_d("Deny iface %s for service %s (port %d)", ifname, inetd->name, inetd->port);
|
||||
filter = calloc(1, sizeof(*filter));
|
||||
if (!filter) {
|
||||
_e("Out of memory, cannot add filter to service %s", inetd->name);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
filter->deny = 1;
|
||||
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
|
||||
TAILQ_INSERT_TAIL(&inetd->filters, filter, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_is_allowed(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
filter = inetd_filter_match(inetd, ifname);
|
||||
if (filter) {
|
||||
_d("Found matching filter for %s, deny: %d ... ", inetd->name, filter->deny);
|
||||
return !filter->deny;
|
||||
}
|
||||
|
||||
_d("No matching filter for %s ... ", inetd->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_match(inetd_t *inetd, char *service, char *proto)
|
||||
{
|
||||
struct servent *sv = NULL;
|
||||
struct protoent *pv = NULL;
|
||||
|
||||
if (!inetd || !service || !proto)
|
||||
return errno = EINVAL;
|
||||
|
||||
if (strncmp(inetd->name, service, sizeof(inetd->name)))
|
||||
return 0;
|
||||
|
||||
if (getent(service, proto, &sv, &pv))
|
||||
return 0;
|
||||
|
||||
if (inetd->proto == ntohs(pv->p_proto) &&
|
||||
inetd->port == ntohs(sv->s_port))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose presentable string of inetd filters: !eth0,eth1,eth2,!eth3 */
|
||||
int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
|
||||
{
|
||||
int prev = 0;
|
||||
char buf[42];
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd || !str || len <= 0) {
|
||||
_e("Dafuq?");
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(str, len, "%s allow %s ", inetd->name,
|
||||
inetd->type == SOCK_DGRAM ? "UDP" : "TCP");
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
if (filter->deny)
|
||||
continue;
|
||||
|
||||
if (!strlen(filter->ifname))
|
||||
snprintf(ifname, sizeof(ifname), "*");
|
||||
else
|
||||
strlcpy(ifname, filter->ifname, sizeof(ifname));
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s:%d", prev ? "," : "",
|
||||
ifname, inetd->port);
|
||||
prev = 1;
|
||||
strlcat(str, buf, len);
|
||||
}
|
||||
|
||||
prev = 0;
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
if (!filter->deny)
|
||||
continue;
|
||||
if (!prev) {
|
||||
snprintf(buf, sizeof(buf), " deny ");
|
||||
strlcat(str, buf, len);
|
||||
}
|
||||
|
||||
if (!strlen(filter->ifname))
|
||||
snprintf(ifname, sizeof(ifname), "*");
|
||||
else
|
||||
strlcpy(ifname, filter->ifname, sizeof(ifname));
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s", prev ? "," : "", ifname);
|
||||
prev = 1;
|
||||
strlcat(str, buf, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* interface. If a similar service runs on another port, this function
|
||||
* must add this ifname as "deny" to that other service.
|
||||
*
|
||||
* Example:
|
||||
* inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
|
||||
* inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i
|
||||
*
|
||||
* In this example eth0:222 is very specific, so when ssh/tcp (default)
|
||||
* is added we must find the previous 'ssh' service and add its eth0 as
|
||||
* deny, so we don't accept port 22 session on eth0.
|
||||
*
|
||||
* In the reverse case, where the default (ssh/tcp) entry is listed
|
||||
* before the specific (eth0:222), the new inetd service must find the
|
||||
* (any!) previous rule and add its ifname to their deny list.
|
||||
*
|
||||
* 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 result;
|
||||
struct servent *sv = NULL;
|
||||
struct protoent *pv = NULL;
|
||||
|
||||
if (!inetd || !service || !proto)
|
||||
return errno = EINVAL;
|
||||
|
||||
result = getent(service, proto, &sv, &pv);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* Setup defaults */
|
||||
inetd->std = 1;
|
||||
inetd->port = ntohs(sv->s_port);
|
||||
inetd->proto = pv->p_proto;
|
||||
inetd->forking = !!forking;
|
||||
if (!name)
|
||||
name = service;
|
||||
strlcpy(inetd->name, name, sizeof(inetd->name));
|
||||
TAILQ_INIT(&inetd->filters);
|
||||
|
||||
/* Naïve mapping tcp->stream, udp->dgram, other->dgram */
|
||||
if (!strcasecmp(sv->s_proto, "tcp"))
|
||||
inetd->type = SOCK_STREAM;
|
||||
else
|
||||
inetd->type = SOCK_DGRAM;
|
||||
|
||||
/* Reset descriptor, used internally */
|
||||
inetd->watcher.fd = -1;
|
||||
|
||||
/* Setup socket callback argument */
|
||||
inetd->arg = arg;
|
||||
|
||||
_d("New service %s (default port %d proto %s:%d)", name, inetd->port, sv->s_proto, pv->p_proto);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_del(inetd_t *inetd)
|
||||
{
|
||||
inetd_filter_t *filter, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(filter, &inetd->filters, link, tmp)
|
||||
free(filter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,81 @@
|
||||
/* Classic inetd services launcher for Finit
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_INETD_H_
|
||||
#define FINIT_INETD_H_
|
||||
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "libuev/uev.h"
|
||||
|
||||
|
||||
typedef struct inetd_filter {
|
||||
TAILQ_ENTRY(inetd_filter) link;
|
||||
int deny; /* 0:allow, 1:deny */
|
||||
char ifname[IFNAMSIZ]; /* E.g., eth0 */
|
||||
} inetd_filter_t;
|
||||
|
||||
typedef struct {
|
||||
uev_t watcher;
|
||||
void *arg; /* 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;
|
||||
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);
|
||||
|
||||
void 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, void *arg);
|
||||
int inetd_del (inetd_t *inetd);
|
||||
|
||||
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);
|
||||
|
||||
#endif /* FINIT_INETD_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,328 @@
|
||||
/* New client tool, complements old /dev/initctl API and telinit tool
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "service.h"
|
||||
|
||||
#include "libite/lite.h"
|
||||
|
||||
typedef struct {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
} command_t;
|
||||
|
||||
int debug = 0;
|
||||
int verbose = 0;
|
||||
int runlevel = 0;
|
||||
|
||||
static int do_send(struct init_request *rq, ssize_t len)
|
||||
{
|
||||
int sd, result = 255;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
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, len) != len)
|
||||
goto error;
|
||||
|
||||
if (read(sd, rq, len) != len)
|
||||
goto error;
|
||||
|
||||
result = 0;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int toggle_debug(char *UNUSED(arg))
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_DEBUG,
|
||||
};
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_runlevel(char *arg)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RUNLVL,
|
||||
.runlevel = (int)arg[0],
|
||||
};
|
||||
|
||||
/* Not compatible with the SysV runlevel(8) command, it prints
|
||||
* "PREVLEVEL RUNLEVEL", we just print the current runlevel. */
|
||||
if (!rq.runlevel) {
|
||||
printf("%d\n", runlevel_get());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_svc(int cmd, char *arg)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.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_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 int show_version(char *UNUSED(arg))
|
||||
{
|
||||
puts("v" 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 *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");
|
||||
}
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
char jobid[10], args[512] = "", *lvls;
|
||||
|
||||
if (svc_is_unique(svc))
|
||||
snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
||||
else
|
||||
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
||||
|
||||
printf("%-5s %7s %-6d ", jobid, svc_status(svc), svc->pid);
|
||||
lvls = runlevel_string(svc->runlevels);
|
||||
if (strchr(lvls, '\e'))
|
||||
printf("%-18.18s ", lvls);
|
||||
else
|
||||
printf("%-10.10s ", lvls);
|
||||
|
||||
if (!verbose) {
|
||||
printf("%-20s %s\n", svc->cmd, svc->desc);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
if (svc_is_inetd(svc)) {
|
||||
char *info;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_QUERY_INETD,
|
||||
};
|
||||
|
||||
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
|
||||
if (do_send(&rq, sizeof(rq))) {
|
||||
snprintf(args, sizeof(args), "Unknown inetd");
|
||||
info = args;
|
||||
} else {
|
||||
info = rq.data;
|
||||
if (strcmp("internal", svc->cmd)) {
|
||||
char *ptr = strchr(info, ' ');
|
||||
if (ptr)
|
||||
info = ptr + 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s %s\n", svc->cmd, info);
|
||||
}
|
||||
else
|
||||
#endif /* !INETD_DISABLED */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
|
||||
strlcat(args, svc->args[i], sizeof(args));
|
||||
strlcat(args, " ", sizeof(args));
|
||||
}
|
||||
|
||||
printf("%s %s\n", svc->cmd, args);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] <COMMAND>\n\n"
|
||||
"Options:\n"
|
||||
" -d, --debug Debug initctl (client)\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
" -h, --help This help text\n\n"
|
||||
"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"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" status | show Show status of services\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"
|
||||
" status <JOB|NAME>[:ID] Show status of a service by job# or name\n"
|
||||
" show <JOB|NAME>[:ID] Alias to 'status <JOB|NAME>[:ID]'\n"
|
||||
" version Show Finit version\n\n", __progname);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
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 */
|
||||
{ "start", do_start },
|
||||
{ "stop", do_stop },
|
||||
{ "restart", do_restart },
|
||||
{ "version", show_version },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"debug", 0, NULL, 'd'},
|
||||
{"verbose", 0, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
verbose = 0;
|
||||
while ((c = getopt_long(argc, argv, "dh?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
char *cmd = argv[optind++];
|
||||
char arg[120] = "";
|
||||
|
||||
while (optind < argc) {
|
||||
strlcat(arg, argv[optind++], sizeof(arg));
|
||||
strlcat(arg, " ", sizeof(arg));
|
||||
}
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!strcmp(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
Submodule
+1
Submodule libite added at 12ed342d55
@@ -1,79 +0,0 @@
|
||||
# Finit libite -- Light-weight utility functions and C-library extensions
|
||||
#
|
||||
# Copyright (c) 2008-2012 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.
|
||||
|
||||
.EXPORT_ALL_VARIABLES:
|
||||
.PHONY: all install clean
|
||||
|
||||
include ../common.mk
|
||||
|
||||
CFLAGS += -fPIC
|
||||
HEADERS = lite.h
|
||||
OBJS := copyfile.o dir.o fexist.o fisdir.o fmode.o rsync.o
|
||||
DEPS := $(addprefix ., $(OBJS:.o=.d))
|
||||
|
||||
VER = 0
|
||||
LIBNAME = libite
|
||||
TARGET = $(LIBNAME).so
|
||||
VERLIB = $(TARGET).$(VER)
|
||||
STATICLIB = $(LIBNAME).a
|
||||
|
||||
CHECK_FLAGS = $(CFLAGS) $(CPPFLAGS)
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(OBJS): Makefile
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
@printf " LINK %s\n" $@
|
||||
# @$(AR) $(ARFLAGS) $(STATICLIB) $(OBJS)
|
||||
@$(CC) $(LDFLAGS) -shared $(OBJS) -lrt -lcrypt -Wl,-soname,$@ -o $@
|
||||
@ln -sf $@ $(VERLIB)
|
||||
|
||||
install-exec: all
|
||||
@install -d $(DESTDIR)$(libdir)
|
||||
@echo " INSTALL $(DESTDIR)$(libdir)/$(TARGET)"
|
||||
@$(STRIPINST) $(TARGET) $(DESTDIR)$(libdir)/$(TARGET)
|
||||
@ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(VERLIB)
|
||||
|
||||
install-dev:
|
||||
@for file in $(HEADERS); do \
|
||||
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
|
||||
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
|
||||
done
|
||||
|
||||
install: install-dev install-exec
|
||||
|
||||
uninstall:
|
||||
-@$(RM) $(DESTDIR)$(libdir)/$(VERLIB)
|
||||
-@$(RM) $(DESTDIR)$(libdir)/$(TARGET)
|
||||
|
||||
clean: uninstall
|
||||
-@$(RM) $(OBJS) $(DEPS) $(TARGET) $(VERLIB) $(STATICLIB)
|
||||
|
||||
distclean: clean
|
||||
-@$(RM) $(JUNK) unittest *.o .*.d *.unittest
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),distclean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
endif
|
||||
@@ -1,317 +0,0 @@
|
||||
/* Fastinit (finit) copyfile() implementation.
|
||||
*
|
||||
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lite.h"
|
||||
|
||||
static int __isdir_and_append_filename(char *src, char **dst);
|
||||
|
||||
/**
|
||||
* copyfile - Copy a file to another.
|
||||
* @src: Source file.
|
||||
* @dst: Target file.
|
||||
* @len: Number of bytes to copy, zero (0) for entire file.
|
||||
*
|
||||
* This is a C implementation of the command line cp(1) utility. It is one
|
||||
* of the classic missing links in the UNIX C library. This version is from
|
||||
* the finit project, http://helllabs.org/finit/, which is a reimplementation
|
||||
* of fastinit for the Asus EeePC.
|
||||
*
|
||||
* Only some small changes to save stack space have been introduced.
|
||||
*
|
||||
* Returns:
|
||||
* The number of bytes copied, zero may be error (check errno!), but it
|
||||
* may also indicate that @src was empty. If @src is a directory errno
|
||||
* will be set to %EISDIR since copyfile() is not recursive.
|
||||
*/
|
||||
ssize_t copyfile(char *src, char *dst, int len)
|
||||
{
|
||||
char *buffer;
|
||||
int s, d, n, isdir = 0, saved_errno = 0;
|
||||
ssize_t size = 0;
|
||||
|
||||
errno = 0; /* Reset before leaving this function. */
|
||||
// fprintf (stderr, "%s() src: %s, dst: %s, len: %d\n", __func__, src, dst, len);
|
||||
|
||||
buffer = malloc(BUFSIZ);
|
||||
if (!buffer)
|
||||
return 0;
|
||||
|
||||
if (fisdir(src)) {
|
||||
errno = EISDIR; /* Error: source is a directory */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if target is a directory, then append the src base filename. */
|
||||
isdir = __isdir_and_append_filename(src, &dst);
|
||||
|
||||
/* Len == 0 means copy entire file */
|
||||
if (len == 0) {
|
||||
/* XXX: This feels a bit unsafe, but 2047 MiB may be sufficient for us.
|
||||
* --Jocke 2008-08-11 */
|
||||
len = INT_MAX;
|
||||
}
|
||||
|
||||
if ((s = open(src, O_RDONLY)) >= 0) {
|
||||
if ((d =
|
||||
open(dst, O_WRONLY | O_CREAT | O_TRUNC,
|
||||
fmode(src))) >= 0) {
|
||||
do {
|
||||
int clen = len > BUFSIZ ? BUFSIZ : len;
|
||||
|
||||
if ((n = read(s, buffer, clen)) > 0) {
|
||||
size += write(d, buffer, n);
|
||||
}
|
||||
len -= clen;
|
||||
}
|
||||
while (len > 0 && n == BUFSIZ);
|
||||
|
||||
close(d);
|
||||
} else {
|
||||
saved_errno = errno;
|
||||
}
|
||||
|
||||
close(s);
|
||||
} else {
|
||||
saved_errno = errno;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
if (isdir)
|
||||
free(dst);
|
||||
errno = saved_errno;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* movefile - Move a file to another location
|
||||
* @src: Source file.
|
||||
* @dst: Target file, or location.
|
||||
*
|
||||
* This is a C implementation of the command line mv(1) utility.
|
||||
* Usually the rename() API is sufficient, but not when moving across
|
||||
* file system boundaries.
|
||||
*
|
||||
* The @src argument must include the full path to the source file,
|
||||
* whereas the @dst argument may only be a directory, in which case the
|
||||
* same file name from @src is used.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0), or non-zero with errno set.
|
||||
*/
|
||||
int movefile(char *src, char *dst)
|
||||
{
|
||||
int isdir, result = 0;
|
||||
|
||||
/* Check if target is a directory, then append the src base filename. */
|
||||
isdir = __isdir_and_append_filename(src, &dst);
|
||||
|
||||
if (rename(src, dst)) {
|
||||
if (errno == EXDEV) {
|
||||
errno = 0;
|
||||
copyfile(src, dst, 0);
|
||||
if (errno)
|
||||
result = 1;
|
||||
else
|
||||
result = remove(src);
|
||||
} else {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (isdir)
|
||||
free(dst);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy_filep - Copy between FILE *fp.
|
||||
* @src: Source FILE.
|
||||
* @dst: Destination FILE.
|
||||
*
|
||||
* Function takes signals into account and will restart the syscalls as long
|
||||
* as error is %EINTR.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0), or non-zero with errno set on error.
|
||||
*/
|
||||
int copy_filep(FILE *src, FILE *dst)
|
||||
{
|
||||
int result = 0;
|
||||
char *buf = (char *)malloc(BUFSIZ);
|
||||
|
||||
if (!src || !dst)
|
||||
return EINVAL;
|
||||
|
||||
if (!buf)
|
||||
return errno;
|
||||
|
||||
while (1) {
|
||||
if (!fgets(buf, BUFSIZ, src)) {
|
||||
if (errno == EINTR) {
|
||||
clearerr(src);
|
||||
continue;
|
||||
}
|
||||
if (feof(src))
|
||||
break;
|
||||
|
||||
result = errno;
|
||||
goto exit;
|
||||
}
|
||||
while (EOF == fputs(buf, dst)) {
|
||||
if (errno == EINTR) {
|
||||
clearerr(dst);
|
||||
continue;
|
||||
}
|
||||
|
||||
result = errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
free(buf);
|
||||
return errno = result;
|
||||
}
|
||||
|
||||
/* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */
|
||||
static int __isdir_and_append_filename(char *src, char **dst)
|
||||
{
|
||||
int isdir = 0;
|
||||
|
||||
if (fisdir(*dst)) {
|
||||
int slash = 0;
|
||||
char *tmp, *ptr = strrchr(src, '/');
|
||||
|
||||
if (!ptr)
|
||||
ptr = src;
|
||||
else
|
||||
ptr++;
|
||||
|
||||
// fprintf (stderr, "Yes %s is a dir --> add %s\n", *dst, ptr);
|
||||
tmp = malloc(strlen(*dst) + strlen(ptr) + 2);
|
||||
if (!tmp) {
|
||||
errno = EISDIR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
isdir = 1; /* Free dst before exit! */
|
||||
slash = fisslashdir(*dst);
|
||||
|
||||
sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr);
|
||||
*dst = tmp;
|
||||
// fprintf (stderr, "NEW DST:%s\n", *dst);
|
||||
}
|
||||
|
||||
return isdir;
|
||||
}
|
||||
|
||||
#ifdef UNITTEST
|
||||
#include <stdio.h>
|
||||
/* XXX: Make a real set of unit tests ... */
|
||||
#if 0
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <src> <dst>\nCopies the <src> file to the <dst> file.\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Copying file %s to %s\n", argv[1], argv[2]);
|
||||
|
||||
return copyfile(argv[1], argv[2], 0);
|
||||
}
|
||||
#else
|
||||
int main(void)
|
||||
{
|
||||
int i = 0;
|
||||
char *files[] = {
|
||||
"/etc/passwd", "/tmp/tok", "/tmp/tok",
|
||||
"/etc/passwd", "/tmp/", "/tmp/passwd",
|
||||
"/etc/passwd", "/tmp", "/tmp/passwd",
|
||||
NULL
|
||||
};
|
||||
FILE *src, *dst;
|
||||
|
||||
fprintf(stderr, "\n=>Start testing copy_filep()\n");
|
||||
while (files[i]) {
|
||||
fprintf(stderr, "copy_filep(%s, %s)\t", files[i],
|
||||
files[i + 1]);
|
||||
src = fopen(files[i], "r");
|
||||
dst = fopen(files[i + 1], "w");
|
||||
if (copy_filep(src, dst))
|
||||
perror("Failed");
|
||||
|
||||
if (src)
|
||||
fclose(src);
|
||||
if (dst)
|
||||
fclose(dst);
|
||||
|
||||
if (fexist(files[i + 2]))
|
||||
fprintf(stderr, "OK => %s\n", files[i + 2]);
|
||||
|
||||
remove(files[i + 2]);
|
||||
i += 3;
|
||||
}
|
||||
|
||||
printf("\n\n=>Start testing copyfile()\n");
|
||||
i = 0;
|
||||
while (files[i]) {
|
||||
fprintf(stderr, "copyfile(%s, %s)\t", files[i], files[i + 1]);
|
||||
if (!copyfile(files[i], files[i + 1], 0))
|
||||
perror("Failed");
|
||||
|
||||
if (fexist(files[i + 2]))
|
||||
fprintf(stderr, "OK => %s\n", files[i + 2]);
|
||||
|
||||
remove(files[i + 2]);
|
||||
i += 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* UNITTEST */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* compile-command: "gcc -g -I../include -o unittest -DUNITTEST copyfile.c && ./unittest"
|
||||
* version-control: t
|
||||
* kept-new-versions: 2
|
||||
* c-file-style: "ellemtel"
|
||||
* End:
|
||||
*/
|
||||
-228
@@ -1,228 +0,0 @@
|
||||
/* Functions for operating on files in directories.
|
||||
*
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *matcher_type = NULL;
|
||||
static int (*matcher_filter) (const char *file) = NULL;
|
||||
static int matcher(const struct dirent *entry)
|
||||
{
|
||||
char *pos = strrchr(entry->d_name, '.');
|
||||
|
||||
if (matcher_filter && !matcher_filter(entry->d_name))
|
||||
/* User matcher overrides the rest. */
|
||||
return 0;
|
||||
|
||||
/* Skip current dir "." from list of files. */
|
||||
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
|
||||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
|
||||
return 0;
|
||||
|
||||
/* filetype == "" */
|
||||
if (matcher_type[0] == 0)
|
||||
return 1;
|
||||
|
||||
/* Entry has no "." */
|
||||
if (!pos)
|
||||
return 0;
|
||||
|
||||
return !strcmp(pos, matcher_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* dir - List all files of a certain type in the given directory.
|
||||
* @dir: Base directory for dir operation.
|
||||
* @type: File type suffix, e.g. ".cfg".
|
||||
* @filter: Optional file name filter.
|
||||
* @list: Pointer to an array of file names.
|
||||
* @strip: Flag, if set dir() strips the file type.
|
||||
*
|
||||
* This function returns a @list of files, matching the @type suffix,
|
||||
* in the given directory @dir.
|
||||
*
|
||||
* The @list argument is a pointer to where to store the dynamically
|
||||
* allocated list of file names. This list should be free'd by first
|
||||
* calling free() on each file name and then on the list itself.
|
||||
*
|
||||
* If @filter is not %NULL it will be called for each file found. If
|
||||
* @filter returns non-zero the @file argument will be included in the
|
||||
* resulting @list. If @filter returns zero for given @file it will
|
||||
* be discarded.
|
||||
*
|
||||
* If the @strip flag is set the resulting @list of files has their
|
||||
* file type stripped, including the dot. So a match "config0.cfg"
|
||||
* would be returned as "config0".
|
||||
*
|
||||
* Returns:
|
||||
* Number of files in @list, zero if no matching files of @type.
|
||||
*/
|
||||
int dir(const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip)
|
||||
{
|
||||
int i, n, num = 0;
|
||||
char **files;
|
||||
struct dirent **namelist;
|
||||
|
||||
assert(list);
|
||||
|
||||
if (!dir)
|
||||
/* Assuming current directory */
|
||||
dir = ".";
|
||||
if (!type)
|
||||
/* Assuming all files. */
|
||||
type = "";
|
||||
|
||||
matcher_type = type;
|
||||
matcher_filter = filter;
|
||||
n = scandir(dir, &namelist, matcher, alphasort);
|
||||
if (n < 0) {
|
||||
perror("scandir");
|
||||
} else {
|
||||
files = (char **)malloc(n * sizeof(char *));
|
||||
for (i = 0; i < n; i++) {
|
||||
if (files) {
|
||||
char *name = namelist[i]->d_name;
|
||||
char *type = strrchr(name, '.');
|
||||
|
||||
if (type && strip)
|
||||
*type = 0;
|
||||
|
||||
files[i] = strdup(name);
|
||||
num++;
|
||||
}
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
if (num)
|
||||
*list = files;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
#ifdef UNITTEST
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "otn/c.h"
|
||||
|
||||
#define DIR_TYPE_IMAGE ".img"
|
||||
#define DIR_TYPE_SYSLOG ""
|
||||
#define DIR_TYPE_CONFIG ".cfg"
|
||||
#define STARTUP_CONFIG "startup-config.cfg"
|
||||
|
||||
int simulate_files(int creat)
|
||||
{
|
||||
int i;
|
||||
char *files[] =
|
||||
{ "config0.cfg", "config1.cfg", "config2.cfg", "config3.cfg",
|
||||
"rr109.img", "rx100.img", "rx107.img", "rm957.img", "messages"
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_ELEMENTS(files); i++) {
|
||||
if (creat)
|
||||
touch(files[i]);
|
||||
else
|
||||
remove(files[i]);
|
||||
}
|
||||
|
||||
if (creat)
|
||||
symlink("config2.cfg", STARTUP_CONFIG);
|
||||
else
|
||||
remove(STARTUP_CONFIG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfg_dir_filter(const char *file)
|
||||
{
|
||||
/* Skip the STARTUP_CONFIG file, it is a symbolic link to the
|
||||
* current startup configuration. */
|
||||
return ! !strcmp(file, STARTUP_CONFIG);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, num;
|
||||
char *type = DIR_TYPE_CONFIG;
|
||||
char **files;
|
||||
int once = 1;
|
||||
|
||||
int is_startup_config(const char *entry) {
|
||||
static char file[80];
|
||||
|
||||
if (once) {
|
||||
int len = readlink(STARTUP_CONFIG, file, sizeof(file));
|
||||
|
||||
if (len == -1)
|
||||
return 0;
|
||||
|
||||
file[len] = 0;
|
||||
once = 0; /* Only once per call to dir() */
|
||||
}
|
||||
//printf ("Comparing link %s with entry %s\n", file, entry);
|
||||
return !strcmp(file, entry);
|
||||
}
|
||||
|
||||
simulate_files(1);
|
||||
|
||||
if (argc >= 2) {
|
||||
if (!strcasecmp("CONFIG", argv[1])) {
|
||||
type = DIR_TYPE_CONFIG;
|
||||
system("ls -l *" DIR_TYPE_CONFIG);
|
||||
}
|
||||
if (!strcasecmp("IMAGE", argv[1])) {
|
||||
type = DIR_TYPE_IMAGE;
|
||||
system("ls -l *" DIR_TYPE_IMAGE);
|
||||
}
|
||||
if (!strcasecmp("SYSLOG", argv[1])) {
|
||||
type = DIR_TYPE_SYSLOG;
|
||||
system("ls -l *");
|
||||
}
|
||||
}
|
||||
|
||||
num = dir(NULL, type, cfg_dir_filter, &files, 0);
|
||||
if (num) {
|
||||
for (i = 0; i < num; i++) {
|
||||
printf("%s", files[i]);
|
||||
if (is_startup_config(files[i]))
|
||||
printf(" --> startup-config");
|
||||
printf("\n");
|
||||
|
||||
free(files[i]);
|
||||
}
|
||||
free(files);
|
||||
}
|
||||
|
||||
simulate_files(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* UNITTEST */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* compile-command: "gcc -g -DUNITTEST -o unittest dir.c"
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
-280
@@ -1,280 +0,0 @@
|
||||
/* Micro "rsync" implementation.
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h> /* NULL, free() */
|
||||
#include <string.h> /* strlen() */
|
||||
#include <strings.h> /* rindex() */
|
||||
#include <stdio.h>
|
||||
#include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "lite.h"
|
||||
#include "../helpers.h"
|
||||
|
||||
static int makedir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode);
|
||||
static int prune(char *dst, char **new_files, int new_num);
|
||||
|
||||
|
||||
/**
|
||||
* rsync - Synchronize contents and optionally remove non-existing backups
|
||||
* @src: Source directory
|
||||
* @dst: Destination directory
|
||||
* @delete: Prune files from @dst that no longer exist in @src.
|
||||
* @filter: Optional filtering function for source directory.
|
||||
*
|
||||
* This is a miniature implementation of the famous rsync for local use only.
|
||||
* In fact, it is not even a true rsync since it copies all files from @src
|
||||
* to @dst. The @delete option is useful for creating backups, when set all
|
||||
* files removed from src since last backup are pruned from the destination
|
||||
* (backup) directory.
|
||||
*
|
||||
* The filter callback, @filter, if provided, is used to determine what files to
|
||||
* include from the source directory when backing up. If a file is to be skipped
|
||||
* the callback should simply return zero.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0), or non-zero with @errno set on error.
|
||||
*/
|
||||
int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
|
||||
{
|
||||
char source[256];
|
||||
char dest[256];
|
||||
int i = 0, num = 0, result = 0;
|
||||
char **files; /* Array of file names. */
|
||||
|
||||
void copy(char *src, char *dst)
|
||||
{
|
||||
errno = 0;
|
||||
copyfile(src, dst, 0);
|
||||
if (errno) result++;
|
||||
}
|
||||
|
||||
if (!fisdir(dst))
|
||||
mkdir(dst, 0755);
|
||||
|
||||
if (!fisdir(src)) {
|
||||
if (!fexist(src))
|
||||
return 1;
|
||||
|
||||
copy(src, dst);
|
||||
return errno;
|
||||
}
|
||||
|
||||
/* Copy dir as well? */
|
||||
if (!fisslashdir(src)) {
|
||||
char *ptr = rindex(src, '/');
|
||||
|
||||
if (!ptr)
|
||||
ptr = src;
|
||||
else
|
||||
ptr++;
|
||||
|
||||
if (makedir(dest, sizeof(dest), dst, ptr, fmode(src)))
|
||||
return 1;
|
||||
dst = dest;
|
||||
}
|
||||
|
||||
num = dir(src, "", filter, &files, 0);
|
||||
for (i = 0; i < num; i++) {
|
||||
/* Recursively copy sub-directries */
|
||||
snprintf(source, sizeof(source), "%s%s%s", src, fisslashdir(src) ? "" : "/", files[i]);
|
||||
if (fisdir(source)) {
|
||||
char dst2[256];
|
||||
|
||||
strcat(source, "/");
|
||||
if (makedir (dst2, sizeof(dst2), dst, files[i], fmode(source))) {
|
||||
result++;
|
||||
continue;
|
||||
}
|
||||
|
||||
rsync(source, dst2, delete, filter);
|
||||
continue; /* Next file/dir in @src to copy... */
|
||||
}
|
||||
copy(source, dst);
|
||||
}
|
||||
|
||||
/* We ignore any errors from the pruning, that phase albeit useful is only
|
||||
* cosmetic. --Jocke 2011-03-24 */
|
||||
if (delete)
|
||||
prune(dst, files, num);
|
||||
|
||||
if (num) {
|
||||
for (i = 0; i < num; i++)
|
||||
free(files[i]);
|
||||
free(files);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Creates dir/name @mode ... skipping / if dir already ends so. */
|
||||
static int makedir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode)
|
||||
{
|
||||
snprintf(buf, buf_len, "%s%s%s/", dir, fisslashdir(dir) ? "" : "/", name);
|
||||
if (mkdir(buf, mode))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prune old files, no longer existing on source, from destination directory. */
|
||||
static int prune(char *dst, char **new_files, int new_num)
|
||||
{
|
||||
int i, num, result = 0;
|
||||
char **files;
|
||||
|
||||
int find(char *file) {
|
||||
int n;
|
||||
|
||||
for (n = 0; n < new_num; n++)
|
||||
if (!strncmp (new_files[n], file, MAX(strlen(new_files[n]), strlen(file))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
num = dir(dst, "", NULL, &files, 0);
|
||||
if (num) {
|
||||
for (i = 0; i < num; i++) {
|
||||
if (!find(files[i])) {
|
||||
char *name;
|
||||
size_t len = strlen(files[i]) + 2 + strlen(dst);
|
||||
|
||||
name = malloc(len);
|
||||
if (name) {
|
||||
snprintf(name, len, "%s%s%s", dst, fisslashdir(dst) ? "" : "/", files[i]);
|
||||
if (remove(name))
|
||||
result++;
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
free(files[i]);
|
||||
}
|
||||
free(files);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef UNITTEST
|
||||
#define BASE "/tmp/.unittest/"
|
||||
#define SRC BASE "src/"
|
||||
#define DST BASE "dst/"
|
||||
|
||||
char *files[] = {
|
||||
SRC "sub1/1.tst",
|
||||
SRC "sub1/2.tst",
|
||||
SRC "sub1/3.tst",
|
||||
SRC "sub2/4.tst",
|
||||
SRC "sub2/5.tst",
|
||||
SRC "sub2/6.tst",
|
||||
SRC "sub3/7.tst",
|
||||
SRC "sub3/8.tst",
|
||||
SRC "sub3/9.tst",
|
||||
NULL
|
||||
};
|
||||
|
||||
void cleanup_test(void)
|
||||
{
|
||||
system("rm -rf " BASE);
|
||||
}
|
||||
|
||||
void setup_test(void)
|
||||
{
|
||||
int i;
|
||||
char cmd[256];
|
||||
mode_t dir_modes[] = { 755, 700 };
|
||||
mode_t file_modes[] = { 644, 600 };
|
||||
|
||||
cleanup_test();
|
||||
|
||||
mkdir(BASE, 0755);
|
||||
mkdir(SRC, 0755);
|
||||
mkdir(DST, 0755);
|
||||
|
||||
for (i = 0; files[i]; i++) {
|
||||
snprintf(cmd, sizeof(cmd), "mkdir -m %d -p `dirname %s`",
|
||||
dir_modes[i % 2], files[i]);
|
||||
system(cmd);
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "touch %s; chmod %d %s", files[i],
|
||||
file_modes[i % 2], files[i]);
|
||||
system(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
int run_test()
|
||||
{
|
||||
#if 0
|
||||
setup_test();
|
||||
puts("Before:");
|
||||
system("tree -np " BASE);
|
||||
|
||||
rsync(SRC, DST, 0, NULL);
|
||||
puts("After:");
|
||||
system("tree -np " BASE);
|
||||
cleanup_test();
|
||||
#endif
|
||||
|
||||
setup_test();
|
||||
rsync(BASE "src", DST, 0, NULL);
|
||||
puts("Only partial rsync of src <-- No slash!");
|
||||
system("tree -np " BASE);
|
||||
#if 0
|
||||
cleanup_test();
|
||||
setup_test();
|
||||
rsync(BASE "src/sub1", BASE "dst", 0, NULL);
|
||||
puts("Only partial rsync of src/sub1 <-- No slashes!!");
|
||||
system("tree -np " BASE);
|
||||
|
||||
cleanup_test();
|
||||
setup_test();
|
||||
rsync(BASE "src/sub1/", DST, 0, NULL);
|
||||
puts("Only partial rsync of src/sub1/");
|
||||
system("tree -np " BASE);
|
||||
|
||||
cleanup_test();
|
||||
setup_test();
|
||||
rsync(BASE "src/sub1", DST, 0, NULL);
|
||||
puts("Only partial rsync of src/sub1 <-- No slash!");
|
||||
system("tree -np " BASE);
|
||||
|
||||
rsync("/etc", "/var/tmp", 0, NULL);
|
||||
puts("Real life test:");
|
||||
system("tree -np /var/tmp");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
atexit(cleanup_test);
|
||||
|
||||
return run_test();
|
||||
}
|
||||
#endif /* UNITTEST */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* compile-command: "make -f rsync.mk"
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -1,22 +0,0 @@
|
||||
# Unittest makefile for rsync.c
|
||||
.PHONY: clean
|
||||
|
||||
SRC = rsync.c
|
||||
OBJS = dir.o copyfile.o fexist.o fisdir.o fmode.o
|
||||
UNIT = rsync.unittest
|
||||
#CPPFLAGS = -DDEBUG -I../../.. -I../../include
|
||||
|
||||
all: build run
|
||||
|
||||
clean:
|
||||
@rm -f $(OBJS) $(UNIT)
|
||||
|
||||
# This rule also forces a cleanup of built objects to not leave any object files
|
||||
# lingering in the tree to interfere with cross builds.
|
||||
build: clean $(OBJS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -o $(UNIT) -DUNITTEST $(SRC) $(OBJS)
|
||||
@rm -f $(OBJS)
|
||||
|
||||
run:
|
||||
@./$(UNIT)
|
||||
|
||||
Submodule
+1
Submodule libuev added at cf5e729dd3
@@ -0,0 +1,99 @@
|
||||
/* Misc. utility functions and C-library extensions for finit and its 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
|
||||
/**
|
||||
* pid_alive - Check if a given process ID is running
|
||||
* @pid: Process ID to check for.
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) if pid is alive (/proc/pid exists), otherwise %FALSE(0)
|
||||
*/
|
||||
int pid_alive(pid_t pid)
|
||||
{
|
||||
char name[24]; /* Enough for max pid_t */
|
||||
|
||||
snprintf(name, sizeof(name), "/proc/%d", pid);
|
||||
|
||||
return fexist(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pid_get_name - Find name of a process
|
||||
* @pid: PID of process to find.
|
||||
* @name: Pointer to buffer where to return process name, may be %NULL
|
||||
* @len: Length in bytes of @name buffer.
|
||||
*
|
||||
* If @name is %NULL, or @len is zero the function will return
|
||||
* a static string. This may be useful to one-off calls.
|
||||
*
|
||||
* Returns:
|
||||
* %NULL on error, otherwise a va
|
||||
*/
|
||||
char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
{
|
||||
int ret = 1;
|
||||
FILE *fp;
|
||||
char *pname, path[32];
|
||||
static char line[64];
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/status", pid);
|
||||
fp = fopen(path, "r");
|
||||
if (fp) {
|
||||
if (fgets(line, sizeof (line), fp)) {
|
||||
pname = line + 6; /* Skip first part of line --> "Name:\t" */
|
||||
chomp(pname);
|
||||
if (name)
|
||||
strlcpy(name, pname, len);
|
||||
ret = 0; /* Found it! */
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
return pname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -26,97 +26,150 @@
|
||||
#include <dirent.h> /* readdir() et al */
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#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)
|
||||
|
||||
static size_t num_fds = 0;
|
||||
static struct pollfd fds[MAX_NUM_FDS];
|
||||
static LIST_HEAD(, plugin) plugins = LIST_HEAD_INITIALIZER();
|
||||
static char *plugpath = NULL; /* Set by first load. */
|
||||
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
static void check_plugin_depends(plugin_t *plugin);
|
||||
#endif
|
||||
|
||||
int plugin_register(plugin_t *plugin)
|
||||
{
|
||||
int i, inuse = 0;
|
||||
|
||||
if (!plugin) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Setup default name if none is provided */
|
||||
if (!plugin->name) {
|
||||
#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";
|
||||
#endif
|
||||
}
|
||||
|
||||
if (is_io_plugin(plugin)) {
|
||||
if (num_fds + 1 >= MAX_NUM_FDS) {
|
||||
num_fds = MAX_NUM_SVC;
|
||||
errno = ENOMEM;
|
||||
return 1;
|
||||
}
|
||||
num_fds++;
|
||||
inuse++;
|
||||
/* Already registered? */
|
||||
if (plugin_find(plugin->name)) {
|
||||
_d("... %s already loaded.", plugin->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (plugin->svc.cb) {
|
||||
svc_t *svc = svc_find(plugin->name);
|
||||
#ifndef ENABLE_STATIC
|
||||
/* Resolve plugin dependencies */
|
||||
check_plugin_depends(plugin);
|
||||
#endif
|
||||
|
||||
if (svc) {
|
||||
inuse++;
|
||||
svc->cb = plugin->svc.cb;
|
||||
svc->dynamic = plugin->svc.dynamic;
|
||||
svc->dynamic_stop = plugin->svc.dynamic_stop;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < HOOK_MAX_NUM; i++) {
|
||||
if (plugin->hook[i].cb)
|
||||
inuse++;
|
||||
}
|
||||
|
||||
if (!inuse) {
|
||||
_d("No service \"%s\" loaded, and no I/O or finit hooks, skipping plugin.", basename(plugin->name));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LIST_INSERT_HEAD(&plugins, plugin, link);
|
||||
TAILQ_INSERT_TAIL(&plugins, plugin, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not called, at the moment plugins cannot be unloaded. */
|
||||
int plugin_unregister(plugin_t *plugin)
|
||||
{
|
||||
LIST_REMOVE(plugin, link);
|
||||
#ifndef ENABLE_STATIC
|
||||
TAILQ_REMOVE(&plugins, plugin, link);
|
||||
|
||||
if (plugin->svc.cb) {
|
||||
svc_t *svc = svc_find(plugin->name);
|
||||
svc_t *svc;
|
||||
|
||||
if (svc) {
|
||||
svc->cb = NULL;
|
||||
svc->dynamic = 0;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: Unfinished, add cleanup code here! */
|
||||
/* Unload plugin */
|
||||
if (dlclose(plugin->handle)) {
|
||||
_e("Failed unloading %s ...", plugin->name);
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
_d("Finit built statically, cannot unload %s ...", plugin->name);
|
||||
#endif
|
||||
|
||||
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
|
||||
*
|
||||
* This function uses an opporunistic search for a suitable plugin and
|
||||
* returns the first match. Albeit with at least some measure of
|
||||
* heuristics.
|
||||
*
|
||||
* First it checks for an exact match. If no match is found and @name
|
||||
* starts with a slash the search ends. Otherwise a new search with the
|
||||
* plugin path prepended to @name is made. Also, if @name does not end
|
||||
* with .so it too is added to @name before searching.
|
||||
*
|
||||
* Returns:
|
||||
* On success the pointer to the matching &plugin_t is returned,
|
||||
* otherwise %NULL is returned.
|
||||
*/
|
||||
plugin_t *plugin_find(char *name)
|
||||
{
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SEARCH_PLUGIN(name);
|
||||
|
||||
if (plugpath && name[0] != '/') {
|
||||
int noext;
|
||||
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" : "");
|
||||
|
||||
SEARCH_PLUGIN(path);
|
||||
}
|
||||
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Private daemon API *******************************************************/
|
||||
void plugin_run_hooks(hook_point_t no)
|
||||
{
|
||||
plugin_t *p;
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p) {
|
||||
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);
|
||||
@@ -125,96 +178,157 @@ void plugin_run_hooks(hook_point_t no)
|
||||
}
|
||||
|
||||
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
|
||||
static void generic_io_cb(int fd, int events)
|
||||
static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
plugin_t *p;
|
||||
plugin_t *p = (plugin_t *)arg;
|
||||
|
||||
/* Find matching plugin, pick first matching fd */
|
||||
PLUGIN_ITERATOR(p) {
|
||||
if (is_io_plugin(p) && p->io.fd == fd) {
|
||||
_d("Calling I/O %s from runloop...", basename(p->name));
|
||||
p->io.cb(p->io.arg, fd, events);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_io_plugin(p) && p->io.fd == w->fd) {
|
||||
/* Stop watcher, callback may close descriptor on us ... */
|
||||
uev_io_stop(w);
|
||||
|
||||
void plugin_monitor(void)
|
||||
{
|
||||
int ret;
|
||||
size_t i;
|
||||
_d("Calling I/O %s from runloop...", basename(p->name));
|
||||
p->io.cb(p->io.arg, w->fd, events);
|
||||
|
||||
while ((ret = poll(fds, num_fds, 500))) {
|
||||
if (-1 == ret) {
|
||||
if (EINTR == errno)
|
||||
continue;
|
||||
|
||||
_e("Failed polling I/O plugin descriptors, error %d: %s",
|
||||
errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Traverse all I/O fds and run callbacks */
|
||||
for (i = 0; i < num_fds; i++) {
|
||||
if (fds[i].revents)
|
||||
generic_io_cb(fds[i].fd, fds[i].revents);
|
||||
}
|
||||
|
||||
break;
|
||||
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
||||
uev_io_set(w, p->io.fd, p->io.flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup any I/O callbacks for plugins that use them */
|
||||
static void init_plugins(void)
|
||||
static void init_plugins(uev_ctx_t *ctx)
|
||||
{
|
||||
int i = 0;
|
||||
plugin_t *p;
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p) {
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (is_io_plugin(p)) {
|
||||
_d("Initializing plugin %s for I/O", basename(p->name));
|
||||
fds[i].revents = 0;
|
||||
fds[i].events = p->io.flags;
|
||||
fds[i++].fd = p->io.fd;
|
||||
uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int plugin_load_all(char *path)
|
||||
#ifndef ENABLE_STATIC
|
||||
/**
|
||||
* load_one - Load one plugin
|
||||
* @path: Path to finit plugins, usually %PLUGIN_PATH
|
||||
* @name: Name of plugin, optionally ending in ".so"
|
||||
*
|
||||
* Loads a plugin from @path/@name[.so]. Note, if ".so" is missing from
|
||||
* the plugin @name it is added before attempting to load.
|
||||
*
|
||||
* It is up to the plugin itself ot register itself as a "ctor" with the
|
||||
* %PLUGIN_INIT macro so that plugin_register() is called automatically.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, non-zero otherwise.
|
||||
*/
|
||||
static int load_one(char *path, char *name)
|
||||
{
|
||||
int fail = 0;
|
||||
DIR *dp = opendir(path);
|
||||
struct dirent *entry;
|
||||
int noext;
|
||||
char sofile[CMD_SIZE];
|
||||
void *handle;
|
||||
plugin_t *plugin;
|
||||
|
||||
if (!dp) {
|
||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||
if (!path || !fisdir(path) || !name) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dp))) {
|
||||
char *ext = entry->d_name + strlen(entry->d_name) - 3;
|
||||
/* Compose full path, with optional .so extension, to plugin */
|
||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
|
||||
|
||||
if (!strcmp(ext, ".so")) {
|
||||
int result = 0;
|
||||
void *handle;
|
||||
char plugin[1024];
|
||||
_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);
|
||||
if (!handle) {
|
||||
_e("Failed loading plugin %s: %s", sofile, dlerror());
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(plugin, sizeof(plugin), "%s/%s", path, entry->d_name);
|
||||
// print_desc(" Loading plugin ", basename(plugin));
|
||||
handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (!handle) {
|
||||
_d("Failed loading plugin %s: %s", plugin, dlerror());
|
||||
result = 1;
|
||||
}
|
||||
if (result)
|
||||
fail++;
|
||||
plugin = TAILQ_LAST(&plugins, plugin_head);
|
||||
if (!plugin) {
|
||||
_e("Plugin %s failed to register, unloading from memory.", sofile);
|
||||
dlclose(handle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// print_result(result);
|
||||
/* Remember handle from dlopen() for plugin_unregister() */
|
||||
plugin->handle = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_plugin_depends - Check and load any plugins this one depends on.
|
||||
* @plugin: Plugin with possible depends to check
|
||||
*
|
||||
* Very simple dependency resolver, should actually load the plugin of
|
||||
* the correct .name, but currently loads a matching filename.
|
||||
*
|
||||
* Works, but only for now. A better way might be to have a try_load()
|
||||
* that actually loads all plugins and checks their &plugin_t for the
|
||||
* correct .name.
|
||||
*/
|
||||
static void check_plugin_depends(plugin_t *plugin)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
|
||||
// _d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
|
||||
if (plugin_find(plugin->depends[i])) {
|
||||
// _d("OK plugin %s was already loaded.", plugin->depends[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
load_one(plugpath, plugin->depends[i]);
|
||||
}
|
||||
}
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
int plugin_load_all(uev_ctx_t *ctx, char *path)
|
||||
{
|
||||
int fail = 0;
|
||||
#ifndef ENABLE_STATIC
|
||||
DIR *dp = opendir(path);
|
||||
struct dirent *entry;
|
||||
|
||||
if (verbose)
|
||||
print_desc("Loading plugins", NULL);
|
||||
if (!dp) {
|
||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||
fail = 1;
|
||||
goto exit;
|
||||
}
|
||||
plugpath = path;
|
||||
|
||||
while ((entry = readdir(dp))) {
|
||||
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);
|
||||
init_plugins();
|
||||
#else
|
||||
_d("Finit built statically, not loading plugins from %s ...", path);
|
||||
if (verbose)
|
||||
print_desc("Initializing plugins", NULL);
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
/* Always initialize plugins */
|
||||
init_plugins(ctx);
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
exit:
|
||||
#endif
|
||||
if (verbose)
|
||||
print_result(fail);
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
@@ -24,27 +24,36 @@
|
||||
#ifndef FINIT_PLUGIN_H_
|
||||
#define FINIT_PLUGIN_H_
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/queue.h> /* BSD sys/queue.h API */
|
||||
#include "queue.h" /* BSD sys/queue.h API */
|
||||
#include "svc.h"
|
||||
#include "libuev/uev.h"
|
||||
|
||||
#define PLUGIN_IO_READ POLLIN
|
||||
#define PLUGIN_IO_WRITE POLLOUT
|
||||
#define PLUGIN_DEP_MAX 10
|
||||
#define PLUGIN_IO_READ UEV_READ
|
||||
#define PLUGIN_IO_WRITE UEV_WRITE
|
||||
|
||||
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
|
||||
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
|
||||
|
||||
#define PLUGIN_ITERATOR(x) LIST_FOREACH(x, &plugins, link)
|
||||
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
|
||||
|
||||
/*
|
||||
* Predefined hook points for easier plugin debugging
|
||||
*/
|
||||
typedef enum {
|
||||
/* Bootstrap hooks */
|
||||
HOOK_ROOTFS_UP = 0,
|
||||
HOOK_MOUNT_ERROR,
|
||||
HOOK_BASEFS_UP,
|
||||
HOOK_NETWORK_UP,
|
||||
HOOK_SYSTEM_UP,
|
||||
HOOK_SVC_UP,
|
||||
HOOK_SYSTEM_UP,
|
||||
|
||||
/* Runtime hooks */
|
||||
HOOK_SVC_RECONF,
|
||||
HOOK_RUNLEVEL_CHANGE,
|
||||
|
||||
/* Shutdown hooks */
|
||||
HOOK_SHUTDOWN,
|
||||
HOOK_MAX_NUM
|
||||
} hook_point_t;
|
||||
@@ -76,10 +85,16 @@ typedef enum {
|
||||
*/
|
||||
typedef struct plugin {
|
||||
/* BSD sys/queue.h linked list node. */
|
||||
LIST_ENTRY(plugin) link;
|
||||
TAILQ_ENTRY(plugin) link;
|
||||
|
||||
/* Plugin name, defaults to plugin path if unset.
|
||||
* NOTE: Must be same as @cmd for service plugins! */
|
||||
/* Handle to be sent to dlclose() when plugins is unloaded. */
|
||||
void *handle;
|
||||
|
||||
/* Event loop handler, used internally by Finit */
|
||||
uev_t watcher;
|
||||
|
||||
/* Plugin name, defaults to basename of plugin path if unset.
|
||||
* NOTE: Must match cmd for services or inetd plugins! */
|
||||
char *name;
|
||||
|
||||
/* Service callback to be called once per lap of runloop. */
|
||||
@@ -101,12 +116,23 @@ typedef struct plugin {
|
||||
void *arg;
|
||||
void (*cb)(void *arg, int fd, int events);
|
||||
} io;
|
||||
|
||||
/* Inetd Plugin, stdio used as client socket.
|
||||
* @type argument will be either SOCK_DGRAM or SOCK_STREAM */
|
||||
struct {
|
||||
int (*cmd)(int type);
|
||||
} inetd;
|
||||
|
||||
char *depends[PLUGIN_DEP_MAX]; /* List of other .name's this depends on. */
|
||||
} plugin_t;
|
||||
|
||||
/* Public plugin API */
|
||||
int plugin_register (plugin_t *plugin);
|
||||
int plugin_unregister (plugin_t *plugin);
|
||||
|
||||
/* Helper API */
|
||||
plugin_t *plugin_find (char *name);
|
||||
|
||||
#endif /* FINIT_PLUGIN_H_ */
|
||||
|
||||
/**
|
||||
|
||||
+27
-10
@@ -1,22 +1,33 @@
|
||||
# Plugin based services architecture for finit -*-Makefile-*-
|
||||
CFLAGS += -fPIC -Os
|
||||
CPPFLAGS += -I..
|
||||
# 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
|
||||
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
|
||||
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) *.o
|
||||
-@$(RM) $(PLUGINS)
|
||||
|
||||
distclean: clean
|
||||
-@$(RM) $(JUNK) $(DEPS)
|
||||
distclean:
|
||||
-@$(RM) *.o *.so $(JUNK) $(DEPS)
|
||||
|
||||
ifneq ($(STATIC), 1)
|
||||
install: all
|
||||
@install -d $(DESTDIR)$(plugindir)
|
||||
@for plugin in $(PLUGINS); do \
|
||||
@@ -30,7 +41,13 @@ uninstall:
|
||||
rm $(DESTDIR)$(plugindir)/$$plugin 2>/dev/null; \
|
||||
done
|
||||
-@rmdir $(DESTDIR)$(plugindir) 2>/dev/null
|
||||
else
|
||||
install:
|
||||
@true
|
||||
uninstall:
|
||||
@true
|
||||
endif
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(DEPS)
|
||||
ifneq ($(MAKECMDGOALS),distclean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
|
||||
@@ -21,23 +21,25 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void save(void *UNUSED(arg))
|
||||
{
|
||||
_d("Saving system clock to RTC ...");
|
||||
run_interactive("/usr/sbin/alsactl store > /dev/null 2>&1", "Saving sound settings");
|
||||
_d("Saving sound settings ...");
|
||||
run_interactive("/usr/sbin/alsactl -g store", "Saving sound settings");
|
||||
}
|
||||
|
||||
static void restore(void *UNUSED(arg))
|
||||
{
|
||||
_d("Restoring system clock from RTC ...");
|
||||
run_interactive("/usr/sbin/alsactl restore > /dev/null 2>&1", "Restoring sound settings");
|
||||
_d("Restoring sound settings ...");
|
||||
run_interactive("/usr/sbin/alsactl -g restore", "Restoring sound settings");
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = restore
|
||||
},
|
||||
|
||||
+34
-7
@@ -24,20 +24,42 @@
|
||||
#include <string.h>
|
||||
#include <utmp.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "../config.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
/*
|
||||
* Setup standard FHS 2.3 structure in /var, and
|
||||
* write runlevel to UTMP, needed by, e.g., printerdrake.
|
||||
*/
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
#ifdef RUNLEVEL
|
||||
struct utmp entry;
|
||||
#endif
|
||||
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
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);
|
||||
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);
|
||||
|
||||
_d("Setting up necessary UTMP files ...");
|
||||
touch("/var/run/utmp");
|
||||
chown("/var/run/utmp", 0, getgroup("utmp"));
|
||||
chmod("/var/run/utmp", 0664);
|
||||
|
||||
#ifdef RUNLEVEL
|
||||
memset(&entry, 0, sizeof(struct utmp));
|
||||
@@ -51,16 +73,21 @@ static void setup(void *UNUSED(arg))
|
||||
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
|
||||
touch("/etc/network/run/ifstate");
|
||||
#else
|
||||
remove("/etc/network/run/ifstate");
|
||||
erase("/etc/network/run/ifstate");
|
||||
#endif
|
||||
|
||||
_d("Setting up misc files ...");
|
||||
mkdir("/var/run/sshd", 01755); /* OpenSSH */
|
||||
mkfifo("/dev/xconsole", 0640); /* sysklogd */
|
||||
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 */
|
||||
chown("/dev/xconsole", 0, getgroup("tty"));
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
|
||||
+13
-7
@@ -21,26 +21,32 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../config.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
#ifdef USE_MESSAGE_BUS
|
||||
#ifdef HAVE_DBUS
|
||||
_d("Starting D-Bus ...");
|
||||
mkdir("/var/run/dbus", 0755);
|
||||
mkdir("/var/lock/subsys/messagebus", 0755);
|
||||
makedir("/var/run/dbus", 0755);
|
||||
makedir("/var/lock/subsys/messagebus", 0755);
|
||||
run("dbus-uuidgen --ensure");
|
||||
remove("/var/run/dbus/pid");
|
||||
erase("/var/run/dbus/pid");
|
||||
run_interactive("dbus-daemon --system", "Starting D-Bus");
|
||||
#endif
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_NETWORK_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+5
-3
@@ -21,9 +21,10 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void save(void *UNUSED(arg))
|
||||
{
|
||||
@@ -40,6 +41,7 @@ static void restore(void *UNUSED(arg))
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = restore
|
||||
},
|
||||
|
||||
+78
-42
@@ -1,7 +1,7 @@
|
||||
/* Optional /dev/initctl FIFO monitor
|
||||
/* Optional /dev/initctl FIFO monitor, for receiving commands from telinit
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -22,87 +22,123 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h" /* Generated by configure script */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> /* O_RDONLY et al */
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h> /* read() */
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "sig.h"
|
||||
#include "../finit.h"
|
||||
#include "../conf.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "../sig.h"
|
||||
#include "../service.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
#define INIT_MAGIC 0x03091969
|
||||
#define INIT_CMD_RUNLVL 1
|
||||
static void parse(void *arg, int fd, int events);
|
||||
|
||||
struct init_request {
|
||||
int magic; /* Magic number */
|
||||
int cmd; /* What kind of request */
|
||||
int runlevel; /* Runlevel to change to */
|
||||
int sleeptime; /* Time between TERM and KILL */
|
||||
char data[368];
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.io = {
|
||||
.cb = parse,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
|
||||
static void fifo_open(void)
|
||||
{
|
||||
plugin.io.fd = open(FINIT_FIFO, O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||
if (-1 == plugin.io.fd) {
|
||||
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Standard reboot/shutdown utilities talk to init using /dev/initctl.
|
||||
* We should check if the fifo was recreated and reopen it.
|
||||
*/
|
||||
static void read_initctl(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
{
|
||||
struct init_request request;
|
||||
struct init_request rq;
|
||||
|
||||
_d("Receiving request on %s...", FINIT_FIFO);
|
||||
while (1) {
|
||||
ssize_t len = read(fd, &request, sizeof(request));
|
||||
ssize_t len = read(fd, &rq, sizeof(rq));
|
||||
|
||||
if (-1 == len) {
|
||||
_d("Error %d", errno);
|
||||
break; /* Probably EAGAIN */
|
||||
if (len <= 0) {
|
||||
if (-1 == len) {
|
||||
if (EINTR == errno)
|
||||
continue;
|
||||
|
||||
if (EAGAIN == errno)
|
||||
break;
|
||||
|
||||
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||
}
|
||||
|
||||
_d("Nothing to do, bailing out.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (request.magic != INIT_MAGIC)
|
||||
continue;
|
||||
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||
_e("Invalid initctl request.");
|
||||
break;
|
||||
}
|
||||
|
||||
_d("Magic OK...");
|
||||
if (request.cmd == INIT_CMD_RUNLVL) {
|
||||
switch (request.runlevel) {
|
||||
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':
|
||||
_d("Setting new runlevel %c", rq.runlevel);
|
||||
service_runlevel(rq.runlevel - '0');
|
||||
break;
|
||||
|
||||
case '6':
|
||||
_d("Rebooting system (SIGUSR1)");
|
||||
do_shutdown(SIGUSR1);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported runlevel: %d", request.runlevel);
|
||||
_d("Unsupported runlevel: %d", rq.runlevel);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_d("Unsupported cmd: %d", request.cmd);
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD:
|
||||
service_reload_dynamic();
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.io = {
|
||||
.cb = read_initctl,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
close(fd);
|
||||
fifo_open();
|
||||
}
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
_d("Setting up %s", FINIT_FIFO);
|
||||
mkfifo(FINIT_FIFO, 0600);
|
||||
|
||||
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK);
|
||||
if (-1 == plugin.io.fd) {
|
||||
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
makefifo(FINIT_FIFO, 0600);
|
||||
|
||||
fifo_open();
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/* Netlink plugin for IFUP/IFDN and GW events
|
||||
*
|
||||
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
|
||||
* Copyright (C) 2009-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <net/if.h> /* IFNAMSIZ */
|
||||
#include <sys/socket.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../finit.h"
|
||||
#include "../event.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
|
||||
static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
|
||||
{
|
||||
if (!NLMSG_OK(nh, len))
|
||||
return 1;
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_DONE) {
|
||||
_d("Done with netlink messages.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_ERROR) {
|
||||
_d("Netlink reports error.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_route(struct nlmsghdr *nlmsg)
|
||||
{
|
||||
struct rtmsg *r;
|
||||
struct rtattr *a;
|
||||
int la;
|
||||
int gw = 0, dst = 0, mask = 0, idx = 0;
|
||||
|
||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
|
||||
_e("Packet too small or truncated!");
|
||||
return;
|
||||
}
|
||||
|
||||
r = NLMSG_DATA(nlmsg);
|
||||
a = RTM_RTA(r);
|
||||
la = RTM_PAYLOAD(nlmsg);
|
||||
while (RTA_OK(a, la)) {
|
||||
void *data = RTA_DATA(a);
|
||||
switch (a->rta_type) {
|
||||
case RTA_GATEWAY:
|
||||
gw = *((int *)data);
|
||||
//_d("GW: 0x%04x", gw);
|
||||
break;
|
||||
|
||||
case RTA_DST:
|
||||
dst = *((int *)data);
|
||||
mask = r->rtm_dst_len;
|
||||
//_d("MASK: 0x%04x", mask);
|
||||
break;
|
||||
|
||||
case RTA_OIF:
|
||||
idx = *((int *)data);
|
||||
//_d("IDX: 0x%04x", idx);
|
||||
break;
|
||||
}
|
||||
|
||||
a = RTA_NEXT(a, la);
|
||||
}
|
||||
|
||||
if ((!dst && !mask) && (gw || idx)) {
|
||||
char msg[MAX_ARG_LEN];
|
||||
|
||||
if (nlmsg->nlmsg_type == RTM_DELROUTE)
|
||||
snprintf(msg, sizeof(msg), "GW:DN");
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "GW:UP");
|
||||
event_dispatch(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg)
|
||||
{
|
||||
int la;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
struct rtattr *a;
|
||||
struct ifinfomsg *i;
|
||||
|
||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
|
||||
_e("Packet too small or truncated!");
|
||||
return;
|
||||
}
|
||||
|
||||
i = NLMSG_DATA(nlmsg);
|
||||
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
|
||||
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
|
||||
|
||||
while (RTA_OK(a, la)) {
|
||||
if (a->rta_type == IFLA_IFNAME) {
|
||||
char msg[MAX_ARG_LEN];
|
||||
|
||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||
switch (nlmsg->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
/*
|
||||
* 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);
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
/* NOTE: Interface has dissapeared, not link down ... */
|
||||
snprintf(msg, sizeof(msg), "IFDEL:%s", ifname);
|
||||
event_dispatch(msg);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
_d("%s: New Address", ifname);
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
_d("%s: Deconfig Address", ifname);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
a = RTA_NEXT(a, la);
|
||||
}
|
||||
}
|
||||
|
||||
static void nl_callback(void *UNUSED(arg), int sd, int UNUSED(events))
|
||||
{
|
||||
ssize_t len;
|
||||
static char buf[4096];
|
||||
struct nlmsghdr *nh;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
len = recv(sd, buf, sizeof(buf), 0);
|
||||
if (len < 0) {
|
||||
if (errno != EINTR) /* Signal */
|
||||
_pe("recv()");
|
||||
return;
|
||||
}
|
||||
|
||||
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
|
||||
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
|
||||
nl_route(nh);
|
||||
else
|
||||
nl_link(nh);
|
||||
}
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.io = {
|
||||
.cb = nl_callback,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
int sd;
|
||||
struct sockaddr_nl sa;
|
||||
|
||||
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (sd < 0) {
|
||||
_pe("socket()");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.nl_family = AF_NETLINK;
|
||||
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; // | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR;
|
||||
sa.nl_pid = getpid();
|
||||
|
||||
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
_pe("bind()");
|
||||
close(sd);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.io.fd = sd;
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -21,9 +21,10 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
@@ -59,15 +60,17 @@ static void setup(void *UNUSED(arg))
|
||||
touch("/etc/resolvconf/run/enable-updates");
|
||||
|
||||
chdir("/etc/resolvconf/run/interface");
|
||||
run_parts("/etc/resolvconf/update.d", "-i", NULL);
|
||||
run_parts("/etc/resolvconf/update.d", "-i");
|
||||
chdir("/");
|
||||
#endif /* USE_ETC_RESOLVCONF_RUN */
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/* Optional inetd plugin for time (rdate) protocol RFC 868
|
||||
*
|
||||
* 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 <arpa/inet.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h> /* STDIN_FILENO */
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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. */
|
||||
|
||||
/* Account for UNIX epoch offset */
|
||||
now += EPOCH_OFFSET;
|
||||
|
||||
/* Convert to network byte order, not explicitly stated in RFC */
|
||||
now = htonl(now);
|
||||
|
||||
/* Send reply, ignore error, simply close connection. */
|
||||
return sendto(sd, &now, sizeof(now), MSG_DONTWAIT, (struct sockaddr *)&sa, sa_len);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = "time", /* Must match the inetd /etc/services entry */
|
||||
.inetd = {
|
||||
.cmd = rfc868
|
||||
}
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
|
||||
*
|
||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||
* Copyright (c) 2013 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 <errno.h>
|
||||
#include <fcntl.h> /* O_RDONLY et al */
|
||||
#include <unistd.h> /* read() */
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.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,
|
||||
},
|
||||
};
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
if (plugin.io.fd)
|
||||
close(plugin.io.fd);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static void watcher(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
{
|
||||
int len = 0;
|
||||
char buf[EVENT_SIZE], name[42];
|
||||
tty_node_t *entry;
|
||||
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||
|
||||
while ((len = read(fd, buf, sizeof(buf)))) {
|
||||
if (-1 == len) {
|
||||
if (errno == EINVAL)
|
||||
setup();
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
break; /* Likely EAGAIN */
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
||||
entry = tty_find(name);
|
||||
if (entry && tty_enabled(&entry->data, runlevel)) {
|
||||
if (notified->mask & IN_CREATE)
|
||||
tty_start(&entry->data);
|
||||
else if (entry->data.pid)
|
||||
tty_stop(&entry->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
setup();
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+46
-10
@@ -22,28 +22,64 @@
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h> /* gettimeofday() */
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "../config.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
copyfile(RANDOMSEED, "/dev/urandom", 0);
|
||||
unlink(RANDOMSEED);
|
||||
if (!fexist(RANDOMSEED)) {
|
||||
int ret = 1;
|
||||
FILE *fp;
|
||||
|
||||
print_desc("Bootstrapping random seed", NULL);
|
||||
umask(077);
|
||||
fp = fopen(RANDOMSEED, "w");
|
||||
if (fp) {
|
||||
int iter = 128;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srandom(tv.tv_sec % 3600);
|
||||
while (iter--) {
|
||||
uint32_t i, prng = random();
|
||||
|
||||
for (i = 0; i < sizeof(prng); i++)
|
||||
fputc((prng >> (i * CHAR_BIT)) & UCHAR_MAX, fp);
|
||||
}
|
||||
ret = fclose(fp);
|
||||
}
|
||||
print_result(ret);
|
||||
umask(0);
|
||||
}
|
||||
|
||||
print_desc("Initializing random number generator", NULL);
|
||||
print_result(512 != copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void save(void *UNUSED(arg))
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
umask(077);
|
||||
copyfile("/dev/urandom", RANDOMSEED, 4096);
|
||||
print_desc("Saving random seed", NULL);
|
||||
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
||||
umask(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = setup },
|
||||
.hook[HOOK_SHUTDOWN] = { .cb = save },
|
||||
.depends = { "bootmisc", }
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
@@ -25,9 +25,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "../finit.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "libite/lite.h"
|
||||
|
||||
static void setup(void *UNUSED(arg))
|
||||
{
|
||||
@@ -36,7 +37,7 @@ static void setup(void *UNUSED(arg))
|
||||
#endif
|
||||
char line[LINE_SIZE];
|
||||
|
||||
makepath("/var/run/console");
|
||||
makedir("/var/run/console", 01777);
|
||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
||||
touch(line);
|
||||
|
||||
@@ -48,14 +49,16 @@ static void setup(void *UNUSED(arg))
|
||||
}
|
||||
#endif
|
||||
|
||||
mkdir("/tmp/.X11-unix", 01777);
|
||||
mkdir("/tmp/.ICE-unix", 01777);
|
||||
makedir("/tmp/.X11-unix", 01777);
|
||||
makedir("/tmp/.ICE-unix", 01777);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_NETWORK_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", }
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Private header file for main finit daemon, not for plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2013 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
|
||||
@@ -28,12 +28,16 @@
|
||||
#include "svc.h"
|
||||
#include "plugin.h"
|
||||
|
||||
void svc_start_all (void);
|
||||
void svc_monitor (void);
|
||||
uev_ctx_t *ctx; /* Main loop context */
|
||||
|
||||
int api_init (uev_ctx_t *ctx);
|
||||
int client (int argc, char *argv[]);
|
||||
|
||||
void service_bootstrap(void);
|
||||
void service_monitor (pid_t lost);
|
||||
|
||||
void plugin_run_hooks (hook_point_t no);
|
||||
int plugin_load_all (char *path);
|
||||
void plugin_monitor (void);
|
||||
int plugin_load_all (uev_ctx_t *ctx, char *path);
|
||||
|
||||
#endif /* FINIT_PRIVATE_H_ */
|
||||
|
||||
|
||||
@@ -0,0 +1,648 @@
|
||||
/* $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_ */
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
initctl show
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $# -lt 2 -o $# -gt 2 ]; then
|
||||
echo "Usage: service [job# | name] <stop | start | restart | reload>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
initctl $2 $1
|
||||
|
||||
@@ -0,0 +1,980 @@
|
||||
/* Finit service monitor, task starter and generic API for managing svc_t
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net/if.h>
|
||||
#include "libite/lite.h"
|
||||
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "event.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "tty.h"
|
||||
#include "service.h"
|
||||
#include "inetd.h"
|
||||
|
||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||
|
||||
static int dyn_stop_cnt = 0;
|
||||
|
||||
static int is_norespawn (void);
|
||||
static void restart_lost_procs (void);
|
||||
static void svc_dance (svc_t *svc);
|
||||
#ifndef INETD_DISABLED
|
||||
static svc_t *find_inetd_svc (char *path, char *service, char *proto);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* service_bootstrap - Start bootstrap services and tasks
|
||||
*
|
||||
* System startup, runlevel S, where only services, tasks and
|
||||
* run commands absolutely essential to bootstrap are located.
|
||||
*/
|
||||
void service_bootstrap(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
svc_cmd_t cmd;
|
||||
|
||||
/* Inetd services cannot be part of bootstrap currently. */
|
||||
if (svc_is_inetd(svc))
|
||||
continue;
|
||||
|
||||
cmd = service_enabled(svc, 0, NULL);
|
||||
if (SVC_START == cmd || (SVC_RELOAD == cmd))
|
||||
service_start(svc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* service_enabled - Should the service run?
|
||||
* @svc: Pointer to &svc_t object
|
||||
* @event: Dynamic event, opaque flag passed to callback
|
||||
* @arg: Event argument, used only by external service plugins.
|
||||
*
|
||||
* This method calls an associated service callback, if registered by a
|
||||
* plugin, and returns the &svc_cmd_t status. If no plugin is registered
|
||||
* the service is statically enabled in /etc/finit.conf and the result
|
||||
* will always be %SVC_START.
|
||||
*
|
||||
* Returns:
|
||||
* Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
|
||||
*/
|
||||
svc_cmd_t service_enabled(svc_t *svc, int event, void *arg)
|
||||
{
|
||||
svc_cmd_t cmd = SVC_START; /* Default to start, since listed in finit.conf */
|
||||
|
||||
if (!svc) {
|
||||
errno = EINVAL;
|
||||
return SVC_STOP;
|
||||
}
|
||||
|
||||
if (!svc_in_runlevel(svc, runlevel))
|
||||
return SVC_STOP;
|
||||
|
||||
/*
|
||||
* Event conditions for services are ignored during bootstrap.
|
||||
*/
|
||||
_d("Checking %s runlevel %d and events %s", svc->cmd, runlevel, svc->events);
|
||||
if (runlevel && !event_service_cond(svc->events))
|
||||
return SVC_STOP;
|
||||
|
||||
if (svc->state == SVC_RELOAD_STATE)
|
||||
cmd = SVC_RELOAD;
|
||||
if (svc->state == SVC_PAUSED_STATE)
|
||||
cmd = SVC_STOP;
|
||||
|
||||
/* Is there a service plugin registered? */
|
||||
if (svc->cb) {
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
/* Let callback run in separate process so it doesn't crash PID 1 */
|
||||
pid = fork();
|
||||
if (-1 == pid) {
|
||||
_pe("Failed in %s callback", svc->cmd);
|
||||
return SVC_STOP;
|
||||
}
|
||||
|
||||
if (!pid)
|
||||
_exit(svc->cb(svc, event, arg));
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
_pe("Failed reading status from %s callback", svc->cmd);
|
||||
return SVC_STOP;
|
||||
}
|
||||
|
||||
/* Callback normally exits here. */
|
||||
if (WIFEXITED(status)) {
|
||||
svc_cmd_t tmp = WEXITSTATUS(status);
|
||||
return tmp == SVC_START ? cmd : tmp;
|
||||
}
|
||||
|
||||
/* Check for SEGFAULT or other error ... */
|
||||
if (WIFSIGNALED(status) && WCOREDUMP(status))
|
||||
_e("Callback to %s crashed!\n", svc->cmd);
|
||||
else
|
||||
_e("Callback to %s did not exit normally!\n", svc->cmd);
|
||||
|
||||
return SVC_STOP;
|
||||
}
|
||||
|
||||
_d("%s => %s", svc->cmd, (cmd == SVC_START
|
||||
? "SVC_START"
|
||||
: (cmd == SVC_RELOAD
|
||||
? "SVC_RELOAD"
|
||||
: "SVC_STOP")));
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/* Remember: service_enabled() must be called before calling service_start() */
|
||||
int service_start(svc_t *svc)
|
||||
{
|
||||
int respawn, sd = 0;
|
||||
pid_t pid;
|
||||
sigset_t nmask, omask;
|
||||
|
||||
if (!svc)
|
||||
return 1;
|
||||
respawn = svc->pid != 0;
|
||||
|
||||
/* Don't try and start service if it doesn't exist. */
|
||||
if (!fexist(svc->cmd) && !svc->inetd.cmd) {
|
||||
if (verbose) {
|
||||
char msg[80];
|
||||
|
||||
snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd);
|
||||
print_desc("", msg);
|
||||
print_result(1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Ignore if finit is SIGSTOP'ed */
|
||||
if (is_norespawn())
|
||||
return 0;
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
if (svc_is_inetd(svc)) {
|
||||
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
||||
|
||||
sd = svc->inetd.watcher.fd;
|
||||
|
||||
if (svc->inetd.type == SOCK_STREAM) {
|
||||
/* Open new client socket from server socket */
|
||||
sd = accept(sd, NULL, NULL);
|
||||
if (sd < 0) {
|
||||
FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
_d("New client socket %d accepted for inetd service %d/tcp", sd, svc->inetd.port);
|
||||
|
||||
/* Find ifname by means of getsockname() and getifaddrs() */
|
||||
inetd_stream_peek(sd, ifname);
|
||||
} else { /* SOCK_DGRAM */
|
||||
/* Find ifname by means of IP_PKTINFO sockopt --> ifindex + if_indextoname() */
|
||||
inetd_dgram_peek(sd, ifname);
|
||||
}
|
||||
|
||||
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
||||
FLOG_INFO("Service %s on port %d not allowed from interface %s.",
|
||||
svc->inetd.name, svc->inetd.port, ifname);
|
||||
if (svc->inetd.type == SOCK_STREAM)
|
||||
close(sd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
FLOG_INFO("Starting inetd service %s for requst from iface %s ...", svc->inetd.name, ifname);
|
||||
} else
|
||||
#endif
|
||||
if (verbose) {
|
||||
if (svc_is_daemon(svc))
|
||||
print_desc("", svc->desc);
|
||||
else if (!respawn)
|
||||
print_desc("Starting ", svc->desc);
|
||||
}
|
||||
|
||||
/* Block sigchild while forking. */
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
|
||||
pid = fork();
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
if (pid == 0) {
|
||||
int i = 0;
|
||||
int status;
|
||||
#ifdef ENABLE_STATIC
|
||||
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
|
||||
#else
|
||||
int uid = getuser(svc->username);
|
||||
#endif
|
||||
struct sigaction sa;
|
||||
char *args[MAX_NUM_SVC_ARGS];
|
||||
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
|
||||
/* Set desired user */
|
||||
if (uid >= 0) {
|
||||
setuid(uid);
|
||||
|
||||
/* Set default path for regular users */
|
||||
if (uid > 0)
|
||||
setenv("PATH", _PATH_DEFPATH, 1);
|
||||
}
|
||||
|
||||
/* Serve copy of args to process in case it modifies them. */
|
||||
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
|
||||
args[i] = svc->args[i];
|
||||
args[i] = NULL;
|
||||
|
||||
/* Redirect inetd socket to stdin for service */
|
||||
if (svc_is_inetd(svc)) {
|
||||
/* sd set previously */
|
||||
dup2(sd, STDIN_FILENO);
|
||||
close(sd);
|
||||
dup2(STDIN_FILENO, STDOUT_FILENO);
|
||||
dup2(STDIN_FILENO, STDERR_FILENO);
|
||||
} else if (debug) {
|
||||
int fd;
|
||||
char buf[CMD_SIZE] = "";
|
||||
|
||||
fd = open(CONSOLE, O_WRONLY | O_APPEND);
|
||||
if (-1 != fd) {
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_NUM_SVC_ARGS && args[i]; i++) {
|
||||
char arg[MAX_ARG_LEN];
|
||||
|
||||
snprintf(arg, sizeof(arg), "%s ", args[i]);
|
||||
if (strlen(arg) < (sizeof(buf) - strlen(buf)))
|
||||
strcat(buf, arg);
|
||||
}
|
||||
_e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf);
|
||||
}
|
||||
|
||||
sig_unblock();
|
||||
|
||||
if (svc->inetd.cmd)
|
||||
status = svc->inetd.cmd(svc->inetd.type);
|
||||
else
|
||||
status = execv(svc->cmd, args); /* XXX: Maybe use execve() to be able to launch scripts? */
|
||||
|
||||
if (svc_is_inetd(svc)) {
|
||||
if (svc->inetd.type == SOCK_STREAM) {
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
}
|
||||
}
|
||||
|
||||
exit(status);
|
||||
}
|
||||
svc->pid = pid;
|
||||
svc->state = SVC_RUNNING_STATE;
|
||||
|
||||
if (svc_is_inetd(svc)) {
|
||||
if (svc->inetd.type == SOCK_STREAM)
|
||||
close(sd);
|
||||
} else {
|
||||
int result;
|
||||
|
||||
if (SVC_TYPE_RUN == svc->type)
|
||||
result = WEXITSTATUS(complete(svc->cmd, pid));
|
||||
else if (!respawn)
|
||||
result = svc->pid > 1 ? 0 : 1;
|
||||
else
|
||||
result = 0;
|
||||
|
||||
if (verbose)
|
||||
print_result(result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int service_stop(svc_t *svc, int state)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
if (svc->pid <= 1) {
|
||||
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
|
||||
res = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (SVC_TYPE_SERVICE != svc->type)
|
||||
return 0;
|
||||
|
||||
_d("Service %s state %d, new state %d, sighup %d", svc->cmd, svc->state, state, svc->sighup);
|
||||
if (state == SVC_RELOAD_STATE && svc->sighup)
|
||||
goto exit;
|
||||
|
||||
if (runlevel != 1 && verbose)
|
||||
print_desc("Stopping ", svc->desc);
|
||||
|
||||
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||
res = kill(svc->pid, SIGTERM);
|
||||
|
||||
if (runlevel != 1 && verbose)
|
||||
print_result(res);
|
||||
exit:
|
||||
if (!res)
|
||||
svc->state = state;
|
||||
svc->restart_counter = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_start_dynamic - Start new or reload modified dynamic services
|
||||
*/
|
||||
void service_start_dynamic(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
_d("Starting enabled/added services ...");
|
||||
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0)) {
|
||||
if (svc_is_updated(svc))
|
||||
svc_dance(svc);
|
||||
}
|
||||
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unless there are services we must collect first (wait for
|
||||
* them to stop), we can call HOOK_SVC_RECONF here.
|
||||
*/
|
||||
static int service_stop_done(svc_t *svc)
|
||||
{
|
||||
if (svc && !svc_is_changed(svc))
|
||||
return 0;
|
||||
|
||||
if (svc && dyn_stop_cnt)
|
||||
dyn_stop_cnt--;
|
||||
|
||||
_d("dyn_stop_cnt %d", dyn_stop_cnt);
|
||||
if (!dyn_stop_cnt) {
|
||||
_d("All disabled/removed services have been stoppped, calling reconf hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_RECONF); /* Reconfigure HW/VLANs/etc here */
|
||||
|
||||
/* Finish off by starting/reload modified/new services */
|
||||
service_start_dynamic();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_stop_dynamic - Stop disabled/removed dynamic services
|
||||
*
|
||||
* We call it "stop", but in reality it could be "skip" as well, if the
|
||||
* service supports SIGHUP. This function is just one step on the road
|
||||
* to reload all modified services.
|
||||
*/
|
||||
void service_stop_dynamic(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
_d("Stopping disabled/removed services ...");
|
||||
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0)) {
|
||||
if (svc_is_changed(svc) && svc->pid) {
|
||||
svc_state_t new_state = SVC_RELOAD_STATE;
|
||||
|
||||
if (svc_is_removed(svc))
|
||||
new_state = SVC_HALTED_STATE;
|
||||
|
||||
if (!svc_has_sighup(svc))
|
||||
dyn_stop_cnt++;
|
||||
|
||||
_d("Marking service %s as state %d", svc->cmd, new_state);
|
||||
service_stop(svc, new_state);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we need to collect any services before calling user HOOK */
|
||||
service_stop_done(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* service_restart - Restart or SIGHUP a process
|
||||
*
|
||||
* Unfortunately we cannot call service_start() here because we must
|
||||
* wait for the svc to actually be collected by the service_monitor()
|
||||
* first. Currently this means that an initctl user will not get
|
||||
* correct feedback when restarting a service. (Prompt may return
|
||||
* before we've actually started upp the service again.)
|
||||
*
|
||||
* XXX: This should be refactored to let initctl check the sighup
|
||||
* flag and then to stop+start or reload.
|
||||
*/
|
||||
int service_restart(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
/* Ignore if finit is SIGSTOP'ed */
|
||||
if (is_norespawn())
|
||||
return 0;
|
||||
|
||||
svc->restart_counter = 0;
|
||||
|
||||
return service_stop(svc, SVC_RESTART_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* service_reload - Send SIGHUP to a service
|
||||
* @svc: Service to reload
|
||||
*
|
||||
* This function does some basic checks of the runtime state of Finit
|
||||
* and a sanity check of the @svc before sending %SIGHUP.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) or non-zero on error.
|
||||
*/
|
||||
int service_reload(svc_t *svc)
|
||||
{
|
||||
/* Ignore if finit is SIGSTOP'ed */
|
||||
if (is_norespawn())
|
||||
return 0;
|
||||
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
if (svc->pid <= 1) {
|
||||
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
|
||||
svc->pid = 0;
|
||||
return 1;
|
||||
}
|
||||
svc->state = SVC_RUNNING_STATE;
|
||||
|
||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||
return kill(svc->pid, SIGHUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* service_reload_dynamic - Called on SIGHUP, 'init q' or 'initctl reload'
|
||||
*
|
||||
* This function is called when Finit has recieved SIGHUP to reload
|
||||
* .conf files in /etc/finit.d. It is responsible for starting,
|
||||
* stopping and reloading (forwarding SIGHUP) to processes affected.
|
||||
*/
|
||||
void service_reload_dynamic(void)
|
||||
{
|
||||
/* First reload all *.conf in /etc/finit.d/ */
|
||||
conf_reload_dynamic();
|
||||
|
||||
/* Then stop any disabled/removed services and non-reloadable */
|
||||
service_stop_dynamic();
|
||||
|
||||
/*
|
||||
* Finish off by starting/reloading modified/new services.
|
||||
* Postponed to service_stop_done() to make sure all services
|
||||
* are guaranteed to have been stopped before being started
|
||||
* again.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* service_runlevel - Change to a new runlevel
|
||||
* @newlevel: New runlevel to activate
|
||||
*
|
||||
* Stops all services not in @newlevel and starts, or lets continue to run,
|
||||
* those in @newlevel. Also updates @prevlevel and active @runlevel.
|
||||
*/
|
||||
void service_runlevel(int newlevel)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (runlevel == newlevel)
|
||||
return;
|
||||
|
||||
if (newlevel < 0 || newlevel > 9)
|
||||
return;
|
||||
|
||||
prevlevel = runlevel;
|
||||
runlevel = newlevel;
|
||||
|
||||
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
|
||||
runlevel_set(prevlevel, newlevel);
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
conf_reload_dynamic();
|
||||
|
||||
_d("Stopping services services not allowed in new runlevel ...");
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!svc_in_runlevel(svc, runlevel)) {
|
||||
#ifndef INETD_DISABLED
|
||||
if (svc_is_inetd(svc))
|
||||
inetd_stop(&svc->inetd);
|
||||
else
|
||||
#endif
|
||||
service_stop(svc, SVC_HALTED_STATE);
|
||||
}
|
||||
|
||||
/* ... or disabled/removed services from /etc/finit.d/ */
|
||||
if (svc_is_dynamic(svc) && svc_is_changed(svc))
|
||||
service_stop(svc, SVC_HALTED_STATE);
|
||||
}
|
||||
|
||||
/* 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 ...");
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
#ifndef INETD_DISABLED
|
||||
/* Inetd services have slightly different semantics */
|
||||
if (svc_is_inetd(svc)) {
|
||||
if (svc_in_runlevel(svc, runlevel))
|
||||
inetd_start(&svc->inetd);
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* All other services consult their callback here */
|
||||
svc_dance(svc);
|
||||
}
|
||||
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
|
||||
if (0 == runlevel) {
|
||||
do_shutdown(SIGUSR2);
|
||||
return;
|
||||
}
|
||||
if (6 == runlevel) {
|
||||
do_shutdown(SIGUSR1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runlevel == 1)
|
||||
touch("/etc/nologin"); /* Disable login in single-user mode */
|
||||
else
|
||||
erase("/etc/nologin");
|
||||
|
||||
/* No TTYs run at bootstrap, they have a delayed start. */
|
||||
if (prevlevel > 0)
|
||||
tty_runlevel(runlevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* service_register - Register service, task or run commands
|
||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||
* @line: A complete command line with -- separated description text
|
||||
* @mtime: The modification time if service is loaded from /etc/finit.d
|
||||
* @username: Optional username to run service as, or %NULL to run as root
|
||||
*
|
||||
* This function is used to register commands to be run on different
|
||||
* system runlevels with optional username. The @type argument details
|
||||
* if it's service to bo monitored/respawned (daemon), a one-shot task
|
||||
* or a command that must run in sequence and not in parallell, like
|
||||
* service and task commands do.
|
||||
*
|
||||
* The @line can optionally start with a username, denoted by an @
|
||||
* character. Like this:
|
||||
*
|
||||
* service @username [!0-6,S] <!EV> /path/to/daemon arg -- Description
|
||||
* task @username [!0-6,S] /path/to/task arg -- Description
|
||||
* run @username [!0-6,S] /path/to/cmd arg -- Description
|
||||
* inetd tcp/ssh nowait [2345] @root:root /sbin/sshd -i -- Description
|
||||
*
|
||||
* If the username is left out the command is started as root. The []
|
||||
* brackets denote the allowed runlevels, if left out the default for a
|
||||
* service is set to [2-5]. Allowed runlevels mimic that of SysV init
|
||||
* with the addition of the 'S' runlevel, which is only run once at
|
||||
* startup. It can be seen as the system bootstrap. If a task or run
|
||||
* command is listed in more than the [S] runlevel they will be called
|
||||
* when changing runlevel.
|
||||
*
|
||||
* Services (daemons, not inetd services) also support an optional <!EV>
|
||||
* argument. This is for services that, e.g., require a system gateway
|
||||
* or interface to be up before they are started. Or restarted, or even
|
||||
* SIGHUP'ed, when the gateway changes or interfaces come and go. The
|
||||
* special case when a service is declared with <!> means it does not
|
||||
* support SIGHUP but must be STOP/START'ed at system reconfiguration.
|
||||
*
|
||||
* Supported service events are: GW, IFUP[:ifname], IFDN[:ifname], where
|
||||
* the interface name (:ifname) is optional. Actully, the check with a
|
||||
* service event declaration is string based, so 'IFUP:ppp' will match
|
||||
* any of "IFUP:ppp0" or "IFUP:pppoe1" sent by the netlink.so plugin.
|
||||
*
|
||||
* For multiple instances of the same command, e.g. multiple DHCP
|
||||
* clients, the user must enter an ID, using the :ID syntax.
|
||||
*
|
||||
* service :1 /sbin/udhcpc -i eth1
|
||||
* service :2 /sbin/udhcpc -i eth2
|
||||
*
|
||||
* Without the :ID syntax Finit will overwrite the first service line
|
||||
* with the contents of the second. The :ID must be [1,MAXINT].
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||
*/
|
||||
int service_register(int type, char *line, time_t mtime, char *username)
|
||||
{
|
||||
int i = 0;
|
||||
int id = 1; /* Default to ID:1 */
|
||||
#ifndef INETD_DISABLED
|
||||
int forking = 0;
|
||||
#endif
|
||||
char *service = NULL, *proto = NULL, *ifaces = NULL;
|
||||
char *cmd, *desc, *runlevels = NULL, *events = NULL;
|
||||
svc_t *svc;
|
||||
plugin_t *plugin = NULL;
|
||||
|
||||
if (!line) {
|
||||
_e("Invalid input argument.");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
desc = strstr(line, "-- ");
|
||||
if (desc)
|
||||
*desc = 0;
|
||||
|
||||
cmd = strtok(line, " ");
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
_e("Incomplete service, cannot register.");
|
||||
return errno = ENOENT;
|
||||
}
|
||||
|
||||
while (cmd) {
|
||||
if (cmd[0] != '/' && strchr(cmd, '/'))
|
||||
service = cmd; /* inetd service/proto */
|
||||
#ifndef INETD_DISABLED
|
||||
else if (!strncasecmp(cmd, "nowait", 6))
|
||||
forking = 1;
|
||||
else if (!strncasecmp(cmd, "wait", 4))
|
||||
forking = 0;
|
||||
#endif
|
||||
else if (cmd[0] == '@') /* @username[:group] */
|
||||
username = &cmd[1];
|
||||
else if (cmd[0] == '[') /* [runlevels] */
|
||||
runlevels = &cmd[0];
|
||||
else if (cmd[0] == '<') /* [!ev] */
|
||||
events = &cmd[1];
|
||||
else if (cmd[0] == ':') /* :ID */
|
||||
id = atoi(&cmd[1]);
|
||||
else
|
||||
break;
|
||||
|
||||
/* Check if valid command follows... */
|
||||
cmd = strtok(NULL, " ");
|
||||
if (!cmd)
|
||||
goto incomplete;
|
||||
}
|
||||
|
||||
/* Example: inetd ssh/tcp@eth0,eth1 or 222/tcp@eth2 */
|
||||
if (service) {
|
||||
ifaces = strchr(service, '@');
|
||||
if (ifaces)
|
||||
*ifaces++ = 0;
|
||||
|
||||
proto = strchr(service, '/');
|
||||
if (!proto)
|
||||
goto incomplete;
|
||||
*proto++ = 0;
|
||||
}
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
/* Find plugin that provides a callback for this inetd service */
|
||||
if (type == SVC_TYPE_INETD) {
|
||||
if (!strncasecmp(cmd, "internal", 8)) {
|
||||
char *ptr, *ps = service;
|
||||
|
||||
/* internal.service */
|
||||
ptr = strchr(cmd, '.');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
ps = ptr;
|
||||
}
|
||||
|
||||
plugin = plugin_find(ps);
|
||||
if (!plugin || !plugin->inetd.cmd) {
|
||||
_e("Inetd service %s has no internal plugin, skipping.", service);
|
||||
return errno = ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if known inetd, then add ifnames for filtering only. */
|
||||
svc = find_inetd_svc(cmd, service, proto);
|
||||
if (svc)
|
||||
goto inetd_setup;
|
||||
|
||||
id = svc_next_id(cmd);
|
||||
}
|
||||
#endif
|
||||
|
||||
svc = svc_find(cmd, id);
|
||||
if (!svc) {
|
||||
_d("Creating new svc for %s id #%d type %d", cmd, id, type);
|
||||
svc = svc_new(cmd, id, type);
|
||||
if (!svc) {
|
||||
_e("Out of memory, cannot register service %s", cmd);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* New, recently modified or unchanged ... used on reload. */
|
||||
svc_check_dirty(svc, mtime);
|
||||
|
||||
if (desc)
|
||||
strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
|
||||
|
||||
if (username) {
|
||||
char *ptr = strchr(username, ':');
|
||||
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
strlcpy(svc->group, ptr, sizeof(svc->group));
|
||||
}
|
||||
strlcpy(svc->username, username, sizeof(svc->username));
|
||||
}
|
||||
|
||||
if (plugin) {
|
||||
/* Internal plugin provides this service */
|
||||
svc->inetd.cmd = plugin->inetd.cmd;
|
||||
} else {
|
||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||
while ((cmd = strtok(NULL, " ")))
|
||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||
svc->args[i][0] = 0;
|
||||
|
||||
plugin = plugin_find(svc->cmd);
|
||||
if (plugin && plugin->svc.cb) {
|
||||
svc->cb = plugin->svc.cb;
|
||||
svc->dynamic = plugin->svc.dynamic;
|
||||
svc->dynamic_stop = plugin->svc.dynamic_stop;
|
||||
}
|
||||
}
|
||||
|
||||
svc->runlevels = conf_parse_runlevels(runlevels);
|
||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||
|
||||
if (type == SVC_TYPE_SERVICE)
|
||||
conf_parse_events(svc, events);
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
if (svc_is_inetd(svc)) {
|
||||
char *iface, *name = service;
|
||||
|
||||
svc->state = SVC_WAITING_STATE;
|
||||
if (svc->inetd.cmd && plugin)
|
||||
name = plugin->name;
|
||||
|
||||
if (inetd_new(&svc->inetd, name, service, proto, forking, svc)) {
|
||||
_e("Failed registering new inetd service %s.", service);
|
||||
inetd_del(&svc->inetd);
|
||||
return svc_del(svc);
|
||||
}
|
||||
|
||||
inetd_setup:
|
||||
if (!ifaces) {
|
||||
_d("No specific iface listed for %s, allowing ANY.", service);
|
||||
return inetd_allow(&svc->inetd, NULL);
|
||||
}
|
||||
|
||||
for (iface = strtok(ifaces, ","); iface; iface = strtok(NULL, ",")) {
|
||||
if (iface[0] == '!')
|
||||
inetd_deny(&svc->inetd, &iface[1]);
|
||||
else
|
||||
inetd_allow(&svc->inetd, iface);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void service_unregister(svc_t *svc)
|
||||
{
|
||||
if (svc->state != SVC_HALTED_STATE)
|
||||
_e("Failed stopping %s, removing anyway from list of monitored services.", svc->cmd);
|
||||
svc_del(svc);
|
||||
}
|
||||
|
||||
void service_monitor(pid_t lost)
|
||||
{
|
||||
svc_t *svc;
|
||||
static int was_stopped = 0;
|
||||
|
||||
if (was_stopped && !is_norespawn()) {
|
||||
was_stopped = 0;
|
||||
restart_lost_procs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||
return;
|
||||
|
||||
/* Power user at the console, don't respawn tasks. */
|
||||
if (is_norespawn()) {
|
||||
was_stopped = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tty_respawn(lost))
|
||||
return;
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
if (inetd_respawn(lost))
|
||||
return;
|
||||
#endif
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (lost != svc->pid)
|
||||
continue;
|
||||
|
||||
if (!prevlevel && svc_clean_bootstrap(svc))
|
||||
continue;
|
||||
|
||||
if (SVC_TYPE_SERVICE != svc->type) {
|
||||
svc->pid = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
_d("Ouch, lost pid %d - %s(%d)", lost, basename(svc->cmd), svc->pid);
|
||||
|
||||
/* No longer running, update books. */
|
||||
svc->pid = 0;
|
||||
|
||||
/* Check if we're still collecting stopped dynamic services */
|
||||
if (service_stop_done(svc))
|
||||
break;
|
||||
|
||||
if (sig_stopped()) {
|
||||
_e("Stopped, not respawning killed processes.");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Restarting lost service. */
|
||||
if (service_enabled(svc, 0, NULL)) {
|
||||
if (svc->restart_counter > RESPAWN_MAX) {
|
||||
_e("Not restarting %s id %d, respawn MAX (%d) reached!",
|
||||
svc->cmd, svc->id, RESPAWN_MAX);
|
||||
break;
|
||||
}
|
||||
|
||||
svc->restart_counter++;
|
||||
service_start(svc);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_norespawn(void)
|
||||
{
|
||||
return sig_stopped() ||
|
||||
fexist("/mnt/norespawn") ||
|
||||
fexist("/tmp/norespawn");
|
||||
}
|
||||
|
||||
static void restart_lost_procs(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->pid > 0 && pid_alive(svc->pid))
|
||||
continue;
|
||||
|
||||
/* Only restart lost daemons, not task/run/inetd services */
|
||||
if (SVC_TYPE_SERVICE != svc->type) {
|
||||
svc->pid = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
service_start(svc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Singing and dancing ... */
|
||||
static void svc_dance(svc_t *svc)
|
||||
{
|
||||
svc_cmd_t cmd = service_enabled(svc, 0, NULL);
|
||||
|
||||
if (svc->pid) {
|
||||
if (SVC_STOP == cmd)
|
||||
service_stop(svc, SVC_HALTED_STATE);
|
||||
else if (SVC_RELOAD == cmd)
|
||||
service_reload(svc);
|
||||
} else {
|
||||
if (SVC_START == cmd || SVC_RELOAD == cmd)
|
||||
service_start(svc);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef INETD_DISABLED
|
||||
static svc_t *find_inetd_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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+18
-24
@@ -1,6 +1,7 @@
|
||||
/* File mode bit operations
|
||||
/* Finit service monitor, task starter and generic API for managing svc_t
|
||||
*
|
||||
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
|
||||
* 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
|
||||
@@ -21,32 +22,25 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#ifndef FINIT_SERVICE_H_
|
||||
#define FINIT_SERVICE_H_
|
||||
|
||||
/**
|
||||
* fmode - Return a file's mode bits.
|
||||
* @file: File to look for, with full path.
|
||||
*
|
||||
* Returns:
|
||||
* The mode bits or zero when @file does not exist and errno set to %ENOENT.
|
||||
*/
|
||||
mode_t fmode(char *file)
|
||||
{
|
||||
struct stat sb;
|
||||
#include "svc.h"
|
||||
|
||||
if (!file) {
|
||||
errno = EINVAL;
|
||||
return 0; /* Doesn't exist ... */
|
||||
}
|
||||
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);
|
||||
|
||||
if (!stat(file, &sb))
|
||||
return sb.st_mode;
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* FINIT_SERVICE_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Signal management - Be conservative with what finit responds to!
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2014 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
|
||||
@@ -38,31 +38,52 @@
|
||||
* It activates the kbrequest action.
|
||||
*/
|
||||
|
||||
#include <sched.h>
|
||||
#include <string.h> /* strerror() */
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/wait.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"
|
||||
|
||||
static int stopped = 0;
|
||||
static int stopped = 0;
|
||||
static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
|
||||
static uev_t sigchld_watcher, sigsegv_watcher;
|
||||
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
|
||||
|
||||
|
||||
void do_shutdown (int sig)
|
||||
void do_shutdown(int sig)
|
||||
{
|
||||
touch(SYNC_SHUTDOWN);
|
||||
|
||||
if (sdown)
|
||||
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
|
||||
|
||||
/* If we enabled terse mode at boot, restore to previous setting at shutdown */
|
||||
if (quiet) {
|
||||
verbose = VERBOSE_MODE;
|
||||
if (verbose) {
|
||||
sched_yield();
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Call all shutdown hooks before rebooting... */
|
||||
plugin_run_hooks(HOOK_SHUTDOWN);
|
||||
|
||||
/* Here is where we signal watchdogd to do a forced reset for us */
|
||||
_d("Sending SIGTERM to all processes.");
|
||||
kill(-1, SIGTERM);
|
||||
|
||||
sleep(1);
|
||||
/* Call all shutdown hooks, this is the last call before rebooting... */
|
||||
plugin_run_hooks(HOOK_SHUTDOWN);
|
||||
/* Wait for WDT to timeout, should be no more than ~1 sec. */
|
||||
do_sleep(2);
|
||||
|
||||
_d("Sending SIGKILL to remaining processes.");
|
||||
kill(-1, SIGKILL);
|
||||
@@ -82,40 +103,64 @@ void do_shutdown (int sig)
|
||||
}
|
||||
|
||||
/*
|
||||
* Shut down on INT USR1 USR2
|
||||
* Reload .conf files in /etc/finit.d/
|
||||
*/
|
||||
static void shutdown_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
||||
static void sighup_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("Rebooting on signal %d from %s (PID %d)",
|
||||
sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
|
||||
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
do_shutdown(sig);
|
||||
/*
|
||||
* Shut down on INT PWR
|
||||
*/
|
||||
static void sigint_cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
do_shutdown(w->signo);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGCHLD: one of our children has died
|
||||
*/
|
||||
static void chld_handler(int UNUSED(sig), siginfo_t *UNUSED(info), void *UNUSED(ctx))
|
||||
static void sigchld_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
/* Do nothing, the svc_monitor() is the designated child reaper. */
|
||||
pid_t pid;
|
||||
|
||||
/* Reap all the children! */
|
||||
do {
|
||||
pid = waitpid(-1, NULL, WNOHANG);
|
||||
if (pid > 0) {
|
||||
_d("Collected child %d", pid);
|
||||
service_monitor(pid);
|
||||
}
|
||||
} while (pid > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGSTOP: Paused by user or netflash
|
||||
* SIGSEGV: mostly if service callbacks segfault
|
||||
*/
|
||||
static void sigstop_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
||||
static void sigsegv_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("Received SIGSTOP(%d) from %s (PID %d)",
|
||||
sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
|
||||
touch(SYNC_STOPPED);
|
||||
stopped ++;
|
||||
_e("PID %d caused a segfault!\n", getpid());
|
||||
exit(-1);
|
||||
}
|
||||
static void sigcont_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
||||
|
||||
/*
|
||||
* SIGSTOP/SIGTSTP: Paused by user or netflash
|
||||
*/
|
||||
static void sigstop_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
touch(SYNC_STOPPED);
|
||||
stopped++;
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGCONT: Restart service monitor
|
||||
*/
|
||||
static void sigcont_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("Received SIGCONT(%d) from %s (PID %d)",
|
||||
sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
|
||||
stopped = 0;
|
||||
remove(SYNC_STOPPED);
|
||||
erase(SYNC_STOPPED);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -129,6 +174,11 @@ int sig_stopped(void)
|
||||
/*
|
||||
* Inital signal setup - ignore everything but SIGCHLD until we're capable of responding
|
||||
*/
|
||||
static void chld_handler(int UNUSED(sig), siginfo_t *UNUSED(info), void *UNUSED(ctx))
|
||||
{
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
void sig_init(void)
|
||||
{
|
||||
int i;
|
||||
@@ -140,26 +190,50 @@ void sig_init(void)
|
||||
SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unblock all signals blocked by finit when starting children
|
||||
*/
|
||||
void sig_unblock(void)
|
||||
{
|
||||
int i;
|
||||
sigset_t nmask;
|
||||
struct sigaction sa;
|
||||
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGHUP);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigaddset(&nmask, SIGINT);
|
||||
sigaddset(&nmask, SIGPWR);
|
||||
sigaddset(&nmask, SIGSTOP);
|
||||
sigaddset(&nmask, SIGTSTP);
|
||||
sigaddset(&nmask, SIGCONT);
|
||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup limited set of SysV compatible signals to respond to
|
||||
*/
|
||||
void sig_setup(void)
|
||||
void sig_setup(uev_ctx_t *ctx)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
_d("Setup signals");
|
||||
|
||||
/* Cleanup any stale finit control files */
|
||||
remove(SYNC_SHUTDOWN);
|
||||
remove(SYNC_STOPPED);
|
||||
erase(SYNC_SHUTDOWN);
|
||||
erase(SYNC_STOPPED);
|
||||
|
||||
/* Standard SysV init calls ctrl-alt-delete handler */
|
||||
SETSIG(sa, SIGINT, shutdown_handler, 0);
|
||||
SETSIG(sa, SIGPWR, shutdown_handler, 0);
|
||||
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. */
|
||||
// SETSIG2(sa, SIGUSR1, reopen_initctl, 0);
|
||||
// SETSIG2(sa, SIGUSR2, pwrdwn_handler, 0);
|
||||
// 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);
|
||||
@@ -167,13 +241,19 @@ void sig_setup(void)
|
||||
/* Some C APIs may need SIGALRM for implementing timers. */
|
||||
IGNSIG(sa, SIGALRM, 0);
|
||||
|
||||
/* We don't have any /etc/inittab yet, reread finit.conf? */
|
||||
IGNSIG(sa, SIGHUP, 0);
|
||||
/* /etc/inittab not supported yet, instead /etc/finit.d/ is scanned for *.conf */
|
||||
uev_signal_init(ctx, &sighup_watcher, sighup_cb, NULL, SIGHUP);
|
||||
|
||||
/* 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. */
|
||||
SETSIG(sa, SIGSTOP, sigstop_handler, 0);
|
||||
SETSIG(sa, SIGTSTP, sigstop_handler, 0);
|
||||
SETSIG(sa, SIGCONT, sigcont_handler, 0);
|
||||
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);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Signal management - Be conservative with what finit responds to!
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-2014 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
|
||||
@@ -26,6 +26,7 @@
|
||||
#define FINIT_SIG_H_
|
||||
|
||||
#include <signal.h>
|
||||
#include "libuev/uev.h"
|
||||
|
||||
#define SYNC_SHUTDOWN "/var/lock/finit.shutdown"
|
||||
#define SYNC_STOPPED "/var/lock/finit.stopped"
|
||||
@@ -57,7 +58,8 @@
|
||||
void do_shutdown (int sig);
|
||||
int sig_stopped (void);
|
||||
void sig_init (void);
|
||||
void sig_setup (void);
|
||||
void sig_unblock (void);
|
||||
void sig_setup (uev_ctx_t *ctx);
|
||||
|
||||
#endif /* FINIT_SIG_H_ */
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Copy src to string dst of size siz. At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0) {
|
||||
while (--n != 0) {
|
||||
if ((*d++ = *s++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0) {
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
return(s - src - 1); /* count does not include NUL */
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* c-file-style: "bsd"
|
||||
* End:
|
||||
*/
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Finit service monitor and heneric API for managing svc_t structures
|
||||
/* Low-level service primitives and generic API for managing svc_t structures
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -22,76 +22,248 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
#include "libite/lite.h"
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "lite.h"
|
||||
#include "svc.h"
|
||||
#include "helpers.h"
|
||||
|
||||
/* The registered services are kept in shared memory for easy read-only access
|
||||
* by 3rd party APIs. Mostly because of the ability to, from the outside, query
|
||||
* liveness state of all daemons that should run. */
|
||||
static int svc_counter = 0; /* Number of registered services to monitor. */
|
||||
static svc_t *services = NULL; /* List of registered services, in shared memory. */
|
||||
/* Each svc_t needs a unique job# */
|
||||
static int jobcounter = 1;
|
||||
|
||||
|
||||
/* Connect to/create shared memory area of registered servies */
|
||||
static void __connect_shm(void)
|
||||
static svc_t *__connect_shm(void)
|
||||
{
|
||||
if (!services) {
|
||||
services = finit_svc_connect();
|
||||
if (!services) {
|
||||
/* This should never happen, but if it does we're probably
|
||||
* knee-deep in more serious problems already... */
|
||||
_e("Failed allocating shared memory, error %d: %s", errno, strerror (errno));
|
||||
abort();
|
||||
}
|
||||
svc_t *list = finit_svc_connect();
|
||||
|
||||
if (!list) {
|
||||
/* This should never happen, but if it does we're probably
|
||||
* knee-deep in more serious problems already... */
|
||||
_e("Failed connecting to shared memory, error %d: %s", errno, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_new - Create a new service object
|
||||
* svc_new - Create a new service
|
||||
* @cmd: External program to call, or 'internal' for internal inetd services
|
||||
* @id: Instance id
|
||||
* @type: Service type, one of service, task, run or inetd
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an empty &svc_t object, or %NULL if out of empty slots.
|
||||
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
|
||||
*/
|
||||
svc_t *svc_new(void)
|
||||
svc_t *svc_new(char *cmd, int id, int type)
|
||||
{
|
||||
svc_t *svc = NULL;
|
||||
int i, job = -1;
|
||||
svc_t *svc, *list = __connect_shm();
|
||||
|
||||
__connect_shm();
|
||||
if (svc_counter < MAX_NUM_SVC) {
|
||||
svc = &services[svc_counter++];
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
} else {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
/* Find first job n:o if registering multiple instances */
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!strcmp(svc->cmd, cmd)) {
|
||||
job = svc->job;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (job == -1)
|
||||
job = jobcounter++;
|
||||
|
||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
||||
svc_t *svc = &list[i];
|
||||
|
||||
if (svc->type == SVC_TYPE_FREE) {
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
svc->type = type;
|
||||
svc->job = job;
|
||||
svc->id = id;
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
|
||||
return svc;
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_del - Delete a service object
|
||||
* @svc: Pointer to an &svc_t object
|
||||
*
|
||||
* Returns:
|
||||
* Always succeeds, returning POSIX OK(0).
|
||||
*/
|
||||
int svc_del(svc_t *svc)
|
||||
{
|
||||
svc->type = SVC_TYPE_FREE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_iterator - Naive iterator over all registered services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first &svc_t when @first is set, otherwise the next &svc_t until
|
||||
* the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_iterator(int first)
|
||||
{
|
||||
static int i = 0;
|
||||
svc_t *list = __connect_shm();
|
||||
|
||||
if (first)
|
||||
i = 0;
|
||||
|
||||
while (i < MAX_NUM_SVC) {
|
||||
svc_t *svc = &list[i++];
|
||||
|
||||
if (svc->type != SVC_TYPE_FREE)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_inetd_iterator - Naive iterator over all registered inetd services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first inetd &svc_t when @first is set, otherwise the next
|
||||
* inetd &svc_t until the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_inetd_iterator(int first)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (svc_is_inetd(svc))
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_dynamic_iterator - Naive iterator over all registered dynamic services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first dynamically loaded &svc_t when @first is set, otherwise the
|
||||
* next dynamically loaded &svc_t until the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_dynamic_iterator(int first)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (svc->mtime)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_named_iterator - Iterates over all instances of a service.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
* @cmd: Service name to look for.
|
||||
*
|
||||
* Returns:
|
||||
* The first matching &svc_t when @first is set, otherwise the next
|
||||
* &svc_t instance with the same @cmd until the end when %NULL is
|
||||
* returned.
|
||||
*/
|
||||
svc_t *svc_named_iterator(int first, char *cmd)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
char *name = basename(svc->cmd);
|
||||
|
||||
if (!strncmp(name, cmd, strlen(name)))
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_job_iterator - Iterates over all instances of a service.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
* @job: Job to look for.
|
||||
*
|
||||
* Returns:
|
||||
* The first matching &svc_t when @first is set, otherwise the next
|
||||
* &svc_t instance with the same @job until the end when %NULL is
|
||||
* returned.
|
||||
*/
|
||||
svc_t *svc_job_iterator(int first, int job)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (svc->job == job)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_foreach - Run a callback for each registered service
|
||||
* @cb: Callback to run for each service
|
||||
*/
|
||||
void svc_foreach(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
|
||||
cb(svc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_foreach_dynamic - Run a callback for each registered dynamic service
|
||||
* @cb: Callback to run for each dynamic service
|
||||
*/
|
||||
void svc_foreach_dynamic(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0))
|
||||
cb(svc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_find - Find a service object by its full path name
|
||||
* @name: Full path name, e.g., /sbin/syslogd
|
||||
* @cmd: Full path name, e.g., /sbin/syslogd
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find(char *name)
|
||||
svc_t *svc_find(char *cmd, int id)
|
||||
{
|
||||
int i;
|
||||
svc_t *svc;
|
||||
|
||||
__connect_shm();
|
||||
for (i = 0; i < svc_counter; i++) {
|
||||
svc_t *svc = &services[i];
|
||||
|
||||
if (!strncmp(name, svc->cmd, strlen(svc->cmd))) {
|
||||
_d("Found a matching svc for %s", name);
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) {
|
||||
_d("Found a matching svc for %s", cmd);
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
@@ -100,322 +272,212 @@ svc_t *svc_find(char *name)
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_iterator - Iterates over all registered services.
|
||||
* @restart: Get first &svc_t object by setting @restart
|
||||
* svc_find_by_pid - Find an service object by its PID
|
||||
* @pid: Process ID to match
|
||||
*
|
||||
* Returns:
|
||||
* The first &svc_t when @restart is set, otherwise the next &svc_t until
|
||||
* the end when %NULL is returned.
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_iterator(int restart)
|
||||
svc_t *svc_find_by_pid(pid_t pid)
|
||||
{
|
||||
static int i = 0;
|
||||
svc_t *svc;
|
||||
|
||||
__connect_shm();
|
||||
if (restart)
|
||||
i = 0;
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->pid == pid)
|
||||
return svc;
|
||||
}
|
||||
|
||||
if (i >= svc_counter)
|
||||
return NULL;
|
||||
|
||||
return &services[i++];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_register - Register a non-backgrounding service to be monitored
|
||||
* @line: A complete command line with -- separated description text
|
||||
* @username: Optional username to run service as, or %NULL to run as root
|
||||
*
|
||||
* Actually, the @line can optionally start with a username, denoted by
|
||||
* an @ character. Like this:
|
||||
*
|
||||
* "service [@username] /path/to/daemon -- Description text"
|
||||
*
|
||||
* The [] brackets means the argument is optional. If the username is
|
||||
* left out the daemon is started as root.
|
||||
* svc_find_by_jobid - Find an service object by its JOB:ID
|
||||
* @job: Job n:o
|
||||
* @id: Instance id
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
int svc_register(char *line, char *username)
|
||||
svc_t *svc_find_by_jobid(int job, int id)
|
||||
{
|
||||
int i = 0;
|
||||
char *cmd, *desc;
|
||||
svc_t *svc;
|
||||
|
||||
if (!line) {
|
||||
_e("Invalid input argument.");
|
||||
return errno = EINVAL;
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->job == job && svc->id == id)
|
||||
return svc;
|
||||
}
|
||||
|
||||
desc = strstr(line, "-- ");
|
||||
if (desc)
|
||||
*desc = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd = strtok(line, " ");
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
_e("Incomplete service, cannot register.");
|
||||
return errno = ENOENT;
|
||||
/**
|
||||
* svc_find_by_nameid - Find an service object by its basename:ID
|
||||
* @name: Process name to match
|
||||
* @id: Instance id
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find_by_nameid(char *name, int id)
|
||||
{
|
||||
char *ptr;
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
ptr = strrchr(svc->cmd, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = svc->cmd;
|
||||
|
||||
if (svc->id == id && !strcmp(name, ptr))
|
||||
return svc;
|
||||
}
|
||||
|
||||
if (cmd[0] == '@') { /* @username */
|
||||
username = &cmd[1];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if valid command follows... */
|
||||
cmd = strtok(NULL, " ");
|
||||
if (!cmd)
|
||||
goto incomplete;
|
||||
/**
|
||||
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
||||
*
|
||||
* This function traverses the list of known services, marking all that
|
||||
* have been loaded from /etc/finit.d/ for deletion. If a .conf file
|
||||
* has been removed svc_cleanup_dynamic() will stop and delete the
|
||||
* service.
|
||||
*
|
||||
* This function is called from sighup_cb().
|
||||
*/
|
||||
void svc_mark_dynamic(void)
|
||||
{
|
||||
svc_t *svc = svc_dynamic_iterator(1);
|
||||
|
||||
while (svc) {
|
||||
svc->dirty = -1;
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
|
||||
svc = svc_new();
|
||||
if (!svc) {
|
||||
_e("Out of memorym, cannot register service %s", cmd);
|
||||
return errno = ENOMEM;
|
||||
void svc_check_dirty(svc_t *svc, time_t mtime)
|
||||
{
|
||||
if (svc->mtime != mtime)
|
||||
svc->dirty = 1;
|
||||
else
|
||||
svc->dirty = 0;
|
||||
svc->mtime = mtime;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
|
||||
* @cb: Callback to run for each stale service
|
||||
*
|
||||
* This function traverses the list of known services to stop and clean
|
||||
* up all non-existing services previously marked by svc_mark_dynamic().
|
||||
*/
|
||||
void svc_clean_dynamic(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc = svc_dynamic_iterator(1);
|
||||
|
||||
while (svc) {
|
||||
if (svc->dirty == -1 && cb)
|
||||
cb(svc);
|
||||
|
||||
svc->dirty = 0;
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (desc)
|
||||
strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
|
||||
if (username)
|
||||
strlcpy(svc->username, username, sizeof(svc->username));
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||
|
||||
while ((cmd = strtok(NULL, " ")))
|
||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||
svc->args[i][0] = 0;
|
||||
/**
|
||||
* svc_clean_bootstrap - Remove bootstrap-only services after boot
|
||||
* @svc: Pointer to &svc_t object
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) if object was bootstrap-only, otherwise %FALSE(0)
|
||||
*/
|
||||
int svc_clean_bootstrap(svc_t *svc)
|
||||
{
|
||||
if (!ISOTHER(svc->runlevels, 0)) {
|
||||
svc->pid = 0;
|
||||
svc_del(svc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_enabled - Should the service run?
|
||||
* @svc: Pointer to &svc_t object
|
||||
* @event: Dynamic event, opaque flag passed to callback
|
||||
* @arg: Event argument, used only by external service plugins.
|
||||
*
|
||||
* This method calls an associated service callback, if registered by a
|
||||
* plugin, and returns the &svc_cmd_t status. If no plugin is registered
|
||||
* the service is statically enabled in /etc/finit.conf and the result
|
||||
* will always be %SVC_START.
|
||||
*
|
||||
* Returns:
|
||||
* Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
|
||||
*/
|
||||
svc_cmd_t svc_enabled(svc_t *svc, int event, void *arg)
|
||||
char *svc_status(svc_t *svc)
|
||||
{
|
||||
if (!svc) {
|
||||
errno = EINVAL;
|
||||
return SVC_STOP;
|
||||
}
|
||||
if (!svc_in_runlevel(svc, runlevel))
|
||||
return "halted";
|
||||
|
||||
/* Is there a service plugin registered? */
|
||||
if (svc->cb)
|
||||
return svc->cb(svc, event, arg);
|
||||
switch (svc->state) {
|
||||
case SVC_WAITING_STATE:
|
||||
return "waiting";
|
||||
|
||||
/* No service plugin, default to start, since listed in finit.conf */
|
||||
return SVC_START;
|
||||
}
|
||||
case SVC_PAUSED_STATE:
|
||||
return "stopped";
|
||||
|
||||
static int is_norespawn(void)
|
||||
{
|
||||
return sig_stopped() ||
|
||||
fexist("/mnt/norespawn") ||
|
||||
fexist("/tmp/norespawn");
|
||||
}
|
||||
case SVC_CONDHALT_STATE:
|
||||
return "nocond";
|
||||
|
||||
void svc_monitor(void)
|
||||
{
|
||||
pid_t lost;
|
||||
svc_t *svc;
|
||||
case SVC_RESTART_STATE:
|
||||
return "restart";
|
||||
|
||||
lost = waitpid(-1, NULL, WNOHANG);
|
||||
if (lost < 1)
|
||||
return;
|
||||
case SVC_RELOAD_STATE:
|
||||
return "reload";
|
||||
|
||||
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||
return;
|
||||
|
||||
/* Power user at the console, don't respawn tasks. */
|
||||
if (is_norespawn())
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
char *name = basename(svc->cmd);
|
||||
|
||||
if (lost != svc->pid)
|
||||
continue;
|
||||
|
||||
_d("Ouch, lost pid %d - %s(%d)", lost, name, svc->pid);
|
||||
if (sig_stopped()) {
|
||||
_e("Stopped, not respawning killed processes.");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Cleanup any lingering or semi-restarting tasks before respawning */
|
||||
_d("Sending SIGTERM to service group %s", name);
|
||||
procname_kill(name, SIGTERM);
|
||||
|
||||
/* Restarting lost service. */
|
||||
if (svc_enabled(svc, 0, NULL))
|
||||
svc_start(svc);
|
||||
case SVC_RUNNING_STATE:
|
||||
if (svc->pid)
|
||||
return "running";
|
||||
/* Fall through */
|
||||
|
||||
case SVC_HALTED_STATE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "halted";
|
||||
}
|
||||
|
||||
/* Remember: svc_enabled() must be called before calling svc_start() */
|
||||
int svc_start(svc_t *svc)
|
||||
{
|
||||
int respawn = svc->pid != 0;
|
||||
pid_t pid;
|
||||
sigset_t nmask, omask;
|
||||
char *args[MAX_NUM_SVC_ARGS];
|
||||
|
||||
/* Ignore if finit is SIGSTOP'ed */
|
||||
if (is_norespawn())
|
||||
return 0;
|
||||
|
||||
if (!respawn)
|
||||
print_desc("Starting ", svc->desc);
|
||||
|
||||
/* Block sigchild while forking. */
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
|
||||
pid = fork();
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
if (pid == 0) {
|
||||
int i = 0;
|
||||
int uid = getuser(svc->username);
|
||||
struct sigaction sa;
|
||||
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
|
||||
/* Set desired user */
|
||||
if (uid >= 0)
|
||||
setuid(uid);
|
||||
|
||||
/* Serve copy of args to process in case it modifies them. */
|
||||
for (i = 0; svc->args[i][0] != 0 && i < MAX_NUM_SVC_ARGS; i++)
|
||||
args[i] = svc->args[i];
|
||||
args[i] = NULL;
|
||||
|
||||
if (debug) {
|
||||
int fd;
|
||||
char buf[256] = "";
|
||||
|
||||
fd = open (CONSOLE, O_WRONLY | O_APPEND);
|
||||
if (-1 != fd) {
|
||||
dup2(STDOUT_FILENO, fd);
|
||||
dup2(STDERR_FILENO, fd);
|
||||
}
|
||||
|
||||
for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) {
|
||||
char arg[MAX_ARG_LEN];
|
||||
|
||||
snprintf(arg, sizeof(arg), "%s ", args[i]);
|
||||
if ((sizeof(buf) - strlen(buf)) < strlen(arg))
|
||||
strcat(buf, arg);
|
||||
}
|
||||
_e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf);
|
||||
}
|
||||
|
||||
execvp(svc->cmd, args);
|
||||
exit(0);
|
||||
}
|
||||
svc->pid = pid;
|
||||
|
||||
if (!respawn)
|
||||
print_result(svc->pid > 1 ? 0 : 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int svc_start_by_name(char *name)
|
||||
{
|
||||
svc_t *svc = svc_find(name);
|
||||
|
||||
if (svc && svc_enabled(svc, 0, NULL))
|
||||
return svc_start(svc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int svc_stop(svc_t *svc)
|
||||
{
|
||||
int res = 1;
|
||||
|
||||
if (!svc) {
|
||||
_e("Failed, no svc pointer.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (svc->pid <= 1) {
|
||||
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
|
||||
svc->pid = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_desc("Stopping ", svc->desc);
|
||||
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||
res = kill(svc->pid, SIGTERM);
|
||||
print_result(res);
|
||||
svc->pid = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int svc_reload(svc_t *svc)
|
||||
{
|
||||
/* Ignore if finit is SIGSTOP'ed */
|
||||
if (is_norespawn())
|
||||
return 0;
|
||||
|
||||
if (!svc) {
|
||||
_e("Failed, no svc pointer.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (svc->pid <= 1) {
|
||||
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
|
||||
svc->pid = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||
return kill(svc->pid, SIGHUP);
|
||||
}
|
||||
|
||||
void svc_start_all(void)
|
||||
/* Same base service, return unique ID */
|
||||
int svc_next_id(char *cmd)
|
||||
{
|
||||
int id = 0;
|
||||
svc_t *svc;
|
||||
svc_cmd_t cmd;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
cmd = svc_enabled(svc, 0, NULL);
|
||||
if (SVC_START == cmd || (SVC_RELOAD == cmd && svc->pid == 0))
|
||||
svc_start(svc);
|
||||
else if (SVC_RELOAD == cmd)
|
||||
svc_reload(svc);
|
||||
if (!strcmp(svc->cmd, cmd) && id < svc->id)
|
||||
id = svc->id;
|
||||
}
|
||||
|
||||
_d("Running svc up hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
return id + 1;
|
||||
}
|
||||
|
||||
int svc_is_unique(svc_t *svc)
|
||||
{
|
||||
svc_t *list = __connect_shm();
|
||||
int i, unique = 1;
|
||||
|
||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
||||
svc_t *s = &list[i];
|
||||
|
||||
if (svc->type == SVC_TYPE_FREE)
|
||||
continue;
|
||||
|
||||
if (s == svc)
|
||||
continue;
|
||||
|
||||
if (s->job == svc->job) {
|
||||
unique = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return unique;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* compile-command: "gcc -g -DUNITTEST -o unittest svc.c"
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Finit service monitor and heneric API for managing svc_t structures
|
||||
/* Low-level service primitives and generic API for managing svc_t structures
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@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
|
||||
@@ -25,10 +25,12 @@
|
||||
#ifndef FINIT_SVC_H_
|
||||
#define FINIT_SVC_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h> /* strerror() */
|
||||
#include <sys/ipc.h> /* IPC_CREAT */
|
||||
#include <sys/shm.h> /* shmat() */
|
||||
#include <sys/types.h> /* pid_t */
|
||||
#include "libite/lite.h"
|
||||
#include "inetd.h"
|
||||
#include "helpers.h"
|
||||
|
||||
typedef enum {
|
||||
SVC_STOP = 0, /* Disabled */
|
||||
@@ -36,33 +38,73 @@ typedef enum {
|
||||
SVC_RELOAD /* Enabled, needs restart */
|
||||
} 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_t;
|
||||
|
||||
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_state_t;
|
||||
|
||||
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
||||
#define MAX_ARG_LEN 64
|
||||
#define MAX_STR_LEN 64
|
||||
#define MAX_USER_LEN 16
|
||||
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
||||
#define MAX_NUM_SVC 64 /* Enough? */
|
||||
#define MAX_NUM_SVC_ARGS 16
|
||||
#define MAX_NUM_SVC_ARGS 32
|
||||
|
||||
/* Default enable for all services, can be stopped by means
|
||||
* of issuing an initctl call. E.g.
|
||||
* initctl <stop|start|restart> service */
|
||||
typedef struct svc {
|
||||
/* Instance specifics */
|
||||
int job, id; /* JOB:ID */
|
||||
|
||||
/* Service details */
|
||||
pid_t pid;
|
||||
int reload;
|
||||
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,
|
||||
* or -1 when marked for removal */
|
||||
int runlevels;
|
||||
int sighup; /* This service supports SIGHUP :) */
|
||||
char events[MAX_ARG_LEN];
|
||||
|
||||
/* Incremented for each restart by service monitor. */
|
||||
unsigned int restart_counter;
|
||||
|
||||
/* For inetd services */
|
||||
inetd_t inetd;
|
||||
|
||||
/* Identity */
|
||||
char username[MAX_USER_LEN];
|
||||
char group[MAX_USER_LEN];
|
||||
|
||||
/* Command, arguments and service description */
|
||||
char cmd[MAX_ARG_LEN];
|
||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
||||
char desc[MAX_STR_LEN];
|
||||
char username[MAX_USER_LEN];
|
||||
|
||||
/* For external plugins. If @cb is set a plugin is loaded.
|
||||
* @dynamic: Set by plugins that want dynamic events.
|
||||
/* 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".
|
||||
* @private: Can be used freely by plugin, e.g., to store "states".
|
||||
*/
|
||||
int dynamic;
|
||||
int dynamic_stop;
|
||||
int private;
|
||||
int dynamic;
|
||||
int dynamic_stop;
|
||||
int private;
|
||||
svc_cmd_t (*cb)(struct svc *svc, int event, void *event_arg);
|
||||
} svc_t;
|
||||
|
||||
@@ -71,11 +113,11 @@ typedef struct svc_map svc_map_t;
|
||||
/* Put services array in shm */
|
||||
static inline svc_t *finit_svc_connect(void)
|
||||
{
|
||||
const long ID = FINIT_SHM_ID;
|
||||
static void *ptr = (void *)-1;
|
||||
|
||||
if ((void *)-1 == ptr) {
|
||||
ptr = shmat (shmget (ID, sizeof(svc_t) * MAX_NUM_SVC, 0600 | IPC_CREAT), NULL, 0);
|
||||
ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC,
|
||||
0600 | IPC_CREAT), NULL, 0);
|
||||
if ((void *)-1 == ptr)
|
||||
return NULL;
|
||||
}
|
||||
@@ -83,17 +125,42 @@ static inline svc_t *finit_svc_connect(void)
|
||||
return (svc_t *)ptr;
|
||||
}
|
||||
|
||||
svc_t *svc_new (void);
|
||||
svc_t *svc_find (char *name);
|
||||
svc_t *svc_iterator (int restart);
|
||||
svc_t *svc_new (char *cmd, int id, int type);
|
||||
int svc_del (svc_t *svc);
|
||||
|
||||
int svc_register (char *line, char *username);
|
||||
int svc_id_by_name (char *name);
|
||||
svc_cmd_t svc_enabled (svc_t *svc, int event, void *arg);
|
||||
int svc_start (svc_t *svc);
|
||||
int svc_start_by_name (char *name);
|
||||
int svc_stop (svc_t *svc);
|
||||
int svc_reload (svc_t *svc);
|
||||
svc_t *svc_find (char *cmd, int id);
|
||||
svc_t *svc_find_by_pid (pid_t pid);
|
||||
svc_t *svc_find_by_jobid (int job, int id);
|
||||
svc_t *svc_find_by_nameid (char *name, int id);
|
||||
|
||||
svc_t *svc_iterator (int first);
|
||||
svc_t *svc_inetd_iterator (int first);
|
||||
svc_t *svc_dynamic_iterator (int first);
|
||||
svc_t *svc_named_iterator (int first, char *cmd);
|
||||
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_mark_dynamic (void);
|
||||
void svc_check_dirty (svc_t *svc, time_t mtime);
|
||||
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_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_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 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; }
|
||||
|
||||
#endif /* FINIT_SVC_H_ */
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/* Finit TTY handling
|
||||
*
|
||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||
* Copyright (c) 2013 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 <signal.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "libite/lite.h"
|
||||
#include "tty.h"
|
||||
|
||||
LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
||||
|
||||
|
||||
/* tty [!1-9,S] <DEV> [BAUD[,BAUD,...]] [TERM] */
|
||||
int tty_register(char *line)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
int insert = 0, baud = 0;
|
||||
char *cmd, *dev;
|
||||
char *runlevels = NULL, *term = NULL;
|
||||
|
||||
if (!line) {
|
||||
_e("Invalid input argument.");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
cmd = strtok(line, " ");
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
_e("Incomplete tty, cannot register.");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
if (cmd[0] == '[') { /* [runlevels] */
|
||||
runlevels = &cmd[0];
|
||||
dev = strtok(NULL, " ");
|
||||
if (!dev)
|
||||
goto incomplete;
|
||||
} else {
|
||||
dev = cmd;
|
||||
}
|
||||
|
||||
cmd = strtok(NULL, " ");
|
||||
if (cmd) {
|
||||
baud = strtonum(cmd, 50, 4000000, NULL);
|
||||
term = strtok(NULL, " ");
|
||||
}
|
||||
|
||||
entry = tty_find(dev);
|
||||
if (!entry) {
|
||||
insert = 1;
|
||||
entry = calloc(1, sizeof(*entry));
|
||||
if (!entry)
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
entry->data.name = strdup(dev);
|
||||
if (!baud)
|
||||
baud = BAUDRATE;
|
||||
entry->data.baud = baud;
|
||||
entry->data.term = term ? strdup(term) : NULL;
|
||||
entry->data.runlevels = conf_parse_runlevels(runlevels);
|
||||
_d("Registering tty %s at %d baud with term=%s on runlevels %s", dev, baud, term ?: "N/A", runlevels);
|
||||
|
||||
if (insert)
|
||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
tty_node_t *tty_find(char *dev)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (!strcmp(dev, entry->data.name))
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t tty_num(void)
|
||||
{
|
||||
size_t num = 0;
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link)
|
||||
num++;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
tty_node_t *tty_find_by_pid(pid_t pid)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (entry->data.pid == pid)
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void tty_start(finit_tty_t *tty)
|
||||
{
|
||||
int i = 0, is_console = 0;
|
||||
char *cmd, *arg;
|
||||
char line[FILENAME_MAX];
|
||||
char *args[42];
|
||||
|
||||
if (tty->pid)
|
||||
return;
|
||||
|
||||
if (console && !strcmp(tty->name, console))
|
||||
is_console = 1;
|
||||
|
||||
if (!strncmp(tty->name, _PATH_DEV, strlen(_PATH_DEV))) {
|
||||
#if defined(GETTY_AGETTY)
|
||||
snprintf(line, sizeof(line), "%s %s %d %s", GETTY, tty->name, tty->baud, tty->term ?: "");
|
||||
#elif defined(GETTY_BUSYBOX)
|
||||
snprintf(line, sizeof(line), "%s %d %s %s", GETTY, tty->baud, tty->name, tty->term ?: "");
|
||||
#else
|
||||
strlcpy(line, FALLBACK_SHELL, sizeof(line));
|
||||
is_console = 1;
|
||||
#endif
|
||||
} else {
|
||||
strlcpy(line, tty->name, sizeof(line));
|
||||
is_console = 1;
|
||||
}
|
||||
cmd = strtok(line, " ");
|
||||
args[i++] = basename(cmd);
|
||||
while ((arg = strtok(NULL, " ")))
|
||||
args[i++] = arg;
|
||||
args[i] = NULL;
|
||||
|
||||
_d("Starting %s: %s on %s", is_console ? "console" : "TTY", cmd, tty->name);
|
||||
tty->pid = run_getty(cmd, args, is_console);
|
||||
}
|
||||
|
||||
void tty_stop(finit_tty_t *tty)
|
||||
{
|
||||
if (!tty->pid)
|
||||
return;
|
||||
|
||||
_d("Stopping TTY %s", tty->name);
|
||||
kill(tty->pid, SIGTERM);
|
||||
do_sleep(2);
|
||||
kill(tty->pid, SIGKILL);
|
||||
waitpid(tty->pid, NULL, 0);
|
||||
tty->pid = 0;
|
||||
}
|
||||
|
||||
int tty_enabled(finit_tty_t *tty, int runlevel)
|
||||
{
|
||||
if (!tty)
|
||||
return 0;
|
||||
|
||||
if (!ISSET(tty->runlevels, runlevel))
|
||||
return 0;
|
||||
if (fexist(tty->name))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TTY monitor, called by service_monitor() */
|
||||
int tty_respawn(pid_t pid)
|
||||
{
|
||||
tty_node_t *entry = tty_find_by_pid(pid);
|
||||
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
/* Clear PID to be able to respawn it. */
|
||||
entry->data.pid = 0;
|
||||
|
||||
if (!tty_enabled(&entry->data, runlevel))
|
||||
tty_stop(&entry->data);
|
||||
else
|
||||
tty_start(&entry->data);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
||||
void tty_runlevel(int runlevel)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (!tty_enabled(&entry->data, runlevel))
|
||||
tty_stop(&entry->data);
|
||||
else
|
||||
tty_start(&entry->data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+32
-26
@@ -1,7 +1,7 @@
|
||||
/* Collection of utility funcs and C library extensions for finit & its plugins
|
||||
/* Finit TTY handling
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||
* Copyright (c) 2013 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
|
||||
@@ -22,34 +22,41 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_LITE_H_
|
||||
#define FINIT_LITE_H_
|
||||
#ifndef FINIT_TTY_H_
|
||||
#define FINIT_TTY_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include "queue.h" /* BSD sys/queue.h API */
|
||||
|
||||
int fexist (char *file);
|
||||
int fisdir (char *file);
|
||||
mode_t fmode (char *file);
|
||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||
|
||||
ssize_t copyfile (char *src, char *dst, int len);
|
||||
int movefile (char *src, char *dst);
|
||||
int copy_filep (FILE *src, FILE *dst);
|
||||
typedef struct {
|
||||
char *name;
|
||||
int baud;
|
||||
char *term;
|
||||
int runlevels;
|
||||
|
||||
int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip);
|
||||
int rsync (char *src, char *dst, int delete, int (*filter) (const char *file));
|
||||
int pid;
|
||||
} finit_tty_t;
|
||||
|
||||
static inline int fisslashdir(char *dir)
|
||||
{
|
||||
if (!dir) return 0;
|
||||
if (strlen (dir) > 0) return dir[strlen (dir) - 1] == '/';
|
||||
return 0;
|
||||
}
|
||||
typedef struct tty_node {
|
||||
LIST_ENTRY(tty_node) link;
|
||||
finit_tty_t data;
|
||||
} tty_node_t;
|
||||
|
||||
#endif /* FINIT_LITE_H_ */
|
||||
//extern LIST_HEAD(, tty_node) tty_list;
|
||||
|
||||
int tty_register (char *line);
|
||||
tty_node_t *tty_find (char *dev);
|
||||
size_t tty_num (void);
|
||||
tty_node_t *tty_find_by_pid (pid_t pid);
|
||||
void tty_start (finit_tty_t *tty);
|
||||
void tty_stop (finit_tty_t *tty);
|
||||
int tty_enabled (finit_tty_t *tty, int runlevel);
|
||||
int tty_respawn (pid_t pid);
|
||||
void tty_runlevel (int runlevel);
|
||||
|
||||
#endif /* FINIT_TTY_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
@@ -58,4 +65,3 @@ static inline int fisslashdir(char *dir)
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user