Compare commits

..
351 Commits
Author SHA1 Message Date
Joachim Nilsson dd566acfd2 Update ChangeLog and bump version for v3.0-rc1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-14 00:04:00 +02:00
Joachim Nilsson 5c56f5c5a7 Fix make distcheck: Add missing header file to build deps.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 23:02:01 +02:00
Joachim Nilsson 7ab963a576 Remove eXecutable flag from source files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 23:00:06 +02:00
Joachim Nilsson 73d462c847 utmp_set(): Fix uninitialized return value
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 22:47:16 +02:00
Joachim Nilsson 56e94a4e4a Merge branch 'westermo-usr/wkz/timeout'
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 21:56:39 +02:00
Joachim Nilsson 51830e0f5b Merge branch 'usr/wkz/timeout' of https://github.com/westermo/finit into westermo-usr/wkz/timeout 2016-09-13 21:53:26 +02:00
Joachim Nilsson 85115eca62 Fix invalid ptr assignment for --enable-static builds
Also changes the default to set $HOME to / instead of nothing.  This can
of course be improved immensely.  Pull requests always welcome ;-)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 21:41:36 +02:00
Joachim Nilsson 659ff9962e Fallback (unused) PLUGIN_PATH for --enable-static builds
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 21:41:11 +02:00
Joachim Nilsson 9436c8b73c Minor whitespace cleanup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 21:25:40 +02:00
Joachim Nilsson 5a3397419f Remove support for service callback plugins
This patch removes the now heavily outdated (and non-working) support
for callback plugins.  I.e. plugins that attach themselves to services
listed in /etc/finit.conf and can be queried to determine if a service
should be started or stopped.

The Finit3 support for adding/removing services at runtime with plain
text *.conf files in /etc/finit.d and `initctl reload`, this old, and
rather cumbersome service callback feature, is not longer supported.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-13 21:20:06 +02:00
Tobias Waldekranz 10b72f1d7f service: kill stubborn services, wait before restart
Add support for registering callbacks to be called after a given
timeout.

Use this to implement forceful termination of services that refuse to
terminate upon receiving a SIGTERM. If the service PID is not collected
within 3 seconds, send a SIGKILL to it.

Also, rework restarting of crashing services. When a service crashes for
the first time, try to restart it immediately. If it crashes again, wait
a few seconds before trying again. Just as before, if it continues to
crash, block the service.
2016-09-13 20:26:28 +02:00
Joachim Nilsson 6b237b7b00 Refactor abstraction of svc blocking state
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-12 19:30:48 +02:00
Joachim Nilsson b4f385d2e0 Only log to stderr if opening /dev/kmsg failed for early logging
This patch prevents double critical/error messages from being printed on
the console when there is no syslog daemon running yet.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-08 17:11:52 +02:00
Joachim Nilsson 4b4572de4d Minor refactor of error/debug messages
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-08 17:11:27 +02:00
Joachim Nilsson 19b81cf434 Fix #40, #39: initctl plugin started too early, before /run FS existed
Delay setup of initctl plugin to after the base filesystems are up and
after the bootmisc plugin has run.

This patch factors out the I/O initialization of plugins so it does not
have to run when plugins are loaded.  Instead, plugins can now call this
API to register an I/O callback when they are ready.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-08 12:58:28 +02:00
Joachim Nilsson 3023433def Do not write DEAD_PROCESS records to UTMP, only WTMP
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-08 12:51:32 +02:00
Joachim Nilsson 49968c188b UTMP: Make sure to use the full width of all UTMP fields
In UTMP a terminating zero in string fields is optional, so we need to
take that into account when preparing entrys.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-08 12:51:20 +02:00
Joachim Nilsson 2499405b27 Misc. whitespace fixes, and comment
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-06 00:08:40 +02:00
Joachim Nilsson 9fc1f50678 bootmisc: The getgroup() API returns int not gid_t
Must use int to check for error code (-1) from getgroup() API.
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-05 00:55:21 +02:00
Joachim Nilsson b1d709ba22 reboot: CMD_SUSPEND not handled atm by Finit
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-05 00:54:52 +02:00
Joachim Nilsson 5ddc35d387 echo: Mark callback argument as unused
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-05 00:54:29 +02:00
Joachim Nilsson 50632f941b Fix missing initializer for array end marker
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-05 00:46:15 +02:00
Joachim Nilsson 7b215e4fd4 Update dependencies for libuEv and libite (-lite)
This patch makes Finit require libuEv v1.4.2, a minor bugfix update, and
libite (-lite) v1.8.2, which is quite a bit more than just a few bug
fixes.  The most relevant fixes are the removal of implicit logging for
touch(), makedir(), makefifo(), and erase() inline functions.  The
release also removed implicit logging to stderr on error, as well as a
a few build fixes for running on musl libc.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-05 00:24:55 +02:00
Joachim Nilsson bb7208c591 bootmisc: Check return value from chmod() API
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 23:23:37 +02:00
Joachim Nilsson bb0505f57c bootmisc: Check return value from getgroup() API
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 23:13:51 +02:00
Joachim Nilsson 4aa569de44 Fix descriptor leak in getty()
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 23:03:42 +02:00
Joachim Nilsson 512a07a25c Fix resource leak if tty_register() fails (minor)
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 22:55:21 +02:00
Joachim Nilsson 8cfd11c909 Use safe string copy using strlcpy()
Fixes issues with buffers not being NUL terminated properly.
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 22:52:34 +02:00
Joachim Nilsson c3adf3bf51 Bump version to v3.0-beta2 no more feature expected
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 20:48:49 +02:00
Joachim Nilsson 3ca28e765d Fix #38: Add --enable-emergency-shell fallback debug mode
This patch adds a very special debug mode to Finit.  With this feature
enabled the actual duties of PID 1 is forked off early to a child.  If
that child ever dies, usually due to a SIGSEGV, this wakes up the real
PID 1 which lauches a debug shell on the console.

Remember to disable 'quiet' and enable '--debug' on the kernel command
line to get all the debug logs -- which these days goes into the kernel
log in /dev/kmsg -- simply `cat /dev/kmsg` and use Ctrl-C to get the
shell prompt back.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 20:27:12 +02:00
Joachim Nilsson 79207dcb22 Support for starting finit-getty with a given user
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 20:02:59 +02:00
Joachim Nilsson 47f3e2ca04 Clarify what --disable-quiet actually does
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 18:40:58 +02:00
Joachim Nilsson 2e7bee9f84 Fix #36: Don't delete inetd on failed registration
This patch fixes to root cause to issue #36, reported by Markus Svilans.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 17:22:50 +02:00
Joachim Nilsson 2681e4c01a Add early logging support to /dev/kmsg
Very early, before syslogd has started, we have traditionally been
forced to use stdout/stderr for debug, warning and error messages.

With this patch we can now rely on /dev/kmsg to preserve all logs
from early Finit problems.  When syslogd/klogd starts up they read
/dev/kmsg and copy logs to /var/log/* files.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 17:19:06 +02:00
Joachim Nilsson 56a3f6882b Improve inetd service registration
- Refactor getent() for improved separation and readability
- Warn and exit if /etc/protocols or /etc/services is missing

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 17:17:22 +02:00
Joachim Nilsson 88d8ab67c9 Add _w() for developer warning logs
Adjust debug levels slightly and add _w() macro for LOG_WARNING,
useful when having /dev/kmsg as backend.  The latter outputs any
log <= LOG_ERR to console as well.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 17:15:41 +02:00
Joachim Nilsson 74eb2bf4d2 initctl: Add dependency on bootmisc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 17:13:38 +02:00
Joachim Nilsson b3fb9ab3ed Fix placement of newline for early debug
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-09-04 16:10:23 +02:00
Markus Svilans 308ae609c4 Fix #35: Build fails with --disable-inetd
This patch fixes a build problem when configured without inetd support.

Signed-off-by: Markus Svilans <msvilans@aeonyx.ca>
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-31 16:30:04 +02:00
Joachim Nilsson e3783e8bac README: Fix spelling error
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-26 21:36:04 +02:00
Joachim Nilsson 002b24fbc2 README: Improve ingress
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-26 21:34:36 +02:00
Joachim Nilsson 3f71ed352d initctl: Remove confusing/wrong debug message
The 's' runlevel after boot is the traditional single-user rescue mode.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-24 22:08:46 +02:00
Joachim Nilsson f7305c36b8 reboot: Clarify -f, --force option in usage text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-24 22:08:26 +02:00
Joachim Nilsson 8bcecd474b Refresh telinit client
- Add support for SysV init setenv to activate poweroff
- Update help text considerably to be more helpful

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-24 20:26:09 +02:00
Joachim Nilsson a9a7b08021 Disable login also in shutdown/reboot
Disable login in single-user mode as well as shutdown/reboot

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-24 19:27:23 +02:00
Joachim Nilsson 62cb683af0 initctl: Show WTMP log first and active UTMP last
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-23 16:35:44 +02:00
Joachim Nilsson 65e44dc88f UTMP: By convention, getty has user "LOGIN"
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-23 07:49:32 +02:00
Joachim Nilsson 54467d4467 configure: Minor whitespace changes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-22 12:39:51 +02:00
Joachim Nilsson cfa258e7f2 UTMP: Log to /var/log/wtmp as well as /run/utmp
The UTMP db tracks currently logged in users and WTMP is an audit trail
of all user logins and logouts, as well as runlevel changes and reboot.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-22 12:36:17 +02:00
Joachim Nilsson 574760df13 UTMP: Set LOGIN_PROCESS for built-in getty
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-22 12:36:03 +02:00
Joachim Nilsson c2ec25efd3 Use logit() for _d() and _e() messages as well
The new logit() function takes care to print to stderr if syslogd has
not yet started.  The _d(), _e(), and _pe() macros have an additional
requirement: to always print on stderr.

After the boot we also want to see what happned during, so this patch
simply adds syslog() support to _d(), _e(), and _pe().

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-18 01:48:41 +02:00
Joachim Nilsson d670e7d3e8 Clean up logging, log to stderr until syslogd has started
This patch removes internal logging macros and adds support for logging
to stderr until a syslogd is up and running, i.e. /dev/log appears.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-18 01:18:31 +02:00
Joachim Nilsson 77561b6071 Replace old ANSI helper macros with libite/conio.h
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-18 00:39:59 +02:00
Joachim Nilsson cec1c64755 Fix build on older (G)LIBC's w/o RB_SW_SUSPEND
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-18 00:11:11 +02:00
Joachim Nilsson 5bab329178 UTMP: Only account for getty processes
This patch removes accounting for all processes to instead implement the
traditional SysV getty accounting.  (Telnet/SSH et al maintain their own
accounding for UTMP.)

TODO: WTMP support

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-17 18:43:49 +02:00
Joachim Nilsson b6bf8bf518 Fix GRUB menu entry generation for Debian & Ubuntu
This patch fixes the /etc/grub.d/40_custom generation for the Debian
sample so that it properly extracts the UUID For /boot and /

Note: the maintenance free symlinks /vmlinuz and /initrd.img may not
work properly with /boot on a separate partition or disk.  If only
the symlinks where in /boot as well ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-15 02:26:01 +02:00
Joachim Nilsson 0a21ff5236 Update Alpine configure example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-15 02:25:13 +02:00
Joachim Nilsson 3853061f09 Remove --enable-embedded configure option
Instead of ugly #ifdefs we now probe for mdev or udevd at runtime.  Take
care to check for udev-finalize and location of udevd, it differs a lot
between distros.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-15 00:25:07 +02:00
Joachim Nilsson 078982d3ec Initial support for mdadm RAID arrays
This patch adds initial support for handling MD RAID arrays.  Adding
a pass at the very end of the shutdown sequence to signal any external
RAID controller to clear its state before we proceed to halt/reboot.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 21:26:18 +02:00
Joachim Nilsson f4327910d9 Only unmount non-protected filesystems at shutdown
This patch adds support for a more fine grained approach to what
filesystems are unmounted at shutdown/reboot.  This so we later
can add support for `mdadm --wait-clean --scan`, which relies on
/proc, /sys, and /dev being there before we call reboot().

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 21:09:15 +02:00
Joachim Nilsson b1fb536468 reboot: Remove sync() is up to user to do that
Also, this code will be refactored soon to be a part of the finit
binary, like telinit, and then we will be called after unmount to
do a forced halt/reboot/poweroff.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 13:29:58 +02:00
Joachim Nilsson 95cb94cf49 Remember to shift use state machine in sig.c:do_shutdown()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 13:26:27 +02:00
Joachim Nilsson e1c0e7fe58 Add install/uninstall for reboot symlinks
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 13:20:48 +02:00
Joachim Nilsson 9783e120af Fix install/uninstall of Finit symlinks
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 13:20:27 +02:00
Joachim Nilsson 3ad149fb28 Workaround for uninstalling .so plugins
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 13:19:35 +02:00
Joachim Nilsson e6430aabc2 Update and remove services script from distribution
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 13:19:20 +02:00
Joachim Nilsson 2743a49d91 reboot: Add halt/poweroff/suspend and force cmd
This patch is a complete rewrite of the Finit reboot command.  Not only
does it bring it up to speed with the new signal model, but it also adds
new features like poweroff, halt, and suspend -- including a forced mode
that goes for reboot(2) immediately.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-14 12:27:52 +02:00
Joachim Nilsson 6f99c1db05 initctl: Add missing signal.h include for older systems, e.g. Travis CI
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-11 00:03:20 +02:00
Joachim Nilsson 0eafe8bcd9 initctl: Show error message when trying to signal PID 1 as non-root
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-10 21:39:09 +02:00
Joachim Nilsson b01fae03ec Copy all chars of UTMP fields: id, line, and user
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-09 20:35:44 +02:00
Joachim Nilsson dbbe6b31b8 UTMP: Fix runlevel regression after refactoring
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-09 20:35:03 +02:00
Joachim Nilsson 2718c709aa UTMP: Add support for INIT_PROCESS & DEAD_PROCESS
Services started by Finit are now properly accounted for in UTMP.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-09 00:58:00 +02:00
Joachim Nilsson 8c54fde36c Fix D-Bus plugin, remove old #ifdefs
Still TODO, replace `run_interactive()` start of dbus-daemon with a
proper service.  Possibly only service_register() is needed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-09 00:12:47 +02:00
Joachim Nilsson 0d1bc167ff initctl: Fix display of UTMP records, add IP addr
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-09 00:12:47 +02:00
Joachim Nilsson f8def336d4 Fix ordering of UTMP events: reboot, then runlevel
This patch fixes the ordering of UTMP events. The first event that is
expected in /var/run/utmp is reboot, next is the runlevel change to
the configured runlevel, e.g. from S --> 2.

Let the bootmisc plugin handle this, it knows when /var/run/utmp has
been created and is writable.  It is not the eact boot time, but close
enough for most usees.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-09 00:12:47 +02:00
Joachim Nilsson addc21bdf6 UTMP: Set BOOT_TIME record and add shutdown
This patch adds support for initializing /var/run/utmp with the initial
reboot as well as a shutdown record in the UTMP db.

Also, the run level change now properly records ~ and ~~ as line and id,
respectively, and the kernel release and time of each UTMP record is now
set properly.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-08 19:06:35 +02:00
Joachim Nilsson 27899feea0 bootmisc: Add /var/log/btmp 0600 support
The file /var/log/btmp holds bad login attempts and must be created with
0600 perms.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-08 00:05:34 +02:00
Joachim Nilsson a016359891 Factor out runlevel handling to new UTMP API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-07 22:36:31 +02:00
Joachim Nilsson 7cdd42195b initctl: Break out UTMP logic to separate file
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-07 22:22:42 +02:00
Joachim Nilsson 07f06c8b95 inictl: Enable UTMP dump command
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-06 18:51:25 +02:00
Joachim Nilsson 2067fd597f initctl: Raw UTMP dump tool
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-06 18:49:22 +02:00
Joachim Nilsson f0318b0df0 Rephrase
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-05 17:46:44 +02:00
Joachim Nilsson 508c7b225a Prune completed TODO items
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-05 17:45:58 +02:00
Joachim Nilsson 8a79b05a97 Add missing bracket
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-05 17:45:25 +02:00
Joachim Nilsson 7b07d5559c Service status 'crashing' --> 'crashed'
Minor coding style cleanup, code simplification, and change service
'crashing' status text to 'crashed' to fit column width.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-05 17:43:45 +02:00
Joachim Nilsson 23cdd6e438 Update Debian HowTo with new build.sh script
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-05 01:16:35 +02:00
Joachim Nilsson 576cb1075c Boiler plate README for contrib/ section
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-05 01:06:23 +02:00
Joachim Nilsson fc74fb110d README: Further simplication of example in ingress
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-05 01:06:20 +02:00
Joachim Nilsson baa914e995 initctl: add reboot, halt, and poweroff commands
Simple wrappers for the signals Finit responds to.  Will need complete
rewrite when support for delayed reboot/halt is added.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-04 22:45:05 +02:00
Joachim Nilsson 6490735ee9 Remove flux condition emulation, confusing to user
The condition flux state does not work like that, so keeping
this sub-command is only confusing to the user.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-04 22:40:20 +02:00
Joachim Nilsson 13159ea99a Mention support for Debian and Alpine early!
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-04 00:38:45 +02:00
Joachim Nilsson b9fa0f85a2 Update inline documentation for Debian finit.conf
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-03 11:12:56 +02:00
Joachim Nilsson dbbe3cecb3 Debian helper build & install scripts
These scripts help a user to configure, build, and install Finit
correctly on a Debian 8 (Jessie) server install.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-03 10:58:45 +02:00
Joachim Nilsson 1a8f1d9604 getfsent(): Read only six fields per line
The fs.fs_type is only for *BSD, we hard-code it to "rw"
in the meantime.  Should be safe in most cases.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-02 02:01:51 +02:00
Joachim Nilsson d76ecad517 fsck: Prevent debug during interactive fsck
The debug mode cannot be used in run_interactive() that early, so we
must disable it during the complete fsck run.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-02 02:00:01 +02:00
Joachim Nilsson f81750e0b1 fsck: Skip progress, does not work the same with BusyBox fsck
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-02 01:59:14 +02:00
Joachim Nilsson 7fc7faf59a fsck: Prevent double-checking filesystems
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-02 01:58:41 +02:00
Joachim Nilsson c38b452ac4 Add set/get/endfsent() support for musl libc
This patch adds support for the following /etc/fstab API's on systems
that do not have them, e.g. musl libc on Alpine Linux.

- setfsent()
- getfsent(), and
- endfsent()

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-02 00:50:23 +02:00
Joachim Nilsson 7eac88b639 Run fsck earlier and print only first 13 chars
Run fsck much earlier, as soon as we can in fact.

The first 13 characters of the UUID, label or device name should
be sufficient to identify the device, without violating max width
of 78 chars.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-01 23:51:11 +02:00
Joachim Nilsson f374f443f4 Allow fsck on UUID= and LABEL= devices
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-01 23:38:55 +02:00
Joachim Nilsson d2a5e65862 Support for checking file systems from /etc/fstab
This patch adds support for running fsck on all file systems listed in
/etc/fstab with a passno > 0.  The fsck runs sequentially, starting with
the root fs (passno 1).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-01 18:08:54 +02:00
Joachim Nilsson 501deae4ed Update doc, support sysctl from all the locations!
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-31 10:21:15 +02:00
Joachim Nilsson 2c29893558 Prune old (and now irrelevant) TODO items
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-31 10:16:52 +02:00
Joachim Nilsson a1a7f69349 Update info on getty, no /bin/login included
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 16:06:35 +02:00
Joachim Nilsson 8d9fbea551 Micro adjustment
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 16:03:32 +02:00
Joachim Nilsson 1d89e1a706 Advertise for new logo ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 16:02:51 +02:00
Joachim Nilsson 596b7cc413 README: Mention built-in getty
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 16:00:31 +02:00
Joachim Nilsson fbed5cd8cb TODO: Timed shutdown/reboot incl. cancellation
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 15:55:59 +02:00
Joachim Nilsson 0cb1ce2483 Disable clear screen in getty
Disabled until we decide on config to disable/enable.  It is quite
annoying to not be able to see the boot messages with it on ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 15:54:37 +02:00
Joachim Nilsson e23b62ffb7 Plugin loader fixes for dep handling and unload
- Plugins without declared name registered with full path + ext
  => Strip path and extension
- Plugins registered with __NAME__ => strip extension
- Do *not* call dlclose() from destructor, must be called by Finit

Also, some minor cleanup and code simplifcation.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 15:54:37 +02:00
Joachim Nilsson 94d41e38f8 Close inotify watcher when TTY plugin is unloaded
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 15:54:37 +02:00
Joachim Nilsson 42800a1cc8 ALSA plugin: only run if /usr/sbin/alsactl exists
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 15:54:37 +02:00
Joachim Nilsson 4cf60b7491 initctl: Add SysV compat support for setenv
- Fix compat INIT_CMD_ defines so Finit clients can talk to SysV init
- Add setenv support, only for INIT_HALT={POWERDOWN,HALT} to runlevel 6

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 15:54:37 +02:00
Joachim Nilsson 72a9832662 Remove developer debug of reboot/umount sequence
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-30 15:54:37 +02:00
Joachim Nilsson eeb59eb22b Adopt BusyBox init signal handling for halt/reboot/poweroff
This patch adopts the BusyBox init signals for handling halt, reboot,
and poweroff.  The SIGINT and SIGPWR signals still behave the same as
before, even though they likely will become configurable in a future
release.

Note: the disabling of Ctrl-Alt-Delete (SIGINT) is also removed!

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-29 16:47:38 +02:00
Joachim Nilsson 0df0f019e3 Alpine Linux finit.conf updates
First lessons from Debian, more to come ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-29 01:08:48 +02:00
Joachim Nilsson bc7f6f1bc1 Debian HowTo: Minor fixup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-29 00:22:32 +02:00
Joachim Nilsson 73b807a007 Debian HowTo: Minor reformatting
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-29 00:20:46 +02:00
Joachim Nilsson 62113a0057 New mini HowTo: Finit on Debian Jessie
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-29 00:13:03 +02:00
Joachim Nilsson 968f1bf991 The science of unmounting file systems on Debian
That's maybe a bit unfair.  This patch implements a brief version of
what is really needed to safely unmount all file systems on a modern
Linux distribution.  Yes, it's this insane.  (Who am I kidding, it's
worse!  Just have a look at the ancient scrolls hidden in your /etc!)

Here is where we use the new plugin_exit() and api_exit() API's from an
earlier patch.  Making sure to exit and close any kernel descriptors.
Then follows a bizarre spell cast on leaking descriptors, rebooting in
a child, remounting / as read-only, and a whole slew of other R rated
stuff to prevent mount/umount from returning 32 ...

Stay safe people, use protection always!

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:39 +02:00
Joachim Nilsson 57514130fc Starting service as @user chdir() to their $HOME
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:39 +02:00
Joachim Nilsson 0710fdd2a3 Plugin and API init/exit for conformance
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:39 +02:00
Joachim Nilsson 08e46782e5 Print banner earlier
Thanks to udev we need to do a lot more stuff even earlier these days.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:39 +02:00
Joachim Nilsson b96d8d3055 Handle tabs in finit.conf better
Should probably be extended to handle any non-space blank character.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:38 +02:00
Joachim Nilsson 4ede2a93b2 Change initctl FIFO from /dev/ to /var/run/
FIFOs, PID files, and other non-devices belong in /var/run (or /run
these days), not /dev -- as per FHS.  Compat with Debian SysV init.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:38 +02:00
Joachim Nilsson dd2eb9ce04 Initial contrib/debian support for Jessie
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:38 +02:00
Joachim Nilsson d15633781b TTY: Major overhaul, move to built-in getty
The TTY handling in Finit has always been a bit of a patch work.  This
commit is an attempt at a first cleanup and simplification.

Included is a brand new built-in getty which should work fine on both
virtual and serial consoles.  It is however completely untested on the
latter serial consoles.

Still TODO: add support for /etc/securetty, add support for actually
setting TERM when calling /bin/login.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-28 23:48:38 +02:00
Joachim Nilsson 789354ab40 Improve udev support on non-embedded systems
Embedded Linux systems usually employ BusyBox mdev.  This patch assumes
that all non-embedded systems employ udev.  Adding support for cleaning
up the udev db after the initramfs, or mounting /dev if that not done.
It also runs the udev-finish script at the end.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-27 19:28:42 +02:00
Joachim Nilsson 8002704a5f bootmisc: Create /var/run/network/ for ifupdown
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-27 19:25:50 +02:00
Joachim Nilsson 82853bf0ea Add support for ifupdown 'allow-hotplug' keyword
Look for 'auto' *and* 'allow-hotplug' in /etc/network/interfaces when
bringing up Debian/BusyBox interfaces at boot.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-27 19:25:19 +02:00
Joachim Nilsson bfe9e011bd Remove fallback to /bin/sh if no TTY is defined
If the user specifies no 'tty /dev/ttyN' in finit.conf this code
took upon itself to add a backdoor to the system.  Let's remove
it and rely on the operator deciding what is best practise.

With this patch no getty, or fallback shell, will be started if
no tty line in finit.conf is available.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-27 19:14:36 +02:00
Joachim Nilsson 9416e14074 Minor cleanup and default signals also for getty
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-26 21:13:19 +02:00
Joachim Nilsson 46a58f058a Simplify, simplify, simplify ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-26 01:29:31 +02:00
Joachim Nilsson 72a484b639 Break out building and bootstrap to separate files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-23 00:25:14 +02:00
Joachim Nilsson 82e483d6c7 Start splitting up the README into separate files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 23:31:57 +02:00
Joachim Nilsson dafa50ee42 Add links to dependencies in build section
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 23:07:25 +02:00
Joachim Nilsson 2187e05e06 Update build instructions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 22:59:08 +02:00
Joachim Nilsson a4fd058607 Update example finit.conf
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 22:58:43 +02:00
Joachim Nilsson c49eb5e10d Replace classic Finit rally car with retro ASCII logo
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 22:57:55 +02:00
Joachim Nilsson 01d1dabaae Remove useless hostname debug
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 22:44:39 +02:00
Joachim Nilsson a3b0022d66 Minor cleanup and additions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 20:00:13 +02:00
Joachim Nilsson e4d882e99e Fix ToC links and add finit.conf example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 19:29:23 +02:00
Joachim Nilsson 5ef426d424 Link to files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-22 19:28:14 +02:00
Joachim Nilsson 5ee1b7c322 Name waiting-for-enter to getty process
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 20:43:23 +02:00
Joachim Nilsson 0d665f3773 Correct adjustment of initctl show columns
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 16:48:29 +02:00
Joachim Nilsson 38619966ee Fix interface bring up message NULL ptr deref
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 16:35:57 +02:00
Joachim Nilsson 6380f17d1f Prototype support for /etc/netork/interfaces
This patch adds prototype support for the Debian/Ubuntu/Busybox network
configuration file /etc/netork/interfaces.  If this file exists Finit
will call `ifup` for each (non-commented-out) interface marked `auto`.

With this implementation we block boot until all ifup commands have
returned, which is less than ideal.  A better approach would be to ifup
only loopback and create deferred tasklets to be run in parallel with
the rest of the boot.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 16:11:50 +02:00
Joachim Nilsson 42181a3031 Add OpenRC-like support for sysctl.d/*.conf
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 15:49:00 +02:00
Joachim Nilsson 77b32ebff0 Update Alpine Linux example config
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 15:37:15 +02:00
Joachim Nilsson 97de3ad0bb Slight adjustment, plus 1, to allow 79 char width
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 15:15:26 +02:00
Joachim Nilsson 44f8a2666f tty: Always detach from initial TTY and open actual /dev/ttyN
This patch fixes a long standing problem with TTY's.  The original code
tried to workaround a limitation in the console of some embedded systems
that was detrimental on actual TTYs.

As an added benefit, the console specific "Press Enter" prompt for the
console port is now available on all TTYs -- meaning no getty needs to
be started and kept needlessly in the background until it is needed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 14:57:58 +02:00
Joachim Nilsson 4cf5b055cf Adjust max width to 79 chars in text output
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 14:28:03 +02:00
Joachim Nilsson 744bb9dd14 Workaround for loopback interface
We never get any NEWLINK message for lo since it as an interface exists
very early.  So we add this workaround when lo comes up so services can
depend on exists for lo as well as for other interfaces.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 14:04:37 +02:00
Joachim Nilsson 1edfecdaab Use -v to also dump asserted condition flags in cond show
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 13:53:51 +02:00
Joachim Nilsson bc84d66018 Allow conditions for tasklets and runlets as well
The introduction of conditions to Finit only focused on
services (monitored daemons) and not parallell one-shot
tasks or blocking one-shot runs.

This patch enables condition support for 'task' and 'run'
stanzas in both finit.conf and finit.d

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 11:47:21 +02:00
Joachim Nilsson 2216923343 Minor whitespace fixes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 11:43:09 +02:00
Joachim Nilsson ac834cfa1c I am the reaper of lost children ...
... but let's keep it quiet, OK?

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 01:16:51 +02:00
Joachim Nilsson f191266a33 Add --with-random-seed to enable urandom plugin
By default the urandom plugin will use /var/lib/misc/random-seed across
reboots to seed /dev/urandom at boot.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 00:41:23 +02:00
Joachim Nilsson 796275af62 HowTo: Finit on Alpine Linux 3.4
Mini HowTo with config files :)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 00:29:49 +02:00
Joachim Nilsson a7e52914d2 Set a default description for all services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-21 00:08:56 +02:00
Joachim Nilsson fcfbd57ca8 With 'log' keyword, always log process name
Do not tag log messages with service description, but use process name.
Possibly add :ID later to denote instance if multiple instances exist.

Also, add priority daemon.info log level.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 19:59:27 +02:00
Joachim Nilsson 5b81d14e51 x11-common: If restorecon exists, run it
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 19:58:42 +02:00
Joachim Nilsson 329af172de Fix default GREET
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 19:58:21 +02:00
Joachim Nilsson 33ce1592c1 bootmisc: Create /var/log/wtmp and lastlog too
Set correct permission bits, the utmp group must have write permission.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 19:57:39 +02:00
Joachim Nilsson 7a954e3743 Run user network start script cleanly
If user has no start script fall back to set up at least loopback.  This
fixes (at least) a problem on Alpine Linux where `ifup -a` returns error
if `lo` is already up.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 18:27:11 +02:00
Joachim Nilsson 48c2937f4f Simplify bootstrap message for rc.local start
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 18:24:30 +02:00
Joachim Nilsson 24961eb3ba Minor grammar fix in bootstrap message
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 17:30:36 +02:00
Joachim Nilsson aced97c2e6 Handle systems with new /run tmpfs
Modern systems today use a RAM disk for /run and /run/lock, with
symlinks to the FHS locations in /var/run and /var/lock.  See the
following link for details http://lwn.net/Articles/436012/

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-20 17:30:29 +02:00
Joachim Nilsson a67350ebf6 Little bit more careful with init/telinit symlinks
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-19 17:00:40 +02:00
Joachim Nilsson c47a0cd812 Conditionalize 'rlimit rttime', not available yet on musl libc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-19 15:56:49 +02:00
Joachim Nilsson 67c0d43912 Merge branch 'refactor-readme'
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 20:26:11 +02:00
Joachim Nilsson 7b00969f6c Merge remote-tracking branch 'origin/inetd-plugins'
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 20:25:36 +02:00
Joachim Nilsson 875080d1e1 Minor update
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 17:30:56 +02:00
Joachim Nilsson 1121965167 Do not build inetd plugins if inetd is disabled
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 17:30:29 +02:00
Joachim Nilsson 45bdadebb6 Minor cleanup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 17:30:14 +02:00
Joachim Nilsson 7dfdc2395b Add protection against UDP inetd looping attacks
This patch adds protection against a common inetd attack vector wherein
the reply port to UDP inetd services is forged to an internal inetd
service port.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 17:27:37 +02:00
Joachim Nilsson 5723b2e75b Refactor RFC 868, time protocol plugin
- Always do a dummy read to get peer address.
- Refactor to new format used by other built-ins

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 12:28:40 +02:00
Joachim Nilsson d4fdf50636 Add optional support for RFC867, Daytime Protocol
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 12:06:59 +02:00
Joachim Nilsson 4f7366640b Add optional support for RFC864, Character Generator Protocol
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 11:48:04 +02:00
Joachim Nilsson 90497af7bb Add optional support for RFC863, Discard Protocol
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-18 09:55:19 +02:00
Joachim Nilsson cd0983a7f6 First draft of README refactor and split
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 15:52:19 +02:00
Joachim Nilsson c7573fedad Replace 'initctl emit <EV>' with 'initctl cond <cmd> <COND>'
This patch replaces the old 'initctl emit <EV>' idea with a more
full blown condition generator:

    initctl cond <set|clear|flux> <COND>

This aligns better with terminology used elsewhere in the new Finit
service state machine and dependency handling already documented.

The 'flux' sub-command is mostly intended for simulation/test of
Finit itself and should perhaps not be documented.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 15:51:57 +02:00
Joachim Nilsson 2b74fb0e93 Minor cleanup and corrections for new terminology
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 15:51:57 +02:00
Joachim Nilsson eb57457522 Document /etc/rc.local support
- Document /etc/rc.local support
- Update documentation around runparts(8)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 13:53:10 +02:00
Joachim Nilsson f0cda83d24 Clean up AC_ARG_WITH and use config.h also for default paths
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 13:17:09 +02:00
Joachim Nilsson fbacae7071 Minor cleanup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 13:17:09 +02:00
Joachim Nilsson 48e2cbd183 Add convenient SysV init /etc/rc.local compat
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 13:17:09 +02:00
Joachim Nilsson 8f9ea0ae57 Fix stupid spelling error
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-15 08:41:06 +02:00
Joachim Nilsson 1c0776b818 Tie in service and condition documents with README
This is a first effort at cleaning up and consolidating all information
about the new service state machine and how conditions control this
state machine.

Also, clarified and added sections for all documents regarding how
services and conditions relate.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-14 16:08:44 +02:00
Joachim Nilsson 6ff9cb2ff3 Rename condition net/gw --> net/route/default in netlink plugin
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-14 15:41:47 +02:00
Joachim Nilsson fbef03a4c6 Update Inetd features and service dependencies
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-14 10:03:19 +02:00
Joachim Nilsson 66f22a0a5f Add syntax highlighting helpers for Emacs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-14 09:52:22 +02:00
Joachim Nilsson 171ae7dc85 Add support for disabling the Netlink plugin
Some vendors provide their own Netlink plugin to Finit.  This patch
makes it possible to disable the default netlink plugin from being
built.  However, even before this patch it was possible to overload
the plugin.  This just discards it from being built.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-13 13:23:07 +02:00
Joachim Nilsson ebfa8f061c Fix default handling in M4 macro AC_PLUGIN()
The AC_PLUGIN() could not properly handle if a plugin is to be enabled
by default.  This patch adds support for outputting the correct help
text.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-13 13:20:56 +02:00
Joachim Nilsson 2baa4f8ea9 Followup to 38203ab, remove queue.h from Makefile as well
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-13 13:07:28 +02:00
Joachim Nilsson 38203ab9e8 Migrate to libite version (newer) of BSD queue.h
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-12 15:34:52 +02:00
Joachim Nilsson 88a4ebf9e7 Use <> instead of "" for external header includes
Libraries and system headers should be included with <> instead of "".
This makes a great difference for the automatic dependency tracking.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-12 15:32:36 +02:00
Joachim Nilsson 51f61939da Do not build insecure inetd time (rdate) plugin by default
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-12 13:36:47 +02:00
Joachim Nilsson 7262a25c85 Merge pull request #34 from troglobit/echo
Basic inetd echo server support and bugfix

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-12 12:38:14 +02:00
Joachim Nilsson 8b9309ee41 Fix regression in starting internal inetd services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-12 12:24:28 +02:00
Joachim Nilsson affc6a97b9 Add support for inetd RFC862 echo server
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-12 09:58:25 +02:00
Joachim Nilsson ec041b3c02 Update configure and build section
Prune standard configure flags and focus on the most important, whatever
they may be.  Mention --disable-inetd and describe how --enable-static
works.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-09 12:38:45 +02:00
Joachim Nilsson 267eeb9692 Fix --enable-static build, only C library shared
This patch fixes regressions in --enable-static introduced with the
conversion to the GNU configure and build system.

The default link flag to libtool is set to -static-libtool-libs, meaning
not a fully static build -- the C library will still be used as an .so
which is usually what people want -- whereas libite (-lite) and libuEv
are linked statically.

$ ldd /sbin/finit /sbin/initctl
/sbin/finit:
	linux-vdso.so.1 =>  (0x00007ffffde62000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6979ad8000)
	/lib64/ld-linux-x86-64.so.2 (0x000055d893e96000)
/sbin/initctl:
	linux-vdso.so.1 =>  (0x00007ffed0dd6000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007feb3a29c000)
	/lib64/ld-linux-x86-64.so.2 (0x00005556ebd2b000)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-09 12:14:21 +02:00
Joachim Nilsson 58e00fe52e Fix #17: Mention kernel CONFIG_UEVENT_HELPER_PATH and /etc/fstab
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-09 11:15:33 +02:00
Joachim Nilsson a40301ef86 Bring Building section more up to date
Now that we have a proper GNU configure script there is no such thing as
lightweight, and the respected environment variables are listed at the
end of `./configure --help`

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-09 11:14:03 +02:00
Joachim Nilsson 3a86db7d9f Fix typo in --enable-rw-rootfs configure option
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-09 10:30:31 +02:00
Joachim Nilsson 927956bf78 Set correct initial bootstrap runlevel 'S'
This patch corrects a long standing issue with Finit.  System initial
bootstrap runlevel must be set to 'S' (0), not the configured fallback
runlevel 'RUNLEVEL', which likely is '2'.

Also, minor cleanup, reduce code duplication.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-09 10:24:15 +02:00
Joachim Nilsson 05938a88ce Fix #33: Set up directory required by D-Bus plugin
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-09 10:04:34 +02:00
Joachim Nilsson 4a8a92af2e Minor audit of new top-level state machine
Also, give Jonas copyright for his contribution to Finit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-07 18:32:31 +02:00
Joachim Nilsson 79b95daf3a The pidfile plugin requires /var/run, so depend on bootmisc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-06 19:44:21 +02:00
Joachim Nilsson ea21bfc4c9 Relocate global sm variable to reduce include file deps
The new state machine handling is not necessary to export to external
plugins, so this patch reduces the exposure of the global variable sm

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 23:10:47 +02:00
Joachim Nilsson e9da876a10 Actually fix fallback shell memory leak
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 22:08:12 +02:00
Joachim Nilsson ef7f0b100b Check return value from fcntl() on inetd server socket
Setting O_NONBLOCK on an inetd server socket may fail, we
must check for this or the event loop may stall and block
Finit completely.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 21:53:42 +02:00
Joachim Nilsson 33ccff7733 Audit ad2bafb: cleanup of stale pid files
Make sure to check return value of remove(), it may actually signal
something useful.  Log useful errors.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 21:46:25 +02:00
Joachim Nilsson 00e5a8f5d9 Audit ec25051: service logging support
- Must check return value from open(), found by Coverity Scan
- Minor refactor (and cleanup) to reduce code complexity

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 21:38:46 +02:00
Joachim Nilsson f74a73be20 Audit commit 1177c73: musl libc compat changes
Must initialize init_request buffer to zero before communicating with
Finit.  Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 21:21:24 +02:00
Joachim Nilsson 34d8848b5c Fix leak if fallback shell cannot be registered
If we cannot register a fallback TTY we leak a few bytes RAM.
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 21:15:16 +02:00
Joachim Nilsson 662f364d09 Check return value from uev_io_init() at runtime
Possible resource leak due to unchecked return value from uev_io_init()
found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 21:03:36 +02:00
Joachim Nilsson e53acd1cdd Fix minor type warning in debug message
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 20:14:43 +02:00
Joachim Nilsson d96d1a3023 Audit rlimit support, handle possible NULL pointer
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 20:10:37 +02:00
Joachim Nilsson f249210c04 Update Travis-CI build script for Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 19:52:33 +02:00
Joachim Nilsson 969ecffc05 Merge branch 'westermo-finit-sm'
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 19:49:33 +02:00
Joachim Nilsson cc15d3f77b Merge branch 'finit-sm' of https://github.com/westermo/finit into westermo-finit-sm
Hand merged conflicts in the following files:
	Makefile
	finit.h
	service.c

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-04 19:48:08 +02:00
Jonas Johansson ad2bafbfe8 services: remove .pid file for (careless) died processes
Signed-off-by: Jonas Johansson <jonas.johansson@westermo.se>
2016-05-20 09:08:38 +02:00
Jonas Johansson c5fad8e659 services: do not start services before all stopped processes has been halted
Signed-off-by: Jonas Johansson <jonas.johansson@westermo.se>
2016-05-19 14:46:59 +02:00
Jonas Johansson c1f725340d plugin/pidfile: also notify on modified pidfiles
Signed-off-by: Jonas Johansson <jonas.johansson@westermo.se>
2016-05-19 13:39:56 +02:00
Jonas Johansson 29315090f9 sm: handle reload event
Signed-off-by: Jonas Johansson <jonas.johansson@westermo.se>
2016-05-18 10:40:01 +02:00
Jonas Johansson 60df2043ac sm: handle runlevel change
Signed-off-by: Jonas Johansson <jonas.johansson@westermo.se>
2016-05-18 10:18:40 +02:00
Joachim Nilsson d48da81026 Merge pull request #31 from thezbyg/master
Fix SIGSEGV due to writes to const char array.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-05-17 20:28:35 +02:00
Albertas Vyšniauskas 5b86c76694 Fix SIGSEGV due to writes to const char array.
If no console is configured, then the following code gets executed:
int conf_parse_config(void)
{
  ...
  char *fallback = FALLBACK_SHELL;
  ...
  tty_register(fallback);
  ...
}
int tty_register(char *line)
{
  ...
  cmd = strtok(line, " ");
  ...
}
FALLBACK_SHELL is constant character array and strtok tries to modify
the first argument, which causes a crash.
2016-05-17 20:38:42 +03:00
Jonas Johansson 364f3bbafb finit: add finit state machine
Signed-off-by: Jonas Johansson <jonas.johansson@westermo.se>
2016-05-16 15:17:02 +02:00
Joachim Nilsson 9a539c636a Merge pull request #30 from westermo/wmo/2.4
Resource limit manipulation (setrlimit)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-05-01 11:14:08 +02:00
Joachim Nilsson ca830b9a09 Compat install-dev hook
Finit <3 did not use GNU configure and build system.  This patch adds an
install-dev rule that is used by some to install required headers for
external plugin developers.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-30 16:53:43 +02:00
Joachim Nilsson f2b0951d78 Install headers for external plugin devs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-30 16:39:51 +02:00
Tobias Waldekranz 6d84fd010e Resource limit manipulation (setrlimit)
Add support for changing the initial hard and soft resource limits for
finit and any processes it launches.

See /etc/finit.conf section in README.md for more information.
2016-04-28 09:36:22 +02:00
Joachim Nilsson 110ee220e7 Merge pull request #29 from westermo/wmo/2.4
Allow debugging of inetd service's arguments
2016-04-26 11:04:47 +02:00
Joachim Nilsson f29dff691f Allow debugging of inetd service's arguments
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-26 11:02:32 +02:00
Joachim Nilsson a1e6febc4f Merge pull request #28 from westermo/wmo/2.4
Westermo fixes for services changing type at runtime

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-26 09:38:41 +02:00
Joachim Nilsson df1fbbc996 Handle type change from inetd to service
When a service changes type from inetd to a regular daemon we must make
sure to first deregister the currently running inetd service properly.

Example: a system boots up with httpd.conf as an inetd service, so Finit
   opens a socket 0.0.0.0:80 pending connections to start `httpd -i`.
   After a while a user changes httpd.conf to be a regular service and
   calls `initctl reload`.  Without this patch the socket will remain
   open and prevent httpd from opening and binding to the same port.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-26 09:30:00 +02:00
Joachim Nilsson 448a9e1198 Stop inetd watcher and close socket when inetd service is removed
This patch fixes a problem with lingering inet sockets when inetd
services are removed.  When removing an inetd service we must stop
the watcher and close the socket.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-26 09:27:30 +02:00
Joachim Nilsson 80624d3ff8 Revert "Do not just mark deleted SVC free, clear memory"
We cannot clear the svc_t memory because other applications may have
connected to the same shared memory.  An application may be inspecting
the current list of services (initctl) and pulling the rug from under
them could cause some really unpredicatble results.

This reverts commit 06d1e90bac.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-26 09:23:59 +02:00
Joachim Nilsson 4b0f78c1b5 Merge pull request #27 from westermo/wmo/2.4
Westermo fixes for UDP inetd services and more

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 12:06:43 +02:00
Joachim Nilsson 5ea3cc0090 Reduce max backlog in listen(): 20 --> 10
Both the original MIT Athena inetd and the GNU inetutils inetd only keep
a backlog of 10 connections in listen() for TCP sockets.  This patch
adjusts the Finit hard coded default accordingly.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 12:01:10 +02:00
Joachim Nilsson 7f5f8ba09d Minor grammar: "Connection" --> "connection"
When starting an inetd service in verbose mode we print a progress
message to the console appended with "Connection".  This patch simply
changes that to append with "connection" in lower case.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 12:01:10 +02:00
Joachim Nilsson efd265c9c8 Fix "broken pipe" problem for UDP inetd services
This patch fixes several issues related to the problem of launching
UDP inetd services:

- Make sure to disable O_NONBLOCK on the socket before passing it to
  the inetd service, that's what is expected.  This goes for both UDP
  and TCP (accepted) connections -- there is no guarantee even for TCP
  sockets that they do *not* inherit the parent socket's flags.
- Mark the UDP inetd parent's SVC as busy and stop its watcher before
  starting the child task to service the connection.  The watcher must
  be stopped or multiple child tasks may be spawned!
- Only copy the most relevant parts to the UDP task.  In particular
  must *not* copy the libuEv watcher data to the task!
- With the child task done and the parent inetd service unblocked we
  must restore O_NONBLOCK to the socket before restarting the watcher

When a UDP inetd service connection is handled we create a child task
to service the request, meanwhile we block the parent service.  There
was a problem with blocking the parent since we called inetd_stop(),
which both did a shutdown() and close() of the socket ... this patch
introduces separate handling of blocked (busy) inetd services in the
inetd_stop() function and also make sure to handle restarting the
parent watcher in inetd_start() when the child task is done.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 12:01:10 +02:00
Joachim Nilsson 7e91cb4afc Minor log message improvements
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 12:01:10 +02:00
Joachim Nilsson 9a03adce5a Do not assume new fd is 0, dup2() with fd instead
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 10:54:31 +02:00
Joachim Nilsson 06d1e90bac Do not just mark deleted SVC free, clear memory
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 10:53:20 +02:00
Joachim Nilsson 439d9df122 Set CLOEXEC flag to prevent leaking descriptors
Both the new initctl API and the new pidfile watcher plugin failed to
set CLOEXEC on their sockets.  This caused forked-off and exec()'d
children to inherit all these descriptors.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-24 10:52:39 +02:00
Joachim Nilsson f44ac5a023 Remove bogus autoconf version requirement
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-17 00:20:26 +02:00
Joachim Nilsson 84c75c4416 Rename internal pkg-config reference to libuEv
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-17 00:18:11 +02:00
Joachim Nilsson ee8a69f5ed Let plugins know search path for lite.h
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-16 22:05:31 +02:00
Joachim Nilsson acf6cb6900 Show configure log to see where hell breaks loose
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-16 21:58:53 +02:00
Joachim Nilsson f51183d5f2 Strip install libs, do not use DESTDIR for Finit
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-16 21:55:39 +02:00
Joachim Nilsson ff5814e4d4 Use pkg-config provided paths for uEv and lite
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-16 21:45:21 +02:00
Joachim Nilsson 831d0c5520 Use /tmp as install --prefix for libs, not DESTDIR
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-16 21:21:48 +02:00
Joachim Nilsson d9cc21b3c8 Travis-CI: Attempt to build w/o root privs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-16 21:12:45 +02:00
Joachim Nilsson 096a3eff51 Mention new log keyword to services et al
Patrick Stewart added the optional `log` keyword to service, run, task
and inetd stanzas in 889bce1.  It redirects stdout/stderr to syslog
using the logger tool.  This addition to the README is mainly just a
place holder so it's not forgotten before the upcoming README refactor.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 23:56:02 +02:00
Joachim Nilsson 889bce1768 Merge pull request #26 from RFCreations/master
Some fixes, and add logging

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 23:46:01 +02:00
Patrick Stewart 0ba8df5c2d Change stdin member of svc to stdin_fd
'stdin' is a macro in stdio.h, so it shouldn't be used as a member name. GLIBC happens to have "#define stdin stdin", but other libcs don't.
2016-04-13 22:09:05 +01:00
Patrick Stewart ec2505191b Add service logging
Adds an extra 'log' parameter to services that routes STDOUT and STDERR to syslog.
Uses a PTY rather than a pipe, as this prevents log messages hanging around in a buffer indefinitely.
2016-04-13 22:09:05 +01:00
Patrick Stewart 3de9e4e5db Fix potential race condition/hang for SVC_TYPE_RUN services
In service_start() the SIGCHLD handler is reenabled before calling complete(), so the service could be reaped by the SIGCHLD handler leaving service_start() waiting forever
Also remove code that duplicates sig_unblock()
2016-04-13 22:09:05 +01:00
Patrick Stewart 1177c734ce Use finit API to get runlevel instead of utmp
utmp is not available with musl libc
2016-04-13 22:07:39 +01:00
Joachim Nilsson f7f222c924 Minor formatting and sectioning
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 07:45:28 +02:00
Joachim Nilsson dd8715f3dc configure.ac: Cleanup, remove commented out old ideas
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 07:43:40 +02:00
Joachim Nilsson 3e93ed2ead TODO: Add support for throttling connections
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 07:40:57 +02:00
Joachim Nilsson e50e12417e README.md: Attempt to update to new build system
First attempt to update and simplify the build instructions and example.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 07:37:34 +02:00
Joachim Nilsson 9e70a12273 configure.ac: Remove datarootdir
Unused after removal of GIT submodules.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 07:36:41 +02:00
Joachim Nilsson b313e623df Remove double slash '//bin' from default prefix path
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 07:22:27 +02:00
Joachim Nilsson 376d7a0fbb m4/plugin.m4: Minor cleanup
Use pure M4 syntax, not mixed with -P syntax.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-13 07:17:02 +02:00
Joachim Nilsson 1398536194 README.md: Update initctl help text
Add missing `initctl cond show` help.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-12 23:48:28 +02:00
Joachim Nilsson 95135ca0a7 README.md: Slightly friendlier warning
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-12 23:42:25 +02:00
Joachim Nilsson ec31930638 README.md: Mark optional plugins as optional
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-12 23:39:00 +02:00
Joachim Nilsson ed8b21db36 plugins/Makfile.am: Make optional plugins optional
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-12 23:36:35 +02:00
Joachim Nilsson b1cd3a2332 Make a few less used plugins optional
The alsa-utils, dbus, lost (PID), resolvconf, and x11-common plugins are
rarely used in the main use-case for Finit (embedded head-less systems).
This patch disables them by default and makes them possible to enable by
configure --enable-$PLUGIN switches.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-12 23:34:27 +02:00
Joachim Nilsson 5675d068f8 m4/plugin.m4: Simplify, fix and cleanup M4 code
Slightly more readable code and fix issue with --enable-$PLUGIN not
biting properly in the generated Makefile.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-12 23:32:52 +02:00
Joachim Nilsson 4baf2611f6 Foundation for selecting plugins in configure
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-12 20:30:29 +02:00
Joachim Nilsson 13d4821abe Disable fast-install by default
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-11 07:29:01 +02:00
Joachim Nilsson 1f1c2e0466 Replace verbose and kernel quiet modes with silent and quiet
This patch replaces the verbose and kernel-quiet runtime options with
a (hopefully) more useful quiet and silent modes of operation.

By default Finit progressively shows all services it starts at boot, but
when boot has completed and services are started/restarted Finit prints
nothing.  If `--disable-quiet` is given all started/restared/stoppping
messages are shown even after boot.

However, if `--enable-silent` is given Finit is completely silent even
at boot, no progress is shown, until the first service is started, which
on most systems is login.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-11 00:50:37 +02:00
Joachim Nilsson c4af8b7cbd configure: Shorten and clarify descriptions
This patch makes a few configure options shorter, but still more clear.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-11 00:49:24 +02:00
Joachim Nilsson 426d915c08 configure: Remove --with-aix-soname=aix|svr4|both
Remove confusing `--with-aix-soname` option to configure.  Replace with
hard-coded `aix-soname=both` => shared + static archives are build as to
be expected.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-11 00:46:31 +02:00
Joachim Nilsson 86be6812b9 configure: Remove GIT submodule check
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-11 00:45:37 +02:00
Joachim Nilsson 4efdc4fe19 Merge pull request #25 from westermo/wmo/2.4
Westermo fixes to Finit3
2016-04-10 20:08:25 +02:00
Joachim Nilsson da76cf4f75 Check return value from strtok(), may be NULL
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-10 19:58:46 +02:00
Joachim Nilsson a2177a8063 Fix GCC warning for signed vs unsigned comparison
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-10 19:58:13 +02:00
Joachim Nilsson cfa155deae Minor cleanup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-10 19:57:48 +02:00
Joachim Nilsson e7832e2ca7 FTBFS followup issue #24
Add missing return keyword to inline function svc_is_starting()

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-10 19:56:13 +02:00
Joachim Nilsson d7d45b65d0 Merge pull request #24 from westermo/wmo/2.4
Assert svc condition only when PID file has been created/updated

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-10 19:50:16 +02:00
Joachim Nilsson 4ae5c54f45 Assert svc condition only when PID file has been created/updated
This patch prevents too early start of services depending on other
services.  E.g, if service B depends on A then we must wait for A to
signal that it is ready before we allow B to start -- in a Finit based
system A does this by creating, or touching (updating mtime), its PID
file.  Services (like A) that support SIGHUP should call utime(), or
utimensat(), on the PID file at the end of their SIGHUP handler.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-10 19:14:48 +02:00
Joachim Nilsson 7b8944730d README: This is Finit3, development version
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-09 20:42:44 +02:00
Joachim Nilsson 87ca6574b8 Travis-CI: Build & install libuEv and libite first
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-09 20:37:09 +02:00
Joachim Nilsson 35844f2d1d Update finit_LDADD for external libuEv and libite
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-09 20:35:38 +02:00
Joachim Nilsson 2e2d87ee78 Update include path for libuEv and libite
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-09 20:33:07 +02:00
Joachim Nilsson ba11d218bc Remove local GIT submodules for libuEv and libite
From now on the user is required to have libuEv and libite installed
before building and installing finit. This is for maintenance reasons.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-09 20:29:26 +02:00
Joachim Nilsson 707c42db63 Update TODO, remember our origins!
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-05 20:18:31 +02:00
Joachim Nilsson 4cfe4781a3 Remove old (now unused) compat symlinks
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-05 20:17:38 +02:00
Joachim Nilsson 8f415aeaf6 Add HOOK_SVC_START to detect started processes
This patch adds support for the runtime `HOOK_SVC_START`.

Customer request.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-05 03:45:23 +02:00
Joachim Nilsson 3f3220e806 Add HOOK_SVC_LOST to detect lost processes
This patch adds support for the runtime `HOOK_SVC_LOST` and the (very
noisy) *example* plugin `lost.so`.

Customer request.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-05 03:33:58 +02:00
Joachim Nilsson e54c7eef65 Fix dlopening of plugins
Finit must export symbols, but plugins do not since they are modules.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-05 03:20:10 +02:00
Joachim Nilsson 05cc6bf5d2 Do not bail on makepath() fail if dir exists
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-05 03:16:40 +02:00
Joachim Nilsson 4fecb8621d Fix PLUGIN_PATH => $(pkglibdir) in build system
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-05 02:38:24 +02:00
Joachim Nilsson 6a49f7ed8c Add missing license, copyright and module description
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-21 15:56:15 +01:00
Joachim Nilsson 8bcc34ce91 Remove Emacs version-control handling
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-21 15:41:51 +01:00
Joachim Nilsson 8da1505454 Minor, use () to denote function in comments
To distinguish between variables and functions in comments Finit use ()
at the end of function names.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-21 15:14:12 +01:00
Joachim Nilsson 34e4d31219 Travis-CI: Set LD_LIBRARY_PATH to /tmp/lib for initctl -h
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-19 23:03:41 +01:00
Joachim Nilsson 6489dcc967 Update .gitignore files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-19 23:00:00 +01:00
Joachim Nilsson 2d6769005a TODO: Add Westermo request for initctl show --json
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-19 22:56:05 +01:00
Joachim Nilsson 98cd6dffb1 Fix build and install temporarily
This patch builds Finit dynamically, including both plugins and GIT
submodules for libite and libuev.  This causes libite.so and libuev.so
to be installed in a public path, which is not good if the user has
other versions of the same libraries already installed.

The correct fix is to remove the GIT submodules for now and require
users of Finit to build and install libite and libuev separately.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-19 22:54:07 +01:00
Joachim Nilsson eb86a76a0d Travis-CI: Remove extra call to configure
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-19 22:51:34 +01:00
Joachim Nilsson 4423a2b16e Fix Travis-CI build, move package install
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-18 21:32:23 +01:00
Joachim Nilsson 0467c571df Merge pull request #22 from westermo/wmo/2.4
initctl: Only show service name, not path, with plain show
2016-03-17 22:04:27 +01:00
Joachim Nilsson cef4b5761a initctl: Only show service name, not path, with plain show
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-16 13:06:47 +01:00
Joachim Nilsson 806d05622a Fix libite.la library dependency in build
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-15 08:20:48 +01:00
Joachim Nilsson 57fb4738f1 Create empty m4 directory for the benefit of autogen.sh and Travis-CI
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-15 07:34:20 +01:00
Joachim Nilsson 0bbc69200b Update Travis-CI to use ./autogen.sh before configure & build
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-15 00:13:07 +01:00
Joachim Nilsson 726f0d428d Initial conversion to GNU Configure & Build System
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-14 22:35:54 +01:00
Joachim Nilsson 0704947c33 Merge pull request #20 from westermo/wmo/2.4
inetd reload fixes

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-19 15:11:20 +01:00
Tobias Waldekranz 2555fe8e96 inetd: Flush existing filters on reload
Flush any existing filter rules when a configuration file is
updated. Otherwise filters are simply appended to the old
configuration which can cause finit to accept connections on
interfaces that are blocked in the new configuration.
2016-02-19 12:53:39 +01:00
Tobias Waldekranz c41016ef32 Only default to SIGHUP support on daemons
When parsing a service configuration, only default to SIGHUP support
if the service is a daemon. inetd services must be stop/started on
change.
2016-02-19 12:50:24 +01:00
Joachim Nilsson 5d86137142 Fix Coverity warning for unchecked return value from mkdir()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 23:13:47 +01:00
Joachim Nilsson bd58345b8c Fix Coverity warning for unterminated string from read()
The read() does not zero terminate the string, then we call strstr() on
that string ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 23:01:24 +01:00
Joachim Nilsson 994b589377 Fix Coverity warning for possible NULL ptr dereference
Add check for return value from strtok()

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 22:55:39 +01:00
Joachim Nilsson caff08f58d Fix Coverity warning for unchecked return value from dirname()
- Check return value from dirname()
- Remove static from local compose buffer

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 22:50:08 +01:00
Joachim Nilsson d58c58d972 Test build with both GCC and Clang (LLVM)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:57:58 +01:00
Joachim Nilsson a97499999e Remove initial make distclean step, not needed
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:54:34 +01:00
Joachim Nilsson 9c7046a25d Fix linking error with new autotools based libraries
We may have reached peak Makefile mastery now ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:44:21 +01:00
Joachim Nilsson cd5d55ef51 Call libuev configure script if local libuev is used
Same as for libite, libuev has also changed to autotools, so
call its autogen.sh and configure script in cascade from our
own.  Aslo add safeguards to clean/distclean targets so we
do not attempt to enter the libuev subdirectory if it's not
configured.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:43:04 +01:00
Joachim Nilsson 5eb172ed46 Update to libuEv v1.3.1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:23:16 +01:00
Joachim Nilsson 3d99940252 Bump version to 3.0-beta1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:10:47 +01:00
Joachim Nilsson fce420121c Fix unused variable warning
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:10:25 +01:00
Joachim Nilsson 6c91c4ec12 Fix unused variable warning
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:09:31 +01:00
Joachim Nilsson 2cb5997abe Fix signed/unsigned comparison warning
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:09:20 +01:00
Joachim Nilsson 5afb47bc9b ChangeLog: Add missing links to referenced previous versions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:08:21 +01:00
Joachim Nilsson d6d92f1df7 Increase indentation in pretty printing slightly for autotools
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:07:17 +01:00
Joachim Nilsson 4899bba06b Call libite configure script if local libite is used
The new libite uses autotools to build, and since we use it as a GIT
submodule we have to call its autogen.sh before we call the configure
script, from within the Finit configure script.  We may have to move
to use autotools even for Finit ...

Also, make sure to only call clean/distclean on libite if the local
version is selected at configure time.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 21:06:46 +01:00
Joachim Nilsson 97a160dd1d Update to libite v1.4.3
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 20:26:40 +01:00
Joachim Nilsson 901bdc68ba Merge pull request #19 from westermo/wmo/2.4
Service state machine and refactored event/condition handling

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-05 16:44:51 +01:00
Tobias Waldekranz c6055031b8 Avoid unnecessary restarts of services that do not support SIGHUP
When a service's condition transitions to `flux`, put it in the
waiting state, even if it does not support SIGHUP. That way, if the
condition returns to `on` we can simply SIGCONT it. If it goes to
`off` it will still be stop/started as before.
2016-02-05 13:45:06 +01:00
Tobias Waldekranz e8d699c194 Mark inetd services as clean on dynamic reload
When reloading dynamic services, inetd services deleted marker was not
being cleaned. This caused finit to stop and start all inetd services
at every other reload.
2016-01-28 14:52:49 +01:00
Tobias Waldekranz 80273555c5 Do all service transitions in a helper function
This eases development, since it makes it easy to set a breakpoint on
a specific state transition.
2016-01-28 13:44:27 +01:00
Tobias Waldekranz 6ece4b8fa3 inetd: Re-integrate the inetd subsystem into the new service model
This patch introduces a new service type called SVC_TYPE_INETD_CONN,
which represents a running inetd connection. These are handled in much
the same way as tasks. I.e. they are considered one-time jobs, with
the difference that they are removed upon completion.
2016-01-15 10:07:15 +01:00
Tobias Waldekranz 0beb566e45 git: Ignore Emacs backup files 2016-01-13 16:02:46 +01:00
Tobias Waldekranz c641a1374e Do not ignore updated services when cleaning up removed ones
When cleaning up removed services, the assumption was made that if a
service was not 'removed', it was 'clean'. In reality, it could also
be 'updated', which was then overwritten, causing services not being
restarted. Don't do that.

As a future precaution, make the dirty field const, only allowing
updates from accessor methods.
2016-01-13 15:14:27 +01:00
Tobias Waldekranz 171d611b14 Improve service state machine debug messages
In addition to logging state transitions, also log the parameters that
might trigger a transition.
2016-01-13 15:09:04 +01:00
Tobias Waldekranz 02eb68f46c Ensure that a service has stopped before taking any further action
During documentation of the state machine, some theoretical problems
where discovered that could have lead finit to spawn a new instance of
a daemon before the previous one had been collected.

Now a service will always go through the STOPPING state when leaving
RUNNING. This ensures that the PID has been collected before any calls
to service_start.

...documenting your work is, apparently, not a bad idea. :)
2016-01-07 11:00:52 +01:00
Tobias Waldekranz 7fe4c3355f doc: Add service state machine documentation 2016-01-07 11:00:51 +01:00
Joachim Nilsson 5b28fed321 Audit.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-05 21:11:27 +01:00
Tobias Waldekranz aa1da0e9f2 doc: Add documentation for conditions 2016-01-05 17:24:31 +01:00
Tobias Waldekranz 2232aea0f7 Dynamically manage service states based on user defined conditions
Old event system has been replaced with a more generic condition
concept. The idea is that finit plugins may provide arbitrary
conditions that services may specify as dependencies that they require
to run.

In order to accomodate this, the service management has been
redesigned to use a state machine.
2016-01-05 15:01:58 +01:00
Tobias Waldekranz 7a3408c173 Optionally skip stripping binaries during install-exec
One may wish to build and install with debug symbols intact, so allow
overriding the arguments sent to install.
2016-01-05 14:50:13 +01:00
Tobias Waldekranz af7b6c17cb Correctly parse service configuration stanzas containing slashes
Parser would erroneously interpret an event specification containing
slashes as an inet service/proto specification.

E.g. "<net/gw>" was interpreted as port "<net" using protocol "gw>".
2016-01-05 14:50:13 +01:00
99 changed files with 6434 additions and 3601 deletions
+22 -2
View File
@@ -1,8 +1,13 @@
*~
*.o *.o
*.d
*.so *.so
*.gz *.gz
*.map *.map
configure
Makefile
Makefile.in
/.deps/*
/.libs/*
/finit /finit
/initctl /initctl
/reboot /reboot
@@ -12,4 +17,19 @@
/GRTAGS /GRTAGS
/GSYMS /GSYMS
/GTAGS /GTAGS
/contrib/* /autom4te.cache/*
/aclocal.m4
/ar-lib
/compile
/config.guess
/config.h.in
/config.log
/config.status
/config.sub
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1
-6
View File
@@ -1,6 +0,0 @@
[submodule "libuev"]
path = libuev
url = https://github.com/troglobit/libuev.git
[submodule "libite"]
path = libite
url = https://github.com/troglobit/libite.git
+35 -7
View File
@@ -2,9 +2,17 @@
# Defaults to GNU GCC and autotools: ./configure && make && make test # Defaults to GNU GCC and autotools: ./configure && make && make test
language: c language: c
# We don't need to install packages, use dockerized build, quicker # Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
sudo: false sudo: false
# Use non-docker builds atm. to install libuEv and libite from source
#sudo: true
# Test build with both GCC and Clang (LLVM)
compiler:
- gcc
- clang
env: env:
global: global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
@@ -12,17 +20,37 @@ env:
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4=" - secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
addons: addons:
apt:
packages:
- tree
coverity_scan: coverity_scan:
project: project:
name: "troglobit/finit" name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux" description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com notification_email: troglobit@gmail.com
build_command_prepend: "./configure" build_command_prepend: "./autogen.sh && PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp"
build_command: "make" build_command: "make"
branch_pattern: dev branch_pattern: dev
install:
- wget https://github.com/troglobit/libuev/releases/download/v1.4.2/libuev-1.4.2.tar.xz
- wget https://github.com/troglobit/libite/releases/download/v1.8.2/libite-1.8.2.tar.xz
- tar xf libuev-1.4.2.tar.xz
- tar xf libite-1.8.2.tar.xz
- (cd libuev-1.4.2 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libite-1.8.2 && ./configure --prefix=/tmp && make && make install-strip)
# Custom build script for the time being, no "make test" yet # Custom build script for the time being, no "make test" yet
script: script:
- make distclean - ./autogen.sh
- ./configure - PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp
- make - cat config.log
- make clean
- make -j5 V=1
- make install-strip
- tree /tmp
- ldd /tmp/sbin/finit
- size /tmp/sbin/finit
- size /tmp/sbin/initctl
- size /tmp/sbin/reboot
- LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
-1
View File
@@ -1 +0,0 @@
ChangeLog.md
+36 -4
View File
@@ -3,6 +3,38 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[3.0][UNRELEASED] - 2016-XX-YY
------------------------------
### Changes
* Manage service states based on user defined conditions
* Manage dependencies between services, w/ conditions (pidfile plugin)
* Manage service dependenceis on network events (netlink plugin)
* Support for dynamically reloading Finit configuration at runtime
* Refactor to use GNU configure and build system
* New hooks for for detecting lost and started services (lost plugin)
* External libraries, libuEv and libite, now build requirements
* Early logging support to `/dev/kmsg` instead of console
* Support for redirecting stdout/stderr of services to syslog
* Support for managing resource limits for Finit and its processes
* Add optional built-in inetd services: echo server, chargen, etc.
* Add simple built-in getty
* Greatly improved accounting support, both UTMP and WTMP fixes+features
* Improved udev support, on non-embedded systems
* Improvide shutdown and file-system unmount support (Debian)
* Added `--enable-emergency-shell` (configure) fallback debug mode
* Support SysV init `/etc/rc.local`
* Inetd protection against UDP looping attacks
* Support systems with `/run` instead of `/var/run` (bootmisc plugin)
* Adopted BusyBox init signals for halt/reboot/poweroff
* SysV init compat support for reboot (setenv)
* Compat support for musl libc
* Add OpenRC-like support for sysctl.d/*.conf
* Add support for Debian/BusyBox `/etc/network/interfaces`
* Add support for running fsck on file systems in `/etc/fstab`
* Added example configs + HowTos for Debian (Jessie) and Alpine Linux
[2.4][] - 2015-12-04 [2.4][] - 2015-12-04
-------------------- --------------------
@@ -17,10 +49,10 @@ Bug fix release.
* Update section mentioning BusyBox getty * Update section mentioning BusyBox getty
* Update debugging documentation * Update debugging documentation
* Allow debug to override terse mode * Allow debug to override terse mode
* Revert confusing change in service state introduced in [v2.3][]. * Revert confusing change in service state introduced in [v2.3][2.3].
As of v2.4 services are listed as "halted" and "stopped", when As of [v2.4][2.4] services are listed as "halted" and "stopped", when
they have been halted due to a runlevel changed or stopped by they have been halted due to a runlevel changed or stopped by the
the user, respectively. user, respectively.
### Fixes ### Fixes
-185
View File
@@ -1,185 +0,0 @@
# Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
#
# Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
# Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
.PHONY: all install clean distclean dist \
install-exec install-data install-dev \
uninstall-exec uninstall-data uninstall-dev
#VERSION = $(shell git tag -l | tail -1)
VERSION = 2.4
NAME = finit
PKG = $(NAME)-$(VERSION)
DEV = $(NAME)-dev
ARCHTOOL = `which git-archive-all`
ARCHIVE = $(PKG).tar
ARCHIVEZ = ../$(ARCHIVE).xz
EXEC = finit initctl reboot
HEADERS = finit.h plugin.h svc.h inetd.h helpers.h queue.h
DISTFILES = LICENSE README ChangeLog finit.conf services
OBJS = finit.o api.o client.o conf.o exec.o helpers.o pid.o sig.o \
svc.o service.o plugin.o tty.o inetd.o event.o
DEPLIBS =
TOPDIR = $(shell pwd)
-include config.mk
# Figure out source and dependency files
SRCS = $(OBJS:.o=.c)
DEPS = $(SRCS:.c=.d)
CFLAGS += -W -Wall
# Disable annoying gcc warning for "warn_unused_result", see GIT 37af997
CPPFLAGS += -U_FORTIFY_SOURCE
CPPFLAGS += -I$(TOPDIR)
CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
include common.mk
all: config.h $(EXEC)
+$(MAKE) -C plugins $@
$(DEPLIBS): Makefile
+$(MAKE) -C `dirname $@` all
$(OBJS): Makefile
config.h: configure
@echo "Please run configure script first."
@exit 1
finit: $(OBJS) $(DEPLIBS)
initctl: initctl.o svc.o helpers.o $(DEPLIBS)
reboot: reboot.o $(DEPLIBS)
install-exec: all
@$(INSTALL) -d $(DESTDIR)$(FINIT_RCSD)
@$(INSTALL) -d $(DESTDIR)$(sbindir)
@for file in $(EXEC); do \
printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \
$(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \
done
@printf " INSTALL $(DESTDIR)$(sbindir)/service\n"
@$(INSTALL) -m 0755 service $(DESTDIR)$(sbindir)/service
printf " INSTALL $(DESTDIR)$(sbindir)/telinit\n"
@ln -sf finit $(DESTDIR)$(sbindir)/telinit
$(MAKE) -C plugins install
ifndef LIBITE
$(MAKE) -C libite install-exec
endif
install-data:
@$(INSTALL) -d $(DESTDIR)$(datadir)
@$(INSTALL) -d $(DESTDIR)$(mandir)
@for file in $(DISTFILES); do \
printf " INSTALL $(DESTDIR)$(datadir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(datadir)/$$file; \
done
install-dev:
@$(INSTALL) -d $(DESTDIR)$(incdir)
@for file in $(HEADERS); do \
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
done
ifndef LIBITE
$(MAKE) -C libite install-dev
endif
ifndef LIBUEV
$(MAKE) -C libuev install-dev
endif
install: install-exec install-data install-dev
uninstall-exec:
-@for file in $(EXEC); do \
printf " REMOVE $(DESTDIR)$(sbindir)/$$file\n"; \
rm $(DESTDIR)$(sbindir)/$$file 2>/dev/null; \
done
-@for file in service telinit; do \
printf " REMOVE $(DESTDIR)$(sbindir)/$$file\n"; \
rm $(DESTDIR)$(sbindir)/$$file 2>/dev/null; \
done
-@rmdir $(DESTDIR)$(sbindir) 2>/dev/null
-@rmdir $(DESTDIR)$(FINIT_RCSD) 2>/dev/null
$(MAKE) -C plugins uninstall
ifndef LIBITE
$(MAKE) -C libite uninstall
endif
uninstall-data:
-@for file in $(DISTFILES); do \
printf " REMOVE $(DESTDIR)$(datadir)/$$file\n"; \
rm $(DESTDIR)$(datadir)/$$file 2>/dev/null; \
done
-@rmdir $(DESTDIR)$(mandir) 2>/dev/null
-@rmdir $(DESTDIR)$(datadir) 2>/dev/null
uninstall-dev:
-@$(RM) -rf $(DESTDIR)$(incdir) 2>/dev/null
uninstall: uninstall-exec uninstall-data uninstall-dev
clean:
+$(MAKE) -C plugins $@
+$(MAKE) -C libite $@
+$(MAKE) -C libuev $@
-@$(RM) $(OBJS) $(DEPS) $(EXEC) 2>/dev/null
distclean: clean
+$(MAKE) -C plugins $@
+$(MAKE) -C libite $@
+$(MAKE) -C libuev $@
-@$(RM) $(JUNK) config.mk config.h unittest *.o *.html 2>/dev/null
check:
$(CHECK) *.c plugins/*.c
dist:
@if [ x"$(ARCHTOOL)" = x"" ]; then \
echo "Missing git-archive-all from https://github.com/Kentzo/git-archive-all"; \
exit 1; \
fi
@if [ -e $(ARCHIVEZ) ]; then \
echo "Distribution already exists."; \
exit 1; \
fi
@echo "Building xz tarball of $(PKG) in parent dir..."
@$(ARCHTOOL) ../$(ARCHIVE)
@xz ../$(ARCHIVE)
@md5sum $(ARCHIVEZ) | tee $(ARCHIVEZ).md5
dev: distclean
@echo "Building unstable xz $(DEV) in parent dir..."
-@$(RM) -f ../$(DEV).tar.xz*
@(dir=`mktemp -d`; mkdir $$dir/$(DEV); cp -a . $$dir/$(DEV); \
cd $$dir; tar --exclude=.git --exclude=contrib \
-c -J -f $(DEV).tar.xz $(DEV); \
cd - >/dev/null; mv $$dir/$(DEV).tar.xz ../; cd ..; \
rm -rf $$dir; md5sum $(DEV).tar.xz | tee $(DEV).tar.xz.md5)
ifneq ($(MAKECMDGOALS),distclean)
-include $(DEPS)
endif
+112
View File
@@ -0,0 +1,112 @@
ACLOCAL_AMFLAGS = -I m4
sbin_PROGRAMS = finit initctl reboot
finit_SOURCES = api.c \
cond.c cond-w.c cond.h \
client.c \
conf.c conf.h \
exec.c finit.c finit.h \
getty.c \
helpers.c helpers.h \
mdadm.c mount.c pid.c \
plugin.c plugin.h private.h \
service.c service.h \
sig.c sig.h \
sm.c sm.h \
svc.c svc.h \
tty.c tty.h \
utmp-api.c utmp-api.h
pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h plugin.h svc.h
if INETD
finit_SOURCES += inetd.c inetd.h
endif
finit_CFLAGS = -W -Wall -Wextra -std=gnu99 $(lite_CFLAGS) $(uev_CFLAGS)
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
if STATIC
finit_LDADD += plugins/libplug.la
else
finit_LDADD += -ldl
endif
initctl_SOURCES = initctl.c svc.c svc.h helpers.c helpers.h cond.c cond.h \
utmp-api.c utmp-api.h
initctl_CFLAGS = $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS)
reboot_SOURCES = reboot.c
INIT_LNKS = telinit init
REBOOT_LNKS = shutdown halt suspend
SUBDIRS = plugins
doc_DATA = README.md LICENSE finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md finit.conf
AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC
AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs
else
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
AM_LDFLAGS = -export-dynamic
endif
# Compat hook
install-dev: install-pkgincludeHEADERS
# Hook in install to add finit and reboot symlink(s)
install-exec-hook:
@for file in $(INIT_LNKS); do \
lnk=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$lnk ]; then \
echo "Skipping $$lnk exists already."; \
else \
$(LN_S) finit $$lnk; \
fi; \
done
@if [ -L $(DESTDIR)$(sbindir)/reboot ]; then \
echo "Non-finit reboot, skipping."; \
else \
for file in $(REBOOT_LNKS); do \
link=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$link ]; then \
echo "$$link exists, skipping.";\
else \
$(LN_S) reboot $$link; \
fi; \
done; \
fi
uninstall-hook:
@for file in $(INIT_LNKS); do \
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
if [ "$$lnk" != "finit" ]; then \
echo "Skipping non-finit $$file."; \
continue; \
fi; \
$(RM) $(DESTDIR)$(sbindir)/$$file; \
done
@if [ -L $(DESTDIR)$(sbindir)/reboot ]; then \
echo "Non-finit reboot, skipping uninstall."; \
else \
for file in $(REBOOT_LNKS); do \
if [ "`readlink $$file`" != "reboot" ]; \
then continue; \
fi; \
$(RM) $(DESTDIR)$(sbindir)/$$file; \
done; \
fi
# Target to run when building a release
release: dist
@for file in $(DIST_ARCHIVES); do \
md5sum $$file > ../$$file.md5; \
done
@mv $(DIST_ARCHIVES) ../
@echo
@echo "Resulting release files:"
@echo "================================================================="
@for file in $(DIST_ARCHIVES); do \
printf "$$file \tDistribution tarball\n"; \
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
done
-1
View File
@@ -1 +0,0 @@
README.md
+168 -616
View File
@@ -1,25 +1,32 @@
Finit ][ Fast & Extensible init for Linux ```
========================================= _______ __ _____ ___ __ ___________ _______
/" "||" \ (\" \|" \ |" \(" _ ")/" __ )
(: ______)|| | |.\\ \ | || |)__/ \\__/(__/ _) ./
\/ | |: | |: \. \\ | |: | \\_ / / //
// ___) |. | |. \ \. | |. | |. | __ \_ \\
(: ( /\ |\| \ \ | /\ |\ \: | (: \__) :\
\__/ (__\_|_)\___|\____\)(__\_|_) \__| \_______) ... Yes, we need a new logo!
```
Fast init for Linux
===================
[![Travis Status]][Travis] [![Coverity Status]][Coverity Scan] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
![Original Finit homepage image](images/finit.jpg "Finit in action!")
Table of Contents Table of Contents
----------------- -----------------
* [Introduction](#introduction) * [Introduction](#introduction)
* [Features](#features) * [Features](#features)
* [/etc/finit.conf](#etcfinitconf) * [/etc/finit.conf](doc/config.md#etcfinitconf)
* [/etc/finit.d](#etcfinitd) * [/etc/finit.d](doc/config.md#etcfinitd)
* [Runparts](#runparts) * [Runparts & /etc/rc.local](#runparts--etcrclocal)
* [Bootstrap](#bootstrap) * [Bootstrap](doc/bootstrap.md#bootstrap)
* [Runlevels](#runlevels) * [Runlevels](#runlevels)
* [Inetd](#inetd) * [Inetd](doc/inetd.md#inetd)
* [Hooks, Callbacks & Plugins](#hooks-callbacks--plugins) * [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
* [Rebooting & Halting](#rebooting--halting) * [Rebooting & Halting](#rebooting--halting)
* [Commands & Status](#commands--status) * [Commands & Status](#commands--status)
* [Building](#building) * [Building](doc/build.md#building)
* [Running](#running) * [Running](#running)
* [Debugging](#debugging) * [Debugging](#debugging)
* [Origin & References](#origin--references) * [Origin & References](#origin--references)
@@ -28,29 +35,71 @@ Table of Contents
Introduction Introduction
------------ ------------
Init is the first userland process started by the UNIX kernel, therefore Finit is an EeePC inspired Fastinit clone with [process supervision][1]
it always has PID 1 and is responsible for starting up the rest of the similar to that of D.J. Bernstein's [daemontools][2] and Gerrit Pape's
system. [runit][3]. The focus of Finit is on small and embedded Linux systems,
although it is fully usable on server and desktop installations as well.
Finit is a plugin-based init with [process supervision][1] similar to See the [contrib section](contrib/) for Debian and Alpine examples.
that of D.J. Bernstein's [daemontools][2] and Gerrit Pape's [runit][3].
The main focus of Finit is on small and embedded GNU/Linux systems, yet
fully functional on standard server and desktop installations as well.
Traditional [SysV init][4] style systems are scripted. For low-resource Traditional [SysV init][4] style systems are scripted. For low-resource
embedded systems this can be quite resource intensive and cause longer embedded systems this is quite resource intensive and often leads to
boot times. Finit is optimized to reduce context switches and forking long boot times. Finit reduces context switches and forking of shell
of processes to provide a very basic bootstrap written entirely in C. scripts to provide a swift [system bootstrap](doc/bootstrap.md) written
entirely in C.
> Finit basically does everything you need from its `main()` function! Configuration is read from [/etc/finit.conf](doc/config.md#etcfinitconf)
which details kernel modules to load, services and TTYs to start. When
initial [bootstrap](doc/bootstrap.md) is done, including setting up
networking, [/etc/finit.d/](doc/config.md#etcfinitd) and the familiar
[/etc/rc.local](#runparts--etcrclocal) are run.
Hence, there is no `/etc/init.d/rcS` script, or similar, instead Finit **Example /etc/finit.conf:**
reads its configuration from [/etc/finit.conf](#etcfinitconf). This
file details what kernel modules to load, programs to run, daemons to
supervise, and inetd services to launch on demand.
See [TroglOS][9] for an example of how to boot a small embedded system ```
with Finit. # Fallback if /etc/hostname is missing
host wopr
# Runlevel to start after bootstrap, runlevel 'S'
runlevel 2
# Services to be monitored and respawned as needed
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
service [S12345] /sbin/klogd -n -- Kernel log daemon
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
# For multiple instances of the same service, add :ID somewhere between
# the service/run/task keyword and the command.
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
service :2 [2345] /sbin/merecat -n -p 8080 /var/www -- Old web server
# Alternative method instead of below runparts, can also use /etc/rc.local
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
#task [S] /etc/init.d/kbd start -- Preparing console
# Inetd services to start on demand, with alternate ports and filtering
inetd ftp/tcp nowait [2345] /sbin/uftpd -i -f -- FTP daemon
inetd tftp/udp wait [2345] /sbin/uftpd -i -y -- TFTP daemon
inetd time/udp wait [2345] internal -- UNIX rdate service
inetd time/tcp nowait [2345] internal -- UNIX rdate service
inetd 3737/tcp nowait [2345] internal.time -- UNIX rdate service
inetd telnet/tcp nowait [2345] /sbin/telnetd -i -F -- Telnet daemon
inetd 2323/tcp nowait [2345] /sbin/telnetd -i -F -- Telnet daemon
inetd 222/tcp@eth0 nowait [2345] /sbin/dropbear -i -R -F -- SSH service
inetd ssh/tcp@*,!eth0 nowait [2345] /sbin/dropbear -i -R -F -- SSH service
# Run start scripts from this directory
# runparts /etc/start.d
# Virtual consoles to start built-in getty on
tty [12345] /dev/tty1 115200 linux
tty [12345] /dev/ttyAMA0 115200 vt100
```
For an example of a full blown embedded Linux, see [TroglOS][9], or take
a look at the `contrib/` section with [Alpine Linux](contrib/alpine/),
[Debian](contrib/debian/), support and more.
Features Features
@@ -58,17 +107,36 @@ Features
**Process Supervision** **Process Supervision**
Start, monitor and restart processes (daemons) if they fail. Start, monitor and restart services should they fail.
**Inetd** **Inetd**
Finit comes with a built-in `inetd` server. No need to maintain a Finit comes with a built-in [inetd server](doc/inetd.md). No need to
separate config file for services that you want to start on demand. maintain a separate config file for services that you want to start on
demand.
All inetd services started can be filtered per port and inbound All inetd services started can be filtered per port and inbound
interface, reducing the need for a full blown firewall. interface, reducing the need for a full blown firewall.
Built-in optional inetd services:
- echo RFC862
- chargen RFC864
- daytime RFC867
- discard RFC863
- time (rdate) RFC868
For more information, see [doc/inetd.md](doc/inetd.md).
**Getty**
Finit also comes with a built-in Getty for Linux console TTYs. It can
parse `/etc/inittab` and set the speed. Then `/bin/login` handles all
nasty bits with PAM etc.
# /etc/finit.conf
tty [12345] /dev/tty1 38400 linux
tty [12345] /dev/ttyAMA0 115200 vt100
**Runlevels** **Runlevels**
@@ -104,222 +172,25 @@ Extensions and functionality not purely related to what an `/sbin/init`
needs to start a system are available as a set of plugins that either 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. hook into the boot process or respond to various I/O.
For more information, see [doc/plugins.md](doc/plugins.md).
/etc/finit.conf
---------------
Contrary to most other script based init alternatives ([SysV init][4],
[upstart][6], [systemd][7], [OpenRC][8], etc.) Finit reads its entire
configuration from `/etc/finit.conf`.
The command line arguments given in `/etc/finit.conf` to each service
provide a default. A plugin can be used to register a callback to a
service and then modify the behavior to suit the current runlevel and
system configuration.
For instance, before starting a heavily resource intensive service like
IPsec or OpenVPN, a callback can check if the outbound interface is up
and has an IP address, or just check if the service is disabled — much
like what a SysV init start script usually does.
Syntax:
* `check <DEV>`
Run fsck on a file system before mounting it
* `module <MODULE>`
Load a kernel module, with optional arguments
* `network <PATH>`
Script or program to bring up networking, with optional arguments
* `runlevel <N>`
N is the runlevel number 1-9, where 6 is reserved for reboot.
Default is 2.
* `run [LVLS] /path/to/cmd ARGS -- Optional description`
One-shot command to run in sequence when entering a runlevel, with
optional arguments and description. This command is guaranteed to be
completed before running the next command.
* `task [LVLS] /path/to/cmd ARGS -- Optional description`
One-shot like 'run', but starts in parallel with the next command
* `service [LVLS] /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Please note that you often need to provide a
`--foreground` or `--no-background` argument to most daemons to
prevent them from forking off a sub-process in the background.
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
Launch a daemon when a client initiates a connection on an Internet
port. Available services are listed in the UNIX `/etc/services` file.
Finit can filter access to from a list of interfaces, `@iflist`, per
inetd service as well as listen to custom ports.
```shell
inetd ftp/tcp nowait @root /usr/sbin/uftpd -i -f
inetd tftp/udp wait @root /usr/sbin/uftpd -i -t
```
The following example listens to port 2323 for telnet connections and
only allows clients connecting from `eth0`:
```shell
inetd 2323/tcp@eth0 nowait [2345] /sbin/telnetd -i -F
```
The interface list, `@iflist`, is of the format `@iface,!iface,iface`,
where a single `!` means to deny access. Notice how interfaces are
comma separated with no spaces.
The `inetd` directive can also have ` -- Optional Description`, only
Finit does not output this text on the console when launching inetd
services. Instead this text is sent to syslog and also shown by the
`initctl` tool. More on inetd below.
* `runparts <DIR>`
Call run-parts(8) on a directory to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order.
* `include <CONF>`
Include another configuration file. Absolute path required.
* `tty [LVLS] <DEV | /path/to/cmd [args]>`
Start a getty on the given TTY device DEV, in the given runlevels. If
no tty setting is given in `finit.conf`, or if `/bin/sh` is given as
argument instead of a device path, a single shell is started on the
default console. Useful for really bare-bones systems.
It is also possible to supply the full command line, with arguments,
to your getty. In this case finit will simply use that command.
See `finit.h` for the `#define GETTY` that is called, along with the
default baud rate.
**Note:** BusyBox getty, used when Finit is built for embedded
systems, can take a `DEV` with *or* without `/dev/` prefix. Make
sure to call the configure script with `--enable-embedded` if this
is what you want.
* `console <DEV | /path/to/cmd [args]>`
Some embedded systems have a dedicated serial console/service port.
This command tells finit to not start getty directly, since there may
not be anyone there. To save RAM and CPU finit instead displays a
friendly message and waits for the user to activate the console with a
key press before starting getty. Finit also does some other magic and
changes the process name to "console".
Like the `tty` command, the `console` command can also be used to
simply start a UNIX shell, e.g. `/bin/sh`.
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
be installed since system requirements differ too much. Try out the
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
Every `run`, `task`, `service`, or `inetd` can also list the privileges
the `/path/to/cmd` should be executed with. Simply prefix the path with
`[@USR[:GRP]]` like this:
```shell
run [2345] @joe:users /usr/bin/logger "Hello world"
```
For multiple instances of the same command, e.g. a DHCP client or
multiple web servers, add `:ID` somewhere between the `run`, `task`,
`service` keyword and the command, like this:
```shell
service :1 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
service :2 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
```
Without the `:ID` to the service the latter will overwrite the former
and only the old web server would be started and supervised.
/etc/finit.d Runparts & /etc/rc.local
------------ ------------------------
Finit supports changes to the overall system configuration at runtime.
For this purpose the (configurable) directory `/etc/finit.d` is used.
Here you can put configuration file snippets, one per service if you
like, which are all sourced automatically by finit at boot when loading
the static configuration from `/etc/finit.conf`. This is the default
behavior, so no include directives are necessary.
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
send `SIGHUP` to PID 1, or call `finit q`. Any service read from this
directory is flagged as a dynamic service, so changes to their `.conf`
files, or even removal of the files, is detected at `SIGHUP`.
- If a service's `.conf` file has been removed, the service is stopped.
- If the file is modified, the service is reloaded, stopped and started.
- If a new service is detected, it is started — respecting runlevels
and return values from any callbacks.
The `/etc/finit.d` directory was previously the default Finit `runparts`
directory. Finit no longer has a default `runparts`, so make sure to
update your setup, or the finit configuration, accordingly.
**Note:** Configurations read from `/etc/finit.d` are read *after*
initial bootstrap, runlevel S(1). Hence, bootstrap services *must* be
in `/etc/finit.conf`!
Runparts
--------
At the end of the boot, when networking and all services are up, finit At the end of the boot, when networking and all services are up, finit
calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory, calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory,
if it exists. Similar to how the `/ec/rc.local` file works in most and `/etc/rc.local`, in that order if they exist.
other init daemons, only finit runs a directory of scripts. This
replaces the earlier support for a `/usr/sbin/services.sh` script in the
original finit.
```shell
runparts /etc/rc.d/
```
Bootstrap 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
1. Populate `/dev` hooks in [doc/plugins.md](doc/plugins.md#hooks), and about the system
2. Parse `/etc/finit.conf` bootstrap in [doc/bootstrap.md](doc/bootstrap.md).
3. Load all `.so` plugins
4. Remount/Pivot `/` to get R+W
5. Call 1st level hooks, `HOOK_ROOTFS_UP`
6. Mount `/etc/fstab` and swap, if available
7. Cleanup stale files from `/tmp/*` et al
8. Enable SysV init signals
9. Call 2nd level hooks, `HOOK_BASEFS_UP`
10. Start all 'S' runlevel tasks and services
11. Load kernel parameters from `/etc/sysctl.conf`
12. Set hostname and bring up loopback interface
13. Call `network` script, if set in `/etc/finit.conf`
14. Call 3rd level hooks, `HOOK_NETWORK_UP`
15. Load all `*.conf` files in `/etc/finit.d/` and switch to the active
active runlevel, as set in `/etc/finit.conf`, default is 2. Here is
where the rest of all tasks and inetd services are started.
16. Call 4th level hooks, `HOOK_SVC_UP`
17. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
18. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
19. Start TTYs defined in `/etc/finit.conf`, or rescue on `/dev/console`
20. Enter main monitor loop
In (10) and (15) tasks and services defined in `/etc/finit.conf` are
started. Remember, all `service` and `task` stanzas are started in
parallel and `run` in sequence, and in the order listed. Hence, to
emulate a SysV `/etc/init.d/rcS` one could write a long file with only
`run` statements.
Notice the five hook points that are called at various point in the
bootstrap process. This is where plugins can extend the boot in any way
they please.
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
USB stick, convert/copy its contents to the system's `/etc/` directory,
well before all 'S' runlevel tasks are started. This could be used with
system images that are created read-only and all configuration is stored
on external media.
Runlevels Runlevels
@@ -373,197 +244,48 @@ previous runlevel again — that way Finit can both stop old services and
start any new ones for you, without rebooting the system. start any new ones for you, without rebooting the system.
Inetd
-----
A built-in *Internet Super Server* support was added in Finit v1.12 and
v1.13, along with an internal `time` inetd service, RFC 868 (rdate).
The latter is supplied as a plugin to illustrate how simple it is to
extend finit with more internal inetd services.
> Please note, not all UNIX daemons are prepared to run as inetd services.
> In the example below `sshd` also need the command line argument `-i`.
The inetd support in finit is quite advanced. Not only does it launch
services on demand, it can do so on custom ports and also filter inbound
traffic using a poor man's [TCP wrappers][]. The syntax is very similar
to the traditional `/etc/inetd.conf`, yet keeping with the style of
Finit:
```shell
# Launch SSH on demand, in runlevels 2-5 as root
inetd ssh/tcp nowait [2345] @root:root /usr/sbin/sshd -i
```
A more advanced example is listed below, please note the *incompatible
syntax change* that was made between Finit v1.12 and v1.13 to support
deny filters:
```shell
# Start sshd if inbound connection on eth0, port 222, or
# inbound on eth1, port 22. Ignore on other interfaces.
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
inetd ssh/tcp@eth1,eth1 nowait [2345] /usr/sbin/sshd -i
```
If `eth0` is your Internet interface you may want to avoid using the
default port. To run ssh on port 222, and all others on port 22:
```shell
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
inetd ssh/tcp@*,!eth0 nowait [2345] /usr/sbin/sshd -i
```
Compared to Finit v1.12 you must *explicitly deny* access from `eth0`!
**Internal Services**
The original `inetd` had a few standard services built-in:
- time
- echo
- chargen
- discard
Finit only supports the `time` service. This is realized as a plugin to
provide a simple means of testing the inetd functionality stand-alone,
but also as a very rudimentary time server for rdate clients.
The `time.so` inetd plugin is set up as follows. Notice the keyword
`internal` which applies to all built-in inetd services:
```shell
inetd time/tcp nowait [2345] internal
```
Then call `rdate` from a remote machine (or use localhost):
```shell
rdate -p <IP>
```
If you use `time/udp` you must use the standard rdate implementation and
then call it with `rdate -up` to connect using UDP. Without the `-p`
argument rdate will try to set the system clock. Please note that rdate
has been deprecated by the NTP protocol and this plugin should only be
used for testing or environments where NTP for some reason is blocked.
Also, remember the UNIX year 2038 bug, or in the case of RFC 868 (and
some NTP implementations), year 2036!
**Note:** There is currently no verification that the same port is used
more than once. So a standard `inetd http/tcp` service will clash
with an ssh entry for the same port `inetd 80/tcp` …
Hooks, Callbacks & Plugins
--------------------------
Finit provides only the bare necessities for starting and supervising
processes, with an emphasis on *bare* — for your convenience it does
however come with support for hooks, service callbacks and plugins that
can used to extend finit with.
### Plugins
For your convenience a set of *optional* plugins are available:
* *alsa-utils.so*: Restore and save ALSA sound settings on
startup/shutdown.
* *bootmisc.so*: Setup necessary files for UTMP, tracks logins at boot.
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
* *hwclock.so*: Restore and save system clock from/to RTC on
startup/shutdown.
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
interfaces. These events are then sent to the Finit service monitor
for services that may want to be SIGHUP'ed on new default route or
interfaces going up/down.
* *resolvconf.so*: Setup necessary files for `resolvconf` at startup.
* *time.so*: RFC 868 (rdate) plugin. Start as inetd service. Useful
for testing inetd filtering — BusyBox has an rdate (TCP) client.
* *tty.so*: Watches `/dev`, using inotify, for new device nodes (TTY's)
to start/stop getty consoles on them on demand. Useful when plugging
in a usb2serial converter to login to your embedded device.
* *urandom.so*: Setup random seed at startup.
* *x11-common.so*: Setup necessary files for X-Window.
Usually you want to hook into the boot process once, simple hook plugins
like `bootmisc.so` are great for that purpose. They are called at each
hook point in the boot process, useful to insert some pre-bootstrap
mechanisms, like generating configuration files, restoring HW device
state, etc. Available hook points are:
### Bootstrap Hooks
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
mounted — very early
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
and default init signals are setup
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
networking is up (`lo` is up and the `network` script has run)
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
* `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d`
has been launched
### Runtime Hooks
* `HOOK_SVC_RECONF`: Called when the user has changed something in the
`/etc/finit.d` directory and issued `SIGHUP`. The hook is called when
all modified/removed services have been stopped. When the hook has
completed, Finit continues to start all modified and new services.
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
change. The hook is called when services not matching the new
runlevel have been been stopped. When the hook has completed, Finit
continues to start all services in the new runlevel.
### Shutdown Hooks
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
services are sent `SIGTERM`
Plugins like `initctl.so` and `tty.so` extend finit by acting on events,
they are called I/O plugins and are called from the finit main loop when
`poll()` detects an event. See the source code for `plugins/*.c` for
more help and ideas.
### Callbacks
Callback plugins are called by finit right before a process is started,
or restarted if it exits. The callback runs as a separate process and
receives a pointer to the `svc_t` of the service, with all command line
parameters free to modify as needed.
All the callback needs to do is respond with one of: `SVC_STOP (0)`
tells Finit to *not* start the service, `SVC_START (1)` to start the
service, or `SVC_RELOAD (2)` to have finit signal the process with
`SIGHUP`.
Rebooting & Halting Rebooting & Halting
------------------- -------------------
Finit handles `SIGUSR1` and `SIGUSR2` for reboot and halt, and listens Finit is often used on embedded and small Linux systems with BusyBox.
to `/dev/initctl` so system reboot and halt commands also work. The For compatibility with its existing toolset (halt, poweroff, reboot),
latter is implemented in the optional `initctl.so` plugin and can be the following signals have been adopted:
accessed with the traditional `telinit` command line tool, symlinked to
`finit`. Hence, if finit is your system init, then `init q` will work * `SIGUSR1`
as the UNIX beards intended. Same effect as `finit q`, reloads all *.conf files in `/etc/finit.d/`
* `SIGUSR1`
Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
processes, and unmounts all file systems. Then tells kernel to
halt. Most people these days want SIGUSR2 though.
SysV init and systemd use this to re-open their FIFO/D-Bus.
* `SIGUSR2`
Like SIGUSR1, but tell kernel to power-off the system, if ACPI
or similar exists to actually do this. If the kernel fails
power-off, Finit falls back to halt.
SysV init N/A, systemd dumps its internal state to log.
* `SIGTERM`
Like `SIGUSR1`, but tell kernel to reboot the system when done.
SysV init N/A, systemd rexecutes itself.
* `SIGINT`
Sent from kernel when the CTRL-ALT-DEL key combo is pressed.
SysV init and systemd default to reboot with `shutdown -r`.
Finit currently forwards this to `SIGTERM`.
* `SIGPWR`
Sent from a power daemon, like `powstatd(8)`, on changes to the
UPS status. Traditionally SysV init read /etc/powerstatus and
acted on "OK", "FAIL", or "LOW" and then removed the file.
Finit currently forwards this to `SIGUSR2`.
Finit also listens to the classic SysV init FIFO, which used to be
located in `/dev` but these days default to `/run/initctl`. So existing
tools like, `poweroff`, `halt`, `reboot`, and `shutdown` work as
expected. Support for this old-style handling is implemented in the
optional `initctl.so` plugin and can be accessed with the traditional
`telinit` command line tool, symlinked to `finit`. Hence, if finit is
your system init, then `init q` will work as the UNIX beards intended.
```shell ```shell
~ $ telinit ~ $ telinit
@@ -598,13 +320,13 @@ not return until the given command has fully completed.
Commands: Commands:
debug Toggle Finit (daemon) debug debug Toggle Finit (daemon) debug
help This help text help This help text
emit <EV> Emit event; a predefined event: RELOAD, STOP, START
or a custom string matching an event in a service
stanza, e.g: GW:UP, IFUP:IFNAME, IFDN:IFNAME. Where
IFNAME is the interface name, e.g. eth0
reload Reload *.conf in /etc/finit.d/ and activate changes reload Reload *.conf in /etc/finit.d/ and activate changes
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
status | show Show status of services status | show Show status of services
cond set <COND> Set (assert) condition => +COND
cond clear <COND> Clear (deassert) condition => -COND
cond flux <COND> Emulate flux condition => ~COND
cond show Show condition status
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID 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 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 restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
@@ -612,55 +334,6 @@ not return until the given command has fully completed.
version Show Finit version version Show Finit version
``` ```
The `emit <EV>` command can be used to send events to Finit. Built-in
events are: RELOAD, STOP, START. These events act on a lower level than
their command counterparts. The `reload` command reloads all `*.conf`
files in `/etc/finit.d/` *and* activates the changes, with `emit RELOAD`
only the `*.conf` files are reloaded.
**Note:** The `emit STOP` event is more like "prepare" than "stop".
Depending on how the service is declared, `<!>` or not, the service
may be stopped, or skipped to be be sent `SIGHUP` later when `emit
START` is issued.
To achieve the same result as `initctl reload` emit all three events:
```shell
~ $ initctl emit "RELOAD,STOP,START"
```
On an embedded system this can be used when changing complete system
configuration. Between `STOP` and `START` reset/change any hardware
or kernel settings required to be in effect before new services are
started.
```shell
# 1. Update/Change/Generate all daemon configuration files
~ $ …
# 2. Update /etc/finit.d/*.conf
~ $ …
# 3. Prepare finit, stop all old/previous services
~ $ initctl emit "RELOAD,STOP"
# 4. Reconfigure hardware, interfaces/bridges/VLANs, etc.
~ $ brctl …
~ $ vconfig …
~ $ ifconfig …
# 5. Complete finit reload by starting all new services and
# SIGHUP any changed services
~ $ initctl emit "START"
```
The `emit <EV>` command can also be used to emit custom events. In
fact, the event is a simple string. Declare a list of events in a
service stanza: `service … <GW:UP,IFUP:eth0>` to reload (`SIGHUP`) a
service when recieving the `"GW:UP"` or `"IFUP:eth0"` strings. If a
service cannot handle reload and must be stopped-started, simply add an
exclamation mark first: `service … <!GW:UP,IFUP:eth0>`.
The `<!>` notation to a service stanza can be used empty, then it will The `<!>` notation to a service stanza can be used empty, then it will
apply to `reload` and `runlevel` commands. I.e., when a service's apply to `reload` and `runlevel` commands. I.e., when a service's
`.conf` file has been changed Finit will stop and start it instead. If `.conf` file has been changed Finit will stop and start it instead. If
@@ -671,8 +344,7 @@ behaviour, but not all daemons support this, unfortunately.
**Note:** even though it is possible to start services not belonging to **Note:** even though it is possible to start services not belonging to
the current runlevel these services will not be respawned automatically the current runlevel these services will not be respawned automatically
by Finit if they exit (crash). Hence, if the runlevel is 2, the below by Finit if they exit (crash). Hence, if the runlevel is 2, the below
Dropbear SSH service will not be restarted if it, for some reason, Dropbear SSH service will not be restarted if it is killed or exits.
exits.
```shell ```shell
~ $ initctl status -v ~ $ initctl status -v
@@ -688,140 +360,24 @@ exits.
6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0 6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
``` ```
Building
--------
Finit comes with a lightweight configure script to control which
features to enable an plugins to build. Below are a few of the main
switches to configure:
* `--prefix=`: Base prefix path for all files, except `--sbindir` and
`--sysconfdir`. Used in concert with the `DESTDIR` variable.
Defaults to `/usr`.
* `--sbindir=`: Path to where resulting binaries should install to. Used
in concert with the `DESTDIR` variable.
Defaults to `/sbin`.
* `--sysconfdir=`: Path to where finit configuration files should
install to. Used in concert with the `DESTDIR` variable.
Defaults to `/etc`, but is currently unused.
* `--plugindir=`: Absolute path to where finit should search for
dynamically loadable plugins at runtime. Can also be controlled
with the `--libdir` switch.
Defaults to `/lib/finit/plugins`.
* `--with-plugins=`: List of stock finit plugins to build and install.
The build system adds `.o` or `.so` extension to the listed plugins
depending on the `--enable-static` switch. Hence, list the plugins
*without* their extension.
* `--enable-embedded`: Target finit for BusyBox getty and mdev instead
of a standard Linux distribution with GNU tools and udev.
* `--enable-debug`: Add GDB symbols and disable code optimization.
* `--enable-static`: Build Finit statically. The plugins will be
built-in (.o files) instead. Note: very untested and not all plugins
can be built static. It is recommended to use `--with-plugins` and
select only the plugins really needed.
* `--disable-inetd`: Disables the built-in inetd server.
The following environment variables are checked by the makefiles and
control what is built and where resulting binaries are installed.
* `ROOTDIR=`: Top directory for building complete system, used in pretty
printing at build time.
* `VERSION=`: Defaults to the currently released version of finit,
e.g., 1.3 but can be overridden by packagers using this variable to
add a suffix or completely alter the version.
* `CFLAGS=`: Default `CFLAGS` are inherited from the environment.
* `CPPFLAGS=`: Default `CPPFLAGS` are inherited from the environment.
* `LDFLAGS=`: Default `LDFLAGS` are inherited from the environment.
* `LDLIBS=`: Default `LIBLIBS` are inherited from the environment.
* `DESTDIR=`: Used by packagers and distributions when building a
relocatable bundle of files. Always prepended to the `prefix`
destination directory.
**Example**
First, unpack the archive:
```shell
$ tar xfJ finit-1.13.tar.xz
$ cd finit-1.13/
```
Then configure, build and install:
```shell
$ ./configure --with-plugins="initctl hwclock"
$ make
CC finit.o
CC conf.o
CC helpers.o
CC sig.o
CC svc.o
CC plugin.o
CC strlcpy.o
LINK finit
CC plugins/initctl.o
PLUGIN plugins/initctl.so
CC plugins/hwclock.o
PLUGIN plugins/hwclock.so
$
$ DESTDIR=/tmp/finit make install
INSTALL /tmp/finit/sbin/finit
INSTALL /tmp/finit/lib/finit/plugins/initctl.so
INSTALL /tmp/finit/lib/finit/plugins/hwclock.so
```
In this example the [finit-1.13.tar.xz][10] archive is unpacked to the
user's home directory, configured, built and installed to a temporary
staging directory. The environment variable `DESTDIR` controls the
destination directory when installing, very useful for building binary
standalone packages.
To target an embedded Linux system, usally a system that use BusyBox
tools instead of udev & C:o, add <kbd>--enable-embedded</kbd> to the
configure command above.
For more configure flags, see <kbd>./configure --help</kbd>
**Note:** If you run into problems starting Finit, take a look at
`finit.c`. One of the most common problems is a custom Linux kernel
build that lack `CONFIG_DEVTMPFS`. Another is too much cruft in the
system `/etc/fstab`.
Running Running
------- -------
The default install does not setup finit as the system default Having successfully [built Finit](doc/build.md) it may now be time to
`/sbin/init`, neither does it setup an initial `/etc/finit.conf`. take it for a test drive. The `make install` attempts to set up finit
as the system system init, `/sbin/init`, but this is usually a symlink
pointing to the current init.
It is assumed that users of finit are competent enough to either setup So either change the symlink, or change your boot loader (GRUB, LOADLIN,
finit as their default `/sbin/init` or alter their respective Grub, LILO, U-Boot/Barebox or RedBoot) configuration to append the following
LOADLIN, LILO, U-Boot/Barebox or RedBoot boot loader configuration to to the kernel command line:
give the kernel the following extra command line:
```shell ```shell
init=/sbin/finit init=/sbin/finit
``` ```
Remember to also set up an initial `/etc/finit.conf` before rebooting!
![Finit starting Debian 6.0](images/finit-screenshot.jpg "Finit screenshot") ![Finit starting Debian 6.0](images/finit-screenshot.jpg "Finit screenshot")
@@ -848,17 +404,13 @@ Finit is developed and maintained by [Joachim Nilsson][] at [GitHub][].
Please file bug reports, clone it, or send pull requests for bug fixes Please file bug reports, clone it, or send pull requests for bug fixes
and proposed extensions. and proposed extensions.
[1]: https://en.wikipedia.org/wiki/Process_supervision [1]: https://en.wikipedia.org/wiki/Process_supervision
[2]: http://cr.yp.to/daemontools.html [2]: http://cr.yp.to/daemontools.html
[3]: http://smarden.org/runit/ [3]: http://smarden.org/runit/
[4]: http://en.wikipedia.org/wiki/Init [4]: http://en.wikipedia.org/wiki/Init
[5]: http://en.wikipedia.org/wiki/Runlevel [5]: http://en.wikipedia.org/wiki/Runlevel
[6]: http://upstart.ubuntu.com/
[7]: http://www.freedesktop.org/wiki/Software/systemd/
[8]: http://www.gentoo.org/proj/en/base/openrc/
[9]: https://github.com/troglobit/troglos [9]: https://github.com/troglobit/troglos
[10]: ftp://troglobit.com/finit/finit-1.13.tar.xz
[TCP Wrappers]: https://en.wikipedia.org/wiki/TCP_Wrapper
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[original finit]: http://helllabs.org/finit/ [original finit]: http://helllabs.org/finit/
[EeePC fastinit]: http://wiki.eeeuser.com/boot_process:the_boot_process [EeePC fastinit]: http://wiki.eeeuser.com/boot_process:the_boot_process
-1
View File
@@ -1 +0,0 @@
TODO.md
+20 -24
View File
@@ -18,29 +18,29 @@ very low dependency on external packages.
General General
------- -------
* Add compile-time support for running `/bin/sh` instead of `getty` * Add halt/poweroff/shutdown symlinks to finit and convert reboot, but
* Add `!` or `[wait <pidfile>]` to service directive to let finit pause only install symlinks if these tools don't already exist.
and wait for a pidfile before starting processes -- both at boot and * Step state machine at shutdown instead of killing, same as shifting to
when starting processes in a runlevel. runlevel 0 or 6, then call external (newly installed?) tool to reboot.
* Allow custom reload for processes like libreSwan's pluto * Add support for timed shutdown in Finit, including cancelled shutdown.
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
service [2345] <!gw,if:eth0,reload:'starter reload'> /sbin/pluto API restart to SIGHUP callback for the same functionality in Finit.
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Add `IFF_RUNNING` support to netlink plugin along `IFF_IUP`
* Allow process callbacks to be scripts. * Allow process callbacks to be scripts.
* Add support for a pipe/popen where we can listen for `STOP`, `START` * Add support for a pipe/popen where we can listen for `STOP`, `START`
and `RELOAD` on `STDOUT` from a script ... call it for example and `RELOAD` on `STDOUT` from a script ... call it for example
`.monitor=` in `svc_t` ... the script named as the basename of the `.monitor=` in `svc_t` ... the script named as the basename of the
service it monitors + `.sh`. service it monitors + `.sh`.
* Add support for `/etc/sysctl.d`
* Implement `initctl stop|start|restart|reload|status <SVC>` and * Implement `initctl stop|start|restart|reload|status <SVC>` and
`service <SVC> stop|start|restart|reload|status` on top `service <SVC> stop|start|restart|reload|status` on top
* Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json`
* Add PRE and POST hooks for when switching between runlevels * Add PRE and POST hooks for when switching between runlevels
* Make sure to install `queue.h` to `$(PREFIX)/include/finit/queue.h`
* Move `finit.conf` "check" command to plugin which checks `/etc/fstab`
instead. This is the de-facto practice. But keep the check command
for really low-end systems w/o `/etc/fstab`.
* Add support for generic events (strings or ubus events) that are passed
verbatim (?) to the service stanza definitions.
Init Init
@@ -55,6 +55,8 @@ to achieve compatibility would be to add a plugin to finit which reads
Inetd Inetd
----- -----
Add support for throttling connections.
Optimize interface filtering by using socket filter. The functions Optimize interface filtering by using socket filter. The functions
`inet_*_peek()` and `inetd_is_allowed()` used for interace filtering `inet_*_peek()` and `inetd_is_allowed()` used for interace filtering
should be possible to rewrite as socket filters. should be possible to rewrite as socket filters.
@@ -109,7 +111,7 @@ Support for monitoring the health of the system and its processes.
- If a process is not heard from within its subscribed period time - If a process is not heard from within its subscribed period time
reboot system or restart process. reboot system or restart process.
- Add client libwdt library, inspired by old Westermo API and the - Add client libwdt library, inspired by old Westermo API and the
[libwdt] API published in the [Fritz!Box source dump], take for [libwdt][] API published in the [Fritz!Box source dump][], take for
instance the `fritzbox7170-source-files-04.87-tar.gz` drop and see instance the `fritzbox7170-source-files-04.87-tar.gz` drop and see
the `GPL-release_kernel.tgz` in `drivers/char/avm_new/` for the the `GPL-release_kernel.tgz` in `drivers/char/avm_new/` for the
kernel driver, and the `LGPL-GPL-release_target_tools.tgz` in `wdt/` kernel driver, and the `LGPL-GPL-release_target_tools.tgz` in `wdt/`
@@ -134,16 +136,10 @@ Documentation
* Write man pages for finit and `finit.conf`, steal from the excellent * Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ... `pimd` man pages ...
* Update Debian `finit.conf` example with runlevels, `tty/console` and * Add simple, *very* simple, `finit-simple.conf` example. To illustrate
dependency handling for Debian 8 (the dreaded systemd release). how close Finit3 still is to the original easy-to-use Finit0 :)
Investigation
-------------
* [FHS changes](http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run)
affecting runtime status, plugins, etc.
[libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so [libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so
[Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/ [Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/
+54 -29
View File
@@ -29,24 +29,24 @@
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <lite/lite.h>
#include <uev/uev.h>
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
#include "cond.h"
#include "conf.h" #include "conf.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "sig.h" #include "sig.h"
#include "service.h" #include "service.h"
#include "libite/lite.h"
#include "libuev/uev.h"
uev_t api_watcher; uev_t api_watcher;
/* Allowed characters in job/id/name */ /* Allowed characters in job/id/name */
static int isallowed(int ch) static int isallowed(int ch)
{ {
return isalnum(ch) || isspace(ch) || ch == ':'; return isprint(ch);
} }
/* Sanitize user input, make sure to NUL terminate. */ /* Sanitize user input, make sure to NUL terminate. */
@@ -113,14 +113,31 @@ static int call(int (*action)(svc_t *), char *buf, size_t len)
return result; return result;
} }
static int service_pause(svc_t *svc) static int service_block(svc_t *svc)
{ {
return service_stop(svc, SVC_PAUSED_STATE); svc_block(svc);
service_step(svc);
return 0;
} }
static int do_start (char *buf, size_t len) { return call(service_start, buf, len); } static int service_unblock(svc_t *svc)
static int do_pause (char *buf, size_t len) { return call(service_pause, buf, len); } {
static int do_reload (char *buf, size_t len) { return call(service_reload, buf, len); } svc_unblock(svc);
service_step(svc);
return 0;
}
static int service_restart(svc_t *svc)
{
svc_mark_dirty(svc);
service_step(svc);
return 0;
}
static int do_start (char *buf, size_t len) { return call(service_unblock, buf, len); }
static int do_pause (char *buf, size_t len) { return call(service_block, buf, len); }
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); } static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
#ifndef INETD_DISABLED #ifndef INETD_DISABLED
@@ -155,9 +172,7 @@ typedef struct {
} ev_t; } ev_t;
ev_t ev_list[] = { ev_t ev_list[] = {
{ "RELOAD", conf_reload_dynamic }, { "RELOAD", service_reload_dynamic },
{ "STOP", service_stop_dynamic },
{ "START", service_start_dynamic },
{ NULL, NULL } { NULL, NULL }
}; };
@@ -175,9 +190,13 @@ static int do_handle_event(char *event)
} }
} }
/* XXX: iterate over all services' events before failing. */ if (event[0] == '-')
cond_clear(&event[1]);
return -1; else if (event[0] == '+')
cond_set(&event[1]);
else
cond_set(event);
return 0;
} }
static int do_handle_emit(char *buf, size_t len) static int do_handle_emit(char *buf, size_t len)
@@ -229,7 +248,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
} }
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) { if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
_e("Invalid initctl request."); _e("Invalid initctl request");
break; break;
} }
@@ -238,7 +257,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
switch (rq.runlevel) { switch (rq.runlevel) {
case '0': case '0':
_d("Halting system (SIGUSR2)"); _d("Halting system (SIGUSR2)");
do_shutdown(SIGUSR2); do_shutdown(SHUT_OFF);
break; break;
case 's': case 's':
@@ -255,7 +274,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
case '6': case '6':
_d("Rebooting system (SIGUSR1)"); _d("Rebooting system (SIGUSR1)");
do_shutdown(SIGUSR1); do_shutdown(SHUT_REBOOT);
break; break;
default: default:
@@ -267,9 +286,9 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
case INIT_CMD_DEBUG: case INIT_CMD_DEBUG:
debug = !debug; debug = !debug;
if (debug) if (debug)
verbose = 1; silent = 0;
else else
verbose = quiet ? 0 : VERBOSE_MODE; silent = quiet ? 1 : SILENT_MODE;
break; break;
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */ case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
@@ -284,10 +303,6 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
result = do_pause(rq.data, sizeof(rq.data)); result = do_pause(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RELOAD_SVC:
result = do_reload(rq.data, sizeof(rq.data));
break;
case INIT_CMD_RESTART_SVC: case INIT_CMD_RESTART_SVC:
result = do_restart(rq.data, sizeof(rq.data)); result = do_restart(rq.data, sizeof(rq.data));
break; break;
@@ -302,8 +317,13 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
result = do_handle_emit(rq.data, sizeof(rq.data)); result = do_handle_emit(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_GET_RUNLEVEL:
rq.runlevel = runlevel;
result = 0;
break;
case INIT_CMD_ACK: case INIT_CMD_ACK:
_d("Client failed reading ACK."); _d("Client failed reading ACK");
goto leave; goto leave;
default: default:
@@ -317,7 +337,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
rq.cmd = INIT_CMD_ACK; rq.cmd = INIT_CMD_ACK;
len = write(sd, &rq, sizeof(rq)); len = write(sd, &rq, sizeof(rq));
if (len != sizeof(rq)) if (len != sizeof(rq))
_d("Failed sending ACK/NACK back to client."); _d("Failed sending ACK/NACK back to client");
} }
leave: leave:
@@ -332,7 +352,7 @@ int api_init(uev_ctx_t *ctx)
.sun_path = INIT_SOCKET, .sun_path = INIT_SOCKET,
}; };
sd = socket(AF_UNIX, SOCK_STREAM, 0); sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (-1 == sd) { if (-1 == sd) {
_pe("Failed starting external API socket"); _pe("Failed starting external API socket");
return 1; return 1;
@@ -345,7 +365,8 @@ int api_init(uev_ctx_t *ctx)
if (-1 == listen(sd, 10)) if (-1 == listen(sd, 10))
goto error; goto error;
return uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ); if (!uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ))
return 0;
error: error:
_pe("Failed intializing API socket"); _pe("Failed intializing API socket");
@@ -353,10 +374,14 @@ error:
return 1; return 1;
} }
int api_exit(void)
{
return shutdown(api_watcher.fd, SHUT_RDWR) ||
close(api_watcher.fd);
}
/** /**
* Local Variables: * Local Variables:
* version-control: t
* indent-tabs-mode: t * indent-tabs-mode: t
* c-file-style: "linux" * c-file-style: "linux"
* End: * End:
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
autoreconf -W portability -vifm
+69 -30
View File
@@ -27,38 +27,70 @@
#include <ctype.h> #include <ctype.h>
#include <getopt.h> #include <getopt.h>
#include <stdio.h> #include <stdio.h>
#include <lite/lite.h>
#include "finit.h" #include "finit.h"
#include "libite/lite.h"
static int do_send(int cmd, int runlevel) static int do_send(struct init_request *rq)
{ {
int fd; static int fd = -1;
int result = 255; int result = 255;
ssize_t len; ssize_t len;
if (fd == -1) {
if (!fexist(FINIT_FIFO)) {
fprintf(stderr, "%s does not support %s!\n", __progname, FINIT_FIFO);
return 1;
}
fd = open(FINIT_FIFO, O_RDWR);
if (-1 == fd) {
perror("Failed opening " FINIT_FIFO);
return 1;
}
}
len = write(fd, rq, sizeof(*rq));
if (sizeof(*rq) == len)
result = 0;
return result;
}
static int do_reload(void)
{
struct init_request rq = { struct init_request rq = {
.magic = INIT_MAGIC, .magic = INIT_MAGIC,
.cmd = cmd, .cmd = INIT_CMD_RELOAD,
};
return do_send(&rq);
}
static int set_env(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SETENV,
};
if (runlevel == 0) {
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
return do_send(&rq);
}
return 0;
}
static int set_runlevel(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = runlevel, .runlevel = runlevel,
}; };
if (!fexist(FINIT_FIFO)) { return set_env(runlevel) || do_send(&rq);
fprintf(stderr, "%s does not support %s!\n", __progname, FINIT_FIFO);
return 1;
}
fd = open(FINIT_FIFO, O_RDWR);
if (-1 == fd) {
perror("Failed opening " FINIT_FIFO);
return 1;
}
len = write(fd, &rq, sizeof(rq));
if (sizeof(rq) == len)
result = 0;
close(fd);
return result;
} }
/* telinit q | telinit <0-9> */ /* telinit q | telinit <0-9> */
@@ -66,11 +98,17 @@ static int usage(int rc)
{ {
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n" fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n" "Options:\n"
" -h, --help This help text\n\n" " -h, --help This help text\n"
" -V, --version Show Finit version\n\n" " -V, --version Show Finit version\n\n"
"Commands:\n" "Commands:\n"
" q | Q Reload *.conf in /etc/finit.d/, like SIGHUP\n" " 0 Power-off the system, same as initctl poweroff\n"
" 0 - 9 Change runlevel: 0 halt, 6 reboot\n\n", __progname); " 6 Reboot the system, same as initctl reboot\n"
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
" services in prev. runlevel that are not allowed in new.\n"
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
" sending SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", __progname);
return rc; return rc;
} }
@@ -84,7 +122,7 @@ int client(int argc, char *argv[])
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };
verbose = 0; silent = 1;
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) { while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
switch(c) { switch(c) {
case 'v': case 'v':
@@ -99,12 +137,14 @@ int client(int argc, char *argv[])
if (optind < argc) { if (optind < argc) {
int req = (int)argv[optind][0]; int req = (int)argv[optind][0];
/* Compat: 'init <0-9>' */
if (isdigit(req)) if (isdigit(req))
return do_send(INIT_CMD_RUNLVL, req); return set_runlevel(req);
if (req == 'q' || req == 'Q') if (req == 'q' || req == 'Q')
return do_send(INIT_CMD_RELOAD, 0); return do_reload();
if (req == 's' || req == 'S')
return set_runlevel(1);
} }
return usage(1); return usage(1);
@@ -112,7 +152,6 @@ int client(int argc, char *argv[])
/** /**
* Local Variables: * Local Variables:
* version-control: t
* indent-tabs-mode: t * indent-tabs-mode: t
* c-file-style: "linux" * c-file-style: "linux"
* End: * End:
+7 -4
View File
@@ -1,13 +1,16 @@
# Top directory for building complete system, fall back to this directory # Top directory for building complete system, fall back to this directory
ROOTDIR ?= $(TOPDIR) ROOTDIR ?= $(TOPDIR)
# Strip binaries unless user explicitly disables it
STRIPARGS ?= -s --strip-program=$(CROSS)strip -m 0755
# Some junk files we always want to be removed when doing a make clean. # Some junk files we always want to be removed when doing a make clean.
JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.* JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.*
MAKE := @$(MAKE) MAKE := @$(MAKE)
MAKEFLAGS = --no-print-directory --silent MAKEFLAGS = --no-print-directory --silent
CHECK := cppcheck $(CPPFLAGS) --quiet --enable=all CHECK := cppcheck $(CPPFLAGS) --quiet --enable=all
INSTALL := install --backup=off INSTALL := install --backup=off
STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755 STRIPINST := $(INSTALL) $(STRIPARGS)
ARFLAGS := crus ARFLAGS := crus
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC
@@ -15,13 +18,13 @@ export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC
# Override default implicit rules # Override default implicit rules
%.o: %.c %.o: %.c
@printf " CC $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n" @printf " CC $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(CFLAGS) $(CPPFLAGS) -c -MMD -MP -o $@ $< @$(CC) $(CFLAGS) $(CPPFLAGS) -c -MMD -MP -o $@ $<
%: %.o %: %.o
@printf " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n" @printf " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@))) @$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@)))
%.so: %.o %.so: %.o
@printf " PLUGIN $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n" @printf " PLUGIN $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) @$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+135
View File
@@ -0,0 +1,135 @@
/* Condition engine (write)
*
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <libgen.h>
#include <lite/lite.h>
#include <stdio.h>
#include <utime.h>
#include "finit.h"
#include "cond.h"
#include "service.h"
int cond_set_path(const char *path, enum cond_state new)
{
char buf[MAX_ARG_LEN], *dir;
enum cond_state old;
old = cond_get_path(path);
switch (new) {
case COND_ON:
strlcpy(buf, path, sizeof(buf));
dir = dirname(buf);
if (!dir) {
_e("Invalid path '%s' for condition", path);
return 0;
}
if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 0;
}
touch(path);
utime(path, NULL);
break;
case COND_OFF:
unlink(path);
break;
default:
_e("Invalid condition state");
return 0;
}
return new != old;
}
/* Has condition in configuration and cond is allowed? */
static int svc_has_cond(svc_t *svc)
{
if (!svc->cond[0])
return 0;
switch (svc->type) {
case SVC_TYPE_SERVICE:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
return 1;
default:
break;
}
return 0;
}
static void cond_update(const char *name)
{
svc_t *svc;
_d("%s", name);
if (!name)
return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue;
_d("%s: match <%s> %s", name, svc->cond, svc->cmd);
service_step(svc);
}
}
void cond_set(const char *name)
{
_d("%s", name);
if (!cond_set_path(cond_path(name), COND_ON))
return;
cond_update(name);
}
void cond_clear(const char *name)
{
_d("%s", name);
if (!cond_set_path(cond_path(name), COND_OFF))
return;
cond_update(name);
}
void cond_reload(void)
{
_d("");
cond_set_path(COND_RECONF, COND_ON);
cond_update(NULL);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+117
View File
@@ -0,0 +1,117 @@
/* Condition engine (read)
*
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <lite/lite.h>
#include <stdio.h>
#include "finit.h"
#include "cond.h"
#include "service.h"
static inline int timespec_newer(const struct timespec *a,
const struct timespec *b)
{
if (a->tv_sec != b->tv_sec)
return a->tv_sec > b->tv_sec;
return a->tv_nsec > b->tv_nsec;
}
const char *condstr(enum cond_state s)
{
static const char *strs[] = {
[COND_OFF] = "off",
[COND_FLUX] = "flux",
[COND_ON] = "on",
};
return strs[s];
}
const char *cond_path(const char *name)
{
static char file[MAX_ARG_LEN];
snprintf(file, sizeof(file), COND_PATH "/%s", name);
return file;
}
enum cond_state cond_get_path(const char *path)
{
struct stat st, st_reconf;
if (stat(path, &st))
return COND_OFF;
if (stat(COND_RECONF, &st_reconf) ||
timespec_newer(&st.st_mtim, &st_reconf.st_mtim))
return COND_ON;
return COND_FLUX;
}
enum cond_state cond_get(const char *name)
{
return cond_get_path(cond_path(name));
}
enum cond_state cond_get_agg(const char *names)
{
char *cond;
enum cond_state s = COND_ON;
static char conds[MAX_ARG_LEN];
if (!names)
return COND_ON;
strlcpy(conds, names, sizeof(conds));
for (cond = strtok(conds, ","); s && cond; cond = strtok(NULL, ","))
s = min(s, cond_get(cond));
return s;
}
int cond_affects(const char *name, const char *names)
{
char *cond;
static char conds[MAX_ARG_LEN];
if (!name || !names)
return 0;
strlcpy(conds, names, sizeof(conds));
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
if (!strcmp(cond, name))
return 1;
}
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+35
View File
@@ -0,0 +1,35 @@
#ifndef FINIT_COND_H_
#define FINIT_COND_H_
#include <paths.h>
#define COND_PATH _PATH_VARRUN "finit/cond"
#define COND_SVC_PATH COND_PATH "/svc/"
#define COND_RECONF COND_PATH "/reconf"
typedef enum cond_state {
COND_OFF = 0,
COND_FLUX,
COND_ON
} cond_state_t;
const char *condstr (enum cond_state s);
const char *cond_path (const char *name);
enum cond_state cond_get_path(const char *path);
enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names);
int cond_set_path(const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_clear (const char *name);
void cond_reload (void);
#endif /* FINIT_COND_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+123 -49
View File
@@ -23,14 +23,15 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <string.h>
#include <sys/resource.h>
#include <lite/lite.h>
#include "finit.h" #include "finit.h"
#include "cond.h"
#include "service.h" #include "service.h"
#include "tty.h" #include "tty.h"
#include "libite/lite.h"
#include "helpers.h" #include "helpers.h"
#define MATCH_CMD(l, c, x) \ #define MATCH_CMD(l, c, x) \
@@ -39,40 +40,22 @@
static int parse_conf(char *file); static int parse_conf(char *file);
static char *strip_line(char *line)
{
char *ptr;
/* Trim leading whitespace */
while (*line && isblank(*line))
line++;
/* Strip any comment at end of line */
ptr = line;
while (*ptr && *ptr != '#')
ptr++;
*ptr = 0;
return line;
}
void conf_parse_cmdline(void) void conf_parse_cmdline(void)
{ {
FILE *fp; FILE *fp;
char line[LINE_SIZE]; char line[LINE_SIZE];
if ((fp = fopen("/proc/cmdline", "r")) != NULL) { fp = fopen("/proc/cmdline", "r");
if (fp) {
fgets(line, sizeof(line), fp); fgets(line, sizeof(line), fp);
_d("Kernel command line: %s", line); _d("Kernel command line: %s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug")) if (strstr(line, "finit_debug") || strstr(line, "--debug"))
debug = 1; debug = 1;
#ifdef KERNEL_QUIET
if (!debug && strstr(line, "quiet")) if (!debug && strstr(line, "quiet"))
quiet = 1; quiet = 1;
else
quiet = 0;
#endif
fclose(fp); fclose(fp);
} }
} }
@@ -113,7 +96,7 @@ int conf_parse_runlevels(char *runlevels)
return bitmask; return bitmask;
} }
void conf_parse_events(svc_t *svc, char *events) void conf_parse_cond(svc_t *svc, char *cond)
{ {
size_t i = 0; size_t i = 0;
char *ptr; char *ptr;
@@ -124,13 +107,14 @@ void conf_parse_events(svc_t *svc, char *events)
} }
/* By default we assume UNIX daemons support SIGHUP */ /* By default we assume UNIX daemons support SIGHUP */
svc->sighup = 1; if (svc_is_daemon(svc))
svc->sighup = 1;
if (!events) if (!cond)
return; return;
/* First character must be '!' if SIGHUP is not supported. */ /* First character must be '!' if SIGHUP is not supported. */
ptr = events; ptr = cond;
if (ptr[i] == '!') { if (ptr[i] == '!') {
svc->sighup = 0; svc->sighup = 0;
ptr++; ptr++;
@@ -140,13 +124,98 @@ void conf_parse_events(svc_t *svc, char *events)
i++; i++;
ptr[i] = 0; ptr[i] = 0;
if (i >= sizeof(svc->events)) { if (i >= sizeof(svc->cond)) {
FLOG_WARN("Too long event list in declaration of %s: %s", svc->cmd, ptr); logit(LOG_WARNING, "Too long event list in declaration of %s: %s", svc->cmd, ptr);
return; return;
} }
svc->state = SVC_CONDHALT_STATE; strlcpy(svc->cond, ptr, sizeof(svc->cond));
strlcpy(svc->events, ptr, sizeof(svc->events)); }
struct rlimit_name {
char *name;
int val;
};
static const struct rlimit_name rlimit_names[] = {
{ "as", RLIMIT_AS },
{ "core", RLIMIT_CORE },
{ "cpu", RLIMIT_CPU },
{ "data", RLIMIT_DATA },
{ "fsize", RLIMIT_FSIZE },
{ "locks", RLIMIT_LOCKS },
{ "memlock", RLIMIT_MEMLOCK },
{ "msgqueue", RLIMIT_MSGQUEUE },
{ "nice", RLIMIT_NICE },
{ "nofile", RLIMIT_NOFILE },
{ "nproc", RLIMIT_NPROC },
{ "rss", RLIMIT_RSS },
{ "rtprio", RLIMIT_RTPRIO },
#ifdef RLIMIT_RTTIME
{ "rttime", RLIMIT_RTTIME },
#endif
{ "sigpending", RLIMIT_SIGPENDING },
{ "stack", RLIMIT_STACK },
{ NULL, 0 }
};
void conf_parse_rlimit(char *line)
{
struct rlimit rlim;
rlim_t new, *set;
const struct rlimit_name *name;
int resource = -1;
char *tok = strtok(line, " \t");
if (tok && !strcmp(tok, "soft"))
set = &rlim.rlim_cur;
else if (tok && !strcmp(tok, "hard"))
set = &rlim.rlim_max;
else
goto error;
tok = strtok(NULL, " \t");
if (!tok)
goto error;
for (name = rlimit_names; name->name; name++) {
if (!strcmp(tok, name->name))
resource = name->val;
}
if (resource < 0)
goto fail;
tok = strtok(NULL, " \t");
if (!tok)
goto fail;
if (!strcmp(tok, "infinity")) {
new = RLIM_INFINITY;
} else {
const char *err = NULL;
new = strtonum(tok, 0, 2 << 31, &err);
if (err)
goto fail;
}
if (getrlimit(resource, &rlim))
goto fail;
*set = new;
if (setrlimit(resource, &rlim))
goto fail;
return;
fail:
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
return;
error:
logit(LOG_WARNING, "rlimit: parse error");
} }
static void parse_static(char *line) static void parse_static(char *line)
@@ -301,6 +370,22 @@ static void parse_dynamic(char *line, time_t mtime)
#endif #endif
return; return;
} }
if (MATCH_CMD(line, "rlimit ", x)) {
conf_parse_rlimit(x);
return;
}
}
static void tabstospaces(char *line)
{
if (!line)
return;
for (int i = 0; line[i]; i++) {
if (line[i] == '\t')
line[i] = ' ';
}
} }
static int parse_conf_dynamic(char *file, time_t mtime) static int parse_conf_dynamic(char *file, time_t mtime)
@@ -319,6 +404,7 @@ static int parse_conf_dynamic(char *file, time_t mtime)
continue; continue;
chomp(line); chomp(line);
tabstospaces(line);
_d("dyn conf: %s", line); _d("dyn conf: %s", line);
parse_dynamic(line, mtime); parse_dynamic(line, mtime);
@@ -359,14 +445,16 @@ static int parse_conf(char *file)
strcat(line, " "); strcat(line, " ");
} }
if (verbose) if (!silent)
print(0, "Loading %sconfiguration", line); print(0, "Loading %sconfiguration", line);
while (!feof(fp)) { while (!feof(fp)) {
if (!fgets(line, sizeof(line), fp)) if (!fgets(line, sizeof(line), fp))
continue; continue;
chomp(line);
chomp(line);
tabstospaces(line);
_d("conf: %s", line); _d("conf: %s", line);
parse_static(line); parse_static(line);
parse_dynamic(line, 0); parse_dynamic(line, 0);
} }
@@ -425,28 +513,14 @@ void conf_reload_dynamic(void)
int conf_parse_config(void) int conf_parse_config(void)
{ {
int result;
username = strdup(DEFUSER); username = strdup(DEFUSER);
hostname = strdup(DEFHOST); hostname = strdup(DEFHOST);
result = parse_conf(FINIT_CONF); return parse_conf(FINIT_CONF);
if (!tty_num()) {
char *fallback = FALLBACK_SHELL;
if (console)
fallback = console;
tty_register(fallback);
}
return result;
} }
/** /**
* Local Variables: * Local Variables:
* version-control: t
* indent-tabs-mode: t * indent-tabs-mode: t
* c-file-style: "linux" * c-file-style: "linux"
* End: * End:
+1 -2
View File
@@ -28,7 +28,7 @@
void conf_parse_cmdline (void); void conf_parse_cmdline (void);
int conf_parse_runlevels (char *runlevels); int conf_parse_runlevels (char *runlevels);
void conf_parse_events (svc_t *svc, char *events); void conf_parse_cond (svc_t *svc, char *cond);
int conf_parse_config (void); int conf_parse_config (void);
void conf_reload_dynamic (void); void conf_reload_dynamic (void);
@@ -36,7 +36,6 @@ void conf_reload_dynamic (void);
/** /**
* Local Variables: * Local Variables:
* version-control: t
* indent-tabs-mode: t * indent-tabs-mode: t
* c-file-style: "linux" * c-file-style: "linux"
* End: * End:
Vendored
-451
View File
@@ -1,451 +0,0 @@
#!/bin/sh
# Simple home brew configure script, not as fancy as GNU Autotools
#
# --with-plugins="time initctl"
# --enable-static
# --disable-inetd
# --disable-plugins
#
# This creates a configuration where Finit is built statically, with the
# time and initctl plugins built-in, and no inetd support.
#
# Setup defaults if undefined
defaults()
{
heading=${heading:='"Finit v" VERSION'}
prefix=${prefix:="/usr"}
sysconfdir=${sysconfdir:="/etc"}
sbindir=${sbindir:="/sbin"}
libdir=${libdir:="/lib"}
pdir="finit/plugins"
plugindir=${plugindir:="$libdir/$pdir"}
# The initctl FIFO, should probably be in /run, not /dev
fifo=${fifo:="/dev/initctl"}
random=${random:="/var/lib/urandom/random-seed"}
conf="finit.conf"
config=${config:="$sysconfdir/$conf"}
confdir=${confdir:="finit.d"}
rcsd=${rcsd:="$sysconfdir/$confdir"}
console=${console:='_PATH_CONSOLE'}
shell=${shell:='_PATH_BSHELL'}
defuser=${defuser:="root"}
defhost=${defhost:="noname"}
runlevel=${runlevel:=2}
debug=${debug:=0}
embedded=${embedded:=0}
dbus=${dbus:=0}
remount=${remount:=0}
inetd=${inetd:=1}
verbose=${verbose:=1}
kernel_quiet=${kernel_quiet:=1}
static=${static:=0}
disable_plugins=${disable_plugins:=0}
}
result()
{
echo
echo "Finit configuration"
echo "========================================================================"
echo "Heading : $heading"
echo "Install prefix : $prefix"
echo "System config dir : $sysconfdir"
echo "Super user bindir : $sbindir"
echo "Shared libs dir : $libdir"
echo "Plugins dir : $plugindir"
echo "FIFO : $fifo"
echo "Config file : $config"
echo "rcs.d/ directory : $rcsd"
echo "Default console : $console"
# echo "Fallback shell : $shell"
if [ x"$sysroot" != x"" ]; then
echo "Swap rootfs to : $sysroot"
fi
echo "Default username : $defuser"
echo "Default hostname : $defhost"
echo "Default runlevel : $runlevel"
if [ $debug -ne 0 ]; then
echo "Debug : ENABLED"
fi
if [ $embedded -ne 0 ]; then
echo "Embedded target : YES (BusyBox getty, and mdev instead of udev)"
fi
if [ $dbus -ne 0 ]; then
echo "D-Bus : YES, remember to install dbus.so plugin as well!"
fi
if [ $remount -ne 0 ]; then
echo "Remount ROOT fs : YES, / will be remounted as read-write at bootstrap"
fi
if [ x"$random" != x"" ]; then
echo "Random seed : $random"
fi
if [ $inetd -ne 0 ]; then
echo "Inetd : Built-in"
else
echo "Inetd : DISABLED"
fi
if [ x"$libite" = x"" ]; then
echo "Libite (LITE) : Built-in"
else
echo "Libite (LITE) : $libite + {/lib, /include}"
fi
if [ x"$libuev" = x"" ]; then
echo "LibuEv : Built-in"
else
echo "LibuEv : $libuev + {/lib, /include}"
fi
if [ $verbose -ne 1 ]; then
echo "Quiet Finit : YES"
else
echo "Quiet Finit : NO, verbose output (default)"
fi
if [ $kernel_quiet -eq 1 ]; then
echo "Kernel Quiet : YES, silent after inital boot (default)"
else
echo "Kernel Quiet : NO"
fi
if [ $static -ne 0 ]; then
echo "Static Finit : YES"
fi
if [ $disable_plugins -eq 1 ]; then
echo "Plugins : DISABLED"
else
if [ x"$plugins" = x"" ]; then
echo "Plugins : ALL"
else
echo "Plugins : $plugins"
fi
fi
echo
}
usage()
{
defaults
echo "Run this script to configure finit for your operating system."
echo
echo "Usage: $0 [ARG]"
echo
echo " --help This help text"
echo " --prefix=PREFIX Set installation prefix, default '$prefix'"
echo " --sysconfdir=CONF Set conf dir, default '$sysconfdir'"
echo " --sbindir=SBIN Set sbin dir, default '$sbindir'"
echo " --libdir=LIB Set lib dir, default '$libdir'"
echo " --plugindir=DIR Set plugin dir, default '\$libdir/$pdir'"
echo
echo " --with-fifo=FIFO FIFO for initctl plugin, default '$fifo'"
echo " --with-config=CONFIG Finit CONFIG file, default '\$sysconfdir/$conf'"
echo " --with-heading=GREET Display GREET instead of Finit version at boot"
echo " --with-randomseed=SEED Seed /dev/urandom at boot from SEED. Usually found in"
echo " standard Linux systems at $random"
echo " which is also the default. Requires urandom plugin!"
echo " --with-plugins=\"a b c\" Build plugins 'a', 'b' and 'c', either as dynamic .so"
echo " files, or as built-in if --enable-static is given,"
echo " defaults to all plugins. Exclude extension!"
echo " --with-rcsd=RCSD Finit RCSD directory, default '\$sysconfdir/$confdir'"
echo " --with-console=DEV Console to use, default: /dev/console"
# echo " --with-shell=PATH Fallback shell to use, default: /bin/sh"
echo " --with-sysroot=PATH Some distributions keep a different (writable) rootfs"
echo " that is used at runtime, usually /sysroot or /mnt"
echo " --with-username=USER User for 'startx' .conf option, default: root"
echo " --with-hostname=NAME Hostname, if /etc/hostname is missing, default: noname"
echo " --with-runlevel=LEVEL Runlevel to switch to after bootstrap (S), default: 2"
echo " --with-libite=PATH Look for libite in PATH/include and PATH/lib"
echo " This is for disabling the use of built-in libite."
echo " --with-libuev=PATH Look for libuEv in PATH/include and PATH/lib"
echo " This is for disabling the use of built-in libuEv."
echo
echo " --enable-embedded Embedded defaults, BusBox getty, mdev etc."
echo " --enable-dbus Enable D-Bus plugin. Note: install plugin as well!"
echo " --enable-rw-rootfs Remount / as read-write at bootstrap, not for embedded"
echo " --enable-debug Enable debug flags, '-O0, -g'"
echo " --enable-quiet Quiet mode, reduce screen output to absolute minimum"
echo " --enable-static Link statically. This disables a few features."
echo " --disable-inetd Disable inetd support if not needed."
echo " --disable-kernel-quiet Disable kernel cmdline quiet, default enabled."
echo " --disable-plugins Disable plugins"
echo
echo "The build system also supports external CFLAGS, CPPFLAGS, LDFLAGS, LDLIBS,"
echo "and DESTDIR, useful for packaging or cross-compiling."
echo
echo "Example:"
echo " ./configure --enable-embedded --with-plugins=\"initctl tty\""
echo
echo "Report bugs to https://github.com/troglobit/finit/issues"
echo
exit 1
}
while [ "$*" != "" ]; do
opt=`expr "$1" : "--\([^=]*\)=.*"`
arg=`expr "$1" : "--[^=]*=\(.*\)"`
if [ -z "$opt" ]; then
opt=`expr "$1" : "--\(.*\)"`
fi
shift
case $opt in
prefix)
prefix="$arg"
;;
sysconfdir)
sysconfdir="$arg"
;;
sbindir)
sbindir="$arg"
;;
libdir)
libdir="$arg"
;;
plugindir)
plugindir="$arg"
;;
with-fifo)
fifo="$arg"
;;
with-config)
config="$arg"
;;
with-heading)
heading="\"$arg\""
;;
with-randomseed)
random="$arg"
;;
with-plugins)
plugins="$arg"
;;
with-rcsd)
rcsd="$arg"
;;
with-console)
console="\"$arg\""
;;
with-shell)
shell="\"$arg\""
;;
with-sysroot)
sysroot="$arg"
;;
with-usename)
defuser="$arg"
;;
with-hostname)
defhost="$arg"
;;
with-runlevel)
runlevel="$arg"
;;
with-libite)
libite="$arg"
;;
with-libuev)
libuev="$arg"
;;
enable-debug)
debug=1
;;
enable-embedded)
embedded=1
;;
enable-dbus)
dbus=1
;;
enable-rw-rootfs)
remount=1
;;
enable-quiet)
verbose=0
;;
enable-static)
static=1
;;
disable-inetd)
inetd=0
;;
disable-kernel-quiet)
kernel_quiet=0
;;
disable-plugins)
disable_plugins=1
;;
help)
usage
;;
*)
echo "Unknown option: $opt"
usage
;;
esac
done
# If no arguments given to configure script, use defaults
defaults
result
##############################################################################
echo "# Generated by finit configure script." > config.mk
echo "prefix = $prefix" >> config.mk
echo "sysconfdir = $sysconfdir" >> config.mk
echo "sbindir = $sbindir" >> config.mk
echo "libdir = $libdir" >> config.mk
echo "plugindir = $plugindir" >> config.mk
echo "incdir = \$(prefix)/include/finit" >> config.mk
echo "datadir = \$(prefix)/share/doc/finit" >> config.mk
echo "mandir = \$(prefix)/share/man/man8" >> config.mk
echo "FINIT_RCSD = $rcsd" >> config.mk
if [ $inetd -eq 0 ]; then
echo >> config.mk
echo "OBJS := \$(filter-out inetd.o, \$(OBJS))" >> config.mk
fi
echo >> config.mk
if [ $debug -eq 1 ]; then
echo "CFLAGS += -O0 -g" >> config.mk
else
echo "CFLAGS += -O2" >> config.mk
fi
echo >> config.mk
if [ $static -ne 0 ]; then
echo "STATIC = 1" >> config.mk
echo "LDFLAGS += -static" >> config.mk
echo "CPPFLAGS += -DENABLE_STATIC" >> config.mk
echo "PLUGINS = \"\"" >> config.mk
if [ $disable_plugins -ne 1 ]; then
if [ x"$plugins" != x"" ]; then
echo "OBJS += \$(addsuffix .o, \$(addprefix plugins/, $plugins))" >> config.mk
else
echo "tmp += \$(wildcard plugins/*.c)" >> config.mk
echo "OBJS += \$(tmp:.c=.o)" >> config.mk
fi
fi
else
echo "STATIC = 0" >> config.mk
echo "LDFLAGS += -rdynamic" >> config.mk
echo "LDLIBS += -ldl" >> config.mk
if [ x"$plugins" != x"" ]; then
echo "PLUGINS = \$(addsuffix .so, $plugins)" >> config.mk
fi
fi
if [ x"$libite" != x"" ]; then
echo "LIBITE = $libite" >> config.mk
echo "CPPFLAGS += -I\$(LIBITE)/include" >> config.mk
echo "LDFLAGS += -L\$(LIBITE)/lib" >> config.mk
echo "LDLIBS += -l:libite.so.2" >> config.mk
else
echo "LDFLAGS += -L\$(TOPDIR)/libite" >> config.mk
echo "LDLIBS += -lite" >> config.mk
echo "DEPLIBS += libite/libite.a" >> config.mk
fi
if [ x"$libuev" != x"" ]; then
echo "LIBUEV = $libuev" >> config.mk
echo "CPPFLAGS += -I\$(LIBUEV)/include" >> config.mk
echo "LDFLAGS += -L\$(LIBUEV)/lib" >> config.mk
echo "LDLIBS += -l:libuev.so.1" >> config.mk
else
echo "LDFLAGS += -L\$(TOPDIR)/libuev" >> config.mk
echo "LDLIBS += -luev" >> config.mk
echo "DEPLIBS += libuev/libuev.a" >> config.mk
fi
echo >> config.mk
echo "export libdir plugindir incdir" >> config.mk
echo "export CFLAGS CPPFLAGS LDFLAGS LDLIBS STATIC" >> config.mk
##############################################################################
echo "/* Generated by finit configure script. */" > config.h
echo >> config.h
echo "#ifndef FINIT_CONFIG_H_" >> config.h
echo "#define FINIT_CONFIG_H_" >> config.h
echo >> config.h
echo "#define INIT_HEADING $heading" >> config.h
echo "#define VERBOSE_MODE $verbose" >> config.h
echo "#define KERNEL_QUIET $kernel_quiet" >> config.h
if [ $embedded -eq 1 ]; then
echo "#define EMBEDDED_SYSTEM 1" >> config.h
echo "#define SETUP_DEVFS \"/sbin/mdev -s\"" >> config.h
echo "#define GETTY \"/sbin/getty -L\"" >> config.h
echo "#define GETTY_BUSYBOX 1" >> config.h
echo "#define BAUDRATE 115200" >> config.h
else
echo "#define SETUP_DEVFS \"/sbin/udevd --daemon\"" >> config.h
echo "#define GETTY \"/sbin/getty -8\"" >> config.h
echo "#define GETTY_AGETTY 1" >> config.h
echo "#define BAUDRATE 38400" >> config.h
fi
if [ $remount -eq 1 ]; then
echo "#define REMOUNT_ROOTFS 1" >> config.h
fi
if [ $dbus -eq 1 ]; then
echo "#define HAVE_DBUS 1" >> config.h
fi
if [ $inetd -eq 0 ]; then
echo "#define INETD_DISABLED" >> config.h
fi
echo "#define FINIT_FIFO \"$fifo\"" >> config.h
echo "#define FINIT_CONF \"$config\"" >> config.h
echo "#define FINIT_RCSD \"$rcsd\"" >> config.h
echo "#define CONSOLE $console" >> config.h
echo "#define FALLBACK_SHELL $shell" >> config.h
if [ x"$sysroot" != x"" ]; then
echo "#define SYSROOT \"$sysroot\"" >> config.h
fi
echo "#define PLUGIN_PATH \"$plugindir\"" >> config.h
if [ x"$random" != x"" ]; then
echo "#define RANDOMSEED \"$random\"" >> config.h
fi
echo "#define DEFUSER \"$defuser\"" >> config.h
echo "#define DEFHOST \"$defhost\"" >> config.h
echo "#define RUNLEVEL $runlevel" >> config.h
echo >> config.h
echo "#endif /* FINIT_CONFIG_H_ */" >> config.h
+210
View File
@@ -0,0 +1,210 @@
AC_INIT(Finit, 3.0-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR([finit.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile plugins/Makefile])
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4])
#m4_include([m4/expand.m4])
AC_CONFIG_MACRO_DIR([m4])
# Change default prefix, we're PID 1 after all ...
AC_PREFIX_DEFAULT([])
# Handle building plugins either as dynamically loadable, or built-in
LT_INIT([shared disable-static dlopen aix-soname=both disable-fast-install])
# Checks for programs.
AC_PROG_CC
AC_PROG_LN_S
AC_PROG_INSTALL
# Configuration.
AC_HEADER_STDC
AC_CHECK_HEADERS(fstaby.h)
AC_CHECK_FUNCS(strstr getopt getfsenty)
# Check for uint[8,16,32]_t
AC_TYPE_UINT8_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 1.4.2])
PKG_CHECK_MODULES([lite], [libite >= 1.8.2])
# Check for configured Finit features
AC_ARG_ENABLE(emergency_shell,
AS_HELP_STRING([--enable-emergency-shell], [Experimental emergency fallback at boot.
Only use this for debugging very early boot problems with Finit.]))
AC_ARG_ENABLE(rw-rootfs,
AS_HELP_STRING([--enable-rw-rootfs], [Remount / R/W at boot, usually not for embedded]))
AC_ARG_ENABLE(silent,
AS_HELP_STRING([--enable-silent], [Completely silent operation, not even boot messages]))
AC_ARG_ENABLE(inetd,
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]))
AC_ARG_ENABLE(quiet,
AS_HELP_STRING([--disable-quiet], [Default Finit obeys the kernel quiet option]))
# Check for extra plugins to enable
enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus])
AC_PLUGIN([inetd-echo], [no], [Inetd plugin: echo server, RFC862])
AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864])
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!])
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
# Check for extra arguments or packages
AC_ARG_WITH(heading,
AS_HELP_STRING([--with-heading=GREET], [Display GREET instead of Finit version at boot]),
[heading=$withval], [heading="$PACKAGE_NAME v$PACKAGE_VERSION"])
AC_ARG_WITH(fifo,
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
[fifo=$withval], [fifo=/var/run/initctl])
AC_ARG_WITH(plugindir,
AS_HELP_STRING([--with-plugindir=DIR], [Finit plugin directory, default /lib/finit/plugins]),
[pkglibdir=$withval], [pkglibdir=$libdir/finit/plugins])
AC_SUBST([pkglibdir])
AC_ARG_WITH(config,
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
[conf=$withval], [conf=$sysconfdir/finit.conf])
AC_ARG_WITH(rcsd,
AS_HELP_STRING([--with-rcsd=DIR], [Finit rcS.d/ directory, default /etc/finit.d]),
[rcsd=$withval], [rcsd=$sysconfdir/finit.d])
AC_ARG_WITH(rc-local,
AS_HELP_STRING([--with-rc-local=FILE], [SysV init /etc/rc.local file, default /etc/rc.local]),
[rclocal=$withval], [rclocal=$sysconfdir/rc.local])
AC_ARG_WITH(console,
AS_HELP_STRING([--with-console=DEV], [Console to use, default: /dev/console]),
[console=$withval], [console=/dev/console])
AC_ARG_WITH(shell,
AS_HELP_STRING([--with-shell=PATH], [Fallback shell to use, default: /bin/sh]),
[shell=$withval], [shell=/bin/sh])
AC_ARG_WITH(username,
AS_HELP_STRING([--with-username=USER], [User for 'startx' .conf option, default: root]),
[username=$withval], [username=root])
AC_ARG_WITH(hostname,
AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]),
[hostname=$withval], [hostname=noname])
AC_ARG_WITH(runlevel,
AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap, default: 2]),
[runlevel=$withval], [runlevel=2])
AC_ARG_WITH(random-seed,
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
[random_seed=$withval], [random_seed=/var/lib/misc/random-seed])
### Enable features ###########################################################################
# Create config.h from selected features and fallback defautls
AS_IF([test "x$enable_emergency_shell" = "xyes"], [
AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
AS_IF([test "x$enable_rw_rootfs" = "xyes"], [
AC_DEFINE(REMOUNT_ROOTFS, 1, [Remount / as rw at boot])])
AS_IF([test "x$enable_silent" = "xyes"], [
AC_DEFINE(SILENT_MODE, 1, [Completely silent operation, not even boot messages])
], [
AC_DEFINE(SILENT_MODE, 0, [Normal output, default])])
### Disable features ###########################################################################
AS_IF([test "x$enable_inetd" != "xno"], enable_inetd="yes", [
AC_DEFINE(INETD_DISABLED, 1, [Disable built-in inetd])])
AS_IF([test "x$enable_quiet" != "xno"], [
AC_DEFINE(QUIET_MODE, 0, [Will print stopping/starting/etc messages at runtime])
], [
AC_DEFINE(QUIET_MODE, 1, [Silent at runtime, after initial boot, default])])
### With features ##############################################################################
AS_IF([test "x$with_fifo" != "xno"], [
AS_IF([test "x$fifo" = "xyes"], [
fifo='/var/run/initctl'])])
AC_EXPAND_DIR(lurvig, "$fifo")
AC_DEFINE_UNQUOTED(FINIT_FIFO, "$lurvig", [FIFO for old style init/telinit client programs])
AS_IF([test "x$with_config" != "xno"], [
AS_IF([test "x$config" = "xyes"], [
conf=$sysconfdir/finit.conf])])
AC_EXPAND_DIR(lurvig, "$conf")
AC_DEFINE_UNQUOTED(FINIT_CONF, "$lurvig", [Main Finit configuration file, default /etc/finit.conf])
AS_IF([test "x$with_rcsd" != "xno"], [
AS_IF([test "x$rcsd" = "xyes"], [
rcsd=$sysconfdir/finit.d])])
AC_EXPAND_DIR(lurvig, "$rcsd")
AC_DEFINE_UNQUOTED(FINIT_RCSD, "$lurvig", [Finit rcS.d/, default /etc/finit.d])
AS_IF([test "x$with_rc_local" != "xno"], [
AS_IF([test "x$rclocal" = "xyes"], [
rclocal=$sysconfdir/rc.local])])
AC_EXPAND_DIR(lurvig, "$rclocal")
AC_DEFINE_UNQUOTED(FINIT_RC_LOCAL, "$lurvig", [Compat SysV /etc/rc.local file to run last at boot.])
AS_IF([test "x$with_heading" != "xno"], [
AS_IF([test "x$heading" = "xyes"], [
heading="$PACKAGE_NAME v$PACKAGE_VERSION"])])
AC_DEFINE_UNQUOTED(INIT_HEADING, "$heading", [Displayed by Finit at boot])
AS_IF([test "x$with_console" != "xno"], [
AS_IF([test "x$console" = "xyes"], [
console=/dev/console])])
AC_DEFINE_UNQUOTED(CONSOLE, "$console", [Default console to use])
AS_IF([test "x$with_shell" != "xno"], [
AS_IF([test "x$shell" = "xyes"], [
shell=/bin/sh])])
AC_DEFINE_UNQUOTED(FALLBACK_SHELL, "$shell", [Used if no login getty])
AS_IF([test "x$with_username" != "xno"], [
AS_IF([test "x$username" = "xyes"], [
username=root])])
AC_DEFINE_UNQUOTED(DEFUSER, "$username", [Used for startx .conf option])
AS_IF([test "x$with_hostname" != "xno"], [
AS_IF([test "x$hostname" = "xyes"], [
hostname=noname])])
AC_DEFINE_UNQUOTED(DEFHOST, "$hostname", [For /etc/hostname])
AS_IF([test "x$with_runlevel" != "xno"], [
AS_IF([test "x$runlevel" = "xyes"], [
AC_DEFINE_UNQUOTED(RUNLEVEL, 2, [Default runlevel to start after S])])
AC_DEFINE_UNQUOTED(RUNLEVEL, $runlevel, [Runlevel to start after S])])
AS_IF([test "x$with_random_seed" != "xno"], [
AS_IF([test "x$random_seed" = "xyes"], [
random_seed=/var/lib/misc/random-seed])
AC_EXPAND_DIR(lurvig, "$random_seed")
AC_DEFINE_UNQUOTED(RANDOMSEED, "$lurvig", [Improve random at boot by seeding it with sth from before.])])
# Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
# Generate all files
AC_OUTPUT
+12
View File
@@ -0,0 +1,12 @@
Finit Examples
==============
This section provides configuration examples and helpful tools to
install and try out Finit on various Linux distributions.
* [Alpine](alpine/)
* [Debian](debian/)
If you have ideas on how to simplify, extend, or even add new example
configurations for other distributions you are most welcome! :)
+46
View File
@@ -0,0 +1,46 @@
HowTo: Finit on Alpine Linux 3.4
================================
Mini HowTo using Finit to boot an Alpine Linux system. It is assumed
that the user has already installed a compiler, C library header files,
and other tools needed to build a GNU configure based project.
To start with you need to first install [libuEv][] and [libite][], by
default they install to `/usr/local` and Alpine's `pkg-config` does not
look for libraries and header files there. So the `PKG_CONFIG_LIBDIR`
environment variable has to be used:
alpine:~/finit# PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure \
--enable-rw-rootfs --enable-x11-common-plugin \
--enable-alsa-utils-plugin --enable-inetd-echo-plugin \
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
--enable-inetd-discard-plugin --enable-inetd-time-plugin \
--with-heading="Alpine Linux 3.4" --with-hostname=alpine
The plugins are optional, but the other configure flags are *not*.
alpine:~/finit# make all install
The installation skips `/sbin/init`, because it already exists, and
Alpine is hard coded to use it. So you have to change the symlink
yourself to point to `finit` instead of `/bin/busybox`.
alpine:~/finit# cd /sbin
alpine:/sbin# rm init
alpine:/sbin# ln -s finit init
Before rebooting, make sure to set up a [/etc/finit.conf](finit.conf),
and possibly also an [/etc/rc.local](rc.local) for one-shot set up and
initialization like keyboard language etc. You can use the files in
this directory as templates -- check them both if they need to be
tweaked for your installation, you may not run Dropbear SSH or speak
Swedish ...
[libuEv]: https://github.com/troglobit/libuev
[libite]: https://github.com/troglobit/libite
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+40
View File
@@ -0,0 +1,40 @@
# /etc/finit.conf: Example for Alpine Linux 3.4
# Modules to load
#module qxl
#module fbcon
module button
module evdev
# Runlevel to start after bootstrap, runlevel 'S'
runlevel 2
# Services to be monitored and respawned as needed
service [S12345] /sbin/klogd -n -- Kernel log daemon
service [S12345] /sbin/syslogd -n -- Syslog daemon
# Network bringup script, blocks boot like run stanza, use below task instead
#network /sbin/ifup -a
### Every line below can be a service/task per file in /etc/finit.d/*.conf ###
# Network bringup not needed if /etc/network/interfaces is used.
task [2345] log <svc/sbin/syslogd> /sbin/ifup -a -- Networking
# Depends on syslogd having started. Redirect any output to log.
service [2345] log <svc/sbin/syslogd> /usr/sbin/acpid -f -- ACPI daemon
# Silly Busybox ntpd has no option to use syslog when running in foreground
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
# Start SSH daemon as soon as basic networking comes up
service [2345] <net/lo/up> /usr/sbin/dropbear -R -F -p 22 -- Dropbear SSH daemon
service [2345] <svc/sbin/syslogd> /usr/sbin/crond -f -S -c /etc/crontabs -- Cron daemon
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
service [2345] /sbin/acpid -f -- ACPI daemon
# Consoles to start getty on when system is up
tty /dev/tty1
tty /dev/tty2
tty /dev/tty3
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
zcat /etc/keymap/sv-latin1.bmap.gz | loadkmap
+52
View File
@@ -0,0 +1,52 @@
HowTo: Finit on Debian Jessie
=============================
Mini HowTo using Finit to boot Debian GNU/Linux 8.5 (Jessie). It is
assumed that the user has already installed a compiler, C library header
files, and other tools needed to build a GNU configure based project.
Also, Finit has only been verified to work with the Debian *Server* ed.
I.e., during install — skip GNOME and other X stuff to get a plain and
usable Debian server install, which we can add X-Window to later :-)
Now, I started off by replacing systemd from the base install with the
old SysV init, using [this HowTo][1], you may not need to do that, but
for me it was easier — and also a great rebuttal to all the systemd
haters out there (yeah, even though I speak for the alternative PID 1
movement I don't hate systemd :P) it is possible to still run Debian
without systemd! Anyway, this text was based on that experience.
Like the [Alpine HowTo](../alpine/), you need to install [libuEv][] and
[libite][], but since this is Debian — which takes infinite care of its
users ♥ ♥ ♥ we don't need to worry about `pkg-config`, except for having
it installed so it can locate the uEv and lite libraries.
> With Debian everything just works!™
The following build and install script can be used to configure, build,
install and set up your system to run Finit:
debian:~/finit$ contrib/debian/build.sh
Since `/sbin/init` already exists on your system the script creates
another entry in your GRUB config, in `/etc/grub.d/40_custom`, where
`init=/sbin/finit` is added to your kernel line. You could of course
also change the `/sbin/init` symlink and uninstall systemd/SysV init if
everything works fine, but that's up to you.
Before rebooting, read up on [/etc/finit.conf](finit.conf) and notice
how `/etc/start.d/` and `/etc/rc.local` can be used to extend the boot
with simpler setup tasks on your system — see the docu for details.
Have fun!
/Joachim ツ
[1]: http://without-systemd.org/wiki/index.php/How_to_remove_systemd_from_a_Debian_jessie/sid_installation
[libuEv]: https://github.com/troglobit/libuev
[libite]: https://github.com/troglobit/libite
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+62
View File
@@ -0,0 +1,62 @@
#!/bin/sh
#
# This script configures Finit for Debian, which needs a few more plugins
# and tweaks than an embedded system usually does.
#
# Usage:
# user@jessie:~/finit$ contrib/debian/build.sh
#
echo
echo "*** Configuring Finit for Debian"
if [ ! -e configure ]; then
echo " The configure script is missing, maybe you're using a GIT checkout?"
echo " Attempting to run the autogen.sh script, you need these tools:"
echo " autoconf, automake, libtool, pkg-config ..."
echo
./autogen.sh
fi
echo
./configure --enable-rw-rootfs --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-random-seed=/var/lib/urandom/random-seed \
--with-heading="Debian GNU/Linux 8.5" --with-hostname=jessie \
--enable-inetd-echo-plugin --enable-inetd-chargen-plugin \
--enable-inetd-daytime-plugin --enable-inetd-discard-plugin \
--enable-inetd-time-plugin
if [ $? -ne 0 ]; then
echo
echo "*** Configure script failed, have you installed libuEV and libite?"
echo
exit 1
fi
echo
echo "*** Building ..."
echo
make
if [ $? -ne 0 ]; then
echo
echo "*** The build failed for some reason"
echo
exit 1
fi
echo
echo "*** Done"
echo
read -p "*** Run (sudo) install script (y/N)? " yorn
if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
sudo contrib/debian/install.sh
else
echo
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
echo
fi
+60
View File
@@ -0,0 +1,60 @@
# /etc/finit.conf: Example for Debian GNU/Linux 8.5
# Here we use one big (?) finit.conf, but all non-bootstrap services
# can just as easily be listed in a separate /etc/finit.d/*.conf
# The host directive is a fallback if /etc/hostname is missing
host jessie
# The user directive is only for the startx directive, which in turn is
# just like a service but with a different user. Services can also run
# with a different user, using the @username syntax
user root
# Linux kernel modules to load, /etc/modprobe is not used
module button
module evdev
module loop
module psmouse
# Runlevel to start after bootstrap, runlevel 'S'.
# Change this to 3 to start XDM at boot.
runlevel 2
# Some services may need to start very early, e.g. a watchdog daemon.
# Runlevel [S] (bootStrap) is useful for that. It cannot be switched
# to at runtime either, so tasks and run directives usually live here.
# Please note, the run directive is blocking, so be careful!
#service [S12345] /sbin/watchdogd -n -- Watchdog daemon
#run [S] /sbin/probe -- system probe
#run [S] /sbin/setupd --bootstrap -- system bootstrap
service [S12345] /usr/sbin/rsyslogd -n -- Syslog daemon
# Network bringup script not needed if /etc/network/interfaces is used
#network service networking start
# Regular services to be monitored and respawned as needed. Here we use
# a (svc) condition, the listed service must be started (waits for the
# PID file to be created in /run/*.pid) before starting either service,
# (net) conditions are also available.
service [2345] <svc/usr/sbin/rsyslogd> log /usr/sbin/acpid -f -- ACPI daemon
service [2345] <svc/usr/sbin/rsyslogd> /usr/sbin/sshd -D -- SSH daemon
# Here we use XDM since it has few dependencies (relatively speaking).
# It is also an example of a service (startx is an alias for service)
# that does not have a -- description.
#service [3] /usr/sbin/lightdm -- X-Window Display Manager
startx [3] /usr/bin/xdm -nodaemon
# Run old-style start scripts from this directory. Very similar to how
# SysV init works: 01first, 02second, etc. see run-parsts(8) for details
runparts /etc/start.d
# |-- S03keyboard-setup
# |-- S17kbd
# `- S18console-setup
# Virtual consoles to start getty on. Runlevels, speed & $TERM optional
# TTYs are also services, sort of, but they are guaranteed to start at
# the very end of the boot sequence. Finit comes with a built-in getty.
tty [12345] /dev/tty1 115200 vt100
tty [2345] /dev/tty2 38400 linux
tty [2345] /dev/tty3 38400 linux
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
menuentry '$MENU with Finit' --class $TYPE --class gnu-linux --class gnu --class os --user {
insmod gzio
insmod part_msdos
insmod ext2
search --no-floppy --fs-uuid --set=root $BOOT
linux /vmlinuz root=UUID=$ROOT ro quiet init=/sbin/finit
initrd /initrd.img
}
+55
View File
@@ -0,0 +1,55 @@
#!/bin/sh
SYMLINKS="start.d/*"
FILES="finit.conf"
if [ "x`id -u`" != "x0" ]; then
echo
echo "=> Unfortunately this script must run as root (sudo) <="
echo
exit 1
fi
# Adjust base directory
grep -q Debian os-release 2>/dev/null
if [ $? -eq 0 ]; then
cd ../..
elif [ ! -e finit.c ]; then
echo "Please run this script from the Finit base directory."
exit 1
fi
echo "Install Finit on Debian 8 (jessie)"
echo "========================================================================"
echo "/sbin/finit - PID 1"
echo "/lib/finit/plugins/* - All enabled Finit plugins"
echo "/etc/finit.conf - Instead of units or /etc/init.d Finit can have"
echo " one config for your system. Or you can split"
echo " it in service(s) per file in /etc/finit.d/"
echo "/etc/start.d/ - Simple Debian setup scripts run with run-parts(8)"
echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader"
echo
read -p "Do you want to continue (y/N)? " yorn
echo
if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
echo "Installing Finit files ..."
make install
cd `dirname $0`
mkdir -p /etc/start.d
for file in $FILES; do
install -vbD $file /etc/$file
done
for link in $SYMLINKS; do
cp -va $link /etc/$link
done
echo "Setting up a GRUB boot entry ..."
MENU=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/menuentry '\([^']*\).*/\1/"`
TYPE=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/.*' --class \([a-z]*\).*/\1/"`
BOOT=`grep set=root /boot/grub/grub.cfg |head -1 |sed 's/.* \([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
ROOT=`grep root=UUID /boot/grub/grub.cfg |head -1 |sed 's/.*UUID=\([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
cat grub.d/40_custom | sed "s,\$BOOT,$BOOT,;s,\$ROOT,$ROOT,;s,\$TYPE,$TYPE,;s,\$MENU,$MENU," >/etc/grub.d/40_custom
update-grub
else
echo "Aborting install."
fi
+8
View File
@@ -0,0 +1,8 @@
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=debian
HOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
+1
View File
@@ -0,0 +1 @@
../init.d/keyboard-setup
+1
View File
@@ -0,0 +1 @@
../init.d/kbd
+1
View File
@@ -0,0 +1 @@
../init.d/console-setup
+7 -2
View File
@@ -1,7 +1,12 @@
#!/bin/sh #!/bin/sh
# Alternative boot method for other distributions than Debian 6.0 # Alternative boot method for other distributions than Debian 6.0
# Place this file in a /etc/finit.d/ directory. At boot finit will # Place this file in a /etc/start.d/ directory and add the following
# run all files, using its built-in run_parts(), in that directory. # line to /etc/finit.conf:
#
# runparts /etc/start.d
#
# At boot finit will run all files, using its built-in run_parts(),
# in that directory.
# #
# Here we chain load Debian /etc/rcS.d and /etc/rc2.d directories # Here we chain load Debian /etc/rcS.d and /etc/rc2.d directories
# in sequence using the standard run-parts tool. # in sequence using the standard run-parts tool.
+53
View File
@@ -0,0 +1,53 @@
Bootstrap
=========
1. Populate `/dev`
2. Parse `/etc/finit.conf`
3. Load all `.so` plugins
4. Remount/Pivot `/` to get R+W
5. Call 1st level hooks, `HOOK_ROOTFS_UP`
6. Mount `/etc/fstab` and swap, if available
7. Cleanup stale files from `/tmp/*` et al
8. Enable SysV init signals
9. Call 2nd level hooks, `HOOK_BASEFS_UP`
10. Start all 'S' runlevel tasks and services
11. Load kernel params from `/etc/sysctl.d/*.conf`, `/etc/sysctl.conf`
et al. (Supports all locations that SysV init does.)
12. Set hostname and bring up loopback interface
13. Call `network` script, if set in `/etc/finit.conf`
14. Call 3rd level hooks, `HOOK_NETWORK_UP`
15. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
(re)loaded and new services, tasks, and blocking run commands are
started. Provided they are allowed in the new runlevel and all of
their conditions, if any, are set.
16. Call 4th level hooks, `HOOK_SVC_UP`
17. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
18. Call `/etc/rc.local`, if it exists and is an executable shell script
19. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
20. Start TTYs defined in `/etc/finit.conf`, or rescue on `/dev/console`
In (10) and (15) tasks and services defined in `/etc/finit.conf` are
started. Remember, all `service` and `task` stanzas are started in
parallel and `run` in sequence, and in the order listed. Hence, to
emulate a SysV `/etc/init.d/rcS` one could write a long file with only
`run` statements.
Notice the five hook points that are called at various point in the
bootstrap process. This is where plugins can extend the boot in any way
they please.
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
USB stick, convert/copy its contents to the system's `/etc/` directory,
well before all 'S' runlevel tasks are started. This could be used with
system images that are created read-only and all configuration is stored
on external media.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+99
View File
@@ -0,0 +1,99 @@
Building
========
Finit comes with a traditional configure script to control features and
optional plugins to enable. It does however depend on two external
libraries that provide some frog DNA needed:
- [libuEv][]
- [libite][] (-lite)
Like most free/open source software that uses `configure` they deafult
to install to `/usr/local`. However, some Linux distributions do no
longer search that path for installed software, e.g. Fedora and Alpine
Linux. To get finit's configure script to find its dependencies you
have to help the `pkg-config` tool a bit if you do not change the
default prefix path:
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
Below are a few of the main switches to
configure:
* `--disable-inetd`: Disable the built-in inetd server.
* `--enable-embedded`: Target finit for BusyBox getty and mdev instead
of a standard Linux distribution with GNU tools and udev.
* `--enable-rw-rootfs`: Most desktop and server systems boot with the
root file stystem read-only. With this setting Finit will remount it
as read-write early at boot so the `bootmisc.so` plugin can run.
Usually not needed on embedded systems.
* `--enable-static`: Build Finit statically. The plugins will be
built-ins (.o files) and all external libraries, except the C library
will be linked statically.
* `--enable-alsa-utils`: Enable the optional `alsa-utils.so` sound plugin.
* `--enable-dbus`: Enable the optional D-Bus `dbus.so` plugin.
* `--enable-lost`: Enable noisy example plugin for `HOOK_SVC_LOST`.
* `--enable-resolvconf`: Enable the `resolvconf.so` optional plugin.
* `--enable-x11-common`: Enable the optional X Window `x11-common.so` plugin.
For more configure flags, see <kbd>./configure --help</kbd>
**Example**
First, unpack the archive:
```shell
$ tar xf finit-3.0.tar.xz
$ cd finit-3.0/
```
Then configure, build and install:
```shell
$ ./configure --enable-embedded --enable-rw-rootfs --enable-inetd-echo-plugin \
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
--enable-inetd-discard-plugin --enable-inetd-time-plugin \
--with-heading="Alpine Linux 3.4" --with-hostname=alpine
$ make
.
.
.
$ DESTDIR=/tmp/finit make install
```
In this example the [finit-3.0.tar.xz][1] archive is unpacked to the
user's home directory, configured, built and installed to a temporary
staging directory. The environment variable `DESTDIR` controls the
destination directory when installing, very useful for building binary
standalone packages.
To target an embedded Linux system, usally a system that use BusyBox
tools instead of udev & C:o, add <kbd>--enable-embedded</kbd> to the
configure command above. This enables `mdev` instead of `udev` and the
BusyBox `getty` syntax. Remember to also change the Linux config to:
CONFIG_UEVENT_HELPER_PATH="/sbin/mdev"
**Note:** If you run into problems starting Finit, take a look at
`finit.c`. One of the most common problems is a custom Linux kernel
build that lack `CONFIG_DEVTMPFS`. Another is too much cruft in the
system `/etc/fstab`.
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
[libuEv]: https://github.com/troglobit/libuev
[libite]: https://github.com/troglobit/libite
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+162
View File
@@ -0,0 +1,162 @@
Finit Conditions
================
![The service state machine](svc-machine.png "The service state machine")
Table of Contents
-----------------
* [Introduction](#introduction)
* [Triggering](#triggering)
* [Built-in Conditions](#built-in--conditions)
* [Debugging](#debugging)
* [Internals](#internals)
Introduction
------------
In addition to declaring allowed runlevels per service, it is also
possible to declare user-defined conditions as dependencies. Conditions
are specified within angle brackets (<>) in the service stanza.
Multiple conditions may be specified separated by comma. Conditions are
AND'ed during evaluation, i.e. all conditions must be satisfied in order
for a service to run.
**Example:**
```shell
service [2345] <svc/sbin/setupd,svc/sbin/zebra> /sbin/netd -- Network monitor
```
In this example the Network monitor daemon `netd` is not started until
both the `svc/sbin/setupd` *and* `svc/sbin/zebra` conditions are
satisfied. An `svc` condition is satisfied by the corresponding
service's pidfile being created.
Triggering
----------
Conditions are triggered either by plugins or by using the `cond`
command of the `initctl` control tool.
* `initctl cond set your/cond/here`
To set a condition
* `initctl cond clear your/cond/here`
To clear a condition
Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the
`flux` state, meaning the condition's state is unknown. (See
[Internals](#internals) for the rationale behind this.) Thus, after a
reconfiguration it is up to the "owner" of the condition to convey the
new (or possibly unchanged) state of it.
Built-in Conditions
-------------------
Finit is distributed with a `pidfile` and `netlink` plugin. If enabled,
the `pidfile` plugin watches `/var/run/` for PID files created by
monitored services, and sets a corresponding condition in the `svc/`
namespace. Similarily, the `netlink` plugin provides basic conditions
for when an interface is brought up/down and when a default route
(gateway) is set.
With the example listed above, finit does not start the `/sbin/netd`
daemon until `setupd` and `zebra` has started *and* created their PID
files. Which they do when they completed their main tasks of setting up
VLANs, bridge, interfaces, etc. When `netd` in turn starts up it
creates the file `/var/run/netd.pid`, and the condition `svc/sbin/netd`
is satisfied. When the file is removed, the condition is cleared.
The full path to the dependency is needed by finit to match the PID file
to a monitored process.
Built-in conditions:
- `svc/<PATH>`
- `net/route/default`
- `net/<IFNAME>/exist`
- `net/<IFNAME>/up`
Note: `up` is administratively up, `IFF_UP`, not link up, `IFF_RUNNING`.
Debugging
---------
If a service is not being started as it should, the problem might be
that one of its conditions is not in the expected state. Use the
command `initctl status` to inspect service status. Services in the
`ready` state are pending a condition.
In that situation, running `initctl cond show` reveals which of the
conditions that are not satisfied. Listed as `off` below.
**Example:**
```shell
~ # initctl cond show
PID Service Status Condition (+ on, ~ flux, - off)
===============================================================================
1419 /sbin/netd on <+svc/sbin/setupd,+svc/sbin/zebra>
0 /sbin/udhcpc off <-net/vlan1/exist>
```
Here we can see that `netd` is allowed to run since both its conditions
are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however
is not allowed to run since `net/vlan1/exist` condition is not satsifed.
As indicated by the `-`-prefix.
To fake interface `vlan1` suddenly appearing, and test what happens to
`udhcpc` we can enable debug mode and assert the condition, like this:
```shell
~ # initctl debug
~ # initctl cond set net/vlan1/exist
```
Then watch the console for the debug messages and then check the output
from `initctl cond show` again. (The client will likely have failed to
start, but at least the condition is now satisfied.)
Internals
---------
A condition is always in one of three states:
* `on` (+): The condition is asserted.
* `off` (-): The condition is deasserted.
* `flux` (~): The conditions state is unknown.
All conditions that have not explicitly been set are interpreted as
being in the `off` state.
When a reconfiguration is requested, Finit transitions all conditions to
the `flux` state. As a result, services that depend on a condition are
sent `SIGSTOP`. Once the new state of the condition is asserted, the
service receives `SIGCONT`. If the condition is no longer satisfied the
service will then be stopped, otherwise no further action is taken.
This STOP/CONT handling minimizes the number of unnecessary service
restarts that would otherwise occur because a depending service was sent
`SIGHUP` for example.
Therefore, any plugin that supplies Finit with conditions must ensure
that their state is updated after each reconfiguration. This can be
done by binding to the `HOOK_SVC_RECONF` hook. For an example of how
to do this, see `plugins/pidfile.c`.
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+216
View File
@@ -0,0 +1,216 @@
Configuration
=============
Table of Contents
-----------------
* [/etc/finit.conf](#etcfinitconf)
* [/etc/finit.d](#etcfinitd)
/etc/finit.conf
---------------
Contrary to most other script based init alternatives (SysV [init][],
[upstart][], [systemd][], [OpenRC][], etc.) Finit relies on its main
configuration file `/etc/finit.conf` and `/etc/finit.d/`.
The command line arguments given in `/etc/finit.conf` to each service
provide a default. A plugin can be used to register a callback to a
service and then modify the behavior to suit the current runlevel and
system configuration.
For instance, before starting a heavily resource intensive service like
IPsec or OpenVPN, a callback can check if the outbound interface is up
and has an IP address, or just check if the service is disabled — much
like what a SysV init start script usually does.
Syntax:
* `check <DEV>`
Run fsck on a file system. Only needed if not listed in `/etc/fstab`,
Finit 3.0 and later check all file systems automatically at boot.
* `module <MODULE>`
Load a kernel module, with optional arguments
* `network <PATH>`
Script or program to bring up networking, with optional arguments
* `rlimit <hard|soft> RESOURCE <LIMIT|infinity>`
Modify the specified resource's hard or soft limit to be the
specifed limit. `RESOURCE` is a lower-case string matching the
constants in `setrlimit(2)` with the `RLIMIT_` prefix
removed. E.g. to select `RLIMIT_CPU`, `RESOURCE` would be
`cpu`. `LIMIT` is an integer whose unit depends on the resource
being modified, see `setrlimit(2)` for more information. The special
limit `infinity` means the there should be no limit on the resource,
i.e. `RLIM_INFINITY` is passed to `setrlimit(2)`.
```shell
# No process is allowed more than 8MB of address space
rlimit hard as 8388608
# Core dumps may be arbitrarily large
rlimit soft core infinity
```
* `runlevel <N>`
N is the runlevel number 1-9, where 6 is reserved for reboot.
Default is 2.
* `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
One-shot command to run in sequence when entering a runlevel, with
optional arguments and description. This command is guaranteed to be
completed before running the next command.
* `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
One-shot like 'run', but starts in parallel with the next command
* `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Please note that you often need to provide a
`--foreground` or `--no-background` argument to most daemons to
prevent them from forking off a sub-process in the background.
```shell
service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon
```
For a detailed description of conditions, and how to debug them,
see the [Finit Conditions](conditions.md) document.
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
Launch a daemon when a client initiates a connection on an Internet
port. Available services are listed in the UNIX `/etc/services` file.
Finit can filter access to from a list of interfaces, `@iflist`, per
inetd service as well as listen to custom ports.
```shell
inetd ftp/tcp nowait @root /usr/sbin/uftpd -i -f
inetd tftp/udp wait @root /usr/sbin/uftpd -i -t
```
The following example listens to port 2323 for telnet connections and
only allows clients connecting from `eth0`:
```shell
inetd 2323/tcp@eth0 nowait [2345] /sbin/telnetd -i -F
```
The interface list, `@iflist`, is of the format `@iface,!iface,iface`,
where a single `!` means to deny access. Notice how interfaces are
comma separated with no spaces.
The `inetd` directive can also have ` -- Optional Description`, only
Finit does not output this text on the console when launching inetd
services. Instead this text is sent to syslog and also shown by the
`initctl` tool. More on inetd below.
* `runparts <DIR>`
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order.
It can be beneficial to use `S01name`, `S02othername`, etc. if there
is a dependency order between the scripts. Symlinks to existing
daemons can talso be used, but make sure they daemonize by default.
As the optional `/etc/rc.local` shell script, make sure that all your
services and programs either terminate or start in the background or
you will block Finit.
* `include <CONF>`
Include another configuration file. Absolute path required.
* `tty [LVLS] <DEV> [BAUD] [TERM]`
Start a getty on the given TTY device DEV, in the given runlevels. If
no tty setting is given in `finit.conf` no login is possible. Use the
`service` stanza to start a stand-alone shell for really bare bones
systems.
Finit comes with a lightweight built-in getty, which after it has
opened a TTY and the user has input a username, calls `/bin/login`
like any other getty.
* `console <DEV>`
Use this, along with a matching `tty DEV` line, to mark this TTY as a
special "console" port with with `prctl()`. Useful in use-cases when
all logins except the console must be stopped, e.g. when performing a
flash upgrade on an embedded system.
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
be installed since system requirements differ too much. Try out the
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
Every `run`, `task`, `service`, or `inetd` can also list the privileges
the `/path/to/cmd` should be executed with. Simply prefix the path with
`[@USR[:GRP]]` like this:
```shell
run [2345] @joe:users /usr/bin/logger "Hello world"
```
For multiple instances of the same command, e.g. a DHCP client or
multiple web servers, add `:ID` somewhere between the `run`, `task`,
`service` keyword and the command, like this:
```shell
service :1 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
service :2 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
```
Without the `:ID` to the service the latter will overwrite the former
and only the old web server would be started and supervised.
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
`log` to redirect `stderr` and `stdout` of the application to syslog,
using `logger`.
/etc/finit.d
------------
Finit supports changes to the overall system configuration at runtime.
For this purpose the (configurable) directory `/etc/finit.d` is used.
Here you can put configuration file snippets, one per service if you
like, which are all sourced automatically by finit at boot when loading
the static configuration from `/etc/finit.conf`. This is the default
behavior, so no include directives are necessary.
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
call `finit q`, but that has been deprecated with the `initctl` tool).
Any service read from this directory is flagged as a dynamic service, so
changes to or removal of `/etc/finit.d/*.conf` files, is detected.
On `initctl reload` the following is checked for all services:
- If a service's `.conf` file has been removed, or its conditions are no
longer satisifed, the service is stopped.
- If the file is modified, or a service it depends on has been reloaded,
the service is reloaded (stopped and started).
- If a new service is added it is automatically started — respecting
runlevels and return values from any callbacks.
For more info on the different states of a service, see the separate
document [Finit Services](service.md).
The `/etc/finit.d` directory was previously the default Finit `runparts`
directory. Finit no longer has a default `runparts`, so make sure to
update your setup, or the finit configuration, accordingly.
**Note:** Configurations read from `/etc/finit.d` are read *after*
initial bootstrap, runlevel S(1). Hence, bootstrap services *must* be
in `/etc/finit.conf`!
[init]: http://en.wikipedia.org/wiki/Init
[upstart]: http://upstart.ubuntu.com/
[systemd]: http://www.freedesktop.org/wiki/Software/systemd/
[openrc]: http://www.gentoo.org/proj/en/base/openrc/
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+131
View File
@@ -0,0 +1,131 @@
Internet Super Server
=====================
Inetd
-----
A built-in *Internet Super Server* support was added in Finit v1.12 and
v1.13, along with an internal `time` inetd service, RFC 868 (rdate).
The latter is supplied as a plugin to illustrate how simple it is to
extend finit with more internal inetd services. Today more built-in
services are available.
> Please note, not all UNIX daemons are prepared to run as inetd services.
> In the example below `sshd` also need the command line argument `-i`.
The inetd support in finit is quite advanced. Not only does it launch
services on demand, it can do so on custom ports and also filter inbound
traffic using a poor man's [TCP wrappers][]. The syntax is very similar
to the traditional `/etc/inetd.conf`, yet keeping with the style of
Finit:
```shell
# Launch SSH on demand, in runlevels 2-5 as root
inetd ssh/tcp nowait [2345] @root:root /usr/sbin/sshd -i
```
A more advanced example is listed below, please note the *incompatible
syntax change* that was made between Finit v1.12 and v1.13 to support
deny filters:
```shell
# Start sshd if inbound connection on eth0, port 222, or
# inbound on eth1, port 22. Ignore on other interfaces.
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
inetd ssh/tcp@eth1,eth1 nowait [2345] /usr/sbin/sshd -i
```
If `eth0` is your Internet interface you may want to avoid using the
default port. To run ssh on port 222, and all others on port 22:
```shell
inetd 222/tcp@eth0 nowait [2345] /usr/sbin/sshd -i
inetd ssh/tcp@*,!eth0 nowait [2345] /usr/sbin/sshd -i
```
Compared to Finit v1.12 you must *explicitly deny* access from `eth0`!
To protect against looping attacks, the inetd server will refuse UDP
service if the reply port corresponds to any internal service. Similar
to how the FreeBSD inetd operates.
**Internal Services**
Like the original `inetd`, Finit has a few standard services built-in.
They are realized as plugins to provide a simple means of testing the
inetd functionality stand-alone. But this also provides both a useful
network testing/availability, as well as a rudimentary time server for
`rdate` clients.
- echo
- chargen
- daytime
- discard
- time
For security reasons they are all disabled by default and have to be
enabled with both the `configure` script and a special `inetd` stanza in
the `finit.conf` or `finit.d/*.conf` like this:
```shell
inetd echo/udp wait [2345] internal
inetd echo/tcp nowait [2345] internal
inetd chargen/udp wait [2345] internal
inetd chargen/tcp nowait [2345] internal
inetd daytime/udp wait [2345] internal
inetd daytime/tcp nowait [2345] internal
inetd discard/udp wait [2345] internal
inetd discard/tcp nowait [2345] internal
inetd time/udp wait [2345] internal
inetd time/tcp nowait [2345] internal
```
Then call `rdate` from a remote machine (or use localhost):
```shell
rdate -p <IP>
rdate -up <IP>
```
Or `echoping` to reach the echo service:
```shell
echoping -v <IP>
echoping -uv <IP>
```
Or `echoping -d` to reach the discard service:
```shell
echoping -dv <IP>
echoping -duv <IP>
```
Or `echoping -c` to reach the chargen service:
```shell
echoping -cv <IP>
echoping -cuv <IP>
```
If you use `time/udp` you must use the standard rdate implementation and
then call it with `rdate -up` to connect using UDP. Without the `-p`
argument rdate will try to set the system clock. Please note that rdate
has been deprecated by the NTP protocol and this plugin should only be
used for testing or environments where NTP for some reason is blocked.
Also, remember the UNIX year 2038 bug, or in the case of RFC 868 (and
some NTP implementations), year 2036!
**Note:** There is currently no verification that the same port is used
more than once. So a standard `inetd http/tcp` service will clash
with an ssh entry for the same port `inetd 80/tcp` …
[TCP Wrappers]: https://en.wikipedia.org/wiki/TCP_Wrapper
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+132
View File
@@ -0,0 +1,132 @@
Hooks & Plugins
===============
Finit can be extended to add general functionality in the form of I/O
monitors, built-in inetd services, or hook plugins.
The following sections detail existing plugins and hook points. For
more information, see the plugins listed below.
Plugins
-------
For your convenience a set of *optional* plugins are available:
* *alsa-utils.so*: Restore and save ALSA sound settings on
startup/shutdown. _Optional plugin._
* *bootmisc.so*: Setup necessary files and system directories for, e.g.,
UTMP (tracks logins at boot). This plugin is central to get a working
system and runs at `HOOK_BASEFS_UP`. The `/var`, `/run`, and `/dev`
file systems must be writable for this plugin to work.
Note: On an embedded system both `/var` and `/run` can be `tmpfs` RAM
disks and `/dev` is usually a `devtmpfs`. This must be defined in the
`/etc/fstab` file and in the Linux kernel config.
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
_Optional plugin._
* *echo.so*: RFC 862 plugin. Start as inetd service, like time below.
* *chargen.so*: RFC 864 plugin. Start as inetd service, like time below.
* *daytime.so*: RFC 867 plugin. Start as inetd service, like time below.
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
* *hwclock.so*: Restore and save system clock from/to RTC on
startup/shutdown.
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
* *lost.so*: Very simple `HOOK_SVC_LOST` example. Logs process ID and
name to syslog. _Optional plugin._
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
interfaces. These events are then sent to the Finit service monitor
for services that may want to be SIGHUP'ed on new default route or
interfaces going up/down.
* *resolvconf.so*: Setup necessary files for `resolvconf` at startup.
_Optional plugin._
* *time.so*: RFC 868 (rdate) plugin. Start as inetd service. Useful
for testing inetd filtering — BusyBox has an rdate (TCP) client.
* *tty.so*: Watches `/dev`, using inotify, for new device nodes (TTY's)
to start/stop getty consoles on them on demand. Useful when plugging
in a usb2serial converter to login to your embedded device.
* *urandom.so*: Setup random seed at startup.
* *x11-common.so*: Setup necessary files for X-Window. _Optional plugin._
Usually you want to hook into the boot process once, simple hook plugins
like `bootmisc.so` are great for that purpose. They are called at each
hook point in the boot process, useful to insert some pre-bootstrap
mechanisms, like generating configuration files, restoring HW device
state, etc. Available hook points are:
Hooks
-----
### Bootstrap Hooks
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
mounted — very early
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
and default init signals are setup
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
networking is up (`lo` is up and the `network` script has run)
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
* `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d`
has been launched
### Runtime Hooks
* `HOOK_SVC_RECONF`: Called when the user has changed something in the
`/etc/finit.d` directory and issued `SIGHUP`. The hook is called when
all modified/removed services have been stopped. When the hook has
completed, Finit continues to start all modified and new services.
* `HOOK_SVC_LOST`: Called when a process is lost. When reconfiguring
services at runtime this hook may be called a lot. However, it may be
a quite useful hook to monitor a system post bootstrap when no, or
few, services are expected to exit. A default plugin `lost.so` is
available in the `plugins/` subdirectory as an example.
**NOTE:** This hook callback gets the lost PID as argument.
* `HOOK_SVC_START`: Like `HOOK_SVC_LOST`, but called when a process is
started. Same caveats apply.
**NOTE:** This hook callback gets the new PID as argument.
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
change. The hook is called when services not matching the new
runlevel have been been stopped. When the hook has completed, Finit
continues to start all services in the new runlevel.
### Shutdown Hooks
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
services are sent `SIGTERM`
Plugins like `initctl.so` and `tty.so` extend finit by acting on events,
they are called I/O plugins and are called from the finit main loop when
`poll()` detects an event. See the source code for `plugins/*.c` for
more help and ideas.
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+38
View File
@@ -0,0 +1,38 @@
Finit Services
==============
![The service state machine](svc-machine.png "The service state machine")
State Machine
-------------
A service is bound to a state machine that is in one of six (6) states.
For `run`s and `task`s there is an additional end state called `DONE`,
not shown in the diagram for brevity. Services start in the `HALTED`
state.
The current state depends on the two following conditions:
* `E`: Service enabled. In order for `E` to be satisfied, the service
must be allowed to run in the current runlevel and must not be
blocked. A service may be blocked for several reasons:
- The user has manually stopped the service using `initctl stop JOB`
- The program exits immediately. I.e. keeps crashing (make sure to use
the 'run this service in the foreground' command line option)
- The binary is missing in the filesystem
* `C`: Service conditions are satisfied:
- `on` (+): The condition is asserted.
- `off` (-): The condition is deasserted.
- `flux` (~): The conditions state is unknown.
For a detailed description of conditions, and how to debug them,
see the [Finit Conditions](conditions.md) document.
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

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