Compare commits

...
86 Commits
Author SHA1 Message Date
Joachim Nilsson 5579ba16b1 Bump version for v3.2-rc1 pre-release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-16 08:58:56 +02:00
Joachim Nilsson 510b36645b Fix potential buffer overruns found by GCC 8.2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-15 22:56:23 +02:00
Joachim Nilsson 67cd88cd23 svc_status(): Fix potential fall-through in switch-case
Found by GCC 8.2

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-15 22:54:33 +02:00
Joachim Nilsson a04a6eda12 Update ChangeLog for upcoming v3.2 bug fix release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-13 21:32:56 +02:00
Joachim Nilsson 94525d6aae Update .gitignore
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-13 20:16:29 +02:00
Joachim Nilsson 1d14806fd8 Never make promises about content in release versions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-13 20:14:45 +02:00
Joachim Nilsson 5e6f64eec9 Bump version for new v3.2 release cycle
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-11 16:10:29 +02:00
Joachim Nilsson df22628183 Refactor end of bootstrap using new work queue helper
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-09 07:14:33 +02:00
Joachim Nilsson 4870cab898 Refactor, use new work queue helper
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-07 21:30:07 +02:00
Joachim Nilsson 859c216347 schedule: Add simple work queue helper
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-07 21:29:10 +02:00
Joachim Nilsson 931565c425 Simplify pidfile_reconf() thanks to the addition of schedule_wq()
We can now rely on service_step() to continue stepping run/taks/services
until no more state transitions are made.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-07 18:35:12 +02:00
Joachim Nilsson 3006caf670 Refactor dddd45e, schedule work to iterate over services again
Instead of changing the API of service_step(), and relying on all the
different callers to step again, we can schedule another event to do
this for us.

In addition to handling the cond_update() case this also handles all
other callers of service_step().  Ensuring that nested conditions do
trigger changes.

Also, reverting dddd45e fixes a minor regression in service_monitor()
which caused it to not clean up collected bootstrap tasks.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-07 18:34:42 +02:00
Joachim Nilsson 1269f082de service_unregister(): Refactor, simplify and set #ifdef correctly
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-05 08:57:01 +02:00
Joachim Nilsson 7d444e07a5 service_stop(): Nothing to do if svc is already stopping or halted
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-05 08:54:08 +02:00
Joachim Nilsson 6ec4dce5f9 Travis-CI: Fix download URL
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-05 07:20:22 +02:00
Joachim Nilsson 8a0edbe801 Travis-CI: Update build requirements
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-05 07:14:43 +02:00
Joachim Nilsson 46683f8eb8 Remove service condition when service is deleted
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-04 21:56:01 +02:00
Joachim Nilsson fb16abd855 Require new libuEV API: uev_init1() to enforce a small event cache
In cases where you have multiple events pending in the cache and some
event may cause later ones, already sent by the kernel to userspace,
to be deleted the pointer returned to the event loop for this later
event may be deleted.

There are two ways around this (accessing deleted memory); 1) use this
function to initialize your event loop and set maxevents to 1, 2) use a
free list in you application that you garbage collect at intervals
relevant to your application.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-04 21:53:36 +02:00
Joachim Nilsson 060de93b5b Wait for all services to complete before changing runlevel
We allow all run/task/services to complete before changing runlevel.
This means stepping them all while we wait for their completion, since
services may depend on each other.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-03 08:40:23 +02:00
Joachim Nilsson 231b887b2a Follow-up to a5491b3, no need to restore svc state anymore
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-03 08:40:23 +02:00
Jonas Johansson dddd45e3b5 Reassert condition when svc_t goes from WAITING --> RUNNING
Reassert condition when an unchanged/unmodified process goes from
WAITING state to RUNNING.  I.e. it had a condition that went to flux
during `initctl reload`, which drove it to WAITING and was then sent
SIGSTOP during reconf.

Also, on condition update, loop through all services until no more state
changes are observed.  This allows long dependency chains of services to
resolve and actually go back to RUNNING state as intended whenever any
condition changes at runtime.

Signed-off-by: Jonas Johansson <jonasj76@gmail.com>
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-02 19:03:30 +02:00
Joachim Nilsson cb9f6213dc Instead of deleting svc_t, mark it for cleanup by garbage collector
When a process, that has been sent SIGTERM by us, takes a full 3 sec to
terminate, the event loop may have both the SIGCHLD event (where we do
the svc_del()) and the 3 sec timeout event to send SIGKILL in its event
cache.

When we call svc_del() it releases the svc_t memory, which can then be
dereferenced by the SIGKILL timer callback and we're doomed.

There are two fixes to this; 1) the event loop, that uses epoll_wait(),
can set maxevents=1 (instead of today's 10).  The kernel will then drop
the SIGKILL timer event before it's delivered to the userspace process.
2) we can postpone deleting the svc_t to a "later stage" when all events
in the event cache have been processed.

This patch implements (2).  A later patch will use uev_init1(ctx, 1) to
ensure the event cache handles only one event at a time.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-02 13:22:21 +02:00
Joachim Nilsson f33ed869e4 Minor, remove unused/malplaced declaration
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-02 13:18:33 +02:00
Joachim Nilsson 4790344fbe Minor, refactor
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-02 13:18:15 +02:00
Joachim Nilsson a40858f162 Fix #103: Register multiple getty if @console resolves to >1 TTY
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-30 16:32:48 +02:00
Joachim Nilsson 682e80fed5 Remove HOOK_SVC_LOST, recommend using accounting instead
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-30 11:28:27 +02:00
Joachim Nilsson cd91e2c66c Skip "Restarting ... " progress for bootstrap processes
Reduce the noise: we don't need to notify the user if a process started
in bootstrap is simply SIGHUPed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-29 15:16:59 +02:00
Joachim Nilsson 470549badb Add debug message to finit.d directory watcher callback
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-28 23:41:42 +02:00
Joachim Nilsson a85c3628b8 Wait for runlevel S to complete before deleting bootstrap tasks
If we delete bootstrap (only) run tasks in service_start() when they
complete they may be found and run again if *.conf reload takes place.

Instead, mark them as having been run, like regular runtasks, and set
their state directly to STOPPING.  The once flag ensures the svc is not
restarted if its .conf file is reloaded.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-28 23:37:02 +02:00
Joachim Nilsson 53ac0ffdfb Merge branch 'master' of ssh://git.troglobit.com:222/srv/git/finit 2018-09-28 06:57:26 +02:00
Joachim Nilsson eee6ffe95c Refactor end of service_start() to prevent deref freed svc
An SVC_TYPE_RUN may be deleted by svc_clean_bootstrap() as soon as it's
been collected.  We must not try to dereference that pointer afterwards,
e.g. in svc_is_daemon().

Refactor the whole of it to make the code easier to follow.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-28 06:50:33 +02:00
Joachim Nilsson a5491b3ded Remove HOOK_SVC_START, caused more problems than it was worth
An operator wanting to monitor processes started by Finit could use the
kernel ftrace framework.  E.g. execsnoop in perf-tools.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-28 06:49:49 +02:00
Petrus Hellgren a1ea8ff564 Bug fix: update inetd service args on config change
If an inetd service is changed, finit must update the command line
arguments associated with the service.

Signed-off-by: Petrus Hellgren <petrus.hellgren@westermo.se>
Signed-off-by: Petrus Hellgren <petrus.hellgren@gmail.com>
2018-09-27 14:55:01 +02:00
Joachim Nilsson 0dffa731d2 Extend bootrap timeout: 10 --> 120 sec
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-09-26 18:49:05 +02:00
Joachim Nilsson 9516c28a8a Add support for disabling (build+)install of docs and contrib section
In massively parallel builds we've seen finit fail in the install phase
of the contrib section with:

ln: failed to create symbolic link '/home/bob/jenkins/workspace/WeOS-feature_5.x_igmp-mld-snooping/staging/share/doc/finit/contrib/alpine/finit.d/getty.conf': No such file or directory
ln: failed to create symbolic link '/home/bob/jenkins/workspace/WeOS-feature_5.x_igmp-mld-snooping/staging/share/doc/finit/contrib/alpine/finit.d/keymap.conf': No such file or directory
ln: failed to create symbolic link '/home/bob/jenkins/workspace/WeOS-feature_5.x_igmp-mld-snooping/staging/share/doc/finit/contrib/alpine/finit.d/modules.conf': No such file or directory
ln: failed to create symbolic link '/home/bob/jenkins/workspace/WeOS-feature_5.x_igmp-mld-snooping/staging/share/doc/finit/contrib/alpine/finit.d/klogd.conf': No such file or directory
ln: failed to create symbolic link '/home/bob/jenkins/workspace/WeOS-feature_5.x_igmp-mld-snooping/staging/share/doc/finit/contrib/alpine/finit.d/syslogd.conf': No such file or directory

This patch is a countermeasure since fixing the root cause may take a
while (to be prioritized).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-08-28 11:25:52 +02:00
Joachim Nilsson fa0a4e8579 Merge branch 'dev' 2018-07-10 22:38:45 +02:00
Joachim Nilsson a487a30814 Refactor wdog tracking and hand-over to use svc_t instead of PID
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-07-09 23:49:04 +02:00
Joachim Nilsson 40db4f4f58 Minor, refactor to avoid namespace confusion with service.c API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-07-09 23:49:04 +02:00
Joachim Nilsson 3a1ad67e92 Convert built-in watchdog to a standalone mini watchdogd
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-07-09 23:49:04 +02:00
Joachim Nilsson 43942ab513 watchdog.c: Update copyright years and reflow license header
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-07-08 19:21:26 +02:00
Joachim Nilsson 3b2f2690e7 initctl: start and restart should behave the same if svc has crashed
Found by Mattias Walström, Westermo.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-06-18 18:33:49 +02:00
Joachim Nilsson 5684b761da Merge pull request #111 from jonasj76/inetd-stop-childen-v2
Only restart inetd services when neccessary
2018-05-07 10:06:33 +02:00
Jonas Johansson 59f22c7d27 fixup! inetd: only restart inetd services when neccessary
Signed-off-by: Jonas Johansson <jonasj76@gmail.com>
2018-05-07 09:25:55 +02:00
Jonas Johansson f522a26125 inetd: only restart inetd services when neccessary
Do not restart a inetd service if the listening interface is changed.
Only bring down established connection which are no longer allowed,
i.e. do not touch already allowed established connections.

Signed-off-by: Jonas Johansson <jonasj76@gmail.com>
2018-05-03 08:57:19 +02:00
Joachim Nilsson efb1d40ffd Merge pull request #107 from jonasj76/inetd-stop-children
Stop inetd spawned child processes when stopping inetd service
2018-03-23 15:48:02 +01:00
Joachim Nilsson ea591c7580 Merge pull request #106 from jonasj76/inetd-dont-mark
Do not mark inetd connections for deletion when reloading services
2018-03-23 15:47:11 +01:00
Jonas Johansson af9a06466c inetd: stop inetd spawned child processes when stopping inetd service
Signed-off-by: Jonas Johansson <jonasj76@gmail.com>
2018-03-23 15:40:42 +01:00
Jonas Johansson 7ed9f2ad05 inetd: do not mark inetd connections for deletion when reloading services
Signed-off-by: Jonas Johansson <jonasj76@gmail.com>
2018-03-23 15:32:41 +01:00
Joachim Nilsson 65ccc407fa Follow-up #100: Remove buggy "optimization", caught by Coverity Scan
We cannot allow pid_runpath() to return its 'file' argument because that
may be a stack variable in a helper function.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-22 08:08:48 +01:00
Joachim Nilsson 7fe9f3c4b6 Travis-CI: Disable CLANG build temporarily for Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-22 08:06:57 +01:00
Joachim Nilsson 24299ab5d9 Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-21 08:44:14 +01:00
Joachim Nilsson e9670b87b3 Refactor, convert struct tty members to static strings
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-18 19:19:45 +01:00
Joachim Nilsson 406339e755 Refactor, unify finit_tty_t + tty_node_t --> struct tty
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-18 18:19:31 +01:00
Joachim Nilsson dc7039b8d8 Fix #100: Adjust path to COND_RECONF /var/run symlink may be missing
On systems with the new /run hierarchy the compat symlink from /var/run
may be missing, or not yet be set up by bootmisc.so.  This patch adds a
layer of safety to the condition layer, both set and get cond ops now
perform an adjustmed of the condition path if needed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-18 11:49:27 +01:00
Joachim Nilsson 90249e50ef initctl: Show available services if none given
In case a user forgets to type in the service to enable, disable, or
touch, we try to be helpful and list available services instead of
bugging out.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-06 18:41:36 +01:00
Joachim Nilsson b8e6954c46 Issue #96: Remember to track run commands having run once as well
All service/task commands are tracked by the state machine, but run
commands are a bit special since they must run in sequence.  Hence,
we must increment their 'once' counter in service_start() instead.

This should fix the issue mentioned in #96 "took a long time before
the next line" -- because Finit was waiting for udevadm to reach a
once count > 0, which we didn't increment.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 23:57:50 +01:00
Joachim Nilsson 4585aa0fb4 Issue #96: Add udevd condition to udevadm trigger commands
We must wait for udevd to be started before we call udevadm.  However,
udevd doesn't create a PID file, so we must also fake this.  There is
still a slight risk of a race condition: udevd not having properly
started before udevadm tries to connect, but it works OK in Debian.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 23:57:27 +01:00
Joachim Nilsson f2655d14c8 Bootstrap condition subsystem as soon as possible
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 23:57:27 +01:00
Joachim Nilsson 6d1d51b28c Remove duplicate 'services' in debug message
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 23:57:27 +01:00
Joachim Nilsson b9d233d19e Issue #96: Register two unique (!) udev triggers, run device trig 1st
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 08:43:15 +01:00
Joachim Nilsson 2ec132c75a Fix #96: Start udevd as a proper service
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 01:06:32 +01:00
Joachim Nilsson 3092d55ec5 dbus: Fix too short buffer for dbus-daemon command line
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 00:40:54 +01:00
Joachim Nilsson cd791afea5 Revert "dbus: Use --syslog-only instead of Finit built-in log redirect"
This reverts commit df89cc79b9.  Not all
distros (Debian, Alpine) have a dbus-daemon that supports --syslog-only

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-03-01 00:22:01 +01:00
Joachim Nilsson 829405f966 watchdog: Do not emit error message if user has no /dev/watchdog
Running Finit on a kernel or system without WDT support built-in should
not trigger an error message from Finit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-26 23:05:24 +01:00
Joachim Nilsson 72526d9e88 Revert #52: don't reload .conf files after run-parts and rc.local
Issue #52 suggested calling service_reload_dynamic() automatically after
run-parts and /etc/rc.local.  This may seem like a good idea, but not
only does it create extra overhead, it currently also freezes the boot
and fails to present a login prompt if a run-parts directory exists.
Yes, the directory can be empty, it just have to exist to trigger this
regression.

While trying to find the root cause I realized this was all just wrong.
If the user does something that requires reloading finit, they should
call `initctl reload` from the script instead.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-26 22:13:02 +01:00
Joachim Nilsson f704eab6d7 Increase debug level when starting a TTY
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-26 22:11:32 +01:00
Joachim Nilsson a50bfc016e Set $SHELL to a sane default value, used by BusyBox
When finit calls out to external tools, like `ifup $IFACE`, that tool in
turn may use other external tools.  On systems with BusyBox our calling
`ifup $IFACE` will result in BusyBox casting a magic spell: if $SHELL is
unset it goes looking in the file /etc/passwd for the shell of $USER
which may not be set to a Bourne compatible UNIX shell, so commands like
`ip link ...` or `run-parts ...` will fail hard.

This patch adds SHELL=/bin/sh as a sane early default.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-26 22:07:31 +01:00
Joachim Nilsson 396c1b491f Document default prio + tag for log redirect option
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-17 17:31:16 +01:00
Joachim Nilsson df89cc79b9 dbus: Use --syslog-only instead of Finit built-in log redirect
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-17 17:28:57 +01:00
Joachim Nilsson 3429901d83 dbus: Silence dbus daemon, redirect stdout/stderr to syslog
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-17 17:26:39 +01:00
Joachim Nilsson dedf885b92 Prefer udev to handle /dev if mdev is also available on the system
Some systems, usually those built around BusyBox, have mdev installed
but have an option to install udev.  In those cases the user likely
prefers to use udev over mdev and this patch addresses that.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-17 16:06:22 +01:00
Joachim Nilsson 93a21197ab Fix artefact bug with missing OS/Finit title in banner
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-17 16:06:22 +01:00
Joachim Nilsson 8a0fffb00d Minor, whitespace
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-17 16:06:22 +01:00
Joachim Nilsson 7fdb280712 initctl: Introduce -b[atch] mode for non-interactive use
This patch introduces a new option to initctl; -b, --batch skips
screen_init(), which might otherwise leak ANSI screen resize codes
to the console.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-16 10:06:37 +01:00
Joachim Nilsson 0a643953c0 Merge pull request #99 from westermo/usr/wkz/inetd-timer-crash
Do not try to SIGKILL inetd services, they are not backed by a pid
2018-02-09 14:44:17 +01:00
Tobias Waldekranz fd837a4242 Do not try to SIGKILL inetd services, they are not backed by a pid
As a precaution, services who are being stopped are automatically sent
a SIGKILL after 3 seconds if they refuse to go away willingly.

Unfortunately this backup mechanism was also enabled for inetd
services which are not backed by a real process. When the service was
freed the timer was not stopped, since it was not expected to be
armed. The timer would then trigger, causing a use-after-free on the
timer watcher containing a bogus callback pointer.
2018-02-09 14:11:48 +01:00
Joachim Nilsson 2759992dd2 contrib/: Fix parallell build, mkdir -p *all* the things
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-02-06 17:30:33 +01:00
Joachim Nilsson d880cd75ae Update TODO, add uredir to list of features for v3.2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-30 20:32:26 +01:00
Joachim Nilsson 29fe8cc73d Move run/tasks to stopping state when they exit, like services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-29 18:40:41 +01:00
Joachim Nilsson 2b6f00f126 Minor, whitespace
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-27 17:53:23 +01:00
Joachim Nilsson 86e534095d Always check svc_t pointer before dereferencing it
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-26 12:47:45 +01:00
Joachim Nilsson 865a3a7a4a Regression, unblock (UDP) parent for terminating inetd connections
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-26 12:46:15 +01:00
Joachim Nilsson 2dc7de3aa2 Make sure to unblock inetd UDP svc when connection terminates
When an UDP inetd connection terminates we must make sure to unblock the
parent, which is (currently) blocked during the transaction.

This patch is a fix to a regression introduced in 2c904b5, for v3.1

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-25 19:14:59 +01:00
Joachim Nilsson 06a5b43005 Outline plans for Finit 3.2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 18:28:28 +01:00
Joachim Nilsson 38939cc57e Travis-CI: Add static + disabled inetd build step
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 15:59:42 +01:00
Joachim Nilsson a3eadb72a7 Fix #98, handle --disable-inetd case in service_unregister()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 15:48:41 +01:00
45 changed files with 814 additions and 481 deletions
+9 -6
View File
@@ -9,9 +9,9 @@ sudo: false
#sudo: true
# Test build with both GCC and Clang (LLVM)
# - clang
compiler:
- gcc
- clang
env:
global:
@@ -33,20 +33,23 @@ addons:
branch_pattern: dev
install:
- wget https://github.com/troglobit/libuev/releases/download/v2.1.0/libuev-2.1.0.tar.xz
- wget https://github.com/troglobit/libuev/releases/download/v2.2.0/libuev-2.2.0.tar.xz
- wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
- tar xf libuev-2.1.0.tar.xz
- tar xf libuev-2.2.0.tar.xz
- tar xf libite-2.0.1.tar.xz
- (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libuev-2.2.0 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip)
# Custom build script for the time being, no "make test" yet
script:
- ./autogen.sh
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --disable-inetd --enable-static
- make clean
- make V=1 -j5
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --enable-inetd-echo-plugin --enable-inetd-time-plugin --enable-x11-common-plugin --enable-inetd-chargen-plugin --enable-fallback-shell --enable-watchdog
- cat config.log
- make clean
- make -j5 V=1
- make V=1 -j5
- make install-strip
- tree /tmp
- ldd /tmp/sbin/finit
+69
View File
@@ -4,6 +4,74 @@ Change Log
All relevant changes are documented in this file.
[3.2][UNRELEASED] - 2018-10-xx
------------------------------
Bug fix release.
### Changes
* Add support for `--disable-docs` and `--disable-contrib` to speed up
builds and work around issue with massively parallel builds
* Add `-b`, batch mode, for non-interactive use to `initctl`
* Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog
* Set `$SHELL`, like `$PATH`, to a sane default value, needed by BusyBox
* Finit no longer automatically reloads its `*.conf` files after running
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
* `initctl` without an argument or option now defaults to list services
* Converted built-in watchdog daemon to standalone mini watchdogd
* Improved watchdog hand-over, now based on `svc_t` and not PID
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
not allowed in the runtime runlevel are unconditionally stopped
* Removed `HOOK_SVC_START` and `HOOK_SVC_LOST`, caused more problems
than they were worth. Users are encouraged to use accounting instead
* Skip displaying "Restarting ..." progress for bootstrap processes
* Added a simple work queue mechanism to queue up work at boot + runtime
- Postpone deletion of `svc_t` until any `SIGKILL` timer has elapsed
- As long as a stepped service changes state we queue another step all
event, because services may depend on each other
* Require new libuEv API: `uev_init1()` to reduce event cache so that
the kernel can invalidate deleted events before enqueing to userspace
### Fixes
* Fix #96: Start udevd as a proper service
* Ensure we track run commands as well as task/service, once per runlevel
* Fix #98: FTBFS with `--disable-inetd`
* Make sure to unblock UDP inetd services when connection terminates.
Regression introduced in v3.1
* Ensure run/tasks also go to stopping state on exit, like services,
otherwise it is unnecessarily hard to restart them
* Fix #99: Do not try to `SIGKILL` inetd services, they are not backed
by a PID. This caused a use after free issue crashing finit. Found
and fixed by Tobias Waldekranz, Westermo
* Fix missing OS/Finit title bug, adds leading newline before banner
* Remove "Failed connecting to watchdog ..." error message on systems
that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
Fixed by Jonas Johansson, Westermo
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
Fixed by Jonas Johansson, Westermo
* Fix #111: Only restart inetd services when necessary. E.g., if the
listening interface is changed. Only stop established connections
which are no longer allowed, i.e. do not touch already allowed
established connections. Fixed by Jonas Johansson, Westermo
* Fix: update inetd service args on config change. Found and fixed by
Petrus Hellgren, Westermo
* Fix `initctl [start | restart]`, should behave the same for services
that have crashed. Found by Mattias Walström, Westermo
* Wait for bootstrap phase to complete before cleaning out any bootstrap
processes that have stopped, they may be restarted again
* Reassert condition when an unmodified run/task/service goes from
WAITING back to RUNNING again after a reconfiguration event.
Found and fixed by Jonas Johansson, Westermo
* Remove service condition when service is deleted
[3.1][] - 2018-01-23
--------------------
@@ -710,6 +778,7 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
[3.2]: https://github.com/troglobit/finit/compare/3.1...3.2
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
+8 -1
View File
@@ -1,8 +1,15 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = contrib docs plugins src
SUBDIRS = plugins src
doc_DATA = README.md LICENSE contrib/finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
if CONTRIB
SUBDIRS += contrib
endif
if DOCS
SUBDIRS += docs
endif
# Compat hook
install-dev:
@make -C src install-pkgincludeHEADERS
+19 -6
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 3.1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AC_INIT(Finit, 3.2-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
AM_SILENT_RULES([yes])
@@ -41,7 +41,7 @@ AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 2.1.0])
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
# Check for configured Finit features
@@ -78,6 +78,14 @@ AC_ARG_ENABLE(logrotate,
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
enable_logrotate=yes])
AC_ARG_ENABLE(docs,
AS_HELP_STRING([--disable-docs], [Disable build and install of docs section]),,[
enable_docs=yes])
AC_ARG_ENABLE(contrib,
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
enable_contrib=yes])
# Check for extra plugins to enable
enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
@@ -87,7 +95,6 @@ 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.])
@@ -216,11 +223,15 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
AM_CONDITIONAL(DOCS, [test "x$enable_docs" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
# Override default libdir, used for plugins and rescue.conf
#pkglibdir=$libdir/finit
#AC_SUBST(pkglibdir)
#AC_EXPAND_DIR(pkglibdir, "$libdir/finit")
pkglibdir=$libdir/finit
AC_SUBST(pkglibexecdir)
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path])
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
AC_SUBST(plugin_path)
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
@@ -252,6 +263,8 @@ Behavior:
Plugins...............: $plugins
Optional features:
Install docs/.........: $enable_docs
Install contrib/......: $enable_contrib
Built-in inetd........: $enable_inetd
Built-in watchdogd....: $enable_watchdog
Built-in logrotate....: $enable_logrotate
+1 -1
View File
@@ -4,7 +4,7 @@ d_LINKS = dropbear.conf getty.conf keymap.conf modules.conf klogd.conf \
syslogd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
+1 -1
View File
@@ -4,7 +4,7 @@ d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
modules.conf rsyslogd.conf sshd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
+1 -1
View File
@@ -3,7 +3,7 @@ docsdir := @docdir@/contrib/void/finit.d
d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
+15 -21
View File
@@ -18,16 +18,27 @@ after a reconfiguration (`initctl reload`), the Finit dependency system
can also be used.
General
-------
Near Future
-----------
* Add support for timed shutdown in Finit, including cancelled shutdown,
possibly using an at-job, see [crond section](#Crond) below
* Merge UDP redirect service uredir as an UDP inetd variant
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Cron/At support, see below
* Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ...
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
how close Finit3 still is to the original easy-to-use Finit0 :)
General
-------
* Add support for timed shutdown in Finit, including cancelled shutdown,
possibly using an at-job, see [crond section](#Crond) below
* Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json`
* [Solaris SMF][] (Service Management Facility) has some interesting
@@ -104,21 +115,4 @@ the shell -- that way setting up one-time jobs would not entail
re-reading `/etc/finit.conf`
Documentation
-------------
* Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ...
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
how close Finit3 still is to the original easy-to-use Finit0 :)
[libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so
[Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
-2
View File
@@ -53,8 +53,6 @@ Below are a few of the main switches to configure:
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
* `--enable-lost-plugin`: Enable noisy example plugin for `HOOK_SVC_LOST`.
* `--enable-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin.
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
+4 -1
View File
@@ -341,11 +341,14 @@ syslog using the native `logit` tool. The full syntax is:
log:null
log
Default `prio` is `daemon.info` and default `tag` is the basename of the
service or run/task command.
Log rotation is controlled using the global `log` setting.
**Example:**
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document.
-16
View File
@@ -47,9 +47,6 @@ For your convenience a set of *optional* plugins are available:
* *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
@@ -102,19 +99,6 @@ Hooks
all modified/removed services have been stopped. When the hook has
completed, Finit continues to start all modified and new services.
* `HOOK_SVC_LOST`: Called when a process is lost. When reconfiguring
services at runtime this hook may be called a lot. However, it may be
a quite useful hook to monitor a system post bootstrap when no, or
few, services are expected to exit. A default plugin `lost.so` is
available in the `plugins/` subdirectory as an example.
**NOTE:** This hook callback gets the lost PID as argument.
* `HOOK_SVC_START`: Like `HOOK_SVC_LOST`, but called when a process is
started. Same caveats apply.
**NOTE:** This hook callback gets the new PID as argument.
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
change. The hook is called when services not matching the new
runlevel have been been stopped. When the hook has completed, Finit
-8
View File
@@ -39,10 +39,6 @@ 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
@@ -84,10 +80,6 @@ pkglib_LTLIBRARIES += time.la
endif
endif
if BUILD_LOST_PLUGIN
pkglib_LTLIBRARIES += lost.la
endif
if BUILD_NETLINK_PLUGIN
pkglib_LTLIBRARIES += netlink.la
endif
+2 -2
View File
@@ -38,7 +38,7 @@
static void setup(void *arg)
{
char *cmd;
char line[80];
char line[256];
cmd = which(DAEMON);
if (!cmd) {
@@ -58,7 +58,7 @@ static void setup(void *arg)
erase("/var/run/dbus/pid");
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345] %s %s -- %s", cmd, ARGS, DESC);
snprintf(line, sizeof(line), "[S12345] log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON);
free(cmd);
+22 -25
View File
@@ -37,12 +37,6 @@ struct context {
int wd;
};
static char *mkcond(char *buf, size_t len, char *nm)
{
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
return buf;
}
static void pidfile_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
@@ -79,35 +73,38 @@ static void pidfile_callback(void *arg, int fd, int events)
}
/*
* Assert condition only if the service is running, but not if it's
* recently been changed or while it's starting up.
* This function is called after `initctl reload` to reassert conditions
* for services that have not been changed.
*
* We must wait for the service to create/touch its pidfile.
* We reassert the run/task/service's condition only if it is running,
* but not if it's recently been changed or while it's starting up.
*/
static void pidfile_reconf(void *arg)
{
static char cond[MAX_COND_LEN];
svc_t *svc, *iter = NULL;
int restart = 0;
do {
restart = 0;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->state != SVC_RUNNING_STATE)
continue;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
mkcond(cond, sizeof(cond), svc->cmd);
if (svc->state == SVC_RUNNING_STATE &&
!svc_is_changed(svc) &&
!svc_is_starting(svc) &&
cond_get(cond) != COND_ON) {
cond_set_path(cond_path(cond), COND_ON);
restart = 1;
}
}
if (svc_is_changed(svc) || svc_is_starting(svc))
continue;
if (restart)
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
mkcond(cond, sizeof(cond), svc->cmd);
if (cond_get(cond) == COND_ON)
continue;
} while (restart);
cond_set_path(cond_path(cond), COND_ON);
}
/*
* This will call service_step(), which in turn will schedule itself
* as long as stepped services change state. Services going from
* WAITING to RUNNING will reassert their conditions in that loop,
* which in turn may unlock other services, and so on.
*/
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
}
static void pidfile_init(void *arg)
+5 -5
View File
@@ -57,7 +57,7 @@ static void watcher(void *arg, int fd, int events)
{
int len = 0;
char buf[EVENT_SIZE], name[42];
tty_node_t *entry;
struct tty *entry;
struct inotify_event *notified = (struct inotify_event *)buf;
while ((len = read(fd, buf, sizeof(buf)))) {
@@ -72,11 +72,11 @@ static void watcher(void *arg, int fd, int events)
snprintf(name, sizeof(name), "/dev/%s", notified->name);
entry = tty_find(name);
if (entry && tty_enabled(&entry->data)) {
if (entry && tty_enabled(entry)) {
if (notified->mask & IN_CREATE)
tty_start(&entry->data);
else if (entry->data.pid)
tty_stop(&entry->data);
tty_start(entry);
else if (entry->pid)
tty_stop(entry);
}
}
}
+1
View File
@@ -3,5 +3,6 @@ finit
initctl
logit
reboot
watchdogd
Makefile
Makefile.in
+10 -3
View File
@@ -10,6 +10,9 @@ endif
bin_PROGRAMS = logit
sbin_PROGRAMS = finit initctl reboot
if WATCHDOGD
pkglibexec_PROGRAMS = watchdogd
endif
logit_SOURCES = logit.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
@@ -25,6 +28,7 @@ finit_SOURCES = api.c \
mdadm.c mount.c \
pid.c pid.h \
plugin.c plugin.h private.h \
schedule.c schedule.h \
service.c service.h \
sig.c sig.h \
sm.c sm.h \
@@ -36,9 +40,6 @@ pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h log.h plugin.h svc.h
if INETD
finit_SOURCES += inetd.c inetd.h
endif
if WATCHDOGD
finit_SOURCES += watchdog.c watchdog.h
endif
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
@@ -63,6 +64,12 @@ reboot_LDADD = $(lite_LIBS)
INIT_LNKS = telinit init
REBOOT_LNKS = shutdown halt poweroff suspend
if WATCHDOGD
watchdogd_SOURCES = watchdog.c watchdog.h
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
#log.c log.h
endif
# Hook in install to add finit and reboot symlink(s)
install-exec-hook:
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
+27 -13
View File
@@ -44,6 +44,7 @@
#include "service.h"
#include "util.h"
extern svc_t *wdog;
static uev_t api_watcher;
static int call(int (*action)(svc_t *), char *buf, size_t len)
@@ -51,7 +52,7 @@ static int call(int (*action)(svc_t *), char *buf, size_t len)
return svc_parse_jobstr(buf, len, action, NULL);
}
static int service_stop(svc_t *svc)
static int stop(svc_t *svc)
{
if (!svc)
return 1;
@@ -62,7 +63,7 @@ static int service_stop(svc_t *svc)
return 0;
}
static int service_start(svc_t *svc)
static int start(svc_t *svc)
{
if (!svc)
return 1;
@@ -73,26 +74,29 @@ static int service_start(svc_t *svc)
return 0;
}
static int service_restart(svc_t *svc)
static int restart(svc_t *svc)
{
if (!svc)
return 1;
if (svc_is_blocked(svc))
svc_start(svc);
svc_mark_dirty(svc);
service_step(svc);
return 0;
}
static int do_start (char *buf, size_t len) { return call(service_start, buf, len); }
static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); }
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
static char query_buf[368];
static int missing(char *job, int id)
{
char buf[20];
char idstr[10] = "";
char idstr[13] = "";
if (!job)
job = "";
@@ -337,13 +341,23 @@ static void api_cb(uev_t *w, void *arg, int events)
break;
}
if (wdogpid > 0 && wdogpid != rq.runlevel) {
_d("Sending SIGTERM to %d", wdogpid);
kill(wdogpid, SIGTERM);
do_sleep(1);
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
svc = svc_find_by_pid(rq.runlevel);
if (!svc) {
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
break;
}
_d("wdog was %d, now %d is in charge", wdogpid, rq.runlevel);
wdogpid = rq.runlevel;
/* Disable and allow Finit to collect bundled watchdog */
if (wdog) {
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
stop(wdog);
if (wdog->protected) {
wdog->protected = 0;
wdog->runlevels = 0;
}
}
wdog = svc;
break;
case INIT_CMD_SVC_ITER:
+7 -3
View File
@@ -31,12 +31,16 @@
#include "pid.h"
#include "service.h"
static int cond_set_gen(const char *path, unsigned int gen)
static int cond_set_gen(const char *file, unsigned int gen)
{
char *ptr, path[256];
FILE *fp;
int ret;
fp = fopen(path, "w");
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "w");
if (!fp)
return -1;
@@ -148,7 +152,7 @@ static void cond_update(const char *name)
}
void cond_set(const char *name)
{
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
+12 -2
View File
@@ -30,6 +30,12 @@
#include "service.h"
char *mkcond(char *buf, size_t len, char *nm)
{
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
return buf;
}
const char *condstr(enum cond_state s)
{
static const char *strs[] = {
@@ -51,13 +57,17 @@ const char *cond_path(const char *name)
return pid_runpath(tmp, file, sizeof(file));
}
unsigned int cond_get_gen(const char *path)
unsigned int cond_get_gen(const char *file)
{
char *ptr, path[256];
unsigned int gen;
FILE *fp;
int ret;
fp = fopen(path, "r");
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "r");
if (!fp)
return 0;
+1
View File
@@ -13,6 +13,7 @@ typedef enum cond_state {
COND_ON
} cond_state_t;
char *mkcond (char *buf, size_t len, char *nm);
const char *condstr (enum cond_state s);
const char *cond_path (const char *name);
unsigned int cond_get_gen (const char *path);
+2 -1
View File
@@ -267,7 +267,7 @@ char *rlim2str(int rlim)
char *lim2str(struct rlimit *rlim)
{
char tmp[20];
char tmp[25];
static char buf[42];
memset(buf, 0, sizeof(buf));
@@ -727,6 +727,7 @@ static int do_change(char *name, uint32_t mask)
return 1;
}
_d("Event registered for %s, mask 0x%x", name, mask);
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
return 0;
+1 -1
View File
@@ -363,7 +363,7 @@ pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, stru
if (!pid) {
speed_t speed = B38400;
if (baud) {
if (baud && baud[0]) {
speed = stty_parse_speed(baud);
if (B0 == speed)
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
+111 -70
View File
@@ -47,9 +47,8 @@
#include "tty.h"
#include "util.h"
#include "utmp-api.h"
#include "watchdog.h"
#include "schedule.h"
int wdogpid = 0; /* No watchdog by default */
int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int prevlevel = -1;
@@ -63,6 +62,10 @@ char *rcsd = FINIT_RCSD;
char *runparts = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
svc_t *wdog = NULL; /* No watchdog by default */
static int udev = 0; /* Runtime detection of udev */
/*
* Show user configured banner before service bootstrap progress
@@ -81,7 +84,7 @@ static void banner(void)
return;
memset(separator, '=', sizeof(separator));
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
fprintf(stderr, "\n\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
}
static int ismnt(char *file, char *dir)
@@ -214,15 +217,20 @@ static void emergency_shell(void)
*/
static void finalize(void)
{
svc_t *svc;
/*
* Track bundled watchdogd in case a better one turns up
*/
svc = svc_find(FINIT_LIBPATH_ "/watchdogd", 1);
if (svc)
wdog = svc;
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue) {
_d("Running startup scripts in %s ...", runparts);
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
if (conf_any_change())
service_reload_dynamic();
}
/*
* Start all tasks/services in the configured runlevel
@@ -240,11 +248,8 @@ static void finalize(void)
service_step_all(SVC_TYPE_ANY);
/* Convenient SysV compat for when you just don't care ... */
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue) {
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
if (conf_any_change())
service_reload_dynamic();
}
/* Hooks that should run at the very end */
_d("Calling all system up hooks ...");
@@ -260,13 +265,61 @@ static void finalize(void)
tty_runlevel();
}
/*
* Start cranking the big state machine
*/
static void crank_worker(void *unused)
{
/*
* Initalize state machine and start all bootstrap tasks
* NOTE: no network available!
*/
sm_init(&sm);
sm_step(&sm);
/* Debian has this little script to copy generated rules while the system was read-only */
if (udev && fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
}
/*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
* allowed to complete their work in [S], or timeout, before we call
* finalize(), should not take more than 120 sec.
*/
static void final_worker(void *work)
{
static int cnt = 120;
_d("Step all services ...");
service_step_all(SVC_TYPE_ANY);
if (cnt-- > 0 && !service_completed()) {
_d("Not all bootstrap run/tasks have completed yet ... %d", cnt);
schedule_work(work);
return;
}
if (cnt > 0)
_d("All run/task have completed, resuming bootstrap.");
else
_d("Timeout, resuming bootstrap.");
finalize();
}
int main(int argc, char* argv[])
{
struct wq crank = {
.cb = crank_worker
};
struct wq final = {
.cb = final_worker,
.delay = 1000
};
uev_ctx_t loop;
char *path;
char cmd[256];
int udev = 0;
uev_t timer; /* Bootstrap timer, on timeout call finalize() */
uev_ctx_t loop;
/*
* finit/init/telinit client tool uses /dev/initctl pipe
@@ -278,13 +331,14 @@ int main(int argc, char* argv[])
/*
* Initalize event context.
*/
uev_init(&loop);
uev_init1(&loop, 1);
ctx = &loop;
/*
* Set the PATH early to something sane
* Set PATH and SHELL early to something sane
*/
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
/*
* Mount base file system, kernel is assumed to run devtmpfs for /dev
@@ -372,54 +426,56 @@ int main(int argc, char* argv[])
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
umask(022);
/* Bootstrap conditions, needed for hooks */
cond_init();
/*
* Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system.
*/
path = which("mdev");
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (log_is_debug())
touch("/dev/mdev.log");
snprintf(cmd, sizeof(cmd), "%s -s", path);
} else {
/* Desktop and server distros usually have a variant of udev */
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
udev = 1;
udev = 1;
/* Register udevd as a monitored service, started much later */
snprintf(cmd, sizeof(cmd), "[12345] %s -- Device event manager daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
udev = 0;
}
/* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
udev = 0;
} else {
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
"udevadm trigger -c add -t devices "
"-- Requesting device events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
/* Start a temporary udevd instance to populate /dev */
snprintf(cmd, sizeof(cmd), "%s --daemon", path);
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
"udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
}
}
if (path) {
free(path);
} else {
path = which("mdev");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (log_is_debug())
touch("/dev/mdev.log");
run_interactive(cmd, "Populating device tree");
if (udev && whichp("udevadm")) {
run("udevadm trigger --action=add --type=subsystems");
run("udevadm trigger --action=add --type=devices");
run("udevadm settle --timeout=120");
snprintf(cmd, sizeof(cmd), "%s -s", path);
free(path);
/* Tell temporary udevd to exit, we'll start a monitored instance later */
run("udevadm control --exit");
run_interactive(cmd, "Populating device tree");
}
}
/*
* Start built-in watchdog as soon as possible, if enabled
* Start bundled watchdogd as soon as possible, if enabled
*/
wdogpid = watchdog(argv[0]);
if (which(FINIT_LIBPATH_ "/watchdogd"))
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd", global_rlimit, NULL);
/*
* Mount filesystems
@@ -433,13 +489,6 @@ int main(int argc, char* argv[])
#endif
}
/* Debian has this little script to copy generated rules while the system was read-only */
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
/* Bootstrap conditions, needed for hooks */
cond_init();
if (!rescue) {
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
@@ -469,26 +518,18 @@ int main(int argc, char* argv[])
*/
conf_monitor(&loop);
/*
* Initalize state machine and start all bootstrap tasks
* NOTE: no network available!
*/
sm_init(&sm);
sm_step(&sm);
/* Start new initctl API responder */
_d("Starting initctl API responder ...");
api_init(&loop);
umask(022);
/*
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
* [S], or timeout, before calling finalize()
*/
_d("Starting the big state machine ...");
schedule_work(&crank);
_d("Starting bootstrap finalize timer ...");
uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000);
schedule_work(&final);
/*
* Enter main loop to monior /dev/initctl and services
* Enter main loop to monitor /dev/initctl and services
*/
_d("Entering main loop ...");
return uev_run(&loop, 0);
-1
View File
@@ -92,7 +92,6 @@ struct init_request {
char data[368];
};
extern int wdogpid;
extern int runlevel;
extern int cfglevel;
extern int prevlevel;
+1 -1
View File
@@ -237,7 +237,7 @@ int getty(char *tty, speed_t speed, char *term, char *user)
stty(fd, speed);
close(fd);
if (term)
if (term && term[0])
setenv("TERM", term, 1);
return do_login(name);
+42 -14
View File
@@ -43,12 +43,13 @@
} while (0);
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
static int inetd_dgram_peek(int sd, char *ifname)
static int inetd_dgram_peek(int sd, char *ifname, size_t len)
{
char cmbuf[0x100];
struct msghdr msgh;
struct cmsghdr *cmsg;
struct msghdr msgh;
char cmbuf[0x100];
memset(ifname, 0, len);
memset(&msgh, 0, sizeof(msgh));
msgh.msg_control = cmbuf;
msgh.msg_controllen = sizeof(cmbuf);
@@ -56,13 +57,15 @@ static int inetd_dgram_peek(int sd, char *ifname)
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
return -1;
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
char tmp[IF_NAMESIZE + 1] = { 0 };
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
continue;
if_indextoname(ipi->ipi_ifindex, ifname);
if_indextoname(ipi->ipi_ifindex, tmp);
strlcpy(ifname, tmp, len);
return 0;
}
@@ -73,11 +76,11 @@ static int inetd_dgram_peek(int sd, char *ifname)
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
static void inetd_dgram_drop(int sd, const char *ifname)
{
char pkt_interface[17];
char pkt_interface[IF_NAMESIZE + 1];
char buf[BUFSIZ];
while (1) {
inetd_dgram_peek(sd, pkt_interface);
inetd_dgram_peek(sd, pkt_interface, sizeof(pkt_interface));
if (string_compare(pkt_interface, ifname)) {
if (recv(sd, buf, sizeof(buf), 0) < 0)
break;
@@ -88,12 +91,13 @@ static void inetd_dgram_drop(int sd, const char *ifname)
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
static int inetd_stream_peek(int sd, char *ifname)
static int inetd_stream_peek(int sd, char *ifname, size_t ilen)
{
struct ifaddrs *ifaddr, *ifa;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
memset(ifname, 0, ilen);
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
return -1;
@@ -112,7 +116,7 @@ static int inetd_stream_peek(int sd, char *ifname)
iin = (struct sockaddr_in *)ifa->ifa_addr;
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE);
strlcpy(ifname, ifa->ifa_name, ilen);
break;
}
}
@@ -122,11 +126,12 @@ static int inetd_stream_peek(int sd, char *ifname)
return 0;
}
static int get_stdin(svc_t *svc)
static int get_stdin(svc_t *svc, char *iifname, size_t len)
{
int stdin = svc->inetd.watcher.fd;
char ifname[IF_NAMESIZE] = "UNKNOWN";
char ifname[IF_NAMESIZE + 1] = "UNKNOWN";
memset(iifname, 0, len);
if (svc->inetd.type == SOCK_STREAM) {
/* Open new client socket from server socket */
stdin = accept(stdin, NULL, NULL);
@@ -137,9 +142,9 @@ static int get_stdin(svc_t *svc)
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
inetd_stream_peek(stdin, ifname);
inetd_stream_peek(stdin, ifname, sizeof(ifname));
} else { /* SOCK_DGRAM */
inetd_dgram_peek(stdin, ifname);
inetd_dgram_peek(stdin, ifname, sizeof(ifname));
}
if (!inetd_is_allowed(&svc->inetd, ifname)) {
@@ -152,6 +157,9 @@ static int get_stdin(svc_t *svc)
return -1;
}
/* Return ingress interface */
strlcpy(iifname, ifname, len);
return stdin;
}
@@ -160,6 +168,7 @@ static void socket_cb(uev_t *w, void *arg, int events)
{
svc_t *svc = (svc_t *)arg, *task;
const char *conn = " connection";
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
int stdin;
_d("%s: Got socket event ...", svc->cmd);
@@ -168,7 +177,7 @@ static void socket_cb(uev_t *w, void *arg, int events)
return;
}
stdin = get_stdin(svc);
stdin = get_stdin(svc, iifname, sizeof(iifname));
if (stdin < 0) {
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
return;
@@ -216,6 +225,7 @@ static void socket_cb(uev_t *w, void *arg, int events)
memcpy(task->args, svc->args, sizeof(task->args));
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
strlcat(task->desc, conn, sizeof(task->desc));
strlcpy(task->iifname, iifname, sizeof(task->iifname));
task->stdin_fd = stdin;
service_step(task);
@@ -335,6 +345,22 @@ int inetd_start(inetd_t *inetd)
return 0;
}
void inetd_stop_children(inetd_t *inetd, int check_allowed)
{
svc_t *svc, *iter = NULL;
svc = svc_job_iterator(&iter, 1, inetd->svc->job);
while (svc) {
if (!svc_is_inetd(svc)) {
if (!check_allowed || !inetd_is_allowed(inetd, svc->iifname)) {
svc_stop(svc);
service_step(svc);
}
}
svc = svc_job_iterator(&iter, 0, inetd->svc->job);
}
}
void inetd_stop(inetd_t *inetd)
{
if (!inetd || !inetd->svc) {
@@ -354,6 +380,8 @@ void inetd_stop(inetd_t *inetd)
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
close(inetd->watcher.fd);
inetd->watcher.fd = -1;
inetd_stop_children(inetd, 0);
}
}
}
+1
View File
@@ -58,6 +58,7 @@ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name);
int inetd_start (inetd_t *inetd);
void inetd_stop (inetd_t *inetd);
void inetd_stop_children (inetd_t *inetd, int check_allowed);
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
int inetd_del (inetd_t *inetd);
+10 -3
View File
@@ -522,6 +522,7 @@ static int usage(int rc)
"Usage: %s [OPTIONS] [COMMAND]\n"
"\n"
"Options:\n"
" -b, --batch Batch mode, no screen size probing\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n"
"\n"
@@ -567,7 +568,7 @@ static int do_help(char *arg)
int main(int argc, char *argv[])
{
int c;
int interactive = 1, c;
char *cmd, arg[120];
struct command command[] = {
{ "debug", toggle_debug },
@@ -598,6 +599,7 @@ int main(int argc, char *argv[])
{ NULL, NULL }
};
struct option long_options[] = {
{"batch", 0, NULL, 'b'},
{"help", 0, NULL, 'h'},
{"debug", 0, NULL, 'd'},
{"verbose", 0, NULL, 'v'},
@@ -605,8 +607,12 @@ int main(int argc, char *argv[])
};
progname(argv[0]);
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "bh?v", long_options, NULL)) != EOF) {
switch(c) {
case 'b':
interactive = 0;
break;
case 'h':
case '?':
return usage(0);
@@ -617,7 +623,8 @@ int main(int argc, char *argv[])
}
}
screen_init();
if (interactive)
screen_init();
if (optind >= argc)
return show_status(NULL);
+11 -1
View File
@@ -34,7 +34,17 @@ char *pid_file (svc_t *svc);
int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg);
static inline char *pid_runpath(char *file, char *path, size_t len)
/**
* pid_runpath - Adjust /var/run --> /run path depending on system
* @file: Path to file in /run/path or /var/run/path
* @path: Pointer to buffer to write correct path
* @len: Length, in bytes, of @path buffer
*
* Returns:
* Always returns a valid pointer, which can be either @path with a /run
* or /var/run prefix to @file, or it may be @file if the prefix is OK.
*/
static inline char *pid_runpath(const char *file, char *path, size_t len)
{
static int unknown = 1;
static char *prefix = "/var/run";
+1 -16
View File
@@ -189,19 +189,11 @@ int plugin_exists(hook_point_t no)
return 0;
}
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
/* Some hooks are called with a fixed argument */
void plugin_run_hook(hook_point_t no, void *arg)
{
static int last = -1;
plugin_t *p, *tmp;
/*
* End recursion: any plugin hook => start service => SVC start
* hook => start service ... err ... wait a second
*/
if (HOOK_SVC_START == last)
return;
PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) {
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
@@ -209,15 +201,8 @@ void plugin_run_hook(hook_point_t no, void *arg)
}
}
/* Guard against infinite recursion */
if (HOOK_SVC_START == no)
last = no;
cond_set_oneshot(hook_cond[no]);
service_step_all(SVC_TYPE_RUNTASK);
if (HOOK_SVC_START == no)
last = -1;
}
/* Regular hooks are called with the registered plugin's argument */
-4
View File
@@ -56,8 +56,6 @@
* according to the following scheme:
*
* - HOOK_SVC_RECONF :: action/svc/reconf
* - HOOK_SVC_LOST :: action/svc/lost
* - HOOK_SVC_START :: action/svc/start
* - HOOK_RUNLEVEL_CHANGE :: action/sys/runlevel
*
* However, the implementation did not turn out to be stable enough for
@@ -76,8 +74,6 @@
\
/* Runtime hooks, runlevel [S1-9] */ \
CHOOSE(HOOK_SVC_RECONF, "nop"), \
CHOOSE(HOOK_SVC_LOST, "nop"), \
CHOOSE(HOOK_SVC_START, "nop"), \
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
\
/* Shutdown hooks, runlevel [06] */ \
-5
View File
@@ -28,11 +28,6 @@
#include "svc.h"
#include "plugin.h"
/*
* Main loop context
*/
uev_ctx_t *ctx;
int api_init (uev_ctx_t *ctx);
int api_exit (void);
+30 -21
View File
@@ -1,6 +1,6 @@
/* Example HOOK_SVC_LOST plugin, very noisy do not use as-is!
/* Work queue helper functions
*
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,35 +21,44 @@
* THE SOFTWARE.
*/
#include "config.h"
#include "finit.h"
#include "svc.h"
#include "plugin.h"
#include "schedule.h"
static void lost_pid(void *arg)
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
/*
* libuEv callback wrapper
*/
static void cb(uev_t *w, void *arg, int events)
{
pid_t pid = (uintptr_t)arg;
svc_t *svc;
struct wq *work = (struct wq *)arg;
svc = svc_find_by_pid(pid);
if (!svc)
if (UEV_ERROR == events) {
uev_timer_start(w);
return;
}
logit(LOG_INFO, "Lost PID %u, svc %s", pid, svc->cmd);
work->cb(work);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_SVC_LOST] = { .cb = lost_pid },
};
PLUGIN_INIT(plugin_init)
/*
* Place work on event queue
*/
int schedule_work(struct wq *work)
{
plugin_register(&plugin);
}
int msec;
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
if (!work)
return errno = EINVAL;
msec = work->delay;
if (work->init != SC_INIT) {
work->init = SC_INIT;
return uev_timer_init(ctx, &work->watcher, cb, work, msec, 0);
}
return uev_timer_set(&work->watcher, msec, 0);
}
/**
+37
View File
@@ -0,0 +1,37 @@
/* Work queue helper functions
*
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_SCHEDULE_H_
#define FINIT_SCHEDULE_H_
struct wq {
uev_t watcher;
int init;
int delay; /* msec delay before starting work */
void (*cb)(void *);
void *arg;
};
int schedule_work(struct wq *work);
#endif /* FINIT_SCHEDULE_H_ */
+9
View File
@@ -131,6 +131,9 @@ int serv_enable(char *arg)
char link[80];
char path[80];
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
@@ -154,6 +157,9 @@ int serv_disable(char *arg)
char link[80];
struct stat st;
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
@@ -173,6 +179,9 @@ int serv_touch(char *arg)
{
char corr[40];
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
+123 -79
View File
@@ -45,9 +45,14 @@
#include "tty.h"
#include "util.h"
#include "utmp-api.h"
#include "schedule.h"
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static struct wq work = {
.cb = service_worker,
};
static void svc_set_state(svc_t *svc, svc_state_t new);
/**
@@ -346,34 +351,33 @@ static int service_start(svc_t *svc)
svc->pid = pid;
svc->start_time = jiffies();
switch (svc->type) {
case SVC_TYPE_RUN:
result = WEXITSTATUS(complete(svc->cmd, pid));
svc->start_time = svc->pid = 0;
svc->once++;
svc_set_state(svc, SVC_STOPPING_STATE);
break;
case SVC_TYPE_SERVICE:
pid_file_create(svc);
break;
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc) && svc->inetd.type == SOCK_STREAM)
close(svc->stdin_fd);
case SVC_TYPE_INETD_CONN:
if (svc->inetd.type == SOCK_STREAM)
close(svc->stdin_fd);
break;
#endif
if (SVC_TYPE_RUN == svc->type) {
result = WEXITSTATUS(complete(svc->cmd, pid));
if (!svc_clean_bootstrap(svc)) {
svc->start_time = svc->pid = 0;
svc_set_state(svc, SVC_STOPPING_STATE);
}
default:
break;
}
if (svc_is_daemon(svc))
pid_file_create(svc);
sigprocmask(SIG_SETMASK, &omask, NULL);
if (do_progress)
print_result(result);
/*
* Only run hook on successful start, and *after* having printed
* the result, otherwise any hook tasks may overwrite it and the
* result would be like double "[ OK ]" but only one service.
*/
if (!result)
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
return result;
}
@@ -409,7 +413,7 @@ static void service_kill(svc_t *svc)
* @svc: Service to stop
*
* Returns:
* 0 if the service was successfully stopped. Non-zero otherwise.
* 0 if the service was successfully stopped. Non-zero otherwise.
*/
static int service_stop(svc_t *svc)
{
@@ -418,6 +422,9 @@ static int service_stop(svc_t *svc)
if (!svc)
return 1;
if (svc->state <= SVC_STOPPING_STATE)
return 0;
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
int do_progress = runlevel != 1 && !svc_is_busy(svc);
@@ -465,6 +472,7 @@ static int service_stop(svc_t *svc)
*/
static int service_restart(svc_t *svc)
{
int do_progress = 1;
int rc;
/* Ignore if finit is SIGSTOP'ed */
@@ -480,10 +488,14 @@ static int service_restart(svc_t *svc)
return 1;
}
_d("Sending SIGHUP to PID %d", svc->pid);
if (svc->desc[0])
/* Skip progress if desc disabled or bootstrap task */
if (!svc->desc[0] || svc_in_runlevel(svc, 0))
do_progress = 0;
if (do_progress)
print_desc("Restarting ", svc->desc);
_d("Sending SIGHUP to PID %d", svc->pid);
rc = kill(svc->pid, SIGHUP);
/* Declare we're waiting for svc to re-assert/touch its pidfile */
@@ -495,7 +507,7 @@ static int service_restart(svc_t *svc)
touch(pid_file(svc));
}
if (svc->desc[0])
if (do_progress)
print_result(rc);
return rc;
@@ -557,6 +569,33 @@ static void parse_log(svc_t *svc, char *arg)
}
}
/**
* parse_cmdline_args - Update the command line args in the svc struct
*
* strtok internal pointer must be positioned at first command line arg
* when this function is called.
*
* Side effect: strtok internal pointer will be modified.
*/
static void parse_cmdline_args(svc_t *svc, char *cmd)
{
int i;
char *arg;
strlcpy(svc->args[0], cmd, sizeof(svc->args[0]));
/* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
* array to be zero-terminated. */
for (i=1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS-1); i++)
strlcpy(svc->args[i], arg, sizeof(svc->args[0]));
/* clear remaining args in case they were set earlier.
* This also zero-terminates the args array.*/
for (; i < MAX_NUM_SVC_ARGS; i++)
svc->args[i][0] = 0;
}
/**
* service_register - Register service, task or run commands
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
@@ -611,7 +650,6 @@ static void parse_log(svc_t *svc, char *arg)
*/
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
{
int i = 0;
int id = -1;
#ifdef INETD_ENABLED
int forking = 0;
@@ -729,11 +767,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
}
/* Check if known inetd, then add ifnames for filtering only. */
/* Check if known inetd, update command line, then add ifnames for filtering only. */
svc = inetd_find_svc(cmd, service, proto);
if (svc)
if (svc) {
parse_cmdline_args(svc, cmd);
goto inetd_setup;
}
if (id <= 0)
id = svc_next_id(cmd);
}
@@ -784,12 +823,8 @@ recreate:
/* Internal plugin provides this service */
svc->inetd.cmd = plugin->inetd.cmd;
svc->inetd.builtin = 1;
} else {
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while ((cmd = strtok(NULL, " ")))
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
svc->args[i][0] = 0;
}
} else
parse_cmdline_args(svc, cmd);
svc->runlevels = levels;
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
@@ -867,16 +902,24 @@ void service_unregister(svc_t *svc)
* Only try stopping @svc if it's *not* an inetd connection.
* Prevents infinite recursion when called from service_step()
*/
if (!svc_is_inetd_conn(svc))
service_stop(svc);
switch (svc->type) {
#ifdef INETD_ENABLED
case SVC_TYPE_INETD:
inetd_del(&svc->inetd);
break;
if (svc_is_inetd(svc)) {
case SVC_TYPE_INETD_CONN:
/* inetd connection, if UDP unblock parent */
if (svc_is_busy(svc->inetd.svc)) {
svc_unblock(svc->inetd.svc);
service_step(svc->inetd.svc);
}
break;
#endif
inetd_del(&svc->inetd);
default:
service_stop(svc);
break;
}
svc_del(svc);
@@ -892,8 +935,6 @@ void service_monitor(pid_t lost)
if (tty_respawn(lost))
return;
plugin_run_hook(HOOK_SVC_LOST, (void *)(uintptr_t)lost);
svc = svc_find_by_pid(lost);
if (!svc) {
_d("collected unknown PID %d", lost);
@@ -962,10 +1003,10 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
*state = new;
/* if the PID isn't collected within 3s, kill it! */
if (*state == SVC_STOPPING_STATE) {
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if ((*state == SVC_STOPPING_STATE) && !svc_is_inetd(svc)) {
service_timeout_cancel(svc);
service_timeout_after(svc, 3000, service_kill);
service_timeout_after(svc, SVC_TERM_TIMEOUT, service_kill);
}
}
@@ -976,11 +1017,12 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
*/
int service_step(svc_t *svc)
{
int err;
char *restart_cnt = (char *)&svc->restart_cnt;
svc_cmd_t enabled;
svc_state_t old_state;
cond_state_t cond;
svc_state_t old_state;
svc_cmd_t enabled;
char *restart_cnt = (char *)&svc->restart_cnt;
int changed = 0;
int err;
restart:
old_state = svc->state;
@@ -1039,21 +1081,16 @@ restart:
if (sm_is_in_teardown(&sm))
break;
/*
* Make state transition *before* service_start(), because
* of HOOK_SVC_START, which may call service_step()
*/
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
err = service_start(svc);
if (err) {
(*restart_cnt)++;
svc_set_state(svc, SVC_READY_STATE);
if (!svc_is_inetd_conn(svc))
break;
}
/* Everything went fine, clean and set state */
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
}
break;
@@ -1084,6 +1121,7 @@ restart:
}
if (svc_is_runtask(svc)) {
svc_set_state(svc, SVC_STOPPING_STATE);
svc->once++;
break;
}
@@ -1108,7 +1146,12 @@ restart:
break;
service_restart(svc);
} else {
service_stop(svc);
#ifdef INETD_ENABLED
if (svc_is_inetd(svc))
inetd_stop_children(&svc->inetd, 1);
else
#endif
service_stop(svc);
}
svc_mark_clean(svc);
}
@@ -1134,6 +1177,13 @@ restart:
case COND_ON:
kill(svc->pid, SIGCONT);
svc_set_state(svc, SVC_RUNNING_STATE);
/* Reassert condition if we go from waiting and no change */
if (!svc_is_changed(svc)) {
char cond[MAX_COND_LEN];
mkcond(cond, sizeof(cond), svc->cmd);
cond_set_path(cond_path(cond), COND_ON);
}
break;
case COND_OFF:
@@ -1149,9 +1199,17 @@ restart:
if (svc->state != old_state) {
_d("%20s(%4d): -> %8s", svc->cmd, svc->pid, svc_status(svc));
changed++;
goto restart;
}
/*
* When a run/task/service changes state, e.g. transitioning from
* waiting to running, other services may need to change state too.
*/
if (changed)
schedule_work(&work);
return 0;
}
@@ -1160,6 +1218,11 @@ void service_step_all(int types)
svc_foreach_type(types, service_step);
}
void service_worker(void *unused)
{
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
}
/**
* svc_clean_runtask - Clear once flag of runtasks
*
@@ -1180,21 +1243,20 @@ void service_runtask_clean(void)
}
/**
* service_runtask_completed - Have run/task completed in current runlevel
* @skip: Hardcoded list of conditions to skip
* service_completed - Have run/task completed in current runlevel
*
* This function checks if all run/task have run once in the current
* runlevel. E.g., at bootstrap we must wait for these scripts or
* programs to complete their run before switching to the configured
* runlevel.
*
* Setting @skip means skipping tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP
* set in their condition mask. These tasks cannot run until finalize()
* All tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP set in their condition
* mask are skipped. These tasks cannot run until finalize()
*
* Returns:
* %TRUE(1) or %FALSE(0)
*/
int service_runtask_completed(int skip)
int service_completed(void)
{
svc_t *svc, *iter = NULL;
@@ -1221,24 +1283,6 @@ int service_runtask_completed(int skip)
return 1;
}
void service_bootstrap_cb(uev_t *w, void *arg, int events)
{
static int cnt = 10;
void (*finalize)(void) = arg;
_d("Checking completion of all run/task ... %d", cnt);
if (cnt-- > 0 && !service_runtask_completed(!runlevel))
return;
if (cnt > 0)
_d("All run/task have completed, resuming bootstrap.");
else
_d("Timeout, resuming bootstrap.");
cnt = 10;
uev_timer_stop(w);
finalize();
}
/**
* Local Variables:
* indent-tabs-mode: t
+2 -1
View File
@@ -36,8 +36,9 @@ void service_reload_dynamic (void);
int service_step (svc_t *svc);
void service_step_all (int types);
void service_worker (void *unused);
void service_bootstrap_cb (uev_t *w, void *arg, int events);
int service_completed (void);
#endif /* FINIT_SERVICE_H_ */
+6 -4
View File
@@ -83,6 +83,8 @@
#include "util.h"
#include "utmp-api.h"
extern svc_t *wdog;
/*
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
@@ -185,8 +187,8 @@ void do_shutdown(shutop_t op)
return;
}
if (wdogpid) {
print(kill(wdogpid, SIGPWR) == 1, "Advising watchdog, system going down");
if (wdog) {
print(kill(wdog->pid, SIGPWR) == 1, "Advising watchdog, system going down");
do_sleep(2);
}
@@ -208,11 +210,11 @@ void do_shutdown(shutop_t op)
/* Reboot via watchdog or kernel, or shutdown? */
if (op == SHUT_REBOOT) {
if (wdogpid) {
if (wdog) {
int timeout = 10;
/* Wait here until the WDT reboots, or timeout with fallback */
print(kill(wdogpid, SIGTERM) == 1, "Pending watchdog reboot");
print(kill(wdog->pid, SIGTERM) == 1, "Pending watchdog reboot");
while (timeout--)
do_sleep(1);
}
+20 -7
View File
@@ -72,6 +72,22 @@ static char *sm_status(sm_state_t state)
}
}
/*
* Disable login in single user mode and shutdown/reboot
*
* Re-enable only when going from these runlevels, this way a user can
* manage /etc/nologin manually within the other runlevels without us
* pulling the rug from under their feet.
*/
static void nologin(void)
{
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
touch("/etc/nologin");
if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6)
erase("/etc/nologin");
}
void sm_set_runlevel(sm_t *sm, int newlevel)
{
sm->newlevel = newlevel;
@@ -137,10 +153,7 @@ restart:
runlevel_set(prevlevel, runlevel);
/* Disable login in single-user mode as well as shutdown/reboot */
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
touch("/etc/nologin");
else
erase("/etc/nologin");
nologin();
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
@@ -149,7 +162,7 @@ restart:
/* Reset once flag of runtasks */
service_runtask_clean();
_d("Stopping services services not allowed in new runlevel ...");
_d("Stopping services not allowed in new runlevel ...");
sm->in_teardown = 1;
service_step_all(SVC_TYPE_ANY);
@@ -171,7 +184,7 @@ restart:
_d("All services have been stoppped, calling runlevel change hooks ...");
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
_d("Starting services services new to this runlevel ...");
_d("Starting services new to this runlevel ...");
sm->in_teardown = 0;
service_step_all(SVC_TYPE_ANY);
@@ -205,7 +218,7 @@ restart:
* Then, mark all affected service conditions as in-flux and
* let all affected services move to WAITING/HALTED
*/
_d("Stopping services services not allowed after reconf ...");
_d("Stopping services not allowed after reconf ...");
sm->in_teardown = 1;
cond_reload();
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
+42 -4
View File
@@ -24,6 +24,7 @@
#include <err.h>
#include <ctype.h> /* isdigit() */
#include <time.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/time.h>
@@ -34,10 +35,38 @@
#include "helpers.h"
#include "pid.h"
#include "util.h"
#include "cond.h"
#include "schedule.h"
/* Each svc_t needs a unique job# */
static int jobcounter = 1;
static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
static void svc_gc(void *arg)
{
struct timespec now;
struct wq *work = (struct wq *)arg;
svc_t *svc, *next;
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
char cond[MAX_COND_LEN];
int msec;
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
(now.tv_nsec - svc->gc.tv_nsec) / 1000000;
if (msec < SVC_TERM_TIMEOUT)
continue;
TAILQ_REMOVE(&gc_list, svc, link);
cond_clear(mkcond(cond, sizeof(cond), svc->cmd));
free(svc);
}
if (!TAILQ_EMPTY(&gc_list))
schedule_work(work);
}
/**
* svc_new - Create a new service
@@ -86,8 +115,13 @@ svc_t *svc_new(char *cmd, int id, int type)
return svc;
}
static struct wq work = {
.cb = svc_gc,
.delay = SVC_TERM_TIMEOUT
};
/**
* svc_del - Delete a service object
* svc_del - Mark a service object for deletion
* @svc: Pointer to an &svc_t object
*
* Returns:
@@ -96,8 +130,10 @@ svc_t *svc_new(char *cmd, int id, int type)
int svc_del(svc_t *svc)
{
TAILQ_REMOVE(&svc_list, svc, link);
memset(svc, 0, sizeof(*svc));
free(svc);
TAILQ_INSERT_TAIL(&gc_list, svc, link);
clock_gettime(CLOCK_MONOTONIC_COARSE, &svc->gc);
schedule_work(&work);
return 0;
}
@@ -381,6 +417,8 @@ void svc_mark_dynamic(void)
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->protected)
continue;
if (svc_is_inetd_conn(svc))
continue;
*((int *)&svc->dirty) = -1;
}
+25 -11
View File
@@ -73,6 +73,9 @@ typedef enum {
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
#define MAX_NUM_SVC_ARGS 32
/* Time after SIGTERM that we SIGKILL stopping processes */
#define SVC_TERM_TIMEOUT 3000
/*
* Default enable for all services, can be stopped by means
* of issuing an initctl call. E.g.
@@ -109,6 +112,7 @@ typedef struct svc {
/* For inetd services */
inetd_t inetd;
int stdin_fd;
char iifname[IF_NAMESIZE + 1]; /* Ingress interface for connection */
/* Set for services we need to redirect stdout/stderr to syslog */
struct {
@@ -134,6 +138,9 @@ typedef struct svc {
*/
uev_t timer;
void (*timer_cb)(struct svc *svc);
/* time at svc_del(), used by gc timer */
struct timespec gc;
} svc_t;
svc_t *svc_new (char *cmd, int id, int type);
@@ -177,26 +184,29 @@ static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && IS
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
#define svc_start(svc) svc_unblock(svc)
static inline void svc_stop (svc_t *svc) { svc->block = SVC_BLOCK_USER; }
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; }
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; }
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_BUSY; }
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
static inline char *svc_status(svc_t *svc)
{
if (!svc)
return "Unknown";
switch (svc->state) {
case SVC_HALTED_STATE:
switch (svc->block) {
@@ -218,6 +228,7 @@ static inline char *svc_status(svc_t *svc)
case SVC_BLOCK_RESTARTING:
return "restart";
}
return "unknown";
case SVC_DONE_STATE:
return "done";
@@ -249,6 +260,9 @@ static inline char *svc_status(svc_t *svc)
static inline const char *svc_dirtystr(svc_t *svc)
{
if (!svc)
return "Unknown";
if (svc_is_removed(svc))
return "removed";
if (svc_is_updated(svc))
+74 -68
View File
@@ -41,32 +41,16 @@
#ifdef FALLBACK_SHELL
static pid_t fallback = 0;
#endif
static LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER();
static char *canonicalize(char *tty)
{
char buf[42];
struct stat st;
static char path[80];
if (!tty)
return NULL;
/* Auto-detect serial console, for embedded devices mostly */
if (!strcmp(tty, "@console")) {
FILE *fp;
fp = fopen("/sys/class/tty/console/active", "r");
if (!fp) {
_e("Cannot find system console, is sysfs not mounted?");
return NULL;
}
if (fgets(buf, sizeof(buf), fp))
tty = chomp(buf);
fclose(fp);
}
strlcpy(path, tty, sizeof(path));
if (stat(path, &st)) {
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
@@ -88,7 +72,7 @@ static char *canonicalize(char *tty)
void tty_mark(void)
{
tty_node_t *tty;
struct tty *tty;
LIST_FOREACH(tty, &tty_list, link)
tty->dirty = -1;
@@ -96,17 +80,17 @@ void tty_mark(void)
void tty_sweep(void)
{
tty_node_t *tty, *tmp;
struct tty *tty, *tmp;
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
if (!tty->dirty)
continue;
_d("TTY %s dirty, stopping ...", tty->data.name);
tty_stop(&tty->data);
_d("TTY %s dirty, stopping ...", tty->name);
tty_stop(tty);
if (tty->dirty == -1) {
_d("TTY %s removed, cleaning up.", tty->data.name);
_d("TTY %s removed, cleaning up.", tty->name);
tty_unregister(tty);
}
}
@@ -136,12 +120,12 @@ void tty_sweep(void)
*/
int tty_register(char *line, struct rlimit rlimit[], char *file)
{
tty_node_t *entry;
int insert = 0, noclear = 0, nowait = 0, nologin = 0;
struct tty *entry;
size_t i, num = 0;
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS];
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS], buf[256];
char *dev = NULL, *baud = NULL;
char *runlevels = NULL, *term = NULL;
int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0;
if (!line) {
_e("Missing argument");
@@ -220,6 +204,24 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
return errno = EINVAL;
}
/* Auto-detect serial console, for embedded devices mostly */
if (!strcmp(dev, "@console")) {
FILE *fp;
fp = fopen("/sys/class/tty/console/active", "r");
if (!fp) {
_e("Cannot find system console, is sysfs not mounted?");
return errno = ENOENT;
}
atcon = 1;
if (fgets(buf, sizeof(buf), fp))
dev = strtok(chomp(buf), " \t");
fclose(fp);
}
again:
/* Ensure all getty (built-in + external) are registered with absolute path */
dev = canonicalize(dev);
if (!dev)
@@ -238,13 +240,13 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
}
}
entry->data.name = dev;
entry->data.baud = baud ? strdup(baud) : NULL;
entry->data.term = term ? strdup(term) : NULL;
entry->data.noclear = noclear;
entry->data.nowait = nowait;
entry->data.nologin = nologin;
entry->data.runlevels = conf_parse_runlevels(runlevels);
strlcpy(entry->name, dev, sizeof(entry->name));
strlcpy(entry->baud, baud ? baud : "", sizeof(entry->baud));
strlcpy(entry->term, term ? term : "", sizeof(entry->term));
entry->noclear = noclear;
entry->nowait = nowait;
entry->nologin = nologin;
entry->runlevels = conf_parse_runlevels(runlevels);
/* External getty */
if (cmd) {
@@ -255,12 +257,12 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
tok = cmd;
else
tok++;
entry->data.cmd = cmd;
entry->cmd = cmd;
args[1] = strdup(tok);
for (i = 1; i < num; i++)
entry->data.args[j++] = strdup(args[i]);
entry->data.args[++j] = NULL;
entry->args[j++] = strdup(args[i]);
entry->args[++j] = NULL;
}
_d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s",
@@ -270,7 +272,7 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
LIST_INSERT_HEAD(&tty_list, entry, link);
/* Register configured limits */
memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit));
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
if (file && conf_changed(file))
entry->dirty = 1; /* Modified, restart */
@@ -278,10 +280,18 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
entry->dirty = 0; /* Not modified */
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
if (atcon) {
dev = strtok(NULL, " \t");
if (dev) {
_d("Found another @console: %s", dev);
goto again;
}
}
return 0;
}
int tty_unregister(tty_node_t *tty)
int tty_unregister(struct tty *tty)
{
if (!tty) {
_e("Missing argument");
@@ -290,20 +300,14 @@ int tty_unregister(tty_node_t *tty)
LIST_REMOVE(tty, link);
if (tty->data.name)
free(tty->data.name);
if (tty->data.baud)
free(tty->data.baud);
if (tty->data.term)
free(tty->data.term);
if (tty->data.cmd) {
if (tty->cmd) {
int i;
free(tty->data.cmd);
free(tty->cmd);
for (i = 0; i < TTY_MAX_ARGS; i++) {
if (tty->data.args[i])
free(tty->data.args[i]);
tty->data.args[i] = NULL;
if (tty->args[i])
free(tty->args[i]);
tty->args[i] = NULL;
}
}
free(tty);
@@ -311,12 +315,12 @@ int tty_unregister(tty_node_t *tty)
return 0;
}
tty_node_t *tty_find(char *dev)
struct tty *tty_find(char *dev)
{
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) {
if (!strcmp(dev, entry->data.name))
if (!strcmp(dev, entry->name))
return entry;
}
@@ -326,7 +330,7 @@ tty_node_t *tty_find(char *dev)
size_t tty_num(void)
{
size_t num = 0;
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link)
num++;
@@ -337,22 +341,22 @@ size_t tty_num(void)
size_t tty_num_active(void)
{
size_t num = 0;
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) {
if (entry->data.pid)
if (entry->pid)
num++;
}
return num;
}
tty_node_t *tty_find_by_pid(pid_t pid)
struct tty *tty_find_by_pid(pid_t pid)
{
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) {
if (entry->data.pid == pid)
if (entry->pid == pid)
return entry;
}
@@ -375,7 +379,7 @@ static int tty_exist(char *dev)
return result;
}
void tty_start(finit_tty_t *tty)
void tty_start(struct tty *tty)
{
char *dev;
@@ -396,17 +400,19 @@ void tty_start(finit_tty_t *tty)
}
if (tty->nologin) {
_d("%s: Starting /bin/sh ...", dev);
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
return;
}
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : "");
if (!tty->cmd)
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
else
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
}
void tty_stop(finit_tty_t *tty)
void tty_stop(struct tty *tty)
{
if (!tty->pid)
return;
@@ -423,7 +429,7 @@ void tty_stop(finit_tty_t *tty)
tty->pid = 0;
}
int tty_enabled(finit_tty_t *tty)
int tty_enabled(struct tty *tty)
{
if (!tty)
return 0;
@@ -471,12 +477,12 @@ int tty_fallback(pid_t lost)
return 0;
}
static void tty_action(tty_node_t *tty)
static void tty_action(struct tty *tty)
{
if (!tty_enabled(&tty->data))
tty_stop(&tty->data);
if (!tty_enabled(tty))
tty_stop(tty);
else
tty_start(&tty->data);
tty_start(tty);
}
/*
@@ -484,7 +490,7 @@ static void tty_action(tty_node_t *tty)
*/
int tty_respawn(pid_t pid)
{
tty_node_t *tty = tty_find_by_pid(pid);
struct tty *tty = tty_find_by_pid(pid);
if (!tty)
return tty_fallback(pid);
@@ -493,7 +499,7 @@ int tty_respawn(pid_t pid)
utmp_set_dead(pid);
/* Clear PID to be able to respawn it. */
tty->data.pid = 0;
tty->pid = 0;
tty_action(tty);
return 1;
@@ -504,7 +510,7 @@ int tty_respawn(pid_t pid)
*/
void tty_reload(char *dev)
{
tty_node_t *tty;
struct tty *tty;
if (dev) {
tty = tty_find(dev);
@@ -529,7 +535,7 @@ void tty_reload(char *dev)
/* Start all TTYs that exist in the system and are allowed at this runlevel */
void tty_runlevel(void)
{
tty_node_t *tty;
struct tty *tty;
LIST_FOREACH(tty, &tty_list, link)
tty_action(tty);
+15 -21
View File
@@ -32,10 +32,12 @@
#define TTY_MAX_ARGS 16
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
typedef struct {
char *name;
char *baud;
char *term;
struct tty {
LIST_ENTRY(tty) link;
char name[42];
char baud[10];
char term[10];
int noclear;
int nowait;
int nologin;
@@ -48,32 +50,24 @@ typedef struct {
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
} finit_tty_t;
typedef struct tty_node {
LIST_ENTRY(tty_node) link;
finit_tty_t data;
/* XXX: Yes, TTYs should be refactored into a separate SVC type. */
int dirty; /* Set if old mtime != new mtime => reloaded,
* or -1 when marked for removal */
} tty_node_t;
//extern LIST_HEAD(, tty_node) tty_list;
/* Set if modified => reloaded, or -1 when marked for removal */
int dirty;
};
void tty_mark (void);
void tty_sweep (void);
int tty_register (char *line, struct rlimit rlimit[], char *file);
int tty_unregister (tty_node_t *tty);
int tty_unregister (struct tty *tty);
tty_node_t *tty_find (char *dev);
struct tty *tty_find (char *dev);
size_t tty_num (void);
size_t tty_num_active (void);
tty_node_t *tty_find_by_pid (pid_t pid);
void tty_start (finit_tty_t *tty);
void tty_stop (finit_tty_t *tty);
int tty_enabled (finit_tty_t *tty);
struct tty *tty_find_by_pid (pid_t pid);
void tty_start (struct tty *tty);
void tty_stop (struct tty *tty);
int tty_enabled (struct tty *tty);
int tty_respawn (pid_t pid);
void tty_reload (char *dev);
void tty_runlevel (void);
+37 -31
View File
@@ -1,6 +1,6 @@
/* Built-in watchdog daemon
*
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2016-2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -9,24 +9,30 @@
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
#include "finit.h"
#include "log.h"
#include "watchdog.h"
int running = 1;
@@ -51,6 +57,9 @@ static int init(char *progname, char *devnode)
signal(SIGTERM, sighandler);
signal(SIGPWR, sighandler);
openlog(&progname[1], LOG_CONS | LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "Finit v%s basic watchdogd starting ...", VERSION);
fd = open(devnode, O_WRONLY);
if (fd == -1)
return -1;
@@ -78,6 +87,7 @@ static int loop(int fd, int timeout)
/* External watchdogd wants to take over ... */
if (handover) {
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
return !write(fd, "V", 1);
}
@@ -85,34 +95,30 @@ static int loop(int fd, int timeout)
return 0;
}
int watchdog(char *progname)
int main(int argc, char *argv[])
{
int pid;
int fd, ret;
pid = fork();
if (pid == 0) {
int fd, ret;
fd = init(progname, WDT_DEVNODE);
if (fd == -1) {
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
_exit(1);
}
ret = loop(fd, WDT_TIMEOUT);
while (!handover) {
/* Waiting for SIGTERM ... */
sleep(1);
/* Set lowest possible timeout on SIGTERM */
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
}
close(fd);
_exit(ret);
fd = init(argv[0], WDT_DEVNODE);
if (fd == -1) {
if (ENOENT != errno)
syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
return 1;
}
return pid;
ret = loop(fd, WDT_TIMEOUT);
while (!handover) {
syslog(LOG_ALERT, "System going down ...");
/* Waiting for SIGTERM ... */
sleep(1);
/* Set lowest possible timeout on SIGTERM */
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
}
close(fd);
return ret;
}
/**