mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
c616270ccb | ||
|
|
890ac2f4e1 | ||
|
|
8063b1bfe1 | ||
|
|
0bcfb464ce | ||
|
|
dc5b1a5980 | ||
|
|
14555d7a7b | ||
|
|
a06127b990 | ||
|
|
3b8e7d677f | ||
|
|
097fce4170 | ||
|
|
306bf0f191 | ||
|
|
f25390004b | ||
|
|
cbd4cc8d34 | ||
|
|
44591c6e51 | ||
|
|
0e51a1eed8 | ||
|
|
c315fb5637 | ||
|
|
1017e7b8e5 | ||
|
|
8ef216af62 | ||
|
|
a9b9631098 | ||
|
|
cc186334e0 | ||
|
|
e1b87d707f | ||
|
|
68e7bcba4c | ||
|
|
f9783b4b41 | ||
|
|
32d9afc7bb | ||
|
|
91b550662e | ||
|
|
d87af8270e | ||
|
|
366c23f5d1 | ||
|
|
0d4bc69584 | ||
|
|
cccf199280 | ||
|
|
a1c33dec21 | ||
|
|
df303c6243 | ||
|
|
6a6ae6fa3b | ||
|
|
4011b5a3b8 | ||
|
|
68ef6ec108 | ||
|
|
89a9d92d6c | ||
|
|
15cb576293 | ||
|
|
90734eee16 | ||
|
|
71915e0e14 | ||
|
|
112ab2c22f | ||
|
|
8f00ac2e90 | ||
|
|
6fb070c75c | ||
|
|
8fd431f3e9 | ||
|
|
50f128cd1d | ||
|
|
f6fa576270 | ||
|
|
b26d30757d | ||
|
|
dc9d0b6fca | ||
|
|
ff95ca92b1 | ||
|
|
c939aed87f | ||
|
|
71ea9e2ea3 | ||
|
|
b6bd10d960 | ||
|
|
87d8157175 | ||
|
|
cf6faa9eeb | ||
|
|
80a71aff07 | ||
|
|
04a4653e9b | ||
|
|
3aa9d9f451 | ||
|
|
c1afca9c33 | ||
|
|
205915d42f | ||
|
|
631f89b58c | ||
|
|
6d1c245985 | ||
|
|
fc08b0c65b | ||
|
|
83021b11d8 | ||
|
|
5309ece2f9 | ||
|
|
6480850ee2 | ||
|
|
eaa82377c4 | ||
|
|
5b3df3654c | ||
|
|
55cb50ef90 | ||
|
|
3f7e03e813 | ||
|
|
ab723d2f31 | ||
|
|
bc149d95fc | ||
|
|
c605874b26 | ||
|
|
c33ba6a477 | ||
|
|
63199d368f | ||
|
|
db3263c441 | ||
|
|
925212f7cf | ||
|
|
ad401280c2 | ||
|
|
f3d2586417 | ||
|
|
6fc1d27bcf | ||
|
|
f3305e75b7 | ||
|
|
e4c12314b1 | ||
|
|
c9a1a64ce5 | ||
|
|
3843f62aa8 | ||
|
|
ef54c4826b | ||
|
|
2fac65becb | ||
|
|
46b253dea6 | ||
|
|
b04983c3f3 | ||
|
|
f2d142ef57 | ||
|
|
33ce746e58 | ||
|
|
6ecb86b4a7 | ||
|
|
338810b886 | ||
|
|
c1344e89ff | ||
|
|
bc35b42bd2 | ||
|
|
dcf8fbe235 | ||
|
|
c82a2ce215 | ||
|
|
e1bb5e3396 | ||
|
|
8198582c10 | ||
|
|
0d19842aa1 | ||
|
|
6f665ab283 | ||
|
|
3f731aa5a3 | ||
|
|
9e8a01669e | ||
|
|
cbafbaea1d | ||
|
|
f534368b33 | ||
|
|
78fc9fccc5 | ||
|
|
3f7df3d35e | ||
|
|
9fc33418f9 | ||
|
|
72bc3b1a91 | ||
|
|
d6ea1c0cab | ||
|
|
523a270f22 | ||
|
|
3258edc246 | ||
|
|
32b7a14cb3 |
+8
-1
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
+47
-4
@@ -4,17 +4,34 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[3.1][] - 2017-12-xx
|
||||
[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
|
||||
|
||||
* Support for more kernel command line settings:
|
||||
- splash, enable boot progress
|
||||
- debug, like `--debug` but also enable kernel debug
|
||||
- single, single user mode (no network)
|
||||
- rescue, new rescue mode
|
||||
* Support for `IFF_RUNNING` to netlink plugin => `net/IFNAME/running`
|
||||
* Support for restarting `initctl` API socket on `SIGHUP`
|
||||
* Greatly updated `initctl status <JOB|NAME>` command
|
||||
* Support for `rlimit` per service/run/task/inetd/tty, issue #45
|
||||
* Support for setting `hard` and `soft` rlimit for a resource at once
|
||||
* 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.
|
||||
* 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 ...`
|
||||
@@ -25,6 +42,9 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
of inetd services, and `Loading configuration ...`. No end user knows
|
||||
what those plugins and configurations are, i.e. internal state+config
|
||||
* Change kernel WDT timeout (3 --> 30 sec) for built-in watchdog daemon
|
||||
* Advise watchdog dawmon on shutdown and reboot using `SIGPWR` and then
|
||||
`SIGTERM`. It is recommended the daemon start a timer on the first
|
||||
signal, in case the shutdown process somehow hangs.
|
||||
* Handle `/etc/` OverlayFS, reload /etc/finit.d/*.conf after `mount -a`
|
||||
* initctl: Add support for printing previous runlevel
|
||||
* initctl: Support short forms of all commands
|
||||
@@ -38,6 +58,25 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
on mtime of `.conf` files, instead an inotify handler tracks file
|
||||
changes for time *insensitive* dependency tracking
|
||||
* Change condition handling to not rely on mtime but a generation id.
|
||||
* New configure script option `--enable-redirect` to automatically
|
||||
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
|
||||
* 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
|
||||
|
||||
@@ -57,6 +96,8 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
plugin before the pidfile plugin on `HOOK_SVC_RECONF` events
|
||||
* Handle event loop failure modes, issue found by Westermo
|
||||
* Handle API socket errors more gracefully, restart socket
|
||||
* Do not attempt to load kernel modules more than once at bootstrap
|
||||
* Remove reboot symlinks properly on uninstall
|
||||
* Fix regression in condition handling, reconf condition must be kept
|
||||
as a reference point to previous reconfiguration, or bootstrap.
|
||||
* Fix nasty race condition with internal service stop, abort kill if the
|
||||
@@ -70,6 +111,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
|
||||
@@ -97,7 +139,7 @@ distributions.
|
||||
* Ensure plugins always have a default name, file name
|
||||
* Reorganization, move all source files to a `src/` sub-directory
|
||||
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
|
||||
distributions. See [doc/distro.md](doc/distro.md) for details
|
||||
distributions. See [docs/distro.md](docs/distro.md) for details
|
||||
* Remove `UNUSED()` macro, mentioned here because it may have been used
|
||||
by external plugin developers. Set `-Wno-unused-parameter` instead
|
||||
* New table headings in `initctl`, using `top` style inverted text
|
||||
@@ -667,7 +709,8 @@ 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.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
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
SUBDIRS = plugins src
|
||||
SUBDIRS = contrib docs plugins src
|
||||
doc_DATA = README.md LICENSE contrib/finit.conf
|
||||
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
||||
|
||||
@@ -8,7 +8,7 @@ install-dev:
|
||||
@make -C src install-pkgincludeHEADERS
|
||||
|
||||
# Target to run when building a release
|
||||
release: dist
|
||||
release: distcheck
|
||||
@for file in $(DIST_ARCHIVES); do \
|
||||
md5sum $$file > ../$$file.md5; \
|
||||
done
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Features](#features)
|
||||
* [Bootstrap](doc/bootstrap.md#bootstrap)
|
||||
* [Bootstrap](docs/bootstrap.md#bootstrap)
|
||||
* [Runlevels](#runlevels)
|
||||
* [Syntax](doc/config.md#syntax)
|
||||
* [Inetd](doc/inetd.md#inetd)
|
||||
* [Syntax](docs/config.md#syntax)
|
||||
* [Inetd](docs/inetd.md#inetd)
|
||||
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
|
||||
* [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
|
||||
* [Hooks, Callbacks & Plugins](docs/plugins.md#hooks-callbacks--plugins)
|
||||
* [Rebooting & Halting](#rebooting--halting)
|
||||
* [Commands & Status](#commands--status)
|
||||
* [Building](doc/build.md#building)
|
||||
* [Running](doc/build.md#running)
|
||||
* [Debugging](doc/build.md#debugging)
|
||||
* [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
|
||||
* [Building](docs/build.md#building)
|
||||
* [Running](docs/build.md#running)
|
||||
* [Recovery](docs/build.md#recovery)
|
||||
* [Debugging](docs/build.md#debugging)
|
||||
* [Requirements](#requirements)
|
||||
* [Origin & References](#origin--references)
|
||||
|
||||
@@ -27,8 +29,11 @@ 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/).
|
||||
@@ -49,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
|
||||
@@ -87,10 +103,13 @@ tty [12345] /sbin/getty -L 115200 /dev/tty3 linux
|
||||
# Use built-in getty for serial port and USB serial
|
||||
tty [12345] /dev/ttyAMA0 115200 noclear nowait
|
||||
tty [12345] /dev/ttyUSB0 115200 noclear
|
||||
|
||||
# Just give me a shell, I need to debug this embedded system!
|
||||
tty [12345] @console noclear nologin
|
||||
```
|
||||
|
||||
The `service` stanza, as well as `task`, `run`, `inetd` and others are
|
||||
described in full in [doc/config.md](doc/config.md). Here's a quick
|
||||
described in full in [docs/config.md](docs/config.md). Here's a quick
|
||||
overview of some of the most common components needed to start a UNIX
|
||||
daemon:
|
||||
|
||||
@@ -111,7 +130,7 @@ advanced uses as well:
|
||||
|
||||
service /usr/sbin/sshd -D
|
||||
|
||||
Dependencies are handled using [conditions](doc/conditions.md). One of
|
||||
Dependencies are handled using [conditions](docs/conditions.md). One of
|
||||
the most common conditions is to wait for basic networking to become
|
||||
available:
|
||||
|
||||
@@ -152,7 +171,7 @@ Start, monitor and restart services should they fail.
|
||||
|
||||
**Inetd**
|
||||
|
||||
Finit comes with a built-in [inetd server](doc/inetd.md). No need to
|
||||
Finit comes with a built-in [inetd server](docs/inetd.md). No need to
|
||||
maintain a separate config file for services that you want to start on
|
||||
demand.
|
||||
|
||||
@@ -167,7 +186,7 @@ Built-in optional inetd services:
|
||||
- discard [RFC863][]
|
||||
- time (rdate) [RFC868][]
|
||||
|
||||
For more information, see [doc/inetd.md](doc/inetd.md).
|
||||
For more information, see [docs/inetd.md](docs/inetd.md).
|
||||
|
||||
|
||||
**Getty**
|
||||
@@ -192,7 +211,9 @@ figures it out by querying sysfs: `/sys/class/tty/console/active`.
|
||||
tty [12345] @console 115200 linux noclear
|
||||
```
|
||||
|
||||
For more information, see [doc/config.md](doc/config.md#syntax).
|
||||
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
|
||||
latter is for skipping the login process entirely. For more information,
|
||||
see [docs/config.md](docs/config.md#syntax).
|
||||
|
||||
|
||||
**Runlevels**
|
||||
@@ -201,10 +222,18 @@ Support for SysV init-style [runlevels][5] is available, in the same
|
||||
minimal style as everything else in Finit. The `[2345]` syntax can be
|
||||
applied to service, task, run, inetd, and TTY stanzas.
|
||||
|
||||
All services in runlevel S(1) are started first, followed by the desired
|
||||
run-time runlevel. Runlevel S can be started in sequence by using `run
|
||||
[S] cmd`. Changing runlevels at runtime is done like any other init,
|
||||
e.g. <kbd>init 4</kbd>, but also using the more advanced `intictl` tool.
|
||||
Reserved runlevels are 0 and 6, halt and reboot, respectively just like
|
||||
SysV init. Runlevel 1 can be configured freely, but is recommended to
|
||||
be kept as the system single-user runlevel since Finit will not start
|
||||
networking here. The configured `runlevel NUM` from `/etc/finit.conf`
|
||||
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
|
||||
given on the kernel cmdline, in which case runlevel 1 is started.
|
||||
|
||||
All services in runlevel S(0) are started first, followed by the desired
|
||||
run-time runlevel. Run tasks in runlevel S can be started in sequence
|
||||
by using `run [S] cmd`. Changing runlevels at runtime is done like any
|
||||
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
|
||||
`intictl` tool.
|
||||
|
||||
|
||||
**Plugins**
|
||||
@@ -229,25 +258,49 @@ Extensions and functionality not purely related to what an `/sbin/init`
|
||||
needs to start a system are available as a set of plugins that either
|
||||
hook into the boot process or respond to various I/O.
|
||||
|
||||
For more information, see [doc/plugins.md](doc/plugins.md).
|
||||
For more information, see [docs/plugins.md](docs/plugins.md).
|
||||
|
||||
|
||||
**Progress**
|
||||
|
||||
Finit prior to v3.0 showed the progress at boot: plugins and modules
|
||||
being loaded, run/tasks/services being started, etc. By default Finit
|
||||
is now silent, but progress can still be enabled at build-time using:
|
||||
|
||||
configure --enable-progress
|
||||
|
||||
or at boot from the kernel cmdline using the boot parameter `splash`:
|
||||
|
||||
```shell
|
||||
append="init=/sbin/finit splash"
|
||||
```
|
||||
|
||||
For a really nice boot, add `quiet` as well. This silences the output
|
||||
from the kernel, leaving only warnings and errors. For other kernel
|
||||
command line parameters, see [docs/build.md](docs/build.md#recovery).
|
||||
|
||||
|
||||
Runparts & /etc/rc.local
|
||||
------------------------
|
||||
|
||||
At the end of the boot, when networking and all services are up, Finit
|
||||
calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory,
|
||||
and `/etc/rc.local`, in that order if they exist.
|
||||
At the end of the boot, when all bootstrap (`S`) tasks and services have
|
||||
started, but not networking, Finit calls its built-in [run-parts(8)][]
|
||||
command on any configured `runparts <DIR>` directory. This happens just
|
||||
before changing to the configured runlevel (default 2). (Networking is
|
||||
enabled just prior to changing from single user mode.)
|
||||
|
||||
```shell
|
||||
runparts /etc/rc.d/
|
||||
```
|
||||
|
||||
Right after the runlevel change when all services have started properly,
|
||||
`/etc/rc.local` is called.
|
||||
|
||||
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
|
||||
If it exists and is an executable shell script, finit calls it at the
|
||||
very end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on
|
||||
hooks in [doc/plugins.md](doc/plugins.md#hooks), and about the system
|
||||
bootstrap in [doc/bootstrap.md](doc/bootstrap.md).
|
||||
If it exists and is an executable shell script Finit calls it at the very
|
||||
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
|
||||
in [docs/plugins.md](docs/plugins.md#hooks), and about the system bootstrap
|
||||
in [docs/bootstrap.md](docs/bootstrap.md).
|
||||
|
||||
|
||||
Runlevels
|
||||
@@ -325,7 +378,7 @@ section.
|
||||
For compatibility reasons Finit listens to the same set of signals as
|
||||
BusyBox init. This is not 100% compatible with SysV init, but clearly
|
||||
the more common combination for Finit. For more details, see
|
||||
[doc/signals.md](doc/signals.md).
|
||||
[docs/signals.md](docs/signals.md).
|
||||
|
||||
Finit also listens to the classic SysV init FIFO, used by `telinit`.
|
||||
Support for this is implemented by the `initctl.so` plugin. Hence,
|
||||
@@ -373,6 +426,7 @@ Commands:
|
||||
list List all .conf in /etc/finit.d/
|
||||
enable <CONF> Enable .conf in /etc/finit.d/available/
|
||||
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
|
||||
touch <CONF> Mark .conf in /etc/finit.d/ for reload
|
||||
reload Reload *.conf in /etc/finit.d/ (activates changes)
|
||||
|
||||
cond set <COND> Set (assert) condition => +COND
|
||||
@@ -384,6 +438,7 @@ Commands:
|
||||
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
|
||||
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
|
||||
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
|
||||
status <JOB|NAME>[:ID] Show service status, by job# or name
|
||||
status | show Show status of services, default command
|
||||
|
||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||
@@ -396,7 +451,7 @@ Commands:
|
||||
|
||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||
file must be used to tell Finit to stop and start it on `reload` and
|
||||
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
|
||||
`runlevel` changes. If `<>` holds more [conditions](docs/conditions.md),
|
||||
these will also affect how a service is maintained.
|
||||
|
||||
**Note:** even though it is possible to start services not belonging in
|
||||
@@ -422,11 +477,13 @@ Requirements
|
||||
------------
|
||||
|
||||
Finit is capable of running on both desktop/server systems with udev and
|
||||
embedded systems that usually come with BusyBox mdev. Finit probes for
|
||||
them at runtime and expects `/dev/` to be writable, usually `devtmpfs`.
|
||||
It is also possible to run on a statically set up `/dev` if needed. It
|
||||
is however not a good idea to have both udev and mdev installed at the
|
||||
same time, this will lead to unpredictable results.
|
||||
embedded systems that usually come with BusyBox mdev. Some systems have
|
||||
systemd-udev or eudev today instead of the original udev, Finit probes
|
||||
for all of them at runtime and expects `/dev/` to be a writable file
|
||||
system using `devtmpfs`. It is also possible to run on a statically set
|
||||
up `/dev` if needed. It is however not a good idea to have both udev
|
||||
and mdev installed at the same time, this will lead to unpredictable
|
||||
results.
|
||||
|
||||
At boot Finit calls either `mdev` or `udevd` to populate `/dev`, this is
|
||||
done slightly differently and on systems with udev you might want to add
|
||||
|
||||
+29
-7
@@ -1,10 +1,17 @@
|
||||
AC_INIT(Finit, 3.1-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 3.1, 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
|
||||
@@ -58,6 +66,10 @@ AC_ARG_ENABLE(watchdog,
|
||||
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
||||
enable_watchdog=no])
|
||||
|
||||
AC_ARG_ENABLE(redirect,
|
||||
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
|
||||
enable_redirect_output=no])
|
||||
|
||||
AC_ARG_ENABLE(inetd,
|
||||
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
|
||||
enable_inetd=yes])
|
||||
@@ -89,11 +101,6 @@ AC_ARG_WITH(fifo,
|
||||
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
|
||||
[fifo=$withval], [fifo=$localstatedir/run/initctl])
|
||||
|
||||
AC_ARG_WITH(plugindir,
|
||||
AS_HELP_STRING([--with-plugindir=DIR], [Finit plugin directory, default /lib/finit/plugins]),
|
||||
[pkglibdir=$withval], [pkglibdir=$libdir/finit/plugins])
|
||||
AC_SUBST([pkglibdir])
|
||||
|
||||
AC_ARG_WITH(config,
|
||||
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
|
||||
[conf=$withval], [conf=$sysconfdir/finit.conf])
|
||||
@@ -143,6 +150,9 @@ AS_IF([test "x$enable_progress" = "xyes"], [
|
||||
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
||||
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
||||
|
||||
AS_IF([test "x$enable_redirect" = "xyes"], [
|
||||
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
||||
|
||||
### Disable features ###########################################################################
|
||||
AS_IF([test "x$enable_inetd" != "xno"], [
|
||||
enable_inetd="yes"
|
||||
@@ -207,6 +217,17 @@ AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
||||
|
||||
# Override default libdir, used for plugins and rescue.conf
|
||||
#pkglibdir=$libdir/finit
|
||||
#AC_SUBST(pkglibdir)
|
||||
#AC_EXPAND_DIR(pkglibdir, "$libdir/finit")
|
||||
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
|
||||
AC_SUBST(plugin_path)
|
||||
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
|
||||
|
||||
AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf")
|
||||
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
|
||||
|
||||
# Generate all files
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -220,6 +241,7 @@ cat <<EOF
|
||||
FIFO path.............: $fifo_path
|
||||
Finit config file.....: $conf_path
|
||||
Finit config.d path...: $rcsd_path
|
||||
Finit plugin path.....: $plugin_path
|
||||
Compat rc.local path..: $rclocal_path
|
||||
Random seed path......: $random_path
|
||||
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
||||
|
||||
@@ -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)
|
||||
@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)
|
||||
@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
|
||||
|
||||
|
||||
Executable
+51
@@ -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
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
|
||||
JOB=$1
|
||||
CMD=$2
|
||||
|
||||
usage() {
|
||||
echo "Usage: service [job# | name] <stop | start | restart | status | show>"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
initctl show
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case $CMD in
|
||||
stop)
|
||||
;;
|
||||
start)
|
||||
;;
|
||||
restart)
|
||||
;;
|
||||
status)
|
||||
;;
|
||||
show)
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
initctl $2 $1
|
||||
|
||||
@@ -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)
|
||||
@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)
|
||||
+15
-15
@@ -21,24 +21,15 @@ can also be used.
|
||||
General
|
||||
-------
|
||||
|
||||
* Add support for `rescue` on the kernel command line, start rescue mode
|
||||
if enabled with `configure` script. Could mean skip `/etc/finit.conf`
|
||||
et al and jump straight to a single getty login on console.
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown.
|
||||
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
|
||||
API restart to SIGHUP callback for the same functionality in Finit.
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown,
|
||||
possibly using an at-job, see [crond section](#Crond) below
|
||||
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
|
||||
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
|
||||
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
|
||||
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
|
||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||
* Add `IFF_RUNNING` support to netlink plugin along `IFF_UP`
|
||||
* Implement `initctl stop|start|restart|reload|status <SVC>` and
|
||||
`service <SVC> stop|start|restart|reload|status` on top
|
||||
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
||||
`initctl show --json`
|
||||
* Improve `initctl show SVC` output. Add service uptime, last ten SVC
|
||||
logs/history (historian), etc. Similar to `systemctl show SVC`
|
||||
* [Solaris SMF][] (Service Management Facility) has some interesting
|
||||
features that may be well worth looking into adopting:
|
||||
|
||||
@@ -49,6 +40,13 @@ General
|
||||
* `initctl add /sbin/service -n -- Service description`, for details
|
||||
see above [Solaris SMF][] featuer, issue #55 and issue #69,
|
||||
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
|
||||
* Changing the name of a pidfile, `pid:[/path/]file.pid`, after a
|
||||
service has been started is not supported atm. The previous name will
|
||||
not be (never) removed and the new name will not be created until the
|
||||
process has been stopped and started again.
|
||||
* PID files created in subdirectories to `/run` is not supported right
|
||||
now inotify listeners should be automatically created (and removed)
|
||||
when a subdirectory is added (or removed).
|
||||
|
||||
|
||||
Init
|
||||
@@ -85,14 +83,16 @@ system-level timed periodic tasks:
|
||||
Proposed syntax:
|
||||
|
||||
# Crontab
|
||||
cron @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
||||
cron @SPEC [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
||||
|
||||
# One-shot 'at' command
|
||||
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
||||
|
||||
Notice the ISO date in notation. The runlevels is extra filtering
|
||||
sugar. E.g., if cron service is only allowed in runlevels two or three
|
||||
it will not start if system currently is in runlevel four.
|
||||
Notice the ISO date in the `at` notation. The `cron @SPEC` could use
|
||||
are mo loose notation, e.g. `@hourly`, `@midnight`, `@daily`, `@weekly`,
|
||||
`@reboot`, etc. The runlevels is extra filtering sugar. E.g., if cron
|
||||
service is only allowed in runlevels two or three it will not start if
|
||||
system currently is in runlevel four.
|
||||
|
||||
To run a command as another user the `.conf` file must have a that
|
||||
owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
|
||||
@@ -5,7 +5,7 @@ Bootstrap
|
||||
2. Set up initial signal handlers
|
||||
3. Set up default $PATH early
|
||||
4. Mount `/proc` and `/sys`
|
||||
5. Check kernel command line for `--debug` to figure out log level
|
||||
5. Check kernel command line for `debug` to figure out log level
|
||||
6. Load all `.so` plugins
|
||||
7. Call `fsck` on file systems listed in `/etc/fstab`
|
||||
8. Populate `/dev` using either udev or mdev, depending on system type
|
||||
@@ -25,13 +25,13 @@ Bootstrap
|
||||
20. Bring up loopback interface and all `/etc/network/interfaces`, if
|
||||
the `.conf` setting `network <SCRIPT>` is set, it is called instead
|
||||
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
||||
22. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
||||
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
||||
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
||||
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
|
||||
(re)loaded and new services, tasks, and blocking run commands are
|
||||
started. Provided they are allowed in the new runlevel and all of
|
||||
their conditions, if any, are set.
|
||||
23. Call 4th level hooks, `HOOK_SVC_UP`
|
||||
24. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
||||
24. Call 4th level hooks, `HOOK_SVC_UP`
|
||||
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
||||
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||
27. Start all configured TTYs, or a fallback shell on `/dev/console`
|
||||
@@ -124,14 +124,32 @@ append="init=/sbin/finit"
|
||||
Remember to also set up an initial `/etc/finit.conf` before rebooting!
|
||||
|
||||
|
||||
Recovery
|
||||
--------
|
||||
|
||||
To rescue a system with Finit, append the following to the kernel
|
||||
command line:
|
||||
|
||||
```shell
|
||||
append="init=/sbin/finit rescue"
|
||||
```
|
||||
|
||||
This tells Finit to start in a very limited recovery mode, no services
|
||||
are loaded, no filesystems are mounted or checked, and no networking is
|
||||
enabled. The default Finit rescue mode configuration is installed into
|
||||
`/lib/finit/rescue.conf`, which can be safely removed or changed.
|
||||
|
||||
By default the a root shell, without login, is started.
|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Add `finit_debug`, or `--debug`, to the kernel command line to enable
|
||||
debug messages.
|
||||
Edit, or append to, the kernel command line: remove `quiet` to enable
|
||||
kernel messages and add `debug` to enable Finit debug messages.
|
||||
|
||||
```shell
|
||||
append="init=/sbin/finit --debug"
|
||||
append="init=/sbin/finit debug"
|
||||
```
|
||||
|
||||
To debug startup issues, in particular issues with getty/login, try
|
||||
@@ -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)`.
|
||||
@@ -95,10 +95,11 @@ Built-in conditions:
|
||||
- `net/route/default`
|
||||
- `net/<IFNAME>/exist`
|
||||
- `net/<IFNAME>/up`
|
||||
- `net/<IFNAME>/running`
|
||||
|
||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`,
|
||||
not link up, `IFF_RUNNING`. This latter conditions is currently not
|
||||
implemented by the `plugins/netlink.c` plugin.
|
||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
||||
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
||||
difference is that `running` tells if the NIC has link.
|
||||
|
||||
|
||||
Debugging
|
||||
+145
-34
@@ -2,6 +2,7 @@ Configuration
|
||||
=============
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||
* [Syntax](#syntax)
|
||||
* [Limitations](#limitations)
|
||||
* [/etc/finit.conf](#etcfinitconf)
|
||||
@@ -16,7 +17,7 @@ or in combination with `/etc/finit.d/*.conf`. Finit 3 can even start a
|
||||
system using only `/etc/finit.d/*.conf`, highly useful for package-based
|
||||
Linux distributions -- each package can provide its own "script" file.
|
||||
|
||||
- `/etc/finit.conf`: main configuration file, read only once at boot
|
||||
- `/etc/finit.conf`: main configuration file
|
||||
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
||||
|
||||
Not all configuration directives are available in `/etc/finit.d/*.conf`
|
||||
@@ -26,8 +27,15 @@ the section [Limitations](#limitations) below for details.
|
||||
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
|
||||
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
|
||||
call `finit q`, but that has been deprecated with the `initctl` tool).
|
||||
Any service read from this directory is flagged as a dynamic service, so
|
||||
changes to or removal of `/etc/finit.d/*.conf` files, is detected.
|
||||
Finit monitors all known active `.conf` files, so if you want to force
|
||||
a restart of any service you can simply touch its corresponding `.conf`
|
||||
file in `/etc/finit.d` and call `initctl reload`. Finit handle any
|
||||
and all conditions and dependencies between services automatically.
|
||||
|
||||
It is also possible to drop `.conf` files in `/etc/finit.d/available/`
|
||||
and use `initctl enable` to enable a service `.conf` file. This may be
|
||||
useful in particular to Linux distributions that may want to install all
|
||||
files for a package and let the user decide when to enable a service.
|
||||
|
||||
On `initctl reload` the following is checked for all services:
|
||||
|
||||
@@ -42,24 +50,63 @@ For more info on the different states of a service, see the separate
|
||||
document [Finit Services](service.md).
|
||||
|
||||
|
||||
Service Wrapper Scripts
|
||||
-----------------------
|
||||
|
||||
If your service requires to run additional commands, executed before the
|
||||
service is actually started, like the systemd `ExecStartPre`, you can
|
||||
use a wrapper shell script to start your service.
|
||||
|
||||
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`:
|
||||
```conf
|
||||
service [235] <!> /etc/start.d/program -- Example Program
|
||||
```
|
||||
|
||||
* `/etc/start.d/program:`
|
||||
```shell
|
||||
#!/bin/sh
|
||||
|
||||
# Prepare the command line options
|
||||
OPTIONS="-u $(cat /etc/username)"
|
||||
|
||||
# Execute the program
|
||||
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.
|
||||
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
* `module <MODULE>`
|
||||
* `host <NAME>`, or `hostname <NAME>`
|
||||
Set system hostname to NAME, unless `/etc/hostname` exists in which
|
||||
case the contents of that file is used.
|
||||
|
||||
* `module <MODULE> [ARGS]`
|
||||
Load a kernel module, with optional arguments
|
||||
|
||||
* `network <PATH>`
|
||||
Script or program to bring up networking, with optional arguments
|
||||
|
||||
* `rlimit <hard|soft> RESOURCE <LIMIT|unlimited>`
|
||||
Set the hard or soft limit for a resource. `RESOURCE` is a lower-case
|
||||
string matching the `RLIMIT_` constants from `setrlimit(2)`, without
|
||||
the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||
* `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)`,
|
||||
without the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||
|
||||
The limit is an integer that depends on the resource being modified,
|
||||
see the man page, or the kernel `/proc/PID/limits` file, for more
|
||||
information. Finit versions before v3.1 used `infinity` for
|
||||
`unlimited`, which is still a supported keyword.
|
||||
LIMIT is an integer that depends on the resource being modified, see
|
||||
the man page, or the kernel `/proc/PID/limits` file, for details.
|
||||
Finit versions before v3.1 used `infinity` for `unlimited`, which is
|
||||
still supported, albeit deprecated.
|
||||
|
||||
```shell
|
||||
# No process is allowed more than 8MB of address space
|
||||
@@ -67,8 +114,14 @@ Syntax
|
||||
|
||||
# Core dumps may be arbitrarily large
|
||||
rlimit soft core infinity
|
||||
|
||||
# CPU limit for all services, soft & hard = 10 sec
|
||||
rlimit cpu 10
|
||||
```
|
||||
|
||||
`rlimit` can be set globally, in `/etc/finit.conf`, or locally for
|
||||
a set of task/run/services, in `/etc/finit.d/*.conf`.
|
||||
|
||||
* `runlevel <N>`
|
||||
N is the runlevel number 1-9, where 6 is reserved for reboot.
|
||||
Default is 2.
|
||||
@@ -107,8 +160,30 @@ Syntax
|
||||
Finit waits for another service, `/sbin/zebra`, to have created its
|
||||
PID file in `/var/run/zebra.pid` before starting `ospfd`.
|
||||
|
||||
For a detailed description of conditions, and how to debug them,
|
||||
see the [Finit Conditions](conditions.md) document.
|
||||
Some services do not maintain a PID file and rather than patching each
|
||||
application Finit provides a workaround. A `pid` keyword can be set
|
||||
to have Finit automatically create (when starting) and later remove
|
||||
(when stopping) the PID file. The file is created in the `/var/run`
|
||||
directory using the `basename(1)` of the service. The default can be
|
||||
modified with an optional argument:
|
||||
|
||||
pid[:[/path/to/]filename[.pid]]
|
||||
|
||||
For example, by adding `pid:/run/foo.pid` to the service `bar` that
|
||||
PID file will, not only be created and removed automatically, but also
|
||||
be used by the Finit condition subsystem. So another service/run/task
|
||||
can depend on `<svc/foo>`.
|
||||
|
||||
If a service `bar` *does* maintain a PID file, but using `foo.pid`, we
|
||||
can inform Finit of this by simplly prepending an `!` to the argument.
|
||||
|
||||
pid:!/run/foo.pid
|
||||
|
||||
Here Finit will *not* create/remove/touch the PID file, only use it
|
||||
for the condition handling instead of the default PID file name.
|
||||
|
||||
For a detailed description of conditions, and how to debug them, see
|
||||
the [Finit Conditions](conditions.md) document.
|
||||
|
||||
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
||||
Launch a daemon when a client initiates a connection on an Internet
|
||||
@@ -140,22 +215,39 @@ Syntax
|
||||
* `runparts <DIR>`
|
||||
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
||||
files, or scripts, in the directory are called, in alphabetic order.
|
||||
The scripts in this directory are executed at the very end of runlevel
|
||||
`S`, bootstrap.
|
||||
|
||||
It can be beneficial to use `S01name`, `S02othername`, etc. if there
|
||||
is a dependency order between the scripts. Symlinks to existing
|
||||
daemons can talso be used, but make sure they daemonize by default.
|
||||
|
||||
As the optional `/etc/rc.local` shell script, make sure that all your
|
||||
Similar to the `/etc/rc.local` shell script, make sure that all your
|
||||
services and programs either terminate or start in the background or
|
||||
you will block Finit.
|
||||
|
||||
* `include <CONF>`
|
||||
Include another configuration file. Absolute path required.
|
||||
|
||||
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [TERM]`
|
||||
* `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
|
||||
TTY device DEV, in the given runlevels. Default baud rate is `38400`.
|
||||
TTY device DEV, in the given runlevels. The DEV may be the special
|
||||
keyword `@console`, which is very useful on embedded systems. Default
|
||||
baud rate is `38400`.
|
||||
|
||||
**Example:**
|
||||
```conf
|
||||
@@ -182,6 +274,11 @@ Syntax
|
||||
embedded systems running multiple unused getty wastes both memory
|
||||
and CPU cycles, so `wait` is the preferred default.
|
||||
|
||||
The `nologin` option disables getty and `/bin/login`, and gives the
|
||||
user a root (login) shell on the given TTY `<DEV>` immediately.
|
||||
Needless to say, this is a rather insecure option, but can be very
|
||||
useful for developer builds, during board bringup, or similar.
|
||||
|
||||
Notice the ordering, the `TERM` option to the built-in getty must be
|
||||
the last argument.
|
||||
|
||||
@@ -236,8 +333,19 @@ Without the `:ID` to the service the latter will overwrite the former
|
||||
and only the old web server would be started and supervised.
|
||||
|
||||
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
|
||||
`log` to redirect `stderr` and `stdout` of the application to syslog,
|
||||
using `logger`.
|
||||
`log` to redirect `stderr` and `stdout` of the application to a file or
|
||||
syslog using the native `logit` tool. The full syntax is:
|
||||
|
||||
log:/path/to/file
|
||||
log:prio:facility.level,tag:ident
|
||||
log:null
|
||||
log
|
||||
|
||||
Log rotation is controlled using the global `log` setting.
|
||||
|
||||
**Example:**
|
||||
|
||||
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.
|
||||
@@ -248,31 +356,34 @@ Limitations
|
||||
|
||||
To understand the limitations of `finit.conf` vs `finit.d` it is useful
|
||||
to picture the different phases of the system: bootstrap, runtime, and
|
||||
shutdown. The `finit.conf` file can be used for all possible settings,
|
||||
but `finit.d/*.conf` can only be used for runtime settings.
|
||||
shutdown.
|
||||
|
||||
### /etc/finit.conf
|
||||
|
||||
This file was the only way to set up and boot a system using Finit. It
|
||||
is used for pre-runtime settings like system hostname, network bringup
|
||||
and shutdown:
|
||||
This file used to be the only way to set up and boot a Finit system.
|
||||
Today it is mainly used for pre-runtime settings like system hostname,
|
||||
network bringup and shutdown:
|
||||
|
||||
- `host`
|
||||
- `mkdod`
|
||||
- `network`
|
||||
- `runparts`
|
||||
- `host`, only at bootstrap, (runlevel `S`)
|
||||
- `mknod`, only at bootstrap
|
||||
- `network`, only at bootstrap
|
||||
- `runparts`, only at bootstrap
|
||||
- `include`
|
||||
- `shtudown`
|
||||
- `runlevel`
|
||||
- `log`, global setting
|
||||
- `shutdown`
|
||||
- `runlevel`, only at bootstrap
|
||||
- ... and all configuration stanzas from `/etc/finit.d` below
|
||||
|
||||
### /etc/finit.d
|
||||
|
||||
Support for partial `.conf` files in `/etc/finit.d` was added to handle
|
||||
changes of the system configuration at runtime. It does *not support*
|
||||
the above `finit.conf` settings described above, only the following:
|
||||
Support for per-service `.conf` files in `/etc/finit.d` was added in
|
||||
v3.0 to handle changes of the system configuration at runtime. As of
|
||||
v3.1 `finit.conf` is also handled at runtime, except of course for any
|
||||
stanza that only runs at bootstrap. However, a `/etc/finit.d/*.conf`
|
||||
does *not support* the above `finit.conf` settings described above, only
|
||||
the following:
|
||||
|
||||
- `module`, but only in runlevel `S`
|
||||
- `module`, but only at bootstrap
|
||||
- `service`
|
||||
- `task`
|
||||
- `run`
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -1,3 +1,4 @@
|
||||
pkglibdir = $(plugin_path)
|
||||
AM_LDFLAGS = -module -avoid-version -shared
|
||||
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+20
-23
@@ -94,6 +94,17 @@ static void nl_route(struct nlmsghdr *nlmsg)
|
||||
}
|
||||
}
|
||||
|
||||
static void net_cond_set(char *ifname, char *cond, int set)
|
||||
{
|
||||
char msg[MAX_ARG_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "net/%s/%s", ifname, cond);
|
||||
if (set)
|
||||
cond_set(msg);
|
||||
else
|
||||
cond_clear(msg);
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg)
|
||||
{
|
||||
int la;
|
||||
@@ -112,39 +123,25 @@ static void nl_link(struct nlmsghdr *nlmsg)
|
||||
|
||||
while (RTA_OK(a, la)) {
|
||||
if (a->rta_type == IFLA_IFNAME) {
|
||||
char msg[MAX_ARG_LEN];
|
||||
|
||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||
switch (nlmsg->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
/*
|
||||
* New interface has appearad, or interface flags has changed.
|
||||
* New interface has appeared, or interface flags has changed.
|
||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||
*/
|
||||
_d("%s: New link, ifi_change 0x%x", ifname, i->ifi_change);
|
||||
if (i->ifi_change & IFF_UP) {
|
||||
snprintf(msg, sizeof(msg), "net/%s/up", ifname);
|
||||
|
||||
if (i->ifi_flags & IFF_UP)
|
||||
cond_set(msg);
|
||||
else
|
||||
cond_clear(msg);
|
||||
|
||||
if (string_compare("lo", ifname)) {
|
||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||
cond_set(msg);
|
||||
}
|
||||
} else {
|
||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||
cond_set(msg);
|
||||
}
|
||||
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||
net_cond_set(ifname, "exist", 1);
|
||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
/* NOTE: Interface has dissapeared, not link down ... */
|
||||
/* NOTE: Interface has disappeared, not link down ... */
|
||||
_d("%s: Delete link", ifname);
|
||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||
cond_clear(msg);
|
||||
net_cond_set(ifname, "exist", 0);
|
||||
net_cond_set(ifname, "up", 0);
|
||||
net_cond_set(ifname, "running", 0);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
|
||||
+18
-13
@@ -50,7 +50,6 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz, len;
|
||||
char *basename;
|
||||
svc_t *svc;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
@@ -60,23 +59,16 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
|
||||
for (ev = (void *)ev_buf;
|
||||
sz > (ssize_t)sizeof(*ev);
|
||||
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||
/* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
|
||||
if (!ev->mask || !strstr(ev->name, ".pid"))
|
||||
continue;
|
||||
|
||||
basename = strtok(ev->name, ".");
|
||||
if (!basename)
|
||||
continue;
|
||||
|
||||
svc = svc_find_by_nameid(basename, 1);
|
||||
svc = svc_find_by_pidfile(ev->name);
|
||||
if (!svc)
|
||||
continue;
|
||||
|
||||
/* TODO FIXME XXX WKZ check that pid is controlled by finit */
|
||||
|
||||
_d("%s: match %s", basename, svc->cmd);
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
|
||||
svc_started(svc);
|
||||
@@ -120,10 +112,23 @@ static void pidfile_reconf(void *arg)
|
||||
|
||||
static void pidfile_init(void *arg)
|
||||
{
|
||||
char *path;
|
||||
struct context *ctx = arg;
|
||||
|
||||
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
|
||||
IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
||||
/*
|
||||
* The bootmisc plugin is responsible for setting up /var/run.
|
||||
* and/or /run, with proper symlinks etc. We depend on bootmisc
|
||||
* so it's safe here to query realpath() and set up inotify.
|
||||
*/
|
||||
path = realpath(_PATH_VARRUN, NULL);
|
||||
if (!path) {
|
||||
_pe("Failed ");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->wd = inotify_add_watch(ctx->fd, path, IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
||||
free(path);
|
||||
|
||||
if (ctx->wd < 0) {
|
||||
_pe("inotify_add_watch()");
|
||||
close(ctx->fd);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.deps/*
|
||||
finit
|
||||
initctl
|
||||
logit
|
||||
reboot
|
||||
Makefile
|
||||
Makefile.in
|
||||
|
||||
+21
-11
@@ -1,14 +1,19 @@
|
||||
EXTRA_DIST = rescue.conf
|
||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
if STATIC
|
||||
AM_CPPFLAGS += -DENABLE_STATIC
|
||||
AM_LDFLAGS = -static-libtool-libs
|
||||
else
|
||||
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
|
||||
AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
bin_PROGRAMS = logit
|
||||
sbin_PROGRAMS = finit initctl reboot
|
||||
|
||||
logit_SOURCES = logit.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
|
||||
finit_SOURCES = api.c \
|
||||
cond.c cond-w.c cond.h \
|
||||
telinit.c \
|
||||
@@ -17,7 +22,8 @@ finit_SOURCES = api.c \
|
||||
getty.c stty.c \
|
||||
helpers.c helpers.h \
|
||||
log.c log.h \
|
||||
mdadm.c mount.c pid.c \
|
||||
mdadm.c mount.c \
|
||||
pid.c pid.h \
|
||||
plugin.c plugin.h private.h \
|
||||
service.c service.h \
|
||||
sig.c sig.h \
|
||||
@@ -38,7 +44,7 @@ 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
|
||||
@@ -59,10 +65,11 @@ REBOOT_LNKS = shutdown halt poweroff suspend
|
||||
|
||||
# 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; \
|
||||
@@ -71,16 +78,17 @@ 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
|
||||
|
||||
uninstall-hook:
|
||||
@$(RM) $(DESTDIR)$(pkglibdir)/rescue.conf
|
||||
@for file in $(INIT_LNKS); do \
|
||||
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
|
||||
if [ "$$lnk" != "finit" ]; then \
|
||||
@@ -93,9 +101,11 @@ uninstall-hook:
|
||||
echo "Non-finit reboot, skipping uninstall."; \
|
||||
else \
|
||||
for file in $(REBOOT_LNKS); do \
|
||||
if [ "`readlink $$file`" != "reboot" ]; \
|
||||
then continue; \
|
||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||
dst=`readlink $$lnk`; \
|
||||
if [ "$$dst" != "reboot" ]; then \
|
||||
continue; \
|
||||
fi; \
|
||||
$(RM) $(DESTDIR)$(sbindir)/$$file; \
|
||||
$(RM) $$lnk; \
|
||||
done; \
|
||||
fi
|
||||
|
||||
@@ -342,6 +342,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
kill(wdogpid, SIGTERM);
|
||||
do_sleep(1);
|
||||
}
|
||||
_d("wdog was %d, now %d is in charge", wdogpid, rq.runlevel);
|
||||
wdogpid = rq.runlevel;
|
||||
break;
|
||||
|
||||
@@ -380,13 +381,11 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
_d("Failed sending ACK/NACK back to client");
|
||||
}
|
||||
|
||||
if (UEV_ERROR == events)
|
||||
goto error;
|
||||
|
||||
leave:
|
||||
close(sd);
|
||||
if (UEV_ERROR == events)
|
||||
goto error;
|
||||
return;
|
||||
|
||||
error:
|
||||
api_exit();
|
||||
if (api_init(w->ctx))
|
||||
@@ -430,8 +429,9 @@ error:
|
||||
|
||||
int api_exit(void)
|
||||
{
|
||||
return shutdown(api_watcher.fd, SHUT_RDWR) ||
|
||||
close(api_watcher.fd);
|
||||
uev_io_stop(&api_watcher);
|
||||
|
||||
return close(api_watcher.fd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,11 +27,6 @@
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
|
||||
typedef struct {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
} command_t;
|
||||
|
||||
int client_connect (void);
|
||||
int client_disconnect (void);
|
||||
|
||||
|
||||
+55
-13
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
static int cond_set_gen(const char *path, unsigned int gen)
|
||||
@@ -59,9 +60,27 @@ static void cond_bump_reconf(void)
|
||||
cond_set_gen(COND_RECONF, rgen);
|
||||
}
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new)
|
||||
static int cond_checkpath(const char *path)
|
||||
{
|
||||
char buf[MAX_ARG_LEN], *dir;
|
||||
|
||||
strlcpy(buf, path, sizeof(buf));
|
||||
dir = dirname(buf);
|
||||
if (!dir) {
|
||||
_e("Invalid path '%s' for condition", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (makepath(dir) && errno != EEXIST) {
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new)
|
||||
{
|
||||
enum cond_state old;
|
||||
unsigned int rgen;
|
||||
|
||||
@@ -77,16 +96,8 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
|
||||
switch (new) {
|
||||
case COND_ON:
|
||||
strlcpy(buf, path, sizeof(buf));
|
||||
dir = dirname(buf);
|
||||
if (!dir) {
|
||||
_e("Invalid path '%s' for condition", path);
|
||||
return 0;
|
||||
}
|
||||
if (makepath(dir) && errno != EEXIST) {
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 0;
|
||||
}
|
||||
if (cond_checkpath(path))
|
||||
return 0;
|
||||
cond_set_gen(path, rgen);
|
||||
break;
|
||||
|
||||
@@ -139,15 +150,38 @@ static void cond_update(const char *name)
|
||||
void cond_set(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_ON))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_set_oneshot(const char *name)
|
||||
{
|
||||
const char *path;
|
||||
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
|
||||
path = cond_path(name);
|
||||
_d("s => %s", name, path);
|
||||
|
||||
if (cond_checkpath(path))
|
||||
return;
|
||||
|
||||
symlink(COND_RECONF, path);
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_clear(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (string_compare(name, "nop"))
|
||||
return;
|
||||
|
||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||
return;
|
||||
|
||||
@@ -172,7 +206,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);
|
||||
|
||||
@@ -191,7 +231,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;
|
||||
}
|
||||
|
||||
+4
-2
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
|
||||
@@ -42,11 +43,12 @@ 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)
|
||||
|
||||
+9
-8
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <paths.h>
|
||||
|
||||
#define COND_PATH _PATH_VARRUN "finit/cond"
|
||||
#define COND_SVC_PATH COND_PATH "/svc/"
|
||||
#define COND_DIR "finit/cond"
|
||||
#define COND_PATH _PATH_VARRUN COND_DIR
|
||||
#define COND_RECONF COND_PATH "/reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
@@ -21,12 +21,13 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
void cond_reassert(const char *pat);
|
||||
void cond_init (void);
|
||||
int cond_set_path (const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
void cond_set_oneshot (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
void cond_reassert (const char *pat);
|
||||
void cond_init (void);
|
||||
|
||||
#endif /* FINIT_COND_H_ */
|
||||
|
||||
|
||||
+173
-41
@@ -36,11 +36,15 @@
|
||||
#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 {
|
||||
@@ -58,24 +62,85 @@ void conf_parse_cmdline(void)
|
||||
{
|
||||
int dbg = 0;
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE], *cmdline, *tok;
|
||||
|
||||
fp = fopen("/proc/cmdline", "r");
|
||||
if (fp) {
|
||||
char line[LINE_SIZE];
|
||||
|
||||
fgets(line, sizeof(line), fp);
|
||||
chomp(line);
|
||||
_d("%s", line);
|
||||
|
||||
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
|
||||
dbg = 1;
|
||||
if (!fp)
|
||||
goto done;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
goto done;
|
||||
}
|
||||
|
||||
cmdline = chomp(line);
|
||||
_d("%s", cmdline);
|
||||
|
||||
while ((tok = strtok(cmdline, " \t"))) {
|
||||
cmdline = NULL;
|
||||
|
||||
/* Catches finit_debug (deprecated), --debug, and debug */
|
||||
if (strstr(tok, "debug"))
|
||||
dbg = 1;
|
||||
|
||||
if (string_compare(tok, "rescue") || string_compare(tok, "recover"))
|
||||
rescue = 1;
|
||||
|
||||
if (string_compare(tok, "single") || string_compare(tok, "S"))
|
||||
single = 1;
|
||||
|
||||
if (string_compare(tok, "splash"))
|
||||
splash = 1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
done:
|
||||
log_init(dbg);
|
||||
}
|
||||
|
||||
static int kmod_exists(char *mod)
|
||||
{
|
||||
int found = 0;
|
||||
FILE *fp;
|
||||
char buf[256];
|
||||
size_t len;
|
||||
|
||||
for (len = 0; mod[len]; len++) {
|
||||
if (mod[len] == ' ' || mod[len] == '\t')
|
||||
break;
|
||||
}
|
||||
|
||||
fp = fopen("/proc/modules", "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (!found && (fgets(buf, sizeof(buf), fp))) {
|
||||
if (!strncmp(buf, mod, len))
|
||||
found = 1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static void kmod_load(char *line)
|
||||
{
|
||||
char *mod;
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
if (runlevel != 0)
|
||||
return;
|
||||
|
||||
mod = strip_line(line);
|
||||
if (kmod_exists(mod))
|
||||
return;
|
||||
|
||||
strcpy(cmd, "modprobe ");
|
||||
strlcat(cmd, mod, sizeof(cmd));
|
||||
|
||||
run_interactive(cmd, "Loading kernel module %s", mod);
|
||||
}
|
||||
|
||||
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||
int conf_parse_runlevels(char *runlevels)
|
||||
{
|
||||
@@ -200,11 +265,33 @@ char *rlim2str(int rlim)
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
char *lim2str(struct rlimit *rlim)
|
||||
{
|
||||
char tmp[20];
|
||||
static char buf[42];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_cur);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
if (RLIM_INFINITY == rlim->rlim_max)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_max);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* First form: `rlimit <hard|soft> RESOURCE LIMIT` */
|
||||
void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||
{
|
||||
char *level, *limit, *val;
|
||||
int resource = -1;
|
||||
rlim_t cfg, *set;
|
||||
rlim_t cfg;
|
||||
|
||||
level = strtok(line, " \t");
|
||||
if (!level)
|
||||
@@ -214,21 +301,18 @@ void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||
if (!limit)
|
||||
goto error;
|
||||
|
||||
val = strtok(NULL, " \t");
|
||||
if (!val) {
|
||||
/* Second form: `rlimit RESOURCE LIMIT` */
|
||||
val = limit;
|
||||
limit = level;
|
||||
level = "both";
|
||||
}
|
||||
|
||||
resource = str2rlim(limit);
|
||||
if (resource < 0 || resource > RLIMIT_NLIMITS)
|
||||
goto error;
|
||||
|
||||
val = strtok(NULL, " \t");
|
||||
if (!val)
|
||||
goto error;
|
||||
|
||||
if (!strcmp(level, "soft"))
|
||||
set = &arr[resource].rlim_cur;
|
||||
else if (!strcmp(level, "hard"))
|
||||
set = &arr[resource].rlim_max;
|
||||
else
|
||||
goto error;
|
||||
|
||||
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
|
||||
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
|
||||
cfg = RLIM_INFINITY;
|
||||
@@ -243,7 +327,15 @@ void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||
}
|
||||
}
|
||||
|
||||
*set = cfg;
|
||||
if (!strcmp(level, "soft"))
|
||||
arr[resource].rlim_cur = cfg;
|
||||
else if (!strcmp(level, "hard"))
|
||||
arr[resource].rlim_max = cfg;
|
||||
else if (!strcmp(level, "both"))
|
||||
arr[resource].rlim_max = arr[resource].rlim_cur = cfg;
|
||||
else
|
||||
goto error;
|
||||
|
||||
return;
|
||||
error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
@@ -254,7 +346,7 @@ static void parse_static(char *line)
|
||||
char *x;
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "host ", x)) {
|
||||
if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) {
|
||||
if (hostname) free(hostname);
|
||||
hostname = strdup(strip_line(x));
|
||||
return;
|
||||
@@ -295,6 +387,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));
|
||||
@@ -322,7 +434,6 @@ static void parse_static(char *line)
|
||||
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
{
|
||||
char *x;
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
/* Skip comments, i.e. lines beginning with # */
|
||||
if (MATCH_CMD(line, "#", x))
|
||||
@@ -330,16 +441,7 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
|
||||
/* Kernel module to load at bootstrap */
|
||||
if (MATCH_CMD(line, "module ", x)) {
|
||||
char *mod;
|
||||
|
||||
if (runlevel != 0)
|
||||
return;
|
||||
|
||||
mod = strip_line(x);
|
||||
strcpy(cmd, "modprobe ");
|
||||
strlcat(cmd, mod, sizeof(cmd));
|
||||
run_interactive(cmd, "Loading kernel module %s", mod);
|
||||
|
||||
kmod_load(x);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -463,7 +565,8 @@ static int parse_conf(char *file)
|
||||
/* Set global limits */
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
if (setrlimit(i, &global_rlimit[i]) == -1)
|
||||
logit(LOG_WARNING, "rlimit: Failed setting %s", rlim2str(i));
|
||||
logit(LOG_WARNING, "rlimit: Failed setting %s: %s",
|
||||
rlim2str(i), lim2str(&global_rlimit[i]));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -481,6 +584,19 @@ int conf_reload(void)
|
||||
svc_mark_dynamic();
|
||||
tty_mark();
|
||||
|
||||
if (rescue) {
|
||||
int rc;
|
||||
char line[80] = "tty [12345] @console noclear nologin";
|
||||
|
||||
/* If rescue.conf is missing, fall back to a root shell */
|
||||
rc = parse_conf(RESCUE_CONF);
|
||||
if (rc)
|
||||
tty_register(line, global_rlimit, NULL);
|
||||
|
||||
print(rc, "Entering rescue mode");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* First, read /etc/finit.conf */
|
||||
parse_conf(FINIT_CONF);
|
||||
|
||||
@@ -488,13 +604,9 @@ int conf_reload(void)
|
||||
num = scandir(rcsd, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("Skipping %s, no files found ...", rcsd);
|
||||
return 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Update global limits in case operator used prlimit(1) on us */
|
||||
for (i = 0; i < RLIMIT_NLIMITS; i++)
|
||||
getrlimit(i, &global_rlimit[i]);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char *name = e[i]->d_name;
|
||||
char path[LINE_SIZE];
|
||||
@@ -542,9 +654,14 @@ int conf_reload(void)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
done:
|
||||
/* Drop record of all .conf changes */
|
||||
drop_changes();
|
||||
|
||||
/* Override configured runlevel, user said 'S' on /proc/cmdline */
|
||||
if (BOOTSTRAP && single)
|
||||
cfglevel = 1;
|
||||
|
||||
/*
|
||||
* Set host name, from %DEFHOST, *.conf or /etc/hostname. The
|
||||
* latter wins, if neither exists we default to "noname"
|
||||
@@ -615,11 +732,22 @@ static int do_change(char *name, uint32_t mask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_any_change(void)
|
||||
{
|
||||
if (TAILQ_EMPTY(&conf_change_list))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int conf_changed(char *file)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (file && (ptr = strrchr(file, '/')))
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
if ((ptr = strrchr(file, '/')))
|
||||
file = ++ptr;
|
||||
|
||||
if (conf_find(file))
|
||||
@@ -717,6 +845,10 @@ int conf_monitor(uev_ctx_t *ctx)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* Skip second run, when called from finit.c in rescue mode */
|
||||
if (ctx && rescue)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If only one watcher fails, that's OK. A user may have only
|
||||
* one of /etc/finit.conf or /etc/finit.d in use, and may also
|
||||
|
||||
@@ -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);
|
||||
@@ -33,6 +36,7 @@ char *rlim2str(int rlim);
|
||||
|
||||
int conf_init (void);
|
||||
void conf_reload (void);
|
||||
int conf_any_change (void);
|
||||
int conf_changed (char *file);
|
||||
int conf_monitor (uev_ctx_t *ctx);
|
||||
|
||||
|
||||
+14
@@ -423,6 +423,20 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, st
|
||||
return pid;
|
||||
}
|
||||
|
||||
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
prepare_tty(tty, B0, "finit-sh", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
_exit(sh(tty));
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
int run_parts(char *dir, char *cmd)
|
||||
{
|
||||
struct dirent **e;
|
||||
|
||||
+76
-105
@@ -53,6 +53,9 @@ int wdogpid = 0; /* No watchdog by default */
|
||||
int runlevel = 0; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
int prevlevel = -1;
|
||||
int rescue = 0; /* rescue mode from kernel cmdline */
|
||||
int single = 0; /* single user mode from kernel cmdline */
|
||||
int splash = 0; /* splash + progress enabled on kernel cmdline */
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *hostname = NULL;
|
||||
@@ -81,14 +84,13 @@ static void banner(void)
|
||||
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
||||
}
|
||||
|
||||
/* Requires /proc to be mounted */
|
||||
static int fismnt(char *dir)
|
||||
static int ismnt(char *file, char *dir)
|
||||
{
|
||||
FILE *fp;
|
||||
int found = 0;
|
||||
struct mntent *mnt;
|
||||
|
||||
fp = setmntent("/proc/mounts", "r");
|
||||
fp = setmntent(file, "r");
|
||||
if (!fp)
|
||||
return 0; /* Dunno, maybe not */
|
||||
|
||||
@@ -103,6 +105,12 @@ static int fismnt(char *dir)
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Requires /proc to be mounted */
|
||||
static int fismnt(char *dir)
|
||||
{
|
||||
return ismnt("/proc/mounts", dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check all filesystems in /etc/fstab with a fs_passno > 0
|
||||
*/
|
||||
@@ -150,54 +158,6 @@ static int fsck(int pass)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void networking(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
/* Run user network start script if enabled */
|
||||
if (network) {
|
||||
run_interactive(network, "Starting networking: %s", network);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Debian/Ubuntu/Busybox/RH/Suse */
|
||||
if (!whichp("ifup"))
|
||||
goto done;
|
||||
|
||||
fp = fopen("/etc/network/interfaces", "r");
|
||||
if (fp) {
|
||||
int i = 0;
|
||||
char buf[160];
|
||||
|
||||
/* Bring up all 'auto' interfaces */
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char cmd[80];
|
||||
char *line, *ifname = NULL;
|
||||
|
||||
chomp(buf);
|
||||
line = strip_line(buf);
|
||||
|
||||
if (!strncmp(line, "auto", 4))
|
||||
ifname = &line[5];
|
||||
if (!strncmp(line, "allow-hotplug", 13))
|
||||
ifname = &line[14];
|
||||
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
snprintf(cmd, 80, "ifup %s", ifname);
|
||||
run_interactive(cmd, "Bringing up interface %s", ifname);
|
||||
i++;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
done:
|
||||
/* Fall back to bring up at least loopback */
|
||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* If everything goes south we can use this to give the operator an
|
||||
* emergency shell to debug the problem -- Finit should not crash!
|
||||
@@ -227,7 +187,7 @@ static void emergency_shell(void)
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
||||
fprintf(stderr, "=> To debug, add '--debug' to the kernel command line.\n\n");
|
||||
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
|
||||
|
||||
/*
|
||||
* Become session leader and set controlling TTY
|
||||
@@ -255,15 +215,14 @@ static void emergency_shell(void)
|
||||
static void finalize(void)
|
||||
{
|
||||
/*
|
||||
* Network stuff
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
_d("Setting up networking ...");
|
||||
networking();
|
||||
umask(022);
|
||||
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
_d("Calling all network up hooks ...");
|
||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||
if (runparts && fisdir(runparts) && !rescue) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
run_parts(runparts, NULL);
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel
|
||||
@@ -280,19 +239,11 @@ static void finalize(void)
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts)) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
run_parts(runparts, NULL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Convenient SysV compat for when you just don't care ... */
|
||||
if (!access(FINIT_RC_LOCAL, X_OK)) {
|
||||
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue) {
|
||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||
service_reload_dynamic();
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
@@ -324,19 +275,6 @@ int main(int argc, char* argv[])
|
||||
if (getpid() != 1)
|
||||
return client(argc, argv);
|
||||
|
||||
/* Set up canvas */
|
||||
screen_init();
|
||||
|
||||
/*
|
||||
* In case of emergency.
|
||||
*/
|
||||
emergency_shell();
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
sig_init();
|
||||
|
||||
/*
|
||||
* Initalize event context.
|
||||
*/
|
||||
@@ -359,15 +297,30 @@ int main(int argc, char* argv[])
|
||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* Parse kernel parameters, including log_init()
|
||||
* Parse kernel command line (debug, rescue, splash, etc.)
|
||||
* Also calls log_init() to set correct log level
|
||||
*/
|
||||
conf_parse_cmdline();
|
||||
|
||||
/* Set up canvas */
|
||||
if (!rescue && !log_is_debug())
|
||||
screen_init();
|
||||
|
||||
/*
|
||||
* In case of emergency.
|
||||
*/
|
||||
emergency_shell();
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
sig_init();
|
||||
|
||||
/*
|
||||
* Load plugins early, finit.conf may contain references to
|
||||
* features implemented by plugins.
|
||||
*/
|
||||
plugin_init(&loop, PLUGIN_PATH);
|
||||
plugin_init(&loop);
|
||||
|
||||
/*
|
||||
* Hello world.
|
||||
@@ -377,11 +330,17 @@ int main(int argc, char* argv[])
|
||||
/*
|
||||
* Check file filesystems in /etc/fstab
|
||||
*/
|
||||
for (int pass = 1; pass < 10; pass++) {
|
||||
for (int pass = 1; pass < 10 && !rescue; pass++) {
|
||||
if (fsck(pass))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize .conf system and load static /etc/finit.conf
|
||||
* Also initializes global_rlimit[] for udevd, below.
|
||||
*/
|
||||
conf_init();
|
||||
|
||||
/*
|
||||
* Some non-embedded systems without an initramfs may not have /dev mounted yet
|
||||
* If they do, check if system has udevadm and perform cleanup from initramfs
|
||||
@@ -395,8 +354,15 @@ int main(int argc, char* argv[])
|
||||
makedir("/dev/pts", 0755);
|
||||
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
||||
|
||||
/* Some systems rely on /dev/shm being there, for `mount -a` below */
|
||||
/*
|
||||
* Some systems rely on us to both create /dev/shm and, to mount
|
||||
* a tmpfs there. Any system with dbus needs shared memory, so
|
||||
* mount it, unless its already mounted, but not if listed in
|
||||
* the /etc/fstab file already.
|
||||
*/
|
||||
makedir("/dev/shm", 0755);
|
||||
if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm"))
|
||||
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* New tmpfs based /run for volatile runtime data
|
||||
@@ -450,11 +416,6 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize .conf system and load static /etc/finit.conf
|
||||
*/
|
||||
conf_init();
|
||||
|
||||
/*
|
||||
* Start built-in watchdog as soon as possible, if enabled
|
||||
*/
|
||||
@@ -463,12 +424,14 @@ int main(int argc, char* argv[])
|
||||
/*
|
||||
* Mount filesystems
|
||||
*/
|
||||
if (!rescue) {
|
||||
#ifdef REMOUNT_ROOTFS
|
||||
run("mount -n -o remount,rw /");
|
||||
run("mount -n -o remount,rw /");
|
||||
#endif
|
||||
#ifdef SYSROOT
|
||||
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Debian has this little script to copy generated rules while the system was read-only */
|
||||
if (fexist("/lib/udev/udev-finish"))
|
||||
@@ -477,28 +440,35 @@ int main(int argc, char* argv[])
|
||||
/* Bootstrap conditions, needed for hooks */
|
||||
cond_init();
|
||||
|
||||
_d("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
if (!rescue) {
|
||||
_d("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
|
||||
umask(0);
|
||||
if (run_interactive("mount -na", "Mounting filesystems"))
|
||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||
umask(0);
|
||||
if (run_interactive("mount -na", "Mounting filesystems"))
|
||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||
|
||||
run("swapon -ea");
|
||||
umask(0022);
|
||||
_d("Calling extra mount hook, after mount -a ...");
|
||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||
|
||||
run("swapon -ea");
|
||||
umask(0022);
|
||||
}
|
||||
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
if (!rescue) {
|
||||
_d("Base FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up inotify watcher for /etc/finit.d and read all .conf
|
||||
* files to figure out how to bootstrap the system.
|
||||
*/
|
||||
conf_monitor(&loop);
|
||||
|
||||
_d("Base FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
|
||||
/*
|
||||
* Initalize state machine and start all bootstrap tasks
|
||||
* NOTE: no network available!
|
||||
@@ -508,6 +478,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
/* Start new initctl API responder */
|
||||
api_init(&loop);
|
||||
umask(022);
|
||||
|
||||
/*
|
||||
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
|
||||
|
||||
@@ -96,6 +96,9 @@ extern int wdogpid;
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int prevlevel;
|
||||
extern int rescue;
|
||||
extern int single;
|
||||
extern int splash;
|
||||
extern char *rcsd;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
|
||||
+54
@@ -243,6 +243,60 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
return do_login(name);
|
||||
}
|
||||
|
||||
int sh(char *tty)
|
||||
{
|
||||
int fd;
|
||||
char *arg0;
|
||||
char *args[2] = {
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
size_t len;
|
||||
struct termios term;
|
||||
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
|
||||
fd = open(tty, O_RDWR);
|
||||
if (fd < 0)
|
||||
err(1, "Failed opening %s", tty);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
warn("Failed TIOCSCTTY");
|
||||
|
||||
/* The getty process is usually responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(fd, B0);
|
||||
close(fd);
|
||||
|
||||
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||
len = strlen(_PATH_BSHELL) + 2;
|
||||
arg0 = malloc(len);
|
||||
if (!arg0)
|
||||
err(1, "Failed allocating memory");
|
||||
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||
args[0] = arg0;
|
||||
|
||||
/* Reenable Ctrl-D and Ctrl-C, and ... */
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_lflag |= ISIG;
|
||||
term.c_cc[VEOF] = CTL('D');
|
||||
term.c_cc[VINTR] = CTL('C');
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||
}
|
||||
|
||||
/* ... unblock signals in general */
|
||||
sig_unblock();
|
||||
|
||||
return execv(_PATH_BSHELL, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
@@ -184,6 +184,9 @@ void set_hostname(char **hostname)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if (rescue)
|
||||
goto done;
|
||||
|
||||
fp = fopen("/etc/hostname", "r");
|
||||
if (fp) {
|
||||
struct stat st;
|
||||
@@ -204,10 +207,86 @@ void set_hostname(char **hostname)
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
done:
|
||||
if (*hostname)
|
||||
sethostname(*hostname, strlen(*hostname));
|
||||
}
|
||||
|
||||
static void ifup(char *ifname, int updown)
|
||||
{
|
||||
char cmd[80];
|
||||
|
||||
if (updown) {
|
||||
snprintf(cmd, sizeof(cmd), "ifup %s", ifname);
|
||||
run_interactive(cmd, "Bringing up interface %s", ifname);
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), "ifdown -f %s", ifname);
|
||||
run_interactive(cmd, "Taking down interface %s", ifname);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Bring up networking, but only if not single-user or rescue mode
|
||||
*/
|
||||
void networking(int updown)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
/* No need to report errors if network is already down */
|
||||
if (!prevlevel && !updown)
|
||||
return;
|
||||
|
||||
if (updown)
|
||||
_d("Setting up networking ...");
|
||||
else
|
||||
_d("Taking down networking ...");
|
||||
|
||||
/* Run user network start script if enabled */
|
||||
if (updown && network) {
|
||||
run_interactive(network, "Starting networking: %s", network);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Debian/Ubuntu/Busybox/RH/Suse */
|
||||
if (!whichp("ifup"))
|
||||
goto done;
|
||||
|
||||
fp = fopen("/etc/network/interfaces", "r");
|
||||
if (fp) {
|
||||
char buf[160];
|
||||
|
||||
/* Bring up, or down, all 'auto' interfaces */
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *line, *ifname = NULL;
|
||||
|
||||
chomp(buf);
|
||||
line = strip_line(buf);
|
||||
|
||||
if (!strncmp(line, "auto", 4))
|
||||
ifname = &line[5];
|
||||
if (!strncmp(line, "allow-hotplug", 13))
|
||||
ifname = &line[14];
|
||||
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
ifup(ifname, updown);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
done:
|
||||
/* Fall back to bring up at least loopback */
|
||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", updown);
|
||||
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
if (updown) {
|
||||
_d("Calling all network up hooks ...");
|
||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_GETFSENT
|
||||
static lfile_t *fstab = NULL;
|
||||
|
||||
|
||||
+3
-3
@@ -52,13 +52,11 @@ struct fstab *getfsent (void);
|
||||
char *strip_line (char *line);
|
||||
|
||||
int getty (char *tty, speed_t speed, char *term, char *user);
|
||||
int sh (char *tty);
|
||||
|
||||
int stty (int fd, speed_t speed);
|
||||
speed_t stty_parse_speed(char *baud);
|
||||
|
||||
int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
void printv (const char *fmt, va_list ap);
|
||||
void print (int action, const char *fmt, ...);
|
||||
void print_desc (char *action, char *desc);
|
||||
@@ -67,6 +65,7 @@ int print_result (int fail);
|
||||
int getuser (char *username, char **home);
|
||||
int getgroup (char *group);
|
||||
void set_hostname (char **hostname);
|
||||
void networking (int updown);
|
||||
|
||||
int complete (char *cmd, int pid);
|
||||
int run (char *cmd);
|
||||
@@ -74,6 +73,7 @@ int run_interactive (char *cmd, char *fmt, ...);
|
||||
int exec_runtask (char *cmd, char *args[]);
|
||||
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
|
||||
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
|
||||
+19
-10
@@ -76,14 +76,15 @@ static void inetd_dgram_drop(int sd, const char *ifname)
|
||||
char pkt_interface[17];
|
||||
char buf[BUFSIZ];
|
||||
|
||||
do {
|
||||
while (1) {
|
||||
inetd_dgram_peek(sd, pkt_interface);
|
||||
if(string_compare(pkt_interface, ifname)) {
|
||||
recv(sd, buf, sizeof(buf), 0);
|
||||
if (string_compare(pkt_interface, ifname)) {
|
||||
if (recv(sd, buf, sizeof(buf), 0) < 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||
@@ -158,6 +159,7 @@ static int get_stdin(svc_t *svc)
|
||||
static void socket_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
svc_t *svc = (svc_t *)arg, *task;
|
||||
const char *conn = " connection";
|
||||
int stdin;
|
||||
|
||||
_d("%s: Got socket event ...", svc->cmd);
|
||||
@@ -207,11 +209,13 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
||||
task->inetd.cmd = svc->inetd.cmd;
|
||||
task->inetd.type = svc->inetd.type;
|
||||
|
||||
memcpy(task->rlimit, svc->rlimit, sizeof(task->rlimit));
|
||||
memcpy(task->cond, svc->cond, sizeof(task->cond));
|
||||
memcpy(task->username, svc->username, sizeof(task->username));
|
||||
memcpy(task->group, svc->group, sizeof(task->group));
|
||||
memcpy(task->args, svc->args, sizeof(task->args));
|
||||
snprintf(task->desc, sizeof(task->desc), "%52.52s connection", svc->desc);
|
||||
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
|
||||
strlcat(task->desc, conn, sizeof(task->desc));
|
||||
|
||||
task->stdin_fd = stdin;
|
||||
service_step(task);
|
||||
@@ -333,16 +337,21 @@ int inetd_start(inetd_t *inetd)
|
||||
|
||||
void inetd_stop(inetd_t *inetd)
|
||||
{
|
||||
if (!inetd || !inetd->svc) {
|
||||
_e("Invalid inetd, cannot stop it...");
|
||||
return;
|
||||
}
|
||||
|
||||
if (inetd->watcher.fd != -1) {
|
||||
_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);
|
||||
if (inetd->type == SOCK_STREAM)
|
||||
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
||||
close(inetd->watcher.fd);
|
||||
inetd->watcher.fd = -1;
|
||||
}
|
||||
|
||||
+15
-5
@@ -43,6 +43,11 @@
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||
|
||||
struct command {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
};
|
||||
|
||||
int verbose = 0;
|
||||
int runlevel = 0;
|
||||
|
||||
@@ -272,7 +277,7 @@ static int do_cond(char *cmd)
|
||||
{
|
||||
int c;
|
||||
char *arg;
|
||||
command_t command[] = {
|
||||
struct command command[] = {
|
||||
{ "show", do_cond_show },
|
||||
{ "set", do_cond_set },
|
||||
{ "clear", do_cond_clear },
|
||||
@@ -411,6 +416,9 @@ static int show_status(char *arg)
|
||||
runlevel = runlevel_get(NULL);
|
||||
|
||||
if (arg && arg[0]) {
|
||||
long now = jiffies();
|
||||
char buf[42] = "N/A";
|
||||
|
||||
svc = client_svc_find(arg);
|
||||
if (!svc)
|
||||
return 1;
|
||||
@@ -418,6 +426,7 @@ static int show_status(char *arg)
|
||||
printf("Service : %s\n", svc->cmd);
|
||||
printf("Description : %s\n", svc->desc);
|
||||
printf("PID : %d\n", svc->pid);
|
||||
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
|
||||
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||
printf("Status : %s\n", svc_status(svc));
|
||||
printf("\n");
|
||||
@@ -426,7 +435,7 @@ 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;
|
||||
@@ -436,7 +445,7 @@ static int show_status(char *arg)
|
||||
// else
|
||||
snprintf(jobid, sizeof(jobid), "%d:%d", 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
|
||||
@@ -457,7 +466,7 @@ static int show_status(char *arg)
|
||||
else
|
||||
name++;
|
||||
|
||||
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc);
|
||||
printf("%-16.16s %-*.*s\n", name, adj, adj, svc->desc);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -537,6 +546,7 @@ static int usage(int rc)
|
||||
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
|
||||
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
|
||||
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
|
||||
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
|
||||
" status | show Show status of services, default command\n"
|
||||
"\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
@@ -559,7 +569,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
char *cmd, arg[120];
|
||||
command_t command[] = {
|
||||
struct command command[] = {
|
||||
{ "debug", toggle_debug },
|
||||
{ "help", do_help },
|
||||
{ "version", show_version },
|
||||
|
||||
@@ -29,12 +29,22 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static int up = 0;
|
||||
static int debug = 0;
|
||||
static int silent = SILENT_MODE; /* Completely silent, including boot */
|
||||
static int loglevel = LOG_NOTICE;
|
||||
|
||||
static void muffler(void)
|
||||
{
|
||||
silent = SILENT_MODE;
|
||||
|
||||
/* User override from kernel cmdline */
|
||||
if (splash)
|
||||
silent = 0;
|
||||
}
|
||||
|
||||
void log_init(int dbg)
|
||||
{
|
||||
if (dbg)
|
||||
@@ -44,16 +54,27 @@ void log_init(int dbg)
|
||||
loglevel = LOG_DEBUG;
|
||||
else
|
||||
loglevel = LOG_NOTICE;
|
||||
|
||||
muffler();
|
||||
}
|
||||
|
||||
/* If we enabled terse mode at boot, restore to previous setting at shutdown */
|
||||
void log_exit(void)
|
||||
{
|
||||
silent = SILENT_MODE;
|
||||
muffler();
|
||||
if (!silent) {
|
||||
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)
|
||||
@@ -91,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");
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* errors and debug messages. For all other user messages, see logit()
|
||||
*
|
||||
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
|
||||
* use `initctl debug` or add `--debug` to the kernel cmdline.
|
||||
* use `initctl debug` or add `debug` to the kernel cmdline.
|
||||
*/
|
||||
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args)
|
||||
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt "\n", __func__, ##args)
|
||||
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define SYSLOG_NAMES
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
|
||||
|
||||
|
||||
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||
* At most @num old versions are kept and by default it starts gzipping
|
||||
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||
* files are kept uncompressed.
|
||||
*/
|
||||
static int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
return 1;
|
||||
|
||||
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||
if (num > 0) {
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
|
||||
if (cnt == 2 && !access(nfile, F_OK)) {
|
||||
size_t len = 5 + strlen(nfile) + 1;
|
||||
char cmd[len];
|
||||
|
||||
snprintf(cmd, len, "gzip %s", nfile);
|
||||
system(cmd);
|
||||
|
||||
remove(nfile);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checksz(FILE *fp, off_t sz)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (sz <= 0)
|
||||
return 0;
|
||||
|
||||
if (!fstat(fileno(fp), &st) && st.st_size > sz) {
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flogit(char *logfile, int num, off_t sz, char *buf, size_t len)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
reopen:
|
||||
fp = fopen(logfile, "a");
|
||||
if (!fp) {
|
||||
syslog(LOG_ERR | LOG_PERROR, "Failed opening %s: %s", logfile, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (buf[0]) {
|
||||
fprintf(fp, "%s\n", buf);
|
||||
fsync(fileno(fp));
|
||||
if (checksz(fp, sz))
|
||||
return logrotate(logfile, num, sz);
|
||||
} else {
|
||||
while ((fgets(buf, len, stdin))) {
|
||||
fputs(buf, fp);
|
||||
fsync(fileno(fp));
|
||||
|
||||
if (checksz(fp, sz)) {
|
||||
logrotate(logfile, num, sz);
|
||||
buf[0] = 0;
|
||||
goto reopen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
static int logit(int level, char *buf, size_t len)
|
||||
{
|
||||
if (buf[0]) {
|
||||
syslog(level, "%s", buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((fgets(buf, len, stdin)))
|
||||
syslog(level, "%s", buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_prio(char *arg, int *f, int *l)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
ptr = strchr(arg, '.');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
|
||||
for (int i = 0; facilitynames[i].c_name; i++) {
|
||||
if (!strcmp(facilitynames[i].c_name, arg)) {
|
||||
*f = facilitynames[i].c_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
arg = ptr;
|
||||
}
|
||||
|
||||
for (int i = 0; prioritynames[i].c_name; i++) {
|
||||
if (!strcmp(prioritynames[i].c_name, arg)) {
|
||||
*l = prioritynames[i].c_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usage(int code)
|
||||
{
|
||||
fprintf(stderr, "Usage: logit [OPTIONS] [MESSAGE]\n"
|
||||
"\n"
|
||||
"Write MESSAGE (or stdin) to syslog, or file (with logrotate)\n"
|
||||
"\n"
|
||||
" -h This help text\n"
|
||||
" -p PRIO Priority (numeric or facility.level pair)\n"
|
||||
" -t TAG Log using the specified tag (defaults to user name)\n"
|
||||
" -s Log to stderr as well as the system log\n"
|
||||
"\n"
|
||||
" -f FILE File to write log messages to, instead of syslog\n"
|
||||
" -n SIZE Number of bytes before rotating, default: 200 kB\n"
|
||||
" -r NUM Number of rotated files to keep, default: 5\n"
|
||||
" -v Show program version\n"
|
||||
"\n"
|
||||
"This version of logit is distributed as part of Finit.\n"
|
||||
"Bug report address: %s\n", PACKAGE_BUGREPORT);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c, rc, num = 5;
|
||||
int facility = LOG_USER;
|
||||
int level = LOG_INFO;
|
||||
int log_opts = LOG_NOWAIT;
|
||||
off_t size = 200 * 1024;
|
||||
char *ident = NULL, *logfile = NULL;
|
||||
char buf[512] = "";
|
||||
|
||||
while ((c = getopt(argc, argv, "f:hn:p:r:st:v")) != EOF) {
|
||||
switch (c) {
|
||||
case 'f':
|
||||
logfile = optarg;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
return usage(0);
|
||||
|
||||
case 'n':
|
||||
size = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (parse_prio(optarg, &facility, &level))
|
||||
return usage(1);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
num = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
log_opts |= LOG_PERROR;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
ident = optarg;
|
||||
break;
|
||||
|
||||
case 'v': /* version */
|
||||
fprintf(stderr, "%s\n", version_info);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ident)
|
||||
ident = getenv("LOGNAME") ?: getenv("USER");
|
||||
|
||||
if (optind < argc) {
|
||||
size_t pos = 0, len = sizeof(buf);
|
||||
|
||||
while (optind < argc) {
|
||||
size_t bytes;
|
||||
|
||||
bytes = snprintf(&buf[pos], len, "%s ", argv[optind++]);
|
||||
pos += bytes;
|
||||
len -= bytes;
|
||||
}
|
||||
}
|
||||
|
||||
openlog(ident, log_opts, facility);
|
||||
|
||||
if (logfile)
|
||||
rc = flogit(logfile, num, size, buf, sizeof(buf));
|
||||
else
|
||||
rc = logit(level, buf, sizeof(buf));
|
||||
|
||||
closelog();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Misc. utility functions and C-library extensions for finit and its plugins
|
||||
/* PID and PID file helpers
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2008-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
|
||||
@@ -23,11 +23,14 @@
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "pid.h"
|
||||
#include "svc.h"
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
@@ -90,6 +93,117 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
return pname;
|
||||
}
|
||||
|
||||
char *pid_file(svc_t *svc)
|
||||
{
|
||||
char fn[MAX_ARG_LEN];
|
||||
static char path[MAX_ARG_LEN];
|
||||
|
||||
if (svc->pidfile[0]) {
|
||||
if (svc->pidfile[0] == '!')
|
||||
return &svc->pidfile[1];
|
||||
return svc->pidfile;
|
||||
}
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
||||
|
||||
return pid_runpath(fn, path, sizeof(path));
|
||||
}
|
||||
|
||||
int pid_file_create(svc_t *svc)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if (!svc->pidfile[0] || svc->pidfile[0] == '!')
|
||||
return 1;
|
||||
|
||||
fp = fopen(svc->pidfile, "w");
|
||||
if (!fp)
|
||||
return 1;
|
||||
fprintf(fp, "%d\n", svc->pid);
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
static int pid_realpath(svc_t *svc, char *file)
|
||||
{
|
||||
int not = 0;
|
||||
|
||||
if (!file)
|
||||
file = pid_file(svc);
|
||||
|
||||
if (file[0] == '!') {
|
||||
not = 1;
|
||||
file++;
|
||||
}
|
||||
|
||||
pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not);
|
||||
if (not)
|
||||
svc->pidfile[0] = '!';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function parses a PID file @arg for @svc.
|
||||
*
|
||||
* The logic is explained below. Please note that using the first form
|
||||
* of the syntax not only creates and removes the PID file, but it also
|
||||
* calls touch to update the mtime on service_restart(), only applicable
|
||||
* to processes that accept SIGHUP. This to ensure dependant services
|
||||
* are sent SIGCONT properly on reload, otherwise their condition would
|
||||
* never be asserted again after `initctl reload`.
|
||||
*
|
||||
* pid --> /run/$(basename).pid
|
||||
* pid:foo --> /run/foo.pid
|
||||
* pid:foo.pid --> /run/foo.pid
|
||||
* pid:foo.tla --> /run/foo.tla.pid
|
||||
* pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!)
|
||||
*
|
||||
* An exclamation mark, or logial NOT, in the first character position
|
||||
* indicates that Finit should *not* manage the PID file, and that the
|
||||
* process uses a "non-standard" location. Eg. if service is 'bar'
|
||||
* Finit would assume the PID file is /run/bar.pid and the below simply
|
||||
* 'redirects' Finit to use the correct PID file.
|
||||
*
|
||||
* pid:!foo --> !/run/foo.pid
|
||||
* pid:!/run/foo.pid --> !/run/foo.pid
|
||||
*
|
||||
* Note, nothing is created or removed by Finit in this latter form.
|
||||
*/
|
||||
int pid_file_parse(svc_t *svc, char *arg)
|
||||
{
|
||||
/* Sanity check ... */
|
||||
if (!arg || !arg[0])
|
||||
return 1;
|
||||
|
||||
/* 'pid:' implies argument following*/
|
||||
if (!strncmp(arg, "pid:", 4)) {
|
||||
int len, not = 0;
|
||||
char path[MAX_ARG_LEN];
|
||||
|
||||
arg += 4;
|
||||
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
|
||||
return pid_realpath(svc, arg);
|
||||
|
||||
if (arg[0] == '!') {
|
||||
arg++;
|
||||
not++;
|
||||
}
|
||||
|
||||
len = snprintf(path, sizeof(path), "%s%s%s", not ? "!" : "", _PATH_VARRUN, arg);
|
||||
if (len > 4 && strcmp(&path[len - 4], ".pid"))
|
||||
strlcat(path, ".pid", sizeof(path));
|
||||
|
||||
return pid_realpath(svc, path);
|
||||
}
|
||||
|
||||
/* 'pid' arg, no argument following */
|
||||
if (!strcmp(arg, "pid"))
|
||||
return pid_realpath(svc, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* PID and PID file helpers
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-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
|
||||
* 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_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);
|
||||
|
||||
char *pid_file (svc_t *svc);
|
||||
int pid_file_create (svc_t *svc);
|
||||
int pid_file_parse (svc_t *svc, char *arg);
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
#endif /* FINIT_PID_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+7
-6
@@ -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);
|
||||
|
||||
@@ -196,10 +199,8 @@ void plugin_run_hook(hook_point_t no, void *arg)
|
||||
* End recursion: any plugin hook => start service => SVC start
|
||||
* hook => start service ... err ... wait a second
|
||||
*/
|
||||
if (HOOK_SVC_START == last) {
|
||||
_d("End plugin recursion in our lifetime!");
|
||||
if (HOOK_SVC_START == last)
|
||||
return;
|
||||
}
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (p->hook[no].cb) {
|
||||
@@ -212,7 +213,7 @@ void plugin_run_hook(hook_point_t no, void *arg)
|
||||
if (HOOK_SVC_START == no)
|
||||
last = no;
|
||||
|
||||
cond_set(hook_cond[no]);
|
||||
cond_set_oneshot(hook_cond[no]);
|
||||
service_step_all(SVC_TYPE_RUNTASK);
|
||||
|
||||
if (HOOK_SVC_START == no)
|
||||
@@ -384,11 +385,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;
|
||||
|
||||
+17
-8
@@ -29,10 +29,6 @@
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
|
||||
#define PLUGIN_PATH "/dev/null"
|
||||
#endif
|
||||
|
||||
#define PLUGIN_DEP_MAX 10
|
||||
|
||||
/*
|
||||
@@ -54,22 +50,35 @@
|
||||
* Predefined hook points and corresponding conditions in Finit,
|
||||
* for use by plugins and scripts. Recommended to use the task
|
||||
* or run stanzas: task <hook/mount/error> /bin/rescue.sh
|
||||
*
|
||||
* Some of the below hooks cannot (currently) be realized as conditions.
|
||||
* The idea was to rename them action scripts, with optional argument,
|
||||
* 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
|
||||
* general release, so it was pulled.
|
||||
*/
|
||||
#define HOOK_TYPES { \
|
||||
/* Bootstrap hooks, runlevel [S] */ \
|
||||
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"), \
|
||||
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
||||
\
|
||||
/* Runtime hooks, runlevel [S1-9] */ \
|
||||
CHOOSE(HOOK_SVC_RECONF, "hook/svc/reconf"), \
|
||||
CHOOSE(HOOK_SVC_LOST, "hook/svc/lost"), \
|
||||
CHOOSE(HOOK_SVC_START, "hook/svc/start"), \
|
||||
CHOOSE(HOOK_RUNLEVEL_CHANGE, "hook/sys/runlevel"), \
|
||||
CHOOSE(HOOK_SVC_RECONF, "nop"), \
|
||||
CHOOSE(HOOK_SVC_LOST, "nop"), \
|
||||
CHOOSE(HOOK_SVC_START, "nop"), \
|
||||
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
|
||||
\
|
||||
/* Shutdown hooks, runlevel [06] */ \
|
||||
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
|
||||
|
||||
+1
-1
@@ -45,7 +45,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
-1
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (c)
|
||||
err(1, "Failed signalling init to %s", msg);
|
||||
do_sleep(2);
|
||||
do_sleep(5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
runlevel 1
|
||||
hostname rescue
|
||||
tty [12345] @console noclear nologin
|
||||
+217
-77
@@ -25,6 +25,7 @@
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h> /* isblank() */
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -36,11 +37,13 @@
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "inetd.h"
|
||||
#include "pid.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "service.h"
|
||||
#include "sm.h"
|
||||
#include "tty.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||
@@ -60,7 +63,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,6 +110,18 @@ static int service_timeout_cancel(svc_t *svc)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void redirect_null(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/dev/null", "w");
|
||||
if (fp) {
|
||||
dup2(fileno(fp), STDOUT_FILENO);
|
||||
dup2(fileno(fp), STDERR_FILENO);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
static int is_norespawn(void)
|
||||
{
|
||||
return sig_stopped() ||
|
||||
@@ -122,7 +138,7 @@ static int is_norespawn(void)
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -145,10 +161,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);
|
||||
@@ -210,30 +231,38 @@ static int service_start(svc_t *svc)
|
||||
} else
|
||||
#endif
|
||||
|
||||
if (svc->log) {
|
||||
if (svc->log.enabled) {
|
||||
int fd;
|
||||
|
||||
if (svc->log.null) {
|
||||
redirect_null();
|
||||
goto logit_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open PTY to connect to logger. A pty isn't buffered
|
||||
* like a pipe, and it eats newlines so they aren't logged
|
||||
*/
|
||||
fd = posix_openpt(O_RDWR);
|
||||
if (fd == -1) {
|
||||
svc->log = 0;
|
||||
goto logger_err;
|
||||
svc->log.enabled = 0;
|
||||
goto logit_done;
|
||||
}
|
||||
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
|
||||
close(fd);
|
||||
svc->log = 0;
|
||||
goto logger_err;
|
||||
svc->log.enabled = 0;
|
||||
goto logit_done;
|
||||
}
|
||||
|
||||
/* SIGCHLD is still blocked for grantpt() and fork() */
|
||||
sigprocmask(SIG_BLOCK, &nmask, NULL);
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
int fds = open(ptsname(fd), O_RDONLY);
|
||||
int fds;
|
||||
char *tag = basename(svc->cmd);
|
||||
char *prio = "daemon.info";
|
||||
|
||||
fds = open(ptsname(fd), O_RDONLY);
|
||||
close(fd);
|
||||
if (fds == -1)
|
||||
_exit(0);
|
||||
@@ -242,7 +271,22 @@ static int service_start(svc_t *svc)
|
||||
/* Reset signals */
|
||||
sig_unblock();
|
||||
|
||||
execlp("logger", "logger", "-t", svc->cmd, "-p", "daemon.info", NULL);
|
||||
if (svc->log.file[0] == '/') {
|
||||
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);
|
||||
}
|
||||
|
||||
if (svc->log.ident[0])
|
||||
tag = svc->log.ident;
|
||||
if (svc->log.prio[0])
|
||||
prio = svc->log.prio;
|
||||
|
||||
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
@@ -258,18 +302,13 @@ static int service_start(svc_t *svc)
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/dev/null", "w");
|
||||
if (fp) {
|
||||
dup2(fileno(fp), STDOUT_FILENO);
|
||||
dup2(fileno(fp), STDERR_FILENO);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
#ifdef REDIRECT_OUTPUT
|
||||
else
|
||||
redirect_null();
|
||||
#endif
|
||||
|
||||
logger_err:
|
||||
logit_done:
|
||||
sig_unblock();
|
||||
|
||||
if (svc->inetd.cmd)
|
||||
@@ -288,9 +327,9 @@ static int service_start(svc_t *svc)
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (svc->log)
|
||||
if (svc->log.enabled && !svc->log.null)
|
||||
waitpid(pid, NULL, 0);
|
||||
exit(status);
|
||||
_exit(status);
|
||||
} else if (log_is_debug()) {
|
||||
char buf[CMD_SIZE] = "";
|
||||
|
||||
@@ -305,6 +344,7 @@ static int service_start(svc_t *svc)
|
||||
}
|
||||
|
||||
svc->pid = pid;
|
||||
svc->start_time = jiffies();
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd_conn(svc) && svc->inetd.type == SOCK_STREAM)
|
||||
@@ -314,13 +354,17 @@ static int service_start(svc_t *svc)
|
||||
if (SVC_TYPE_RUN == svc->type) {
|
||||
result = WEXITSTATUS(complete(svc->cmd, pid));
|
||||
if (!svc_clean_bootstrap(svc)) {
|
||||
svc->pid = 0;
|
||||
svc->start_time = svc->pid = 0;
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (svc_is_daemon(svc))
|
||||
pid_file_create(svc);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
print_result(result);
|
||||
if (do_progress)
|
||||
print_result(result);
|
||||
|
||||
/*
|
||||
* Only run hook on successful start, and *after* having printed
|
||||
@@ -349,10 +393,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] */
|
||||
@@ -376,31 +420,31 @@ static int service_stop(svc_t *svc)
|
||||
|
||||
#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)
|
||||
@@ -421,6 +465,8 @@ static int service_stop(svc_t *svc)
|
||||
*/
|
||||
static int service_restart(svc_t *svc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Ignore if finit is SIGSTOP'ed */
|
||||
if (is_norespawn())
|
||||
return 1;
|
||||
@@ -430,17 +476,29 @@ static int service_restart(svc_t *svc)
|
||||
|
||||
if (svc->pid <= 1) {
|
||||
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
|
||||
svc->pid = 0;
|
||||
svc->start_time = svc->pid = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_desc("Restarting ", svc->desc);
|
||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||
if (svc->desc[0])
|
||||
print_desc("Restarting ", svc->desc);
|
||||
|
||||
rc = kill(svc->pid, SIGHUP);
|
||||
|
||||
/* Declare we're waiting for svc to re-assert/touch its pidfile */
|
||||
svc_starting(svc);
|
||||
|
||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||
return print_result(kill(svc->pid, SIGHUP));
|
||||
/* Service does not maintain a PID file on its own */
|
||||
if (svc_has_pidfile(svc)) {
|
||||
sched_yield();
|
||||
touch(pid_file(svc));
|
||||
}
|
||||
|
||||
if (svc->desc[0])
|
||||
print_result(rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,8 +523,38 @@ void service_reload_dynamic(void)
|
||||
*/
|
||||
void service_runlevel(int newlevel)
|
||||
{
|
||||
if (!rescue && runlevel <= 1 && newlevel > 1)
|
||||
networking(1);
|
||||
|
||||
sm_set_runlevel(&sm, newlevel);
|
||||
sm_step(&sm);
|
||||
|
||||
if (!rescue && runlevel <= 1)
|
||||
networking(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* log:/path/to/logfile,priority:facility.level,tag:ident
|
||||
*/
|
||||
static void parse_log(svc_t *svc, char *arg)
|
||||
{
|
||||
char *tok;
|
||||
|
||||
tok = strtok(arg, ":, ");
|
||||
while (tok) {
|
||||
if (!strcmp(tok, "log"))
|
||||
svc->log.enabled = 1;
|
||||
else if (!strcmp(tok, "null") || !strcmp(tok, "/dev/null"))
|
||||
svc->log.null = 1;
|
||||
else if (tok[0] == '/')
|
||||
strlcpy(svc->log.file, tok, sizeof(svc->log.file));
|
||||
else if (!strcmp(tok, "priority") || !strcmp(tok, "prio"))
|
||||
strlcpy(svc->log.prio, strtok(NULL, ","), sizeof(svc->log.prio));
|
||||
else if (!strcmp(tok, "tag") || !strcmp(tok, "identity") || !strcmp(tok, "ident"))
|
||||
strlcpy(svc->log.ident, strtok(NULL, ","), sizeof(svc->log.ident));
|
||||
|
||||
tok = strtok(NULL, ":=, ");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,7 +595,7 @@ void service_runlevel(int newlevel)
|
||||
*
|
||||
* Service conditions can be: svc/<PATH> for PID files, net/<IFNAME>/up
|
||||
* and net/<IFNAME>/exists. The condition handling is further described
|
||||
* doc/conditions.md.
|
||||
* docs/conditions.md.
|
||||
*
|
||||
* For multiple instances of the same command, e.g. multiple DHCP
|
||||
* clients, the user must enter an ID, using the :ID syntax.
|
||||
@@ -528,8 +616,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
#ifdef INETD_ENABLED
|
||||
int forking = 0;
|
||||
#endif
|
||||
int log = 0, levels = 0;
|
||||
char *username = NULL, *line;
|
||||
int levels = 0;
|
||||
char *line;
|
||||
char *username = NULL, *log = NULL, *pid = NULL;
|
||||
char *service = NULL, *proto = NULL, *ifaces = NULL;
|
||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
||||
svc_t *svc;
|
||||
@@ -551,6 +640,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, " ");
|
||||
@@ -562,7 +660,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
|
||||
while (cmd) {
|
||||
if (cmd[0] == '@') /* @username[:group] */
|
||||
if (cmd[0] == '@') /* @username[:group] */
|
||||
username = &cmd[1];
|
||||
else if (cmd[0] == '[') /* [runlevels] */
|
||||
runlevels = &cmd[0];
|
||||
@@ -576,10 +674,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
else if (!strncasecmp(cmd, "wait", 4))
|
||||
forking = 0;
|
||||
#endif
|
||||
else if (!strncasecmp(cmd, "log", 3))
|
||||
log = cmd;
|
||||
else if (!strncasecmp(cmd, "pid", 3))
|
||||
pid = cmd;
|
||||
else if (cmd[0] != '/' && strchr(cmd, '/'))
|
||||
service = cmd; /* inetd service/proto */
|
||||
else if (!strncasecmp(cmd, "log", 3))
|
||||
log = 1;
|
||||
else
|
||||
break;
|
||||
|
||||
@@ -664,9 +764,11 @@ recreate:
|
||||
}
|
||||
#endif
|
||||
|
||||
svc->log = log;
|
||||
if (desc)
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
/* Always clear svc PID file, for now. See TODO */
|
||||
svc->pidfile[0] = 0;
|
||||
/* Decode any optional pid:/optional/path/to/file.pid */
|
||||
if (pid && svc_is_daemon(svc) && pid_file_parse(svc, pid))
|
||||
_e("Invalid 'pid' argument to service: %s", pid);
|
||||
|
||||
if (username) {
|
||||
char *ptr = strchr(username, ':');
|
||||
@@ -694,6 +796,11 @@ recreate:
|
||||
|
||||
conf_parse_cond(svc, cond);
|
||||
|
||||
if (log)
|
||||
parse_log(svc, log);
|
||||
if (desc)
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd(svc)) {
|
||||
char *iface, *name = service;
|
||||
@@ -732,20 +839,52 @@ recreate:
|
||||
else
|
||||
svc_mark_clean(svc);
|
||||
|
||||
if (!file)
|
||||
svc->protected = 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()
|
||||
*/
|
||||
if (!svc_is_inetd_conn(svc))
|
||||
service_stop(svc);
|
||||
|
||||
if (svc_is_inetd(svc)) {
|
||||
if (svc_is_busy(svc->inetd.svc)) {
|
||||
svc_unblock(svc->inetd.svc);
|
||||
service_step(svc->inetd.svc);
|
||||
}
|
||||
|
||||
inetd_del(&svc->inetd);
|
||||
}
|
||||
|
||||
svc_del(svc);
|
||||
}
|
||||
|
||||
void service_monitor(pid_t lost)
|
||||
{
|
||||
svc_t *svc;
|
||||
char pidfile[MAX_ARG_LEN];
|
||||
|
||||
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||
return;
|
||||
@@ -764,19 +903,22 @@ void service_monitor(pid_t lost)
|
||||
_d("collected %s(%d)", svc->cmd, lost);
|
||||
|
||||
/* Try removing PID file (in case service does not clean up after itself) */
|
||||
snprintf(pidfile, sizeof(pidfile), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
||||
if (remove(pidfile)) {
|
||||
if (errno != ENOENT)
|
||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), pidfile);
|
||||
if (svc_is_daemon(svc)) {
|
||||
char *fn;
|
||||
|
||||
fn = pid_file(svc);
|
||||
if (remove(fn) && errno != ENOENT)
|
||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn);
|
||||
}
|
||||
|
||||
/* No longer running, update books. */
|
||||
svc->pid = 0;
|
||||
service_step(svc);
|
||||
svc->start_time = svc->pid = 0;
|
||||
|
||||
/* Clean out any bootstrap tasks, they've had their time in the sun. */
|
||||
if (svc_clean_bootstrap(svc))
|
||||
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
|
||||
if (!service_step(svc)) {
|
||||
/* Clean out any bootstrap tasks, they've had their time in the sun. */
|
||||
if (svc_clean_bootstrap(svc))
|
||||
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
|
||||
}
|
||||
|
||||
sm_step(&sm);
|
||||
}
|
||||
@@ -790,13 +932,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);
|
||||
@@ -805,7 +947,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);
|
||||
|
||||
@@ -827,7 +969,12 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
||||
}
|
||||
}
|
||||
|
||||
void service_step(svc_t *svc)
|
||||
/*
|
||||
* Transition inetd/task/run/service
|
||||
*
|
||||
* Returns: non-zero if the @svc is no longer valid (removed)
|
||||
*/
|
||||
int service_step(svc_t *svc)
|
||||
{
|
||||
int err;
|
||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||
@@ -852,12 +999,8 @@ 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;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
if (svc_is_changed(svc))
|
||||
@@ -917,7 +1060,6 @@ restart:
|
||||
case SVC_RUNNING_STATE:
|
||||
if (!enabled) {
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -930,7 +1072,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;
|
||||
}
|
||||
@@ -951,7 +1093,6 @@ restart:
|
||||
switch (cond) {
|
||||
case COND_OFF:
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
@@ -968,7 +1109,6 @@ restart:
|
||||
service_restart(svc);
|
||||
} else {
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
}
|
||||
svc_mark_clean(svc);
|
||||
}
|
||||
@@ -980,7 +1120,6 @@ restart:
|
||||
if (!enabled) {
|
||||
kill(svc->pid, SIGCONT);
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1000,7 +1139,6 @@ restart:
|
||||
case COND_OFF:
|
||||
kill(svc->pid, SIGCONT);
|
||||
service_stop(svc);
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
@@ -1013,6 +1151,8 @@ restart:
|
||||
_d("%20s(%4d): -> %8s", svc->cmd, svc->pid, svc_status(svc));
|
||||
goto restart;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void service_step_all(int types)
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ void service_unregister (svc_t *svc);
|
||||
void service_runtask_clean (void);
|
||||
void service_reload_dynamic (void);
|
||||
|
||||
void service_step (svc_t *svc);
|
||||
int service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
|
||||
void service_bootstrap_cb (uev_t *w, void *arg, int events);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
*
|
||||
* SIGHUP
|
||||
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
|
||||
* Also restarts API (initctl) socket, like SysV init and systemd
|
||||
* does on USR1 with their FIFO/D-Bus.
|
||||
*
|
||||
* SIGUSR1
|
||||
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
||||
@@ -131,8 +133,12 @@ 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]);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
@@ -179,6 +185,11 @@ void do_shutdown(shutop_t op)
|
||||
return;
|
||||
}
|
||||
|
||||
if (wdogpid) {
|
||||
print(kill(wdogpid, SIGPWR) == 1, "Advising watchdog, system going down");
|
||||
do_sleep(2);
|
||||
}
|
||||
|
||||
/* Unmount any tmpfs before unmounting swap ... */
|
||||
unmount_tmpfs();
|
||||
run("swapoff -e -a");
|
||||
@@ -201,7 +212,7 @@ void do_shutdown(shutop_t op)
|
||||
int timeout = 10;
|
||||
|
||||
/* Wait here until the WDT reboots, or timeout with fallback */
|
||||
print(kill(wdogpid, SIGPWR) == 1, "Pending watchdog reboot");
|
||||
print(kill(wdogpid, SIGTERM) == 1, "Pending watchdog reboot");
|
||||
while (timeout--)
|
||||
do_sleep(1);
|
||||
}
|
||||
@@ -229,6 +240,10 @@ static void sighup_cb(uev_t *w, void *arg, int events)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Restart initctl API domain socket, similar to systemd/SysV init */
|
||||
api_exit();
|
||||
api_init(w->ctx);
|
||||
|
||||
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ restart:
|
||||
erase("/etc/nologin");
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (runlevel != 0 && runlevel != 6)
|
||||
if (conf_any_change())
|
||||
conf_reload();
|
||||
|
||||
/* Reset once flag of runtasks */
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
#include "helpers.h"
|
||||
#include "pid.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Each svc_t needs a unique job# */
|
||||
@@ -73,7 +74,7 @@ svc_t *svc_new(char *cmd, int id, int type)
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
|
||||
/* Default description, if missing */
|
||||
desc = rindex(cmd, '/');
|
||||
desc = strrchr(cmd, '/');
|
||||
if (desc)
|
||||
desc++;
|
||||
else
|
||||
@@ -205,7 +206,7 @@ svc_t *svc_job_iterator(svc_t **iter, int first, int job)
|
||||
* svc_foreach - Run a callback for each registered service
|
||||
* @cb: Callback to run for each service
|
||||
*/
|
||||
void svc_foreach(void (*cb)(svc_t *))
|
||||
void svc_foreach(int (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
@@ -222,7 +223,7 @@ void svc_foreach(void (*cb)(svc_t *))
|
||||
* @types: Mask of service types
|
||||
* @cb: Callback to run for each matching type
|
||||
*/
|
||||
void svc_foreach_type(int types, void (*cb)(svc_t *))
|
||||
void svc_foreach_type(int types, int (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
@@ -342,6 +343,27 @@ svc_t *svc_find_by_nameid(char *name, int id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_find_by_plidfile - Find an service object by its PID file
|
||||
* @fn: PID file, can be absolute path or relative to /run
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find_by_pidfile(char *fn)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
char path[MAX_ARG_LEN];
|
||||
|
||||
if (string_compare(pid_runpath(fn, path, sizeof(path)), pid_file(svc)))
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
||||
*
|
||||
@@ -356,8 +378,12 @@ void svc_mark_dynamic(void)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->protected)
|
||||
continue;
|
||||
|
||||
*((int *)&svc->dirty) = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void svc_mark_dirty(svc_t *svc)
|
||||
@@ -382,10 +408,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,8 +90,11 @@ typedef struct svc {
|
||||
|
||||
/* 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 */
|
||||
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
|
||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||
int runlevels;
|
||||
@@ -108,7 +111,13 @@ typedef struct svc {
|
||||
int stdin_fd;
|
||||
|
||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
||||
int log;
|
||||
struct {
|
||||
char enabled;
|
||||
char null;
|
||||
char file[64];
|
||||
char prio[20];
|
||||
char ident[20];
|
||||
} log;
|
||||
|
||||
/* Identity */
|
||||
char username[MAX_USER_LEN];
|
||||
@@ -134,14 +143,15 @@ svc_t *svc_find (char *cmd, int id);
|
||||
svc_t *svc_find_by_pid (pid_t pid);
|
||||
svc_t *svc_find_by_jobid (int job, int id);
|
||||
svc_t *svc_find_by_nameid (char *name, int id);
|
||||
svc_t *svc_find_by_pidfile (char *fn);
|
||||
|
||||
svc_t *svc_iterator (svc_t **iter, int first);
|
||||
svc_t *svc_inetd_iterator (svc_t **iter, int first);
|
||||
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
|
||||
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
|
||||
|
||||
void svc_foreach (void (*cb)(svc_t *));
|
||||
void svc_foreach_type (int types, void (*cb)(svc_t *));
|
||||
void svc_foreach (int (*cb)(svc_t *));
|
||||
void svc_foreach_type (int types, int (*cb)(svc_t *));
|
||||
|
||||
svc_t *svc_stop_completed (void);
|
||||
|
||||
@@ -158,8 +168,14 @@ 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));
|
||||
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
||||
|
||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||
static inline int svc_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; }
|
||||
@@ -169,11 +185,6 @@ static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
||||
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
|
||||
|
||||
@@ -137,7 +137,7 @@ void tty_sweep(void)
|
||||
int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
int insert = 0, noclear = 0, nowait = 0;
|
||||
int insert = 0, noclear = 0, nowait = 0, nologin = 0;
|
||||
size_t i, num = 0;
|
||||
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS];
|
||||
char *dev = NULL, *baud = NULL;
|
||||
@@ -159,6 +159,8 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
noclear = 1;
|
||||
else if (!strcmp(tok, "nowait"))
|
||||
nowait = 1;
|
||||
else if (!strcmp(tok, "nologin"))
|
||||
nologin = 1;
|
||||
else
|
||||
args[num++] = tok;
|
||||
|
||||
@@ -241,6 +243,7 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
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);
|
||||
|
||||
/* External getty */
|
||||
@@ -392,6 +395,11 @@ void tty_start(finit_tty_t *tty)
|
||||
return;
|
||||
}
|
||||
|
||||
if (tty->nologin) {
|
||||
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tty->cmd)
|
||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
||||
else
|
||||
|
||||
@@ -38,6 +38,7 @@ typedef struct {
|
||||
char *term;
|
||||
int noclear;
|
||||
int nowait;
|
||||
int nologin;
|
||||
int runlevels;
|
||||
|
||||
char *cmd; /* NULL when running built-in getty */
|
||||
|
||||
+98
@@ -23,8 +23,12 @@
|
||||
|
||||
#include "config.h"
|
||||
#include <ctype.h> /* isprint() */
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <lite/lite.h> /* strlcat() */
|
||||
#include "util.h"
|
||||
|
||||
int screen_rows = 24;
|
||||
@@ -42,12 +46,92 @@ char *progname(char *arg0)
|
||||
return prognm;
|
||||
}
|
||||
|
||||
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)))
|
||||
;
|
||||
}
|
||||
|
||||
/* Seconds since boot, from sysinfo() */
|
||||
long jiffies(void)
|
||||
{
|
||||
struct sysinfo si;
|
||||
|
||||
if (!sysinfo(&si))
|
||||
return si.uptime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *uptime(long secs, char *buf, size_t len)
|
||||
{
|
||||
long mins, hours, days, years;
|
||||
char y[20] = "", d[20] = "", h[20] = "", m[20] = "", s[20] = "";
|
||||
|
||||
if (!buf) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
years = secs / 31556926;
|
||||
secs = secs % 31556926;
|
||||
days = secs / 86400;
|
||||
secs = secs % 86400;
|
||||
hours = secs / 3600;
|
||||
secs = secs % 3600;
|
||||
mins = secs / 60;
|
||||
secs = secs % 60;
|
||||
|
||||
if (years)
|
||||
snprintf(y, sizeof(y), "%ld year", years);
|
||||
if (days)
|
||||
snprintf(d, sizeof(d), "%ld day", days);
|
||||
if (hours)
|
||||
snprintf(h, sizeof(h), "%ld hour", hours);
|
||||
if (mins)
|
||||
snprintf(m, sizeof(m), "%ld min", mins);
|
||||
if (secs)
|
||||
snprintf(s, sizeof(s), "%ld sec", secs);
|
||||
|
||||
snprintf(buf, len, "%s%s%s%s%s%s%s%s%s",
|
||||
y, years ? " " : "",
|
||||
d, days ? " " : "",
|
||||
h, hours ? " " : "",
|
||||
m, mins ? " " : "",
|
||||
s);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Allowed characters in job/id/name */
|
||||
static int isallowed(int ch)
|
||||
{
|
||||
@@ -73,6 +157,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))
|
||||
@@ -81,6 +169,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,10 +39,16 @@ extern char *prognm;
|
||||
#include <lite/conio.h>
|
||||
|
||||
char *progname (char *arg0);
|
||||
int strtobytes (char *arg);
|
||||
void do_sleep (unsigned int sec);
|
||||
|
||||
long jiffies (void);
|
||||
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_ */
|
||||
|
||||
+25
-6
@@ -31,15 +31,19 @@
|
||||
|
||||
int running = 1;
|
||||
int handover = 0;
|
||||
int shutdown = 0;
|
||||
|
||||
static void sighandler(int signo)
|
||||
{
|
||||
if (signo == SIGTERM)
|
||||
handover = 1;
|
||||
if (signo == SIGPWR)
|
||||
shutdown = 1;
|
||||
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static int init(char *progname, char *devnode, int timeout)
|
||||
static int init(char *progname, char *devnode)
|
||||
{
|
||||
int fd;
|
||||
|
||||
@@ -51,20 +55,28 @@ static int init(char *progname, char *devnode, int timeout)
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int loop(int fd, int period)
|
||||
static int loop(int fd, int timeout)
|
||||
{
|
||||
int dummy = 0;
|
||||
int period = timeout / 2;
|
||||
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
||||
|
||||
while (running) {
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
sleep(period);
|
||||
}
|
||||
|
||||
/* System is going down, prepare to reboot system on TERM */
|
||||
if (shutdown) {
|
||||
timeout /= 3;
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
||||
}
|
||||
|
||||
/* External watchdogd wants to take over ... */
|
||||
if (handover) {
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
return !write(fd, "V", 1);
|
||||
@@ -81,13 +93,20 @@ int watchdog(char *progname)
|
||||
if (pid == 0) {
|
||||
int fd, ret;
|
||||
|
||||
fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT);
|
||||
fd = init(progname, WDT_DEVNODE);
|
||||
if (fd == -1) {
|
||||
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
ret = loop(fd, WDT_TIMEOUT / 2);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user