Compare commits

...
261 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
Joachim Nilsson 6d41725387 Bump version for v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:51:55 +01:00
Joachim Nilsson ceb91f746c Revert "Disable clang build during Coverity Scan run"
This reverts commit 22a69d8cca.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:45:24 +01:00
Joachim Nilsson a8802ab0d2 Mention Void Linux tutorial in README, change to bullet list
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:31:03 +01:00
Joachim Nilsson ad98f69560 Ignore return value from rename(), ofile may not exist yet.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:03:07 +01:00
Joachim Nilsson 5953090688 Document some relevant cmdline options
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 13:45:22 +01:00
Joachim Nilsson 22a69d8cca Disable clang build during Coverity Scan run
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 11:45:55 +01:00
Joachim Nilsson 710a3d2c8f Fix Coverity warning, array cannot be NULL
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 11:45:15 +01:00
Joachim Nilsson 658fb9b037 Check return value from rename(), fall back to truncate() on error
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 11:39:50 +01:00
Joachim Nilsson a84bb9042f Update and add ignore for WeOS build system files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:51:39 +01:00
Joachim Nilsson eeb13dba74 Toggle silent mode (progress) when toggling debug mode
Starting in debug mode and disabling debug at runtime did not exhibit
the same behavior as if starting in normal mode and toggling debug mode
at runtime.  This patch adds toggling of silent mode (progress output)
to runtime debug toggling.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:44:44 +01:00
Joachim Nilsson 3d20237959 Follow-up to f858d94, only reinitialize screen when toggling debug mode
The screen_init() call for each print() & C:o caused serious screen
artefacts in debug mode.  This patch reworks f858d94 to only re-init
screen when toggling debug.

Also, avoid screen_init() completely if started in debug mode.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:33:24 +01:00
Joachim Nilsson ddf1bce642 Add screen_exit() fn to restore screen defaults in debug mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:31:44 +01:00
Joachim Nilsson 93cd4116db Update ChangeLog and prepare for v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 01:21:37 +01:00
Joachim Nilsson 1df6a91a94 Document sufficiently complex, yet deceptively simple, function
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 00:44:40 +01:00
Joachim Nilsson c64a71ff30 Follow-up to ba2ab4b, crashing services may not have a PID rn
- Don't warn of missing PID, even if only _d(), perfectly normal
- Allow stopping restart timer of non-services (e.g. task/run)
- Allow stopping services with no PID rn, i.e. stop libuEv timer

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 00:17:02 +01:00
Joachim Nilsson b2b6fd25d8 Relocate service helper script to contrib/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 22:28:18 +01:00
Joachim Nilsson 3ca548b681 Add list of major contributors, from Westermo
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 21:31:05 +01:00
Joachim Nilsson ba2ab4b446 Follow-up to 2683049, only stop running, non-inet, services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 20:52:37 +01:00
Joachim Nilsson a8183b0559 Check if symlink exists before trying to create symlink
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 20:45:05 +01:00
Joachim Nilsson 227e4fa097 Create destination contrib/*/finit.d dir if missing
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 19:03:06 +01:00
Joachim Nilsson 3d68ebbf54 Fix off-by-one in condition reassert()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 15:24:11 +01:00
Joachim Nilsson 6e017025aa Fix bad string pointer arithmetic in condition reassert
On `initctl reload` we reassert conditions, i.e. bring each still viable
condition back in sync with the new reconf generation.  This patch fixes
an assumption in the reassert() callback that caused the following nasty
transformation:

	/run/finit/cond/net/lo/up --> /run/finit/cond/lo/up

The transformation was caused by the reassert() code assuming a /var/run
prefix rather than /run.  We must check the actual runpath, or like this
patch does, use a path neutral way to find the base condition string.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 15:17:07 +01:00
Joachim Nilsson 5f818f917e cond_init(): Create correct condition path, /var/run might not exist
The cond_init() function is called before the bootmisc.so plugin has
run.  It is responsble for setting up the /var/run symlink to /run on
systems with the new layout.  Therefore we must wrap COND_PATH with
pid_runpath() to create the correct runtime path.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 15:17:07 +01:00
Joachim Nilsson 0da1deb487 Add docs/*.md to distribution
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:02:19 +01:00
Joachim Nilsson 0337985422 Ignore generated files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:02:19 +01:00
Joachim Nilsson f59d41cdec Add silly process monitor script to distribution.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:02:18 +01:00
Joachim Nilsson 1cc998eb60 Distribute and install contrib/ directory with all examples
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:01:58 +01:00
Joachim Nilsson 8c1d84305d Minor, refactor
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 08:38:52 +01:00
Joachim Nilsson 7e4e5127c7 Preserve Markdown links on installed .md files in docs/ sub-directory
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-21 10:22:25 +01:00
Joachim Nilsson 3b813bca68 Ignore generated files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-20 20:48:26 +01:00
Joachim Nilsson a1f210bc70 Distribute and install relevant docs/*.md
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-20 20:47:33 +01:00
Joachim Nilsson 0ecf46d7a9 Prefer _exit() over exit() in forked service handlers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:35:15 +01:00
Joachim Nilsson 268304927d Fix segfault, stop a crashing service before deleting it
Finit tries to restart crashing services.  If a service is removed
before Finit has given up restarting it, then the background timer
responsible the restart may dereference a free():d svc_t.

Hence, make sure to properly stop (and step) a service and its timer
callbacks in service_unregister() before calling svc_del().

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:27:25 +01:00
Joachim Nilsson fa02b59db5 Refactor, set STOPPING state in service_stop()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:24:39 +01:00
Joachim Nilsson 2c904b5b44 service_unregister(): Also call inetd_del() for inetd services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:07:22 +01:00
Joachim Nilsson 4191abc79f service_timeout_cb(): Check callback pointer before calling it
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:07:22 +01:00
Joachim Nilsson abb928f60b service_stop(): Also stop any pending restart timers
In service_stop() we call svc_del() which, as of Finit v3.1, free():s
the svc_t, so we don't want any lingering restart timers to wake up
after free() and start dereferencing the svc_t.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:07:22 +01:00
Joachim Nilsson 3c52118a8e svc_clean_dynamic(): Fix access after free()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 13:20:25 +01:00
Joachim Nilsson 5354a57313 plugin_unregister(): Stop I/O watcher before free():ing watcher
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 18:35:18 +01:00
Joachim Nilsson 92b1d882e5 do_kill(): Also skip gdbserver from being killed
To improve debug possibilities at shutdown, keep gdbserver running even
during shutdown.

Note: The gdbserver service runlevels must be sth like [123456789] for
      this to work in the first place.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 18:21:37 +01:00
Joachim Nilsson fd7a6c5020 cond.c: Normalize COND_PATH, some systems have /run not /var/run
On most modern Linux systems /run is a tmpfs that replaces /var/run.
The latter is set up as a symlink to /run by the bootmisc.so plugin.

However, Finit conditions rely on the /var/run/finit/cond prefix, which
does not exist until bootmisc.so has run, which is *after* `mount -a`
has run.  Therefore, to have working conditions before we run `mount -a`
we must normalize the path constructed by cond_path() to use either the
/run or /var/run (default) prefix.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 14:44:25 +01:00
Joachim Nilsson a61551d15f pid.c: Relocate pid_runpath() to a static inline fn
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 14:44:00 +01:00
Joachim Nilsson 7f62a69f74 New hook/mount/post, runs after mount -a but before hook/mount/all
This gives the operator a way to add a post-mount script, e.g. to peform
a 2nd stage mount or mount preparations, before the bootmisc.so plugin
and others run in hook/mount/all

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 13:34:28 +01:00
Joachim Nilsson c969bda62d bootmisc.so: Create /etc/mtab symlink if missing
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 13:34:28 +01:00
Joachim Nilsson af41fcd35e api_exit(): Must stop libuEv watcher before closing descriptor
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 13:34:28 +01:00
Joachim Nilsson 84e9569be6 initctl: Adjust field widths in show/status
The job:id field can now be used to display job:INET_PORT_NUMBER, e.g.
100:16161 for job 100 localhost SNMP port 16161.

The service name field has been reduced from 20 --> 16 bytes since the
full path has been reduced to display the basename.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-17 18:42:24 +01:00
Joachim Nilsson a4ece0fe3f Add log rotate, log, condition and non-SIGHUP example .conf sample
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 17:41:03 +01:00
Joachim Nilsson b2f66da1f9 Cleanup TODO, last item turned out to be bogus
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 17:06:55 +01:00
Joachim Nilsson abbc0a1d5c Fix Ctrl-C, and signals in general, on "logins" in nologin mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 16:25:11 +01:00
Joachim Nilsson 37e420bd35 Minor, itemize example files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 16:03:36 +01:00
Joachim Nilsson d72eb45d6e Refactor, simplify text and rename section
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 16:02:06 +01:00
Joachim Nilsson 30c593f64e Minor, reorder debug messages to not mess up progress text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:55:57 +01:00
Joachim Nilsson f858d94d2a Reinitialize screen at shutdown and when in debug mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:47:14 +01:00
Joachim Nilsson 992bab2f8f Allow empty -- run/task/service descriptions => no progress
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:38:55 +01:00
Joachim Nilsson 72c9681ffb Change debug messages to use svc->cmd rather than empty svc->desc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:15:49 +01:00
Joachim Nilsson e439b79e82 Refactor, only show process progress if -- Description is set
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:15:10 +01:00
Joachim Nilsson 36e001d636 Refactor, rindex() --> strrchr(), LEGACY as of POSIX.1-2001
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:13:48 +01:00
Joachim Nilsson 75aa14466f Allow disabling log rotation, log size:0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 07:50:52 +01:00
Joachim Nilsson 7f899b7a91 Clarify size:200k and count:5 global log rotation options
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 07:46:27 +01:00
Joachim Nilsson 1c177fd953 TODO before v3.1 GA release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 07:41:20 +01:00
Joachim Nilsson 617ec7c147 Document global log rotation setting log size:200k count:5
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 19:42:33 +01:00
Joachim Nilsson 192d3aafe7 Add support for global logfile max size and max num. rotated
# Max file size for each log file: 100 kiB, rotate max 4 copies:
    # log => log.1 => log.2.gz => log.3.gz => log.4.gz
    log size=100k count=4

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 19:42:33 +01:00
Joachim Nilsson f11ebb0046 New function, strtobytes(), allows k/M/G ISO size modifiers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 19:42:33 +01:00
Joachim Nilsson b5e260b9e0 Minor refactor
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 18:30:41 +01:00
Joachim Nilsson 45c10edad6 Fix static build, libplug.la is now in ../plugins/, not plugins/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 16:05:39 +01:00
Joachim Nilsson b60d8ce4b6 Fix bootstrap ordering problem, inotify watchers *after* HOOK_BASEFS_UP
An external plugin to set up /etc can use HOOK_BASEFS_UP, but the Finit
conf_monitor() must then be called *after* not before that hook, or the
inotify watchers will listen on the wrong inode.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 15:23:25 +01:00
Joachim Nilsson d35a8bbda9 Fix problem building external plugins
The file plugin.h is installed to $prefix/include and used by external
plugins.  Therefore it must not include, or depend on, features set by
the configure script in config.h

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 15:07:24 +01:00
Joachim Nilsson c616270ccb Bump version for v3.1-rc2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 890ac2f4e1 Update ChangeLog for upcoming v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 8063b1bfe1 Include rescue.conf in distribution
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 0bcfb464ce make release: Call distcheck target verify build from tarball
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson dc5b1a5980 Increase size of intermediate buffers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 14555d7a7b configure: Fix misspelled --enable-redirect option
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:17:50 +01:00
Joachim Nilsson a06127b990 Document new log:... option to task/run/services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:10:20 +01:00
Joachim Nilsson 3b8e7d677f rlimit can be set globally or locally depending on .conf file
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:06:19 +01:00
Joachim Nilsson 097fce4170 Ignore logit binary
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:03:12 +01:00
Joachim Nilsson 306bf0f191 Add support for configurable redirect to /dev/null, issue #21
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson f25390004b Add support for redirecting output to a file using logit, issue #21
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson cbd4cc8d34 Extend .conf syntax for 'log' option, issue #21
This is the first patch in a series to add support for

    log:/path/to/logfile,priority:facility.level,tag:ident

In this patch support for 'priority:' and 'tag:', log file support
coming later.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson 44591c6e51 logit: Sync each log entry to file
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson 0e51a1eed8 logit: Hard code program name and clarify distribution scope
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 06:58:44 +01:00
Joachim Nilsson c315fb5637 Add logit v1.0 to Finit distribution
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 06:58:44 +01:00
Joachim Nilsson 1017e7b8e5 Disable conditions for some non-oneshot (runtime) hooks
The SVC_RECONF, SVC_LOST, SVC_START, and RUNLEVEL_CHANGE hooks are not
one-shot, they are also not regular conditions since there exist no
mechanism to reset them from flux.

One idea was to turn them into actions, but the lost + start hooks need
to be called multiple times per trigger, e.g. `initctl reload`, which
turned out to be non-trivial to implement right now.

Therefore, for (at least) the Finit v3.1 release these conditions have
been disabled ("nop") and ignored by Finit.  Only actual C-style plugins
will be called.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson 8ef216af62 Let hook conditions be oneshot, prevents flux on reload
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson a9b9631098 Minor, reduce debug noise from plugin recursion
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson cc186334e0 Add support for one-shot conditions, like most hooks
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson e1b87d707f Support touching custom PID files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 22:03:17 +01:00
Joachim Nilsson 68e7bcba4c Refactor and document pid_file_parse()
This patch cleans up the implmentation, adds documentation and also
makes sure to adjust the prefix path of a pid:/prefix/path argument.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:52:06 +01:00
Joachim Nilsson f9783b4b41 pidfile: Use realpath() to figure out of _PATH_VARRUN is a symlink
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:49:18 +01:00
Joachim Nilsson 32d9afc7bb Use pid_runpath() in svc_find_by_pidfile(), used by pidfile plugin
The pidfile plugin just listens to changes in files in /run or /var/run,
so when trying to locate an svc_t from a file change we need to compare
using the proper prefix path.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:47:11 +01:00
Joachim Nilsson 91b550662e New function, pid_runpath(), figures out /var/run or /run prefix
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:42:13 +01:00
Joachim Nilsson d87af8270e docs/config.md: Document new pid:/optional/file.pid argument
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-12 21:09:55 +01:00
Joachim Nilsson 366c23f5d1 TODO: Remember to handle PID files in subdirectories to /run
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-12 21:07:15 +01:00
Joachim Nilsson 0d4bc69584 pidfile: Use new svc_find_by_pidfile() in plugin
A service may now have a custom PID file, possibly because it doesn't
really create one itself.  This needs to be taken into account also in
the pidfile plugin.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 10:50:19 +01:00
Joachim Nilsson cccf199280 New function, svc_find_by_pidfile()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 10:49:58 +01:00
Joachim Nilsson a1c33dec21 Refactor, move all PID file related functions to the same module
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 10:48:17 +01:00
Joachim Nilsson df303c6243 Nota bene, changing name of PID file of running process
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 09:05:24 +01:00
Joachim Nilsson 6a6ae6fa3b Fix #95: Create PID file if program does not
This patch adds support for creating, and removing, a PID file for
programs (services) that for some reason does not create one.  With
Finit this is very useful since it allows us to use our condition
support even for "broken" daemons.

To enable this for a service, use the following syntax:

    service pid[:[/path/to/]file[.pid]] /path/to/daemon

I.e., `pid` is a required argument if you want to enable the feature,
but the rest is optional:

    pid             --> /run/`basename $CMD`.pid
    pid:file        --> /run/file.pid
    pid:file.dip    --> /run/file.dip
    pid:/mnt/k      --> /mnt/k.pid
    pid:/mnt/k.foo  --> /mnt/k.foo

There is an additional feature with this patch.  For daemons that run
with a different identiy one usually wants to create the PID file with
that identity, e.g.:

    service inadyn -I dyn     -- Dyn.com DDNS client
    service inadyn -I freedns -- FreeDNS DDNS client

Here inadyn will look for /etc/dyn.conf and create /run/dyn.pid, and
/etc/freedns.conf and create /run/freedns.pid, respetively.  While Finit
will in both cases assume the PID file is /run/inadyn.pid ... err fail!

To get out of this mess the `pid` argument can be used to tell Finit
what the real PID file the daemon creates is called:

    service pid:!dyn     inadyn -I dyn     -- Dyn.com DDNS client
    service pid:!freedns inadyn -I freedns -- FreeDNS DDNS client

The same composition rules listed above applies also to the `!` variant.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 23:27:05 +01:00
Joachim Nilsson 4011b5a3b8 Relocate all docs from doc/* to docs/*, integrates better w/ GitHub
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 02:28:33 +01:00
Joachim Nilsson 68ef6ec108 Fix v3.1 regression, segfault when inetd service connection exits
The service_step() function may call svc_del() on an svc from the
service_monitor(), so we need a way to tell the latter that it no
longer can dereference the pointer.

This regression is limited to v3.1, due to the introduction of the
new non-shared-memory store of svc_t.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 02:10:14 +01:00
Joachim Nilsson 89a9d92d6c Refactor construction of inetd service connection description
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 01:41:14 +01:00
Joachim Nilsson 15cb576293 Fix v3.1 regression in start of inetd services, missing rlimit
Per service rlimit is new in v3.1.  When an inetd service connection is
created we do a selective copy from the parent inetd.  We also need to
copy the parent's rlimits, otherwise the child will be killed as soon
as the kernel starts it.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 01:36:24 +01:00
Joachim Nilsson 90734eee16 reboot: Reboot via Finit + watchdogd may take a while
This patch increases the default wait, 2 --> 5 sec, while pending for
Finit + watchdogd controlled reboot/shutdown.  We don't want to hand
back the prompt when we're not really ready yet.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:20:08 +01:00
Joachim Nilsson 71915e0e14 Make sure log_exit() restores progress output on shutdown/reboot
This patch builds on top of the recently introduced "silent by default"
and support for kernel cmdline `splash` to disable silent operation.

At shutdown/reboot `log_exit()` is called, the intention is to re-enable
progress output so the user can see what's going on.  So, we need to re-
set `silent` to the same setting as log_init() started with, hence the
new `muffler()`.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:16:58 +01:00
Joachim Nilsson 112ab2c22f initctl: Refactor, use struct command rather than command_t
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:15:55 +01:00
Joachim Nilsson 8f00ac2e90 Do *not* redirect stdout/stderr of services by default
Automatic redirection of output was introduced in v3.0 and for the
upcoming v3.1 a configure option was added to disable it.

By popular demand this patch flips the default.  One now has to
explicitly enable this feature to get redirection.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:12:59 +01:00
Joachim Nilsson 6fb070c75c TODO: Update and remove recently fixed items \o/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:31:02 +01:00
Joachim Nilsson 8fd431f3e9 initctl: Show uptime of running services in show <NAME> output
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:30:08 +01:00
Joachim Nilsson 50f128cd1d Track service start time using new jiffies() function
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:29:43 +01:00
Joachim Nilsson f6fa576270 New functions: jiffies() and uptime(), implemented with sysinfo()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:28:29 +01:00
Joachim Nilsson b26d30757d Document new initctl status <JOB|NAME> command
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 17:11:15 +01:00
Joachim Nilsson dc9d0b6fca Add service wrapper script, sometimes more convenient to use
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 17:08:35 +01:00
Joachim Nilsson ff95ca92b1 Update cron syntax proposal: @hourly, @daily, @weekly, @reboot, etc.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 16:52:09 +01:00
Joachim Nilsson c939aed87f Add net/IFNAME/running to list of supported conditions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 15:01:11 +01:00
Joachim Nilsson 71ea9e2ea3 Support for restarting initctl API socket on SIGHUP
This patch adds support for restarting the API socket on SIGHUP, similar
to how SysV init and systemd use SIGUSR1 to restart their FIFO or D-Bus.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 13:28:27 +01:00
Joachim Nilsson b6bd10d960 Follow-up to c1344e8, never shutdown() API socket, or segfault
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 13:27:25 +01:00
Joachim Nilsson 87d8157175 fixup! netlink.so: Add support for IFF_RUNNING => net/IFNAME/running
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 13:26:58 +01:00
Joachim Nilsson cf6faa9eeb netlink.so: Refactor, break out condition set/clear func.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 12:41:27 +01:00
Joachim Nilsson 80a71aff07 netlink.so. Refactor (simplify) and fix problem with loss of events
An interface may come up with IFF_RUNNING already set, but we would
only detect IFF_UP.  This patch simplfies the code a lot by pivoting
the qualifier from using ifi_change to ifi_flags, which is the actual
current status of an interface.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 12:25:50 +01:00
Joachim Nilsson 04a4653e9b netlink.so: Add support for IFF_RUNNING => net/IFNAME/running
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 12:07:47 +01:00
Joachim Nilsson 3aa9d9f451 README: Document how to re-enable progress with configure or splash
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 22:10:10 +01:00
Joachim Nilsson c1afca9c33 README: Clarify that runparts runs *before* networking is up
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 22:01:56 +01:00
Joachim Nilsson 205915d42f Document new rescue (recovery) mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:58:26 +01:00
Joachim Nilsson 631f89b58c conf_parse_cmdline(): Relax check for 'debug' flag
Relax check for 'debug' flag, we want to catch '--debug' and 'finit_debug'

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:57:36 +01:00
Joachim Nilsson 6d1c245985 Update documentation, debug replaces --debug and finit_debug
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:56:24 +01:00
Joachim Nilsson fc08b0c65b README: Clarify runlevel section a bit, new 'single' user mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:46:03 +01:00
Joachim Nilsson 83021b11d8 No need to take down network if it's already down (at boot)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:33:45 +01:00
Joachim Nilsson 5309ece2f9 Add support for splash progress mode from kernel cmdline
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:28:30 +01:00
Joachim Nilsson 6480850ee2 Enforce *no networking* in single-user mode, runlevel 1
This is a quite intrusive change.  In addition to the already reserved
runlevels 0 and 6, halt and reboot, respectively, this patch reserves
runlevel 1 as *no networking*, like the traditional SysV init did.  One
of the reasons for this change is the new 'single' user mode, introduced
earlier.  Most users when entering 'S' or 'single' on the kernel cmdline
expect the single user mode (runlevel 1) to be without networking.

This leaves 2, 3, 4, 5, and 7, 8, 9 as fully user-configurable runlevels
with networking.

Of course, networking can still be enabled in runlevel 1 using ifconfig
or ifupdown, or change runlevel.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson eaa82377c4 Add support for single (S) user mode from kernel cmdline
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 5b3df3654c conf_parse_cmdline(): Use strict string comparison
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 55cb50ef90 In rescue mode, always set hostname from /lib/finit/rescue.conf
If hostname is missing from /lib/finit/rescue.conf the system will
default to the configured default hostname (noname).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 3f7e03e813 Refactor, re-order a bit so screen_init() doesn't run in rescue mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson ab723d2f31 Add support for a rescue (recovery) mode from kernel cmdline
This patch adds a rescue (revovery) mode to Finit.  Simply add `rescue`
to the kernel cmdline at boot and the following steps at bootstrap will
be skipped:

- Load of services/run/task/etc in /etc/finit.conf + /etc/finit.d/*.conf
- fsck of filesystems in /etc/fstab
- Remount of / to read-write
- Mount of all filesystems in /etc/fstab
- swapon
- A few plugin hooks:
  - HOOK_ROOTFS_UP
  - HOOK_MOUNT_ERROR
  - HOOK_BASEFS_UP
- `runparts DIR`
- /etc/rc.local

Instead of loading /etc/finit.conf and /etc/finit.d/*.conf Finit loads
the file /lib/finit/rescue.conf, which can be overridden by the operator
if needed.  If this file is removed (or lost), Finit falls back to start
a simple root shell, without any login.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson bc149d95fc conf: Add hostname alias to host
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson c605874b26 Remove --with-plugindir=DIR, use --prefix or --libdir instead
The /lib/finit/plugins path is as of this patch not longer possible to
change.  Use --prefix or --libdir instead to change the leading path.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson c33ba6a477 Remove reboot symlinks properly on uninstall
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 63199d368f conf: lim2str(): Fix buffer overflow in local debug function
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson db3263c441 Refactor conf_parse_cmdline(), support 'debug' as well as '--debug'
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:19:55 +01:00
Joachim Nilsson 925212f7cf Merge pull request #94 from joerg-krause/patch-1
doc/config.md: Add entry about using wrapper scripts to execute pre commands
2017-12-30 21:03:03 +01:00
Jörg Krause ad401280c2 doc/config.md: Add entry about using wrapper scripts to execute pre commands
See: https://github.com/troglobit/finit/issues/93
2017-12-30 17:55:12 +01:00
Joachim Nilsson f3d2586417 Fix build warning on musl libc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 13:12:05 +01:00
Joachim Nilsson 6fc1d27bcf conf: Check if a kernel module has been loaded first
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 13:09:34 +01:00
Joachim Nilsson f3305e75b7 conf_reload(): Exit properly, even if no /etc/finit.d/*.conf exitsts
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 12:47:27 +01:00
Joachim Nilsson e4c12314b1 doc/config.md: Mention /etc/finit.d/available/ and initctl enable
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 21:00:46 +01:00
Joachim Nilsson c9a1a64ce5 doc/config.md: Update with changes to finit.conf and stanza limits
Finit v3.1 adds support for reloading /etc/finit.conf, document what
actually is read at runtime, compared to at bootstrap, and clarify
which stanzas are for what part of the boot process.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:52:43 +01:00
Joachim Nilsson 3843f62aa8 README.md: Update with difference between runparts & /etc/rc.local
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:51:42 +01:00
Joachim Nilsson ef54c4826b bootstrap.md: runparts goes before the runlevel change
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:51:14 +01:00
Joachim Nilsson 2fac65becb Fix long-standing bug, runparts() should run before runlevel change
The difference between `runparts DIR` and `/etc/rc.local` is that one
should run just before the first runlevel change (to the configured
runlevel) at the end of bootstrap, whereas the other should run just
before launcing the TTYs, i.e. after all tasks in the configured
runlevel have been started.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:48:47 +01:00
Joachim Nilsson 46b253dea6 doc: The module configuration stanza supports optional args
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:15:29 +01:00
Joachim Nilsson b04983c3f3 conf: Make rlimit [hard|soft] optional => possible to set both
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:15:12 +01:00
Joachim Nilsson f2d142ef57 Only reload .conf and services if any .conf file has changed
This patch is an optimization.  Now that we monitor for any .conf file
changes (as soon as the event loop starts) we can skip automatic reload
of all .conf files and services if no .conf file has changed.  Unless
the user issues an `initctl reload`, `init q`, or sends us SIGHUP.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 19:22:01 +01:00
Joachim Nilsson 33ce746e58 parse_conf(): Increase log with actual limits in case of problems.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 16:57:36 +01:00
Joachim Nilsson 6ecb86b4a7 conf_reload(): No need to reload global rlimits again
This patch removes duplicate reload of global_rlimit[], which
parse_conf() already has done.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 16:56:30 +01:00
Joachim Nilsson 338810b886 Fix #92: Load .conf files and initialize global_rlimit[] earlier
As reported in #92, systems with an udevd very early register a service
using global_rlimit[].  The change was introduced in b68c5c1 but did
not take the ordering problem into consideration, global_rlimit[] was
left uninitialized, which likely led to severely limited udevd that
was continously crashing.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 16:51:49 +01:00
Joachim Nilsson c1344e89ff api_exit(): Only shutdown() of API socket at runtime
Not needed at shutdown/reboot.  Also, it may segfault at that stage.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:48:44 +01:00
Joachim Nilsson bc35b42bd2 inet_stop(): Check argument and skip shutdown(), just close() socket
- Validate function argument
- Skip¹ shutdown() when closing a TCP socket, not needed

___
¹ also, shutdown() segfaults a late shutdown/reboot

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:46:19 +01:00
Joachim Nilsson dcf8fbe235 do_shutdown(): Advise watchdog using SIGPWR, system is going down
On shutdown/reboot, send SIGPR as a heads-up to watchdogd that the
system is going down.  All file systems are about to be unmounted.
It's just about the last process left running in the system, so we
give it two seconds to complete its work before we continue.

When everything has been unmounted and we're ready to call reboot(),
we send SIGTERM to the registered watchdog, to give ti the chance to
let the WDT reset the system.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:45:02 +01:00
Joachim Nilsson c82a2ce215 Built-in watchdog: on SIGPWR, exit main loop and prepare for SIGTERM
When Finit enters shutdown/reboot we need to advise any watchdogd that
we are going down *before* we unmount any file systems.  This to let
the user save any state, e.g. reboot counters, to disk.  Right before
we call reboot() in Finit we send SIGTERM to the watchdogd to give it
the chance to use the WDT to reboot the system.

The WDT cricuitry on embedded systems is often connected to the RESETn
logic of all relevant board compoents, whereas reboot() only reboots the
SoC with the CPU.  Hence, a WDT reboot is more efficient and more like a
regular power cycle, which users of embedded systems often want.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:24:32 +01:00
Joachim Nilsson e1bb5e3396 Add --disable-redirect-output to configure script
This patch makes it possible to control the default redirection of
(misbehaving) services output to /dev/null.  With this disabled,
a service may write to its stdout/stderr and mess up the console.

By default all service/run/tasks stdout/stderr is redirected.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:22:06 +01:00
Joachim Nilsson 8198582c10 Revert "Travis-CI: Disable clang temporarily, for Coverity Scan run"
This reverts commit cbafbaea1d.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 11:01:24 +01:00
Joachim Nilsson 0d19842aa1 conf_changed(): Fix possible NULL ptr deref, found by Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:35:07 +01:00
Joachim Nilsson 6f665ab283 inet_dgram_drop(): Fix control flow logic, found by Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:31:19 +01:00
Joachim Nilsson 3f731aa5a3 inet_dgram_drop(): Check return value from recv()
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:28:22 +01:00
Joachim Nilsson 9e8a01669e api: Fix possible descriptor leak, found by Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:02:49 +01:00
Joachim Nilsson cbafbaea1d Travis-CI: Disable clang temporarily, for Coverity Scan run
... so we don't lose tokens ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 09:53:14 +01:00
Joachim Nilsson f534368b33 The @console and nologin flags deserve mentioning early
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 09:51:16 +01:00
Joachim Nilsson 78fc9fccc5 Update initctl usage text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 09:51:04 +01:00
Joachim Nilsson 3f7df3d35e Update ChangeLog with new nologin option to tty configs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 03:29:38 +01:00
Joachim Nilsson 9fc33418f9 Document nologin option flag to tty configuration directive
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 03:26:31 +01:00
Joachim Nilsson 72bc3b1a91 Fix #91: Add support for tty nologin, to start /bin/sh immediately
This patch adds a `nologin` flag to the `tty` configuration directive,
making it possible to set up really simple embedded systems with no
login.

Example, put the following line in `/etc/finit.d/getty.conf` to get
a "Please press Enter to ..." prompt before being presented with a
root shell prompt.

    tty [12345] @console noclear nologin

Needless to say, this is not very secure, but can be really useful
for developer setups, during board bringup, or similar.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 01:54:07 +01:00
Joachim Nilsson d6ea1c0cab Protect internal services like dbus-daemon and udevd
In f3669c7 the difference between static and dynamic services was
removed.  This led to a regression in handling internally created
services for, e.g., dbus-daemon and udevd, mentioned in issue #90.

This patch introduces a "protected" flag for svc_t to prevent the
mark-and-sweep handling of regularly loaded services.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 21:57:31 +01:00
Joachim Nilsson 523a270f22 Update README slightly, there's systemd-udevd and eudev these days
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 19:33:27 +01:00
Joachim Nilsson 3258edc246 Fix #90: Restore tmpfs mount of /dev/shm, removed in d7401b2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 19:02:06 +01:00
Joachim Nilsson 32b7a14cb3 Add ismnt() helper function, wrap fismnt()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 19:00:41 +01:00
86 changed files with 2919 additions and 905 deletions
+8 -1
View File
@@ -2,13 +2,15 @@
*.o
*.so
*.gz
ID
GPATH
GRTAGS
GSYMS
GTAGS
# GNU configure and build system
/autom4te.cache/*
/.deps
/autom4te.cache
/aclocal.m4
/ar-lib
/compile
@@ -33,3 +35,8 @@ GTAGS
/.config
/.stamp
/.unpacked
# WeOS build system
/.autoconf
/.automake
/.built
+9 -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
+5
View File
@@ -1,6 +1,11 @@
Current Maintainer(s):
* Joachim Nilsson <troglobit@gmail.com>
Major Contributors:
* Tobias Waldekranz <tobias@waldekranz.com>
* Mattias Walström <mattias.walstrom@westermo.se>
* Jonas Johansson <jonas.johansson@westermo.se>
Original Author(s):
* Claudio Matsuoka <cmatsuoka@gmail.com>
+116 -4
View File
@@ -4,17 +4,102 @@ Change Log
All relevant changes are documented in this file.
[3.1][] - 2017-12-xx
[3.2][UNRELEASED] - 2018-10-xx
------------------------------
Bug fix release.
### Changes
* Add support for `--disable-docs` and `--disable-contrib` to speed up
builds and work around issue with massively parallel builds
* Add `-b`, batch mode, for non-interactive use to `initctl`
* Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog
* Set `$SHELL`, like `$PATH`, to a sane default value, needed by BusyBox
* Finit no longer automatically reloads its `*.conf` files after running
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
* `initctl` without an argument or option now defaults to list services
* Converted built-in watchdog daemon to standalone mini watchdogd
* Improved watchdog hand-over, now based on `svc_t` and not PID
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
not allowed in the runtime runlevel are unconditionally stopped
* Removed `HOOK_SVC_START` and `HOOK_SVC_LOST`, caused more problems
than they were worth. Users are encouraged to use accounting instead
* Skip displaying "Restarting ..." progress for bootstrap processes
* Added a simple work queue mechanism to queue up work at boot + runtime
- Postpone deletion of `svc_t` until any `SIGKILL` timer has elapsed
- As long as a stepped service changes state we queue another step all
event, because services may depend on each other
* Require new libuEv API: `uev_init1()` to reduce event cache so that
the kernel can invalidate deleted events before enqueing to userspace
### Fixes
* Fix #96: Start udevd as a proper service
* Ensure we track run commands as well as task/service, once per runlevel
* Fix #98: FTBFS with `--disable-inetd`
* Make sure to unblock UDP inetd services when connection terminates.
Regression introduced in v3.1
* Ensure run/tasks also go to stopping state on exit, like services,
otherwise it is unnecessarily hard to restart them
* Fix #99: Do not try to `SIGKILL` inetd services, they are not backed
by a PID. This caused a use after free issue crashing finit. Found
and fixed by Tobias Waldekranz, Westermo
* Fix missing OS/Finit title bug, adds leading newline before banner
* Remove "Failed connecting to watchdog ..." error message on systems
that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
Fixed by Jonas Johansson, Westermo
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
Fixed by Jonas Johansson, Westermo
* Fix #111: Only restart inetd services when necessary. E.g., if the
listening interface is changed. Only stop established connections
which are no longer allowed, i.e. do not touch already allowed
established connections. Fixed by Jonas Johansson, Westermo
* Fix: update inetd service args on config change. Found and fixed by
Petrus Hellgren, Westermo
* Fix `initctl [start | restart]`, should behave the same for services
that have crashed. Found by Mattias Walström, Westermo
* Wait for bootstrap phase to complete before cleaning out any bootstrap
processes that have stopped, they may be restarted again
* Reassert condition when an unmodified run/task/service goes from
WAITING back to RUNNING again after a reconfiguration event.
Found and fixed by Jonas Johansson, Westermo
* Remove service condition when service is deleted
[3.1][] - 2018-01-23
--------------------
Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
Improvements to `netlink.so` plugin, per-service `rlimit` support,
improved integration with `watchdogd`, auto-detect TTY console. Much
improved debug, rescue and logging support. Also, many fixes to both
big and small issues, most notably in the condition handling, which no
longer is sensitive to time skips.
This version requires at least libuEv v2.1.0 and libite v2.0.1
### Changes
* Support for more kernel command line settings:
- splash, enable boot progress
- debug, like `--debug` but also enable kernel debug
- single, single user mode (no network)
- rescue, new rescue mode
* Support for `IFF_RUNNING` to netlink plugin => `net/IFNAME/running`
* Support for restarting `initctl` API socket on `SIGHUP`
* Greatly updated `initctl status <JOB|NAME>` command
* Support for `rlimit` per service/run/task/inetd/tty, issue #45
* Support for setting `hard` and `soft` rlimit for a resource at once
* Support for auto-detecting serial console using Linux SysFS, the new
`tty @console` eliminates the need to keep track of different console
devices across embedded platforms: `/dev/ttyS0`, `/dev/ttyAMA0`, etc.
* Add TTY `nologin` option. Bypasses login and skips immediately to a
root shell. Useful during board bringup, in developer builds, etc.
* Support for calling run/tasks on Finit internal HOOK points, issue #18
* Removed support for long-since deprecated `console DEV` setting
* Cosmetic change to login, pressing enter at the `Press enter to ...`
@@ -25,6 +110,9 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
of inetd services, and `Loading configuration ...`. No end user knows
what those plugins and configurations are, i.e. internal state+config
* Change kernel WDT timeout (3 --> 30 sec) for built-in watchdog daemon
* Advise watchdog dawmon on shutdown and reboot using `SIGPWR` and then
`SIGTERM`. It is recommended the daemon start a timer on the first
signal, in case the shutdown process somehow hangs.
* Handle `/etc/` OverlayFS, reload /etc/finit.d/*.conf after `mount -a`
* initctl: Add support for printing previous runlevel
* initctl: Support short forms of all commands
@@ -38,6 +126,25 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
on mtime of `.conf` files, instead an inotify handler tracks file
changes for time *insensitive* dependency tracking
* Change condition handling to not rely on mtime but a generation id.
* New configure script option `--enable-redirect` to automatically
redirect `stdout` and `stderr` of all applications to `/dev/null`
* New `pid` sub-option to services when a service does not create a PID
file, or when the PID file has another name. Issue #95
* Greatly improved `log` sub-option to service/run/tasks, selectively
redirect `stdout` and `stderr` using the new tool `logit` to either
syslog or a logfile. Issue #44
* Support for automatic log rotation of logfiles created by `log`
option. Use `configure --disable-logrotate` on systems with a
dedicated log rotation service. Issue #44
* Support for disabling service/run/task progress with empty ` --`
description. Note: no description separator gives a default desc.
* Create `/etc/mtab` symlink if missing on system (bootmisc plugin)
* New hook: `hook/mount/post` runs after `mount -a` but before the
`hook/mount/all`, where `bootmisc.so` runs. This provides the
possibility of running a second stage mount command before files in
`/run` and similar are created
* Skip `gdbserver` when unleashing the grim reaper at shutdown
* Distribute and install `docs/` and `contrib/` directories
### Fixes
@@ -57,6 +164,8 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
plugin before the pidfile plugin on `HOOK_SVC_RECONF` events
* Handle event loop failure modes, issue found by Westermo
* Handle API socket errors more gracefully, restart socket
* Do not attempt to load kernel modules more than once at bootstrap
* Remove reboot symlinks properly on uninstall
* Fix regression in condition handling, reconf condition must be kept
as a reference point to previous reconfiguration, or bootstrap.
* Fix nasty race condition with internal service stop, abort kill if the
@@ -70,6 +179,7 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
* Fix issue #81 properly, remove use of SYSV shm IPC completely. Finit
now use the API socket for all communication between PID 1 and initctl
* Fix segfault on x86_64 when started with kernel cmdline `--debug`
* Normalize condition paths on systems with `/run` instead of `/var/run`
[3.0][] - 2017-10-19
@@ -97,7 +207,7 @@ distributions.
* Ensure plugins always have a default name, file name
* Reorganization, move all source files to a `src/` sub-directory
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
distributions. See [doc/distro.md](doc/distro.md) for details
distributions. See [docs/distro.md](docs/distro.md) for details
* Remove `UNUSED()` macro, mentioned here because it may have been used
by external plugin developers. Set `-Wno-unused-parameter` instead
* New table headings in `initctl`, using `top` style inverted text
@@ -667,7 +777,9 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.0...HEAD
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
[3.2]: https://github.com/troglobit/finit/compare/3.1...3.2
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
+8 -1
View File
@@ -3,12 +3,19 @@ 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
# Target to run when building a release
release: dist
release: distcheck
@for file in $(DIST_ARCHIVES); do \
md5sum $$file > ../$$file.md5; \
done
+90 -33
View File
@@ -2,17 +2,19 @@
* [Introduction](#introduction)
* [Features](#features)
* [Bootstrap](doc/bootstrap.md#bootstrap)
* [Bootstrap](docs/bootstrap.md#bootstrap)
* [Runlevels](#runlevels)
* [Syntax](doc/config.md#syntax)
* [Inetd](doc/inetd.md#inetd)
* [Syntax](docs/config.md#syntax)
* [Inetd](docs/inetd.md#inetd)
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
* [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
* [Hooks, Callbacks & Plugins](docs/plugins.md#hooks-callbacks--plugins)
* [Rebooting & Halting](#rebooting--halting)
* [Commands & Status](#commands--status)
* [Building](doc/build.md#building)
* [Running](doc/build.md#running)
* [Debugging](doc/build.md#debugging)
* [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
* [Building](docs/build.md#building)
* [Running](docs/build.md#running)
* [Recovery](docs/build.md#recovery)
* [Debugging](docs/build.md#debugging)
* [Requirements](#requirements)
* [Origin & References](#origin--references)
@@ -27,8 +29,11 @@ was reverse engineered from the [EeePC fastinit][] ten years ago by
Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section where both [Debian](contrib/debian/) and
up to date [Alpine Linux](contrib/alpine/) tutorials can be found.
[contrib/](contrib/) section with tutorials for:
* [Alpine Linux](contrib/alpine/),
* [Debian GNU/Linux](contrib/debian/), and
* [Void Linux](contrib/void/)
![Alpine screenshot](images/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
@@ -49,12 +54,23 @@ host default
# Runlevel to start after bootstrap, 'S', default: 2
runlevel 2
# Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size=100k count=4
# Services to be monitored and respawned as needed
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
service [S12345] /sbin/klogd -n -- Kernel log daemon
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
# The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The
# log file is rotated with the above settings. The condition declares
# a dependency on a system default route (gateway) to be set. A single
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
# For multiple instances of the same service, add :ID somewhere between
# the service/run/task keyword and the command.
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
@@ -87,10 +103,13 @@ tty [12345] /sbin/getty -L 115200 /dev/tty3 linux
# Use built-in getty for serial port and USB serial
tty [12345] /dev/ttyAMA0 115200 noclear nowait
tty [12345] /dev/ttyUSB0 115200 noclear
# Just give me a shell, I need to debug this embedded system!
tty [12345] @console noclear nologin
```
The `service` stanza, as well as `task`, `run`, `inetd` and others are
described in full in [doc/config.md](doc/config.md). Here's a quick
described in full in [docs/config.md](docs/config.md). Here's a quick
overview of some of the most common components needed to start a UNIX
daemon:
@@ -111,7 +130,7 @@ advanced uses as well:
service /usr/sbin/sshd -D
Dependencies are handled using [conditions](doc/conditions.md). One of
Dependencies are handled using [conditions](docs/conditions.md). One of
the most common conditions is to wait for basic networking to become
available:
@@ -152,7 +171,7 @@ Start, monitor and restart services should they fail.
**Inetd**
Finit comes with a built-in [inetd server](doc/inetd.md). No need to
Finit comes with a built-in [inetd server](docs/inetd.md). No need to
maintain a separate config file for services that you want to start on
demand.
@@ -167,7 +186,7 @@ Built-in optional inetd services:
- discard [RFC863][]
- time (rdate) [RFC868][]
For more information, see [doc/inetd.md](doc/inetd.md).
For more information, see [docs/inetd.md](docs/inetd.md).
**Getty**
@@ -192,7 +211,9 @@ figures it out by querying sysfs: `/sys/class/tty/console/active`.
tty [12345] @console 115200 linux noclear
```
For more information, see [doc/config.md](doc/config.md#syntax).
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
latter is for skipping the login process entirely. For more information,
see [docs/config.md](docs/config.md#syntax).
**Runlevels**
@@ -201,10 +222,18 @@ Support for SysV init-style [runlevels][5] is available, in the same
minimal style as everything else in Finit. The `[2345]` syntax can be
applied to service, task, run, inetd, and TTY stanzas.
All services in runlevel S(1) are started first, followed by the desired
run-time runlevel. Runlevel S can be started in sequence by using `run
[S] cmd`. Changing runlevels at runtime is done like any other init,
e.g. <kbd>init 4</kbd>, but also using the more advanced `intictl` tool.
Reserved runlevels are 0 and 6, halt and reboot, respectively just like
SysV init. Runlevel 1 can be configured freely, but is recommended to
be kept as the system single-user runlevel since Finit will not start
networking here. The configured `runlevel NUM` from `/etc/finit.conf`
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
given on the kernel cmdline, in which case runlevel 1 is started.
All services in runlevel S(0) are started first, followed by the desired
run-time runlevel. Run tasks in runlevel S can be started in sequence
by using `run [S] cmd`. Changing runlevels at runtime is done like any
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
`intictl` tool.
**Plugins**
@@ -229,25 +258,49 @@ Extensions and functionality not purely related to what an `/sbin/init`
needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O.
For more information, see [doc/plugins.md](doc/plugins.md).
For more information, see [docs/plugins.md](docs/plugins.md).
**Progress**
Finit prior to v3.0 showed the progress at boot: plugins and modules
being loaded, run/tasks/services being started, etc. By default Finit
is now silent, but progress can still be enabled at build-time using:
configure --enable-progress
or at boot from the kernel cmdline using the boot parameter `splash`:
```shell
append="init=/sbin/finit splash"
```
For a really nice boot, add `quiet` as well. This silences the output
from the kernel, leaving only warnings and errors. For other kernel
command line parameters, see [docs/build.md](docs/build.md#recovery).
Runparts & /etc/rc.local
------------------------
At the end of the boot, when networking and all services are up, Finit
calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory,
and `/etc/rc.local`, in that order if they exist.
At the end of the boot, when all bootstrap (`S`) tasks and services have
started, but not networking, Finit calls its built-in [run-parts(8)][]
command on any configured `runparts <DIR>` directory. This happens just
before changing to the configured runlevel (default 2). (Networking is
enabled just prior to changing from single user mode.)
```shell
runparts /etc/rc.d/
```
Right after the runlevel change when all services have started properly,
`/etc/rc.local` is called.
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
If it exists and is an executable shell script, finit calls it at the
very end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on
hooks in [doc/plugins.md](doc/plugins.md#hooks), and about the system
bootstrap in [doc/bootstrap.md](doc/bootstrap.md).
If it exists and is an executable shell script Finit calls it at the very
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
in [docs/plugins.md](docs/plugins.md#hooks), and about the system bootstrap
in [docs/bootstrap.md](docs/bootstrap.md).
Runlevels
@@ -325,7 +378,7 @@ section.
For compatibility reasons Finit listens to the same set of signals as
BusyBox init. This is not 100% compatible with SysV init, but clearly
the more common combination for Finit. For more details, see
[doc/signals.md](doc/signals.md).
[docs/signals.md](docs/signals.md).
Finit also listens to the classic SysV init FIFO, used by `telinit`.
Support for this is implemented by the `initctl.so` plugin. Hence,
@@ -373,6 +426,7 @@ Commands:
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
touch <CONF> Mark .conf in /etc/finit.d/ for reload
reload Reload *.conf in /etc/finit.d/ (activates changes)
cond set <COND> Set (assert) condition => +COND
@@ -384,6 +438,7 @@ Commands:
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
status <JOB|NAME>[:ID] Show service status, by job# or name
status | show Show status of services, default command
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
@@ -396,7 +451,7 @@ Commands:
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
file must be used to tell Finit to stop and start it on `reload` and
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
`runlevel` changes. If `<>` holds more [conditions](docs/conditions.md),
these will also affect how a service is maintained.
**Note:** even though it is possible to start services not belonging in
@@ -422,11 +477,13 @@ Requirements
------------
Finit is capable of running on both desktop/server systems with udev and
embedded systems that usually come with BusyBox mdev. Finit probes for
them at runtime and expects `/dev/` to be writable, usually `devtmpfs`.
It is also possible to run on a statically set up `/dev` if needed. It
is however not a good idea to have both udev and mdev installed at the
same time, this will lead to unpredictable results.
embedded systems that usually come with BusyBox mdev. Some systems have
systemd-udev or eudev today instead of the original udev, Finit probes
for all of them at runtime and expects `/dev/` to be a writable file
system using `devtmpfs`. It is also possible to run on a statically set
up `/dev` if needed. It is however not a good idea to have both udev
and mdev installed at the same time, this will lead to unpredictable
results.
At boot Finit calls either `mdev` or `udevd` to populate `/dev`, this is
done slightly differently and on systems with udev you might want to add
+44 -9
View File
@@ -1,10 +1,17 @@
AC_INIT(Finit, 3.1-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AC_INIT(Finit, 3.2-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR([src/finit.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile plugins/Makefile src/Makefile])
AC_CONFIG_FILES([Makefile
contrib/Makefile
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
docs/Makefile
plugins/Makefile
src/Makefile])
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4])
@@ -21,6 +28,7 @@ LT_INIT([shared disable-static dlopen aix-soname=both disable-fast-install])
AC_PROG_CC
AC_PROG_LN_S
AC_PROG_INSTALL
AC_PROG_MKDIR_P
# Configuration.
AC_HEADER_STDC
@@ -33,7 +41,7 @@ AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 2.1.0])
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
# Check for configured Finit features
@@ -58,6 +66,10 @@ AC_ARG_ENABLE(watchdog,
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
enable_watchdog=no])
AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
enable_redirect_output=no])
AC_ARG_ENABLE(inetd,
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
enable_inetd=yes])
@@ -66,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])
@@ -75,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.])
@@ -89,11 +108,6 @@ AC_ARG_WITH(fifo,
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
[fifo=$withval], [fifo=$localstatedir/run/initctl])
AC_ARG_WITH(plugindir,
AS_HELP_STRING([--with-plugindir=DIR], [Finit plugin directory, default /lib/finit/plugins]),
[pkglibdir=$withval], [pkglibdir=$libdir/finit/plugins])
AC_SUBST([pkglibdir])
AC_ARG_WITH(config,
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
[conf=$withval], [conf=$sysconfdir/finit.conf])
@@ -143,6 +157,9 @@ AS_IF([test "x$enable_progress" = "xyes"], [
AS_IF([test "x$enable_watchdog" = "xyes"], [
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
AS_IF([test "x$enable_redirect" = "xyes"], [
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
### Disable features ###########################################################################
AS_IF([test "x$enable_inetd" != "xno"], [
enable_inetd="yes"
@@ -206,6 +223,21 @@ 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(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])
AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf")
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
# Generate all files
AC_OUTPUT
@@ -220,6 +252,7 @@ cat <<EOF
FIFO path.............: $fifo_path
Finit config file.....: $conf_path
Finit config.d path...: $rcsd_path
Finit plugin path.....: $plugin_path
Compat rc.local path..: $rclocal_path
Random seed path......: $random_path
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
@@ -230,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
+2
View File
@@ -0,0 +1,2 @@
Makefile
Makefile.in
+5
View File
@@ -0,0 +1,5 @@
SUBDIRS = alpine debian void
bin_SCRIPT = service
docsdir := @docdir@/contrib
docs_DATA = README.md finit.conf procmon.sh
EXTRA_DIST = $(docs_DATA)
+5
View File
@@ -0,0 +1,5 @@
SUBDIRS = finit.d
docsdir := @docdir@/contrib/alpine
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
EXTRA_DIST = $(docs_DATA)
+27
View File
@@ -0,0 +1,27 @@
SUBDIRS = available
docsdir := @docdir@/contrib/alpine/finit.d
d_LINKS = dropbear.conf getty.conf keymap.conf modules.conf klogd.conf \
syslogd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ -L $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) $$src $$lnk; \
fi; \
done
uninstall-hook:
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ "`readlink $$lnk`" != "$$src" ]; then \
continue; \
fi; \
$(RM) $$lnk; \
done
@@ -0,0 +1,4 @@
docsdir := @docdir@/contrib/alpine/finit.d/available
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
lxdm.conf modules.conf ntpd.conf klogd.conf syslogd.conf
EXTRA_DIST = $(docs_DATA)
+4
View File
@@ -0,0 +1,4 @@
SUBDIRS = finit.d grub.d
docsdir := @docdir@/contrib/debian
docs_DATA = README.md finit.conf os-release build.sh install.sh
EXTRA_DIST = $(docs_DATA)
+27
View File
@@ -0,0 +1,27 @@
SUBDIRS = available
docsdir := @docdir@/contrib/debian/finit.d
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
modules.conf rsyslogd.conf sshd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ -L $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) $$src $$lnk; \
fi; \
done
uninstall-hook:
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ "`readlink $$lnk`" != "$$src" ]; then \
continue; \
fi; \
$(RM) $$lnk; \
done
@@ -0,0 +1,5 @@
docsdir := @docdir@/contrib/debian/finit.d/available
docs_DATA = acpid.conf console-setup.conf getty.conf \
keyboard-setup.conf lightdm.conf modules.conf \
ntpclient.conf rsyslogd.conf sshd.conf xdm.conf
EXTRA_DIST = $(docs_DATA)
+3
View File
@@ -0,0 +1,3 @@
docsdir := @docdir@/contrib/debian/grub.d
docs_DATA = 40_custom
EXTRA_DIST = $(docs_DATA)
+11
View File
@@ -17,6 +17,10 @@ runlevel 2
# Network bringup script
network service networking start
# Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size=100k count=4
# Virtual consoles to start getty on
tty /dev/tty1
tty /dev/tty2
@@ -33,6 +37,13 @@ service [2345] /sbin/klogd -n -- Kernel logging server
service [2345] /sbin/syslogd -n -- Syslog server
service [3] /usr/sbin/gdm -- GNOME Display Manager
# The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The
# log file is rotated with the above settings. The condition declares
# a dependency on a system default route (gateway) to be set. A single
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
# Run start scripts from this directory
# runparts /etc/start.d
+51
View File
@@ -0,0 +1,51 @@
#!/bin/sh
USAGE="Usage: $0 processName"
LOG_FILE="/tmp/memusage.csv"
ELAPSED_TIME=0
PERIOD=1 # seconds
if [ $# -ne 1 ]; then
echo $USAGE
exit 1
fi
echo "ElapsedTime,VmSize,VmRSS" > $LOG_FILE
# Monitor memory usage forever until script is killed
while :
do
SUM_VM_SIZE=0
SUM_RSS_SIZE=0
# In case the monitored process has not yet started
# keep searching until its PID is found
PROCESS_PIDS=""
while :
do
PROCESS_PIDS=`/bin/pidof $1`
if [ "$PROCESS_PIDS.X" != ".X" ]; then
break
fi
done
for PID in ${PROCESS_PIDS} ; do
VM_SIZE=`awk '/VmSize/ {print $2}' < /proc/$PID/status`
if [ "$VM_SIZE.X" = ".X" ]; then
continue
fi
#echo exprVM_ $SUM_VM_SIZE + $VM_SIZE
SUM_VM_SIZE=`expr $SUM_VM_SIZE + $VM_SIZE`
VM_RSS=`awk '/VmRSS/ {print $2}' < /proc/$PID/status`
if [ "$VM_RSS.X" = ".X" ]; then
continue
fi
SUM_RSS_SIZE=`expr $SUM_RSS_SIZE + $VM_RSS`
done
echo "$ELAPSED_TIME sec, $SUM_VM_SIZE KB, $SUM_RSS_SIZE KB"
echo "$ELAPSED_TIME,$SUM_VM_SIZE,$SUM_RSS_SIZE" >> $LOG_FILE
sleep $PERIOD
VM_SIZE=""
VM_RSS=""
# Needs to get actual elapsed time instead of doing this
ELAPSED_TIME=`expr $ELAPSED_TIME + $PERIOD`
done
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
JOB=$1
CMD=$2
usage() {
echo "Usage: service [job# | name] <stop | start | restart | status | show>"
exit 1
}
if [ $# -eq 0 ]; then
initctl show
exit 0
fi
case $CMD in
stop)
;;
start)
;;
restart)
;;
status)
;;
show)
;;
*)
usage
;;
esac
initctl $2 $1
+4
View File
@@ -0,0 +1,4 @@
SUBDIRS = finit.d
docsdir := @docdir@/contrib/void
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
EXTRA_DIST = $(docs_DATA)
+26
View File
@@ -0,0 +1,26 @@
SUBDIRS = available
docsdir := @docdir@/contrib/void/finit.d
d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ -L $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) $$src $$lnk; \
fi; \
done
uninstall-hook:
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ "`readlink $$lnk`" != "$$src" ]; then \
continue; \
fi; \
$(RM) $$lnk; \
done
@@ -0,0 +1,5 @@
docsdir := @docdir@/contrib/void/finit.d/available
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
sshd.conf syslogd.conf uuidd.conf
EXTRA_DIST = $(docs_DATA)
+2
View File
@@ -0,0 +1,2 @@
Makefile
Makefile.in
+4
View File
@@ -0,0 +1,4 @@
docsdir := @docdir@/docs
docs_DATA = config.md conditions.md distro.md inetd.md plugins.md \
service.md signals.md svc-machine.png cmdline.md
EXTRA_DIST = $(docs_DATA)
+28 -34
View File
@@ -18,27 +18,29 @@ after a reconfiguration (`initctl reload`), the Finit dependency system
can also be used.
General
-------
Near Future
-----------
* Add support for `rescue` on the kernel command line, start rescue mode
if enabled with `configure` script. Could mean skip `/etc/finit.conf`
et al and jump straight to a single getty login on console.
* Add support for timed shutdown in Finit, including cancelled shutdown.
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
API restart to SIGHUP callback for the same functionality in Finit.
* 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.
* Add `IFF_RUNNING` support to netlink plugin along `IFF_UP`
* Implement `initctl stop|start|restart|reload|status <SVC>` and
`service <SVC> stop|start|restart|reload|status` on top
* 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`
* Improve `initctl show SVC` output. Add service uptime, last ten SVC
logs/history (historian), etc. Similar to `systemctl show SVC`
* [Solaris SMF][] (Service Management Facility) has some interesting
features that may be well worth looking into adopting:
@@ -49,6 +51,13 @@ General
* `initctl add /sbin/service -n -- Service description`, for details
see above [Solaris SMF][] featuer, issue #55 and issue #69,
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
* Changing the name of a pidfile, `pid:[/path/]file.pid`, after a
service has been started is not supported atm. The previous name will
not be (never) removed and the new name will not be created until the
process has been stopped and started again.
* PID files created in subdirectories to `/run` is not supported right
now inotify listeners should be automatically created (and removed)
when a subdirectory is added (or removed).
Init
@@ -85,14 +94,16 @@ system-level timed periodic tasks:
Proposed syntax:
# Crontab
cron @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
cron @SPEC [LVLS] /path/to/cmd [ARGS] -- Optional descr
# One-shot 'at' command
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
Notice the ISO date in notation. The runlevels is extra filtering
sugar. E.g., if cron service is only allowed in runlevels two or three
it will not start if system currently is in runlevel four.
Notice the ISO date in the `at` notation. The `cron @SPEC` could use
are mo loose notation, e.g. `@hourly`, `@midnight`, `@daily`, `@weekly`,
`@reboot`, etc. The runlevels is extra filtering sugar. E.g., if cron
service is only allowed in runlevels two or three it will not start if
system currently is in runlevel four.
To run a command as another user the `.conf` file must have a that
owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
@@ -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:
-->
+4 -4
View File
@@ -5,7 +5,7 @@ Bootstrap
2. Set up initial signal handlers
3. Set up default $PATH early
4. Mount `/proc` and `/sys`
5. Check kernel command line for `--debug` to figure out log level
5. Check kernel command line for `debug` to figure out log level
6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type
@@ -25,13 +25,13 @@ Bootstrap
20. Bring up loopback interface and all `/etc/network/interfaces`, if
the `.conf` setting `network <SCRIPT>` is set, it is called instead
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
22. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
(re)loaded and new services, tasks, and blocking run commands are
started. Provided they are allowed in the new runlevel and all of
their conditions, if any, are set.
23. Call 4th level hooks, `HOOK_SVC_UP`
24. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
24. Call 4th level hooks, `HOOK_SVC_UP`
25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs, or a fallback shell on `/dev/console`
+21 -5
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.
@@ -124,14 +122,32 @@ append="init=/sbin/finit"
Remember to also set up an initial `/etc/finit.conf` before rebooting!
Recovery
--------
To rescue a system with Finit, append the following to the kernel
command line:
```shell
append="init=/sbin/finit rescue"
```
This tells Finit to start in a very limited recovery mode, no services
are loaded, no filesystems are mounted or checked, and no networking is
enabled. The default Finit rescue mode configuration is installed into
`/lib/finit/rescue.conf`, which can be safely removed or changed.
By default the a root shell, without login, is started.
Debugging
---------
Add `finit_debug`, or `--debug`, to the kernel command line to enable
debug messages.
Edit, or append to, the kernel command line: remove `quiet` to enable
kernel messages and add `debug` to enable Finit debug messages.
```shell
append="init=/sbin/finit --debug"
append="init=/sbin/finit debug"
```
To debug startup issues, in particular issues with getty/login, try
+38
View File
@@ -0,0 +1,38 @@
Tips & Tricks with the kernel cmdline
=====================================
This document summarizes the different boot parameters that can be
passed on the Linux kernel command line. Not just limited to Finit.
* `debug`, or `--debug`
Enable debug mode. The former syntax also enable kernel debug, and
the latter only in Finit. Debug messages are printed to the console
until a syslog daemon has been started.
* `init=/bin/sh`
Bypass system default init and tell kernel to start a shell. Note,
this shell is very limited and does not support signals and has no
job control. Recommend using, and modifying, `rescue` mode instead.
* `panic=SEC`
By default the kernel does not reboot after a kernel panic. This
setting will cause a kernel reboot after SEC seconds.
* `quiet`
Suppress kernel logging to console, except for warnings and errors.
* `rescue`
Start Finit rescue mode; no network, no `fsck` of file systems in
`/etc/fstab`, no `/etc/rc.local`, no `runparts`. The configuration
is read from `/etc/rescue.conf`, can be modified by the operator.
* `single`, or `S`
Single user mode, runlevel 1, in Finit. Useful to debug problems with
the regular runlevel. All services and TTYs in `[1]` will be started,
so a `tty [1] @console nologin` configuration presents you with a
root console without login.
* `splash`
Display Finit splash screen with progress at bootstrap and shutdown.
For more on kernel boot paramaters, see the man page `bootparam(7)`.
+4 -3
View File
@@ -95,10 +95,11 @@ Built-in conditions:
- `net/route/default`
- `net/<IFNAME>/exist`
- `net/<IFNAME>/up`
- `net/<IFNAME>/running`
**Note:** `up` means administratively up, the interface flag `IFF_UP`,
not link up, `IFF_RUNNING`. This latter conditions is currently not
implemented by the `plugins/netlink.c` plugin.
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
difference is that `running` tells if the NIC has link.
Debugging
+148 -34
View File
@@ -2,6 +2,7 @@ Configuration
=============
* [Introduction](#introduction)
* [Service Wrapper Scripts](#service-wrapper-scripts)
* [Syntax](#syntax)
* [Limitations](#limitations)
* [/etc/finit.conf](#etcfinitconf)
@@ -16,7 +17,7 @@ or in combination with `/etc/finit.d/*.conf`. Finit 3 can even start a
system using only `/etc/finit.d/*.conf`, highly useful for package-based
Linux distributions -- each package can provide its own "script" file.
- `/etc/finit.conf`: main configuration file, read only once at boot
- `/etc/finit.conf`: main configuration file
- `/etc/finit.d/*.conf`: snippets, usually one service per file
Not all configuration directives are available in `/etc/finit.d/*.conf`
@@ -26,8 +27,15 @@ the section [Limitations](#limitations) below for details.
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
call `finit q`, but that has been deprecated with the `initctl` tool).
Any service read from this directory is flagged as a dynamic service, so
changes to or removal of `/etc/finit.d/*.conf` files, is detected.
Finit monitors all known active `.conf` files, so if you want to force
a restart of any service you can simply touch its corresponding `.conf`
file in `/etc/finit.d` and call `initctl reload`. Finit handle any
and all conditions and dependencies between services automatically.
It is also possible to drop `.conf` files in `/etc/finit.d/available/`
and use `initctl enable` to enable a service `.conf` file. This may be
useful in particular to Linux distributions that may want to install all
files for a package and let the user decide when to enable a service.
On `initctl reload` the following is checked for all services:
@@ -42,24 +50,63 @@ For more info on the different states of a service, see the separate
document [Finit Services](service.md).
Service Wrapper Scripts
-----------------------
If your service requires to run additional commands, executed before the
service is actually started, like the systemd `ExecStartPre`, you can
use a wrapper shell script to start your service.
The Finit service `.conf` file can be put into `/etc/finit.d/available`,
so you can control the service using `initctl`. Then use the path to
the wrapper script in the Finit `.conf` service stanza. The following
example employs a wrapper script in `/etc/start.d`.
**Example:**
* `/etc/finit.d/available/program.conf`:
```conf
service [235] <!> /etc/start.d/program -- Example Program
```
* `/etc/start.d/program:`
```shell
#!/bin/sh
# Prepare the command line options
OPTIONS="-u $(cat /etc/username)"
# Execute the program
exec /usr/bin/program $OPTIONS
```
**Note:**, that the example sets `<!>` to denote that it doesn't support
`SIGHUP`. That way Finit will stop/start such the service instead of
sending SIGHUP at restart/reload.
Syntax
------
* `module <MODULE>`
* `host <NAME>`, or `hostname <NAME>`
Set system hostname to NAME, unless `/etc/hostname` exists in which
case the contents of that file is used.
* `module <MODULE> [ARGS]`
Load a kernel module, with optional arguments
* `network <PATH>`
Script or program to bring up networking, with optional arguments
* `rlimit <hard|soft> RESOURCE <LIMIT|unlimited>`
Set the hard or soft limit for a resource. `RESOURCE` is a lower-case
string matching the `RLIMIT_` constants from `setrlimit(2)`, without
the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
* `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
limit for a resource, or both if that argument is omitted. `RESOURCE`
is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
without the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
The limit is an integer that depends on the resource being modified,
see the man page, or the kernel `/proc/PID/limits` file, for more
information. Finit versions before v3.1 used `infinity` for
`unlimited`, which is still a supported keyword.
LIMIT is an integer that depends on the resource being modified, see
the man page, or the kernel `/proc/PID/limits` file, for details.
Finit versions before v3.1 used `infinity` for `unlimited`, which is
still supported, albeit deprecated.
```shell
# No process is allowed more than 8MB of address space
@@ -67,8 +114,14 @@ Syntax
# Core dumps may be arbitrarily large
rlimit soft core infinity
# CPU limit for all services, soft & hard = 10 sec
rlimit cpu 10
```
`rlimit` can be set globally, in `/etc/finit.conf`, or locally for
a set of task/run/services, in `/etc/finit.d/*.conf`.
* `runlevel <N>`
N is the runlevel number 1-9, where 6 is reserved for reboot.
Default is 2.
@@ -107,8 +160,30 @@ Syntax
Finit waits for another service, `/sbin/zebra`, to have created its
PID file in `/var/run/zebra.pid` before starting `ospfd`.
For a detailed description of conditions, and how to debug them,
see the [Finit Conditions](conditions.md) document.
Some services do not maintain a PID file and rather than patching each
application Finit provides a workaround. A `pid` keyword can be set
to have Finit automatically create (when starting) and later remove
(when stopping) the PID file. The file is created in the `/var/run`
directory using the `basename(1)` of the service. The default can be
modified with an optional argument:
pid[:[/path/to/]filename[.pid]]
For example, by adding `pid:/run/foo.pid` to the service `bar` that
PID file will, not only be created and removed automatically, but also
be used by the Finit condition subsystem. So another service/run/task
can depend on `<svc/foo>`.
If a service `bar` *does* maintain a PID file, but using `foo.pid`, we
can inform Finit of this by simplly prepending an `!` to the argument.
pid:!/run/foo.pid
Here Finit will *not* create/remove/touch the PID file, only use it
for the condition handling instead of the default PID file name.
For a detailed description of conditions, and how to debug them, see
the [Finit Conditions](conditions.md) document.
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
Launch a daemon when a client initiates a connection on an Internet
@@ -140,22 +215,39 @@ Syntax
* `runparts <DIR>`
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order.
The scripts in this directory are executed at the very end of runlevel
`S`, bootstrap.
It can be beneficial to use `S01name`, `S02othername`, etc. if there
is a dependency order between the scripts. Symlinks to existing
daemons can talso be used, but make sure they daemonize by default.
As the optional `/etc/rc.local` shell script, make sure that all your
Similar to the `/etc/rc.local` shell script, make sure that all your
services and programs either terminate or start in the background or
you will block Finit.
* `include <CONF>`
Include another configuration file. Absolute path required.
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [TERM]`
* `log size:200k count:5`
Log rotation for run/task/services using the `log` sub-option with
redirection to a log file. Global setting, applies to all services.
The size can be given as bytes, without a specifier, or in `k`, `M`,
or `G`, e.g. `size:10M`, or `size:3G`. A value of `size:0` disables
log rotation. The default is `200k`.
The count value is recommended to be between 1-5, with a default 5.
Setting count to 0 means the logfile will be truncated when the MAX
size limit is reached.
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
The first variant of this option uses the built-in getty on the given
TTY device DEV, in the given runlevels. Default baud rate is `38400`.
TTY device DEV, in the given runlevels. The DEV may be the special
keyword `@console`, which is very useful on embedded systems. Default
baud rate is `38400`.
**Example:**
```conf
@@ -182,6 +274,11 @@ Syntax
embedded systems running multiple unused getty wastes both memory
and CPU cycles, so `wait` is the preferred default.
The `nologin` option disables getty and `/bin/login`, and gives the
user a root (login) shell on the given TTY `<DEV>` immediately.
Needless to say, this is a rather insecure option, but can be very
useful for developer builds, during board bringup, or similar.
Notice the ordering, the `TERM` option to the built-in getty must be
the last argument.
@@ -236,8 +333,22 @@ Without the `:ID` to the service the latter will overwrite the former
and only the old web server would be started and supervised.
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
`log` to redirect `stderr` and `stdout` of the application to syslog,
using `logger`.
`log` to redirect `stderr` and `stdout` of the application to a file or
syslog using the native `logit` tool. The full syntax is:
log:/path/to/file
log:prio:facility.level,tag:ident
log:null
log
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
Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document.
@@ -248,31 +359,34 @@ Limitations
To understand the limitations of `finit.conf` vs `finit.d` it is useful
to picture the different phases of the system: bootstrap, runtime, and
shutdown. The `finit.conf` file can be used for all possible settings,
but `finit.d/*.conf` can only be used for runtime settings.
shutdown.
### /etc/finit.conf
This file was the only way to set up and boot a system using Finit. It
is used for pre-runtime settings like system hostname, network bringup
and shutdown:
This file used to be the only way to set up and boot a Finit system.
Today it is mainly used for pre-runtime settings like system hostname,
network bringup and shutdown:
- `host`
- `mkdod`
- `network`
- `runparts`
- `host`, only at bootstrap, (runlevel `S`)
- `mknod`, only at bootstrap
- `network`, only at bootstrap
- `runparts`, only at bootstrap
- `include`
- `shtudown`
- `runlevel`
- `log`, global setting
- `shutdown`
- `runlevel`, only at bootstrap
- ... and all configuration stanzas from `/etc/finit.d` below
### /etc/finit.d
Support for partial `.conf` files in `/etc/finit.d` was added to handle
changes of the system configuration at runtime. It does *not support*
the above `finit.conf` settings described above, only the following:
Support for per-service `.conf` files in `/etc/finit.d` was added in
v3.0 to handle changes of the system configuration at runtime. As of
v3.1 `finit.conf` is also handled at runtime, except of course for any
stanza that only runs at bootstrap. However, a `/etc/finit.d/*.conf`
does *not support* the above `finit.conf` settings described above, only
the following:
- `module`, but only in runlevel `S`
- `module`, but only at bootstrap
- `service`
- `task`
- `run`
View File
View File
-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
View File
View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

+1 -8
View File
@@ -1,3 +1,4 @@
pkglibdir = $(plugin_path)
AM_LDFLAGS = -module -avoid-version -shared
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
@@ -38,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
@@ -83,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
+3
View File
@@ -170,6 +170,9 @@ static void setup(void *arg)
makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, gid);
if (!fexist("/etc/mtab"))
symlink("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
uid = getuser("uuidd", NULL);
if (uid >= 0) {
+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);
+20 -23
View File
@@ -94,6 +94,17 @@ static void nl_route(struct nlmsghdr *nlmsg)
}
}
static void net_cond_set(char *ifname, char *cond, int set)
{
char msg[MAX_ARG_LEN];
snprintf(msg, sizeof(msg), "net/%s/%s", ifname, cond);
if (set)
cond_set(msg);
else
cond_clear(msg);
}
static void nl_link(struct nlmsghdr *nlmsg)
{
int la;
@@ -112,39 +123,25 @@ static void nl_link(struct nlmsghdr *nlmsg)
while (RTA_OK(a, la)) {
if (a->rta_type == IFLA_IFNAME) {
char msg[MAX_ARG_LEN];
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK:
/*
* New interface has appearad, or interface flags has changed.
* New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN
*/
_d("%s: New link, ifi_change 0x%x", ifname, i->ifi_change);
if (i->ifi_change & IFF_UP) {
snprintf(msg, sizeof(msg), "net/%s/up", ifname);
if (i->ifi_flags & IFF_UP)
cond_set(msg);
else
cond_clear(msg);
if (string_compare("lo", ifname)) {
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
cond_set(msg);
}
} else {
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
cond_set(msg);
}
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
break;
case RTM_DELLINK:
/* NOTE: Interface has dissapeared, not link down ... */
/* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname);
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
cond_clear(msg);
net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
break;
case RTM_NEWADDR:
+40 -38
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];
@@ -50,7 +44,6 @@ static void pidfile_callback(void *arg, int fd, int events)
struct inotify_event *ev;
ssize_t sz, len;
char *basename;
svc_t *svc;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
@@ -60,23 +53,16 @@ static void pidfile_callback(void *arg, int fd, int events)
}
ev_buf[sz] = 0;
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
for (ev = (void *)ev_buf;
sz > (ssize_t)sizeof(*ev);
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
/* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
if (!ev->mask || !strstr(ev->name, ".pid"))
continue;
basename = strtok(ev->name, ".");
if (!basename)
continue;
svc = svc_find_by_nameid(basename, 1);
svc = svc_find_by_pidfile(ev->name);
if (!svc)
continue;
/* TODO FIXME XXX WKZ check that pid is controlled by finit */
_d("%s: match %s", basename, svc->cmd);
mkcond(cond, sizeof(cond), svc->cmd);
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
svc_started(svc);
@@ -87,43 +73,59 @@ static void pidfile_callback(void *arg, int fd, int events)
}
/*
* Assert condition only if the service is running, but not if it's
* recently been changed or while it's starting up.
* This function is called after `initctl reload` to reassert conditions
* for services that have not been changed.
*
* We must wait for the service to create/touch its pidfile.
* We reassert the run/task/service's condition only if it is running,
* but not if it's recently been changed or while it's starting up.
*/
static void pidfile_reconf(void *arg)
{
static char cond[MAX_COND_LEN];
svc_t *svc, *iter = NULL;
int restart = 0;
do {
restart = 0;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->state != SVC_RUNNING_STATE)
continue;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
mkcond(cond, sizeof(cond), svc->cmd);
if (svc->state == SVC_RUNNING_STATE &&
!svc_is_changed(svc) &&
!svc_is_starting(svc) &&
cond_get(cond) != COND_ON) {
cond_set_path(cond_path(cond), COND_ON);
restart = 1;
}
}
if (svc_is_changed(svc) || svc_is_starting(svc))
continue;
if (restart)
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
mkcond(cond, sizeof(cond), svc->cmd);
if (cond_get(cond) == COND_ON)
continue;
} while (restart);
cond_set_path(cond_path(cond), COND_ON);
}
/*
* This will call service_step(), which in turn will schedule itself
* as long as stepped services change state. Services going from
* WAITING to RUNNING will reassert their conditions in that loop,
* which in turn may unlock other services, and so on.
*/
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
}
static void pidfile_init(void *arg)
{
char *path;
struct context *ctx = arg;
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
/*
* The bootmisc plugin is responsible for setting up /var/run.
* and/or /run, with proper symlinks etc. We depend on bootmisc
* so it's safe here to query realpath() and set up inotify.
*/
path = realpath(_PATH_VARRUN, NULL);
if (!path) {
_pe("Failed ");
return;
}
ctx->wd = inotify_add_watch(ctx->fd, path, IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
free(path);
if (ctx->wd < 0) {
_pe("inotify_add_watch()");
close(ctx->fd);
+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);
}
}
}
+2
View File
@@ -1,6 +1,8 @@
.deps/*
finit
initctl
logit
reboot
watchdogd
Makefile
Makefile.in
+31 -14
View File
@@ -1,14 +1,22 @@
EXTRA_DIST = rescue.conf
AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC
AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs
else
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
AM_LDFLAGS = -export-dynamic
endif
bin_PROGRAMS = logit
sbin_PROGRAMS = finit initctl reboot
if WATCHDOGD
pkglibexec_PROGRAMS = watchdogd
endif
logit_SOURCES = logit.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_SOURCES = api.c \
cond.c cond-w.c cond.h \
telinit.c \
@@ -17,8 +25,10 @@ finit_SOURCES = api.c \
getty.c stty.c \
helpers.c helpers.h \
log.c log.h \
mdadm.c mount.c pid.c \
mdadm.c mount.c \
pid.c pid.h \
plugin.c plugin.h private.h \
schedule.c schedule.h \
service.c service.h \
sig.c sig.h \
sm.c sm.h \
@@ -30,15 +40,12 @@ pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h log.h plugin.h svc.h
if INETD
finit_SOURCES += inetd.c inetd.h
endif
if WATCHDOGD
finit_SOURCES += watchdog.c watchdog.h
endif
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
if STATIC
finit_LDADD += plugins/libplug.la
finit_LDADD += ../plugins/libplug.la
else
finit_LDADD += -ldl
endif
@@ -57,12 +64,19 @@ reboot_LDADD = $(lite_LIBS)
INIT_LNKS = telinit init
REBOOT_LNKS = shutdown halt poweroff suspend
if WATCHDOGD
watchdogd_SOURCES = watchdog.c watchdog.h
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
#log.c log.h
endif
# Hook in install to add finit and reboot symlink(s)
install-exec-hook:
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
@for file in $(INIT_LNKS); do \
lnk=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$lnk ]; then \
echo "Skipping $$lnk exists already."; \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) finit $$lnk; \
fi; \
@@ -71,16 +85,17 @@ install-exec-hook:
echo "Non-finit reboot, skipping install."; \
else \
for file in $(REBOOT_LNKS); do \
link=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$link ]; then \
echo "$$link exists, skipping.";\
lnk=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) reboot $$link; \
$(LN_S) reboot $$lnk; \
fi; \
done; \
fi
uninstall-hook:
@$(RM) $(DESTDIR)$(pkglibdir)/rescue.conf
@for file in $(INIT_LNKS); do \
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
if [ "$$lnk" != "finit" ]; then \
@@ -93,9 +108,11 @@ uninstall-hook:
echo "Non-finit reboot, skipping uninstall."; \
else \
for file in $(REBOOT_LNKS); do \
if [ "`readlink $$file`" != "reboot" ]; \
then continue; \
lnk=$(DESTDIR)$(sbindir)/$$file; \
dst=`readlink $$lnk`; \
if [ "$$dst" != "reboot" ]; then \
continue; \
fi; \
$(RM) $(DESTDIR)$(sbindir)/$$file; \
$(RM) $$lnk; \
done; \
fi
+32 -18
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,12 +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;
}
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:
@@ -380,13 +395,11 @@ static void api_cb(uev_t *w, void *arg, int events)
_d("Failed sending ACK/NACK back to client");
}
if (UEV_ERROR == events)
goto error;
leave:
close(sd);
if (UEV_ERROR == events)
goto error;
return;
error:
api_exit();
if (api_init(w->ctx))
@@ -430,8 +443,9 @@ error:
int api_exit(void)
{
return shutdown(api_watcher.fd, SHUT_RDWR) ||
close(api_watcher.fd);
uev_io_stop(&api_watcher);
return close(api_watcher.fd);
}
/**
-5
View File
@@ -27,11 +27,6 @@
#include "finit.h"
#include "svc.h"
typedef struct {
char *cmd;
int (*cb)(char *arg);
} command_t;
int client_connect (void);
int client_disconnect (void);
+62 -16
View File
@@ -28,14 +28,19 @@
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "service.h"
static int cond_set_gen(const char *path, unsigned int gen)
static int cond_set_gen(const char *file, unsigned int gen)
{
char *ptr, path[256];
FILE *fp;
int ret;
fp = fopen(path, "w");
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "w");
if (!fp)
return -1;
@@ -59,9 +64,27 @@ static void cond_bump_reconf(void)
cond_set_gen(COND_RECONF, rgen);
}
int cond_set_path(const char *path, enum cond_state new)
static int cond_checkpath(const char *path)
{
char buf[MAX_ARG_LEN], *dir;
strlcpy(buf, path, sizeof(buf));
dir = dirname(buf);
if (!dir) {
_e("Invalid path '%s' for condition", path);
return 1;
}
if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1;
}
return 0;
}
int cond_set_path(const char *path, enum cond_state new)
{
enum cond_state old;
unsigned int rgen;
@@ -77,16 +100,8 @@ int cond_set_path(const char *path, enum cond_state new)
switch (new) {
case COND_ON:
strlcpy(buf, path, sizeof(buf));
dir = dirname(buf);
if (!dir) {
_e("Invalid path '%s' for condition", path);
return 0;
}
if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 0;
}
if (cond_checkpath(path))
return 0;
cond_set_gen(path, rgen);
break;
@@ -137,17 +152,40 @@ static void cond_update(const char *name)
}
void cond_set(const char *name)
{
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_ON))
return;
cond_update(name);
}
void cond_set_oneshot(const char *name)
{
const char *path;
if (string_compare(name, "nop"))
return;
path = cond_path(name);
_d("s => %s", name, path);
if (cond_checkpath(path))
return;
symlink(COND_RECONF, path);
cond_update(name);
}
void cond_clear(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_OFF))
return;
@@ -172,7 +210,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
if (tflg != FTW_F)
return 0;
nm = (char *)fpath + sizeof(COND_PATH);
nm = strstr((char *)fpath, COND_DIR);
if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath);
return 1;
}
nm += sizeof(COND_DIR);
_d("Reasserting %s => %s", fpath, nm);
cond_set(nm);
@@ -191,7 +235,9 @@ void cond_reassert(const char *type)
void cond_init(void)
{
if (makepath(COND_PATH) && errno != EEXIST) {
char path[MAX_ARG_LEN];
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", COND_PATH);
return;
}
+16 -4
View File
@@ -26,9 +26,16 @@
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "service.h"
char *mkcond(char *buf, size_t len, char *nm)
{
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
return buf;
}
const char *condstr(enum cond_state s)
{
static const char *strs[] = {
@@ -42,20 +49,25 @@ const char *condstr(enum cond_state s)
const char *cond_path(const char *name)
{
char tmp[MAX_ARG_LEN];
static char file[MAX_ARG_LEN];
snprintf(file, sizeof(file), COND_PATH "/%s", name);
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
return file;
return pid_runpath(tmp, file, sizeof(file));
}
unsigned int cond_get_gen(const char *path)
unsigned int cond_get_gen(const char *file)
{
char *ptr, path[256];
unsigned int gen;
FILE *fp;
int ret;
fp = fopen(path, "r");
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "r");
if (!fp)
return 0;
+10 -8
View File
@@ -3,8 +3,8 @@
#include <paths.h>
#define COND_PATH _PATH_VARRUN "finit/cond"
#define COND_SVC_PATH COND_PATH "/svc/"
#define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
#define COND_RECONF COND_PATH "/reconf"
typedef enum cond_state {
@@ -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);
@@ -21,12 +22,13 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names);
int cond_set_path(const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_clear (const char *name);
void cond_reload (void);
void cond_reassert(const char *pat);
void cond_init (void);
int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_set_oneshot (const char *name);
void cond_clear (const char *name);
void cond_reload (void);
void cond_reassert (const char *pat);
void cond_init (void);
#endif /* FINIT_COND_H_ */
+174 -41
View File
@@ -36,11 +36,15 @@
#include "service.h"
#include "tty.h"
#include "helpers.h"
#include "util.h"
#define BOOTSTRAP (runlevel == 0)
#define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5;
struct rlimit global_rlimit[RLIMIT_NLIMITS];
struct conf_change {
@@ -58,24 +62,85 @@ void conf_parse_cmdline(void)
{
int dbg = 0;
FILE *fp;
char line[LINE_SIZE], *cmdline, *tok;
fp = fopen("/proc/cmdline", "r");
if (fp) {
char line[LINE_SIZE];
fgets(line, sizeof(line), fp);
chomp(line);
_d("%s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
dbg = 1;
if (!fp)
goto done;
if (!fgets(line, sizeof(line), fp)) {
fclose(fp);
goto done;
}
cmdline = chomp(line);
_d("%s", cmdline);
while ((tok = strtok(cmdline, " \t"))) {
cmdline = NULL;
/* Catches finit_debug (deprecated), --debug, and debug */
if (strstr(tok, "debug"))
dbg = 1;
if (string_compare(tok, "rescue") || string_compare(tok, "recover"))
rescue = 1;
if (string_compare(tok, "single") || string_compare(tok, "S"))
single = 1;
if (string_compare(tok, "splash"))
splash = 1;
}
fclose(fp);
done:
log_init(dbg);
}
static int kmod_exists(char *mod)
{
int found = 0;
FILE *fp;
char buf[256];
size_t len;
for (len = 0; mod[len]; len++) {
if (mod[len] == ' ' || mod[len] == '\t')
break;
}
fp = fopen("/proc/modules", "r");
if (!fp)
return 0;
while (!found && (fgets(buf, sizeof(buf), fp))) {
if (!strncmp(buf, mod, len))
found = 1;
}
fclose(fp);
return found;
}
static void kmod_load(char *line)
{
char *mod;
char cmd[CMD_SIZE];
if (runlevel != 0)
return;
mod = strip_line(line);
if (kmod_exists(mod))
return;
strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
}
/* Convert optional "[!123456789S]" string into a bitmask */
int conf_parse_runlevels(char *runlevels)
{
@@ -200,11 +265,33 @@ char *rlim2str(int rlim)
return "unknown";
}
char *lim2str(struct rlimit *rlim)
{
char tmp[25];
static char buf[42];
memset(buf, 0, sizeof(buf));
if (RLIM_INFINITY == rlim->rlim_cur)
snprintf(tmp, sizeof(tmp), "unlimited, ");
else
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_cur);
strlcat(buf, tmp, sizeof(buf));
if (RLIM_INFINITY == rlim->rlim_max)
snprintf(tmp, sizeof(tmp), "unlimited, ");
else
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_max);
strlcat(buf, tmp, sizeof(buf));
return buf;
}
/* First form: `rlimit <hard|soft> RESOURCE LIMIT` */
void conf_parse_rlimit(char *line, struct rlimit arr[])
{
char *level, *limit, *val;
int resource = -1;
rlim_t cfg, *set;
rlim_t cfg;
level = strtok(line, " \t");
if (!level)
@@ -214,21 +301,18 @@ void conf_parse_rlimit(char *line, struct rlimit arr[])
if (!limit)
goto error;
val = strtok(NULL, " \t");
if (!val) {
/* Second form: `rlimit RESOURCE LIMIT` */
val = limit;
limit = level;
level = "both";
}
resource = str2rlim(limit);
if (resource < 0 || resource > RLIMIT_NLIMITS)
goto error;
val = strtok(NULL, " \t");
if (!val)
goto error;
if (!strcmp(level, "soft"))
set = &arr[resource].rlim_cur;
else if (!strcmp(level, "hard"))
set = &arr[resource].rlim_max;
else
goto error;
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
cfg = RLIM_INFINITY;
@@ -243,7 +327,15 @@ void conf_parse_rlimit(char *line, struct rlimit arr[])
}
}
*set = cfg;
if (!strcmp(level, "soft"))
arr[resource].rlim_cur = cfg;
else if (!strcmp(level, "hard"))
arr[resource].rlim_max = cfg;
else if (!strcmp(level, "both"))
arr[resource].rlim_max = arr[resource].rlim_cur = cfg;
else
goto error;
return;
error:
logit(LOG_WARNING, "rlimit: parse error");
@@ -254,7 +346,7 @@ static void parse_static(char *line)
char *x;
char cmd[CMD_SIZE];
if (BOOTSTRAP && MATCH_CMD(line, "host ", x)) {
if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) {
if (hostname) free(hostname);
hostname = strdup(strip_line(x));
return;
@@ -295,6 +387,26 @@ static void parse_static(char *line)
return;
}
if (MATCH_CMD(line, "log ", x)) {
char *tok;
static int size = 200000, count = 5;
tok = strtok(x, ":= ");
while (tok) {
if (!strncmp(tok, "size", 4))
size = strtobytes(strtok(NULL, ":= "));
else if (!strncmp(tok, "count", 5))
count = strtobytes(strtok(NULL, ":= "));
tok = strtok(NULL, ":= ");
}
if (size >= 0)
logfile_size_max = size;
if (count >= 0)
logfile_count_max = count;
}
if (MATCH_CMD(line, "shutdown ", x)) {
if (sdown) free(sdown);
sdown = strdup(strip_line(x));
@@ -322,7 +434,6 @@ static void parse_static(char *line)
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
{
char *x;
char cmd[CMD_SIZE];
/* Skip comments, i.e. lines beginning with # */
if (MATCH_CMD(line, "#", x))
@@ -330,16 +441,7 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
/* Kernel module to load at bootstrap */
if (MATCH_CMD(line, "module ", x)) {
char *mod;
if (runlevel != 0)
return;
mod = strip_line(x);
strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
kmod_load(x);
return;
}
@@ -463,7 +565,8 @@ static int parse_conf(char *file)
/* Set global limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &global_rlimit[i]) == -1)
logit(LOG_WARNING, "rlimit: Failed setting %s", rlim2str(i));
logit(LOG_WARNING, "rlimit: Failed setting %s: %s",
rlim2str(i), lim2str(&global_rlimit[i]));
}
return 0;
@@ -481,6 +584,19 @@ int conf_reload(void)
svc_mark_dynamic();
tty_mark();
if (rescue) {
int rc;
char line[80] = "tty [12345] @console noclear nologin";
/* If rescue.conf is missing, fall back to a root shell */
rc = parse_conf(RESCUE_CONF);
if (rc)
tty_register(line, global_rlimit, NULL);
print(rc, "Entering rescue mode");
goto done;
}
/* First, read /etc/finit.conf */
parse_conf(FINIT_CONF);
@@ -488,13 +604,9 @@ int conf_reload(void)
num = scandir(rcsd, &e, NULL, alphasort);
if (num < 0) {
_d("Skipping %s, no files found ...", rcsd);
return 0;
goto done;
}
/* Update global limits in case operator used prlimit(1) on us */
for (i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
for (i = 0; i < num; i++) {
char *name = e[i]->d_name;
char path[LINE_SIZE];
@@ -542,9 +654,14 @@ int conf_reload(void)
free(e[num]);
free(e);
done:
/* Drop record of all .conf changes */
drop_changes();
/* Override configured runlevel, user said 'S' on /proc/cmdline */
if (BOOTSTRAP && single)
cfglevel = 1;
/*
* Set host name, from %DEFHOST, *.conf or /etc/hostname. The
* latter wins, if neither exists we default to "noname"
@@ -610,16 +727,28 @@ 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;
}
int conf_any_change(void)
{
if (TAILQ_EMPTY(&conf_change_list))
return 0;
return 1;
}
int conf_changed(char *file)
{
char *ptr;
if (file && (ptr = strrchr(file, '/')))
if (!file)
return 0;
if ((ptr = strrchr(file, '/')))
file = ++ptr;
if (conf_find(file))
@@ -717,6 +846,10 @@ int conf_monitor(uev_ctx_t *ctx)
{
int rc = 0;
/* Skip second run, when called from finit.c in rescue mode */
if (ctx && rescue)
return 0;
/*
* If only one watcher fails, that's OK. A user may have only
* one of /etc/finit.conf or /etc/finit.d in use, and may also
+4
View File
@@ -26,6 +26,9 @@
#include "svc.h"
extern int logfile_size_max;
extern int logfile_count_max;
extern struct rlimit global_rlimit[];
int str2rlim(char *str);
@@ -33,6 +36,7 @@ char *rlim2str(int rlim);
int conf_init (void);
void conf_reload (void);
int conf_any_change (void);
int conf_changed (char *file);
int conf_monitor (uev_ctx_t *ctx);
+15 -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);
@@ -423,6 +423,20 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, st
return pid;
}
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
pid = fork();
if (!pid) {
prepare_tty(tty, B0, "finit-sh", rlimit);
if (activate_console(noclear, nowait))
_exit(sh(tty));
}
return pid;
}
int run_parts(char *dir, char *cmd)
{
struct dirent **e;
+179 -167
View File
@@ -47,12 +47,14 @@
#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;
int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */
int splash = 0; /* splash + progress enabled on kernel cmdline */
char *sdown = NULL;
char *network = NULL;
char *hostname = NULL;
@@ -60,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
@@ -78,17 +84,16 @@ 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);
}
/* Requires /proc to be mounted */
static int fismnt(char *dir)
static int ismnt(char *file, char *dir)
{
FILE *fp;
int found = 0;
struct mntent *mnt;
fp = setmntent("/proc/mounts", "r");
fp = setmntent(file, "r");
if (!fp)
return 0; /* Dunno, maybe not */
@@ -103,6 +108,12 @@ static int fismnt(char *dir)
return found;
}
/* Requires /proc to be mounted */
static int fismnt(char *dir)
{
return ismnt("/proc/mounts", dir);
}
/*
* Check all filesystems in /etc/fstab with a fs_passno > 0
*/
@@ -150,54 +161,6 @@ static int fsck(int pass)
return 0;
}
static void networking(void)
{
FILE *fp;
/* Run user network start script if enabled */
if (network) {
run_interactive(network, "Starting networking: %s", network);
goto done;
}
/* Debian/Ubuntu/Busybox/RH/Suse */
if (!whichp("ifup"))
goto done;
fp = fopen("/etc/network/interfaces", "r");
if (fp) {
int i = 0;
char buf[160];
/* Bring up all 'auto' interfaces */
while (fgets(buf, sizeof(buf), fp)) {
char cmd[80];
char *line, *ifname = NULL;
chomp(buf);
line = strip_line(buf);
if (!strncmp(line, "auto", 4))
ifname = &line[5];
if (!strncmp(line, "allow-hotplug", 13))
ifname = &line[14];
if (!ifname)
continue;
snprintf(cmd, 80, "ifup %s", ifname);
run_interactive(cmd, "Bringing up interface %s", ifname);
i++;
}
fclose(fp);
}
done:
/* Fall back to bring up at least loopback */
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
}
/*
* If everything goes south we can use this to give the operator an
* emergency shell to debug the problem -- Finit should not crash!
@@ -227,7 +190,7 @@ static void emergency_shell(void)
}
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
fprintf(stderr, "=> To debug, add '--debug' to the kernel command line.\n\n");
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
/*
* Become session leader and set controlling TTY
@@ -254,16 +217,20 @@ static void emergency_shell(void)
*/
static void finalize(void)
{
/*
* Network stuff
*/
_d("Setting up networking ...");
networking();
umask(022);
svc_t *svc;
/* Hooks that rely on loopback, or basic networking being up. */
_d("Calling all network up hooks ...");
plugin_run_hooks(HOOK_NETWORK_UP);
/*
* 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)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
@@ -280,20 +247,9 @@ static void finalize(void)
plugin_run_hooks(HOOK_SVC_UP);
service_step_all(SVC_TYPE_ANY);
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts)) {
_d("Running startup scripts in %s ...", runparts);
run_parts(runparts, NULL);
service_reload_dynamic();
}
/* Convenient SysV compat for when you just don't care ... */
if (!access(FINIT_RC_LOCAL, X_OK)) {
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
service_reload_dynamic();
}
/* Hooks that should run at the very end */
_d("Calling all system up hooks ...");
@@ -309,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
@@ -324,29 +328,17 @@ int main(int argc, char* argv[])
if (getpid() != 1)
return client(argc, argv);
/* Set up canvas */
screen_init();
/*
* In case of emergency.
*/
emergency_shell();
/*
* Initial setup of signals, ignore all until we're up.
*/
sig_init();
/*
* Initalize event context.
*/
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
@@ -359,15 +351,30 @@ int main(int argc, char* argv[])
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
/*
* Parse kernel parameters, including log_init()
* Parse kernel command line (debug, rescue, splash, etc.)
* Also calls log_init() to set correct log level
*/
conf_parse_cmdline();
/* Set up canvas */
if (!rescue && !log_is_debug())
screen_init();
/*
* In case of emergency.
*/
emergency_shell();
/*
* Initial setup of signals, ignore all until we're up.
*/
sig_init();
/*
* Load plugins early, finit.conf may contain references to
* features implemented by plugins.
*/
plugin_init(&loop, PLUGIN_PATH);
plugin_init(&loop);
/*
* Hello world.
@@ -377,11 +384,17 @@ int main(int argc, char* argv[])
/*
* Check file filesystems in /etc/fstab
*/
for (int pass = 1; pass < 10; pass++) {
for (int pass = 1; pass < 10 && !rescue; pass++) {
if (fsck(pass))
break;
}
/*
* Initialize .conf system and load static /etc/finit.conf
* Also initializes global_rlimit[] for udevd, below.
*/
conf_init();
/*
* Some non-embedded systems without an initramfs may not have /dev mounted yet
* If they do, check if system has udevadm and perform cleanup from initramfs
@@ -395,8 +408,15 @@ int main(int argc, char* argv[])
makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
/* Some systems rely on /dev/shm being there, for `mount -a` below */
/*
* Some systems rely on us to both create /dev/shm and, to mount
* a tmpfs there. Any system with dbus needs shared memory, so
* mount it, unless its already mounted, but not if listed in
* the /etc/fstab file already.
*/
makedir("/dev/shm", 0755);
if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm"))
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
/*
* New tmpfs based /run for volatile runtime data
@@ -406,118 +426,110 @@ 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");
}
}
/*
* Initialize .conf system and load static /etc/finit.conf
* Start bundled watchdogd as soon as possible, if enabled
*/
conf_init();
/*
* Start built-in watchdog 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
*/
if (!rescue) {
#ifdef REMOUNT_ROOTFS
run("mount -n -o remount,rw /");
run("mount -n -o remount,rw /");
#endif
#ifdef SYSROOT
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif
}
/* Debian has this little script to copy generated rules while the system was read-only */
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
if (!rescue) {
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
/* Bootstrap conditions, needed for hooks */
cond_init();
umask(0);
if (run_interactive("mount -na", "Mounting filesystems"))
plugin_run_hooks(HOOK_MOUNT_ERROR);
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
_d("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST);
umask(0);
if (run_interactive("mount -na", "Mounting filesystems"))
plugin_run_hooks(HOOK_MOUNT_ERROR);
run("swapon -ea");
umask(0022);
run("swapon -ea");
umask(0022);
}
/* Base FS up, enable standard SysV init signals */
sig_setup(&loop);
if (!rescue) {
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
}
/*
* Set up inotify watcher for /etc/finit.d and read all .conf
* files to figure out how to bootstrap the system.
*/
conf_monitor(&loop);
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
/*
* Initalize state machine and start all bootstrap tasks
* NOTE: no network available!
*/
sm_init(&sm);
sm_step(&sm);
/* Start new initctl API responder */
_d("Starting initctl API responder ...");
api_init(&loop);
umask(022);
_d("Starting the big state machine ...");
schedule_work(&crank);
/*
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
* [S], or timeout, before calling finalize()
*/
_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);
+3 -1
View File
@@ -92,10 +92,12 @@ struct init_request {
char data[368];
};
extern int wdogpid;
extern int runlevel;
extern int cfglevel;
extern int prevlevel;
extern int rescue;
extern int single;
extern int splash;
extern char *rcsd;
extern char *sdown;
extern char *network;
+55 -1
View File
@@ -237,12 +237,66 @@ 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);
}
int sh(char *tty)
{
int fd;
char *arg0;
char *args[2] = {
NULL,
NULL
};
size_t len;
struct termios term;
/* Detach from initial controlling TTY */
vhangup();
fd = open(tty, O_RDWR);
if (fd < 0)
err(1, "Failed opening %s", tty);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
warn("Failed TIOCSCTTY");
/* The getty process is usually responsible for the UTMP login record */
utmp_set_login(tty, NULL);
/* Set up TTY, re-enabling ISIG et al. */
stty(fd, B0);
close(fd);
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
len = strlen(_PATH_BSHELL) + 2;
arg0 = malloc(len);
if (!arg0)
err(1, "Failed allocating memory");
snprintf(arg0, len, "-%s", _PATH_BSHELL);
args[0] = arg0;
/* Reenable Ctrl-D and Ctrl-C, and ... */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_lflag |= ISIG;
term.c_cc[VEOF] = CTL('D');
term.c_cc[VINTR] = CTL('C');
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
/* ... unblock signals in general */
sig_unblock();
return execv(_PATH_BSHELL, args);
}
/**
* Local Variables:
* indent-tabs-mode: t
+79
View File
@@ -184,6 +184,9 @@ void set_hostname(char **hostname)
{
FILE *fp;
if (rescue)
goto done;
fp = fopen("/etc/hostname", "r");
if (fp) {
struct stat st;
@@ -204,10 +207,86 @@ void set_hostname(char **hostname)
fclose(fp);
}
done:
if (*hostname)
sethostname(*hostname, strlen(*hostname));
}
static void ifup(char *ifname, int updown)
{
char cmd[80];
if (updown) {
snprintf(cmd, sizeof(cmd), "ifup %s", ifname);
run_interactive(cmd, "Bringing up interface %s", ifname);
} else {
snprintf(cmd, sizeof(cmd), "ifdown -f %s", ifname);
run_interactive(cmd, "Taking down interface %s", ifname);
}
}
/*
* Bring up networking, but only if not single-user or rescue mode
*/
void networking(int updown)
{
FILE *fp;
/* No need to report errors if network is already down */
if (!prevlevel && !updown)
return;
if (updown)
_d("Setting up networking ...");
else
_d("Taking down networking ...");
/* Run user network start script if enabled */
if (updown && network) {
run_interactive(network, "Starting networking: %s", network);
goto done;
}
/* Debian/Ubuntu/Busybox/RH/Suse */
if (!whichp("ifup"))
goto done;
fp = fopen("/etc/network/interfaces", "r");
if (fp) {
char buf[160];
/* Bring up, or down, all 'auto' interfaces */
while (fgets(buf, sizeof(buf), fp)) {
char *line, *ifname = NULL;
chomp(buf);
line = strip_line(buf);
if (!strncmp(line, "auto", 4))
ifname = &line[5];
if (!strncmp(line, "allow-hotplug", 13))
ifname = &line[14];
if (!ifname)
continue;
ifup(ifname, updown);
}
fclose(fp);
}
done:
/* Fall back to bring up at least loopback */
ifconfig("lo", "127.0.0.1", "255.0.0.0", updown);
/* Hooks that rely on loopback, or basic networking being up. */
if (updown) {
_d("Calling all network up hooks ...");
plugin_run_hooks(HOOK_NETWORK_UP);
}
}
#ifndef HAVE_GETFSENT
static lfile_t *fstab = NULL;
+3 -3
View File
@@ -52,13 +52,11 @@ struct fstab *getfsent (void);
char *strip_line (char *line);
int getty (char *tty, speed_t speed, char *term, char *user);
int sh (char *tty);
int stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud);
int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
void printv (const char *fmt, va_list ap);
void print (int action, const char *fmt, ...);
void print_desc (char *action, char *desc);
@@ -67,6 +65,7 @@ int print_result (int fail);
int getuser (char *username, char **home);
int getgroup (char *group);
void set_hostname (char **hostname);
void networking (int updown);
int complete (char *cmd, int pid);
int run (char *cmd);
@@ -74,6 +73,7 @@ int run_interactive (char *cmd, char *fmt, ...);
int exec_runtask (char *cmd, char *args[]);
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
int run_parts (char *dir, char *cmd);
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
+61 -24
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,26 +76,28 @@ 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];
do {
inetd_dgram_peek(sd, pkt_interface);
if(string_compare(pkt_interface, ifname)) {
recv(sd, buf, sizeof(buf), 0);
while (1) {
inetd_dgram_peek(sd, pkt_interface, sizeof(pkt_interface));
if (string_compare(pkt_interface, ifname)) {
if (recv(sd, buf, sizeof(buf), 0) < 0)
break;
continue;
}
} while (0);
break;
}
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
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;
@@ -111,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;
}
}
@@ -121,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);
@@ -136,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)) {
@@ -151,6 +157,9 @@ static int get_stdin(svc_t *svc)
return -1;
}
/* Return ingress interface */
strlcpy(iifname, ifname, len);
return stdin;
}
@@ -158,6 +167,8 @@ static int get_stdin(svc_t *svc)
static void socket_cb(uev_t *w, void *arg, int events)
{
svc_t *svc = (svc_t *)arg, *task;
const char *conn = " connection";
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
int stdin;
_d("%s: Got socket event ...", svc->cmd);
@@ -166,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;
@@ -207,11 +218,14 @@ static void socket_cb(uev_t *w, void *arg, int events)
task->inetd.cmd = svc->inetd.cmd;
task->inetd.type = svc->inetd.type;
memcpy(task->rlimit, svc->rlimit, sizeof(task->rlimit));
memcpy(task->cond, svc->cond, sizeof(task->cond));
memcpy(task->username, svc->username, sizeof(task->username));
memcpy(task->group, svc->group, sizeof(task->group));
memcpy(task->args, svc->args, sizeof(task->args));
snprintf(task->desc, sizeof(task->desc), "%52.52s connection", svc->desc);
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
strlcat(task->desc, conn, sizeof(task->desc));
strlcpy(task->iifname, iifname, sizeof(task->iifname));
task->stdin_fd = stdin;
service_step(task);
@@ -331,20 +345,43 @@ 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) {
_e("Invalid inetd, cannot stop it...");
return;
}
if (inetd->watcher.fd != -1) {
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
uev_io_stop(&inetd->watcher);
/* For dgram inetd services we block the parent SVC
* and halt the watcher, so don't close the socket! */
/*
* For dgram inetd services we block the parent SVC
* and halt the watcher, so don't close the socket!
*/
if (!svc_is_busy(inetd->svc)) {
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
if (inetd->type == SOCK_STREAM)
shutdown(inetd->watcher.fd, SHUT_RDWR);
close(inetd->watcher.fd);
inetd->watcher.fd = -1;
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);
+25 -8
View File
@@ -43,6 +43,11 @@
#define _PATH_COND _PATH_VARRUN "finit/cond/"
struct command {
char *cmd;
int (*cb)(char *arg);
};
int verbose = 0;
int runlevel = 0;
@@ -272,7 +277,7 @@ static int do_cond(char *cmd)
{
int c;
char *arg;
command_t command[] = {
struct command command[] = {
{ "show", do_cond_show },
{ "set", do_cond_set },
{ "clear", do_cond_clear },
@@ -411,6 +416,9 @@ static int show_status(char *arg)
runlevel = runlevel_get(NULL);
if (arg && arg[0]) {
long now = jiffies();
char buf[42] = "N/A";
svc = client_svc_find(arg);
if (!svc)
return 1;
@@ -418,6 +426,7 @@ static int show_status(char *arg)
printf("Service : %s\n", svc->cmd);
printf("Description : %s\n", svc->desc);
printf("PID : %d\n", svc->pid);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
printf("Status : %s\n", svc_status(svc));
printf("\n");
@@ -426,7 +435,7 @@ static int show_status(char *arg)
}
if (!verbose)
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char jobid[10], args[512] = "", *lvls;
@@ -436,7 +445,7 @@ static int show_status(char *arg)
// else
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
printf("%-5s %7s ", jobid, svc_status(svc));
printf("%-9s %7s ", jobid, svc_status(svc));
if (svc_is_inetd(svc))
printf("inetd ");
else
@@ -457,7 +466,7 @@ static int show_status(char *arg)
else
name++;
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc);
printf("%-16.16s %-*.*s\n", name, adj, adj, svc->desc);
continue;
}
@@ -513,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"
@@ -537,6 +547,7 @@ static int usage(int rc)
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
" status | show Show status of services, default command\n"
"\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
@@ -557,9 +568,9 @@ static int do_help(char *arg)
int main(int argc, char *argv[])
{
int c;
int interactive = 1, c;
char *cmd, arg[120];
command_t command[] = {
struct command command[] = {
{ "debug", toggle_debug },
{ "help", do_help },
{ "version", show_version },
@@ -588,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'},
@@ -595,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);
@@ -607,7 +623,8 @@ int main(int argc, char *argv[])
}
}
screen_init();
if (interactive)
screen_init();
if (optind >= argc)
return show_status(NULL);
+29 -1
View File
@@ -29,12 +29,22 @@
#include "finit.h"
#include "log.h"
#include "util.h"
static int up = 0;
static int debug = 0;
static int silent = SILENT_MODE; /* Completely silent, including boot */
static int loglevel = LOG_NOTICE;
static void muffler(void)
{
silent = SILENT_MODE;
/* User override from kernel cmdline */
if (splash)
silent = 0;
}
void log_init(int dbg)
{
if (dbg)
@@ -44,16 +54,27 @@ void log_init(int dbg)
loglevel = LOG_DEBUG;
else
loglevel = LOG_NOTICE;
muffler();
}
/* If we enabled terse mode at boot, restore to previous setting at shutdown */
void log_exit(void)
{
silent = SILENT_MODE;
muffler();
if (!silent) {
sched_yield();
fputs("\n", stderr);
}
/*
* Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime
*/
if (debug)
screen_exit();
else
screen_init();
}
void log_open(void)
@@ -91,12 +112,19 @@ void log_debug(void)
if (debug) {
silent = 0;
loglevel = LOG_DEBUG;
screen_exit();
} else {
silent = SILENT_MODE;
loglevel = LOG_NOTICE;
screen_init();
}
log_open();
/* Also toggle silent mode (progress) */
log_silent();
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
}
+1 -1
View File
@@ -33,7 +33,7 @@
* errors and debug messages. For all other user messages, see logit()
*
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
* use `initctl debug` or add `--debug` to the kernel cmdline.
* use `initctl debug` or add `debug` to the kernel cmdline.
*/
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt "\n", __func__, ##args)
+286
View File
@@ -0,0 +1,286 @@
/*
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <config.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SYSLOG_NAMES
#include <syslog.h>
#include <unistd.h>
#include <sys/stat.h>
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
{
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
}
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
static int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
if (cnt == 2 && !access(nfile, F_OK)) {
size_t len = 5 + strlen(nfile) + 1;
char cmd[len];
snprintf(cmd, len, "gzip %s", nfile);
system(cmd);
remove(nfile);
}
}
if (rename(file, nfile))
(void)truncate(file, 0);
else
create(file, st.st_mode, st.st_uid, st.st_gid);
} else {
if (truncate(file, 0))
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
}
}
return 0;
}
static int checksz(FILE *fp, off_t sz)
{
struct stat st;
if (sz <= 0)
return 0;
if (!fstat(fileno(fp), &st) && st.st_size > sz) {
fclose(fp);
return 1;
}
return 0;
}
static int flogit(char *logfile, int num, off_t sz, char *buf, size_t len)
{
FILE *fp;
reopen:
fp = fopen(logfile, "a");
if (!fp) {
syslog(LOG_ERR | LOG_PERROR, "Failed opening %s: %s", logfile, strerror(errno));
return 1;
}
if (buf[0]) {
fprintf(fp, "%s\n", buf);
fsync(fileno(fp));
if (checksz(fp, sz))
return logrotate(logfile, num, sz);
} else {
while ((fgets(buf, len, stdin))) {
fputs(buf, fp);
fsync(fileno(fp));
if (checksz(fp, sz)) {
logrotate(logfile, num, sz);
buf[0] = 0;
goto reopen;
}
}
}
return fclose(fp);
}
static int logit(int level, char *buf, size_t len)
{
if (buf[0]) {
syslog(level, "%s", buf);
return 0;
}
while ((fgets(buf, len, stdin)))
syslog(level, "%s", buf);
return 0;
}
static int parse_prio(char *arg, int *f, int *l)
{
char *ptr;
ptr = strchr(arg, '.');
if (ptr) {
*ptr++ = 0;
for (int i = 0; facilitynames[i].c_name; i++) {
if (!strcmp(facilitynames[i].c_name, arg)) {
*f = facilitynames[i].c_val;
break;
}
}
arg = ptr;
}
for (int i = 0; prioritynames[i].c_name; i++) {
if (!strcmp(prioritynames[i].c_name, arg)) {
*l = prioritynames[i].c_val;
break;
}
}
return 0;
}
static int usage(int code)
{
fprintf(stderr, "Usage: logit [OPTIONS] [MESSAGE]\n"
"\n"
"Write MESSAGE (or stdin) to syslog, or file (with logrotate)\n"
"\n"
" -h This help text\n"
" -p PRIO Priority (numeric or facility.level pair)\n"
" -t TAG Log using the specified tag (defaults to user name)\n"
" -s Log to stderr as well as the system log\n"
"\n"
" -f FILE File to write log messages to, instead of syslog\n"
" -n SIZE Number of bytes before rotating, default: 200 kB\n"
" -r NUM Number of rotated files to keep, default: 5\n"
" -v Show program version\n"
"\n"
"This version of logit is distributed as part of Finit.\n"
"Bug report address: %s\n", PACKAGE_BUGREPORT);
return code;
}
int main(int argc, char *argv[])
{
int c, rc, num = 5;
int facility = LOG_USER;
int level = LOG_INFO;
int log_opts = LOG_NOWAIT;
off_t size = 200 * 1024;
char *ident = NULL, *logfile = NULL;
char buf[512] = "";
while ((c = getopt(argc, argv, "f:hn:p:r:st:v")) != EOF) {
switch (c) {
case 'f':
logfile = optarg;
break;
case 'h':
return usage(0);
case 'n':
size = atoi(optarg);
break;
case 'p':
if (parse_prio(optarg, &facility, &level))
return usage(1);
break;
case 'r':
num = atoi(optarg);
break;
case 's':
log_opts |= LOG_PERROR;
break;
case 't':
ident = optarg;
break;
case 'v': /* version */
fprintf(stderr, "%s\n", version_info);
return 0;
default:
return usage(1);
}
}
if (!ident)
ident = getenv("LOGNAME") ?: getenv("USER");
if (optind < argc) {
size_t pos = 0, len = sizeof(buf);
while (optind < argc) {
size_t bytes;
bytes = snprintf(&buf[pos], len, "%s ", argv[optind++]);
pos += bytes;
len -= bytes;
}
}
openlog(ident, log_opts, facility);
if (logfile)
rc = flogit(logfile, num, size, buf, sizeof(buf));
else
rc = logit(level, buf, sizeof(buf));
closelog();
return rc;
}
+116 -2
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions and C-library extensions for finit and its plugins
/* PID and PID file helpers
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2008-2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,11 +23,14 @@
*/
#include <errno.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <lite/lite.h>
#include "pid.h"
#include "svc.h"
#include "helpers.h"
@@ -90,6 +93,117 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
return pname;
}
char *pid_file(svc_t *svc)
{
char fn[MAX_ARG_LEN];
static char path[MAX_ARG_LEN];
if (svc->pidfile[0]) {
if (svc->pidfile[0] == '!')
return &svc->pidfile[1];
return svc->pidfile;
}
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
return pid_runpath(fn, path, sizeof(path));
}
int pid_file_create(svc_t *svc)
{
FILE *fp;
if (!svc->pidfile[0] || svc->pidfile[0] == '!')
return 1;
fp = fopen(svc->pidfile, "w");
if (!fp)
return 1;
fprintf(fp, "%d\n", svc->pid);
return fclose(fp);
}
static int pid_realpath(svc_t *svc, char *file)
{
int not = 0;
if (!file)
file = pid_file(svc);
if (file[0] == '!') {
not = 1;
file++;
}
pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not);
if (not)
svc->pidfile[0] = '!';
return 0;
}
/*
* This function parses a PID file @arg for @svc.
*
* The logic is explained below. Please note that using the first form
* of the syntax not only creates and removes the PID file, but it also
* calls touch to update the mtime on service_restart(), only applicable
* to processes that accept SIGHUP. This to ensure dependant services
* are sent SIGCONT properly on reload, otherwise their condition would
* never be asserted again after `initctl reload`.
*
* pid --> /run/$(basename).pid
* pid:foo --> /run/foo.pid
* pid:foo.pid --> /run/foo.pid
* pid:foo.tla --> /run/foo.tla.pid
* pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!)
*
* An exclamation mark, or logial NOT, in the first character position
* indicates that Finit should *not* manage the PID file, and that the
* process uses a "non-standard" location. Eg. if service is 'bar'
* Finit would assume the PID file is /run/bar.pid and the below simply
* 'redirects' Finit to use the correct PID file.
*
* pid:!foo --> !/run/foo.pid
* pid:!/run/foo.pid --> !/run/foo.pid
*
* Note, nothing is created or removed by Finit in this latter form.
*/
int pid_file_parse(svc_t *svc, char *arg)
{
/* Sanity check ... */
if (!arg || !arg[0])
return 1;
/* 'pid:' implies argument following*/
if (!strncmp(arg, "pid:", 4)) {
int len, not = 0;
char path[MAX_ARG_LEN];
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
return pid_realpath(svc, arg);
if (arg[0] == '!') {
arg++;
not++;
}
len = snprintf(path, sizeof(path), "%s%s%s", not ? "!" : "", _PATH_VARRUN, arg);
if (len > 4 && strcmp(&path[len - 4], ".pid"))
strlcat(path, ".pid", sizeof(path));
return pid_realpath(svc, path);
}
/* 'pid' arg, no argument following */
if (!strcmp(arg, "pid"))
return pid_realpath(svc, NULL);
return 1;
}
/**
* Local Variables:
* indent-tabs-mode: t
+75
View File
@@ -0,0 +1,75 @@
/* PID and PID file helpers
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_PID_H_
#define FINIT_PID_H_
#include "svc.h"
int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc);
int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg);
/**
* pid_runpath - Adjust /var/run --> /run path depending on system
* @file: Path to file in /run/path or /var/run/path
* @path: Pointer to buffer to write correct path
* @len: Length, in bytes, of @path buffer
*
* Returns:
* Always returns a valid pointer, which can be either @path with a /run
* or /var/run prefix to @file, or it may be @file if the prefix is OK.
*/
static inline char *pid_runpath(const char *file, char *path, size_t len)
{
static int unknown = 1;
static char *prefix = "/var/run";
if (unknown) {
if (fisdir("/run"))
prefix = "/run";
unknown = 0;
}
if (!strncmp(file, "/var/run/", 9))
file += 9;
else if (!strncmp(file, "/run/", 5))
file += 5;
snprintf(path, len, "%s/%s", prefix, file);
return path;
}
#endif /* FINIT_PID_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+7 -21
View File
@@ -108,6 +108,9 @@ int plugin_register(plugin_t *plugin)
/* Not called, at the moment plugins cannot be unloaded. */
int plugin_unregister(plugin_t *plugin)
{
if (is_io_plugin(plugin))
uev_io_stop(&plugin->watcher);
#ifndef ENABLE_STATIC
TAILQ_REMOVE(&plugins, plugin, link);
@@ -186,21 +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) {
_d("End plugin recursion in our lifetime!");
return;
}
PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) {
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
@@ -208,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(hook_cond[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 */
@@ -384,11 +370,11 @@ static int load_plugins(char *path)
}
#endif /* ENABLE_STATIC */
int plugin_init(uev_ctx_t *ctx, char *path)
int plugin_init(uev_ctx_t *ctx)
{
int fail = 1;
if (!load_plugins(path))
if (!load_plugins(PLUGIN_PATH))
fail = init_plugins(ctx);
return fail;
+13 -8
View File
@@ -29,10 +29,6 @@
#include "svc.h"
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
#define PLUGIN_PATH "/dev/null"
#endif
#define PLUGIN_DEP_MAX 10
/*
@@ -54,22 +50,31 @@
* Predefined hook points and corresponding conditions in Finit,
* for use by plugins and scripts. Recommended to use the task
* or run stanzas: task <hook/mount/error> /bin/rescue.sh
*
* Some of the below hooks cannot (currently) be realized as conditions.
* The idea was to rename them action scripts, with optional argument,
* according to the following scheme:
*
* - HOOK_SVC_RECONF :: action/svc/reconf
* - HOOK_RUNLEVEL_CHANGE :: action/sys/runlevel
*
* However, the implementation did not turn out to be stable enough for
* general release, so it was pulled.
*/
#define HOOK_TYPES { \
/* Bootstrap hooks, runlevel [S] */ \
CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
\
/* Runtime hooks, runlevel [S1-9] */ \
CHOOSE(HOOK_SVC_RECONF, "hook/svc/reconf"), \
CHOOSE(HOOK_SVC_LOST, "hook/svc/lost"), \
CHOOSE(HOOK_SVC_START, "hook/svc/start"), \
CHOOSE(HOOK_RUNLEVEL_CHANGE, "hook/sys/runlevel"), \
CHOOSE(HOOK_SVC_RECONF, "nop"), \
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
\
/* Shutdown hooks, runlevel [06] */ \
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
+1 -6
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);
@@ -45,7 +40,7 @@ int plugin_exists (hook_point_t no);
void plugin_run_hook (hook_point_t no, void *arg);
void plugin_run_hooks (hook_point_t no);
int plugin_init (uev_ctx_t *ctx, char *path);
int plugin_init (uev_ctx_t *ctx);
void plugin_exit (void);
#endif /* FINIT_PRIVATE_H_ */
+1 -1
View File
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
if (c)
err(1, "Failed signalling init to %s", msg);
do_sleep(2);
do_sleep(5);
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
runlevel 1
hostname rescue
tty [12345] @console noclear nologin
+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;
+329 -145
View File
@@ -25,6 +25,7 @@
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */
#include <sched.h> /* sched_yield() */
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
@@ -36,15 +37,22 @@
#include "finit.h"
#include "helpers.h"
#include "inetd.h"
#include "pid.h"
#include "private.h"
#include "sig.h"
#include "service.h"
#include "sm.h"
#include "tty.h"
#include "util.h"
#include "utmp-api.h"
#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);
/**
@@ -60,7 +68,8 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
svc_t *svc = arg;
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */
svc->timer_cb(svc);
if (svc->timer_cb)
svc->timer_cb(svc);
}
/**
@@ -106,6 +115,18 @@ static int service_timeout_cancel(svc_t *svc)
return err;
}
static void redirect_null(void)
{
FILE *fp;
fp = fopen("/dev/null", "w");
if (fp) {
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
fclose(fp);
}
}
static int is_norespawn(void)
{
return sig_stopped() ||
@@ -122,7 +143,7 @@ static int is_norespawn(void)
*/
static int service_start(svc_t *svc)
{
int i, result = 0;
int i, result = 0, do_progress = 1;
pid_t pid;
sigset_t nmask, omask;
@@ -145,10 +166,15 @@ static int service_start(svc_t *svc)
return inetd_start(&svc->inetd);
#endif
if (svc_is_daemon(svc))
print_desc("Starting ", svc->desc);
else
print_desc("", svc->desc);
if (!svc->desc[0])
do_progress = 0;
if (do_progress) {
if (svc_is_daemon(svc))
print_desc("Starting ", svc->desc);
else
print_desc("", svc->desc);
}
/* Declare we're waiting for svc to create its pidfile */
svc_starting(svc);
@@ -210,30 +236,38 @@ static int service_start(svc_t *svc)
} else
#endif
if (svc->log) {
if (svc->log.enabled) {
int fd;
if (svc->log.null) {
redirect_null();
goto logit_done;
}
/*
* Open PTY to connect to logger. A pty isn't buffered
* like a pipe, and it eats newlines so they aren't logged
*/
fd = posix_openpt(O_RDWR);
if (fd == -1) {
svc->log = 0;
goto logger_err;
svc->log.enabled = 0;
goto logit_done;
}
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
close(fd);
svc->log = 0;
goto logger_err;
svc->log.enabled = 0;
goto logit_done;
}
/* SIGCHLD is still blocked for grantpt() and fork() */
sigprocmask(SIG_BLOCK, &nmask, NULL);
pid = fork();
if (pid == 0) {
int fds = open(ptsname(fd), O_RDONLY);
int fds;
char *tag = basename(svc->cmd);
char *prio = "daemon.info";
fds = open(ptsname(fd), O_RDONLY);
close(fd);
if (fds == -1)
_exit(0);
@@ -242,7 +276,22 @@ static int service_start(svc_t *svc)
/* Reset signals */
sig_unblock();
execlp("logger", "logger", "-t", svc->cmd, "-p", "daemon.info", NULL);
if (svc->log.file[0] == '/') {
char sz[20], num[3];
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
snprintf(num, sizeof(num), "%d", logfile_count_max);
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
_exit(0);
}
if (svc->log.ident[0])
tag = svc->log.ident;
if (svc->log.prio[0])
prio = svc->log.prio;
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
_exit(0);
}
@@ -258,18 +307,13 @@ static int service_start(svc_t *svc)
dup2(fd, STDERR_FILENO);
close(fd);
}
} else {
FILE *fp;
fp = fopen("/dev/null", "w");
if (fp) {
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
fclose(fp);
}
}
#ifdef REDIRECT_OUTPUT
else
redirect_null();
#endif
logger_err:
logit_done:
sig_unblock();
if (svc->inetd.cmd)
@@ -288,9 +332,9 @@ static int service_start(svc_t *svc)
}
} else
#endif
if (svc->log)
if (svc->log.enabled && !svc->log.null)
waitpid(pid, NULL, 0);
exit(status);
_exit(status);
} else if (log_is_debug()) {
char buf[CMD_SIZE] = "";
@@ -305,30 +349,34 @@ 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->pid = 0;
svc_set_state(svc, SVC_STOPPING_STATE);
}
default:
break;
}
sigprocmask(SIG_SETMASK, &omask, NULL);
print_result(result);
/*
* Only run hook on successful start, and *after* having printed
* the result, otherwise any hook tasks may overwrite it and the
* result would be like double "[ OK ]" but only one service.
*/
if (!result)
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
sigprocmask(SIG_SETMASK, &omask, NULL);
if (do_progress)
print_result(result);
return result;
}
@@ -349,10 +397,10 @@ static void service_kill(svc_t *svc)
return;
}
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
if (runlevel != 1)
print_desc("Killing ", svc->desc);
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
kill(svc->pid, SIGKILL);
/* Let SIGKILLs stand out, show result as [WARN] */
@@ -365,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)
{
@@ -374,33 +422,36 @@ static int service_stop(svc_t *svc)
if (!svc)
return 1;
if (svc->state <= SVC_STOPPING_STATE)
return 0;
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
int do_print = runlevel != 1 && !svc_is_busy(svc);
int do_progress = runlevel != 1 && !svc_is_busy(svc);
if (do_print)
if (do_progress)
print_desc("Stopping ", svc->desc);
inetd_stop(&svc->inetd);
if (do_print)
if (do_progress)
print_result(0);
svc_set_state(svc, SVC_STOPPING_STATE);
return 0;
}
} else
#endif
service_timeout_cancel(svc);
if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
if (svc->pid <= 1)
return 1;
}
if (SVC_TYPE_SERVICE != svc->type)
return 0;
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
svc_set_state(svc, SVC_STOPPING_STATE);
if (runlevel != 1)
print_desc("Stopping ", svc->desc);
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
res = kill(svc->pid, SIGTERM);
if (runlevel != 1)
@@ -421,6 +472,9 @@ 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 */
if (is_norespawn())
return 1;
@@ -430,17 +484,33 @@ static int service_restart(svc_t *svc)
if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
svc->pid = 0;
svc->start_time = svc->pid = 0;
return 1;
}
print_desc("Restarting ", svc->desc);
/* 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 */
svc_starting(svc);
_d("Sending SIGHUP to PID %d", svc->pid);
return print_result(kill(svc->pid, SIGHUP));
/* Service does not maintain a PID file on its own */
if (svc_has_pidfile(svc)) {
sched_yield();
touch(pid_file(svc));
}
if (do_progress)
print_result(rc);
return rc;
}
/**
@@ -465,10 +535,67 @@ void service_reload_dynamic(void)
*/
void service_runlevel(int newlevel)
{
if (!rescue && runlevel <= 1 && newlevel > 1)
networking(1);
sm_set_runlevel(&sm, newlevel);
sm_step(&sm);
if (!rescue && runlevel <= 1)
networking(0);
}
/*
* log:/path/to/logfile,priority:facility.level,tag:ident
*/
static void parse_log(svc_t *svc, char *arg)
{
char *tok;
tok = strtok(arg, ":, ");
while (tok) {
if (!strcmp(tok, "log"))
svc->log.enabled = 1;
else if (!strcmp(tok, "null") || !strcmp(tok, "/dev/null"))
svc->log.null = 1;
else if (tok[0] == '/')
strlcpy(svc->log.file, tok, sizeof(svc->log.file));
else if (!strcmp(tok, "priority") || !strcmp(tok, "prio"))
strlcpy(svc->log.prio, strtok(NULL, ","), sizeof(svc->log.prio));
else if (!strcmp(tok, "tag") || !strcmp(tok, "identity") || !strcmp(tok, "ident"))
strlcpy(svc->log.ident, strtok(NULL, ","), sizeof(svc->log.ident));
tok = strtok(NULL, ":=, ");
}
}
/**
* 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)
@@ -507,7 +634,7 @@ void service_runlevel(int newlevel)
*
* Service conditions can be: svc/<PATH> for PID files, net/<IFNAME>/up
* and net/<IFNAME>/exists. The condition handling is further described
* doc/conditions.md.
* docs/conditions.md.
*
* For multiple instances of the same command, e.g. multiple DHCP
* clients, the user must enter an ID, using the :ID syntax.
@@ -523,13 +650,13 @@ void service_runlevel(int newlevel)
*/
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
{
int i = 0;
int id = -1;
#ifdef INETD_ENABLED
int forking = 0;
#endif
int log = 0, levels = 0;
char *username = NULL, *line;
int levels = 0;
char *line;
char *username = NULL, *log = NULL, *pid = NULL;
char *service = NULL, *proto = NULL, *ifaces = NULL;
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
svc_t *svc;
@@ -551,6 +678,15 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
while (*desc && isblank(*desc))
desc++;
} else {
int pos;
/* Find "--\n" to denote empty/no description */
pos = (int)strlen(line) - 2;
if (pos > 0 && !strcmp(&line[pos], "--")) {
line[pos] = 0;
desc = &line[pos];
}
}
cmd = strtok(line, " ");
@@ -562,7 +698,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
while (cmd) {
if (cmd[0] == '@') /* @username[:group] */
if (cmd[0] == '@') /* @username[:group] */
username = &cmd[1];
else if (cmd[0] == '[') /* [runlevels] */
runlevels = &cmd[0];
@@ -576,10 +712,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else if (!strncasecmp(cmd, "wait", 4))
forking = 0;
#endif
else if (!strncasecmp(cmd, "log", 3))
log = cmd;
else if (!strncasecmp(cmd, "pid", 3))
pid = cmd;
else if (cmd[0] != '/' && strchr(cmd, '/'))
service = cmd; /* inetd service/proto */
else if (!strncasecmp(cmd, "log", 3))
log = 1;
else
break;
@@ -629,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);
}
@@ -664,9 +803,11 @@ recreate:
}
#endif
svc->log = log;
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
/* Always clear svc PID file, for now. See TODO */
svc->pidfile[0] = 0;
/* Decode any optional pid:/optional/path/to/file.pid */
if (pid && svc_is_daemon(svc) && pid_file_parse(svc, pid))
_e("Invalid 'pid' argument to service: %s", pid);
if (username) {
char *ptr = strchr(username, ':');
@@ -682,18 +823,19 @@ recreate:
/* Internal plugin provides this service */
svc->inetd.cmd = plugin->inetd.cmd;
svc->inetd.builtin = 1;
} else {
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while ((cmd = strtok(NULL, " ")))
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
svc->args[i][0] = 0;
}
} else
parse_cmdline_args(svc, cmd);
svc->runlevels = levels;
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
conf_parse_cond(svc, cond);
if (log)
parse_log(svc, log);
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
char *iface, *name = service;
@@ -732,20 +874,60 @@ recreate:
else
svc_mark_clean(svc);
if (!file)
svc->protected = 1;
/* Free duped line, from above */
free(line);
return 0;
}
/*
* This function is called when cleaning up lingering (stopped) services
* after a .conf reload, as well as when an inetd connection terminates.
*
* We need to ensure we properly stop the service before removing it,
* including stopping any pending restart or SIGKILL timers before we
* proceed to free() the svc itself.
*
* Remember to not try to stop inetd connections, they only get here
* when already stopped, if we do we end up in a recursive loop.
*/
void service_unregister(svc_t *svc)
{
if (!svc)
return;
/*
* Only try stopping @svc if it's *not* an inetd connection.
* Prevents infinite recursion when called from service_step()
*/
switch (svc->type) {
#ifdef INETD_ENABLED
case SVC_TYPE_INETD:
inetd_del(&svc->inetd);
break;
case SVC_TYPE_INETD_CONN:
/* inetd connection, if UDP unblock parent */
if (svc_is_busy(svc->inetd.svc)) {
svc_unblock(svc->inetd.svc);
service_step(svc->inetd.svc);
}
break;
#endif
default:
service_stop(svc);
break;
}
svc_del(svc);
}
void service_monitor(pid_t lost)
{
svc_t *svc;
char pidfile[MAX_ARG_LEN];
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
return;
@@ -753,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);
@@ -764,19 +944,22 @@ void service_monitor(pid_t lost)
_d("collected %s(%d)", svc->cmd, lost);
/* Try removing PID file (in case service does not clean up after itself) */
snprintf(pidfile, sizeof(pidfile), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
if (remove(pidfile)) {
if (errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), pidfile);
if (svc_is_daemon(svc)) {
char *fn;
fn = pid_file(svc);
if (remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn);
}
/* No longer running, update books. */
svc->pid = 0;
service_step(svc);
svc->start_time = svc->pid = 0;
/* Clean out any bootstrap tasks, they've had their time in the sun. */
if (svc_clean_bootstrap(svc))
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
if (!service_step(svc)) {
/* Clean out any bootstrap tasks, they've had their time in the sun. */
if (svc_clean_bootstrap(svc))
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
}
sm_step(&sm);
}
@@ -790,13 +973,13 @@ static void service_retry(svc_t *svc)
if (svc->state != SVC_HALTED_STATE ||
svc->block != SVC_BLOCK_RESTARTING) {
_d("%s not crashing anymore", svc->desc);
_d("%s not crashing anymore", svc->cmd);
*restart_cnt = 0;
return;
}
if (*restart_cnt >= RESPAWN_MAX) {
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->desc);
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->cmd);
svc_crashing(svc);
*restart_cnt = 0;
service_step(svc);
@@ -805,7 +988,7 @@ static void service_retry(svc_t *svc)
(*restart_cnt)++;
_d("%s crashed, trying to start it again, attempt %d", svc->desc, *restart_cnt);
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
svc_unblock(svc);
service_step(svc);
@@ -820,20 +1003,26 @@ 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);
}
}
void service_step(svc_t *svc)
/*
* Transition inetd/task/run/service
*
* Returns: non-zero if the @svc is no longer valid (removed)
*/
int service_step(svc_t *svc)
{
int err;
char *restart_cnt = (char *)&svc->restart_cnt;
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;
@@ -852,12 +1041,8 @@ restart:
case SVC_DONE_STATE:
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc)) {
if (svc_is_busy(svc->inetd.svc)) {
svc_unblock(svc->inetd.svc);
service_step(svc->inetd.svc);
}
service_unregister(svc);
return;
return -1;
}
#endif
if (svc_is_changed(svc))
@@ -896,28 +1081,22 @@ restart:
if (sm_is_in_teardown(&sm))
break;
/*
* Make state transition *before* service_start(), because
* of HOOK_SVC_START, which may call service_step()
*/
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
err = service_start(svc);
if (err) {
(*restart_cnt)++;
svc_set_state(svc, SVC_READY_STATE);
if (!svc_is_inetd_conn(svc))
break;
}
/* Everything went fine, clean and set state */
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
}
break;
case SVC_RUNNING_STATE:
if (!enabled) {
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
@@ -930,7 +1109,7 @@ restart:
* Restart directly after the first crash,
* then retry after 2 sec
*/
_d("delayed restart of %s", svc->desc);
_d("delayed restart of %s", svc->cmd);
service_timeout_after(svc, 1, service_retry);
break;
}
@@ -942,6 +1121,7 @@ restart:
}
if (svc_is_runtask(svc)) {
svc_set_state(svc, SVC_STOPPING_STATE);
svc->once++;
break;
}
@@ -951,7 +1131,6 @@ restart:
switch (cond) {
case COND_OFF:
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
case COND_FLUX:
@@ -967,8 +1146,12 @@ restart:
break;
service_restart(svc);
} else {
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
#ifdef INETD_ENABLED
if (svc_is_inetd(svc))
inetd_stop_children(&svc->inetd, 1);
else
#endif
service_stop(svc);
}
svc_mark_clean(svc);
}
@@ -980,7 +1163,6 @@ restart:
if (!enabled) {
kill(svc->pid, SIGCONT);
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
@@ -995,12 +1177,18 @@ restart:
case COND_ON:
kill(svc->pid, SIGCONT);
svc_set_state(svc, SVC_RUNNING_STATE);
/* Reassert condition if we go from waiting and no change */
if (!svc_is_changed(svc)) {
char cond[MAX_COND_LEN];
mkcond(cond, sizeof(cond), svc->cmd);
cond_set_path(cond_path(cond), COND_ON);
}
break;
case COND_OFF:
kill(svc->pid, SIGCONT);
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
case COND_FLUX:
@@ -1011,8 +1199,18 @@ 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;
}
void service_step_all(int types)
@@ -1020,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
*
@@ -1040,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;
@@ -1081,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
+3 -2
View File
@@ -34,10 +34,11 @@ void service_unregister (svc_t *svc);
void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_step (svc_t *svc);
int service_step (svc_t *svc);
void service_step_all (int types);
void service_worker (void *unused);
void service_bootstrap_cb (uev_t *w, void *arg, int events);
int service_completed (void);
#endif /* FINIT_SERVICE_H_ */
+20 -3
View File
@@ -27,6 +27,8 @@
*
* SIGHUP
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
* Also restarts API (initctl) socket, like SysV init and systemd
* does on USR1 with their FIFO/D-Bus.
*
* SIGUSR1
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
@@ -81,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
@@ -131,8 +135,12 @@ void do_kill(int signo)
continue;
if (fgets(file, sizeof(file), fp)) {
if (file[0] != '@')
if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file);
else if (file[0] != '@')
kill(pid, signo);
else
_d("Skipping %s ...", &file[1]);
}
fclose(fp);
}
@@ -179,6 +187,11 @@ void do_shutdown(shutop_t op)
return;
}
if (wdog) {
print(kill(wdog->pid, SIGPWR) == 1, "Advising watchdog, system going down");
do_sleep(2);
}
/* Unmount any tmpfs before unmounting swap ... */
unmount_tmpfs();
run("swapoff -e -a");
@@ -197,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, SIGPWR) == 1, "Pending watchdog reboot");
print(kill(wdog->pid, SIGTERM) == 1, "Pending watchdog reboot");
while (timeout--)
do_sleep(1);
}
@@ -229,6 +242,10 @@ static void sighup_cb(uev_t *w, void *arg, int events)
return;
}
/* Restart initctl API domain socket, similar to systemd/SysV init */
api_exit();
api_init(w->ctx);
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
service_reload_dynamic();
}
+21 -8
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,19 +153,16 @@ 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 (runlevel != 0 && runlevel != 6)
if (conf_any_change())
conf_reload();
/* 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);
+73 -11
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>
@@ -32,11 +33,40 @@
#include "finit.h"
#include "svc.h"
#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
@@ -73,7 +103,7 @@ svc_t *svc_new(char *cmd, int id, int type)
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
/* Default description, if missing */
desc = rindex(cmd, '/');
desc = strrchr(cmd, '/');
if (desc)
desc++;
else
@@ -85,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:
@@ -95,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;
}
@@ -205,7 +242,7 @@ svc_t *svc_job_iterator(svc_t **iter, int first, int job)
* svc_foreach - Run a callback for each registered service
* @cb: Callback to run for each service
*/
void svc_foreach(void (*cb)(svc_t *))
void svc_foreach(int (*cb)(svc_t *))
{
svc_t *svc, *iter = NULL;
@@ -222,7 +259,7 @@ void svc_foreach(void (*cb)(svc_t *))
* @types: Mask of service types
* @cb: Callback to run for each matching type
*/
void svc_foreach_type(int types, void (*cb)(svc_t *))
void svc_foreach_type(int types, int (*cb)(svc_t *))
{
svc_t *svc, *iter = NULL;
@@ -342,6 +379,27 @@ svc_t *svc_find_by_nameid(char *name, int id)
return NULL;
}
/**
* svc_find_by_plidfile - Find an service object by its PID file
* @fn: PID file, can be absolute path or relative to /run
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find_by_pidfile(char *fn)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char path[MAX_ARG_LEN];
if (string_compare(pid_runpath(fn, path, sizeof(path)), pid_file(svc)))
return svc;
}
return NULL;
}
/**
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
*
@@ -356,8 +414,14 @@ void svc_mark_dynamic(void)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->protected)
continue;
if (svc_is_inetd_conn(svc))
continue;
*((int *)&svc->dirty) = -1;
}
}
void svc_mark_dirty(svc_t *svc)
@@ -382,10 +446,8 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->dirty == -1 && cb) {
if (svc->dirty == -1 && cb)
cb(svc);
svc_mark_clean(svc);
}
}
}
+47 -22
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.
@@ -90,8 +93,11 @@ typedef struct svc {
/* Service details */
pid_t pid;
char pidfile[MAX_ARG_LEN];
long start_time; /* Start time, as seconds since boot, from sysinfo() */
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; /* Service, run, task, inetd, ... */
int protected; /* Services like dbus-daemon & udev by Finit */
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
int starting; /* ... waiting for pidfile to be re-asserted */
int runlevels;
@@ -106,9 +112,16 @@ 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 */
int log;
struct {
char enabled;
char null;
char file[64];
char prio[20];
char ident[20];
} log;
/* Identity */
char username[MAX_USER_LEN];
@@ -125,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);
@@ -134,14 +150,15 @@ svc_t *svc_find (char *cmd, int id);
svc_t *svc_find_by_pid (pid_t pid);
svc_t *svc_find_by_jobid (int job, int id);
svc_t *svc_find_by_nameid (char *name, int id);
svc_t *svc_find_by_pidfile (char *fn);
svc_t *svc_iterator (svc_t **iter, int first);
svc_t *svc_inetd_iterator (svc_t **iter, int first);
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
void svc_foreach (void (*cb)(svc_t *));
void svc_foreach_type (int types, void (*cb)(svc_t *));
void svc_foreach (int (*cb)(svc_t *));
void svc_foreach_type (int types, int (*cb)(svc_t *));
svc_t *svc_stop_completed (void);
@@ -158,34 +175,38 @@ int svc_is_unique (svc_t *svc);
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int));
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
static inline 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 int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline void svc_starting (svc_t *svc) { 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 && 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) {
@@ -207,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";
@@ -238,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))
+81 -67
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;
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");
@@ -159,6 +143,8 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
noclear = 1;
else if (!strcmp(tok, "nowait"))
nowait = 1;
else if (!strcmp(tok, "nologin"))
nologin = 1;
else
args[num++] = tok;
@@ -218,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)
@@ -236,12 +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.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) {
@@ -252,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",
@@ -267,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 */
@@ -275,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");
@@ -287,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);
@@ -308,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;
}
@@ -323,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++;
@@ -334,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;
}
@@ -372,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;
@@ -392,13 +399,20 @@ void tty_start(finit_tty_t *tty)
return;
}
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;
@@ -415,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;
@@ -463,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);
}
/*
@@ -476,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);
@@ -485,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;
@@ -496,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);
@@ -521,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);
+16 -21
View File
@@ -32,12 +32,15 @@
#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;
int runlevels;
char *cmd; /* NULL when running built-in getty */
@@ -47,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);
+98
View File
@@ -23,8 +23,12 @@
#include "config.h"
#include <ctype.h> /* isprint() */
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */
#include "util.h"
int screen_rows = 24;
@@ -42,12 +46,92 @@ char *progname(char *arg0)
return prognm;
}
int strtobytes(char *arg)
{
int mod = 0, bytes;
size_t pos;
if (!arg)
return -1;
pos = strspn(arg, "0123456789");
if (arg[pos] != 0) {
if (arg[pos] == 'G')
mod = 3;
else if (arg[pos] == 'M')
mod = 2;
else if (arg[pos] == 'k')
mod = 1;
else
return -1;
arg[pos] = 0;
}
bytes = atoi(arg);
while (mod--)
bytes *= 1000;
return bytes;
}
void do_sleep(unsigned int sec)
{
while ((sec = sleep(sec)))
;
}
/* Seconds since boot, from sysinfo() */
long jiffies(void)
{
struct sysinfo si;
if (!sysinfo(&si))
return si.uptime;
return 0;
}
char *uptime(long secs, char *buf, size_t len)
{
long mins, hours, days, years;
char y[20] = "", d[20] = "", h[20] = "", m[20] = "", s[20] = "";
if (!buf) {
errno = EINVAL;
return NULL;
}
years = secs / 31556926;
secs = secs % 31556926;
days = secs / 86400;
secs = secs % 86400;
hours = secs / 3600;
secs = secs % 3600;
mins = secs / 60;
secs = secs % 60;
if (years)
snprintf(y, sizeof(y), "%ld year", years);
if (days)
snprintf(d, sizeof(d), "%ld day", days);
if (hours)
snprintf(h, sizeof(h), "%ld hour", hours);
if (mins)
snprintf(m, sizeof(m), "%ld min", mins);
if (secs)
snprintf(s, sizeof(s), "%ld sec", secs);
snprintf(buf, len, "%s%s%s%s%s%s%s%s%s",
y, years ? " " : "",
d, days ? " " : "",
h, hours ? " " : "",
m, mins ? " " : "",
s);
return buf;
}
/* Allowed characters in job/id/name */
static int isallowed(int ch)
{
@@ -73,6 +157,10 @@ char *sanitize(char *arg, size_t len)
return NULL;
}
/*
* Called at boot, and shutdown, to (re)initialize the
* screen size for print() et al.
*/
void screen_init(void)
{
if (!isatty(STDOUT_FILENO))
@@ -81,6 +169,16 @@ void screen_init(void)
initscr(&screen_rows, &screen_cols);
}
/*
* Called when debug mode is enabled to revert back
* to old-school safe defaults.
*/
void screen_exit(void)
{
screen_rows = 24;
screen_cols = 80;
}
/**
* Local Variables:
* indent-tabs-mode: t
+6
View File
@@ -39,10 +39,16 @@ extern char *prognm;
#include <lite/conio.h>
char *progname (char *arg0);
int strtobytes (char *arg);
void do_sleep (unsigned int sec);
long jiffies (void);
char *uptime (long secs, char *buf, size_t len);
char *sanitize (char *arg, size_t len);
void screen_init (void);
void screen_exit (void);
int screen_width (void);
#endif /* FINIT_UTIL_H_ */
+53 -28
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,37 +9,47 @@
* 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;
int handover = 0;
int shutdown = 0;
static void sighandler(int signo)
{
if (signo == SIGTERM)
handover = 1;
if (signo == SIGPWR)
shutdown = 1;
running = 0;
}
static int init(char *progname, char *devnode, int timeout)
static int init(char *progname, char *devnode)
{
int fd;
@@ -47,25 +57,37 @@ static int init(char *progname, char *devnode, int timeout)
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;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
return fd;
}
static int loop(int fd, int period)
static int loop(int fd, int timeout)
{
int dummy = 0;
int period = timeout / 2;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
while (running) {
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
sleep(period);
}
/* System is going down, prepare to reboot system on TERM */
if (shutdown) {
timeout /= 3;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
}
/* External watchdogd wants to take over ... */
if (handover) {
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
return !write(fd, "V", 1);
}
@@ -73,27 +95,30 @@ static int loop(int fd, int period)
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, WDT_TIMEOUT);
if (fd == -1) {
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
_exit(1);
}
ret = loop(fd, WDT_TIMEOUT / 2);
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;
}
/**