Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44137806e4 | ||
|
|
0848b9b07a | ||
|
|
c64188bfc5 | ||
|
|
5e789401d4 | ||
|
|
744fdd654f | ||
|
|
f51fde4d46 | ||
|
|
85261c9acd | ||
|
|
6a1082a947 | ||
|
|
2094d2a53a | ||
|
|
d7da177742 | ||
|
|
170e5aa3fd | ||
|
|
f8ef8a1616 | ||
|
|
f5f1a55747 | ||
|
|
a36aade7f4 | ||
|
|
76fc72770e | ||
|
|
b64dbbf8d0 | ||
|
|
d371b85c47 | ||
|
|
053304012f | ||
|
|
12d14aae84 | ||
|
|
6a7f115781 | ||
|
|
423c8d41a7 | ||
|
|
f2cab53e55 | ||
|
|
33fd6f0049 | ||
|
|
e83dcafb02 | ||
|
|
9a98bff23f | ||
|
|
1a2f7d162b | ||
|
|
5579ba16b1 | ||
|
|
510b36645b | ||
|
|
67cd88cd23 | ||
|
|
a04a6eda12 | ||
|
|
94525d6aae | ||
|
|
1d14806fd8 | ||
|
|
5e6f64eec9 | ||
|
|
df22628183 | ||
|
|
4870cab898 | ||
|
|
859c216347 | ||
|
|
931565c425 | ||
|
|
3006caf670 | ||
|
|
1269f082de | ||
|
|
7d444e07a5 | ||
|
|
6ec4dce5f9 | ||
|
|
8a0edbe801 | ||
|
|
46683f8eb8 | ||
|
|
fb16abd855 | ||
|
|
060de93b5b | ||
|
|
231b887b2a | ||
|
|
dddd45e3b5 | ||
|
|
cb9f6213dc | ||
|
|
f33ed869e4 | ||
|
|
4790344fbe | ||
|
|
a40858f162 | ||
|
|
682e80fed5 | ||
|
|
cd91e2c66c | ||
|
|
470549badb | ||
|
|
a85c3628b8 | ||
|
|
53ac0ffdfb | ||
|
|
eee6ffe95c | ||
|
|
a5491b3ded | ||
|
|
a1ea8ff564 | ||
|
|
0dffa731d2 | ||
|
|
9516c28a8a | ||
|
|
fa0a4e8579 | ||
|
|
a487a30814 | ||
|
|
40db4f4f58 | ||
|
|
3a1ad67e92 | ||
|
|
43942ab513 | ||
|
|
3b2f2690e7 | ||
|
|
5684b761da | ||
|
|
59f22c7d27 | ||
|
|
f522a26125 | ||
|
|
efb1d40ffd | ||
|
|
ea591c7580 | ||
|
|
af9a06466c | ||
|
|
7ed9f2ad05 | ||
|
|
65ccc407fa | ||
|
|
7fe9f3c4b6 | ||
|
|
24299ab5d9 | ||
|
|
e9670b87b3 | ||
|
|
406339e755 | ||
|
|
dc7039b8d8 | ||
|
|
90249e50ef | ||
|
|
b8e6954c46 | ||
|
|
4585aa0fb4 | ||
|
|
f2655d14c8 | ||
|
|
6d1d51b28c | ||
|
|
b9d233d19e | ||
|
|
2ec132c75a | ||
|
|
3092d55ec5 | ||
|
|
cd791afea5 | ||
|
|
829405f966 | ||
|
|
72526d9e88 | ||
|
|
f704eab6d7 | ||
|
|
a50bfc016e | ||
|
|
396c1b491f | ||
|
|
df89cc79b9 | ||
|
|
3429901d83 | ||
|
|
dedf885b92 | ||
|
|
93a21197ab | ||
|
|
8a0fffb00d | ||
|
|
7fdb280712 | ||
|
|
0a643953c0 | ||
|
|
fd837a4242 | ||
|
|
2759992dd2 | ||
|
|
d880cd75ae | ||
|
|
29fe8cc73d | ||
|
|
2b6f00f126 | ||
|
|
86e534095d | ||
|
|
865a3a7a4a | ||
|
|
2dc7de3aa2 | ||
|
|
06a5b43005 | ||
|
|
38939cc57e | ||
|
|
a3eadb72a7 | ||
|
|
6d41725387 | ||
|
|
ceb91f746c | ||
|
|
a8802ab0d2 | ||
|
|
ad98f69560 | ||
|
|
5953090688 | ||
|
|
22a69d8cca | ||
|
|
710a3d2c8f | ||
|
|
658fb9b037 | ||
|
|
a84bb9042f | ||
|
|
eeb13dba74 | ||
|
|
3d20237959 | ||
|
|
ddf1bce642 | ||
|
|
93cd4116db | ||
|
|
1df6a91a94 | ||
|
|
c64a71ff30 | ||
|
|
b2b6fd25d8 | ||
|
|
3ca548b681 | ||
|
|
ba2ab4b446 | ||
|
|
a8183b0559 | ||
|
|
227e4fa097 | ||
|
|
3d68ebbf54 | ||
|
|
6e017025aa | ||
|
|
5f818f917e | ||
|
|
0da1deb487 | ||
|
|
0337985422 | ||
|
|
f59d41cdec | ||
|
|
1cc998eb60 | ||
|
|
8c1d84305d | ||
|
|
7e4e5127c7 | ||
|
|
3b813bca68 | ||
|
|
a1f210bc70 | ||
|
|
0ecf46d7a9 | ||
|
|
268304927d | ||
|
|
fa02b59db5 | ||
|
|
2c904b5b44 | ||
|
|
4191abc79f | ||
|
|
abb928f60b | ||
|
|
3c52118a8e | ||
|
|
5354a57313 | ||
|
|
92b1d882e5 | ||
|
|
fd7a6c5020 | ||
|
|
a61551d15f | ||
|
|
7f62a69f74 | ||
|
|
c969bda62d | ||
|
|
af41fcd35e | ||
|
|
84e9569be6 | ||
|
|
a4ece0fe3f | ||
|
|
b2f66da1f9 | ||
|
|
abbc0a1d5c | ||
|
|
37e420bd35 | ||
|
|
d72eb45d6e | ||
|
|
30c593f64e | ||
|
|
f858d94d2a | ||
|
|
992bab2f8f | ||
|
|
72c9681ffb | ||
|
|
e439b79e82 | ||
|
|
36e001d636 | ||
|
|
75aa14466f | ||
|
|
7f899b7a91 | ||
|
|
1c177fd953 | ||
|
|
617ec7c147 | ||
|
|
192d3aafe7 | ||
|
|
f11ebb0046 | ||
|
|
b5e260b9e0 | ||
|
|
45c10edad6 | ||
|
|
b60d8ce4b6 | ||
|
|
d35a8bbda9 |
@@ -2,13 +2,15 @@
|
||||
*.o
|
||||
*.so
|
||||
*.gz
|
||||
ID
|
||||
GPATH
|
||||
GRTAGS
|
||||
GSYMS
|
||||
GTAGS
|
||||
|
||||
# GNU configure and build system
|
||||
/autom4te.cache/*
|
||||
/.deps
|
||||
/autom4te.cache
|
||||
/aclocal.m4
|
||||
/ar-lib
|
||||
/compile
|
||||
@@ -33,3 +35,8 @@ GTAGS
|
||||
/.config
|
||||
/.stamp
|
||||
/.unpacked
|
||||
|
||||
# WeOS build system
|
||||
/.autoconf
|
||||
/.automake
|
||||
/.built
|
||||
|
||||
@@ -9,9 +9,9 @@ sudo: false
|
||||
#sudo: true
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
# - clang
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
env:
|
||||
global:
|
||||
@@ -33,20 +33,23 @@ addons:
|
||||
branch_pattern: dev
|
||||
|
||||
install:
|
||||
- wget https://github.com/troglobit/libuev/releases/download/v2.1.0/libuev-2.1.0.tar.xz
|
||||
- wget https://github.com/troglobit/libuev/releases/download/v2.2.0/libuev-2.2.0.tar.xz
|
||||
- wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
|
||||
- tar xf libuev-2.1.0.tar.xz
|
||||
- tar xf libuev-2.2.0.tar.xz
|
||||
- tar xf libite-2.0.1.tar.xz
|
||||
- (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- (cd libuev-2.2.0 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --disable-inetd --enable-static
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --enable-inetd-echo-plugin --enable-inetd-time-plugin --enable-x11-common-plugin --enable-inetd-chargen-plugin --enable-fallback-shell --enable-watchdog
|
||||
- cat config.log
|
||||
- make clean
|
||||
- make -j5 V=1
|
||||
- make V=1 -j5
|
||||
- make install-strip
|
||||
- tree /tmp
|
||||
- ldd /tmp/sbin/finit
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
Current Maintainer(s):
|
||||
* Joachim Nilsson <troglobit@gmail.com>
|
||||
|
||||
Major Contributors:
|
||||
* Tobias Waldekranz <tobias@waldekranz.com>
|
||||
* Mattias Walström <mattias.walstrom@westermo.se>
|
||||
* Jonas Johansson <jonas.johansson@westermo.se>
|
||||
|
||||
Original Author(s):
|
||||
* Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
|
||||
|
||||
@@ -4,10 +4,87 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[3.1][] - 2017-12-xx
|
||||
[3.2][UNRELEASED] - 2018-10-xx
|
||||
------------------------------
|
||||
|
||||
Bug fix release.
|
||||
|
||||
### Changes
|
||||
* Add support for `--disable-docs` and `--disable-contrib` to speed up
|
||||
builds and work around issue with massively parallel builds
|
||||
* Add `-b`, batch mode, for non-interactive use to `initctl`
|
||||
* Prefer udev to handle `/dev/` if mdev is also available
|
||||
* Redirect dbus daemon output to syslog
|
||||
* Set `$SHELL`, like `$PATH`, to a sane default value, needed by BusyBox
|
||||
* Finit no longer automatically reloads its `*.conf` files after running
|
||||
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
|
||||
* `initctl` without an argument or option now defaults to list services
|
||||
* Converted built-in watchdog daemon to standalone mini watchdogd
|
||||
* Improved watchdog hand-over, now based on `svc_t` and not PID
|
||||
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
|
||||
not allowed in the runtime runlevel are unconditionally stopped
|
||||
* Removed `HOOK_SVC_START` and `HOOK_SVC_LOST`, caused more problems
|
||||
than they were worth. Users are encouraged to use accounting instead
|
||||
* Skip displaying "Restarting ..." progress for bootstrap processes
|
||||
* Added a simple work queue mechanism to queue up work at boot + runtime
|
||||
- Postpone deletion of `svc_t` until any `SIGKILL` timer has elapsed
|
||||
- As long as a stepped service changes state we queue another step all
|
||||
event, because services may depend on each other
|
||||
* Require new libuEv API: `uev_init1()` to reduce event cache so that
|
||||
the kernel can invalidate deleted events before enqueing to userspace
|
||||
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
|
||||
from the `hwclock` tool. Note: the kernel can also be set to load
|
||||
and store RTC to/from system clock at boot/halt as well.
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix #96: Start udevd as a proper service
|
||||
* Ensure we track run commands as well as task/service, once per runlevel
|
||||
* Fix #98: FTBFS with `--disable-inetd`
|
||||
* Make sure to unblock UDP inetd services when connection terminates.
|
||||
Regression introduced in v3.1
|
||||
* Ensure run/tasks also go to stopping state on exit, like services,
|
||||
otherwise it is unnecessarily hard to restart them
|
||||
* Fix #99: Do not try to `SIGKILL` inetd services, they are not backed
|
||||
by a PID. This caused a use after free issue crashing finit. Found
|
||||
and fixed by Tobias Waldekranz, Westermo
|
||||
* Fix missing OS/Finit title bug, adds leading newline before banner
|
||||
* Remove "Failed connecting to watchdog ..." error message on systems
|
||||
that do not have a watchdog
|
||||
* Fix #100: Early condition handling may not work if `/var/run` does
|
||||
not yet exist (symlink to `/run`). Added compat layer for access
|
||||
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY
|
||||
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
|
||||
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
|
||||
Fixed by Jonas Johansson, Westermo
|
||||
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
|
||||
Fixed by Jonas Johansson, Westermo
|
||||
* Fix #111: Only restart inetd services when necessary. E.g., if the
|
||||
listening interface is changed. Only stop established connections
|
||||
which are no longer allowed, i.e. do not touch already allowed
|
||||
established connections. Fixed by Jonas Johansson, Westermo
|
||||
* Fix: update inetd service args on config change. Found and fixed by
|
||||
Petrus Hellgren, Westermo
|
||||
* Fix `initctl [start | restart]`, should behave the same for services
|
||||
that have crashed. Found by Mattias Walström, Westermo
|
||||
* Wait for bootstrap phase to complete before cleaning out any bootstrap
|
||||
processes that have stopped, they may be restarted again
|
||||
* Reassert condition when an unmodified run/task/service goes from
|
||||
WAITING back to RUNNING again after a reconfiguration event.
|
||||
Found and fixed by Jonas Johansson, Westermo
|
||||
* Remove service condition when service is deleted
|
||||
|
||||
|
||||
[3.1][] - 2018-01-23
|
||||
--------------------
|
||||
|
||||
Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
Improvements to `netlink.so` plugin, per-service `rlimit` support,
|
||||
improved integration with `watchdogd`, auto-detect TTY console. Much
|
||||
improved debug, rescue and logging support. Also, many fixes to both
|
||||
big and small issues, most notably in the condition handling, which no
|
||||
longer is sensitive to time skips.
|
||||
|
||||
This version requires at least libuEv v2.1.0 and libite v2.0.1
|
||||
|
||||
### Changes
|
||||
|
||||
@@ -24,9 +101,8 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
* Support for auto-detecting serial console using Linux SysFS, the new
|
||||
`tty @console` eliminates the need to keep track of different console
|
||||
devices across embedded platforms: `/dev/ttyS0`, `/dev/ttyAMA0`, etc.
|
||||
* Support for the special debug option `nologin` to tty configurations,
|
||||
skips the login prompt and skips immediately to a root shell. Useful
|
||||
during board bringup, in developer builds, etc. Be careful though ...
|
||||
* Add TTY `nologin` option. Bypasses login and skips immediately to a
|
||||
root shell. Useful during board bringup, in developer builds, etc.
|
||||
* Support for calling run/tasks on Finit internal HOOK points, issue #18
|
||||
* Removed support for long-since deprecated `console DEV` setting
|
||||
* Cosmetic change to login, pressing enter at the `Press enter to ...`
|
||||
@@ -57,9 +133,21 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
redirect `stdout` and `stderr` of all applications to `/dev/null`
|
||||
* New `pid` sub-option to services when a service does not create a PID
|
||||
file, or when the PID file has another name. Issue #95
|
||||
* New `log` sub-option to service/run/task to selectively redirect
|
||||
`stdout` and `stderr` using the new tool `logit` to either syslog
|
||||
or a logfile. Issue #44
|
||||
* Greatly improved `log` sub-option to service/run/tasks, selectively
|
||||
redirect `stdout` and `stderr` using the new tool `logit` to either
|
||||
syslog or a logfile. Issue #44
|
||||
* Support for automatic log rotation of logfiles created by `log`
|
||||
option. Use `configure --disable-logrotate` on systems with a
|
||||
dedicated log rotation service. Issue #44
|
||||
* Support for disabling service/run/task progress with empty ` --`
|
||||
description. Note: no description separator gives a default desc.
|
||||
* Create `/etc/mtab` symlink if missing on system (bootmisc plugin)
|
||||
* New hook: `hook/mount/post` runs after `mount -a` but before the
|
||||
`hook/mount/all`, where `bootmisc.so` runs. This provides the
|
||||
possibility of running a second stage mount command before files in
|
||||
`/run` and similar are created
|
||||
* Skip `gdbserver` when unleashing the grim reaper at shutdown
|
||||
* Distribute and install `docs/` and `contrib/` directories
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -94,6 +182,7 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
* Fix issue #81 properly, remove use of SYSV shm IPC completely. Finit
|
||||
now use the API socket for all communication between PID 1 and initctl
|
||||
* Fix segfault on x86_64 when started with kernel cmdline `--debug`
|
||||
* Normalize condition paths on systems with `/run` instead of `/var/run`
|
||||
|
||||
|
||||
[3.0][] - 2017-10-19
|
||||
@@ -691,7 +780,9 @@ Major bug fix release.
|
||||
|
||||
* Initial release
|
||||
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.0...HEAD
|
||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
|
||||
[3.2]: https://github.com/troglobit/finit/compare/3.1...3.2
|
||||
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
|
||||
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
|
||||
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
|
||||
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
SUBDIRS = plugins src
|
||||
bin_SCRIPT = service
|
||||
doc_DATA = README.md LICENSE contrib/finit.conf
|
||||
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
||||
|
||||
if CONTRIB
|
||||
SUBDIRS += contrib
|
||||
endif
|
||||
if DOCS
|
||||
SUBDIRS += docs
|
||||
endif
|
||||
|
||||
# Compat hook
|
||||
install-dev:
|
||||
@make -C src install-pkgincludeHEADERS
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<img align="right" src="images/finit3.png" alt="Finit: Fast Init">
|
||||
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Features](#features)
|
||||
@@ -10,6 +10,7 @@
|
||||
* [Hooks, Callbacks & Plugins](docs/plugins.md#hooks-callbacks--plugins)
|
||||
* [Rebooting & Halting](#rebooting--halting)
|
||||
* [Commands & Status](#commands--status)
|
||||
* [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
|
||||
* [Building](docs/build.md#building)
|
||||
* [Running](docs/build.md#running)
|
||||
* [Recovery](docs/build.md#recovery)
|
||||
@@ -28,10 +29,13 @@ was reverse engineered from the [EeePC fastinit][] ten years ago by
|
||||
|
||||
Focus is on small and embedded systems, although Finit is fully usable
|
||||
on server and desktop systems as well. For working examples, see the
|
||||
[contrib/](contrib/) section where both [Debian](contrib/debian/) and
|
||||
up to date [Alpine Linux](contrib/alpine/) tutorials can be found.
|
||||
[contrib/](contrib/) section with tutorials for:
|
||||
|
||||

|
||||
* [Alpine Linux](contrib/alpine/),
|
||||
* [Debian GNU/Linux](contrib/debian/), and
|
||||
* [Void Linux](contrib/void/)
|
||||
|
||||

|
||||
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
||||
|
||||
|
||||
@@ -50,12 +54,23 @@ host default
|
||||
# Runlevel to start after bootstrap, 'S', default: 2
|
||||
runlevel 2
|
||||
|
||||
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||
log size=100k count=4
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
|
||||
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
|
||||
service [S12345] /sbin/klogd -n -- Kernel log daemon
|
||||
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
||||
|
||||
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||
# log file is rotated with the above settings. The condition declares
|
||||
# a dependency on a system default route (gateway) to be set. A single
|
||||
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
|
||||
|
||||
# For multiple instances of the same service, add :ID somewhere between
|
||||
# the service/run/task keyword and the command.
|
||||
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
AC_INIT(Finit, 3.1-rc2, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 3.2-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/finit.c])
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile plugins/Makefile src/Makefile])
|
||||
AC_CONFIG_FILES([Makefile
|
||||
contrib/Makefile
|
||||
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
|
||||
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile
|
||||
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
|
||||
docs/Makefile
|
||||
plugins/Makefile
|
||||
src/Makefile])
|
||||
|
||||
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
||||
#m4_include([m4/plugin.m4])
|
||||
@@ -21,6 +28,7 @@ LT_INIT([shared disable-static dlopen aix-soname=both disable-fast-install])
|
||||
AC_PROG_CC
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_MKDIR_P
|
||||
|
||||
# Configuration.
|
||||
AC_HEADER_STDC
|
||||
@@ -33,7 +41,7 @@ AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
|
||||
# Check for required libraries
|
||||
PKG_CHECK_MODULES([uev], [libuev >= 2.1.0])
|
||||
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
|
||||
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
|
||||
|
||||
# Check for configured Finit features
|
||||
@@ -70,6 +78,14 @@ AC_ARG_ENABLE(logrotate,
|
||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
||||
enable_logrotate=yes])
|
||||
|
||||
AC_ARG_ENABLE(docs,
|
||||
AS_HELP_STRING([--disable-docs], [Disable build and install of docs section]),,[
|
||||
enable_docs=yes])
|
||||
|
||||
AC_ARG_ENABLE(contrib,
|
||||
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
|
||||
enable_contrib=yes])
|
||||
|
||||
# Check for extra plugins to enable
|
||||
enable_all_plugins=auto
|
||||
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
|
||||
@@ -79,7 +95,7 @@ AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864])
|
||||
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
|
||||
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
|
||||
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
|
||||
AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!])
|
||||
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
|
||||
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
|
||||
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
|
||||
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
|
||||
@@ -208,11 +224,15 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
||||
AM_CONDITIONAL(DOCS, [test "x$enable_docs" = "xyes"])
|
||||
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||
|
||||
# Override default libdir, used for plugins and rescue.conf
|
||||
#pkglibdir=$libdir/finit
|
||||
#AC_SUBST(pkglibdir)
|
||||
#AC_EXPAND_DIR(pkglibdir, "$libdir/finit")
|
||||
pkglibdir=$libdir/finit
|
||||
AC_SUBST(pkglibexecdir)
|
||||
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
|
||||
AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path])
|
||||
|
||||
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
|
||||
AC_SUBST(plugin_path)
|
||||
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
|
||||
@@ -244,6 +264,8 @@ Behavior:
|
||||
Plugins...............: $plugins
|
||||
|
||||
Optional features:
|
||||
Install docs/.........: $enable_docs
|
||||
Install contrib/......: $enable_contrib
|
||||
Built-in inetd........: $enable_inetd
|
||||
Built-in watchdogd....: $enable_watchdog
|
||||
Built-in logrotate....: $enable_logrotate
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
@@ -0,0 +1,5 @@
|
||||
SUBDIRS = alpine debian void
|
||||
bin_SCRIPT = service
|
||||
docsdir := @docdir@/contrib
|
||||
docs_DATA = README.md finit.conf procmon.sh
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -0,0 +1,5 @@
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/alpine
|
||||
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
SUBDIRS = available
|
||||
docsdir := @docdir@/contrib/alpine/finit.d
|
||||
d_LINKS = dropbear.conf getty.conf keymap.conf modules.conf klogd.conf \
|
||||
syslogd.conf
|
||||
|
||||
install-exec-hook:
|
||||
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||
@for file in $(d_LINKS); do \
|
||||
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||
src=available/$$file; \
|
||||
if [ -L $$lnk ]; then \
|
||||
echo "Skipping $$lnk exists."; \
|
||||
else \
|
||||
$(LN_S) $$src $$lnk; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
|
||||
uninstall-hook:
|
||||
@for file in $(d_LINKS); do \
|
||||
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||
src=available/$$file; \
|
||||
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||
continue; \
|
||||
fi; \
|
||||
$(RM) $$lnk; \
|
||||
done
|
||||
@@ -0,0 +1,4 @@
|
||||
docsdir := @docdir@/contrib/alpine/finit.d/available
|
||||
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
||||
lxdm.conf modules.conf ntpd.conf klogd.conf syslogd.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -0,0 +1,4 @@
|
||||
SUBDIRS = finit.d grub.d
|
||||
docsdir := @docdir@/contrib/debian
|
||||
docs_DATA = README.md finit.conf os-release build.sh install.sh
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -0,0 +1,27 @@
|
||||
SUBDIRS = available
|
||||
docsdir := @docdir@/contrib/debian/finit.d
|
||||
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
|
||||
modules.conf rsyslogd.conf sshd.conf
|
||||
|
||||
install-exec-hook:
|
||||
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||
@for file in $(d_LINKS); do \
|
||||
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||
src=available/$$file; \
|
||||
if [ -L $$lnk ]; then \
|
||||
echo "Skipping $$lnk exists."; \
|
||||
else \
|
||||
$(LN_S) $$src $$lnk; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
|
||||
uninstall-hook:
|
||||
@for file in $(d_LINKS); do \
|
||||
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||
src=available/$$file; \
|
||||
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||
continue; \
|
||||
fi; \
|
||||
$(RM) $$lnk; \
|
||||
done
|
||||
@@ -0,0 +1,5 @@
|
||||
docsdir := @docdir@/contrib/debian/finit.d/available
|
||||
docs_DATA = acpid.conf console-setup.conf getty.conf \
|
||||
keyboard-setup.conf lightdm.conf modules.conf \
|
||||
ntpclient.conf rsyslogd.conf sshd.conf xdm.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -0,0 +1,3 @@
|
||||
docsdir := @docdir@/contrib/debian/grub.d
|
||||
docs_DATA = 40_custom
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -17,6 +17,10 @@ runlevel 2
|
||||
# Network bringup script
|
||||
network service networking start
|
||||
|
||||
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||
log size=100k count=4
|
||||
|
||||
# Virtual consoles to start getty on
|
||||
tty /dev/tty1
|
||||
tty /dev/tty2
|
||||
@@ -33,6 +37,13 @@ service [2345] /sbin/klogd -n -- Kernel logging server
|
||||
service [2345] /sbin/syslogd -n -- Syslog server
|
||||
service [3] /usr/sbin/gdm -- GNOME Display Manager
|
||||
|
||||
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||
# log file is rotated with the above settings. The condition declares
|
||||
# a dependency on a system default route (gateway) to be set. A single
|
||||
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
USAGE="Usage: $0 processName"
|
||||
LOG_FILE="/tmp/memusage.csv"
|
||||
ELAPSED_TIME=0
|
||||
PERIOD=1 # seconds
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo $USAGE
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ElapsedTime,VmSize,VmRSS" > $LOG_FILE
|
||||
|
||||
# Monitor memory usage forever until script is killed
|
||||
while :
|
||||
do
|
||||
SUM_VM_SIZE=0
|
||||
SUM_RSS_SIZE=0
|
||||
# In case the monitored process has not yet started
|
||||
# keep searching until its PID is found
|
||||
PROCESS_PIDS=""
|
||||
while :
|
||||
do
|
||||
PROCESS_PIDS=`/bin/pidof $1`
|
||||
if [ "$PROCESS_PIDS.X" != ".X" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
for PID in ${PROCESS_PIDS} ; do
|
||||
VM_SIZE=`awk '/VmSize/ {print $2}' < /proc/$PID/status`
|
||||
if [ "$VM_SIZE.X" = ".X" ]; then
|
||||
continue
|
||||
fi
|
||||
#echo exprVM_ $SUM_VM_SIZE + $VM_SIZE
|
||||
SUM_VM_SIZE=`expr $SUM_VM_SIZE + $VM_SIZE`
|
||||
|
||||
VM_RSS=`awk '/VmRSS/ {print $2}' < /proc/$PID/status`
|
||||
if [ "$VM_RSS.X" = ".X" ]; then
|
||||
continue
|
||||
fi
|
||||
SUM_RSS_SIZE=`expr $SUM_RSS_SIZE + $VM_RSS`
|
||||
done
|
||||
echo "$ELAPSED_TIME sec, $SUM_VM_SIZE KB, $SUM_RSS_SIZE KB"
|
||||
echo "$ELAPSED_TIME,$SUM_VM_SIZE,$SUM_RSS_SIZE" >> $LOG_FILE
|
||||
sleep $PERIOD
|
||||
VM_SIZE=""
|
||||
VM_RSS=""
|
||||
# Needs to get actual elapsed time instead of doing this
|
||||
ELAPSED_TIME=`expr $ELAPSED_TIME + $PERIOD`
|
||||
done
|
||||
@@ -0,0 +1,4 @@
|
||||
SUBDIRS = finit.d
|
||||
docsdir := @docdir@/contrib/void
|
||||
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -0,0 +1,26 @@
|
||||
SUBDIRS = available
|
||||
docsdir := @docdir@/contrib/void/finit.d
|
||||
d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf
|
||||
|
||||
install-exec-hook:
|
||||
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||
@for file in $(d_LINKS); do \
|
||||
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||
src=available/$$file; \
|
||||
if [ -L $$lnk ]; then \
|
||||
echo "Skipping $$lnk exists."; \
|
||||
else \
|
||||
$(LN_S) $$src $$lnk; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
|
||||
uninstall-hook:
|
||||
@for file in $(d_LINKS); do \
|
||||
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||
src=available/$$file; \
|
||||
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||
continue; \
|
||||
fi; \
|
||||
$(RM) $$lnk; \
|
||||
done
|
||||
@@ -0,0 +1,5 @@
|
||||
docsdir := @docdir@/contrib/void/finit.d/available
|
||||
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
|
||||
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
|
||||
sshd.conf syslogd.conf uuidd.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -0,0 +1,2 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
@@ -0,0 +1,4 @@
|
||||
docsdir := @docdir@/docs
|
||||
docs_DATA = config.md conditions.md distro.md inetd.md plugins.md \
|
||||
service.md signals.md svc-machine.png cmdline.md
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
@@ -0,0 +1 @@
|
||||
config.md
|
||||
@@ -18,16 +18,25 @@ after a reconfiguration (`initctl reload`), the Finit dependency system
|
||||
can also be used.
|
||||
|
||||
|
||||
General
|
||||
-------
|
||||
Near Future
|
||||
-----------
|
||||
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown,
|
||||
possibly using an at-job, see [crond section](#Crond) below
|
||||
* Merge UDP redirect service uredir as an UDP inetd variant
|
||||
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
|
||||
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
|
||||
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
|
||||
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
|
||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||
* Cron/At support, see below
|
||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||
`pimd` man pages ...
|
||||
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown,
|
||||
possibly using an at-job, see [crond section](#Crond) below
|
||||
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
||||
`initctl show --json`
|
||||
* [Solaris SMF][] (Service Management Facility) has some interesting
|
||||
@@ -48,6 +57,8 @@ General
|
||||
now inotify listeners should be automatically created (and removed)
|
||||
when a subdirectory is added (or removed).
|
||||
|
||||
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
|
||||
|
||||
|
||||
Init
|
||||
----
|
||||
@@ -103,22 +114,3 @@ 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`
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||
`pimd` man pages ...
|
||||
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
|
||||
how close Finit3 still is to the original easy-to-use Finit0 :)
|
||||
|
||||
|
||||
[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/
|
||||
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
|
||||
@@ -53,8 +53,6 @@ Below are a few of the main switches to configure:
|
||||
|
||||
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
|
||||
|
||||
* `--enable-lost-plugin`: Enable noisy example plugin for `HOOK_SVC_LOST`.
|
||||
|
||||
* `--enable-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin.
|
||||
|
||||
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
Tips & Tricks with the kernel cmdline
|
||||
=====================================
|
||||
|
||||
This document summarizes the different boot parameters that can be
|
||||
passed on the Linux kernel command line. Not just limited to Finit.
|
||||
|
||||
* `debug`, or `--debug`
|
||||
Enable debug mode. The former syntax also enable kernel debug, and
|
||||
the latter only in Finit. Debug messages are printed to the console
|
||||
until a syslog daemon has been started.
|
||||
|
||||
* `init=/bin/sh`
|
||||
Bypass system default init and tell kernel to start a shell. Note,
|
||||
this shell is very limited and does not support signals and has no
|
||||
job control. Recommend using, and modifying, `rescue` mode instead.
|
||||
|
||||
* `panic=SEC`
|
||||
By default the kernel does not reboot after a kernel panic. This
|
||||
setting will cause a kernel reboot after SEC seconds.
|
||||
|
||||
* `quiet`
|
||||
Suppress kernel logging to console, except for warnings and errors.
|
||||
|
||||
* `rescue`
|
||||
Start Finit rescue mode; no network, no `fsck` of file systems in
|
||||
`/etc/fstab`, no `/etc/rc.local`, no `runparts`. The configuration
|
||||
is read from `/etc/rescue.conf`, can be modified by the operator.
|
||||
|
||||
* `single`, or `S`
|
||||
Single user mode, runlevel 1, in Finit. Useful to debug problems with
|
||||
the regular runlevel. All services and TTYs in `[1]` will be started,
|
||||
so a `tty [1] @console nologin` configuration presents you with a
|
||||
root console without login.
|
||||
|
||||
* `splash`
|
||||
Display Finit splash screen with progress at bootstrap and shutdown.
|
||||
|
||||
For more on kernel boot paramaters, see the man page `bootparam(7)`.
|
||||
@@ -2,6 +2,7 @@ Configuration
|
||||
=============
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||
* [Syntax](#syntax)
|
||||
* [Limitations](#limitations)
|
||||
* [/etc/finit.conf](#etcfinitconf)
|
||||
@@ -48,25 +49,27 @@ On `initctl reload` the following is checked for all services:
|
||||
For more info on the different states of a service, see the separate
|
||||
document [Finit Services](service.md).
|
||||
|
||||
### Execute pre commands for a service
|
||||
|
||||
If your service requires to run additional commands that are executed before
|
||||
the service is actually started (something like systemds `ExecStartPre`) you
|
||||
can use a wrapper shell script which runs the additional commands before
|
||||
executing the service.
|
||||
Service Wrapper Scripts
|
||||
-----------------------
|
||||
|
||||
The Finit service .conf file can be put into `/etc/finit.d/available` so you
|
||||
can control the service using `initctl`. Just place in the service .conf file
|
||||
the path to the wrapper shell script, e.g. if the wrapper is located in
|
||||
`/etc/start.d`:
|
||||
If your service requires to run additional commands, executed before the
|
||||
service is actually started, like the systemd `ExecStartPre`, you can
|
||||
use a wrapper shell script to start your service.
|
||||
|
||||
The Finit service `.conf` file can be put into `/etc/finit.d/available`,
|
||||
so you can control the service using `initctl`. Then use the path to
|
||||
the wrapper script in the Finit `.conf` service stanza. The following
|
||||
example employs a wrapper script in `/etc/start.d`.
|
||||
|
||||
**Example:**
|
||||
`/etc/finit.d/available/program.conf`:
|
||||
|
||||
* `/etc/finit.d/available/program.conf`:
|
||||
```conf
|
||||
service [235] <!> /etc/start.d/program -- Example Program
|
||||
```
|
||||
**Example:**
|
||||
`/etc/start.d/program:`
|
||||
|
||||
* `/etc/start.d/program:`
|
||||
```shell
|
||||
#!/bin/sh
|
||||
|
||||
@@ -77,9 +80,10 @@ OPTIONS="-u $(cat /etc/username)"
|
||||
exec /usr/bin/program $OPTIONS
|
||||
```
|
||||
|
||||
*Note, that the example sets `<!>` to denote that it doesn't support SIGHUP. That
|
||||
way Finit will stop/start such the service instead of sending SIGHUP at
|
||||
restart/reload.*
|
||||
**Note:**, that the example sets `<!>` to denote that it doesn't support
|
||||
`SIGHUP`. That way Finit will stop/start such the service instead of
|
||||
sending SIGHUP at restart/reload.
|
||||
|
||||
|
||||
Syntax
|
||||
------
|
||||
@@ -94,6 +98,54 @@ Syntax
|
||||
* `network <PATH>`
|
||||
Script or program to bring up networking, with optional arguments
|
||||
|
||||
* `cgroup <NAME> cpuset:SPEC cpu:SPEC mem:SPEC`
|
||||
Create a control group for resource limiting services using cgroups.
|
||||
Unlike `rlimit` this setting defines a common group to be selected for
|
||||
any set of run/task/services using the `group` directive (below).
|
||||
|
||||
A cpuset for a cgroup can be the traditional list of CPU cores to
|
||||
assign to the group, or a range that Finit will use to dynamically
|
||||
allocate cores based on the amount available. The range values can be
|
||||
either in absolute form or percent.
|
||||
|
||||
```shell
|
||||
cgroup first cpuset:1
|
||||
```
|
||||
|
||||
To create a cgroup with one exclusive CPU:
|
||||
|
||||
```shell
|
||||
cgroup single cpuset:[1,1]
|
||||
```
|
||||
|
||||
To create cgroup that uses at most two shared CPUs:
|
||||
|
||||
```shell
|
||||
cgroup uptotwo cpuset:[,2]
|
||||
```
|
||||
|
||||
To create cgroup with at least two shared CPUs:
|
||||
|
||||
```shell
|
||||
cgroup dual cpuset:[2,]
|
||||
```
|
||||
|
||||
To create cgroup with at least one CPU and at most half of all
|
||||
available CPUs:
|
||||
|
||||
```shell
|
||||
cgroup greedy cpuset:[1,50%]
|
||||
```
|
||||
|
||||
|
||||
* `group <NAME>`
|
||||
All subsequent `service`, `task`, or `run` directives are executed in the
|
||||
given control group `NAME`, which must be defined prior to this line. If
|
||||
`NAME` does not yet exist the default group `default` is used.
|
||||
|
||||
When separate Finit `.conf` files are used for services the default group
|
||||
is reset for each `.conf` file read.
|
||||
|
||||
* `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
|
||||
limit for a resource, or both if that argument is omitted. `RESOURCE`
|
||||
is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
|
||||
@@ -181,6 +233,18 @@ Syntax
|
||||
For a detailed description of conditions, and how to debug them, see
|
||||
the [Finit Conditions](conditions.md) document.
|
||||
|
||||
If a service should not be automatically started, it can be configured
|
||||
as manual with the optional `manual` argument. The service can then be
|
||||
started at any time by running `initctl start <service>`.
|
||||
|
||||
manual:yes
|
||||
|
||||
The name of a service, shown by the `initctl` tool, defaults to the
|
||||
basename of the service executable. It can be changed with the
|
||||
optional `name` argument:
|
||||
|
||||
name:<service-name>
|
||||
|
||||
* `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.
|
||||
@@ -225,6 +289,19 @@ Syntax
|
||||
* `include <CONF>`
|
||||
Include another configuration file. Absolute path required.
|
||||
|
||||
* `log size:200k count:5`
|
||||
|
||||
Log rotation for run/task/services using the `log` sub-option with
|
||||
redirection to a log file. Global setting, applies to all services.
|
||||
|
||||
The size can be given as bytes, without a specifier, or in `k`, `M`,
|
||||
or `G`, e.g. `size:10M`, or `size:3G`. A value of `size:0` disables
|
||||
log rotation. The default is `200k`.
|
||||
|
||||
The count value is recommended to be between 1-5, with a default 5.
|
||||
Setting count to 0 means the logfile will be truncated when the MAX
|
||||
size limit is reached.
|
||||
|
||||
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
|
||||
The first variant of this option uses the built-in getty on the given
|
||||
@@ -321,12 +398,18 @@ syslog using the native `logit` tool. The full syntax is:
|
||||
|
||||
log:/path/to/file
|
||||
log:prio:facility.level,tag:ident
|
||||
log:console
|
||||
log:null
|
||||
log
|
||||
|
||||
Default `prio` is `daemon.info` and default `tag` is the basename of the
|
||||
service or run/task command.
|
||||
|
||||
Log rotation is controlled using the global `log` setting.
|
||||
|
||||
**Example:**
|
||||
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
|
||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
||||
detailed description, see the [Conditions](conditions.md) document.
|
||||
@@ -350,6 +433,7 @@ network bringup and shutdown:
|
||||
- `network`, only at bootstrap
|
||||
- `runparts`, only at bootstrap
|
||||
- `include`
|
||||
- `log`, global setting
|
||||
- `shutdown`
|
||||
- `runlevel`, only at bootstrap
|
||||
- ... and all configuration stanzas from `/etc/finit.d` below
|
||||
|
||||
@@ -42,13 +42,11 @@ For your convenience a set of *optional* plugins are available:
|
||||
|
||||
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
|
||||
|
||||
* *hwclock.so*: Restore and save system clock from/to RTC on
|
||||
startup/shutdown.
|
||||
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
|
||||
|
||||
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
|
||||
|
||||
* *lost.so*: Very simple `HOOK_SVC_LOST` example. Logs process ID and
|
||||
name to syslog. _Optional plugin._
|
||||
* *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
|
||||
|
||||
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
||||
interfaces. These events are then sent to the Finit service monitor
|
||||
@@ -102,19 +100,6 @@ Hooks
|
||||
all modified/removed services have been stopped. When the hook has
|
||||
completed, Finit continues to start all modified and new services.
|
||||
|
||||
* `HOOK_SVC_LOST`: Called when a process is lost. When reconfiguring
|
||||
services at runtime this hook may be called a lot. However, it may be
|
||||
a quite useful hook to monitor a system post bootstrap when no, or
|
||||
few, services are expected to exit. A default plugin `lost.so` is
|
||||
available in the `plugins/` subdirectory as an example.
|
||||
|
||||
**NOTE:** This hook callback gets the lost PID as argument.
|
||||
|
||||
* `HOOK_SVC_START`: Like `HOOK_SVC_LOST`, but called when a process is
|
||||
started. Same caveats apply.
|
||||
|
||||
**NOTE:** This hook callback gets the new PID as argument.
|
||||
|
||||
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
|
||||
change. The hook is called when services not matching the new
|
||||
runlevel have been been stopped. When the hook has completed, Finit
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
libplug_la_SOURCES = bootmisc.c hwclock.c initctl.c pidfile.c procps.c tty.c urandom.c
|
||||
libplug_la_SOURCES = bootmisc.c rtc.c initctl.c pidfile.c procps.c tty.c urandom.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
@@ -39,8 +39,8 @@ libplug_la_SOURCES += time.c
|
||||
endif
|
||||
endif
|
||||
|
||||
if BUILD_LOST_PLUGIN
|
||||
libplug_la_SOURCES += lost.c
|
||||
if BUILD_MODULES_LOAD_PLUGIN
|
||||
libplug_la_SOURCES += modules-load.c
|
||||
endif
|
||||
|
||||
if BUILD_NETLINK_PLUGIN
|
||||
@@ -52,7 +52,7 @@ libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
else
|
||||
pkglib_LTLIBRARIES = bootmisc.la hwclock.la initctl.la pidfile.la procps.la tty.la urandom.la
|
||||
pkglib_LTLIBRARIES = bootmisc.la rtc.la initctl.la pidfile.la procps.la tty.la urandom.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
@@ -84,8 +84,8 @@ pkglib_LTLIBRARIES += time.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if BUILD_LOST_PLUGIN
|
||||
pkglib_LTLIBRARIES += lost.la
|
||||
if BUILD_MODULES_LOAD_PLUGIN
|
||||
pkglib_LTLIBRARIES += modules-load.la
|
||||
endif
|
||||
|
||||
if BUILD_NETLINK_PLUGIN
|
||||
|
||||
@@ -170,6 +170,9 @@ static void setup(void *arg)
|
||||
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
||||
chown("/dev/xconsole", 0, gid);
|
||||
|
||||
if (!fexist("/etc/mtab"))
|
||||
symlink("../proc/self/mounts", "/etc/mtab");
|
||||
|
||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
||||
uid = getuser("uuidd", NULL);
|
||||
if (uid >= 0) {
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
static void setup(void *arg)
|
||||
{
|
||||
char *cmd;
|
||||
char line[80];
|
||||
char line[256];
|
||||
|
||||
cmd = which(DAEMON);
|
||||
if (!cmd) {
|
||||
@@ -58,7 +58,7 @@ static void setup(void *arg)
|
||||
erase("/var/run/dbus/pid");
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345] %s %s -- %s", cmd, ARGS, DESC);
|
||||
snprintf(line, sizeof(line), "[S12345] log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/* Scans /etc/modules-load.d for modules to load.
|
||||
*
|
||||
* Copyright (c) 2018 Robert Andersson <robert.m.andersson@se.atlascopco.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "conf.h"
|
||||
|
||||
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||
#define SERVICE_LINE \
|
||||
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
||||
|
||||
static void load(void *arg)
|
||||
{
|
||||
FILE *fp;
|
||||
DIR *dirp;
|
||||
struct dirent *direntp;
|
||||
char line[256];
|
||||
int index = 1;
|
||||
|
||||
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
|
||||
|
||||
dirp = opendir(MODULES_LOAD_PATH);
|
||||
if (!dirp) return;
|
||||
|
||||
while ((direntp = readdir(dirp)))
|
||||
{
|
||||
char module_path[PATH_MAX];
|
||||
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
||||
strlcat(module_path, direntp->d_name, sizeof(module_path));
|
||||
|
||||
fp = fopen(module_path, "r");
|
||||
if (!fp) continue;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
char cmd[CMD_SIZE], *mod, *args;
|
||||
|
||||
mod = strip_line(line);
|
||||
if (!*mod) continue;
|
||||
|
||||
mod = chomp(mod);
|
||||
if (!mod || !*mod) continue;
|
||||
|
||||
mod = strtok_r(mod, " ", &args);
|
||||
|
||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
|
||||
index, mod, mod, args, mod);
|
||||
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = load
|
||||
},
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -37,12 +37,6 @@ struct context {
|
||||
int wd;
|
||||
};
|
||||
|
||||
static char *mkcond(char *buf, size_t len, char *nm)
|
||||
{
|
||||
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void pidfile_callback(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
@@ -70,7 +64,7 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
continue;
|
||||
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
cond_set(cond);
|
||||
} else if (ev->mask & IN_DELETE)
|
||||
@@ -79,41 +73,45 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
}
|
||||
|
||||
/*
|
||||
* Assert condition only if the service is running, but not if it's
|
||||
* recently been changed or while it's starting up.
|
||||
* This function is called after `initctl reload` to reassert conditions
|
||||
* for services that have not been changed.
|
||||
*
|
||||
* We must wait for the service to create/touch its pidfile.
|
||||
* We reassert the run/task/service's condition only if it is running,
|
||||
* but not if it's recently been changed or while it's starting up.
|
||||
*/
|
||||
static void pidfile_reconf(void *arg)
|
||||
{
|
||||
static char cond[MAX_COND_LEN];
|
||||
svc_t *svc, *iter = NULL;
|
||||
int restart = 0;
|
||||
|
||||
do {
|
||||
restart = 0;
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->state != SVC_RUNNING_STATE)
|
||||
continue;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
if (svc->state == SVC_RUNNING_STATE &&
|
||||
!svc_is_changed(svc) &&
|
||||
!svc_is_starting(svc) &&
|
||||
cond_get(cond) != COND_ON) {
|
||||
cond_set_path(cond_path(cond), COND_ON);
|
||||
restart = 1;
|
||||
}
|
||||
}
|
||||
if (svc_is_changed(svc) || svc_is_starting(svc))
|
||||
continue;
|
||||
|
||||
if (restart)
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
if (cond_get(cond) == COND_ON)
|
||||
continue;
|
||||
|
||||
} while (restart);
|
||||
cond_set_path(cond_path(cond), COND_ON);
|
||||
}
|
||||
|
||||
/*
|
||||
* This will call service_step(), which in turn will schedule itself
|
||||
* as long as stepped services change state. Services going from
|
||||
* WAITING to RUNNING will reassert their conditions in that loop,
|
||||
* which in turn may unlock other services, and so on.
|
||||
*/
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
|
||||
}
|
||||
|
||||
static void pidfile_init(void *arg)
|
||||
{
|
||||
char *path;
|
||||
struct context *ctx = arg;
|
||||
uint32_t mask = IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO;
|
||||
|
||||
/*
|
||||
* The bootmisc plugin is responsible for setting up /var/run.
|
||||
@@ -126,7 +124,7 @@ static void pidfile_init(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->wd = inotify_add_watch(ctx->fd, path, IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
||||
ctx->wd = inotify_add_watch(ctx->fd, path, mask);
|
||||
free(path);
|
||||
|
||||
if (ctx->wd < 0) {
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/* Save and restore RTC using hwclock
|
||||
*
|
||||
* Copyright (c) 2012-2019 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 <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/* Kernel RTC driver validates against this date for sanity check */
|
||||
#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */
|
||||
|
||||
static int rtc_open(void)
|
||||
{
|
||||
char *alt[] = {
|
||||
"/dev/rtc",
|
||||
"/dev/rtc0",
|
||||
"/dev/misc/rtc"
|
||||
};
|
||||
size_t i;
|
||||
int fd;
|
||||
|
||||
for (i = 0; i < NELEMS(alt); i++) {
|
||||
fd = open(alt[i], O_RDONLY);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rtc_save(void *arg)
|
||||
{
|
||||
struct timeval tv = { 0 };
|
||||
struct tm tm = { 0 };
|
||||
int fd, rc = 0;
|
||||
|
||||
fd = rtc_open();
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
rc = gettimeofday(&tv, NULL);
|
||||
if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
||||
print_desc(NULL, "System clock invalid, not saving to RTC");
|
||||
invalid:
|
||||
logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC");
|
||||
rc = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_desc(NULL, "Saving system time (UTC) to RTC");
|
||||
gmtime_r(&tv.tv_sec, &tm);
|
||||
if (ioctl(fd, RTC_SET_TIME, &tm) < 0) {
|
||||
if (EINVAL == errno)
|
||||
goto invalid;
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
print(rc, NULL);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void rtc_restore(void *arg)
|
||||
{
|
||||
struct timeval tv = { 0 };
|
||||
struct tm tm = { 0 };
|
||||
int fd, rc = 0;
|
||||
|
||||
fd = rtc_open();
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
|
||||
char msg[120];
|
||||
|
||||
snprintf(msg, sizeof(msg), "Failed restoring system clock, %s",
|
||||
EINVAL == errno ? "RTC time is too old" :
|
||||
ENOENT == errno ? "RTC has no saved time" : "see log for details");
|
||||
print_desc(NULL, msg);
|
||||
|
||||
invalid:
|
||||
logit(LOG_ERR, "Failed restoring system clock from RTC.");
|
||||
if (EINVAL == errno)
|
||||
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)");
|
||||
else if (ENOENT == errno)
|
||||
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
||||
else
|
||||
logit(LOG_ERR, "RTC error code %d: %m", errno);
|
||||
rc = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
||||
tv.tv_sec = mktime(&tm);
|
||||
if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
||||
errno = EINVAL;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (settimeofday(&tv, NULL) == -1)
|
||||
rc = 1;
|
||||
|
||||
out:
|
||||
print(rc, NULL);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = rtc_restore
|
||||
},
|
||||
.hook[HOOK_SHUTDOWN] = {
|
||||
.cb = rtc_save
|
||||
}
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -57,7 +57,7 @@ static void watcher(void *arg, int fd, int events)
|
||||
{
|
||||
int len = 0;
|
||||
char buf[EVENT_SIZE], name[42];
|
||||
tty_node_t *entry;
|
||||
struct tty *entry;
|
||||
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||
|
||||
while ((len = read(fd, buf, sizeof(buf)))) {
|
||||
@@ -72,11 +72,11 @@ static void watcher(void *arg, int fd, int events)
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
||||
entry = tty_find(name);
|
||||
if (entry && tty_enabled(&entry->data)) {
|
||||
if (entry && tty_enabled(entry)) {
|
||||
if (notified->mask & IN_CREATE)
|
||||
tty_start(&entry->data);
|
||||
else if (entry->data.pid)
|
||||
tty_stop(&entry->data);
|
||||
tty_start(entry);
|
||||
else if (entry->pid)
|
||||
tty_stop(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ finit
|
||||
initctl
|
||||
logit
|
||||
reboot
|
||||
watchdogd
|
||||
Makefile
|
||||
Makefile.in
|
||||
|
||||
@@ -10,11 +10,15 @@ endif
|
||||
|
||||
bin_PROGRAMS = logit
|
||||
sbin_PROGRAMS = finit initctl reboot
|
||||
pkglibexec_SCRIPTS = cgreaper.sh
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS = watchdogd
|
||||
endif
|
||||
|
||||
logit_SOURCES = logit.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
|
||||
finit_SOURCES = api.c \
|
||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
cond.c cond-w.c cond.h \
|
||||
telinit.c \
|
||||
conf.c conf.h \
|
||||
@@ -25,6 +29,7 @@ finit_SOURCES = api.c \
|
||||
mdadm.c mount.c \
|
||||
pid.c pid.h \
|
||||
plugin.c plugin.h private.h \
|
||||
schedule.c schedule.h \
|
||||
service.c service.h \
|
||||
sig.c sig.h \
|
||||
sm.c sm.h \
|
||||
@@ -36,15 +41,12 @@ pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h log.h plugin.h svc.h
|
||||
if INETD
|
||||
finit_SOURCES += inetd.c inetd.h
|
||||
endif
|
||||
if WATCHDOGD
|
||||
finit_SOURCES += watchdog.c watchdog.h
|
||||
endif
|
||||
|
||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
if STATIC
|
||||
finit_LDADD += plugins/libplug.la
|
||||
finit_LDADD += ../plugins/libplug.la
|
||||
else
|
||||
finit_LDADD += -ldl
|
||||
endif
|
||||
@@ -63,13 +65,19 @@ reboot_LDADD = $(lite_LIBS)
|
||||
INIT_LNKS = telinit init
|
||||
REBOOT_LNKS = shutdown halt poweroff suspend
|
||||
|
||||
if WATCHDOGD
|
||||
watchdogd_SOURCES = watchdog.c watchdog.h
|
||||
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
#log.c log.h
|
||||
endif
|
||||
|
||||
# Hook in install to add finit and reboot symlink(s)
|
||||
install-exec-hook:
|
||||
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
|
||||
@for file in $(INIT_LNKS); do \
|
||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ -e $$lnk ]; then \
|
||||
echo "Skipping $$lnk exists already."; \
|
||||
echo "Skipping $$lnk exists."; \
|
||||
else \
|
||||
$(LN_S) finit $$lnk; \
|
||||
fi; \
|
||||
@@ -78,11 +86,11 @@ install-exec-hook:
|
||||
echo "Non-finit reboot, skipping install."; \
|
||||
else \
|
||||
for file in $(REBOOT_LNKS); do \
|
||||
link=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ -e $$link ]; then \
|
||||
echo "$$link exists, skipping.";\
|
||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ -e $$lnk ]; then \
|
||||
echo "Skipping $$lnk exists."; \
|
||||
else \
|
||||
$(LN_S) reboot $$link; \
|
||||
$(LN_S) reboot $$lnk; \
|
||||
fi; \
|
||||
done; \
|
||||
fi
|
||||
@@ -101,10 +109,11 @@ uninstall-hook:
|
||||
echo "Non-finit reboot, skipping uninstall."; \
|
||||
else \
|
||||
for file in $(REBOOT_LNKS); do \
|
||||
link=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ "`readlink $$link`" != "reboot" ]; \
|
||||
then continue; \
|
||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||
dst=`readlink $$lnk`; \
|
||||
if [ "$$dst" != "reboot" ]; then \
|
||||
continue; \
|
||||
fi; \
|
||||
$(RM) $$link; \
|
||||
$(RM) $$lnk; \
|
||||
done; \
|
||||
fi
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "service.h"
|
||||
#include "util.h"
|
||||
|
||||
extern svc_t *wdog;
|
||||
static uev_t api_watcher;
|
||||
|
||||
static int call(int (*action)(svc_t *), char *buf, size_t len)
|
||||
@@ -51,7 +52,7 @@ static int call(int (*action)(svc_t *), char *buf, size_t len)
|
||||
return svc_parse_jobstr(buf, len, action, NULL);
|
||||
}
|
||||
|
||||
static int service_stop(svc_t *svc)
|
||||
static int stop(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
@@ -62,7 +63,7 @@ static int service_stop(svc_t *svc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_start(svc_t *svc)
|
||||
static int start(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
@@ -73,33 +74,35 @@ static int service_start(svc_t *svc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_restart(svc_t *svc)
|
||||
static int restart(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
if (svc_is_blocked(svc))
|
||||
svc_start(svc);
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_start (char *buf, size_t len) { return call(service_start, buf, len); }
|
||||
static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); }
|
||||
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
|
||||
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
|
||||
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
|
||||
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
|
||||
|
||||
static char query_buf[368];
|
||||
static int missing(char *job, int id)
|
||||
static int missing(char *job, char *id)
|
||||
{
|
||||
char buf[20];
|
||||
char idstr[10] = "";
|
||||
|
||||
if (!job)
|
||||
job = "";
|
||||
if (id > 1)
|
||||
snprintf(idstr, sizeof(idstr), ":%d", id);
|
||||
if (!id)
|
||||
id = "";
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s ", job, idstr);
|
||||
snprintf(buf, sizeof(buf), "%s:%s ", job, id);
|
||||
strlcat(query_buf, buf, sizeof(query_buf));
|
||||
|
||||
return 1;
|
||||
@@ -118,8 +121,9 @@ static int do_query(struct init_request *rq, size_t len)
|
||||
|
||||
static svc_t *do_find(char *buf, size_t len)
|
||||
{
|
||||
int id = 1;
|
||||
char *id = NULL;
|
||||
char *ptr, *input;
|
||||
svc_t *iter = NULL;
|
||||
|
||||
input = sanitize(buf, len);
|
||||
if (!input)
|
||||
@@ -128,11 +132,28 @@ static svc_t *do_find(char *buf, size_t len)
|
||||
ptr = strchr(input, ':');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
id = atonum(ptr);
|
||||
id = ptr;
|
||||
}
|
||||
|
||||
if (isdigit(input[0]))
|
||||
return svc_find_by_jobid(atonum(input), id);
|
||||
if (isdigit(input[0])) {
|
||||
char *ep;
|
||||
long job = 0;
|
||||
|
||||
errno = 0;
|
||||
job = strtol(input, &ep, 10);
|
||||
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
|
||||
(errno != 0 && job == 0) ||
|
||||
(input == ep))
|
||||
return NULL;
|
||||
|
||||
if (!id)
|
||||
return svc_job_iterator(&iter, 1, job);
|
||||
|
||||
return svc_find_by_jobid(job, id);
|
||||
}
|
||||
|
||||
if (!id)
|
||||
return svc_named_iterator(&iter, 1, input);
|
||||
|
||||
return svc_find_by_nameid(input, id);
|
||||
}
|
||||
@@ -337,13 +358,23 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
break;
|
||||
}
|
||||
|
||||
if (wdogpid > 0 && wdogpid != rq.runlevel) {
|
||||
_d("Sending SIGTERM to %d", wdogpid);
|
||||
kill(wdogpid, SIGTERM);
|
||||
do_sleep(1);
|
||||
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
||||
svc = svc_find_by_pid(rq.runlevel);
|
||||
if (!svc) {
|
||||
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
||||
break;
|
||||
}
|
||||
_d("wdog was %d, now %d is in charge", wdogpid, rq.runlevel);
|
||||
wdogpid = rq.runlevel;
|
||||
|
||||
/* Disable and allow Finit to collect bundled watchdog */
|
||||
if (wdog) {
|
||||
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
|
||||
stop(wdog);
|
||||
if (wdog->protect) {
|
||||
wdog->protect = 0;
|
||||
wdog->runlevels = 0;
|
||||
}
|
||||
}
|
||||
wdog = svc;
|
||||
break;
|
||||
|
||||
case INIT_CMD_SVC_ITER:
|
||||
@@ -429,6 +460,8 @@ error:
|
||||
|
||||
int api_exit(void)
|
||||
{
|
||||
uev_io_stop(&api_watcher);
|
||||
|
||||
return close(api_watcher.fd);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
# Called by kernel when last child in cgroup exists
|
||||
|
||||
BASE=/sys/fs/cgroup/finit
|
||||
PROC=`basename $1`
|
||||
DIR="$BASE$1"
|
||||
|
||||
logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
|
||||
rmdir $DIR
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,219 @@
|
||||
/* Finit control group support functions
|
||||
*
|
||||
* Copyright (c) 2019 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 <string.h>
|
||||
#include <lite/lite.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static struct {
|
||||
int active;
|
||||
char cgroup[16];
|
||||
char path[32];
|
||||
} cg_avail[200];
|
||||
|
||||
static int cg_init = 0;
|
||||
static int cg_num = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Called by Finit at early boot to mount initial cgroups
|
||||
*/
|
||||
void cgroup_init(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[80];
|
||||
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||
|
||||
fp = fopen("/proc/cgroups", "r");
|
||||
if (!fp) {
|
||||
_d("No cgroup support");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
|
||||
_d("Failed mounting cgroups");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Skip first line, header */
|
||||
fgets(buf, sizeof(buf), fp);
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *cgroup;
|
||||
char rc[80];
|
||||
|
||||
cgroup = strtok(buf, "\t ");
|
||||
if (!cgroup)
|
||||
continue;
|
||||
|
||||
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
||||
mkdir(rc, 0755);
|
||||
if (mount("cgroup", rc, "cgroup", opts, cgroup)) {
|
||||
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
cg_avail[cg_num].active = 1;
|
||||
strlcpy(cg_avail[cg_num].cgroup, cgroup, sizeof(cg_avail[cg_num].cgroup));
|
||||
strlcpy(cg_avail[cg_num].path, rc, sizeof(cg_avail[cg_num].path));
|
||||
cg_num++;
|
||||
|
||||
/* Create default group in all available cgroups */
|
||||
strlcat(rc, "/default", sizeof(rc));
|
||||
mkdir(rc, 0755);
|
||||
}
|
||||
|
||||
/* Default cgroups for process monitoring */
|
||||
mkdir("/sys/fs/cgroup/finit", 0755);
|
||||
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
|
||||
_pe("Failed mounting Finit cgroup hierarchy");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set up reaper and enable callbacks */
|
||||
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
||||
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
|
||||
|
||||
/* Default groups, PID 1, services, and user/login processes */
|
||||
mkdir("/sys/fs/cgroup/finit/init", 0755);
|
||||
mkdir("/sys/fs/cgroup/finit/system", 0755);
|
||||
mkdir("/sys/fs/cgroup/finit/user", 0755);
|
||||
|
||||
/* Move ourselves to init */
|
||||
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
|
||||
|
||||
/* We have signal, main screen turn on! */
|
||||
cg_init = 1;
|
||||
fail:
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
int cgroup_add(char *name, void (*cb)(char *, void *), void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cg_num; i++) {
|
||||
char path[80];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", cg_avail[i].path, name);
|
||||
if (mkdir(path, 0755) && EEXIST != errno) {
|
||||
rc++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* XXX: Add to cache of added groups, for cgroup_find() */
|
||||
if (cb)
|
||||
cb(path, arg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: Temporary hackish implementation */
|
||||
int cgroup_find(char *name, char *group, size_t len)
|
||||
{
|
||||
char path[80];
|
||||
|
||||
if (cg_num <= 0)
|
||||
return 1;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", cg_avail[0].path, name);
|
||||
if (access(path, F_OK))
|
||||
return 1;
|
||||
|
||||
strlcpy(group, name, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int cgroup_cpuset_range(char *group, int min, int max) */
|
||||
/* { */
|
||||
/* } */
|
||||
|
||||
static int move_pid(char *group, char *name, int pid)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||
if (mkdir(path, 0755) && errno != EEXIST)
|
||||
return 1;
|
||||
|
||||
strlcat(path, "/cgroup.procs", sizeof(path));
|
||||
|
||||
return echo(path, 0, "%d", pid);
|
||||
}
|
||||
|
||||
int cgroup_user(char *name)
|
||||
{
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
return move_pid("finit/user", name, getpid());
|
||||
}
|
||||
|
||||
int cgroup_service(char *cmd, int pid)
|
||||
{
|
||||
char *nm;
|
||||
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
if (pid <= 0) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
nm = strrchr(cmd, '/');
|
||||
if (nm)
|
||||
nm++;
|
||||
else
|
||||
nm = cmd;
|
||||
|
||||
return move_pid("finit/system", nm, pid);
|
||||
}
|
||||
|
||||
int cgroup_assign(char *name, int pid)
|
||||
{
|
||||
char path[120];
|
||||
int i, rc = 0;
|
||||
|
||||
for (i = 0; i < cg_num; i++) {
|
||||
snprintf(path, sizeof(path), "%s/%s/cgroup.procs",
|
||||
cg_avail[i].path, name);
|
||||
rc += echo(path, 0, "%d", pid);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Finit control group support functions
|
||||
*
|
||||
* Copyright (c) 2019 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_CGROUP_H_
|
||||
#define FINIT_CGROUP_H_
|
||||
|
||||
void cgroup_init (void);
|
||||
|
||||
int cgroup_add (char *name, void (*cb)(char *, void *), void *arg);
|
||||
int cgroup_find (char *name, char *group, size_t len);
|
||||
|
||||
int cgroup_user (char *name);
|
||||
int cgroup_service (char *cmd, int pid);
|
||||
int cgroup_assign (char *name, int pid);
|
||||
|
||||
#endif /* FINIT_CGROUP_H_ */
|
||||
@@ -125,7 +125,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
svc_t *client_svc_find(char *arg)
|
||||
svc_t *client_svc_find(const char *arg)
|
||||
{
|
||||
int sd = -1;
|
||||
struct init_request rq = {
|
||||
|
||||
@@ -32,6 +32,6 @@ int client_disconnect (void);
|
||||
|
||||
int client_send (struct init_request *rq, ssize_t len);
|
||||
svc_t *client_svc_iterator (int first);
|
||||
svc_t *client_svc_find (char *arg);
|
||||
svc_t *client_svc_find (const char *arg);
|
||||
|
||||
#endif /* FINIT_CLIENT_H_ */
|
||||
|
||||
@@ -28,14 +28,19 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
static int cond_set_gen(const char *path, unsigned int gen)
|
||||
static int cond_set_gen(const char *file, unsigned int gen)
|
||||
{
|
||||
char *ptr, path[256];
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
fp = fopen(path, "w");
|
||||
/* /var/run --> /run symlink may not exist (yet) */
|
||||
ptr = pid_runpath(file, path, sizeof(path));
|
||||
|
||||
fp = fopen(ptr, "w");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
@@ -147,7 +152,7 @@ static void cond_update(const char *name)
|
||||
}
|
||||
|
||||
void cond_set(const char *name)
|
||||
{
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
@@ -205,7 +210,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
if (tflg != FTW_F)
|
||||
return 0;
|
||||
|
||||
nm = (char *)fpath + sizeof(COND_PATH);
|
||||
nm = strstr((char *)fpath, COND_DIR);
|
||||
if (!nm) {
|
||||
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
nm += sizeof(COND_DIR);
|
||||
_d("Reasserting %s => %s", fpath, nm);
|
||||
cond_set(nm);
|
||||
|
||||
@@ -224,7 +235,9 @@ void cond_reassert(const char *type)
|
||||
|
||||
void cond_init(void)
|
||||
{
|
||||
if (makepath(COND_PATH) && errno != EEXIST) {
|
||||
char path[MAX_ARG_LEN];
|
||||
|
||||
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,16 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
|
||||
char *mkcond(char *buf, size_t len, char *nm)
|
||||
{
|
||||
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *condstr(enum cond_state s)
|
||||
{
|
||||
static const char *strs[] = {
|
||||
@@ -42,20 +49,25 @@ const char *condstr(enum cond_state s)
|
||||
|
||||
const char *cond_path(const char *name)
|
||||
{
|
||||
char tmp[MAX_ARG_LEN];
|
||||
static char file[MAX_ARG_LEN];
|
||||
|
||||
snprintf(file, sizeof(file), COND_PATH "/%s", name);
|
||||
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
|
||||
|
||||
return file;
|
||||
return pid_runpath(tmp, file, sizeof(file));
|
||||
}
|
||||
|
||||
unsigned int cond_get_gen(const char *path)
|
||||
unsigned int cond_get_gen(const char *file)
|
||||
{
|
||||
char *ptr, path[256];
|
||||
unsigned int gen;
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
/* /var/run --> /run symlink may not exist (yet) */
|
||||
ptr = pid_runpath(file, path, sizeof(path));
|
||||
|
||||
fp = fopen(ptr, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
@@ -89,7 +101,7 @@ enum cond_state cond_get_agg(const char *names)
|
||||
{
|
||||
char *cond;
|
||||
enum cond_state s = COND_ON;
|
||||
static char conds[MAX_ARG_LEN];
|
||||
static char conds[MAX_COND_LEN];
|
||||
|
||||
if (!names)
|
||||
return COND_ON;
|
||||
@@ -104,7 +116,7 @@ enum cond_state cond_get_agg(const char *names)
|
||||
int cond_affects(const char *name, const char *names)
|
||||
{
|
||||
char *cond;
|
||||
static char conds[MAX_ARG_LEN];
|
||||
static char conds[MAX_COND_LEN];
|
||||
|
||||
if (!name || !names)
|
||||
return 0;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <paths.h>
|
||||
|
||||
#define COND_PATH _PATH_VARRUN "finit/cond"
|
||||
#define COND_DIR "finit/cond"
|
||||
#define COND_PATH _PATH_VARRUN COND_DIR
|
||||
#define COND_RECONF COND_PATH "/reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
@@ -12,6 +13,7 @@ typedef enum cond_state {
|
||||
COND_ON
|
||||
} cond_state_t;
|
||||
|
||||
char *mkcond (char *buf, size_t len, char *nm);
|
||||
const char *condstr (enum cond_state s);
|
||||
const char *cond_path (const char *name);
|
||||
unsigned int cond_get_gen (const char *path);
|
||||
|
||||
@@ -30,17 +30,23 @@
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#include <sys/time.h>
|
||||
#include <glob.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
|
||||
#define BOOTSTRAP (runlevel == 0)
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
|
||||
int logfile_size_max = 200000; /* 200 kB */
|
||||
int logfile_count_max = 5;
|
||||
|
||||
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
struct conf_change {
|
||||
@@ -48,7 +54,8 @@ struct conf_change {
|
||||
char *name;
|
||||
};
|
||||
|
||||
static uev_t w1, w2, w3;
|
||||
static char cgroup[32];
|
||||
static uev_t w1, w2, w3, w4;
|
||||
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
||||
|
||||
static int parse_conf(char *file);
|
||||
@@ -263,7 +270,7 @@ char *rlim2str(int rlim)
|
||||
|
||||
char *lim2str(struct rlimit *rlim)
|
||||
{
|
||||
char tmp[20];
|
||||
char tmp[25];
|
||||
static char buf[42];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
@@ -337,6 +344,45 @@ error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
}
|
||||
|
||||
static void cpuset_cb(char *grp, void *arg)
|
||||
{
|
||||
int cpuset = *(int *)arg;
|
||||
char path[128];
|
||||
|
||||
if (!strstr(grp, "cpuset"))
|
||||
return;
|
||||
|
||||
/* Both .cpus and .mems are mandatory settings */
|
||||
snprintf(path, sizeof(path), "%s/cpuset.cpus", grp);
|
||||
echo(path, 0, "%d", cpuset);
|
||||
snprintf(path, sizeof(path), "%s/cpuset.mems", grp);
|
||||
echo(path, 0, "0");
|
||||
|
||||
/* XXX: HACK for now, fix proper CPU allocation */
|
||||
snprintf(path, sizeof(path), "%s/cpuset.cpu_exclusive", grp);
|
||||
echo(path, 0, "1");
|
||||
}
|
||||
|
||||
static void parse_cgroup(char *line)
|
||||
{
|
||||
char *group, *token;
|
||||
int cpuset = -1;
|
||||
|
||||
group = strtok(line, " ");
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
while ((token = strtok(NULL, " "))) {
|
||||
if (!strncmp(token, "cpuset:", 7)) {
|
||||
token += 7;
|
||||
cpuset = atoi(token); /* XXX: fixme */
|
||||
}
|
||||
}
|
||||
|
||||
if (cpuset >= 0)
|
||||
cgroup_add(group, cpuset_cb, &cpuset);
|
||||
}
|
||||
|
||||
static void parse_static(char *line)
|
||||
{
|
||||
char *x;
|
||||
@@ -383,6 +429,26 @@ static void parse_static(char *line)
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "log ", x)) {
|
||||
char *tok;
|
||||
static int size = 200000, count = 5;
|
||||
|
||||
tok = strtok(x, ":= ");
|
||||
while (tok) {
|
||||
if (!strncmp(tok, "size", 4))
|
||||
size = strtobytes(strtok(NULL, ":= "));
|
||||
else if (!strncmp(tok, "count", 5))
|
||||
count = strtobytes(strtok(NULL, ":= "));
|
||||
|
||||
tok = strtok(NULL, ":= ");
|
||||
}
|
||||
|
||||
if (size >= 0)
|
||||
logfile_size_max = size;
|
||||
if (count >= 0)
|
||||
logfile_count_max = count;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "shutdown ", x)) {
|
||||
if (sdown) free(sdown);
|
||||
sdown = strdup(strip_line(x));
|
||||
@@ -405,6 +471,11 @@ static void parse_static(char *line)
|
||||
cfglevel = 2; /* Fallback */
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "cgroup ", x)) {
|
||||
parse_cgroup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
@@ -423,26 +494,26 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
|
||||
/* Monitored daemon, will be respawned on exit */
|
||||
if (MATCH_CMD(line, "service ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, x, rlimit, file);
|
||||
service_register(SVC_TYPE_SERVICE, x, rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* One-shot task, will not be respawned */
|
||||
if (MATCH_CMD(line, "task ", x)) {
|
||||
service_register(SVC_TYPE_TASK, x, rlimit, file);
|
||||
service_register(SVC_TYPE_TASK, x, rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Like task but waits for completion, useful w/ [S] */
|
||||
if (MATCH_CMD(line, "run ", x)) {
|
||||
service_register(SVC_TYPE_RUN, x, rlimit, file);
|
||||
service_register(SVC_TYPE_RUN, x, rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Classic inetd service */
|
||||
if (MATCH_CMD(line, "inetd ", x)) {
|
||||
#ifdef INETD_ENABLED
|
||||
service_register(SVC_TYPE_INETD, x, rlimit, file);
|
||||
service_register(SVC_TYPE_INETD, x, rlimit, cgroup, file);
|
||||
#else
|
||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||
#endif
|
||||
@@ -455,9 +526,15 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
return;
|
||||
}
|
||||
|
||||
/* All subsequent run/task/service/tty/etc. directives to use this cgroup */
|
||||
if (MATCH_CMD(line, "group ", x)) {
|
||||
cgroup_find(x, cgroup, sizeof(cgroup));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Regular or serial TTYs to run getty */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
tty_register(strip_line(x), rlimit, file);
|
||||
tty_register(strip_line(x), rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -475,8 +552,8 @@ static void tabstospaces(char *line)
|
||||
|
||||
static int parse_conf_dynamic(char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
@@ -487,6 +564,9 @@ static int parse_conf_dynamic(char *file)
|
||||
/* Prepare default limits for each service */
|
||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||
|
||||
/* Prepare default cgroup for each service */
|
||||
strlcpy(cgroup, "default", sizeof(cgroup));
|
||||
|
||||
_d("Parsing %s <<<<<<", file);
|
||||
while (!feof(fp)) {
|
||||
char line[LINE_SIZE] = "";
|
||||
@@ -553,8 +633,8 @@ static int parse_conf(char *file)
|
||||
*/
|
||||
int conf_reload(void)
|
||||
{
|
||||
int i, num;
|
||||
struct dirent **e;
|
||||
size_t i;
|
||||
glob_t gl;
|
||||
|
||||
/* Mark and sweep */
|
||||
svc_mark_dynamic();
|
||||
@@ -567,7 +647,7 @@ int conf_reload(void)
|
||||
/* If rescue.conf is missing, fall back to a root shell */
|
||||
rc = parse_conf(RESCUE_CONF);
|
||||
if (rc)
|
||||
tty_register(line, global_rlimit, NULL);
|
||||
tty_register(line, global_rlimit, cgroup, NULL);
|
||||
|
||||
print(rc, "Entering rescue mode");
|
||||
goto done;
|
||||
@@ -577,20 +657,14 @@ int conf_reload(void)
|
||||
parse_conf(FINIT_CONF);
|
||||
|
||||
/* Next, read all *.conf in /etc/finit.d/ */
|
||||
num = scandir(rcsd, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("Skipping %s, no files found ...", rcsd);
|
||||
goto done;
|
||||
}
|
||||
glob("/etc/finit.d/*.conf", 0, NULL, &gl);
|
||||
glob("/etc/finit.d/enabled/*.conf", GLOB_APPEND, NULL, &gl);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char *name = e[i]->d_name;
|
||||
char path[LINE_SIZE];
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
char *path = gl.gl_pathv[i];
|
||||
size_t len;
|
||||
struct stat st;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", rcsd, name);
|
||||
|
||||
/* Check that it's an actual file ... beyond any symlinks */
|
||||
if (lstat(path, &st)) {
|
||||
_d("Skipping %s, cannot access: %s", path, strerror(errno));
|
||||
@@ -626,9 +700,7 @@ int conf_reload(void)
|
||||
parse_conf_dynamic(path);
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
globfree(&gl);
|
||||
|
||||
done:
|
||||
/* Drop record of all .conf changes */
|
||||
@@ -703,6 +775,7 @@ static int do_change(char *name, uint32_t mask)
|
||||
return 1;
|
||||
}
|
||||
|
||||
_d("Event registered for %s, mask 0x%x", name, mask);
|
||||
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
|
||||
|
||||
return 0;
|
||||
@@ -758,6 +831,9 @@ static void conf_cb(uev_t *w, void *arg, int events)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
|
||||
@@ -832,8 +908,9 @@ int conf_monitor(uev_ctx_t *ctx)
|
||||
* changes to either symlink or target.
|
||||
*/
|
||||
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
|
||||
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW);
|
||||
rc += add_watcher(ctx, &w3, FINIT_CONF, 0);
|
||||
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available/", IN_DONT_FOLLOW);
|
||||
rc += add_watcher(ctx, &w3, FINIT_RCSD "/enabled/", 0);
|
||||
rc += add_watcher(ctx, &w4, FINIT_CONF, 0);
|
||||
|
||||
return rc + conf_reload();
|
||||
}
|
||||
@@ -844,7 +921,7 @@ int conf_monitor(uev_ctx_t *ctx)
|
||||
int conf_init(void)
|
||||
{
|
||||
hostname = strdup(DEFHOST);
|
||||
w1.fd = w2.fd = w3.fd = -1;
|
||||
w1.fd = w2.fd = w3.fd = w4.fd = -1;
|
||||
|
||||
return conf_monitor(NULL);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
extern int logfile_size_max;
|
||||
extern int logfile_count_max;
|
||||
|
||||
extern struct rlimit global_rlimit[];
|
||||
|
||||
int str2rlim(char *str);
|
||||
|
||||
@@ -363,7 +363,7 @@ pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, stru
|
||||
if (!pid) {
|
||||
speed_t speed = B38400;
|
||||
|
||||
if (baud) {
|
||||
if (baud && baud[0]) {
|
||||
speed = stty_parse_speed(baud);
|
||||
if (B0 == speed)
|
||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||
@@ -449,14 +449,18 @@ int run_parts(char *dir, char *cmd)
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
int j = 0;
|
||||
pid_t pid = 0;
|
||||
struct stat st;
|
||||
char *args[NUM_ARGS];
|
||||
char path[512];
|
||||
char *argv[4] = {
|
||||
"sh",
|
||||
"-c",
|
||||
path,
|
||||
NULL
|
||||
};
|
||||
char *name = e[i]->d_name;
|
||||
char path[LINE_SIZE];
|
||||
pid_t pid = 0;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
snprintf(path, sizeof(path), "%s/%s ", dir, name);
|
||||
if (stat(path, &st)) {
|
||||
_d("Failed stat(%s): %s", path, strerror(errno));
|
||||
continue;
|
||||
@@ -467,29 +471,22 @@ int run_parts(char *dir, char *cmd)
|
||||
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";
|
||||
}
|
||||
if (name[0] == 'S' && isdigit(name[1]))
|
||||
strlcat(path, "start", sizeof(path));
|
||||
else if (name[0] == 'K' && isdigit(name[1]))
|
||||
strlcat(path, "stop", sizeof(path));
|
||||
} else {
|
||||
args[j++] = cmd;
|
||||
strlcat(path, cmd, sizeof(path));
|
||||
}
|
||||
args[j++] = NULL;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
_d("Calling %s ...", path);
|
||||
sig_unblock();
|
||||
execv(path, args);
|
||||
exit(0);
|
||||
return execvp(_PATH_BSHELL, argv);
|
||||
}
|
||||
|
||||
complete(path, pid);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
@@ -47,9 +48,8 @@
|
||||
#include "tty.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
#include "watchdog.h"
|
||||
#include "schedule.h"
|
||||
|
||||
int wdogpid = 0; /* No watchdog by default */
|
||||
int runlevel = 0; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
int prevlevel = -1;
|
||||
@@ -63,6 +63,10 @@ char *rcsd = FINIT_RCSD;
|
||||
char *runparts = NULL;
|
||||
|
||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||
svc_t *wdog = NULL; /* No watchdog by default */
|
||||
|
||||
static int udev = 0; /* Runtime detection of udev */
|
||||
|
||||
|
||||
/*
|
||||
* Show user configured banner before service bootstrap progress
|
||||
@@ -81,7 +85,7 @@ static void banner(void)
|
||||
return;
|
||||
|
||||
memset(separator, '=', sizeof(separator));
|
||||
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
||||
fprintf(stderr, "\n\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
||||
}
|
||||
|
||||
static int ismnt(char *file, char *dir)
|
||||
@@ -214,15 +218,20 @@ static void emergency_shell(void)
|
||||
*/
|
||||
static void finalize(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
/*
|
||||
* Track bundled watchdogd in case a better one turns up
|
||||
*/
|
||||
svc = svc_find(FINIT_LIBPATH_ "/watchdogd", "1");
|
||||
if (svc)
|
||||
wdog = svc;
|
||||
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts) && !rescue) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
if (runparts && fisdir(runparts) && !rescue)
|
||||
run_parts(runparts, NULL);
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel
|
||||
@@ -240,11 +249,8 @@ static void finalize(void)
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Convenient SysV compat for when you just don't care ... */
|
||||
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue) {
|
||||
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
|
||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
_d("Calling all system up hooks ...");
|
||||
@@ -260,13 +266,61 @@ static void finalize(void)
|
||||
tty_runlevel();
|
||||
}
|
||||
|
||||
/*
|
||||
* Start cranking the big state machine
|
||||
*/
|
||||
static void crank_worker(void *unused)
|
||||
{
|
||||
/*
|
||||
* Initalize state machine and start all bootstrap tasks
|
||||
* NOTE: no network available!
|
||||
*/
|
||||
sm_init(&sm);
|
||||
sm_step(&sm);
|
||||
|
||||
/* Debian has this little script to copy generated rules while the system was read-only */
|
||||
if (udev && fexist("/lib/udev/udev-finish"))
|
||||
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||
* allowed to complete their work in [S], or timeout, before we call
|
||||
* finalize(), should not take more than 120 sec.
|
||||
*/
|
||||
static void final_worker(void *work)
|
||||
{
|
||||
static int cnt = 120;
|
||||
|
||||
_d("Step all services ...");
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
if (cnt-- > 0 && !service_completed()) {
|
||||
_d("Not all bootstrap run/tasks have completed yet ... %d", cnt);
|
||||
schedule_work(work);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cnt > 0)
|
||||
_d("All run/task have completed, resuming bootstrap.");
|
||||
else
|
||||
_d("Timeout, resuming bootstrap.");
|
||||
|
||||
finalize();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
struct wq crank = {
|
||||
.cb = crank_worker
|
||||
};
|
||||
struct wq final = {
|
||||
.cb = final_worker,
|
||||
.delay = 1000
|
||||
};
|
||||
uev_ctx_t loop;
|
||||
char *path;
|
||||
char cmd[256];
|
||||
int udev = 0;
|
||||
uev_t timer; /* Bootstrap timer, on timeout call finalize() */
|
||||
uev_ctx_t loop;
|
||||
|
||||
/*
|
||||
* finit/init/telinit client tool uses /dev/initctl pipe
|
||||
@@ -278,24 +332,32 @@ int main(int argc, char* argv[])
|
||||
/*
|
||||
* Initalize event context.
|
||||
*/
|
||||
uev_init(&loop);
|
||||
uev_init1(&loop, 1);
|
||||
ctx = &loop;
|
||||
|
||||
/*
|
||||
* Set the PATH early to something sane
|
||||
* Set PATH and SHELL early to something sane
|
||||
*/
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
setenv("SHELL", _PATH_BSHELL, 1);
|
||||
|
||||
/*
|
||||
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
||||
*/
|
||||
chdir("/");
|
||||
umask(0);
|
||||
mount("none", "/proc", "proc", 0, NULL);
|
||||
mount("none", "/sys", "sysfs", 0, NULL);
|
||||
if (mount("none", "/proc", "proc", 0, NULL))
|
||||
_pe("Failed mounting /proc");
|
||||
if (mount("none", "/sys", "sysfs", 0, NULL))
|
||||
_pe("Failed mounting /sysfs");
|
||||
if (fisdir("/proc/bus/usb"))
|
||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* Initialize default control groups, if available
|
||||
*/
|
||||
cgroup_init();
|
||||
|
||||
/*
|
||||
* Parse kernel command line (debug, rescue, splash, etc.)
|
||||
* Also calls log_init() to set correct log level
|
||||
@@ -303,7 +365,7 @@ int main(int argc, char* argv[])
|
||||
conf_parse_cmdline();
|
||||
|
||||
/* Set up canvas */
|
||||
if (!rescue)
|
||||
if (!rescue && !log_is_debug())
|
||||
screen_init();
|
||||
|
||||
/*
|
||||
@@ -320,7 +382,7 @@ int main(int argc, char* argv[])
|
||||
* Load plugins early, finit.conf may contain references to
|
||||
* features implemented by plugins.
|
||||
*/
|
||||
plugin_init(&loop, PLUGIN_PATH);
|
||||
plugin_init(&loop);
|
||||
|
||||
/*
|
||||
* Hello world.
|
||||
@@ -372,54 +434,56 @@ int main(int argc, char* argv[])
|
||||
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
|
||||
umask(022);
|
||||
|
||||
/* Bootstrap conditions, needed for hooks */
|
||||
cond_init();
|
||||
|
||||
/*
|
||||
* Populate /dev and prepare for runtime events from kernel.
|
||||
* Prefer udev if mdev is also available on the system.
|
||||
*/
|
||||
path = which("mdev");
|
||||
path = which("udevd");
|
||||
if (!path)
|
||||
path = which("/lib/systemd/systemd-udevd");
|
||||
if (path) {
|
||||
/* Embedded Linux systems usually have BusyBox mdev */
|
||||
if (log_is_debug())
|
||||
touch("/dev/mdev.log");
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "%s -s", path);
|
||||
} else {
|
||||
/* Desktop and server distros usually have a variant of udev */
|
||||
path = which("udevd");
|
||||
if (!path)
|
||||
path = which("/lib/systemd/systemd-udevd");
|
||||
if (path) {
|
||||
udev = 1;
|
||||
udev = 1;
|
||||
|
||||
/* Register udevd as a monitored service, started much later */
|
||||
snprintf(cmd, sizeof(cmd), "[12345] %s -- Device event manager daemon", path);
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
||||
_pe("Failed registering %s", path);
|
||||
udev = 0;
|
||||
}
|
||||
/* Register udevd as a monitored service */
|
||||
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, "default", NULL)) {
|
||||
_pe("Failed registering %s", path);
|
||||
udev = 0;
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
|
||||
"udevadm trigger -c add -t devices "
|
||||
"-- Requesting device events", path);
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||
|
||||
/* Start a temporary udevd instance to populate /dev */
|
||||
snprintf(cmd, sizeof(cmd), "%s --daemon", path);
|
||||
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
|
||||
"udevadm trigger -c add -t subsystems "
|
||||
"-- Requesting subsystem events", path);
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (path) {
|
||||
free(path);
|
||||
} else {
|
||||
path = which("mdev");
|
||||
if (path) {
|
||||
/* Embedded Linux systems usually have BusyBox mdev */
|
||||
if (log_is_debug())
|
||||
touch("/dev/mdev.log");
|
||||
|
||||
run_interactive(cmd, "Populating device tree");
|
||||
if (udev && whichp("udevadm")) {
|
||||
run("udevadm trigger --action=add --type=subsystems");
|
||||
run("udevadm trigger --action=add --type=devices");
|
||||
run("udevadm settle --timeout=120");
|
||||
snprintf(cmd, sizeof(cmd), "%s -s", path);
|
||||
free(path);
|
||||
|
||||
/* Tell temporary udevd to exit, we'll start a monitored instance later */
|
||||
run("udevadm control --exit");
|
||||
run_interactive(cmd, "Populating device tree");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Start built-in watchdog as soon as possible, if enabled
|
||||
* Start bundled watchdogd as soon as possible, if enabled
|
||||
*/
|
||||
wdogpid = watchdog(argv[0]);
|
||||
if (which(FINIT_LIBPATH_ "/watchdogd"))
|
||||
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd", global_rlimit, "default", NULL);
|
||||
|
||||
/*
|
||||
* Mount filesystems
|
||||
@@ -433,13 +497,6 @@ int main(int argc, char* argv[])
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Debian has this little script to copy generated rules while the system was read-only */
|
||||
if (fexist("/lib/udev/udev-finish"))
|
||||
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
||||
|
||||
/* Bootstrap conditions, needed for hooks */
|
||||
cond_init();
|
||||
|
||||
if (!rescue) {
|
||||
_d("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
@@ -448,6 +505,9 @@ int main(int argc, char* argv[])
|
||||
if (run_interactive("mount -na", "Mounting filesystems"))
|
||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||
|
||||
_d("Calling extra mount hook, after mount -a ...");
|
||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||
|
||||
run("swapon -ea");
|
||||
umask(0022);
|
||||
}
|
||||
@@ -455,37 +515,29 @@ int main(int argc, char* argv[])
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
/*
|
||||
* Set up inotify watcher for /etc/finit.d and read all .conf
|
||||
* files to figure out how to bootstrap the system.
|
||||
*/
|
||||
conf_monitor(&loop);
|
||||
|
||||
if (!rescue) {
|
||||
_d("Base FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initalize state machine and start all bootstrap tasks
|
||||
* NOTE: no network available!
|
||||
* Set up inotify watcher for /etc/finit.d and read all .conf
|
||||
* files to figure out how to bootstrap the system.
|
||||
*/
|
||||
sm_init(&sm);
|
||||
sm_step(&sm);
|
||||
conf_monitor(&loop);
|
||||
|
||||
/* Start new initctl API responder */
|
||||
_d("Starting initctl API responder ...");
|
||||
api_init(&loop);
|
||||
umask(022);
|
||||
|
||||
/*
|
||||
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
|
||||
* [S], or timeout, before calling finalize()
|
||||
*/
|
||||
_d("Starting the big state machine ...");
|
||||
schedule_work(&crank);
|
||||
|
||||
_d("Starting bootstrap finalize timer ...");
|
||||
uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000);
|
||||
schedule_work(&final);
|
||||
|
||||
/*
|
||||
* Enter main loop to monior /dev/initctl and services
|
||||
* Enter main loop to monitor /dev/initctl and services
|
||||
*/
|
||||
_d("Entering main loop ...");
|
||||
return uev_run(&loop, 0);
|
||||
|
||||
@@ -92,7 +92,6 @@ struct init_request {
|
||||
char data[368];
|
||||
};
|
||||
|
||||
extern int wdogpid;
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int prevlevel;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <termios.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
@@ -192,6 +193,7 @@ static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
cgroup_user(name);
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
@@ -237,7 +239,7 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
|
||||
if (term)
|
||||
if (term && term[0])
|
||||
setenv("TERM", term, 1);
|
||||
|
||||
return do_login(name);
|
||||
@@ -252,6 +254,7 @@ int sh(char *tty)
|
||||
NULL
|
||||
};
|
||||
size_t len;
|
||||
struct termios term;
|
||||
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
@@ -282,6 +285,17 @@ int sh(char *tty)
|
||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||
args[0] = arg0;
|
||||
|
||||
/* Reenable Ctrl-D and Ctrl-C, and ... */
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_lflag |= ISIG;
|
||||
term.c_cc[VEOF] = CTL('D');
|
||||
term.c_cc[VINTR] = CTL('C');
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||
}
|
||||
|
||||
/* ... unblock signals in general */
|
||||
sig_unblock();
|
||||
|
||||
return execv(_PATH_BSHELL, args);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,12 +43,13 @@
|
||||
} while (0);
|
||||
|
||||
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||
static int inetd_dgram_peek(int sd, char *ifname)
|
||||
static int inetd_dgram_peek(int sd, char *ifname, size_t len)
|
||||
{
|
||||
char cmbuf[0x100];
|
||||
struct msghdr msgh;
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr msgh;
|
||||
char cmbuf[0x100];
|
||||
|
||||
memset(ifname, 0, len);
|
||||
memset(&msgh, 0, sizeof(msgh));
|
||||
msgh.msg_control = cmbuf;
|
||||
msgh.msg_controllen = sizeof(cmbuf);
|
||||
@@ -56,13 +57,15 @@ static int inetd_dgram_peek(int sd, char *ifname)
|
||||
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
|
||||
return -1;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
|
||||
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
|
||||
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||
char tmp[IF_NAMESIZE + 1] = { 0 };
|
||||
|
||||
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
|
||||
continue;
|
||||
|
||||
if_indextoname(ipi->ipi_ifindex, ifname);
|
||||
if_indextoname(ipi->ipi_ifindex, tmp);
|
||||
strlcpy(ifname, tmp, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -73,11 +76,11 @@ static int inetd_dgram_peek(int sd, char *ifname)
|
||||
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||
static void inetd_dgram_drop(int sd, const char *ifname)
|
||||
{
|
||||
char pkt_interface[17];
|
||||
char pkt_interface[IF_NAMESIZE + 1];
|
||||
char buf[BUFSIZ];
|
||||
|
||||
while (1) {
|
||||
inetd_dgram_peek(sd, pkt_interface);
|
||||
inetd_dgram_peek(sd, pkt_interface, sizeof(pkt_interface));
|
||||
if (string_compare(pkt_interface, ifname)) {
|
||||
if (recv(sd, buf, sizeof(buf), 0) < 0)
|
||||
break;
|
||||
@@ -88,12 +91,13 @@ static void inetd_dgram_drop(int sd, const char *ifname)
|
||||
}
|
||||
|
||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||
static int inetd_stream_peek(int sd, char *ifname)
|
||||
static int inetd_stream_peek(int sd, char *ifname, size_t ilen)
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t len = sizeof(sin);
|
||||
|
||||
memset(ifname, 0, ilen);
|
||||
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
|
||||
return -1;
|
||||
|
||||
@@ -112,7 +116,7 @@ static int inetd_stream_peek(int sd, char *ifname)
|
||||
|
||||
iin = (struct sockaddr_in *)ifa->ifa_addr;
|
||||
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
|
||||
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE);
|
||||
strlcpy(ifname, ifa->ifa_name, ilen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -122,11 +126,12 @@ static int inetd_stream_peek(int sd, char *ifname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_stdin(svc_t *svc)
|
||||
static int get_stdin(svc_t *svc, char *iifname, size_t len)
|
||||
{
|
||||
int stdin = svc->inetd.watcher.fd;
|
||||
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
||||
char ifname[IF_NAMESIZE + 1] = "UNKNOWN";
|
||||
|
||||
memset(iifname, 0, len);
|
||||
if (svc->inetd.type == SOCK_STREAM) {
|
||||
/* Open new client socket from server socket */
|
||||
stdin = accept(stdin, NULL, NULL);
|
||||
@@ -137,9 +142,9 @@ static int get_stdin(svc_t *svc)
|
||||
|
||||
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
|
||||
|
||||
inetd_stream_peek(stdin, ifname);
|
||||
inetd_stream_peek(stdin, ifname, sizeof(ifname));
|
||||
} else { /* SOCK_DGRAM */
|
||||
inetd_dgram_peek(stdin, ifname);
|
||||
inetd_dgram_peek(stdin, ifname, sizeof(ifname));
|
||||
}
|
||||
|
||||
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
||||
@@ -152,6 +157,9 @@ static int get_stdin(svc_t *svc)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return ingress interface */
|
||||
strlcpy(iifname, ifname, len);
|
||||
|
||||
return stdin;
|
||||
}
|
||||
|
||||
@@ -160,6 +168,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
svc_t *svc = (svc_t *)arg, *task;
|
||||
const char *conn = " connection";
|
||||
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
|
||||
char id[MAX_ID_LEN];
|
||||
int stdin;
|
||||
|
||||
_d("%s: Got socket event ...", svc->cmd);
|
||||
@@ -168,7 +178,7 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
||||
return;
|
||||
}
|
||||
|
||||
stdin = get_stdin(svc);
|
||||
stdin = get_stdin(svc, iifname, sizeof(iifname));
|
||||
if (stdin < 0) {
|
||||
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
|
||||
return;
|
||||
@@ -185,7 +195,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
||||
return;
|
||||
}
|
||||
|
||||
task = svc_new(svc->cmd, svc->inetd.next_id++, SVC_TYPE_INETD_CONN);
|
||||
snprintf(id, sizeof(id), "%d", svc->inetd.next_id++);
|
||||
task = svc_new(svc->cmd, id, SVC_TYPE_INETD_CONN);
|
||||
if (!task) {
|
||||
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
|
||||
if (svc->inetd.type == SOCK_STREAM)
|
||||
@@ -216,6 +227,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
||||
memcpy(task->args, svc->args, sizeof(task->args));
|
||||
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
|
||||
strlcat(task->desc, conn, sizeof(task->desc));
|
||||
strlcpy(task->iifname, iifname, sizeof(task->iifname));
|
||||
strlcpy(task->name, svc->name, sizeof(task->name));
|
||||
|
||||
task->stdin_fd = stdin;
|
||||
service_step(task);
|
||||
@@ -335,6 +348,22 @@ int inetd_start(inetd_t *inetd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inetd_stop_children(inetd_t *inetd, int check_allowed)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
svc = svc_job_iterator(&iter, 1, inetd->svc->job);
|
||||
while (svc) {
|
||||
if (!svc_is_inetd(svc)) {
|
||||
if (!check_allowed || !inetd_is_allowed(inetd, svc->iifname)) {
|
||||
svc_stop(svc);
|
||||
service_step(svc);
|
||||
}
|
||||
}
|
||||
svc = svc_job_iterator(&iter, 0, inetd->svc->job);
|
||||
}
|
||||
}
|
||||
|
||||
void inetd_stop(inetd_t *inetd)
|
||||
{
|
||||
if (!inetd || !inetd->svc) {
|
||||
@@ -346,12 +375,16 @@ void inetd_stop(inetd_t *inetd)
|
||||
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
|
||||
uev_io_stop(&inetd->watcher);
|
||||
|
||||
/* For dgram inetd services we block the parent SVC
|
||||
* and halt the watcher, so don't close the socket! */
|
||||
/*
|
||||
* For dgram inetd services we block the parent SVC
|
||||
* and halt the watcher, so don't close the socket!
|
||||
*/
|
||||
if (!svc_is_busy(inetd->svc)) {
|
||||
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
|
||||
close(inetd->watcher.fd);
|
||||
inetd->watcher.fd = -1;
|
||||
|
||||
inetd_stop_children(inetd, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name);
|
||||
|
||||
int inetd_start (inetd_t *inetd);
|
||||
void inetd_stop (inetd_t *inetd);
|
||||
void inetd_stop_children (inetd_t *inetd, int check_allowed);
|
||||
|
||||
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
|
||||
int inetd_del (inetd_t *inetd);
|
||||
|
||||
@@ -173,7 +173,7 @@ static int do_restart(char *arg) { return do_startstop(INIT_CMD_RESTART_SVC, arg
|
||||
|
||||
static void show_cond_one(const char *_conds)
|
||||
{
|
||||
static char conds[MAX_ARG_LEN];
|
||||
static char conds[MAX_COND_LEN];
|
||||
char *cond;
|
||||
|
||||
strlcpy(conds, _conds, sizeof(conds));
|
||||
@@ -435,17 +435,17 @@ static int show_status(char *arg)
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
||||
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
char jobid[10], args[512] = "", *lvls;
|
||||
char jobid[20], 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);
|
||||
snprintf(jobid, sizeof(jobid), "%d:%s", svc->job, svc->id);
|
||||
|
||||
printf("%-5s %7s ", jobid, svc_status(svc));
|
||||
printf("%-9s %7s ", jobid, svc_status(svc));
|
||||
if (svc_is_inetd(svc))
|
||||
printf("inetd ");
|
||||
else
|
||||
@@ -459,14 +459,7 @@ static int show_status(char *arg)
|
||||
|
||||
if (!verbose) {
|
||||
int adj = screen_cols - 60;
|
||||
char *name = strrchr(svc->cmd, '/');
|
||||
|
||||
if (!name)
|
||||
name = svc->cmd;
|
||||
else
|
||||
name++;
|
||||
|
||||
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc);
|
||||
printf("%-16.16s %-*.*s\n", svc->name, adj, adj, svc->desc);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -516,12 +509,77 @@ static int show_status(char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||
{
|
||||
FILE *fp;
|
||||
char *group;
|
||||
char path[256];
|
||||
char buf[256];
|
||||
int user = 0;
|
||||
int num = 0;
|
||||
|
||||
if (tflag == FTW_F)
|
||||
return 0;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/cgroup.procs", fpath);
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
if (strstr(fpath, "finit/user"))
|
||||
user = 1;
|
||||
|
||||
group = strrchr(fpath, '/');
|
||||
if (!group)
|
||||
return 0;
|
||||
group++;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
FILE *cfp;
|
||||
int pid;
|
||||
|
||||
if (num == 0)
|
||||
printf("%c :- %s/", user ? ' ' : '|', group);
|
||||
num++;
|
||||
|
||||
pid = atoi(chomp(buf));
|
||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||
cfp = fopen(path, "r");
|
||||
if (!cfp)
|
||||
continue;
|
||||
fgets(buf, sizeof(buf), cfp);
|
||||
fclose(cfp);
|
||||
|
||||
printf("\n%c :- %d %s", user ? ' ' : '|', pid, buf);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (num)
|
||||
printf("\r%c\n", user ? ' ' : '|');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_cgroup(char *arg)
|
||||
{
|
||||
puts("finit/");
|
||||
puts("|- init/");
|
||||
nftw("/sys/fs/cgroup/finit/init", dump_cgroup, 20, 0);
|
||||
puts("|- system/");
|
||||
nftw("/sys/fs/cgroup/finit/system", dump_cgroup, 20, 0);
|
||||
puts("`- user/");
|
||||
nftw("/sys/fs/cgroup/finit/user", dump_cgroup, 20, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [OPTIONS] [COMMAND]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -b, --batch Batch mode, no screen size probing\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
" -h, --help This help text\n"
|
||||
"\n"
|
||||
@@ -549,6 +607,8 @@ static int usage(int rc)
|
||||
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
|
||||
" status | show Show status of services, default command\n"
|
||||
"\n"
|
||||
" ps List processes based on cgroups\n"
|
||||
"\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" reboot Reboot system\n"
|
||||
" halt Halt system\n"
|
||||
@@ -567,7 +627,7 @@ static int do_help(char *arg)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
int interactive = 1, c;
|
||||
char *cmd, arg[120];
|
||||
struct command command[] = {
|
||||
{ "debug", toggle_debug },
|
||||
@@ -589,6 +649,8 @@ int main(int argc, char *argv[])
|
||||
{ "status", show_status },
|
||||
{ "show", show_status }, /* Convenience alias */
|
||||
|
||||
{ "ps", show_cgroup },
|
||||
|
||||
{ "runlevel", do_runlevel },
|
||||
{ "reboot", do_reboot },
|
||||
{ "halt", do_halt },
|
||||
@@ -598,6 +660,7 @@ int main(int argc, char *argv[])
|
||||
{ NULL, NULL }
|
||||
};
|
||||
struct option long_options[] = {
|
||||
{"batch", 0, NULL, 'b'},
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"debug", 0, NULL, 'd'},
|
||||
{"verbose", 0, NULL, 'v'},
|
||||
@@ -605,8 +668,12 @@ int main(int argc, char *argv[])
|
||||
};
|
||||
|
||||
progname(argv[0]);
|
||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
||||
while ((c = getopt_long(argc, argv, "bh?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'b':
|
||||
interactive = 0;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
@@ -617,7 +684,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
screen_init();
|
||||
if (interactive)
|
||||
screen_init();
|
||||
|
||||
if (optind >= argc)
|
||||
return show_status(NULL);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static int up = 0;
|
||||
static int debug = 0;
|
||||
@@ -65,6 +66,15 @@ void log_exit(void)
|
||||
sched_yield();
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unless in debug mode at shutdown, Reinitialize screen,
|
||||
* terminal may have been resized at runtime
|
||||
*/
|
||||
if (debug)
|
||||
screen_exit();
|
||||
else
|
||||
screen_init();
|
||||
}
|
||||
|
||||
void log_open(void)
|
||||
@@ -102,12 +112,19 @@ void log_debug(void)
|
||||
if (debug) {
|
||||
silent = 0;
|
||||
loglevel = LOG_DEBUG;
|
||||
|
||||
screen_exit();
|
||||
} else {
|
||||
silent = SILENT_MODE;
|
||||
loglevel = LOG_NOTICE;
|
||||
|
||||
screen_init();
|
||||
}
|
||||
log_open();
|
||||
|
||||
/* Also toggle silent mode (progress) */
|
||||
log_silent();
|
||||
|
||||
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ static int logrotate(char *file, int num, off_t sz)
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
rename(ofile, nfile);
|
||||
(void)rename(ofile, nfile);
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
@@ -86,8 +86,10 @@ static int logrotate(char *file, int num, off_t sz)
|
||||
}
|
||||
}
|
||||
|
||||
rename(file, nfile);
|
||||
create(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
if (rename(file, nfile))
|
||||
(void)truncate(file, 0);
|
||||
else
|
||||
create(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
if (truncate(file, 0))
|
||||
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <unistd.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "pid.h"
|
||||
#include "svc.h"
|
||||
#include "helpers.h"
|
||||
|
||||
@@ -92,27 +93,6 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
return pname;
|
||||
}
|
||||
|
||||
char *pid_runpath(char *file, char *path, size_t len)
|
||||
{
|
||||
static int unknown = 1;
|
||||
static char *prefix = "/var/run";
|
||||
|
||||
if (unknown) {
|
||||
if (fisdir("/run"))
|
||||
prefix = "/run";
|
||||
unknown = 0;
|
||||
}
|
||||
|
||||
if (!strncmp(file, "/var/run/", 9))
|
||||
file += 9;
|
||||
else if (!strncmp(file, "/run/", 5))
|
||||
file += 5;
|
||||
|
||||
snprintf(path, len, "%s/%s", prefix, file);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
char *pid_file(svc_t *svc)
|
||||
{
|
||||
char fn[MAX_ARG_LEN];
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_PID_H_
|
||||
#define FINIT_PID_H_
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
@@ -29,7 +34,38 @@ char *pid_file (svc_t *svc);
|
||||
int pid_file_create (svc_t *svc);
|
||||
int pid_file_parse (svc_t *svc, char *arg);
|
||||
|
||||
char *pid_runpath (char *file, char *path, size_t len);
|
||||
/**
|
||||
* pid_runpath - Adjust /var/run --> /run path depending on system
|
||||
* @file: Path to file in /run/path or /var/run/path
|
||||
* @path: Pointer to buffer to write correct path
|
||||
* @len: Length, in bytes, of @path buffer
|
||||
*
|
||||
* Returns:
|
||||
* Always returns a valid pointer, which can be either @path with a /run
|
||||
* or /var/run prefix to @file, or it may be @file if the prefix is OK.
|
||||
*/
|
||||
static inline char *pid_runpath(const char *file, char *path, size_t len)
|
||||
{
|
||||
static int unknown = 1;
|
||||
static char *prefix = "/var/run";
|
||||
|
||||
if (unknown) {
|
||||
if (fisdir("/run"))
|
||||
prefix = "/run";
|
||||
unknown = 0;
|
||||
}
|
||||
|
||||
if (!strncmp(file, "/var/run/", 9))
|
||||
file += 9;
|
||||
else if (!strncmp(file, "/run/", 5))
|
||||
file += 5;
|
||||
|
||||
snprintf(path, len, "%s/%s", prefix, file);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#endif /* FINIT_PID_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
|
||||
@@ -108,6 +108,9 @@ int plugin_register(plugin_t *plugin)
|
||||
/* Not called, at the moment plugins cannot be unloaded. */
|
||||
int plugin_unregister(plugin_t *plugin)
|
||||
{
|
||||
if (is_io_plugin(plugin))
|
||||
uev_io_stop(&plugin->watcher);
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
TAILQ_REMOVE(&plugins, plugin, link);
|
||||
|
||||
@@ -186,19 +189,11 @@ int plugin_exists(hook_point_t no)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
|
||||
/* Some hooks are called with a fixed argument */
|
||||
void plugin_run_hook(hook_point_t no, void *arg)
|
||||
{
|
||||
static int last = -1;
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
/*
|
||||
* End recursion: any plugin hook => start service => SVC start
|
||||
* hook => start service ... err ... wait a second
|
||||
*/
|
||||
if (HOOK_SVC_START == last)
|
||||
return;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (p->hook[no].cb) {
|
||||
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
|
||||
@@ -206,15 +201,8 @@ void plugin_run_hook(hook_point_t no, void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
/* Guard against infinite recursion */
|
||||
if (HOOK_SVC_START == no)
|
||||
last = no;
|
||||
|
||||
cond_set_oneshot(hook_cond[no]);
|
||||
service_step_all(SVC_TYPE_RUNTASK);
|
||||
|
||||
if (HOOK_SVC_START == no)
|
||||
last = -1;
|
||||
}
|
||||
|
||||
/* Regular hooks are called with the registered plugin's argument */
|
||||
@@ -382,11 +370,11 @@ static int load_plugins(char *path)
|
||||
}
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
int plugin_init(uev_ctx_t *ctx, char *path)
|
||||
int plugin_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int fail = 1;
|
||||
|
||||
if (!load_plugins(path))
|
||||
if (!load_plugins(PLUGIN_PATH))
|
||||
fail = init_plugins(ctx);
|
||||
|
||||
return fail;
|
||||
|
||||
@@ -24,16 +24,11 @@
|
||||
#ifndef FINIT_PLUGIN_H_
|
||||
#define FINIT_PLUGIN_H_
|
||||
|
||||
#include <config.h>
|
||||
#include <uev/uev.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
|
||||
#define PLUGIN_PATH "/dev/null"
|
||||
#endif
|
||||
|
||||
#define PLUGIN_DEP_MAX 10
|
||||
|
||||
/*
|
||||
@@ -61,8 +56,6 @@
|
||||
* according to the following scheme:
|
||||
*
|
||||
* - HOOK_SVC_RECONF :: action/svc/reconf
|
||||
* - HOOK_SVC_LOST :: action/svc/lost
|
||||
* - HOOK_SVC_START :: action/svc/start
|
||||
* - HOOK_RUNLEVEL_CHANGE :: action/sys/runlevel
|
||||
*
|
||||
* However, the implementation did not turn out to be stable enough for
|
||||
@@ -73,6 +66,7 @@
|
||||
CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
|
||||
CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
|
||||
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
||||
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
|
||||
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
||||
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
||||
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
||||
@@ -80,8 +74,6 @@
|
||||
\
|
||||
/* Runtime hooks, runlevel [S1-9] */ \
|
||||
CHOOSE(HOOK_SVC_RECONF, "nop"), \
|
||||
CHOOSE(HOOK_SVC_LOST, "nop"), \
|
||||
CHOOSE(HOOK_SVC_START, "nop"), \
|
||||
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
|
||||
\
|
||||
/* Shutdown hooks, runlevel [06] */ \
|
||||
|
||||
@@ -28,11 +28,6 @@
|
||||
#include "svc.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/*
|
||||
* Main loop context
|
||||
*/
|
||||
uev_ctx_t *ctx;
|
||||
|
||||
int api_init (uev_ctx_t *ctx);
|
||||
int api_exit (void);
|
||||
|
||||
@@ -45,7 +40,7 @@ int plugin_exists (hook_point_t no);
|
||||
void plugin_run_hook (hook_point_t no, void *arg);
|
||||
void plugin_run_hooks (hook_point_t no);
|
||||
|
||||
int plugin_init (uev_ctx_t *ctx, char *path);
|
||||
int plugin_init (uev_ctx_t *ctx);
|
||||
void plugin_exit (void);
|
||||
|
||||
#endif /* FINIT_PRIVATE_H_ */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Example HOOK_SVC_LOST plugin, very noisy do not use as-is!
|
||||
/* Work queue helper functions
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2018 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,35 +21,44 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
#include "plugin.h"
|
||||
#include "schedule.h"
|
||||
|
||||
static void lost_pid(void *arg)
|
||||
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
|
||||
|
||||
/*
|
||||
* libuEv callback wrapper
|
||||
*/
|
||||
static void cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
pid_t pid = (uintptr_t)arg;
|
||||
svc_t *svc;
|
||||
struct wq *work = (struct wq *)arg;
|
||||
|
||||
svc = svc_find_by_pid(pid);
|
||||
if (!svc)
|
||||
if (UEV_ERROR == events) {
|
||||
uev_timer_start(w);
|
||||
return;
|
||||
}
|
||||
|
||||
logit(LOG_INFO, "Lost PID %u, svc %s", pid, svc->cmd);
|
||||
work->cb(work);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_SVC_LOST] = { .cb = lost_pid },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
/*
|
||||
* Place work on event queue
|
||||
*/
|
||||
int schedule_work(struct wq *work)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
int msec;
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
if (!work)
|
||||
return errno = EINVAL;
|
||||
|
||||
msec = work->delay;
|
||||
if (work->init != SC_INIT) {
|
||||
work->init = SC_INIT;
|
||||
return uev_timer_init(ctx, &work->watcher, cb, work, msec, 0);
|
||||
}
|
||||
|
||||
return uev_timer_set(&work->watcher, msec, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Save and restore RTC using hwclock
|
||||
/* Work queue helper functions
|
||||
*
|
||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2018 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,49 +21,17 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#ifndef FINIT_SCHEDULE_H_
|
||||
#define FINIT_SCHEDULE_H_
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
static void save(void *arg)
|
||||
{
|
||||
_d("Saving system clock to RTC ...");
|
||||
/* -w,--systohc, -u,--utc */
|
||||
run_interactive("hwclock -w -u", "Saving system time (UTC) to RTC");
|
||||
}
|
||||
|
||||
static void restore(void *arg)
|
||||
{
|
||||
_d("Restoring system clock from RTC ...");
|
||||
/* -s,--hctosys, -u,--utc */
|
||||
run_interactive("hwclock -s -u", "Restoring system clock (UTC) from RTC");
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = restore
|
||||
},
|
||||
.hook[HOOK_SHUTDOWN] = {
|
||||
.cb = save
|
||||
}
|
||||
struct wq {
|
||||
uev_t watcher;
|
||||
int init;
|
||||
int delay; /* msec delay before starting work */
|
||||
void (*cb)(void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
int schedule_work(struct wq *work);
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
#endif /* FINIT_SCHEDULE_H_ */
|
||||
@@ -120,7 +120,7 @@ int serv_list(char *arg)
|
||||
if (fisdir(enabled))
|
||||
do_list("ENABLED ", enabled);
|
||||
if (fisdir(FINIT_RCSD))
|
||||
do_list("ENABLED ", FINIT_RCSD);
|
||||
do_list("STATIC ", FINIT_RCSD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -131,17 +131,22 @@ int serv_enable(char *arg)
|
||||
char link[80];
|
||||
char path[80];
|
||||
|
||||
if (!arg || !arg[0])
|
||||
return serv_list(NULL);
|
||||
|
||||
if (!strstr(arg, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||
arg = corr;
|
||||
}
|
||||
|
||||
pushd(FINIT_RCSD);
|
||||
snprintf(path, sizeof(path), "available/%s", arg);
|
||||
mkdir("enabled", 0755);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", available, arg);
|
||||
if (!fexist(path))
|
||||
errx(1, "Cannot find %s", path);
|
||||
|
||||
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
|
||||
snprintf(link, sizeof(link), "%s/%s", enabled, arg);
|
||||
if (fexist(link))
|
||||
errx(1, "%s already exists", link);
|
||||
|
||||
@@ -154,12 +159,15 @@ int serv_disable(char *arg)
|
||||
char link[80];
|
||||
struct stat st;
|
||||
|
||||
if (!arg || !arg[0])
|
||||
return serv_list(NULL);
|
||||
|
||||
if (!strstr(arg, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||
arg = corr;
|
||||
}
|
||||
|
||||
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
|
||||
snprintf(link, sizeof(link), "%s/%s", enabled, arg);
|
||||
if (stat(link, &st))
|
||||
err(1, "Cannot find %s", link);
|
||||
|
||||
@@ -173,6 +181,9 @@ int serv_touch(char *arg)
|
||||
{
|
||||
char corr[40];
|
||||
|
||||
if (!arg || !arg[0])
|
||||
return serv_list(NULL);
|
||||
|
||||
if (!strstr(arg, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||
arg = corr;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <net/if.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "cgroup.h"
|
||||
#include "conf.h"
|
||||
#include "cond.h"
|
||||
#include "finit.h"
|
||||
@@ -45,9 +46,14 @@
|
||||
#include "tty.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
#include "schedule.h"
|
||||
|
||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||
|
||||
static struct wq work = {
|
||||
.cb = service_worker,
|
||||
};
|
||||
|
||||
static void svc_set_state(svc_t *svc, svc_state_t new);
|
||||
|
||||
/**
|
||||
@@ -63,7 +69,8 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
|
||||
svc_t *svc = arg;
|
||||
|
||||
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */
|
||||
svc->timer_cb(svc);
|
||||
if (svc->timer_cb)
|
||||
svc->timer_cb(svc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,11 +140,11 @@ static int is_norespawn(void)
|
||||
* @svc: Service to start
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the service was successfully started. Non-zero otherwise.
|
||||
* 0 if the service was successfully started. Non-zero otherwise.
|
||||
*/
|
||||
static int service_start(svc_t *svc)
|
||||
{
|
||||
int i, result = 0;
|
||||
int i, result = 0, do_progress = 1;
|
||||
pid_t pid;
|
||||
sigset_t nmask, omask;
|
||||
|
||||
@@ -160,10 +167,15 @@ static int service_start(svc_t *svc)
|
||||
return inetd_start(&svc->inetd);
|
||||
#endif
|
||||
|
||||
if (svc_is_daemon(svc))
|
||||
print_desc("Starting ", svc->desc);
|
||||
else
|
||||
print_desc("", svc->desc);
|
||||
if (!svc->desc[0])
|
||||
do_progress = 0;
|
||||
|
||||
if (do_progress) {
|
||||
if (svc_is_daemon(svc))
|
||||
print_desc("Starting ", svc->desc);
|
||||
else
|
||||
print_desc("", svc->desc);
|
||||
}
|
||||
|
||||
/* Declare we're waiting for svc to create its pidfile */
|
||||
svc_starting(svc);
|
||||
@@ -174,6 +186,12 @@ static int service_start(svc_t *svc)
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid > 0) {
|
||||
cgroup_service(svc->cmd, pid);
|
||||
cgroup_assign(svc->cgroup, pid);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
int status;
|
||||
char *home = NULL;
|
||||
@@ -232,6 +250,9 @@ static int service_start(svc_t *svc)
|
||||
redirect_null();
|
||||
goto logit_done;
|
||||
}
|
||||
if (svc->log.console) {
|
||||
goto logit_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open PTY to connect to logger. A pty isn't buffered
|
||||
@@ -266,7 +287,12 @@ static int service_start(svc_t *svc)
|
||||
sig_unblock();
|
||||
|
||||
if (svc->log.file[0] == '/') {
|
||||
execlp("logit", "logit", "-f", svc->log.file, NULL);
|
||||
char sz[20], num[3];
|
||||
|
||||
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
|
||||
snprintf(num, sizeof(num), "%d", logfile_count_max);
|
||||
|
||||
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
@@ -318,7 +344,7 @@ static int service_start(svc_t *svc)
|
||||
#endif
|
||||
if (svc->log.enabled && !svc->log.null)
|
||||
waitpid(pid, NULL, 0);
|
||||
exit(status);
|
||||
_exit(status);
|
||||
} else if (log_is_debug()) {
|
||||
char buf[CMD_SIZE] = "";
|
||||
|
||||
@@ -335,32 +361,32 @@ static int service_start(svc_t *svc)
|
||||
svc->pid = pid;
|
||||
svc->start_time = jiffies();
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_RUN:
|
||||
result = WEXITSTATUS(complete(svc->cmd, pid));
|
||||
svc->start_time = svc->pid = 0;
|
||||
svc->once++;
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
|
||||
case SVC_TYPE_SERVICE:
|
||||
pid_file_create(svc);
|
||||
break;
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd_conn(svc) && svc->inetd.type == SOCK_STREAM)
|
||||
close(svc->stdin_fd);
|
||||
case SVC_TYPE_INETD_CONN:
|
||||
if (svc->inetd.type == SOCK_STREAM)
|
||||
close(svc->stdin_fd);
|
||||
break;
|
||||
#endif
|
||||
|
||||
if (SVC_TYPE_RUN == svc->type) {
|
||||
result = WEXITSTATUS(complete(svc->cmd, pid));
|
||||
if (!svc_clean_bootstrap(svc)) {
|
||||
svc->start_time = svc->pid = 0;
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (svc_is_daemon(svc))
|
||||
pid_file_create(svc);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
print_result(result);
|
||||
|
||||
/*
|
||||
* Only run hook on successful start, and *after* having printed
|
||||
* the result, otherwise any hook tasks may overwrite it and the
|
||||
* result would be like double "[ OK ]" but only one service.
|
||||
*/
|
||||
if (!result)
|
||||
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
|
||||
if (do_progress)
|
||||
print_result(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -381,10 +407,10 @@ static void service_kill(svc_t *svc)
|
||||
return;
|
||||
}
|
||||
|
||||
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
|
||||
if (runlevel != 1)
|
||||
print_desc("Killing ", svc->desc);
|
||||
|
||||
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
|
||||
kill(svc->pid, SIGKILL);
|
||||
|
||||
/* Let SIGKILLs stand out, show result as [WARN] */
|
||||
@@ -397,7 +423,7 @@ static void service_kill(svc_t *svc)
|
||||
* @svc: Service to stop
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the service was successfully stopped. Non-zero otherwise.
|
||||
* 0 if the service was successfully stopped. Non-zero otherwise.
|
||||
*/
|
||||
static int service_stop(svc_t *svc)
|
||||
{
|
||||
@@ -406,33 +432,36 @@ static int service_stop(svc_t *svc)
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
if (svc->state <= SVC_STOPPING_STATE)
|
||||
return 0;
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd(svc)) {
|
||||
int do_print = runlevel != 1 && !svc_is_busy(svc);
|
||||
int do_progress = runlevel != 1 && !svc_is_busy(svc);
|
||||
|
||||
if (do_print)
|
||||
if (do_progress)
|
||||
print_desc("Stopping ", svc->desc);
|
||||
|
||||
inetd_stop(&svc->inetd);
|
||||
|
||||
if (do_print)
|
||||
if (do_progress)
|
||||
print_result(0);
|
||||
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
if (svc->pid <= 1) {
|
||||
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
|
||||
if (svc->pid <= 1)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (SVC_TYPE_SERVICE != svc->type)
|
||||
return 0;
|
||||
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
|
||||
if (runlevel != 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);
|
||||
|
||||
if (runlevel != 1)
|
||||
@@ -447,12 +476,13 @@ static int service_stop(svc_t *svc)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static int service_restart(svc_t *svc)
|
||||
{
|
||||
int do_progress = 1;
|
||||
int rc;
|
||||
|
||||
/* Ignore if finit is SIGSTOP'ed */
|
||||
@@ -468,21 +498,29 @@ static int service_restart(svc_t *svc)
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_desc("Restarting ", svc->desc);
|
||||
/* Skip progress if desc disabled or bootstrap task */
|
||||
if (!svc->desc[0] || svc_in_runlevel(svc, 0))
|
||||
do_progress = 0;
|
||||
|
||||
/* Declare we're waiting for svc to re-assert/touch its pidfile */
|
||||
svc_starting(svc);
|
||||
if (do_progress)
|
||||
print_desc("Restarting ", svc->desc);
|
||||
|
||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||
rc = kill(svc->pid, SIGHUP);
|
||||
|
||||
/* Declare we're waiting for svc to re-assert/touch its pidfile */
|
||||
svc_starting(svc);
|
||||
|
||||
/* Service does not maintain a PID file on its own */
|
||||
if (svc_has_pidfile(svc)) {
|
||||
sched_yield();
|
||||
touch(pid_file(svc));
|
||||
}
|
||||
|
||||
return print_result(rc);
|
||||
if (do_progress)
|
||||
print_result(rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,6 +568,8 @@ static void parse_log(svc_t *svc, char *arg)
|
||||
svc->log.enabled = 1;
|
||||
else if (!strcmp(tok, "null") || !strcmp(tok, "/dev/null"))
|
||||
svc->log.null = 1;
|
||||
else if (!strcmp(tok, "console") || !strcmp(tok, "/dev/console"))
|
||||
svc->log.console = 1;
|
||||
else if (tok[0] == '/')
|
||||
strlcpy(svc->log.file, tok, sizeof(svc->log.file));
|
||||
else if (!strcmp(tok, "priority") || !strcmp(tok, "prio"))
|
||||
@@ -541,11 +581,56 @@ static void parse_log(svc_t *svc, char *arg)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* name:<name>
|
||||
*/
|
||||
static void parse_name(svc_t *svc, char *arg)
|
||||
{
|
||||
char *name = NULL;
|
||||
|
||||
if (arg && !strncasecmp(arg, "name:", 5)) {
|
||||
name = arg + 5;
|
||||
} else {
|
||||
name = strrchr(svc->cmd, '/');
|
||||
name = name ? name + 1 : svc->cmd;
|
||||
}
|
||||
|
||||
strlcpy(svc->name, name, sizeof(svc->name));
|
||||
}
|
||||
|
||||
/**
|
||||
* parse_cmdline_args - Update the command line args in the svc struct
|
||||
*
|
||||
* strtok internal pointer must be positioned at first command line arg
|
||||
* when this function is called.
|
||||
*
|
||||
* Side effect: strtok internal pointer will be modified.
|
||||
*/
|
||||
static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
{
|
||||
int i;
|
||||
char *arg;
|
||||
|
||||
strlcpy(svc->args[0], cmd, sizeof(svc->args[0]));
|
||||
|
||||
/* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
|
||||
* array to be zero-terminated. */
|
||||
for (i=1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS-1); i++)
|
||||
strlcpy(svc->args[i], arg, sizeof(svc->args[0]));
|
||||
|
||||
/* clear remaining args in case they were set earlier.
|
||||
* This also zero-terminates the args array.*/
|
||||
for (; i < MAX_NUM_SVC_ARGS; i++)
|
||||
svc->args[i][0] = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* service_register - Register service, task or run commands
|
||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||
* @cfg: Configuration, complete command, with -- for description text
|
||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||
* @cgroup: Control group, may be default
|
||||
* @file: The file name service was loaded from
|
||||
*
|
||||
* This function is used to register commands to be run on different
|
||||
@@ -593,18 +678,20 @@ static void parse_log(svc_t *svc, char *arg)
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||
*/
|
||||
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
int service_register(int type, char *cfg, struct rlimit rlimit[], char *cgroup, char *file)
|
||||
{
|
||||
int i = 0;
|
||||
int id = -1;
|
||||
char id_str[MAX_ID_LEN];
|
||||
#ifdef INETD_ENABLED
|
||||
int forking = 0;
|
||||
#endif
|
||||
int levels = 0;
|
||||
int manual = 0;
|
||||
char *line;
|
||||
char *id = NULL;
|
||||
char *username = NULL, *log = NULL, *pid = NULL;
|
||||
char *service = NULL, *proto = NULL, *ifaces = NULL;
|
||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
||||
char *name = NULL;
|
||||
svc_t *svc;
|
||||
plugin_t *plugin = NULL;
|
||||
|
||||
@@ -624,6 +711,15 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
|
||||
while (*desc && isblank(*desc))
|
||||
desc++;
|
||||
} else {
|
||||
int pos;
|
||||
|
||||
/* Find "--\n" to denote empty/no description */
|
||||
pos = (int)strlen(line) - 2;
|
||||
if (pos > 0 && !strcmp(&line[pos], "--")) {
|
||||
line[pos] = 0;
|
||||
desc = &line[pos];
|
||||
}
|
||||
}
|
||||
|
||||
cmd = strtok(line, " ");
|
||||
@@ -642,7 +738,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
else if (cmd[0] == '<') /* <[!][cond][,cond..]> */
|
||||
cond = &cmd[1];
|
||||
else if (cmd[0] == ':') /* :ID */
|
||||
id = atoi(&cmd[1]);
|
||||
id = &cmd[1];
|
||||
#ifdef INETD_ENABLED
|
||||
else if (!strncasecmp(cmd, "nowait", 6))
|
||||
forking = 1;
|
||||
@@ -653,6 +749,10 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
log = cmd;
|
||||
else if (!strncasecmp(cmd, "pid", 3))
|
||||
pid = cmd;
|
||||
else if (!strncasecmp(cmd, "name:", 5))
|
||||
name = cmd;
|
||||
else if (!strncasecmp(cmd, "manual:yes", 10))
|
||||
manual = 1;
|
||||
else if (cmd[0] != '/' && strchr(cmd, '/'))
|
||||
service = cmd; /* inetd service/proto */
|
||||
else
|
||||
@@ -704,29 +804,40 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if known inetd, then add ifnames for filtering only. */
|
||||
/* Check if known inetd, update command line, then add ifnames for filtering only. */
|
||||
svc = inetd_find_svc(cmd, service, proto);
|
||||
if (svc)
|
||||
if (svc) {
|
||||
parse_cmdline_args(svc, cmd);
|
||||
goto inetd_setup;
|
||||
}
|
||||
if (!id) {
|
||||
int n = svc_next_id_int(cmd);
|
||||
|
||||
if (id <= 0)
|
||||
id = svc_next_id(cmd);
|
||||
if (n) {
|
||||
snprintf(id_str, sizeof(id_str), "%d", n);
|
||||
id = id_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
recreate:
|
||||
#endif
|
||||
|
||||
if (id <= 0)
|
||||
id = 1;
|
||||
if (!id)
|
||||
id = "1";
|
||||
|
||||
svc = svc_find(cmd, id);
|
||||
if (!svc) {
|
||||
_d("Creating new svc for %s id #%d type %d", cmd, id, type);
|
||||
_d("Creating new svc for %s id #%s type %d", cmd, id, type);
|
||||
svc = svc_new(cmd, id, type);
|
||||
if (!svc) {
|
||||
_e("Out of memory, cannot register service %s", cmd);
|
||||
free(line);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
if (type == SVC_TYPE_SERVICE && manual) {
|
||||
svc_stop(svc);
|
||||
}
|
||||
}
|
||||
#ifdef INETD_ENABLED
|
||||
else {
|
||||
@@ -759,18 +870,16 @@ recreate:
|
||||
/* Internal plugin provides this service */
|
||||
svc->inetd.cmd = plugin->inetd.cmd;
|
||||
svc->inetd.builtin = 1;
|
||||
} 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;
|
||||
}
|
||||
} else
|
||||
parse_cmdline_args(svc, cmd);
|
||||
|
||||
svc->runlevels = levels;
|
||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||
|
||||
conf_parse_cond(svc, cond);
|
||||
|
||||
parse_name(svc, name);
|
||||
|
||||
if (log)
|
||||
parse_log(svc, log);
|
||||
if (desc)
|
||||
@@ -808,6 +917,9 @@ recreate:
|
||||
/* Set configured limits */
|
||||
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
||||
|
||||
/* Register desired control group */
|
||||
strlcpy(svc->cgroup, cgroup, sizeof(svc->cgroup));
|
||||
|
||||
/* New, recently modified or unchanged ... used on reload. */
|
||||
if (file && conf_changed(file))
|
||||
svc_mark_dirty(svc);
|
||||
@@ -815,15 +927,53 @@ recreate:
|
||||
svc_mark_clean(svc);
|
||||
|
||||
if (!file)
|
||||
svc->protected = 1;
|
||||
svc->protect = 1;
|
||||
|
||||
/* Free duped line, from above */
|
||||
free(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called when cleaning up lingering (stopped) services
|
||||
* after a .conf reload, as well as when an inetd connection terminates.
|
||||
*
|
||||
* We need to ensure we properly stop the service before removing it,
|
||||
* including stopping any pending restart or SIGKILL timers before we
|
||||
* proceed to free() the svc itself.
|
||||
*
|
||||
* Remember to not try to stop inetd connections, they only get here
|
||||
* when already stopped, if we do we end up in a recursive loop.
|
||||
*/
|
||||
void service_unregister(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Only try stopping @svc if it's *not* an inetd connection.
|
||||
* Prevents infinite recursion when called from service_step()
|
||||
*/
|
||||
switch (svc->type) {
|
||||
#ifdef INETD_ENABLED
|
||||
case SVC_TYPE_INETD:
|
||||
inetd_del(&svc->inetd);
|
||||
break;
|
||||
|
||||
case SVC_TYPE_INETD_CONN:
|
||||
/* inetd connection, if UDP unblock parent */
|
||||
if (svc_is_busy(svc->inetd.svc)) {
|
||||
svc_unblock(svc->inetd.svc);
|
||||
service_step(svc->inetd.svc);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
service_stop(svc);
|
||||
break;
|
||||
}
|
||||
|
||||
svc_del(svc);
|
||||
}
|
||||
|
||||
@@ -837,8 +987,6 @@ void service_monitor(pid_t lost)
|
||||
if (tty_respawn(lost))
|
||||
return;
|
||||
|
||||
plugin_run_hook(HOOK_SVC_LOST, (void *)(uintptr_t)lost);
|
||||
|
||||
svc = svc_find_by_pid(lost);
|
||||
if (!svc) {
|
||||
_d("collected unknown PID %d", lost);
|
||||
@@ -877,13 +1025,13 @@ static void service_retry(svc_t *svc)
|
||||
|
||||
if (svc->state != SVC_HALTED_STATE ||
|
||||
svc->block != SVC_BLOCK_RESTARTING) {
|
||||
_d("%s not crashing anymore", svc->desc);
|
||||
_d("%s not crashing anymore", svc->cmd);
|
||||
*restart_cnt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*restart_cnt >= RESPAWN_MAX) {
|
||||
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->desc);
|
||||
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->cmd);
|
||||
svc_crashing(svc);
|
||||
*restart_cnt = 0;
|
||||
service_step(svc);
|
||||
@@ -892,7 +1040,7 @@ static void service_retry(svc_t *svc)
|
||||
|
||||
(*restart_cnt)++;
|
||||
|
||||
_d("%s crashed, trying to start it again, attempt %d", svc->desc, *restart_cnt);
|
||||
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
|
||||
svc_unblock(svc);
|
||||
service_step(svc);
|
||||
|
||||
@@ -907,10 +1055,10 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
||||
|
||||
*state = new;
|
||||
|
||||
/* if the PID isn't collected within 3s, kill it! */
|
||||
if (*state == SVC_STOPPING_STATE) {
|
||||
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
|
||||
if ((*state == SVC_STOPPING_STATE) && !svc_is_inetd(svc)) {
|
||||
service_timeout_cancel(svc);
|
||||
service_timeout_after(svc, 3000, service_kill);
|
||||
service_timeout_after(svc, SVC_TERM_TIMEOUT, service_kill);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,11 +1069,12 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
||||
*/
|
||||
int service_step(svc_t *svc)
|
||||
{
|
||||
int err;
|
||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||
svc_cmd_t enabled;
|
||||
svc_state_t old_state;
|
||||
cond_state_t cond;
|
||||
svc_state_t old_state;
|
||||
svc_cmd_t enabled;
|
||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||
int changed = 0;
|
||||
int err;
|
||||
|
||||
restart:
|
||||
old_state = svc->state;
|
||||
@@ -944,10 +1093,6 @@ restart:
|
||||
case SVC_DONE_STATE:
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd_conn(svc)) {
|
||||
if (svc_is_busy(svc->inetd.svc)) {
|
||||
svc_unblock(svc->inetd.svc);
|
||||
service_step(svc->inetd.svc);
|
||||
}
|
||||
service_unregister(svc);
|
||||
return -1;
|
||||
}
|
||||
@@ -988,28 +1133,22 @@ restart:
|
||||
if (sm_is_in_teardown(&sm))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Make state transition *before* service_start(), because
|
||||
* of HOOK_SVC_START, which may call service_step()
|
||||
*/
|
||||
svc_mark_clean(svc);
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
|
||||
err = service_start(svc);
|
||||
if (err) {
|
||||
(*restart_cnt)++;
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
|
||||
if (!svc_is_inetd_conn(svc))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Everything went fine, clean and set state */
|
||||
svc_mark_clean(svc);
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
}
|
||||
break;
|
||||
|
||||
case SVC_RUNNING_STATE:
|
||||
if (!enabled) {
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1022,7 +1161,7 @@ restart:
|
||||
* Restart directly after the first crash,
|
||||
* then retry after 2 sec
|
||||
*/
|
||||
_d("delayed restart of %s", svc->desc);
|
||||
_d("delayed restart of %s", svc->cmd);
|
||||
service_timeout_after(svc, 1, service_retry);
|
||||
break;
|
||||
}
|
||||
@@ -1034,6 +1173,7 @@ restart:
|
||||
}
|
||||
|
||||
if (svc_is_runtask(svc)) {
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
svc->once++;
|
||||
break;
|
||||
}
|
||||
@@ -1043,7 +1183,6 @@ restart:
|
||||
switch (cond) {
|
||||
case COND_OFF:
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
@@ -1059,8 +1198,12 @@ restart:
|
||||
break;
|
||||
service_restart(svc);
|
||||
} else {
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd(svc))
|
||||
inetd_stop_children(&svc->inetd, 1);
|
||||
else
|
||||
#endif
|
||||
service_stop(svc);
|
||||
}
|
||||
svc_mark_clean(svc);
|
||||
}
|
||||
@@ -1072,7 +1215,6 @@ restart:
|
||||
if (!enabled) {
|
||||
kill(svc->pid, SIGCONT);
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1087,12 +1229,18 @@ restart:
|
||||
case COND_ON:
|
||||
kill(svc->pid, SIGCONT);
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
/* Reassert condition if we go from waiting and no change */
|
||||
if (!svc_is_changed(svc)) {
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
cond_set_path(cond_path(cond), COND_ON);
|
||||
}
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
kill(svc->pid, SIGCONT);
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
@@ -1103,9 +1251,17 @@ restart:
|
||||
|
||||
if (svc->state != old_state) {
|
||||
_d("%20s(%4d): -> %8s", svc->cmd, svc->pid, svc_status(svc));
|
||||
changed++;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a run/task/service changes state, e.g. transitioning from
|
||||
* waiting to running, other services may need to change state too.
|
||||
*/
|
||||
if (changed)
|
||||
schedule_work(&work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1114,6 +1270,11 @@ void service_step_all(int types)
|
||||
svc_foreach_type(types, service_step);
|
||||
}
|
||||
|
||||
void service_worker(void *unused)
|
||||
{
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_clean_runtask - Clear once flag of runtasks
|
||||
*
|
||||
@@ -1134,21 +1295,20 @@ void service_runtask_clean(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* service_runtask_completed - Have run/task completed in current runlevel
|
||||
* @skip: Hardcoded list of conditions to skip
|
||||
* service_completed - Have run/task completed in current runlevel
|
||||
*
|
||||
* This function checks if all run/task have run once in the current
|
||||
* runlevel. E.g., at bootstrap we must wait for these scripts or
|
||||
* programs to complete their run before switching to the configured
|
||||
* runlevel.
|
||||
*
|
||||
* Setting @skip means skipping tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP
|
||||
* set in their condition mask. These tasks cannot run until finalize()
|
||||
* All tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP set in their condition
|
||||
* mask are skipped. These tasks cannot run until finalize()
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) or %FALSE(0)
|
||||
*/
|
||||
int service_runtask_completed(int skip)
|
||||
int service_completed(void)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
@@ -1175,24 +1335,6 @@ int service_runtask_completed(int skip)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void service_bootstrap_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static int cnt = 10;
|
||||
void (*finalize)(void) = arg;
|
||||
|
||||
_d("Checking completion of all run/task ... %d", cnt);
|
||||
if (cnt-- > 0 && !service_runtask_completed(!runlevel))
|
||||
return;
|
||||
|
||||
if (cnt > 0)
|
||||
_d("All run/task have completed, resuming bootstrap.");
|
||||
else
|
||||
_d("Timeout, resuming bootstrap.");
|
||||
cnt = 10;
|
||||
uev_timer_stop(w);
|
||||
finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "svc.h"
|
||||
|
||||
void service_runlevel (int newlevel);
|
||||
int service_register (int type, char *line, struct rlimit rlimit[], char *file);
|
||||
int service_register (int type, char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||
void service_unregister (svc_t *svc);
|
||||
|
||||
void service_runtask_clean (void);
|
||||
@@ -36,8 +36,9 @@ void service_reload_dynamic (void);
|
||||
|
||||
int service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
void service_worker (void *unused);
|
||||
|
||||
void service_bootstrap_cb (uev_t *w, void *arg, int events);
|
||||
int service_completed (void);
|
||||
|
||||
#endif /* FINIT_SERVICE_H_ */
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
extern svc_t *wdog;
|
||||
|
||||
/*
|
||||
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
|
||||
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
|
||||
@@ -133,7 +135,9 @@ void do_kill(int signo)
|
||||
continue;
|
||||
|
||||
if (fgets(file, sizeof(file), fp)) {
|
||||
if (file[0] != '@')
|
||||
if (strstr(file, "gdbserver"))
|
||||
_d("Skipping %s ...", file);
|
||||
else if (file[0] != '@')
|
||||
kill(pid, signo);
|
||||
else
|
||||
_d("Skipping %s ...", &file[1]);
|
||||
@@ -183,8 +187,8 @@ void do_shutdown(shutop_t op)
|
||||
return;
|
||||
}
|
||||
|
||||
if (wdogpid) {
|
||||
print(kill(wdogpid, SIGPWR) == 1, "Advising watchdog, system going down");
|
||||
if (wdog) {
|
||||
print(kill(wdog->pid, SIGPWR) == 1, "Advising watchdog, system going down");
|
||||
do_sleep(2);
|
||||
}
|
||||
|
||||
@@ -206,11 +210,11 @@ void do_shutdown(shutop_t op)
|
||||
|
||||
/* Reboot via watchdog or kernel, or shutdown? */
|
||||
if (op == SHUT_REBOOT) {
|
||||
if (wdogpid) {
|
||||
if (wdog) {
|
||||
int timeout = 10;
|
||||
|
||||
/* Wait here until the WDT reboots, or timeout with fallback */
|
||||
print(kill(wdogpid, SIGTERM) == 1, "Pending watchdog reboot");
|
||||
print(kill(wdog->pid, SIGTERM) == 1, "Pending watchdog reboot");
|
||||
while (timeout--)
|
||||
do_sleep(1);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,22 @@ static char *sm_status(sm_state_t state)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable login in single user mode and shutdown/reboot
|
||||
*
|
||||
* Re-enable only when going from these runlevels, this way a user can
|
||||
* manage /etc/nologin manually within the other runlevels without us
|
||||
* pulling the rug from under their feet.
|
||||
*/
|
||||
static void nologin(void)
|
||||
{
|
||||
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
|
||||
touch("/etc/nologin");
|
||||
|
||||
if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6)
|
||||
erase("/etc/nologin");
|
||||
}
|
||||
|
||||
void sm_set_runlevel(sm_t *sm, int newlevel)
|
||||
{
|
||||
sm->newlevel = newlevel;
|
||||
@@ -137,10 +153,7 @@ restart:
|
||||
runlevel_set(prevlevel, runlevel);
|
||||
|
||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
|
||||
touch("/etc/nologin");
|
||||
else
|
||||
erase("/etc/nologin");
|
||||
nologin();
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (conf_any_change())
|
||||
@@ -149,7 +162,7 @@ restart:
|
||||
/* Reset once flag of runtasks */
|
||||
service_runtask_clean();
|
||||
|
||||
_d("Stopping services services not allowed in new runlevel ...");
|
||||
_d("Stopping services not allowed in new runlevel ...");
|
||||
sm->in_teardown = 1;
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
@@ -171,7 +184,7 @@ restart:
|
||||
_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 ...");
|
||||
_d("Starting services new to this runlevel ...");
|
||||
sm->in_teardown = 0;
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
@@ -205,7 +218,7 @@ restart:
|
||||
* Then, mark all affected service conditions as in-flux and
|
||||
* let all affected services move to WAITING/HALTED
|
||||
*/
|
||||
_d("Stopping services services not allowed after reconf ...");
|
||||
_d("Stopping services not allowed after reconf ...");
|
||||
sm->in_teardown = 1;
|
||||
cond_reload();
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
|
||||
@@ -115,6 +115,9 @@ void stty(int fd, speed_t speed)
|
||||
term.c_cc[VKILL] = CTRL('U');
|
||||
term.c_cc[VERASE] = CERASE;
|
||||
tcsetattr(fd, TCSANOW, &term);
|
||||
|
||||
/* Restore cursor if it was hidden previously */
|
||||
write(fd, "\033[?25h", 6);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <err.h>
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <sys/time.h>
|
||||
@@ -34,10 +35,38 @@
|
||||
#include "helpers.h"
|
||||
#include "pid.h"
|
||||
#include "util.h"
|
||||
#include "cond.h"
|
||||
#include "schedule.h"
|
||||
|
||||
/* Each svc_t needs a unique job# */
|
||||
static int jobcounter = 1;
|
||||
static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
||||
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
||||
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
|
||||
|
||||
static void svc_gc(void *arg)
|
||||
{
|
||||
struct timespec now;
|
||||
struct wq *work = (struct wq *)arg;
|
||||
svc_t *svc, *next;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
|
||||
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
|
||||
char cond[MAX_COND_LEN];
|
||||
int msec;
|
||||
|
||||
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
|
||||
(now.tv_nsec - svc->gc.tv_nsec) / 1000000;
|
||||
if (msec < SVC_TERM_TIMEOUT)
|
||||
continue;
|
||||
|
||||
TAILQ_REMOVE(&gc_list, svc, link);
|
||||
cond_clear(mkcond(cond, sizeof(cond), svc->cmd));
|
||||
free(svc);
|
||||
}
|
||||
|
||||
if (!TAILQ_EMPTY(&gc_list))
|
||||
schedule_work(work);
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_new - Create a new service
|
||||
@@ -48,10 +77,9 @@ static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
||||
* Returns:
|
||||
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
|
||||
*/
|
||||
svc_t *svc_new(char *cmd, int id, int type)
|
||||
svc_t *svc_new(char *cmd, char *id, int type)
|
||||
{
|
||||
int job = -1;
|
||||
char *desc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
/* Find first job n:o if registering multiple instances */
|
||||
@@ -70,24 +98,24 @@ svc_t *svc_new(char *cmd, int id, int type)
|
||||
|
||||
svc->type = type;
|
||||
svc->job = job;
|
||||
svc->id = id;
|
||||
strlcpy(svc->id, id, sizeof(svc->id));
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
|
||||
/* Default description, if missing */
|
||||
desc = rindex(cmd, '/');
|
||||
if (desc)
|
||||
desc++;
|
||||
else
|
||||
desc = cmd;
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
|
||||
|
||||
TAILQ_INSERT_TAIL(&svc_list, svc, link);
|
||||
|
||||
return svc;
|
||||
}
|
||||
|
||||
static struct wq work = {
|
||||
.cb = svc_gc,
|
||||
.delay = SVC_TERM_TIMEOUT
|
||||
};
|
||||
|
||||
/**
|
||||
* svc_del - Delete a service object
|
||||
* svc_del - Mark a service object for deletion
|
||||
* @svc: Pointer to an &svc_t object
|
||||
*
|
||||
* Returns:
|
||||
@@ -96,8 +124,10 @@ svc_t *svc_new(char *cmd, int id, int type)
|
||||
int svc_del(svc_t *svc)
|
||||
{
|
||||
TAILQ_REMOVE(&svc_list, svc, link);
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
free(svc);
|
||||
TAILQ_INSERT_TAIL(&gc_list, svc, link);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_COARSE, &svc->gc);
|
||||
schedule_work(&work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -168,7 +198,7 @@ svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||
char *name = basename(svc->cmd);
|
||||
char *name = svc->name;
|
||||
|
||||
if (!strncmp(name, cmd, strlen(name)))
|
||||
return svc;
|
||||
@@ -265,12 +295,12 @@ svc_t *svc_stop_completed(void)
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find(char *cmd, int id)
|
||||
svc_t *svc_find(char *cmd, char *id)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
||||
if (svc->id && !strcmp(svc->id, id) && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
||||
return svc;
|
||||
}
|
||||
|
||||
@@ -304,12 +334,12 @@ svc_t *svc_find_by_pid(pid_t pid)
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find_by_jobid(int job, int id)
|
||||
svc_t *svc_find_by_jobid(int job, char *id)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->job == job && svc->id == id)
|
||||
if (svc->job == job && svc->id && !strcmp(svc->id, id))
|
||||
return svc;
|
||||
}
|
||||
|
||||
@@ -324,19 +354,12 @@ svc_t *svc_find_by_jobid(int job, int id)
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find_by_nameid(char *name, int id)
|
||||
svc_t *svc_find_by_nameid(char *name, char *id)
|
||||
{
|
||||
char *ptr;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
ptr = strrchr(svc->cmd, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = svc->cmd;
|
||||
|
||||
if (svc->id == id && !strcmp(name, ptr))
|
||||
if (svc->id && !strcmp(svc->id, id) && !strcmp(name, svc->name))
|
||||
return svc;
|
||||
}
|
||||
|
||||
@@ -379,7 +402,9 @@ void svc_mark_dynamic(void)
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->protected)
|
||||
if (svc->protect)
|
||||
continue;
|
||||
if (svc_is_inetd_conn(svc))
|
||||
continue;
|
||||
|
||||
*((int *)&svc->dirty) = -1;
|
||||
@@ -408,10 +433,8 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->dirty == -1 && cb) {
|
||||
if (svc->dirty == -1 && cb)
|
||||
cb(svc);
|
||||
svc_mark_clean(svc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,18 +496,24 @@ int svc_enabled(svc_t *svc)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Same base service, return unique ID */
|
||||
int svc_next_id(char *cmd)
|
||||
/* Same base service, return unique ID as an integer */
|
||||
int svc_next_id_int(char *cmd)
|
||||
{
|
||||
int id = 0;
|
||||
int n = 1;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!strcmp(svc->cmd, cmd) && id < svc->id)
|
||||
id = svc->id;
|
||||
char id[MAX_ID_LEN];
|
||||
|
||||
snprintf(id, sizeof(id), "%d", n);
|
||||
|
||||
if (!strcmp(svc->cmd, cmd) && svc->id && strcmp(svc->id, id))
|
||||
return n;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
return id + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int svc_is_unique(svc_t *svc)
|
||||
@@ -511,7 +540,7 @@ int svc_is_unique(svc_t *svc)
|
||||
/*
|
||||
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
|
||||
*/
|
||||
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int))
|
||||
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *))
|
||||
{
|
||||
int result = 0;
|
||||
char *input, *token, *pos;
|
||||
@@ -522,17 +551,27 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
||||
|
||||
token = strtok_r(input, " ", &pos);
|
||||
while (token) {
|
||||
int id = 1;
|
||||
char *id = NULL;
|
||||
svc_t *svc, *iter = NULL;
|
||||
char *ptr = strchr(token, ':');
|
||||
|
||||
if (isdigit(token[0])) {
|
||||
int job = atonum(token);
|
||||
char *ep;
|
||||
long job = 0;
|
||||
|
||||
errno = 0;
|
||||
job = strtol(token, &ep, 10);
|
||||
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
|
||||
(errno != 0 && job == 0) ||
|
||||
(token == ep)) {
|
||||
result++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ptr) {
|
||||
svc = svc_job_iterator(&iter, 1, job);
|
||||
if (!svc && not_found)
|
||||
result += not_found(NULL, job);
|
||||
result += not_found(NULL, token);
|
||||
|
||||
while (svc) {
|
||||
if (found)
|
||||
@@ -541,8 +580,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
id = atonum(ptr);
|
||||
job = atonum(token);
|
||||
id = ptr;
|
||||
|
||||
svc = svc_find_by_jobid(job, id);
|
||||
if (!svc && not_found)
|
||||
@@ -563,7 +601,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
id = atonum(ptr);
|
||||
id = ptr;
|
||||
|
||||
svc = svc_find_by_nameid(token, id);
|
||||
if (!svc && not_found)
|
||||
|
||||
@@ -66,6 +66,7 @@ typedef enum {
|
||||
SVC_BLOCK_RESTARTING,
|
||||
} svc_block_t;
|
||||
|
||||
#define MAX_ID_LEN 16
|
||||
#define MAX_ARG_LEN 64
|
||||
#define MAX_STR_LEN 64
|
||||
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
||||
@@ -73,6 +74,9 @@ typedef enum {
|
||||
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
||||
#define MAX_NUM_SVC_ARGS 32
|
||||
|
||||
/* Time after SIGTERM that we SIGKILL stopping processes */
|
||||
#define SVC_TERM_TIMEOUT 3000
|
||||
|
||||
/*
|
||||
* Default enable for all services, can be stopped by means
|
||||
* of issuing an initctl call. E.g.
|
||||
@@ -83,24 +87,29 @@ typedef struct svc {
|
||||
TAILQ_ENTRY(svc) link;
|
||||
|
||||
/* Instance specifics */
|
||||
int job, id; /* JOB:ID */
|
||||
int job; /* JOB: */
|
||||
char id[MAX_ID_LEN]; /* :ID */
|
||||
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
/* Control group */
|
||||
char cgroup[32];
|
||||
|
||||
/* Service details */
|
||||
pid_t pid;
|
||||
char pidfile[MAX_ARG_LEN];
|
||||
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
svc_type_t type; /* Service, run, task, inetd, ... */
|
||||
int protected; /* Services like dbus-daemon & udev by Finit */
|
||||
int protect; /* Services like dbus-daemon & udev by Finit */
|
||||
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
|
||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||
int runlevels;
|
||||
int sighup; /* This service supports SIGHUP :) */
|
||||
svc_block_t block; /* Reason that this service is currently stopped */
|
||||
char cond[MAX_COND_LEN];
|
||||
char name[MAX_ARG_LEN];
|
||||
|
||||
/* Counters */
|
||||
char once; /* run/task, (at least) once per runlevel */
|
||||
@@ -109,11 +118,13 @@ typedef struct svc {
|
||||
/* For inetd services */
|
||||
inetd_t inetd;
|
||||
int stdin_fd;
|
||||
char iifname[IF_NAMESIZE + 1]; /* Ingress interface for connection */
|
||||
|
||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
||||
struct {
|
||||
char enabled;
|
||||
char null;
|
||||
char console;
|
||||
char file[64];
|
||||
char prio[20];
|
||||
char ident[20];
|
||||
@@ -134,15 +145,18 @@ typedef struct svc {
|
||||
*/
|
||||
uev_t timer;
|
||||
void (*timer_cb)(struct svc *svc);
|
||||
|
||||
/* time at svc_del(), used by gc timer */
|
||||
struct timespec gc;
|
||||
} svc_t;
|
||||
|
||||
svc_t *svc_new (char *cmd, int id, int type);
|
||||
svc_t *svc_new (char *cmd, char *id, int type);
|
||||
int svc_del (svc_t *svc);
|
||||
|
||||
svc_t *svc_find (char *cmd, int id);
|
||||
svc_t *svc_find (char *cmd, char *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_find_by_jobid (int job, char *id);
|
||||
svc_t *svc_find_by_nameid (char *name, char *id);
|
||||
svc_t *svc_find_by_pidfile (char *fn);
|
||||
|
||||
svc_t *svc_iterator (svc_t **iter, int first);
|
||||
@@ -163,10 +177,10 @@ int svc_clean_bootstrap (svc_t *svc);
|
||||
void svc_prune_bootstrap (void);
|
||||
|
||||
int svc_enabled (svc_t *svc);
|
||||
int svc_next_id (char *cmd);
|
||||
int svc_next_id_int (char *cmd);
|
||||
int svc_is_unique (svc_t *svc);
|
||||
|
||||
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int));
|
||||
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *));
|
||||
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||
@@ -177,26 +191,29 @@ static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && IS
|
||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
||||
|
||||
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
|
||||
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
|
||||
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
|
||||
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
||||
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
|
||||
|
||||
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_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
||||
#define svc_start(svc) svc_unblock(svc)
|
||||
static inline void svc_stop (svc_t *svc) { svc->block = SVC_BLOCK_USER; }
|
||||
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; }
|
||||
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; }
|
||||
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
|
||||
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
|
||||
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
|
||||
static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_BUSY; }
|
||||
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
|
||||
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
|
||||
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
|
||||
|
||||
static inline char *svc_status(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return "Unknown";
|
||||
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
switch (svc->block) {
|
||||
@@ -218,6 +235,7 @@ static inline char *svc_status(svc_t *svc)
|
||||
case SVC_BLOCK_RESTARTING:
|
||||
return "restart";
|
||||
}
|
||||
return "unknown";
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
return "done";
|
||||
@@ -249,6 +267,9 @@ static inline char *svc_status(svc_t *svc)
|
||||
|
||||
static inline const char *svc_dirtystr(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return "Unknown";
|
||||
|
||||
if (svc_is_removed(svc))
|
||||
return "removed";
|
||||
if (svc_is_updated(svc))
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "tty.h"
|
||||
@@ -41,32 +42,16 @@
|
||||
#ifdef FALLBACK_SHELL
|
||||
static pid_t fallback = 0;
|
||||
#endif
|
||||
static LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
||||
static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER();
|
||||
|
||||
static char *canonicalize(char *tty)
|
||||
{
|
||||
char buf[42];
|
||||
struct stat st;
|
||||
static char path[80];
|
||||
|
||||
if (!tty)
|
||||
return NULL;
|
||||
|
||||
/* Auto-detect serial console, for embedded devices mostly */
|
||||
if (!strcmp(tty, "@console")) {
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/sys/class/tty/console/active", "r");
|
||||
if (!fp) {
|
||||
_e("Cannot find system console, is sysfs not mounted?");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
tty = chomp(buf);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
strlcpy(path, tty, sizeof(path));
|
||||
if (stat(path, &st)) {
|
||||
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
|
||||
@@ -88,7 +73,7 @@ static char *canonicalize(char *tty)
|
||||
|
||||
void tty_mark(void)
|
||||
{
|
||||
tty_node_t *tty;
|
||||
struct tty *tty;
|
||||
|
||||
LIST_FOREACH(tty, &tty_list, link)
|
||||
tty->dirty = -1;
|
||||
@@ -96,17 +81,17 @@ void tty_mark(void)
|
||||
|
||||
void tty_sweep(void)
|
||||
{
|
||||
tty_node_t *tty, *tmp;
|
||||
struct tty *tty, *tmp;
|
||||
|
||||
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
|
||||
if (!tty->dirty)
|
||||
continue;
|
||||
|
||||
_d("TTY %s dirty, stopping ...", tty->data.name);
|
||||
tty_stop(&tty->data);
|
||||
_d("TTY %s dirty, stopping ...", tty->name);
|
||||
tty_stop(tty);
|
||||
|
||||
if (tty->dirty == -1) {
|
||||
_d("TTY %s removed, cleaning up.", tty->data.name);
|
||||
_d("TTY %s removed, cleaning up.", tty->name);
|
||||
tty_unregister(tty);
|
||||
}
|
||||
}
|
||||
@@ -116,6 +101,7 @@ void tty_sweep(void)
|
||||
* tty_register - Register a getty on a device
|
||||
* @line: Configuration, text after initial "tty"
|
||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||
* @cgroup: Control group, may be default
|
||||
* @file: The file name TTY was loaded from
|
||||
*
|
||||
* A Finit tty line can use the internal getty implementation or an
|
||||
@@ -134,14 +120,14 @@ void tty_sweep(void)
|
||||
* Different getty implementations prefer the TTY device argument in
|
||||
* different order, so take care to investigate this first.
|
||||
*/
|
||||
int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
int tty_register(char *line, struct rlimit rlimit[], char *cgroup, char *file)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
int insert = 0, noclear = 0, nowait = 0, nologin = 0;
|
||||
struct tty *entry;
|
||||
size_t i, num = 0;
|
||||
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS];
|
||||
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS], buf[256];
|
||||
char *dev = NULL, *baud = NULL;
|
||||
char *runlevels = NULL, *term = NULL;
|
||||
int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0;
|
||||
|
||||
if (!line) {
|
||||
_e("Missing argument");
|
||||
@@ -220,6 +206,24 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
/* Auto-detect serial console, for embedded devices mostly */
|
||||
if (!strcmp(dev, "@console")) {
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/sys/class/tty/console/active", "r");
|
||||
if (!fp) {
|
||||
_e("Cannot find system console, is sysfs not mounted?");
|
||||
return errno = ENOENT;
|
||||
}
|
||||
|
||||
atcon = 1;
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
dev = strtok(chomp(buf), " \t");
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
again:
|
||||
/* Ensure all getty (built-in + external) are registered with absolute path */
|
||||
dev = canonicalize(dev);
|
||||
if (!dev)
|
||||
@@ -238,13 +242,13 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
}
|
||||
|
||||
entry->data.name = dev;
|
||||
entry->data.baud = baud ? strdup(baud) : NULL;
|
||||
entry->data.term = term ? strdup(term) : NULL;
|
||||
entry->data.noclear = noclear;
|
||||
entry->data.nowait = nowait;
|
||||
entry->data.nologin = nologin;
|
||||
entry->data.runlevels = conf_parse_runlevels(runlevels);
|
||||
strlcpy(entry->name, dev, sizeof(entry->name));
|
||||
strlcpy(entry->baud, baud ? baud : "", sizeof(entry->baud));
|
||||
strlcpy(entry->term, term ? term : "", sizeof(entry->term));
|
||||
entry->noclear = noclear;
|
||||
entry->nowait = nowait;
|
||||
entry->nologin = nologin;
|
||||
entry->runlevels = conf_parse_runlevels(runlevels);
|
||||
|
||||
/* External getty */
|
||||
if (cmd) {
|
||||
@@ -255,12 +259,12 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
tok = cmd;
|
||||
else
|
||||
tok++;
|
||||
entry->data.cmd = cmd;
|
||||
entry->cmd = cmd;
|
||||
args[1] = strdup(tok);
|
||||
|
||||
for (i = 1; i < num; i++)
|
||||
entry->data.args[j++] = strdup(args[i]);
|
||||
entry->data.args[++j] = NULL;
|
||||
entry->args[j++] = strdup(args[i]);
|
||||
entry->args[++j] = NULL;
|
||||
}
|
||||
|
||||
_d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s",
|
||||
@@ -270,7 +274,10 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||
|
||||
/* Register configured limits */
|
||||
memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit));
|
||||
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
|
||||
|
||||
/* Register desired control group */
|
||||
strlcpy(entry->cgroup, cgroup, sizeof(entry->cgroup));
|
||||
|
||||
if (file && conf_changed(file))
|
||||
entry->dirty = 1; /* Modified, restart */
|
||||
@@ -278,10 +285,18 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
entry->dirty = 0; /* Not modified */
|
||||
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
|
||||
|
||||
if (atcon) {
|
||||
dev = strtok(NULL, " \t");
|
||||
if (dev) {
|
||||
_d("Found another @console: %s", dev);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tty_unregister(tty_node_t *tty)
|
||||
int tty_unregister(struct tty *tty)
|
||||
{
|
||||
if (!tty) {
|
||||
_e("Missing argument");
|
||||
@@ -290,20 +305,14 @@ int tty_unregister(tty_node_t *tty)
|
||||
|
||||
LIST_REMOVE(tty, link);
|
||||
|
||||
if (tty->data.name)
|
||||
free(tty->data.name);
|
||||
if (tty->data.baud)
|
||||
free(tty->data.baud);
|
||||
if (tty->data.term)
|
||||
free(tty->data.term);
|
||||
if (tty->data.cmd) {
|
||||
if (tty->cmd) {
|
||||
int i;
|
||||
|
||||
free(tty->data.cmd);
|
||||
free(tty->cmd);
|
||||
for (i = 0; i < TTY_MAX_ARGS; i++) {
|
||||
if (tty->data.args[i])
|
||||
free(tty->data.args[i]);
|
||||
tty->data.args[i] = NULL;
|
||||
if (tty->args[i])
|
||||
free(tty->args[i]);
|
||||
tty->args[i] = NULL;
|
||||
}
|
||||
}
|
||||
free(tty);
|
||||
@@ -311,12 +320,12 @@ int tty_unregister(tty_node_t *tty)
|
||||
return 0;
|
||||
}
|
||||
|
||||
tty_node_t *tty_find(char *dev)
|
||||
struct tty *tty_find(char *dev)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
struct tty *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (!strcmp(dev, entry->data.name))
|
||||
if (!strcmp(dev, entry->name))
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -326,7 +335,7 @@ tty_node_t *tty_find(char *dev)
|
||||
size_t tty_num(void)
|
||||
{
|
||||
size_t num = 0;
|
||||
tty_node_t *entry;
|
||||
struct tty *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link)
|
||||
num++;
|
||||
@@ -337,22 +346,22 @@ size_t tty_num(void)
|
||||
size_t tty_num_active(void)
|
||||
{
|
||||
size_t num = 0;
|
||||
tty_node_t *entry;
|
||||
struct tty *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (entry->data.pid)
|
||||
if (entry->pid)
|
||||
num++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
tty_node_t *tty_find_by_pid(pid_t pid)
|
||||
struct tty *tty_find_by_pid(pid_t pid)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
struct tty *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (entry->data.pid == pid)
|
||||
if (entry->pid == pid)
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -375,7 +384,7 @@ static int tty_exist(char *dev)
|
||||
return result;
|
||||
}
|
||||
|
||||
void tty_start(finit_tty_t *tty)
|
||||
void tty_start(struct tty *tty)
|
||||
{
|
||||
char *dev;
|
||||
|
||||
@@ -396,17 +405,21 @@ void tty_start(finit_tty_t *tty)
|
||||
}
|
||||
|
||||
if (tty->nologin) {
|
||||
_d("%s: Starting /bin/sh ...", dev);
|
||||
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : "");
|
||||
if (!tty->cmd)
|
||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
||||
else
|
||||
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
|
||||
|
||||
cgroup_assign(tty->cgroup, tty->pid);
|
||||
}
|
||||
|
||||
void tty_stop(finit_tty_t *tty)
|
||||
void tty_stop(struct tty *tty)
|
||||
{
|
||||
if (!tty->pid)
|
||||
return;
|
||||
@@ -423,7 +436,7 @@ void tty_stop(finit_tty_t *tty)
|
||||
tty->pid = 0;
|
||||
}
|
||||
|
||||
int tty_enabled(finit_tty_t *tty)
|
||||
int tty_enabled(struct tty *tty)
|
||||
{
|
||||
if (!tty)
|
||||
return 0;
|
||||
@@ -471,12 +484,12 @@ int tty_fallback(pid_t lost)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tty_action(tty_node_t *tty)
|
||||
static void tty_action(struct tty *tty)
|
||||
{
|
||||
if (!tty_enabled(&tty->data))
|
||||
tty_stop(&tty->data);
|
||||
if (!tty_enabled(tty))
|
||||
tty_stop(tty);
|
||||
else
|
||||
tty_start(&tty->data);
|
||||
tty_start(tty);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -484,7 +497,7 @@ static void tty_action(tty_node_t *tty)
|
||||
*/
|
||||
int tty_respawn(pid_t pid)
|
||||
{
|
||||
tty_node_t *tty = tty_find_by_pid(pid);
|
||||
struct tty *tty = tty_find_by_pid(pid);
|
||||
|
||||
if (!tty)
|
||||
return tty_fallback(pid);
|
||||
@@ -493,7 +506,7 @@ int tty_respawn(pid_t pid)
|
||||
utmp_set_dead(pid);
|
||||
|
||||
/* Clear PID to be able to respawn it. */
|
||||
tty->data.pid = 0;
|
||||
tty->pid = 0;
|
||||
tty_action(tty);
|
||||
|
||||
return 1;
|
||||
@@ -504,7 +517,7 @@ int tty_respawn(pid_t pid)
|
||||
*/
|
||||
void tty_reload(char *dev)
|
||||
{
|
||||
tty_node_t *tty;
|
||||
struct tty *tty;
|
||||
|
||||
if (dev) {
|
||||
tty = tty_find(dev);
|
||||
@@ -529,7 +542,7 @@ void tty_reload(char *dev)
|
||||
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
||||
void tty_runlevel(void)
|
||||
{
|
||||
tty_node_t *tty;
|
||||
struct tty *tty;
|
||||
|
||||
LIST_FOREACH(tty, &tty_list, link)
|
||||
tty_action(tty);
|
||||
|
||||
@@ -32,10 +32,12 @@
|
||||
#define TTY_MAX_ARGS 16
|
||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *baud;
|
||||
char *term;
|
||||
struct tty {
|
||||
LIST_ENTRY(tty) link;
|
||||
|
||||
char name[42];
|
||||
char baud[10];
|
||||
char term[10];
|
||||
int noclear;
|
||||
int nowait;
|
||||
int nologin;
|
||||
@@ -48,32 +50,27 @@ typedef struct {
|
||||
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
} finit_tty_t;
|
||||
|
||||
typedef struct tty_node {
|
||||
LIST_ENTRY(tty_node) link;
|
||||
finit_tty_t data;
|
||||
/* Control group */
|
||||
char cgroup[32];
|
||||
|
||||
/* XXX: Yes, TTYs should be refactored into a separate SVC type. */
|
||||
int dirty; /* Set if old mtime != new mtime => reloaded,
|
||||
* or -1 when marked for removal */
|
||||
} tty_node_t;
|
||||
|
||||
//extern LIST_HEAD(, tty_node) tty_list;
|
||||
/* Set if modified => reloaded, or -1 when marked for removal */
|
||||
int dirty;
|
||||
};
|
||||
|
||||
void tty_mark (void);
|
||||
void tty_sweep (void);
|
||||
|
||||
int tty_register (char *line, struct rlimit rlimit[], char *file);
|
||||
int tty_unregister (tty_node_t *tty);
|
||||
int tty_register (char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||
int tty_unregister (struct tty *tty);
|
||||
|
||||
tty_node_t *tty_find (char *dev);
|
||||
struct tty *tty_find (char *dev);
|
||||
size_t tty_num (void);
|
||||
size_t tty_num_active (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);
|
||||
struct tty *tty_find_by_pid (pid_t pid);
|
||||
void tty_start (struct tty *tty);
|
||||
void tty_stop (struct tty *tty);
|
||||
int tty_enabled (struct tty *tty);
|
||||
int tty_respawn (pid_t pid);
|
||||
void tty_reload (char *dev);
|
||||
void tty_runlevel (void);
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <ctype.h> /* isprint() */
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <lite/lite.h> /* strlcat() */
|
||||
@@ -45,18 +47,67 @@ char *progname(char *arg0)
|
||||
return prognm;
|
||||
}
|
||||
|
||||
int echo(char *file, int append, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
FILE *fp;
|
||||
|
||||
if (!file)
|
||||
fp = stdout;
|
||||
else
|
||||
fp = fopen(file, append ? "a" : "w");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
if (fmt) {
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fp, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* echo(1) always adds a newline */
|
||||
fprintf(fp, "\n");
|
||||
if (file)
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strtobytes(char *arg)
|
||||
{
|
||||
int mod = 0, bytes;
|
||||
size_t pos;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
|
||||
pos = strspn(arg, "0123456789");
|
||||
if (arg[pos] != 0) {
|
||||
if (arg[pos] == 'G')
|
||||
mod = 3;
|
||||
else if (arg[pos] == 'M')
|
||||
mod = 2;
|
||||
else if (arg[pos] == 'k')
|
||||
mod = 1;
|
||||
else
|
||||
return -1;
|
||||
|
||||
arg[pos] = 0;
|
||||
}
|
||||
|
||||
bytes = atoi(arg);
|
||||
while (mod--)
|
||||
bytes *= 1000;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
while ((sec = sleep(sec)))
|
||||
;
|
||||
}
|
||||
|
||||
/* Allowed characters in job/id/name */
|
||||
static int isallowed(int ch)
|
||||
{
|
||||
return isprint(ch);
|
||||
}
|
||||
|
||||
/* Seconds since boot, from sysinfo() */
|
||||
long jiffies(void)
|
||||
{
|
||||
@@ -108,6 +159,12 @@ char *uptime(long secs, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Allowed characters in job/id/name */
|
||||
static int isallowed(int ch)
|
||||
{
|
||||
return isprint(ch);
|
||||
}
|
||||
|
||||
/* Sanitize user input, make sure to NUL terminate. */
|
||||
char *sanitize(char *arg, size_t len)
|
||||
{
|
||||
@@ -127,6 +184,10 @@ char *sanitize(char *arg, size_t len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at boot, and shutdown, to (re)initialize the
|
||||
* screen size for print() et al.
|
||||
*/
|
||||
void screen_init(void)
|
||||
{
|
||||
if (!isatty(STDOUT_FILENO))
|
||||
@@ -135,6 +196,16 @@ void screen_init(void)
|
||||
initscr(&screen_rows, &screen_cols);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when debug mode is enabled to revert back
|
||||
* to old-school safe defaults.
|
||||
*/
|
||||
void screen_exit(void)
|
||||
{
|
||||
screen_rows = 24;
|
||||
screen_cols = 80;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
@@ -39,6 +39,8 @@ extern char *prognm;
|
||||
#include <lite/conio.h>
|
||||
|
||||
char *progname (char *arg0);
|
||||
int echo (char *file, int append, char *fmt, ...);
|
||||
int strtobytes (char *arg);
|
||||
void do_sleep (unsigned int sec);
|
||||
|
||||
long jiffies (void);
|
||||
@@ -47,6 +49,7 @@ char *uptime (long secs, char *buf, size_t len);
|
||||
char *sanitize (char *arg, size_t len);
|
||||
|
||||
void screen_init (void);
|
||||
void screen_exit (void);
|
||||
int screen_width (void);
|
||||
|
||||
#endif /* FINIT_UTIL_H_ */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Built-in watchdog daemon
|
||||
*
|
||||
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2016-2018 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
|
||||
@@ -9,24 +9,30 @@
|
||||
* 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 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.
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "log.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
int running = 1;
|
||||
@@ -51,6 +57,9 @@ static int init(char *progname, char *devnode)
|
||||
signal(SIGTERM, sighandler);
|
||||
signal(SIGPWR, sighandler);
|
||||
|
||||
openlog(&progname[1], LOG_CONS | LOG_PID, LOG_DAEMON);
|
||||
syslog(LOG_INFO, "Finit v%s basic watchdogd starting ...", VERSION);
|
||||
|
||||
fd = open(devnode, O_WRONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
@@ -78,6 +87,7 @@ static int loop(int fd, int timeout)
|
||||
|
||||
/* External watchdogd wants to take over ... */
|
||||
if (handover) {
|
||||
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
return !write(fd, "V", 1);
|
||||
}
|
||||
@@ -85,34 +95,30 @@ static int loop(int fd, int timeout)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int watchdog(char *progname)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int pid;
|
||||
int fd, ret;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
int fd, ret;
|
||||
|
||||
fd = init(progname, WDT_DEVNODE);
|
||||
if (fd == -1) {
|
||||
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
ret = loop(fd, WDT_TIMEOUT);
|
||||
while (!handover) {
|
||||
/* Waiting for SIGTERM ... */
|
||||
sleep(1);
|
||||
|
||||
/* Set lowest possible timeout on SIGTERM */
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
_exit(ret);
|
||||
fd = init(argv[0], WDT_DEVNODE);
|
||||
if (fd == -1) {
|
||||
if (ENOENT != errno)
|
||||
syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return pid;
|
||||
ret = loop(fd, WDT_TIMEOUT);
|
||||
while (!handover) {
|
||||
syslog(LOG_ALERT, "System going down ...");
|
||||
|
||||
/* Waiting for SIGTERM ... */
|
||||
sleep(1);
|
||||
|
||||
/* Set lowest possible timeout on SIGTERM */
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||