Compare commits

...
374 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
Joachim Nilsson 55479cc511 Bump version for first -rc, v3.1-rc1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 11:52:40 +01:00
Joachim Nilsson b702f7eeef Update ChangeLog for upcoming v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 11:52:40 +01:00
Joachim Nilsson 0e37bb0924 Follow-up to b0663d0, cond_init() must run before first hooks
The new condition triggered hooks requrie /var/run/finit/cond to
be set up before calling the first user-configurable hooks.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 11:19:35 +01:00
Joachim Nilsson 360f3e72bc Audit b0663d0, add newline to generation ID in cond files
- Add newline to cond generation ID, like PID files, easier when
  debugging.  Does not affect fscanf() in cond_get_gen()
- Update copyright years

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 10:05:45 +01:00
Joachim Nilsson c1023c03c7 Merge pull request #89 from westermo/cond-generation
Cond generation
2017-12-23 09:37:16 +01:00
Joachim Nilsson e94d2a8c08 Merge branch 'master' into cond-generation 2017-12-23 09:31:41 +01:00
Joachim Nilsson f7c0366fa2 Fix segfault on X86_64, call va_end()+va_start() for each vfprintf()
... varargs is a special kind of hell.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 00:38:01 +01:00
Joachim Nilsson 8992e7bf2b Refactor svc_iterator() into a proper iterator, add first flag
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 23:56:14 +01:00
Joachim Nilsson 95518df62d Some systems rely on /dev/shm being there for mount -a
This was accidentally removed in d7401b2, when svc_t was refactored
from using shared memory.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 16:44:52 +01:00
Joachim Nilsson 85fe95835a doc/build.md: Mention required library versions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 13:17:39 +01:00
Joachim Nilsson baf9c4f61f Travis-CI: Update build deps, libite v1.9.2 --> v2.0.1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 13:12:04 +01:00
Joachim Nilsson b439ac670d .conf: don't rely on mtime to determine if service needs reloading
Until now Finit has used mtime to determine if a file has been changed,
or simply touched to request reload, when `initctl reload` is called.
Using mtime for this purpose is a monumentally bad idea since time can
be changed at any point: a user may adjust the time, NTP continously
tunes the clock, and time stamps are stored as the walltime.  So if we
compare timestamps against the last time we (booted or) did reload, we
would miss .conf file changes.

This patch replaces the mtime mistake with an inotify watcher for the
following files: /etc/finit.d/*.conf, /etc/finit.d/available/*.conf,
and /etc/finit.conf.  All file changes between (bootstraps and) calls
to `initctl reload` are tracked, like the mtime backend it replaces.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 13:08:38 +01:00
Joachim Nilsson bb3d9c8670 initctl: Add support for touch <CONF>, mark .conf for reload
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 12:50:18 +01:00
Joachim Nilsson 2f9b97b3a7 Update libite (-lite) requried version: 1.9.2 --> 2.0.1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 12:49:04 +01:00
Joachim Nilsson c59f7d23f4 Add and improve debug messages, clean out old dev. comments
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 12:40:43 +01:00
Tobias Waldekranz b0663d04e4 cond: don't rely on mtime for condition management
TIL, using mtimes for tracking event orderings is a monumentally bad
idea (queue the nodding UNIX-beards). Mtimes are in wallclock time which
is not necessarily monotonically increasing. A user may adjust the time,
an NTP daemon will continously tune the clock and so on.

Instead, store an explicit generation number in each condition file,
which will be monotonically increased by finit on each reconf.
2017-12-21 11:10:47 +01:00
Joachim Nilsson 6f26f2f9d0 TODO: Idea about addding support for an optional rescue mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:30:49 +01:00
Joachim Nilsson e7cca05fe3 Reserve certain configuration directives for bootstrap only
The following configuration directives, previously only allowed
in /etc/finit.conf, are now also only allowed in runlevel S, i.e.
at bootstrap:

- host
- mknod
- network
- runparts
- runlevel

This change is in preparation for allowing reload of finit.conf
as well as /etc/finit.d/*.conf, which we already support.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:16:55 +01:00
Joachim Nilsson f3669c7537 svc: Remove svc_dynamic_iterator(), soon no difference between svc's
This is a prelude to a major behavioral change.  The difference between
a static and dynamic service will be removed in Finit v3.1.  This means
all .conf files, including /etc/finit.conf, will be re-read on reload.

However, not all configuration directives will be re-read, some will
only be applicable at bootstrap, i.e. runlevel S.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:04:14 +01:00
Joachim Nilsson a9456cec41 svc: Remove unused function, svc_foreach_dynamic()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:02:38 +01:00
Joachim Nilsson 61a9926442 Minor, staticify
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 00:13:21 +01:00
Joachim Nilsson 7784480abe Postpone networking stuff, + hooks, a bit to the finalize() step
This patch moves the networking bit to the latter part of bootstrap,
called finalize(), in favor of starting the event loop earlier.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 00:10:29 +01:00
Joachim Nilsson db50c09ec5 initctl: Improve show SVC command output a bit, provide summary
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 21:04:17 +01:00
Joachim Nilsson 0f1f51b5ad Refactor, change from static array of svc_t to linked list
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 17:50:02 +01:00
Joachim Nilsson d7401b2a96 Follow-up to issue #81: remove SysV shared memory IPC altogether
This change removes a long standing kernel requirement and architectural
mishap in the design of Finit v1.  It is no longer necessary to include
CONFIG_SYSVIPC in the kernel to use Finit and its command line tool
initctl.  It is also no longer possible to connect to the internals of
PID 1 using shmat(), the initctl tool has already been updated to use
the domain socket API.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 11:20:21 +01:00
Joachim Nilsson c0515d112e New client side API to access svc_t entries without shared memory
This change is one of the required intermediate steps to remove the
shared memory store for all svc_t, this in turn to avoid any external
programs manipulating the internal memory of PID 1.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 11:06:19 +01:00
Joachim Nilsson 9a8b55737f Move svc_status() + svc_dirtystr() to header file as static inline
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 11:03:15 +01:00
Joachim Nilsson d7d8b8487d Refactor other iterators to use new re-entrant svc_iterator1() API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 07:49:45 +01:00
Joachim Nilsson 7f04382eee Refactor, use new re-entrant svc_iterator1() API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 00:21:07 +01:00
Joachim Nilsson bf004d9ec4 svc: Re-entrant replacement to svc_iterator() --> svc_iterator1()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 00:18:13 +01:00
Joachim Nilsson 23e0928e5c Minor, unused arguments no longer need magic tricks or hand waving
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 00:17:00 +01:00
Joachim Nilsson d6b2ac94dd Fix regression in 32b9096, only pick next :ID for inetd services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 14:54:46 +01:00
Joachim Nilsson 7362e6d00a initctl: Fix error handling for intictl start/stop if missing arg
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 12:12:57 +01:00
Joachim Nilsson d94d11e00a initctl: Allow short forms of commands
For ease of use, allow short forms of commands, like:

   initctl h
   initctl c d
   initctl l

Also, default to show conditions if no argument to cond is given.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 12:09:41 +01:00
Joachim Nilsson f06a0fcb71 initctl: Support printing previous runlevel
This patch adds support for "PREVLEVEL RUNLEVEL" output to the initctl
runlevel command.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 10:54:06 +01:00
Joachim Nilsson cae61ad6df initctl refactor, break out domain socket/client API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-16 12:15:28 +01:00
Joachim Nilsson 27e4af5ed8 Rename, client.c --> telinit.c
We need the name client.c for the client side of api.c, so let's rename
the client to its traditional name.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-15 14:00:11 +01:00
Joachim Nilsson f10a724503 Add mention of dropping UDP connections from blocked interfaces
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-14 17:23:54 +01:00
Joachim Nilsson 33037b012a Merge pull request #88 from ccb3/master
Drop UDP packets from unwanted interfaces
2017-12-14 10:44:29 +01:00
Lennart Eriksson 0c75e59b77 Drop UDP packets from unwanted interfaces
Previously UDP packets were never dropped and if a request came
from a not valid interface inetd got stuck in an inifinite
loop looking at the same packet every time and not dropping it.

Signed-off-by: Lennart Eriksson <lennart.eriksson@westermo.se>
2017-12-14 10:34:07 +01:00
Joachim Nilsson 8e55a76732 Initial draft of v3.1 release notes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-14 09:13:13 +01:00
Joachim Nilsson 32b9096e31 Fix #87: Allow inetd services to be registered with an id != 1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 22:20:30 +01:00
Joachim Nilsson f51723e8b7 Fix #86: Ensure that run/tasks are run at least once per runlevel
This patch fixes the problem with run/task jobs with conditions, run at
runlevel S (bootstrap), not being given enough time to actually start
and run even once.

The new svc counter 'once' is now inspected before transitioning away
from runlevel S to the configured runlevel.  A global (currently hard
coded) timeout of 10 sec is given to all run/tasks.

As a spin-off, this patch starts up the event loop much earlier than
before.  Meaning the initial runlevel change at boot is now done in
a finalize() callback when all run/tasks have completed, or when the
global timeout has expired.  Starting the event loop earlier gives us
the possibility to rely on the pidfile plugin to provide our run/tasks
with the conditions necessary to start.

TODO: What remains is to add similar handling to all runlevel
      transitions, not just the initial one at boot.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 9e31bd8a23 Fix #18: Add support for calling run/tasks on HOOK points
This patch implements support for calling run/task jobs on the built-in
`HOOK_*` points in Finit.  To map each HOOK point to a condition a new
classification was made, as shown in plugin.h and in the example below.

task <hook/mount/root> /sbin/attachubi.sh      # run in parallel with other tasks/services
run  <hook/net/up>     /sbin/dosomething.sh    # run in sequence with other commands

Please note, only rudimentary tested.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson e841984a4e service_step(): Fix problem with double start of service/run/tasks
Make svc state transition to RUNNING *before* service_start(), because
in that function we may call service_step() as well, which would start
the svc again (since it was still in READY).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 594476c4fc Fix progress print-out ordering bug, and run HOOK_SVC_START only on success
Only run HOOK_SVC_START on successful start, and *after* having printed
the result of the started service, otherwise any print-outs from plugins
or tasks we start may overwrite the started service's progress.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 9c423dfca4 Disable toggling of log/debug in fsck() loop
Don't really see the point of this code anymore, was possibly an issue
before we refactored the logging subsystem.  Comment out for now, and
remove later.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 77cd0f0243 Fix problem with duplicate log messages before syslogd has started
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 5dc4e7c058 cond_update(): Minor, log service description as well as cmd
If you have lots of service/run/tasks using the same cmd it's impossible
to tell them apart in debug mode.  This patch logs description as well
as cmd to improve on this situation a bit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 3c015f384d Track run/tasks, should run at least once per declared runlevel
This patch adds a 'once' counter to the svc_t for run/task jobs.  This
counter is incremented when Finit calls service_start() and is reset on
runlevel changes.

Also, at the end of a runlevel each run/task have their state reset to
SVC_HALTED_STATE so they can be launched again for the new runlevel, if
so declared.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 5117b723c2 Minor, fix missing rlim2str() declaration
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson b55a609834 Handle /etc overlayfs: reload /etc/finit.d/*.conf after mount -a
Systems running OverlayFS, or similar, on /etc may have new/modified
.conf files in /etc/finit.d.  This patch makes sure to reload them
after `mount -a` has run so any configured bootstrap tasks and the
new hook callback scripts may run as expected.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 60eccbac31 Refactor, relocate+rename service_enabled() --> svc_enabled()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson b182e6bcc5 Minor update and refactor, use kernel doc style comments
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson b8bf26b107 Refactor, rename restart counter and change type to char, MAX=10
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 14c0d80f63 Merge pull request #85 from reverendhomer/doc-fix-typo
doc: fix couple of typos in config.md
2017-12-01 18:45:20 +01:00
Reverend Homer 1f55f677d1 doc: fix couple of typos in config.md 2017-12-01 20:07:58 +03:00
Joachim Nilsson 40267a5f0f Minor, comment fixes and additions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-28 08:07:06 +01:00
Joachim Nilsson 9f50b033d9 Abort service kill if already terminated, otherwise kill(0, SIGKILL)
This patch fixes a rather nasty bug in the SIGTERM+SIGKILL logic
employed by Finit when stopping tasks, e.g. at a runlevel change.

When a process terminates, after Finit having sent it SIGTERM, and the
SIGKILL timer expires *before* the timer has been cancelled, then the
timer callback would cause kill(0, SIGKILL).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-28 08:00:46 +01:00
Joachim Nilsson bbaa7a1403 TODO: Process callbacks replaced with conditions in Finit 3.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-27 22:37:38 +01:00
Joachim Nilsson b68c5c1291 Fix #45: add support for rlimit per service/run/task/inetd/tty
This patch extends the existing rlimit implementation to support setting
limits per service, run/task, inetd, and tty.

Use rlimit in /etc/finit.conf to change the global setting, which is
then inherited to each /etc/finit.d/*.conf.  For each .conf file the
rlimit is reinitialized to the global finit.conf settings.

Also, add `unlimited` keyword, to replace the now deprecated `infinity`
keyword.  The latter is however kept, for compatibility with previous
releases, for the foreseeable future.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-26 16:35:37 +01:00
Joachim Nilsson 44007c86f2 initctl: Fix problem with start/stop using job ID
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-24 08:51:50 +01:00
Joachim Nilsson cd73c1c951 Replace libite:fmode() with stat(), removed in libite v2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-22 14:40:02 +01:00
Joachim Nilsson 5406af2054 watchdog: Adjust WDT timeout (30 sec), use half that as kick period
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:51:33 +01:00
Joachim Nilsson 3c800f6508 Refactor, simplify registration of built-in watchdog daemon
This patch not only greatly simplifies the registration of the built-in
watchdog daemon, it also fixes a nasty race condition with an external
watchdog daemon.  The INIT_CMD_WDOG_HELLO could reach finit in reversed
order, leading to the built-in daemon shutting down, but finit killing
the just registered external daemon.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:48:32 +01:00
Joachim Nilsson e0fb2b036d Improve debug of initctl api
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:45:07 +01:00
Joachim Nilsson 0ee4657f1c Follow-up to 6c563a2, clean up unused variables
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:39:27 +01:00
Joachim Nilsson ec2382b6d4 Silence, remove "Starting ..." of inetd services from boot progress
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:30:04 +01:00
Joachim Nilsson 0fc2df1a65 Silence, remove "Loading %sconfiguration ..." from boot progress
Most Linux distributions, embedded or otherwise, do not need this
and only confuses the user of an OS with Finit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:29:31 +01:00
Joachim Nilsson 6c563a2b22 Silence, remove "Loading plugins ..." from boot progress
Most Linux distributions, embedded or otherwise, do not need this
and only confuses the user of an OS with Finit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:27:57 +01:00
Joachim Nilsson 6a2911223d Fix screen_init() regression introduced in a6290da
A simple missing semicolon caused initscr() to not be called properly.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:26:45 +01:00
Joachim Nilsson 7f499ed1d0 watchdog: Kick one last time before handover to external watchdogd
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-18 14:48:46 +01:00
Joachim Nilsson 2d2807bc65 Change log message for crashing services, remove dev. debug info
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-18 14:48:08 +01:00
Joachim Nilsson 4af7974fb9 Travis-CI: Install new required version of libuEv, v2.1.0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:26:12 +01:00
Joachim Nilsson 3fd45eac7c bootmisc: Handle _PATH_VARDB inconsistency gracefully
Some systems use /var/db while others use /var/lib/misc, we need to
support both and cannot rely on _PATH_VARDB, since it too might not
be set properly.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:24:25 +01:00
Joachim Nilsson bb4578292f Bump version for v3.1 release cycle
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:01:02 +01:00
Joachim Nilsson 656fa3d0e7 Bump required libuEv version to v2.1.0, fixed failure mode support
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:00:36 +01:00
Joachim Nilsson 9a75813350 api.c: Handle domain socket errors more gracefully, restart socket
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:00:05 +01:00
Joachim Nilsson 5af1f05606 plugin: Only close() PID file descriptors >0, skip -1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 14:34:08 +01:00
Joachim Nilsson fcdb5c2965 initctl: Refactor, reduce code duplication
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 22:58:50 +01:00
Joachim Nilsson af3e560d79 tty.c: Refactor, move LIST_REMOVE() to tty_unregister()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 21:33:22 +01:00
Joachim Nilsson b6e695b656 Bump required libuEv version to v2.0.0, with failure mode support
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 19:40:54 +01:00
Joachim Nilsson 123df18847 Travis-CI: Bump required libuEv version to v2.0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 19:40:28 +01:00
Joachim Nilsson dae21d9d64 Simplify rlimit documentation a bit
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 19:18:32 +01:00
Joachim Nilsson bf4a0abe5c conf_parse_rlimit(): Refactor, replace 'new' with 'cfg'
'new' is a reserved keyword in C++, it is recommnded to avoid it also in C.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 14:57:22 +01:00
Joachim Nilsson 6826d7ee3f Handle event loop watcher failure modes
When alibuEv event watcher callback has `UEV_ERROR` in the events mask
we must handle the error.  Unfortunately, in most cases there is not
much we can do but ignore it or log the error.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 11:18:12 +01:00
Joachim Nilsson 168da2a5c6 plugin: Update list of available events for I/O plugins
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 02:47:33 +01:00
Joachim Nilsson c82dc54205 initctl: Minor refactor, handle errors first
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 02:46:19 +01:00
Joachim Nilsson eb0a759371 TTY: If parsing speed argument fails, do NOT default to B38400
If the speed argument to the `tty` stanza is invalid we should not
default to some other random speed.  Instead, leave the line speed
as-is, like we do when starting an external getty.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-02 10:48:04 +01:00
Joachim Nilsson 830abc1971 getty: Remove leading \n in do_issue(), not needed
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-31 20:57:36 +01:00
Joachim Nilsson 4c3a11131f Minor cleanup + use strlen() rather than sizeof() on const strings
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-31 20:56:48 +01:00
Joachim Nilsson 796701c854 getty: Ignore SIGINT & C:o earlier
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-31 20:54:56 +01:00
Joachim Nilsson ab760aa3ff Mention @console TTYs in top-level README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 17:21:21 +01:00
Joachim Nilsson 2b9eecbef3 Spell check and mention use of logger tool in service stanza
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 17:17:21 +01:00
Joachim Nilsson b8190c8a2a Refactor, move speed parser from getty() to prepare_tty()
This patch relocates the speed parser so that exec.c can set the TTY
speed properly as soon as possible.  For external getty we make sure
to not touch the TTY speed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 16:53:26 +01:00
Joachim Nilsson adb4670d2f Clear current line before printing \nPlease press Enter ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 16:52:14 +01:00
Joachim Nilsson d31575538c getty: Disable VINTR + VEOF, re-enable before calling /bin/login
This patch makes the Finit built-in getty behave a bit more like other
getty implementations.  We now not only disable ISIG but also VEOF and
VINTR when prearing the TTY for the getty (built-in or external).

Like before, it is up to the getty process to leave the TTY in a
usable state to /bin/login, so we also move the stty() call to
just before calling /bin/login in getty.c

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 16:11:59 +01:00
Joachim Nilsson faf0c680b8 getty: Disable ECHO, XON/OFF while waiting for initial <CR>
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 12:14:04 +01:00
Joachim Nilsson 250e26f2f6 Fix #84: Reset TTY w/ stty() before taking any pre-getty action
This patch moves the stty() function from getty.c to a separate file so
it can be called from exec.c as well.  The fix to #84 is simply to call
stty() in prepare_tty(), which is shared by run_getty() and run_getty2()
and called before "Please press Enter ...".

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 11:59:48 +01:00
Joachim Nilsson b45c3d5cb8 timespec_newer(): time a should be > or >= to b
Time comparision should be >= not just >.  May seem a bit silly to
nitpick like this since we're comparing nano seconds, but on systems
with no high-res timers this happens more often than you would like.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 16:04:18 +02:00
Joachim Nilsson 6b2678c2ed pidfile: On HOOK_SVC_RECONF the netlink plugin must run first
When performing an `initctl reload` with one (unchanged) service
depending on, e.g. `net/iface/lo`, its condition will not be set
to ON by the pidfile plugin unless the netlink plugin hook runs
first.

Example:

    service <net/iface/lo> /sbin/dropbear ...

Which provides the <svc/sbin/dropbear> condition, will not be
set by pidfile.so during `initctl reload` because dropbear is
still SIGSTP:ed waiting for <net/iface/lo>.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 15:55:35 +02:00
Joachim Nilsson 0d6f203e0b tty_reload(): When reloading a single TTY dev, remember to clean it
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 13:48:25 +02:00
Joachim Nilsson 781ba66905 Revert "Remove reconf condition when .conf reload completes"
This reverts commit 06479aa5f1.
2017-10-24 13:07:48 +02:00
Joachim Nilsson 9a97ab29dc Revert "cond_reassert(): Use cond_set_path(), not cond_clear(), for reconf"
This reverts commit afb3fada50.
2017-10-24 13:07:15 +02:00
Joachim Nilsson b2b17ea4fb doc/config.md: Document new tty @console support in finit.conf
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:30:24 +02:00
Joachim Nilsson bfa99266e7 Minor, cleanup old unused function prototype
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:30:04 +02:00
Joachim Nilsson c76a58ef7a Remove long since deprecated finit.conf: console <DEV> support
All this code did was trigger prctl() to set process name to "console",
which you could use as a simple means of figuring out if a process was
started from the system console.

There are other ways to do this, which Finit should not be involved in.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:28:11 +02:00
Joachim Nilsson 51c5f3cbfa Length of the ANSI escaped runlevels string is 20 chars, need 21
Problem reported by @wkz

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:20:25 +02:00
Joachim Nilsson a6290da210 screen_init(): Verify that STDOUT is a TTY
Before we call screen_init(), which in turn calls libite:initscr(),
which in turn does a lot of tcgetattr() and termios to STDOUT to figure
out the size, we need to check that we're not backgrounded, or don't
actually have a TTY.  Problem reported by @wkz

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:16:51 +02:00
Joachim Nilsson 949f02eff4 tty: Add support for auto-detecting serial console from SysFS
This patch adds support for a new TTY device keyword `@console` which
auto-detects the system console by reading the SysFS file:

    /sys/class/tty/console/active

Embedded systems with one of /dev/ttyAMA0, /tty/ttyMXC0 or the original
/dev/ttyS0, as their active serial console can now instead use a single
line to /etc/finit.d/getty.conf to start the built-in getty on the same
TTY as the kernel already runs on:

    tty [12345789] @console 115200 noclear

With no TTY defined Finit will either do nothing (default), or start a
fallback shell if enabled in the configure script.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 17:24:46 +02:00
Joachim Nilsson 7970e45f45 Follow-up to cf60cdd, make sure to disable ISIG before calling getty
Disable INTR, QUIT, SUSP, and DSUSP while handing over to getty.  It is
up to the getty process to allow ISIG again.

This patch is due to the differences in control line setup between ARM,
x86/x86_64, and most notably PPC.  PPC in particular seem to have ISIG
set by default from the kernel.

Without this patch pressing Ctrl-C before getty is started will lead to
Finit receiving SIGINT and defaulting to a reboot, as introduced in the
BusyBox init signal adoption in eeb59eb.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 11:57:58 +02:00
Joachim Nilsson a36dafaa6c README: Update initctl usage
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-19 20:16:37 +02:00
Joachim Nilsson c092d22d48 initctl: Default to show services listing, not usage text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-19 20:01:15 +02:00
91 changed files with 4775 additions and 1855 deletions
+8 -1
View File
@@ -2,13 +2,15 @@
*.o *.o
*.so *.so
*.gz *.gz
ID
GPATH GPATH
GRTAGS GRTAGS
GSYMS GSYMS
GTAGS GTAGS
# GNU configure and build system # GNU configure and build system
/autom4te.cache/* /.deps
/autom4te.cache
/aclocal.m4 /aclocal.m4
/ar-lib /ar-lib
/compile /compile
@@ -33,3 +35,8 @@ GTAGS
/.config /.config
/.stamp /.stamp
/.unpacked /.unpacked
# WeOS build system
/.autoconf
/.automake
/.built
+12 -9
View File
@@ -9,9 +9,9 @@ sudo: false
#sudo: true #sudo: true
# Test build with both GCC and Clang (LLVM) # Test build with both GCC and Clang (LLVM)
# - clang
compiler: compiler:
- gcc - gcc
- clang
env: env:
global: global:
@@ -33,20 +33,23 @@ addons:
branch_pattern: dev branch_pattern: dev
install: install:
- wget https://github.com/troglobit/libuev/releases/download/v1.5.2/libuev-1.5.2.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/v1.9.2/libite-1.9.2.tar.xz - wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
- tar xf libuev-1.5.2.tar.xz - tar xf libuev-2.2.0.tar.xz
- tar xf libite-1.9.2.tar.xz - tar xf libite-2.0.1.tar.xz
- (cd libuev-1.5.2 && ./configure --prefix=/tmp && make && make install-strip) - (cd libuev-2.2.0 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libite-1.9.2 && ./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 # Custom build script for the time being, no "make test" yet
script: script:
- ./autogen.sh - ./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 - cat config.log
- make clean - make clean
- make -j5 V=1 - make V=1 -j5
- make install-strip - make install-strip
- tree /tmp - tree /tmp
- ldd /tmp/sbin/finit - ldd /tmp/sbin/finit
+5
View File
@@ -1,6 +1,11 @@
Current Maintainer(s): Current Maintainer(s):
* Joachim Nilsson <troglobit@gmail.com> * 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): Original Author(s):
* Claudio Matsuoka <cmatsuoka@gmail.com> * Claudio Matsuoka <cmatsuoka@gmail.com>
+183 -2
View File
@@ -3,6 +3,185 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[3.2][UNRELEASED] - 2018-10-xx
------------------------------
Bug fix release.
### Changes
* Add support for `--disable-docs` and `--disable-contrib` to speed up
builds and work around issue with massively parallel builds
* Add `-b`, batch mode, for non-interactive use to `initctl`
* Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog
* Set `$SHELL`, like `$PATH`, to a sane default value, needed by BusyBox
* Finit no longer automatically reloads its `*.conf` files after running
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
* `initctl` without an argument or option now defaults to list services
* Converted built-in watchdog daemon to standalone mini watchdogd
* Improved watchdog hand-over, now based on `svc_t` and not PID
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
not allowed in the runtime runlevel are unconditionally stopped
* Removed `HOOK_SVC_START` and `HOOK_SVC_LOST`, caused more problems
than they were worth. Users are encouraged to use accounting instead
* Skip displaying "Restarting ..." progress for bootstrap processes
* Added a simple work queue mechanism to queue up work at boot + runtime
- Postpone deletion of `svc_t` until any `SIGKILL` timer has elapsed
- As long as a stepped service changes state we queue another step all
event, because services may depend on each other
* Require new libuEv API: `uev_init1()` to reduce event cache so that
the kernel can invalidate deleted events before enqueing to userspace
### Fixes
* Fix #96: Start udevd as a proper service
* Ensure we track run commands as well as task/service, once per runlevel
* Fix #98: FTBFS with `--disable-inetd`
* Make sure to unblock UDP inetd services when connection terminates.
Regression introduced in v3.1
* Ensure run/tasks also go to stopping state on exit, like services,
otherwise it is unnecessarily hard to restart them
* Fix #99: Do not try to `SIGKILL` inetd services, they are not backed
by a PID. This caused a use after free issue crashing finit. Found
and fixed by Tobias Waldekranz, Westermo
* Fix missing OS/Finit title bug, adds leading newline before banner
* Remove "Failed connecting to watchdog ..." error message on systems
that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
Fixed by Jonas Johansson, Westermo
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
Fixed by Jonas Johansson, Westermo
* Fix #111: Only restart inetd services when necessary. E.g., if the
listening interface is changed. Only stop established connections
which are no longer allowed, i.e. do not touch already allowed
established connections. Fixed by Jonas Johansson, Westermo
* Fix: update inetd service args on config change. Found and fixed by
Petrus Hellgren, Westermo
* Fix `initctl [start | restart]`, should behave the same for services
that have crashed. Found by Mattias Walström, Westermo
* Wait for bootstrap phase to complete before cleaning out any bootstrap
processes that have stopped, they may be restarted again
* Reassert condition when an unmodified run/task/service goes from
WAITING back to RUNNING again after a reconfiguration event.
Found and fixed by Jonas Johansson, Westermo
* Remove service condition when service is deleted
[3.1][] - 2018-01-23
--------------------
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 ...`
prompt will now replace that line with the login issue text
* Calling `initctl` without any arguments or options now defaults to
show status of all enabled services, and run/task/inetd jobs
* Cosmetic change to boot messages, removed `Loading plugins ...`, start
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
* initctl: Support for `initctl touch <CONF>` to be used with `reload`
* initctl: Improved output of `initctl show <SVC>`
* Support reloading `/etc/finit.conf`. The main finit.conf file
previously did not support reloading at runtime, as of v3.1 all
configuration directives supported in `/etc/finit.d/*.conf` are now
supported in `/etc/finit.conf`
* Change `.conf` dependency + reload handling. Finit no longer relies
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
* Reset TTY before restarting it. A program may manipulate the TTY in
various ways before the user logs out, Finit needs to reset the TTY to
a sane state before restarting it. Issue #84
* On .conf parse errors, do *not* default to set TTY speed 38400, reuse
current TTY speed instead
* Fix run/tasks, must be guaranteed to run once per declared runlevel.
All run/tasks on `[S]` with a condition `<...>` failed to run. Finit
now tracks run/tasks more carefully, waiting for them to finish before
switching to the configured runlevel at boot. Issue #86
* Allow inetd services to be registered with a unique ID, e.g. `:161`,
issue #87. Found by Westermo
* inetd: drop UDP packets from blocked interfaces, issue #88
* Handle obscure inter-plugin dependency issue by calling the netlink
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
service has already terminated, otherwise we may do `kill(0, SIGKILL)`
* Fix reconfiguration issue with (very quick) systems that don't have
highres timers
* Fix formatting of runlevel string in `initctl show`
* Allow running `initctl` with `STDOUT` redirected
* Fix regression in `initctl start/stop <ID>`, using name worked not id
* Fix error handling in `initctl start/stop` without any argument
* 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 [3.0][] - 2017-10-19
-------------------- --------------------
@@ -28,7 +207,7 @@ distributions.
* Ensure plugins always have a default name, file name * Ensure plugins always have a default name, file name
* Reorganization, move all source files to a `src/` sub-directory * Reorganization, move all source files to a `src/` sub-directory
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by * 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 * Remove `UNUSED()` macro, mentioned here because it may have been used
by external plugin developers. Set `-Wno-unused-parameter` instead by external plugin developers. Set `-Wno-unused-parameter` instead
* New table headings in `initctl`, using `top` style inverted text * New table headings in `initctl`, using `top` style inverted text
@@ -598,7 +777,9 @@ Major bug fix release.
* Initial 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 [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.4]: https://github.com/troglobit/finit/compare/2.3...2.4
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3 [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 doc_DATA = README.md LICENSE contrib/finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md 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 # Compat hook
install-dev: install-dev:
@make -C src install-pkgincludeHEADERS @make -C src install-pkgincludeHEADERS
# Target to run when building a release # Target to run when building a release
release: dist release: distcheck
@for file in $(DIST_ARCHIVES); do \ @for file in $(DIST_ARCHIVES); do \
md5sum $$file > ../$$file.md5; \ md5sum $$file > ../$$file.md5; \
done done
+121 -40
View File
@@ -2,17 +2,19 @@
* [Introduction](#introduction) * [Introduction](#introduction)
* [Features](#features) * [Features](#features)
* [Bootstrap](doc/bootstrap.md#bootstrap) * [Bootstrap](docs/bootstrap.md#bootstrap)
* [Runlevels](#runlevels) * [Runlevels](#runlevels)
* [Syntax](doc/config.md#syntax) * [Syntax](docs/config.md#syntax)
* [Inetd](doc/inetd.md#inetd) * [Inetd](docs/inetd.md#inetd)
* [Runparts & /etc/rc.local](#runparts--etcrclocal) * [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) * [Rebooting & Halting](#rebooting--halting)
* [Commands & Status](#commands--status) * [Commands & Status](#commands--status)
* [Building](doc/build.md#building) * [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
* [Running](doc/build.md#running) * [Building](docs/build.md#building)
* [Debugging](doc/build.md#debugging) * [Running](docs/build.md#running)
* [Recovery](docs/build.md#recovery)
* [Debugging](docs/build.md#debugging)
* [Requirements](#requirements) * [Requirements](#requirements)
* [Origin & References](#origin--references) * [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 Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section where both [Debian](contrib/debian/) and [contrib/](contrib/) section with tutorials for:
up to date [Alpine Linux](contrib/alpine/) tutorials can be found.
* [Alpine Linux](contrib/alpine/),
* [Debian GNU/Linux](contrib/debian/), and
* [Void Linux](contrib/void/)
![Alpine screenshot](images/alpine-screenshot.png) ![Alpine screenshot](images/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/). The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
@@ -49,12 +54,23 @@ host default
# Runlevel to start after bootstrap, 'S', default: 2 # Runlevel to start after bootstrap, 'S', default: 2
runlevel 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 # Services to be monitored and respawned as needed
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
service [S12345] /sbin/klogd -n -- Kernel log daemon service [S12345] /sbin/klogd -n -- Kernel log daemon
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab) 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 # For multiple instances of the same service, add :ID somewhere between
# the service/run/task keyword and the command. # the service/run/task keyword and the command.
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server 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 # Use built-in getty for serial port and USB serial
tty [12345] /dev/ttyAMA0 115200 noclear nowait tty [12345] /dev/ttyAMA0 115200 noclear nowait
tty [12345] /dev/ttyUSB0 115200 noclear 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 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 overview of some of the most common components needed to start a UNIX
daemon: daemon:
@@ -111,7 +130,7 @@ advanced uses as well:
service /usr/sbin/sshd -D service /usr/sbin/sshd -D
Dependencies are handled using [conditions](doc/conditions.md). On of Dependencies are handled using [conditions](docs/conditions.md). One of
the most common conditions is to wait for basic networking to become the most common conditions is to wait for basic networking to become
available: available:
@@ -124,6 +143,10 @@ to create its PID file, by default `/var/run/syslogd.pid`.
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
service [S12345] /sbin/syslogd -n -- Syslog daemon service [S12345] /sbin/syslogd -n -- Syslog daemon
Notice the `log` keyword, BusyBox `ntpd` uses `stderr` for logging when
run in the foreground. With `log` Finit redirects `stdout` + `stderr`
to the system log daemon using the command line `logger(1)` tool.
A service, or task, can have multiple dependencies listed. Here we wait A service, or task, can have multiple dependencies listed. Here we wait
for *both* `syslogd` to have started and basic networking to be up: for *both* `syslogd` to have started and basic networking to be up:
@@ -148,7 +171,7 @@ Start, monitor and restart services should they fail.
**Inetd** **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 maintain a separate config file for services that you want to start on
demand. demand.
@@ -163,7 +186,7 @@ Built-in optional inetd services:
- discard [RFC863][] - discard [RFC863][]
- time (rdate) [RFC868][] - time (rdate) [RFC868][]
For more information, see [doc/inetd.md](doc/inetd.md). For more information, see [docs/inetd.md](docs/inetd.md).
**Getty** **Getty**
@@ -179,7 +202,18 @@ tty [12345] /dev/ttyAMA0 115200 noclear vt100
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100 tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
``` ```
For more information, see [doc/config.md](doc/config.md#syntax). Users of embedded systems may want to enable automatic serial console
with the special `@console` device. This works regardless weather the
system uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit
figures it out by querying sysfs: `/sys/class/tty/console/active`.
```conf
tty [12345] @console 115200 linux noclear
```
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** **Runlevels**
@@ -188,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 minimal style as everything else in Finit. The `[2345]` syntax can be
applied to service, task, run, inetd, and TTY stanzas. applied to service, task, run, inetd, and TTY stanzas.
All services in runlevel S(1) are started first, followed by the desired Reserved runlevels are 0 and 6, halt and reboot, respectively just like
run-time runlevel. Runlevel S can be started in sequence by using `run SysV init. Runlevel 1 can be configured freely, but is recommended to
[S] cmd`. Changing runlevels at runtime is done like any other init, be kept as the system single-user runlevel since Finit will not start
e.g. <kbd>init 4</kbd>, but also using the more advanced `intictl` tool. 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** **Plugins**
@@ -216,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 needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O. hook into the boot process or respond to various I/O.
For more information, see [doc/plugins.md](doc/plugins.md). 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 Runparts & /etc/rc.local
------------------------ ------------------------
At the end of the boot, when networking and all services are up, Finit At the end of the boot, when all bootstrap (`S`) tasks and services have
calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory, started, but not networking, Finit calls its built-in [run-parts(8)][]
and `/etc/rc.local`, in that order if they exist. 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 ```shell
runparts /etc/rc.d/ 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`. 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 If it exists and is an executable shell script Finit calls it at the very
very end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
hooks in [doc/plugins.md](doc/plugins.md#hooks), and about the system in [docs/plugins.md](docs/plugins.md#hooks), and about the system bootstrap
bootstrap in [doc/bootstrap.md](doc/bootstrap.md). in [docs/bootstrap.md](docs/bootstrap.md).
Runlevels Runlevels
@@ -312,7 +378,7 @@ section.
For compatibility reasons Finit listens to the same set of signals as For compatibility reasons Finit listens to the same set of signals as
BusyBox init. This is not 100% compatible with SysV init, but clearly BusyBox init. This is not 100% compatible with SysV init, but clearly
the more common combination for Finit. For more details, see 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`. Finit also listens to the classic SysV init FIFO, used by `telinit`.
Support for this is implemented by the `initctl.so` plugin. Hence, Support for this is implemented by the `initctl.so` plugin. Hence,
@@ -346,33 +412,46 @@ not return until the given command has fully completed.
```shell ```shell
~ $ initctl -h ~ $ initctl -h
Usage: initctl [OPTIONS] <COMMAND> Usage: initctl [OPTIONS] [COMMAND]
Options: Options:
-d, --debug Debug initctl (client)
-v, --verbose Verbose output -v, --verbose Verbose output
-h, --help This help text -h, --help This help text
Commands: Commands:
debug Toggle Finit (daemon) debug debug Toggle Finit (daemon) debug
help This help text help This help text
reload Reload *.conf in /etc/finit.d/ and activate changes version Show Finit version
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
status | show Show status of services 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 cond set <COND> Set (assert) condition => +COND
cond clear <COND> Clear (deassert) condition => -COND cond clear <COND> Clear (deassert) condition => -COND
cond show Show condition status cond show Show condition status
cond dump Dump all conditions and their status cond dump Dump all conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name status <JOB|NAME>[:ID] Show service status, by job# or name
version Show Finit version status | show Show status of services, default command
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system
halt Halt system
poweroff Halt and power off system
utmp show Raw dump of UTMP/WTMP db
``` ```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf 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 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. these will also affect how a service is maintained.
**Note:** even though it is possible to start services not belonging in **Note:** even though it is possible to start services not belonging in
@@ -398,11 +477,13 @@ Requirements
------------ ------------
Finit is capable of running on both desktop/server systems with udev and Finit is capable of running on both desktop/server systems with udev and
embedded systems that usually come with BusyBox mdev. Finit probes for embedded systems that usually come with BusyBox mdev. Some systems have
them at runtime and expects `/dev/` to be writable, usually `devtmpfs`. systemd-udev or eudev today instead of the original udev, Finit probes
It is also possible to run on a statically set up `/dev` if needed. It for all of them at runtime and expects `/dev/` to be a writable file
is however not a good idea to have both udev and mdev installed at the system using `devtmpfs`. It is also possible to run on a statically set
same time, this will lead to unpredictable results. 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 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 done slightly differently and on systems with udev you might want to add
+45 -10
View File
@@ -1,10 +1,17 @@
AC_INIT(Finit, 3.0, 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_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR([src/finit.c]) AC_CONFIG_SRCDIR([src/finit.c])
AC_CONFIG_HEADER([config.h]) 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() # Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4]) #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_CC
AC_PROG_LN_S AC_PROG_LN_S
AC_PROG_INSTALL AC_PROG_INSTALL
AC_PROG_MKDIR_P
# Configuration. # Configuration.
AC_HEADER_STDC AC_HEADER_STDC
@@ -33,8 +41,8 @@ AC_TYPE_UINT16_T
AC_TYPE_UINT32_T AC_TYPE_UINT32_T
# Check for required libraries # Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 1.5.2]) PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 1.9.2]) PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
# Check for configured Finit features # Check for configured Finit features
AC_ARG_ENABLE(emergency_shell, AC_ARG_ENABLE(emergency_shell,
@@ -58,6 +66,10 @@ AC_ARG_ENABLE(watchdog,
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[ AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
enable_watchdog=no]) 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, AC_ARG_ENABLE(inetd,
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[ AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
enable_inetd=yes]) 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]),,[ AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
enable_logrotate=yes]) 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 # Check for extra plugins to enable
enable_all_plugins=auto enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl]) 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-daytime], [no], [Inetd plugin: daytime server, RFC867])
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863]) AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868]) 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([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)]) 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.]) 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]), AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
[fifo=$withval], [fifo=$localstatedir/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, AC_ARG_WITH(config,
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]), AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
[conf=$withval], [conf=$sysconfdir/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"], [ AS_IF([test "x$enable_watchdog" = "xyes"], [
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])]) 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 ########################################################################### ### Disable features ###########################################################################
AS_IF([test "x$enable_inetd" != "xno"], [ AS_IF([test "x$enable_inetd" != "xno"], [
enable_inetd="yes" 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(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"]) AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "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 # Generate all files
AC_OUTPUT AC_OUTPUT
@@ -220,6 +252,7 @@ cat <<EOF
FIFO path.............: $fifo_path FIFO path.............: $fifo_path
Finit config file.....: $conf_path Finit config file.....: $conf_path
Finit config.d path...: $rcsd_path Finit config.d path...: $rcsd_path
Finit plugin path.....: $plugin_path
Compat rc.local path..: $rclocal_path Compat rc.local path..: $rclocal_path
Random seed path......: $random_path Random seed path......: $random_path
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
@@ -230,6 +263,8 @@ Behavior:
Plugins...............: $plugins Plugins...............: $plugins
Optional features: Optional features:
Install docs/.........: $enable_docs
Install contrib/......: $enable_contrib
Built-in inetd........: $enable_inetd Built-in inetd........: $enable_inetd
Built-in watchdogd....: $enable_watchdog Built-in watchdogd....: $enable_watchdog
Built-in logrotate....: $enable_logrotate 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 bringup script
network service networking start 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 # Virtual consoles to start getty on
tty /dev/tty1 tty /dev/tty1
tty /dev/tty2 tty /dev/tty2
@@ -33,6 +37,13 @@ service [2345] /sbin/klogd -n -- Kernel logging server
service [2345] /sbin/syslogd -n -- Syslog server service [2345] /sbin/syslogd -n -- Syslog server
service [3] /usr/sbin/gdm -- GNOME Display Manager 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 # Run start scripts from this directory
# runparts /etc/start.d # 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 -39
View File
@@ -18,32 +18,29 @@ after a reconfiguration (`initctl reload`), the Finit dependency system
can also be used. can also be used.
General Near Future
------- -----------
* Remove shm, let `initctl` call `api.c` for each `svc_t` instead of * Merge UDP redirect service uredir as an UDP inetd variant
opening up the inner parts of PID 1 to other processes. Also, this
would prevent issues like #81 from happening.
* Add support for timed shutdown in Finit, including cancelled shutdown.
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
API restart to SIGHUP callback for the same functionality in Finit.
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task * 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 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, * Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task, i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling. e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Add `IFF_RUNNING` support to netlink plugin along `IFF_UP` * Cron/At support, see below
* Allow scripts, instead of C code, as process callbacks * Write man pages for finit and `finit.conf`, steal from the excellent
* Add support for a pipe/popen where we can listen for `STOP`, `START` `pimd` man pages ...
and `RELOAD` on `STDOUT` from a script ... call it for example * Add simple, *very* simple, `finit-simple.conf` example. To illustrate
`.monitor=` in `svc_t` ... the script named as the basename of the how close Finit3 still is to the original easy-to-use Finit0 :)
service it monitors + `.sh`.
* Implement `initctl stop|start|restart|reload|status <SVC>` and
`service <SVC> stop|start|restart|reload|status` on top 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. * Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json` `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 * [Solaris SMF][] (Service Management Facility) has some interesting
features that may be well worth looking into adopting: features that may be well worth looking into adopting:
@@ -54,6 +51,13 @@ General
* `initctl add /sbin/service -n -- Service description`, for details * `initctl add /sbin/service -n -- Service description`, for details
see above [Solaris SMF][] featuer, issue #55 and issue #69, see above [Solaris SMF][] featuer, issue #55 and issue #69,
https://github.com/troglobit/finit/issues/69#issuecomment-287907610 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 Init
@@ -90,14 +94,16 @@ system-level timed periodic tasks:
Proposed syntax: Proposed syntax:
# Crontab # 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 # One-shot 'at' command
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
Notice the ISO date in notation. The runlevels is extra filtering Notice the ISO date in the `at` notation. The `cron @SPEC` could use
sugar. E.g., if cron service is only allowed in runlevels two or three are mo loose notation, e.g. `@hourly`, `@midnight`, `@daily`, `@weekly`,
it will not start if system currently is in runlevel four. `@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 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 owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
@@ -109,21 +115,4 @@ the shell -- that way setting up one-time jobs would not entail
re-reading `/etc/finit.conf` 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 [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 2. Set up initial signal handlers
3. Set up default $PATH early 3. Set up default $PATH early
4. Mount `/proc` and `/sys` 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 6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab` 7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type 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 20. Bring up loopback interface and all `/etc/network/interfaces`, if
the `.conf` setting `network <SCRIPT>` is set, it is called instead the `.conf` setting `network <SCRIPT>` is set, it is called instead
21. Call 3rd level hooks, `HOOK_NETWORK_UP` 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 At every runlevel change all `*.conf` files in `/etc/finit.d/` are
(re)loaded and new services, tasks, and blocking run commands are (re)loaded and new services, tasks, and blocking run commands are
started. Provided they are allowed in the new runlevel and all of started. Provided they are allowed in the new runlevel and all of
their conditions, if any, are set. their conditions, if any, are set.
23. Call 4th level hooks, `HOOK_SVC_UP` 24. Call 4th level hooks, `HOOK_SVC_UP`
24. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
25. Call `/etc/rc.local`, if it exists and is an executable shell script 25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP` 26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs, or a fallback shell on `/dev/console` 27. Start all configured TTYs, or a fallback shell on `/dev/console`
+27 -5
View File
@@ -26,6 +26,12 @@ optional plugins to enable. It depends on two external libraries:
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
The configure script checks for all dependencies, including the correct
version of the above mentioned libraries. Currently requried versions:
- libite v2.0.1
- libuEv v2.1.0
Configure Configure
--------- ---------
@@ -47,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-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-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin.
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin. * `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
@@ -118,14 +122,32 @@ append="init=/sbin/finit"
Remember to also set up an initial `/etc/finit.conf` before rebooting! 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 Debugging
--------- ---------
Add `finit_debug`, or `--debug`, to the kernel command line to enable Edit, or append to, the kernel command line: remove `quiet` to enable
debug messages. kernel messages and add `debug` to enable Finit debug messages.
```shell ```shell
append="init=/sbin/finit --debug" append="init=/sbin/finit debug"
``` ```
To debug startup issues, in particular issues with getty/login, try 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/route/default`
- `net/<IFNAME>/exist` - `net/<IFNAME>/exist`
- `net/<IFNAME>/up` - `net/<IFNAME>/up`
- `net/<IFNAME>/running`
**Note:** `up` means administratively up, the interface flag `IFF_UP`, **Note:** `up` means administratively up, the interface flag `IFF_UP`.
not link up, `IFF_RUNNING`. This latter conditions is currently not `running` is the `IFF_RUNNING` flag, meaning operatively up. The
implemented by the `plugins/netlink.c` plugin. difference is that `running` tells if the NIC has link.
Debugging Debugging
+169 -49
View File
@@ -2,6 +2,7 @@ Configuration
============= =============
* [Introduction](#introduction) * [Introduction](#introduction)
* [Service Wrapper Scripts](#service-wrapper-scripts)
* [Syntax](#syntax) * [Syntax](#syntax)
* [Limitations](#limitations) * [Limitations](#limitations)
* [/etc/finit.conf](#etcfinitconf) * [/etc/finit.conf](#etcfinitconf)
@@ -13,10 +14,10 @@ Introduction
Finit can be configured using only the original `/etc/finit.conf` file Finit can be configured using only the original `/etc/finit.conf` file
or in combination with `/etc/finit.d/*.conf`. Finit 3 can even start a 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 system using only `/etc/finit.d/*.conf`, highly useful for package-based
Linux distributions -- each package can provide its own "script" file. 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 - `/etc/finit.d/*.conf`: snippets, usually one service per file
Not all configuration directives are available in `/etc/finit.d/*.conf` 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 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 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). 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 Finit monitors all known active `.conf` files, so if you want to force
changes to or removal of `/etc/finit.d/*.conf` files, is detected. 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: 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). 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 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 Load a kernel module, with optional arguments
* `network <PATH>` * `network <PATH>`
Script or program to bring up networking, with optional arguments Script or program to bring up networking, with optional arguments
* `rlimit <hard|soft> RESOURCE <LIMIT|infinity>` * `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
Modify the specified resource's hard or soft limit to be the limit for a resource, or both if that argument is omitted. `RESOURCE`
specifed limit. `RESOURCE` is a lower-case string matching the is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
constants in `setrlimit(2)` with the `RLIMIT_` prefix without the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
removed. E.g. to select `RLIMIT_CPU`, `RESOURCE` would be
`cpu`. `LIMIT` is an integer whose unit depends on the resource LIMIT is an integer that depends on the resource being modified, see
being modified, see `setrlimit(2)` for more information. The special the man page, or the kernel `/proc/PID/limits` file, for details.
limit `infinity` means the there should be no limit on the resource, Finit versions before v3.1 used `infinity` for `unlimited`, which is
i.e. `RLIM_INFINITY` is passed to `setrlimit(2)`. still supported, albeit deprecated.
```shell ```shell
# No process is allowed more than 8MB of address space # No process is allowed more than 8MB of address space
@@ -67,8 +114,14 @@ Syntax
# Core dumps may be arbitrarily large # Core dumps may be arbitrarily large
rlimit soft core infinity 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>` * `runlevel <N>`
N is the runlevel number 1-9, where 6 is reserved for reboot. N is the runlevel number 1-9, where 6 is reserved for reboot.
Default is 2. Default is 2.
@@ -107,8 +160,30 @@ Syntax
Finit waits for another service, `/sbin/zebra`, to have created its Finit waits for another service, `/sbin/zebra`, to have created its
PID file in `/var/run/zebra.pid` before starting `ospfd`. PID file in `/var/run/zebra.pid` before starting `ospfd`.
For a detailed description of conditions, and how to debug them, Some services do not maintain a PID file and rather than patching each
see the [Finit Conditions](conditions.md) document. 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` * `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
Launch a daemon when a client initiates a connection on an Internet Launch a daemon when a client initiates a connection on an Internet
@@ -140,31 +215,48 @@ Syntax
* `runparts <DIR>` * `runparts <DIR>`
Call [run-parts(8)][] on `DIR` to run start scripts. All executable Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order. 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 It can be beneficial to use `S01name`, `S02othername`, etc. if there
is a dependency order between the scripts. Symlinks to existing is a dependency order between the scripts. Symlinks to existing
daemons can talso be used, but make sure they daemonize by default. 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 services and programs either terminate or start in the background or
you will block Finit. you will block Finit.
* `include <CONF>` * `include <CONF>`
Include another configuration file. Absolute path required. 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]` `tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
The first variant of this option uses the built-in getty on the given 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:** **Example:**
```conf ```conf
tty [12345] /dev/ttyAMA0 115200 noclear vt220 tty [12345] /dev/ttyAMA0 115200 noclear vt220
``` ```
The second variant is for using an external getty, like agetty or the The second `tty` syntax variant is for using an external getty, like
BusyBox getty. agetty or the BusyBox getty.
By default both variants *clear* the TTY and *wait* for the user to By default both variants *clear* the TTY and *wait* for the user to
press enter before starting getty. press enter before starting getty.
@@ -182,10 +274,28 @@ Syntax
embedded systems running multiple unused getty wastes both memory embedded systems running multiple unused getty wastes both memory
and CPU cycles, so `wait` is the preferred default. 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 Notice the ordering, the `TERM` option to the built-in getty must be
the last argument. the last argument.
On really bare bones systems Finit offers a fallback shell: Embedded systems may want to enable automatic `DEV` by supplying the
special `@console` device. This works regardless weather the system
uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
it out by querying sysfs: `/sys/class/tty/console/active`.
**Example:**
```conf
tty [12345] @console 115200 noclear vt220
```
On really bare bones systems Finit offers a fallback shell, which
should not be enabled on production systems since. This because it
may give a user root access without having to log in. However, for
bringup and system debugging it can come in handy:
```shell ```shell
configure --enable-fallback-shell configure --enable-fallback-shell
@@ -197,16 +307,10 @@ Syntax
service [12345] /bin/sh -l service [12345] /bin/sh -l
``` ```
* `console <DEV>`
Use this, along with a matching `tty DEV` line, to mark this TTY as a
special "console" port with `prctl()`. Useful in use-cases when
all logins except the console must be stopped, e.g. when performing a
flash upgrade on an embedded system.
When running <kbd>make install</kbd> no default `/etc/finit.conf` will When running <kbd>make install</kbd> no default `/etc/finit.conf` will
be installed since system requirements differ too much. Try out the be installed since system requirements differ too much. Try out the
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
is capable of service monitoring SSH, sysklogd, gdm and a console getty! is capable of service monitoring SSH, sysklogd, gdm and getty!
Every `run`, `task`, `service`, or `inetd` can also list the privileges Every `run`, `task`, `service`, or `inetd` can also list the privileges
the `/path/to/cmd` should be executed with. Simply prefix the path with the `/path/to/cmd` should be executed with. Simply prefix the path with
@@ -229,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. and only the old web server would be started and supervised.
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
`log` to redirect `stderr` and `stdout` of the application to syslog, `log` to redirect `stderr` and `stdout` of the application to a file or
using `logger`. 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 Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document. detailed description, see the [Conditions](conditions.md) document.
@@ -241,40 +359,42 @@ Limitations
To understand the limitations of `finit.conf` vs `finit.d` it is useful To understand the limitations of `finit.conf` vs `finit.d` it is useful
to picture the different phases of the system: bootstrap, runtime, and to picture the different phases of the system: bootstrap, runtime, and
shutdown. The `finit.conf` file can be used for all possible settings, shutdown.
but `finit.d/*.conf` can only be used for runtime settings.
### /etc/finit.conf ### /etc/finit.conf
This file was the only way to set up and boot a system using Finit. It This file used to be the only way to set up and boot a Finit system.
is used for pre-runtime settings like system hostname, network bringup Today it is mainly used for pre-runtime settings like system hostname,
and shutdown: network bringup and shutdown:
- `host` - `host`, only at bootstrap, (runlevel `S`)
- `mkdod` - `mknod`, only at bootstrap
- `network` - `network`, only at bootstrap
- `runparts` - `runparts`, only at bootstrap
- `include` - `include`
- `shtudown` - `log`, global setting
- `runlevel` - `shutdown`
- `runlevel`, only at bootstrap
- ... and all configuration stanzas from `/etc/finit.d` below - ... and all configuration stanzas from `/etc/finit.d` below
### /etc/finit.d ### /etc/finit.d
Support for partial `.conf` files in `/etc/finit.d` was added to handle Support for per-service `.conf` files in `/etc/finit.d` was added in
changes of the system configuration at runtime. It does *not support* v3.0 to handle changes of the system configuration at runtime. As of
the above `finit.conf` settings described above, only the following: 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` - `service`
- `task` - `task`
- `run` - `run`
- `inetd` - `inetd`
- `rlimit` - `rlimit`
- `tty` - `tty`
- `console`
**NOTE:*** The `/etc/finit.d` directory was previously the default Finit **NOTE:** The `/etc/finit.d` directory was previously the default Finit
`runparts` directory. Finit no longer has a default `runparts`, make `runparts` directory. Finit no longer has a default `runparts`, make
sure to update your setup, or the finit configuration, accordingly. sure to update your setup, or the finit configuration, accordingly.
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. * *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 * *netlink.so*: Listens to Linux kernel Netlink events for gateway and
interfaces. These events are then sent to the Finit service monitor 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 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 all modified/removed services have been stopped. When the hook has
completed, Finit continues to start all modified and new services. 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 * `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
change. The hook is called when services not matching the new change. The hook is called when services not matching the new
runlevel have been been stopped. When the hook has completed, Finit 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_LDFLAGS = -module -avoid-version -shared
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
@@ -38,10 +39,6 @@ libplug_la_SOURCES += time.c
endif endif
endif endif
if BUILD_LOST_PLUGIN
libplug_la_SOURCES += lost.c
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
libplug_la_SOURCES += netlink.c libplug_la_SOURCES += netlink.c
endif endif
@@ -83,10 +80,6 @@ pkglib_LTLIBRARIES += time.la
endif endif
endif endif
if BUILD_LOST_PLUGIN
pkglib_LTLIBRARIES += lost.la
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
pkglib_LTLIBRARIES += netlink.la pkglib_LTLIBRARIES += netlink.la
endif endif
+5 -1
View File
@@ -106,9 +106,10 @@ static void setup(void *arg)
_d("Setting up FHS structure in /var ..."); _d("Setting up FHS structure in /var ...");
umask(0); umask(0);
makedir("/var/cache", 0755); makedir("/var/cache", 0755);
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755); makedir("/var/games", 0755);
makedir("/var/lib", 0755); makedir("/var/lib", 0755);
makedir("/var/lib/misc", 0755); makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
makedir("/var/lib/alarm", 0755); makedir("/var/lib/alarm", 0755);
makedir("/var/lib/urandom",0755); makedir("/var/lib/urandom",0755);
if (fisdir("/run")) { if (fisdir("/run")) {
@@ -169,6 +170,9 @@ static void setup(void *arg)
makefifo("/dev/xconsole", 0640); /* sysklogd */ makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, gid); 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 */ /* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
uid = getuser("uuidd", NULL); uid = getuser("uuidd", NULL);
if (uid >= 0) { if (uid >= 0) {
+4 -3
View File
@@ -29,6 +29,7 @@
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "service.h" #include "service.h"
#include "conf.h"
#define DAEMON "dbus-daemon" #define DAEMON "dbus-daemon"
#define ARGS "--nofork --system" #define ARGS "--nofork --system"
@@ -37,7 +38,7 @@
static void setup(void *arg) static void setup(void *arg)
{ {
char *cmd; char *cmd;
char line[80]; char line[256];
cmd = which(DAEMON); cmd = which(DAEMON);
if (!cmd) { if (!cmd) {
@@ -57,8 +58,8 @@ static void setup(void *arg)
erase("/var/run/dbus/pid"); erase("/var/run/dbus/pid");
/* Register service with Finit */ /* 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, NULL)) if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON); _pe("Failed registering %s", DAEMON);
free(cmd); 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) static void nl_link(struct nlmsghdr *nlmsg)
{ {
int la; int la;
@@ -112,39 +123,25 @@ static void nl_link(struct nlmsghdr *nlmsg)
while (RTA_OK(a, la)) { while (RTA_OK(a, la)) {
if (a->rta_type == IFLA_IFNAME) { if (a->rta_type == IFLA_IFNAME) {
char msg[MAX_ARG_LEN];
strlcpy(ifname, RTA_DATA(a), sizeof(ifname)); strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
switch (nlmsg->nlmsg_type) { switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK: 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 * Check ifi_flags here to see if the interface is UP/DOWN
*/ */
_d("%s: New link, ifi_change 0x%x", ifname, i->ifi_change); _d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
if (i->ifi_change & IFF_UP) { net_cond_set(ifname, "exist", 1);
snprintf(msg, sizeof(msg), "net/%s/up", ifname); net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
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);
}
break; break;
case RTM_DELLINK: case RTM_DELLINK:
/* NOTE: Interface has dissapeared, not link down ... */ /* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname); _d("%s: Delete link", ifname);
snprintf(msg, sizeof(msg), "net/%s/exist", ifname); net_cond_set(ifname, "exist", 0);
cond_clear(msg); net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
break; break;
case RTM_NEWADDR: case RTM_NEWADDR:
+43 -42
View File
@@ -37,12 +37,6 @@ struct context {
int wd; 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 void pidfile_callback(void *arg, int fd, int events)
{ {
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; 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; struct inotify_event *ev;
ssize_t sz, len; ssize_t sz, len;
char *basename;
svc_t *svc; svc_t *svc;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); 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; 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) { 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")) if (!ev->mask || !strstr(ev->name, ".pid"))
continue; continue;
basename = strtok(ev->name, "."); svc = svc_find_by_pidfile(ev->name);
if (!basename)
continue;
svc = svc_find_by_nameid(basename, 1);
if (!svc) if (!svc)
continue; 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); mkcond(cond, sizeof(cond), svc->cmd);
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) { if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
svc_started(svc); svc_started(svc);
@@ -87,44 +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 * This function is called after `initctl reload` to reassert conditions
* recently been changed or while it's starting up. * 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 *_null) static void pidfile_reconf(void *arg)
{ {
static char cond[MAX_COND_LEN]; static char cond[MAX_COND_LEN];
svc_t *svc; svc_t *svc, *iter = NULL;
(void)(_null);
int restart = 0;
do { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
restart = 0; if (svc->state != SVC_RUNNING_STATE)
continue;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { if (svc_is_changed(svc) || svc_is_starting(svc))
mkcond(cond, sizeof(cond), svc->cmd); continue;
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 (restart) mkcond(cond, sizeof(cond), svc->cmd);
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); 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) static void pidfile_init(void *arg)
{ {
char *path;
struct context *ctx = arg; 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) { if (ctx->wd < 0) {
_pe("inotify_add_watch()"); _pe("inotify_add_watch()");
close(ctx->fd); close(ctx->fd);
@@ -148,7 +149,7 @@ static plugin_t plugin = {
.cb = pidfile_callback, .cb = pidfile_callback,
.flags = PLUGIN_IO_READ, .flags = PLUGIN_IO_READ,
}, },
.depends = { "bootmisc", }, .depends = { "bootmisc", "netlink" },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+6 -6
View File
@@ -45,7 +45,7 @@ static plugin_t plugin = {
static void setup(void) static void setup(void)
{ {
if (plugin.io.fd) if (plugin.io.fd > 0)
close(plugin.io.fd); close(plugin.io.fd);
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
@@ -57,7 +57,7 @@ static void watcher(void *arg, int fd, int events)
{ {
int len = 0; int len = 0;
char buf[EVENT_SIZE], name[42]; char buf[EVENT_SIZE], name[42];
tty_node_t *entry; struct tty *entry;
struct inotify_event *notified = (struct inotify_event *)buf; struct inotify_event *notified = (struct inotify_event *)buf;
while ((len = read(fd, buf, sizeof(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); snprintf(name, sizeof(name), "/dev/%s", notified->name);
entry = tty_find(name); entry = tty_find(name);
if (entry && tty_enabled(&entry->data)) { if (entry && tty_enabled(entry)) {
if (notified->mask & IN_CREATE) if (notified->mask & IN_CREATE)
tty_start(&entry->data); tty_start(entry);
else if (entry->data.pid) else if (entry->pid)
tty_stop(&entry->data); tty_stop(entry);
} }
} }
} }
+2
View File
@@ -1,6 +1,8 @@
.deps/* .deps/*
finit finit
initctl initctl
logit
reboot reboot
watchdogd
Makefile Makefile
Makefile.in Makefile.in
+35 -17
View File
@@ -1,24 +1,34 @@
EXTRA_DIST = rescue.conf
AM_CPPFLAGS = -U_FORTIFY_SOURCE AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC if STATIC
AM_CPPFLAGS += -DENABLE_STATIC AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs AM_LDFLAGS = -static-libtool-libs
else else
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
AM_LDFLAGS = -export-dynamic AM_LDFLAGS = -export-dynamic
endif endif
bin_PROGRAMS = logit
sbin_PROGRAMS = finit initctl reboot 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 \ finit_SOURCES = api.c \
cond.c cond-w.c cond.h \ cond.c cond-w.c cond.h \
client.c \ telinit.c \
conf.c conf.h \ conf.c conf.h \
exec.c finit.c finit.h \ exec.c finit.c finit.h \
getty.c \ getty.c stty.c \
helpers.c helpers.h \ helpers.c helpers.h \
log.c log.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 \ plugin.c plugin.h private.h \
schedule.c schedule.h \
service.c service.h \ service.c service.h \
sig.c sig.h \ sig.c sig.h \
sm.c sm.h \ sm.c sm.h \
@@ -30,20 +40,18 @@ pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h log.h plugin.h svc.h
if INETD if INETD
finit_SOURCES += inetd.c inetd.h finit_SOURCES += inetd.c inetd.h
endif endif
if WATCHDOGD
finit_SOURCES += watchdog.c watchdog.h
endif
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
finit_LDADD = $(lite_LIBS) $(uev_LIBS) finit_LDADD = $(lite_LIBS) $(uev_LIBS)
if STATIC if STATIC
finit_LDADD += plugins/libplug.la finit_LDADD += ../plugins/libplug.la
else else
finit_LDADD += -ldl finit_LDADD += -ldl
endif endif
initctl_SOURCES = initctl.c serv.c serv.h svc.c svc.h \ initctl_SOURCES = initctl.c client.c client.h \
serv.c serv.h svc.h \
cond.c cond.h util.c util.h cond.c cond.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS) initctl_CFLAGS += $(lite_CFLAGS)
@@ -56,12 +64,19 @@ reboot_LDADD = $(lite_LIBS)
INIT_LNKS = telinit init INIT_LNKS = telinit init
REBOOT_LNKS = shutdown halt poweroff suspend 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) # Hook in install to add finit and reboot symlink(s)
install-exec-hook: install-exec-hook:
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
@for file in $(INIT_LNKS); do \ @for file in $(INIT_LNKS); do \
lnk=$(DESTDIR)$(sbindir)/$$file; \ lnk=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$lnk ]; then \ if [ -e $$lnk ]; then \
echo "Skipping $$lnk exists already."; \ echo "Skipping $$lnk exists."; \
else \ else \
$(LN_S) finit $$lnk; \ $(LN_S) finit $$lnk; \
fi; \ fi; \
@@ -70,16 +85,17 @@ install-exec-hook:
echo "Non-finit reboot, skipping install."; \ echo "Non-finit reboot, skipping install."; \
else \ else \
for file in $(REBOOT_LNKS); do \ for file in $(REBOOT_LNKS); do \
link=$(DESTDIR)$(sbindir)/$$file; \ lnk=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$link ]; then \ if [ -e $$lnk ]; then \
echo "$$link exists, skipping.";\ echo "Skipping $$lnk exists."; \
else \ else \
$(LN_S) reboot $$link; \ $(LN_S) reboot $$lnk; \
fi; \ fi; \
done; \ done; \
fi fi
uninstall-hook: uninstall-hook:
@$(RM) $(DESTDIR)$(pkglibdir)/rescue.conf
@for file in $(INIT_LNKS); do \ @for file in $(INIT_LNKS); do \
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \ lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
if [ "$$lnk" != "finit" ]; then \ if [ "$$lnk" != "finit" ]; then \
@@ -92,9 +108,11 @@ uninstall-hook:
echo "Non-finit reboot, skipping uninstall."; \ echo "Non-finit reboot, skipping uninstall."; \
else \ else \
for file in $(REBOOT_LNKS); do \ for file in $(REBOOT_LNKS); do \
if [ "`readlink $$file`" != "reboot" ]; \ lnk=$(DESTDIR)$(sbindir)/$$file; \
then continue; \ dst=`readlink $$lnk`; \
if [ "$$dst" != "reboot" ]; then \
continue; \
fi; \ fi; \
$(RM) $(DESTDIR)$(sbindir)/$$file; \ $(RM) $$lnk; \
done; \ done; \
fi fi
+138 -26
View File
@@ -21,6 +21,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
@@ -38,18 +39,20 @@
#include "helpers.h" #include "helpers.h"
#include "log.h" #include "log.h"
#include "plugin.h" #include "plugin.h"
#include "private.h"
#include "sig.h" #include "sig.h"
#include "service.h" #include "service.h"
#include "util.h" #include "util.h"
uev_t api_watcher; extern svc_t *wdog;
static uev_t api_watcher;
static int call(int (*action)(svc_t *), char *buf, size_t len) static int call(int (*action)(svc_t *), char *buf, size_t len)
{ {
return svc_parse_jobstr(buf, len, action, NULL); return svc_parse_jobstr(buf, len, action, NULL);
} }
static int service_stop(svc_t *svc) static int stop(svc_t *svc)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -60,7 +63,7 @@ static int service_stop(svc_t *svc)
return 0; return 0;
} }
static int service_start(svc_t *svc) static int start(svc_t *svc)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -71,30 +74,60 @@ static int service_start(svc_t *svc)
return 0; return 0;
} }
static int service_restart(svc_t *svc) static int restart(svc_t *svc)
{ {
if (!svc) if (!svc)
return 1; return 1;
if (svc_is_blocked(svc))
svc_start(svc);
svc_mark_dirty(svc); svc_mark_dirty(svc);
service_step(svc); service_step(svc);
return 0; return 0;
} }
static int do_start (char *buf, size_t len) { return call(service_start, 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(service_stop, 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(service_restart, buf, len); } static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
#ifdef INETD_ENABLED static char query_buf[368];
static int do_query_inetd(char *buf, size_t len) static int missing(char *job, int id)
{
char buf[20];
char idstr[13] = "";
if (!job)
job = "";
if (id > 1)
snprintf(idstr, sizeof(idstr), ":%d", id);
snprintf(buf, sizeof(buf), "%s%s ", job, idstr);
strlcat(query_buf, buf, sizeof(query_buf));
return 1;
}
static int do_query(struct init_request *rq, size_t len)
{
memset(query_buf, 0, sizeof(query_buf));
if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) {
memcpy(rq->data, query_buf, sizeof(rq->data));
return 1;
}
return 0;
}
static svc_t *do_find(char *buf, size_t len)
{ {
int id = 1; int id = 1;
char *ptr, *input = sanitize(buf, len); char *ptr, *input;
svc_t *svc;
input = sanitize(buf, len);
if (!input) if (!input)
return -1; return NULL;
ptr = strchr(input, ':'); ptr = strchr(input, ':');
if (ptr) { if (ptr) {
@@ -102,11 +135,20 @@ static int do_query_inetd(char *buf, size_t len)
id = atonum(ptr); id = atonum(ptr);
} }
svc = svc_find_by_jobid(atonum(input), id); if (isdigit(input[0]))
if (!svc || !svc_is_inetd(svc)) { return svc_find_by_jobid(atonum(input), id);
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input);
return svc_find_by_nameid(input, id);
}
#ifdef INETD_ENABLED
static int do_query_inetd(char *buf, size_t len)
{
svc_t *svc;
svc = do_find(buf, len);
if (!svc || !svc_is_inetd(svc))
return 1; return 1;
}
return inetd_filter_str(&svc->inetd, buf, len); return inetd_filter_str(&svc->inetd, buf, len);
} }
@@ -163,20 +205,38 @@ static int do_handle_emit(char *buf, size_t len)
return result; return result;
} }
static void send_svc(int sd, svc_t *svc)
{
svc_t empty;
size_t len;
if (!svc) {
empty.pid = -1;
svc = &empty;
}
len = write(sd, svc, sizeof(*svc));
if (len != sizeof(*svc))
_d("Failed sending svc_t to client");
}
/* /*
* In contrast to the SysV compat handling in plugins/initctl.c, when * In contrast to the SysV compat handling in plugins/initctl.c, when
* `initctl runlevel 0` is issued we default to POWERDOWN the system * `initctl runlevel 0` is issued we default to POWERDOWN the system
* instead of just halting. * instead of just halting.
*/ */
static void cb(uev_t *w, void *arg, int events) static void api_cb(uev_t *w, void *arg, int events)
{ {
int sd, lvl; int sd, lvl;
svc_t *svc;
static svc_t *iter = NULL;
struct init_request rq; struct init_request rq;
sd = accept(w->fd, NULL, NULL); sd = accept(w->fd, NULL, NULL);
if (sd < 0) { if (sd < 0) {
_pe("Failed serving API request"); _pe("Failed serving API request");
return; goto error;
} }
while (1) { while (1) {
@@ -228,37 +288,46 @@ static void cb(uev_t *w, void *arg, int events)
break; break;
case INIT_CMD_DEBUG: case INIT_CMD_DEBUG:
_d("debug");
log_debug(); log_debug();
break; break;
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */ case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
_d("reload");
service_reload_dynamic(); service_reload_dynamic();
break; break;
case INIT_CMD_START_SVC: case INIT_CMD_START_SVC:
_d("start %s", rq.data);
result = do_start(rq.data, sizeof(rq.data)); result = do_start(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_STOP_SVC: case INIT_CMD_STOP_SVC:
_d("stop %s", rq.data);
result = do_stop(rq.data, sizeof(rq.data)); result = do_stop(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RESTART_SVC: case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data);
result = do_restart(rq.data, sizeof(rq.data)); result = do_restart(rq.data, sizeof(rq.data));
break; break;
#ifdef INETD_ENABLED #ifdef INETD_ENABLED
case INIT_CMD_QUERY_INETD: case INIT_CMD_QUERY_INETD:
_d("query inetd");
result = do_query_inetd(rq.data, sizeof(rq.data)); result = do_query_inetd(rq.data, sizeof(rq.data));
break; break;
#endif #endif
case INIT_CMD_EMIT: case INIT_CMD_EMIT:
_d("emit %s", rq.data);
result = do_handle_emit(rq.data, sizeof(rq.data)); result = do_handle_emit(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_GET_RUNLEVEL: case INIT_CMD_GET_RUNLEVEL:
rq.runlevel = runlevel; _d("get runlevel");
rq.runlevel = runlevel;
rq.sleeptime = prevlevel;
break; break;
case INIT_CMD_ACK: case INIT_CMD_ACK:
@@ -266,18 +335,52 @@ static void cb(uev_t *w, void *arg, int events)
goto leave; goto leave;
case INIT_CMD_WDOG_HELLO: case INIT_CMD_WDOG_HELLO:
_d("wdog hello");
if (rq.runlevel <= 0) { if (rq.runlevel <= 0) {
result = 1; result = 1;
break; break;
} }
if (wdogpid > 0 && wdogpid != rq.runlevel) { _e("Request to hand-over wdog ... to PID %d", rq.runlevel);
kill(wdogpid, SIGTERM); svc = svc_find_by_pid(rq.runlevel);
do_sleep(1); 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; break;
case INIT_CMD_SVC_ITER:
_d("svc iter, first: %d", rq.runlevel);
/*
* XXX: This severly limits the number of
* simultaneous client connections, but will
* have to do for now.
*/
svc = svc_iterator(&iter, rq.runlevel);
send_svc(sd, svc);
goto leave;
case INIT_CMD_SVC_QUERY:
_d("svc query: %s", rq.data);
result = do_query(&rq, len);
break;
case INIT_CMD_SVC_FIND:
_d("svc find: %s", rq.data);
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
goto leave;
default: default:
_d("Unsupported cmd: %d", rq.cmd); _d("Unsupported cmd: %d", rq.cmd);
break; break;
@@ -294,6 +397,13 @@ static void cb(uev_t *w, void *arg, int events)
leave: leave:
close(sd); close(sd);
if (UEV_ERROR == events)
goto error;
return;
error:
api_exit();
if (api_init(w->ctx))
_e("Unrecoverable error on API socket");
} }
int api_init(uev_ctx_t *ctx) int api_init(uev_ctx_t *ctx)
@@ -305,6 +415,7 @@ int api_init(uev_ctx_t *ctx)
.sun_path = INIT_SOCKET, .sun_path = INIT_SOCKET,
}; };
_d("Setting up external API socket ...");
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (-1 == sd) { if (-1 == sd) {
_pe("Failed starting external API socket"); _pe("Failed starting external API socket");
@@ -320,7 +431,7 @@ int api_init(uev_ctx_t *ctx)
goto error; goto error;
umask(oldmask); umask(oldmask);
if (!uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ)) if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
return 0; return 0;
error: error:
@@ -332,8 +443,9 @@ error:
int api_exit(void) int api_exit(void)
{ {
return shutdown(api_watcher.fd, SHUT_RDWR) || uev_io_stop(&api_watcher);
close(api_watcher.fd);
return close(api_watcher.fd);
} }
/** /**
+109 -103
View File
@@ -1,7 +1,6 @@
/* Simple telinit client /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2015-2017 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -22,133 +21,140 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h" /* Generated by configure script */ #include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <ctype.h> #include "client.h"
#include <getopt.h>
#include <stdio.h>
#include <lite/lite.h>
#include "finit.h" static int sd = -1;
#include "util.h"
static int do_send(struct init_request *rq) int client_connect(void)
{ {
static int fd = -1; struct sockaddr_un sun = {
int result = 255; .sun_family = AF_UNIX,
ssize_t len; .sun_path = INIT_SOCKET,
};
if (fd == -1) { sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (!fexist(FINIT_FIFO)) { if (-1 == sd)
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO); goto error;
return 1;
}
fd = open(FINIT_FIFO, O_RDWR); if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
if (-1 == fd) { close(sd);
perror("Failed opening " FINIT_FIFO); goto error;
return 1;
}
} }
len = write(fd, rq, sizeof(*rq)); return sd;
if (sizeof(*rq) == len) error:
result = 0; perror("Failed connecting to finit");
return -1;
return result;
} }
static int do_reload(void) int client_disconnect(void)
{ {
struct init_request rq = { int rc;
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RELOAD,
};
return do_send(&rq); if (sd < 0) {
} errno = EINVAL;
return -1;
static int set_env(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SETENV,
};
if (runlevel == 0) {
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
return do_send(&rq);
} }
return 0; rc = close(sd);
} sd = -1;
static int set_runlevel(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = runlevel,
};
return set_env(runlevel) || do_send(&rq);
}
/* telinit q | telinit <0-9> */
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n"
" -h, --help This help text\n"
" -V, --version Show Finit version\n\n"
"Commands:\n"
" 0 Power-off the system, same as initctl poweroff\n"
" 6 Reboot the system, same as initctl reboot\n"
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
" services in prev. runlevel that are not allowed in new.\n"
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
" sending SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", prognm);
return rc; return rc;
} }
int client(int argc, char *argv[]) int client_send(struct init_request *rq, ssize_t len)
{ {
int c; int sd, result = 255;
struct option long_options[] = {
{"help", 0, NULL, 'h'}, sd = client_connect();
{"version", 0, NULL, 'v'}, if (-1 == sd)
{NULL, 0, NULL, 0} return -1;
if (write(sd, rq, len) != len)
goto error;
if (read(sd, rq, len) != len)
goto error;
result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit:
client_disconnect();
return result;
}
svc_t *client_svc_iterator(int first)
{
int sd = -1;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_ITER,
}; };
static svc_t svc;
progname(argv[0]); sd = client_connect();
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) { if (sd == -1)
switch(c) { return NULL;
case 'v':
return puts("v" VERSION) == EOF;
case 'h': if (first)
case '?': rq.runlevel = 1;
return usage(0); else
} rq.runlevel = 0;
}
if (optind < argc) { if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
int req = (int)argv[optind][0]; goto error;
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
goto error;
if (isdigit(req)) client_disconnect();
return set_runlevel(req); if (svc.pid < 0)
return NULL;
if (req == 'q' || req == 'Q') return &svc;
return do_reload(); error:
perror("Failed communicating with finit");
client_disconnect();
sd = -1;
if (req == 's' || req == 'S') return NULL;
return set_runlevel(1); }
}
return usage(1); svc_t *client_svc_find(char *arg)
{
int sd = -1;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_FIND,
};
static svc_t svc;
sd = client_connect();
if (sd == -1)
return NULL;
strlcpy(rq.data, arg, sizeof(rq.data));
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
goto error;
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
goto error;
client_disconnect();
if (svc.pid < 0)
return NULL;
return &svc;
error:
client_disconnect();
perror("Failed communicating with finit");
sd = -1;
return NULL;
} }
/** /**
+11 -34
View File
@@ -1,6 +1,6 @@
/* Example HOOK_SVC_LOST plugin, very noisy do not use as-is! /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2015-2017 Joachim Nilsson <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,40 +21,17 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef FINIT_CLIENT_H_
#define FINIT_CLIENT_H_
#include "finit.h" #include "finit.h"
#include "svc.h" #include "svc.h"
#include "plugin.h"
static void lost_pid(void *arg) int client_connect (void);
{ int client_disconnect (void);
pid_t pid = (uintptr_t)arg;
svc_t *svc;
svc = svc_find_by_pid(pid); int client_send (struct init_request *rq, ssize_t len);
if (!svc) svc_t *client_svc_iterator (int first);
return; svc_t *client_svc_find (char *arg);
logit(LOG_INFO, "Lost PID %u, svc %s", pid, svc->cmd); #endif /* FINIT_CLIENT_H_ */
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_SVC_LOST] = { .cb = lost_pid },
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+113 -27
View File
@@ -1,6 +1,6 @@
/* Condition engine (write) /* Condition engine (write)
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,33 +25,84 @@
#include <libgen.h> #include <libgen.h>
#include <lite/lite.h> #include <lite/lite.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h>
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "pid.h"
#include "service.h" #include "service.h"
static int cond_set_gen(const char *file, unsigned int gen)
{
char *ptr, path[256];
FILE *fp;
int ret;
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "w");
if (!fp)
return -1;
ret = fprintf(fp, "%u\n", gen);
fclose(fp);
return (ret > 0) ? 0 : ret;
}
static void cond_bump_reconf(void)
{
unsigned int rgen;
/*
* If %COND_RECONF does not exist, cond_get_gen() returns 0
* meaning that rgen++ is always what we want.
*/
rgen = cond_get_gen(COND_RECONF);
rgen++;
cond_set_gen(COND_RECONF, rgen);
}
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) int cond_set_path(const char *path, enum cond_state new)
{ {
char buf[MAX_ARG_LEN], *dir;
enum cond_state old; enum cond_state old;
unsigned int rgen;
_d("%s", path);
rgen = cond_get_gen(COND_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
}
old = cond_get_path(path); old = cond_get_path(path);
switch (new) { switch (new) {
case COND_ON: case COND_ON:
strlcpy(buf, path, sizeof(buf)); if (cond_checkpath(path))
dir = dirname(buf); return 0;
if (!dir) { cond_set_gen(path, rgen);
_e("Invalid path '%s' for condition", path);
return 0;
}
if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 0;
}
touch(path);
utimensat(0, path, NULL, 0);
break; break;
case COND_OFF: case COND_OFF:
@@ -88,30 +139,53 @@ static int svc_has_cond(svc_t *svc)
static void cond_update(const char *name) static void cond_update(const char *name)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
_d("%s", name); _d("%s", name);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue; continue;
_d("%s: match <%s> %s", name ?: "nil", svc->cond, svc->cmd); _d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
service_step(svc); service_step(svc);
} }
} }
void cond_set(const char *name) void cond_set(const char *name)
{ {
_d("%s", name); _d("%s", name);
if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_ON)) if (!cond_set_path(cond_path(name), COND_ON))
return; return;
cond_update(name); 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) void cond_clear(const char *name)
{ {
_d("%s", name); _d("%s", name);
if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_OFF)) if (!cond_set_path(cond_path(name), COND_OFF))
return; return;
@@ -121,8 +195,8 @@ void cond_clear(const char *name)
void cond_reload(void) void cond_reload(void)
{ {
_d(""); _d("");
cond_set_path(COND_RECONF, COND_ON);
cond_bump_reconf();
cond_update(NULL); cond_update(NULL);
} }
@@ -136,7 +210,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
if (tflg != FTW_F) if (tflg != FTW_F)
return 0; 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); _d("Reasserting %s => %s", fpath, nm);
cond_set(nm); cond_set(nm);
@@ -149,15 +229,21 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
*/ */
void cond_reassert(const char *type) void cond_reassert(const char *type)
{ {
_d("%s", type ?: "reload done"); _d("%s", type);
if (!type) {
cond_set_path(COND_RECONF, COND_OFF);
return;
}
nftw(cond_path(type), reassert, 20, FTW_DEPTH); nftw(cond_path(type), reassert, 20, FTW_DEPTH);
} }
void cond_init(void)
{
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;
}
cond_bump_reconf();
}
/** /**
* Local Variables: * Local Variables:
+36 -15
View File
@@ -1,6 +1,6 @@
/* Condition engine (read) /* Condition engine (read)
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,15 +26,14 @@
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "pid.h"
#include "service.h" #include "service.h"
static inline int timespec_newer(const struct timespec *a,
const struct timespec *b)
{
if (a->tv_sec != b->tv_sec)
return a->tv_sec > b->tv_sec;
return a->tv_nsec > b->tv_nsec; 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) const char *condstr(enum cond_state s)
@@ -50,25 +49,47 @@ const char *condstr(enum cond_state s)
const char *cond_path(const char *name) const char *cond_path(const char *name)
{ {
char tmp[MAX_ARG_LEN];
static char file[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 *file)
{
char *ptr, path[256];
unsigned int gen;
FILE *fp;
int ret;
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "r");
if (!fp)
return 0;
ret = fscanf(fp, "%u", &gen);
fclose(fp);
return (ret == 1) ? gen : 0;
} }
enum cond_state cond_get_path(const char *path) enum cond_state cond_get_path(const char *path)
{ {
struct stat st, st_reconf; int cgen, rgen;
if (stat(path, &st)) rgen = cond_get_gen(COND_RECONF);
if (!rgen)
return COND_OFF; return COND_OFF;
if (stat(COND_RECONF, &st_reconf) || cgen = cond_get_gen(path);
timespec_newer(&st.st_mtim, &st_reconf.st_mtim)) if (!cgen)
return COND_ON; return COND_OFF;
return COND_FLUX; return (cgen == rgen) ? COND_ON : COND_FLUX;
} }
enum cond_state cond_get(const char *name) enum cond_state cond_get(const char *name)
+11 -7
View File
@@ -3,8 +3,8 @@
#include <paths.h> #include <paths.h>
#define COND_PATH _PATH_VARRUN "finit/cond" #define COND_DIR "finit/cond"
#define COND_SVC_PATH COND_PATH "/svc/" #define COND_PATH _PATH_VARRUN COND_DIR
#define COND_RECONF COND_PATH "/reconf" #define COND_RECONF COND_PATH "/reconf"
typedef enum cond_state { typedef enum cond_state {
@@ -13,18 +13,22 @@ typedef enum cond_state {
COND_ON COND_ON
} cond_state_t; } cond_state_t;
char *mkcond (char *buf, size_t len, char *nm);
const char *condstr (enum cond_state s); const char *condstr (enum cond_state s);
const char *cond_path (const char *name); const char *cond_path (const char *name);
unsigned int cond_get_gen (const char *path);
enum cond_state cond_get_path(const char *path); enum cond_state cond_get_path(const char *path);
enum cond_state cond_get (const char *name); enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names); enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names); int cond_affects (const char *name, const char *names);
int cond_set_path(const char *path, enum cond_state new); int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name); void cond_set (const char *name);
void cond_clear (const char *name); void cond_set_oneshot (const char *name);
void cond_reload (void); void cond_clear (const char *name);
void cond_reassert(const char *pat); void cond_reload (void);
void cond_reassert (const char *pat);
void cond_init (void);
#endif /* FINIT_COND_H_ */ #endif /* FINIT_COND_H_ */
+467 -130
View File
@@ -25,8 +25,10 @@
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <string.h>
#include <sys/inotify.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <lite/lite.h> #include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include <sys/time.h> #include <sys/time.h>
#include "finit.h" #include "finit.h"
@@ -34,35 +36,111 @@
#include "service.h" #include "service.h"
#include "tty.h" #include "tty.h"
#include "helpers.h" #include "helpers.h"
#include "util.h"
#define BOOTSTRAP (runlevel == 0)
#define MATCH_CMD(l, c, x) \ #define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c))) (!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
static int parse_conf(char *file); int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5;
struct rlimit global_rlimit[RLIMIT_NLIMITS];
struct conf_change {
TAILQ_ENTRY(conf_change) link;
char *name;
};
static uev_t w1, w2, w3;
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
static int parse_conf(char *file);
static void drop_changes(void);
void conf_parse_cmdline(void) void conf_parse_cmdline(void)
{ {
int dbg = 0; int dbg = 0;
FILE *fp; FILE *fp;
char line[LINE_SIZE], *cmdline, *tok;
fp = fopen("/proc/cmdline", "r"); fp = fopen("/proc/cmdline", "r");
if (fp) { if (!fp)
char line[LINE_SIZE]; goto done;
fgets(line, sizeof(line), fp);
chomp(line);
_d("%s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
dbg = 1;
if (!fgets(line, sizeof(line), fp)) {
fclose(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); 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 */ /* Convert optional "[!123456789S]" string into a bitmask */
int conf_parse_runlevels(char *runlevels) int conf_parse_runlevels(char *runlevels)
{ {
@@ -163,59 +241,101 @@ static const struct rlimit_name rlimit_names[] = {
{ NULL, 0 } { NULL, 0 }
}; };
void conf_parse_rlimit(char *line) int str2rlim(char *str)
{ {
struct rlimit rlim; const struct rlimit_name *rn;
rlim_t new, *set;
const struct rlimit_name *name;
int resource = -1;
char *tok = strtok(line, " \t"); for (rn = rlimit_names; rn->name; rn++) {
if (!strcmp(str, rn->name))
if (tok && !strcmp(tok, "soft")) return rn->val;
set = &rlim.rlim_cur;
else if (tok && !strcmp(tok, "hard"))
set = &rlim.rlim_max;
else
goto error;
tok = strtok(NULL, " \t");
if (!tok)
goto error;
for (name = rlimit_names; name->name; name++) {
if (!strcmp(tok, name->name))
resource = name->val;
} }
if (resource < 0) return -1;
goto fail; }
tok = strtok(NULL, " \t"); char *rlim2str(int rlim)
if (!tok) {
goto fail; const struct rlimit_name *rn;
if (!strcmp(tok, "infinity")) { for (rn = rlimit_names; rn->name; rn++) {
new = RLIM_INFINITY; if (rn->val == rlim)
return rn->name;
}
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;
level = strtok(line, " \t");
if (!level)
goto error;
limit = strtok(NULL, " \t");
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;
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
cfg = RLIM_INFINITY;
} else { } else {
const char *err = NULL; const char *err = NULL;
new = strtonum(tok, 0, (long long)2 << 31, &err); cfg = strtonum(val, 0, (long long)2 << 31, &err);
if (err) if (err) {
goto fail; logit(LOG_WARNING, "rlimit: invalid %s value: %s",
rlim2str(resource), val);
return;
}
} }
if (getrlimit(resource, &rlim)) if (!strcmp(level, "soft"))
goto fail; 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;
*set = new;
if (setrlimit(resource, &rlim))
goto fail;
return;
fail:
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
return; return;
error: error:
logit(LOG_WARNING, "rlimit: parse error"); logit(LOG_WARNING, "rlimit: parse error");
@@ -226,13 +346,13 @@ static void parse_static(char *line)
char *x; char *x;
char cmd[CMD_SIZE]; char cmd[CMD_SIZE];
if (MATCH_CMD(line, "host ", x)) { if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) {
if (hostname) free(hostname); if (hostname) free(hostname);
hostname = strdup(strip_line(x)); hostname = strdup(strip_line(x));
return; return;
} }
if (MATCH_CMD(line, "mknod ", x)) { if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) {
char *dev = strip_line(x); char *dev = strip_line(x);
strcpy(cmd, "mknod "); strcpy(cmd, "mknod ");
@@ -242,13 +362,13 @@ static void parse_static(char *line)
return; return;
} }
if (MATCH_CMD(line, "network ", x)) { if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) {
if (network) free(network); if (network) free(network);
network = strdup(strip_line(x)); network = strdup(strip_line(x));
return; return;
} }
if (MATCH_CMD(line, "runparts ", x)) { if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
if (runparts) free(runparts); if (runparts) free(runparts);
runparts = strdup(strip_line(x)); runparts = strdup(strip_line(x));
return; return;
@@ -267,16 +387,38 @@ static void parse_static(char *line)
return; 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 (MATCH_CMD(line, "shutdown ", x)) {
if (sdown) free(sdown); if (sdown) free(sdown);
sdown = strdup(strip_line(x)); sdown = strdup(strip_line(x));
return; return;
} }
/* The desired runlevel to start when leaving bootstrap (S). /*
* The desired runlevel to start when leaving bootstrap (S).
* Finit supports 1-9, but most systems only use 1-6, where * Finit supports 1-9, but most systems only use 1-6, where
* 6 is reserved for reboot */ * 6 is reserved for reboot and 0 for halt/poweroff.
if (MATCH_CMD(line, "runlevel ", x)) { */
if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) {
char *token = strip_line(x); char *token = strip_line(x);
const char *err = NULL; const char *err = NULL;
@@ -289,10 +431,9 @@ static void parse_static(char *line)
} }
} }
static void parse_dynamic(char *line, struct timeval *mtime) static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
{ {
char *x; char *x;
char cmd[CMD_SIZE];
/* Skip comments, i.e. lines beginning with # */ /* Skip comments, i.e. lines beginning with # */
if (MATCH_CMD(line, "#", x)) if (MATCH_CMD(line, "#", x))
@@ -300,41 +441,32 @@ static void parse_dynamic(char *line, struct timeval *mtime)
/* Kernel module to load at bootstrap */ /* Kernel module to load at bootstrap */
if (MATCH_CMD(line, "module ", x)) { if (MATCH_CMD(line, "module ", x)) {
char *mod; kmod_load(x);
if (runlevel != 0)
return;
mod = strip_line(x);
strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
return; return;
} }
/* Monitored daemon, will be respawned on exit */ /* Monitored daemon, will be respawned on exit */
if (MATCH_CMD(line, "service ", x)) { if (MATCH_CMD(line, "service ", x)) {
service_register(SVC_TYPE_SERVICE, x, mtime); service_register(SVC_TYPE_SERVICE, x, rlimit, file);
return; return;
} }
/* One-shot task, will not be respawned */ /* One-shot task, will not be respawned */
if (MATCH_CMD(line, "task ", x)) { if (MATCH_CMD(line, "task ", x)) {
service_register(SVC_TYPE_TASK, x, mtime); service_register(SVC_TYPE_TASK, x, rlimit, file);
return; return;
} }
/* Like task but waits for completion, useful w/ [S] */ /* Like task but waits for completion, useful w/ [S] */
if (MATCH_CMD(line, "run ", x)) { if (MATCH_CMD(line, "run ", x)) {
service_register(SVC_TYPE_RUN, x, mtime); service_register(SVC_TYPE_RUN, x, rlimit, file);
return; return;
} }
/* Classic inetd service */ /* Classic inetd service */
if (MATCH_CMD(line, "inetd ", x)) { if (MATCH_CMD(line, "inetd ", x)) {
#ifdef INETD_ENABLED #ifdef INETD_ENABLED
service_register(SVC_TYPE_INETD, x, mtime); service_register(SVC_TYPE_INETD, x, rlimit, file);
#else #else
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x); _e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
#endif #endif
@@ -343,20 +475,13 @@ static void parse_dynamic(char *line, struct timeval *mtime)
/* Read resource limits */ /* Read resource limits */
if (MATCH_CMD(line, "rlimit ", x)) { if (MATCH_CMD(line, "rlimit ", x)) {
conf_parse_rlimit(x); conf_parse_rlimit(x, rlimit);
return;
}
/* Special TTY console powers */
if (MATCH_CMD(line, "console ", x)) {
if (console) free(console);
console = strdup(strip_line(x));
return; return;
} }
/* Regular or serial TTYs to run getty */ /* Regular or serial TTYs to run getty */
if (MATCH_CMD(line, "tty ", x)) { if (MATCH_CMD(line, "tty ", x)) {
tty_register(strip_line(x), mtime); tty_register(strip_line(x), rlimit, file);
return; return;
} }
} }
@@ -372,9 +497,10 @@ static void tabstospaces(char *line)
} }
} }
static int parse_conf_dynamic(char *file, struct timeval *mtime) static int parse_conf_dynamic(char *file)
{ {
FILE *fp; FILE *fp;
struct rlimit rlimit[RLIMIT_NLIMITS];
fp = fopen(file, "r"); fp = fopen(file, "r");
if (!fp) { if (!fp) {
@@ -382,7 +508,10 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
return 1; return 1;
} }
_d("Parsing %s", file); /* Prepare default limits for each service */
memcpy(rlimit, global_rlimit, sizeof(rlimit));
_d("Parsing %s <<<<<<", file);
while (!feof(fp)) { while (!feof(fp)) {
char line[LINE_SIZE] = ""; char line[LINE_SIZE] = "";
@@ -393,7 +522,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
tabstospaces(line); tabstospaces(line);
_d("%s", line); _d("%s", line);
parse_dynamic(line, mtime); parse_dynamic(line, rlimit, file);
} }
fclose(fp); fclose(fp);
@@ -405,34 +534,19 @@ static int parse_conf(char *file)
{ {
FILE *fp; FILE *fp;
char line[LINE_SIZE] = ""; char line[LINE_SIZE] = "";
char *x;
/*
* Get current global limits, which may be overridden from both
* finit.conf, for Finit and its services like inetd+getty, and
* *.conf in finit.d/, for each service(s) listed there.
*/
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
fp = fopen(file, "r"); fp = fopen(file, "r");
if (!fp) if (!fp)
return 1; return 1;
/*
* If not standard finit.conf, then we want to show just the base name
* Loading configuration ............. vs
* Loading services configuration ....
*/
if (!string_match (file, FINIT_CONF)) {
/* Remove leading path */
x = strrchr(file, '/');
if (!x) x = file;
else x++;
/* Remove ending .conf */
strlcpy(line, x, sizeof(line));
x = strstr(line, ".conf");
if (x) *x = 0;
/* Add empty space. */
strcat(line, " ");
}
print(0, "Loading %sconfiguration", line);
_d("Parsing %s", file); _d("Parsing %s", file);
while (!feof(fp)) { while (!feof(fp)) {
if (!fgets(line, sizeof(line), fp)) if (!fgets(line, sizeof(line), fp))
@@ -443,29 +557,54 @@ static int parse_conf(char *file)
_d("%s", line); _d("%s", line);
parse_static(line); parse_static(line);
parse_dynamic(line, NULL); parse_dynamic(line, global_rlimit, NULL);
} }
fclose(fp); fclose(fp);
/* 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: %s",
rlim2str(i), lim2str(&global_rlimit[i]));
}
return 0; return 0;
} }
/* Reload all *.conf in /etc/finit.d/ */ /*
int conf_reload_dynamic(void) * Reload /etc/finit.conf and all *.conf in /etc/finit.d/
*/
int conf_reload(void)
{ {
int i, num; int i, num;
char *dir = rcsd;
struct dirent **e; struct dirent **e;
/* Mark and sweep */ /* Mark and sweep */
svc_mark_dynamic(); svc_mark_dynamic();
tty_mark(); tty_mark();
num = scandir(dir, &e, NULL, alphasort); 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);
/* Next, read all *.conf in /etc/finit.d/ */
num = scandir(rcsd, &e, NULL, alphasort);
if (num < 0) { if (num < 0) {
_d("Skipping %s, no files found ...", dir); _d("Skipping %s, no files found ...", rcsd);
return 1; goto done;
} }
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
@@ -473,13 +612,11 @@ int conf_reload_dynamic(void)
char path[LINE_SIZE]; char path[LINE_SIZE];
size_t len; size_t len;
struct stat st; struct stat st;
struct timeval mtime;
snprintf(path, sizeof(path), "%s/%s", dir, name); snprintf(path, sizeof(path), "%s/%s", rcsd, name);
/* Check that it's an actual file ... beyond any symlinks */ /* Check that it's an actual file ... beyond any symlinks */
if (lstat(path, &st)) { if (lstat(path, &st)) {
lost:
_d("Skipping %s, cannot access: %s", path, strerror(errno)); _d("Skipping %s, cannot access: %s", path, strerror(errno));
continue; continue;
} }
@@ -496,10 +633,6 @@ int conf_reload_dynamic(void)
rp = realpath(path, NULL); rp = realpath(path, NULL);
if (!rp) { if (!rp) {
/*
* XXX: Prune from rcsd?
* XXX: Possibly temporary service from last boot
*/
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno)); logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
continue; continue;
} }
@@ -514,27 +647,231 @@ int conf_reload_dynamic(void)
continue; continue;
} }
if (stat(path, &st)) parse_conf_dynamic(path);
goto lost; /* Weird, lost file between here and lstat() */
TIMESPEC_TO_TIMEVAL(&mtime, &st.st_mtim);
parse_conf_dynamic(path, &mtime);
} }
while (num--) while (num--)
free(e[num]); free(e[num]);
free(e); 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"
*/
set_hostname(&hostname); set_hostname(&hostname);
return 0; return 0;
} }
int conf_parse_config(void) static struct conf_change *conf_find(char *file)
{
struct conf_change *node, *tmp;
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) {
if (string_compare(node->name, file))
return node;
}
return NULL;
}
static void drop_change(struct conf_change *node)
{
if (!node)
return;
TAILQ_REMOVE(&conf_change_list, node, link);
free(node->name);
free(node);
}
static void drop_changes(void)
{
struct conf_change *node, *tmp;
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp)
drop_change(node);
}
static int do_change(char *name, uint32_t mask)
{
struct conf_change *node;
node = conf_find(name);
if (mask & (IN_DELETE | IN_MOVED_FROM)) {
drop_change(node);
return 0;
}
if (node) {
_d("Event already registered for %s ...", name);
return 0;
}
node = malloc(sizeof(*node));
if (!node)
return 1;
node->name = strdup(name);
if (!node->name) {
free(node);
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)
return 0;
if ((ptr = strrchr(file, '/')))
file = ++ptr;
if (conf_find(file))
return 1;
return 0;
}
static void conf_cb(uev_t *w, void *arg, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz, len;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
ev = (struct inotify_event *)ev_buf;
if (arg) {
do_change(arg, ev->mask);
return;
}
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
if (do_change(ev->name, ev->mask)) {
_pe("conf_monitor: Out of memory");
break;
}
}
}
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
{
struct stat st;
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE;
char *arg = NULL;
int fd, wd;
if (!ctx)
return 0;
if (stat(path, &st)) {
_d("No such file or directory, skipping %s", path);
w->fd = -1;
return 0;
}
if (!S_ISDIR(st.st_mode)) {
arg = strrchr(path, '/');
if (!arg)
arg = path;
else
arg++;
}
if (w->fd >= 0)
close(w->fd);
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd < 0) {
_pe("Failed creating inotify descriptor");
w->fd = -1;
return 1;
}
/*
* Only forward error, don't report error,
* user may not have @path and that's OK
*/
wd = inotify_add_watch(fd, path, mask | opt);
if (wd < 0) {
w->fd = -1;
close(fd);
return 1;
}
if (uev_io_init(ctx, w, conf_cb, arg, fd, UEV_READ)) {
_pe("Failed setting up I/O callback for %s watcher", path);
w->fd = -1;
close(fd);
return 1;
}
return 0;
}
/*
* Set up inotify watcher and load all *.conf in /etc/finit.d/
*/
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
* have or not have symlinks in place. We need to monitor for
* changes to either symlink or target.
*/
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW);
rc += add_watcher(ctx, &w3, FINIT_CONF, 0);
return rc + conf_reload();
}
/*
* Prepare .conf parser and load all .conf files
*/
int conf_init(void)
{ {
hostname = strdup(DEFHOST); hostname = strdup(DEFHOST);
w1.fd = w2.fd = w3.fd = -1;
return parse_conf(FINIT_CONF) || conf_reload_dynamic(); return conf_monitor(NULL);
} }
/** /**
+14 -2
View File
@@ -26,11 +26,23 @@
#include "svc.h" #include "svc.h"
extern int logfile_size_max;
extern int logfile_count_max;
extern struct rlimit global_rlimit[];
int str2rlim(char *str);
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);
void conf_parse_cmdline (void); void conf_parse_cmdline (void);
int conf_parse_runlevels (char *runlevels); int conf_parse_runlevels (char *runlevels);
void conf_parse_cond (svc_t *svc, char *cond); void conf_parse_cond (svc_t *svc, char *cond);
int conf_parse_config (void);
void conf_reload_dynamic (void);
#endif /* FINIT_CONF_H_ */ #endif /* FINIT_CONF_H_ */
+121 -17
View File
@@ -30,17 +30,18 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
#include <lite/lite.h> #include <lite/lite.h>
#include "finit.h" #include "finit.h"
#include "conf.h"
#include "helpers.h" #include "helpers.h"
#include "sig.h" #include "sig.h"
#include "utmp-api.h" #include "utmp-api.h"
#define NUM_ARGS 16 #define NUM_ARGS 16
int getty(char *tty, char *baud, char *term, char *user);
/* Wait for process completion, returns status of waitpid(2) syscall */ /* Wait for process completion, returns status of waitpid(2) syscall */
int complete(char *cmd, int pid) int complete(char *cmd, int pid)
@@ -234,30 +235,77 @@ int exec_runtask(char *cmd, char *args[])
return execvp(_PATH_BSHELL, argv); return execvp(_PATH_BSHELL, argv);
} }
static void prepare_tty(char *tty, char *procname, int console) static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
{ {
struct sigaction sa;
struct termios term;
/*
* Reset to sane defaults in case of messup from prev. session
*/
stty(STDIN_FILENO, speed);
/*
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
* It is up to the getty process to allow them again.
*/
tcdrain(STDIN_FILENO);
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_lflag &= ~ISIG;
term.c_cc[VEOF] = _POSIX_VDISABLE;
term.c_cc[VINTR] = _POSIX_VDISABLE;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
/* Reset signal handlers that were set by the parent process */ /* Reset signal handlers that were set by the parent process */
sig_unblock(); sig_unblock();
/*
* Ignore a few signals, needed to prevent Ctrl-C at login:
* prompt and to prevent QUIT from dumping core.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
/* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &rlimit[i]) == -1)
logit(LOG_WARNING, "%s: rlimit: Failed setting %s", tty, rlim2str(i));
}
/* Create new session and process group */
setsid(); setsid();
/* Set INIT_PROCESS UTMP entry */ /* Finit is responsible for the UTMP INIT_PROCESS record */
utmp_set_init(tty, 0); utmp_set_init(tty, 0);
if (console)
procname = "console";
prctl(PR_SET_NAME, procname, 0, 0, 0); prctl(PR_SET_NAME, procname, 0, 0, 0);
} }
static int activate_console(int noclear, int nowait) static int activate_console(int noclear, int nowait)
{ {
int ret = 0;
struct termios term;
if (!noclear) if (!noclear)
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9); (void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
if (nowait) if (nowait)
return 1; return 1;
/* Disable ECHO, XON/OFF while waiting for <CR> */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_iflag &= ~(IXON|IXOFF);
term.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
while (!fexist(SYNC_SHUTDOWN)) { while (!fexist(SYNC_SHUTDOWN)) {
char c; char c;
static const char clr[] = "\r\e[2K";
static const char msg[] = "\nPlease press Enter to activate this console."; static const char msg[] = "\nPlease press Enter to activate this console.";
if (fexist(SYNC_STOPPED)) { if (fexist(SYNC_STOPPED)) {
@@ -265,26 +313,63 @@ static int activate_console(int noclear, int nowait)
continue; continue;
} }
(void)write(STDERR_FILENO, msg, sizeof(msg)); (void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, msg, strlen(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n') while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
continue; continue;
if (fexist(SYNC_STOPPED)) if (fexist(SYNC_STOPPED))
continue; continue;
return 1; (void)write(STDERR_FILENO, clr, strlen(clr));
ret = 1;
break;
} }
return 0; /* Restore ECHO, XON/OFF */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_iflag |= IXON|IXOFF;
term.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
return ret;
} }
pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int console) /*
* Start a getty on @tty
*
* At the login: prompt, no signals are allowed, both Ctrl-C and Ctrl-D
* should be disabled. Ctrl-S and Ctrl-Q are optional, but most getty
* allow them.
*
* Prior to getty is called and login: is printed, Finit may display the
* "Please press Enter ..." if @nowait is unset. This mode must be RAW,
* only accepting <CR> and not echoing anything, this also means no
* signals are allowed. For ease of implementation Finit will call the
* stty() function to reset the TTY and then force RAW mode until a <CR>
* has been received. This is handled in the same fashion for both this
* function and run_getty2(), which is used for external getty.
*
* When handing over to /bin/login, Ctrl-C and Ctrl-D must be enabled
* since /bin/login usually only disables ECHO until a password line has
* been entered. Upon starting the user's $SHELL the ISIG flag is reset
*/
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
{ {
pid_t pid; pid_t pid;
pid = fork(); pid = fork();
if (!pid) { if (!pid) {
prepare_tty(tty, "finit-getty", console); speed_t speed = B38400;
if (baud && baud[0]) {
speed = stty_parse_speed(baud);
if (B0 == speed)
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
}
prepare_tty(tty, speed, "finit-getty", rlimit);
if (activate_console(noclear, nowait)) if (activate_console(noclear, nowait))
_exit(getty(tty, speed, term, NULL)); _exit(getty(tty, speed, term, NULL));
} }
@@ -292,7 +377,7 @@ pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int
return pid; return pid;
} }
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, int console) pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
{ {
pid_t pid; pid_t pid;
@@ -321,7 +406,8 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, in
dup2(fd, STDOUT_FILENO); dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
prepare_tty(tty, "getty", console); /* Dunno speed, tell stty() to not mess with it */
prepare_tty(tty, B0, "getty", rlimit);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
_pe("Failed TIOCSCTTY"); _pe("Failed TIOCSCTTY");
@@ -337,6 +423,20 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, in
return pid; 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) int run_parts(char *dir, char *cmd)
{ {
struct dirent **e; struct dirent **e;
@@ -351,14 +451,18 @@ int run_parts(char *dir, char *cmd)
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
int j = 0; int j = 0;
pid_t pid = 0; pid_t pid = 0;
mode_t mode; struct stat st;
char *args[NUM_ARGS]; char *args[NUM_ARGS];
char *name = e[i]->d_name; char *name = e[i]->d_name;
char path[LINE_SIZE]; char path[LINE_SIZE];
snprintf(path, sizeof(path), "%s/%s", dir, name); snprintf(path, sizeof(path), "%s/%s", dir, name);
mode = fmode(path); if (stat(path, &st)) {
if (!S_ISEXEC(mode) || S_ISDIR(mode)) { _d("Failed stat(%s): %s", path, strerror(errno));
continue;
}
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
_d("Skipping %s ...", path); _d("Skipping %s ...", path);
continue; continue;
} }
+235 -189
View File
@@ -36,6 +36,7 @@
#include <lite/lite.h> #include <lite/lite.h>
#include "finit.h" #include "finit.h"
#include "cond.h"
#include "conf.h" #include "conf.h"
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
@@ -46,20 +47,25 @@
#include "tty.h" #include "tty.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
#include "watchdog.h" #include "schedule.h"
int wdogpid = 0; /* No watchdog by default */
int runlevel = 0; /* Bootstrap 'S' */ int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */ int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int prevlevel = -1; int prevlevel = -1;
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 *sdown = NULL;
char *network = NULL; char *network = NULL;
char *hostname = NULL; char *hostname = NULL;
char *rcsd = FINIT_RCSD; char *rcsd = FINIT_RCSD;
char *runparts = NULL; char *runparts = NULL;
char *console = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */ 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 * Show user configured banner before service bootstrap progress
@@ -78,17 +84,16 @@ static void banner(void)
return; return;
memset(separator, '=', sizeof(separator)); 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 ismnt(char *file, char *dir)
static int fismnt(char *dir)
{ {
FILE *fp; FILE *fp;
int found = 0; int found = 0;
struct mntent *mnt; struct mntent *mnt;
fp = setmntent("/proc/mounts", "r"); fp = setmntent(file, "r");
if (!fp) if (!fp)
return 0; /* Dunno, maybe not */ return 0; /* Dunno, maybe not */
@@ -103,12 +108,18 @@ static int fismnt(char *dir)
return found; 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 * Check all filesystems in /etc/fstab with a fs_passno > 0
*/ */
static int fsck(int pass) static int fsck(int pass)
{ {
int save; // int save;
struct fstab *fs; struct fstab *fs;
if (!setfsent()) { if (!setfsent()) {
@@ -116,8 +127,8 @@ static int fsck(int pass)
return 1; return 1;
} }
if ((save = log_is_debug())) // if ((save = log_is_debug()))
log_debug(); // log_debug();
while ((fs = getfsent())) { while ((fs = getfsent())) {
char cmd[80]; char cmd[80];
@@ -143,61 +154,13 @@ static int fsck(int pass)
run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec); run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
} }
if (save) // if (save)
log_debug(); // log_debug();
endfsent(); endfsent();
return 0; 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 * If everything goes south we can use this to give the operator an
* emergency shell to debug the problem -- Finit should not crash! * 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, "\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 * Become session leader and set controlling TTY
@@ -241,12 +204,122 @@ static void emergency_shell(void)
#endif /* EMERGENCY_SHELL */ #endif /* EMERGENCY_SHELL */
} }
/*
* Handle bootstrap transition to configured runlevel, start TTYs
*
* This is the final stage of bootstrap. It changes to the default
* (configured) runlevel, calls all external start scripts and final
* bootstrap hooks before bringing up TTYs.
*
* We must ensure that all declared `task [S]` and `run [S]` jobs in
* finit.conf, or *.conf in finit.d/, run to completion before we
* finalize the bootstrap process by calling this function.
*/
static void finalize(void)
{
svc_t *svc;
/*
* Track bundled watchdogd in case a better one turns up
*/
svc = svc_find(FINIT_LIBPATH_ "/watchdogd", 1);
if (svc)
wdog = svc;
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel, start all services ...");
service_runlevel(cfglevel);
/* Clean up bootstrap-only tasks/services that never started */
_d("Clean up all bootstrap-only tasks/services ...");
svc_prune_bootstrap();
/* All services/tasks/inetd/etc. in configure runlevel have started */
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
service_step_all(SVC_TYPE_ANY);
/* Convenient SysV compat for when you just don't care ... */
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
/* Hooks that should run at the very end */
_d("Calling all system up hooks ...");
plugin_run_hooks(HOOK_SYSTEM_UP);
service_step_all(SVC_TYPE_ANY);
/* Enable silent mode before starting TTYs */
_d("Going silent ...");
log_silent();
/* Delayed start of TTYs at bootstrap */
_d("Launching all getty services ...");
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[]) 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 *path;
char cmd[256]; char cmd[256];
int ret, udev = 0;
uev_ctx_t loop;
/* /*
* finit/init/telinit client tool uses /dev/initctl pipe * finit/init/telinit client tool uses /dev/initctl pipe
@@ -255,29 +328,17 @@ int main(int argc, char* argv[])
if (getpid() != 1) if (getpid() != 1)
return client(argc, argv); 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. * Initalize event context.
*/ */
uev_init(&loop); uev_init1(&loop, 1);
ctx = &loop; ctx = &loop;
/* /*
* Set the PATH early to something sane * Set PATH and SHELL early to something sane
*/ */
setenv("PATH", _PATH_STDPATH, 1); setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
/* /*
* Mount base file system, kernel is assumed to run devtmpfs for /dev * Mount base file system, kernel is assumed to run devtmpfs for /dev
@@ -290,32 +351,50 @@ int main(int argc, char* argv[])
mount("none", "/proc/bus/usb", "usbfs", 0, NULL); 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(); 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 * Load plugins early, finit.conf may contain references to
* features implemented by plugins. * features implemented by plugins.
*/ */
ret = plugin_init(&loop, PLUGIN_PATH); plugin_init(&loop);
/* /*
* Hello world. * Hello world.
*/ */
banner(); banner();
/* Show results after banner() */
print(ret, "Loading plugins");
/* /*
* Check file filesystems in /etc/fstab * Check file filesystems in /etc/fstab
*/ */
for (int pass = 1; pass < 10; pass++) { for (int pass = 1; pass < 10 && !rescue; pass++) {
if (fsck(pass)) if (fsck(pass))
break; 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 * Some non-embedded systems without an initramfs may not have /dev mounted yet
* If they do, check if system has udevadm and perform cleanup from initramfs * If they do, check if system has udevadm and perform cleanup from initramfs
@@ -329,8 +408,14 @@ int main(int argc, char* argv[])
makedir("/dev/pts", 0755); makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620"); mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
/*
* 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); makedir("/dev/shm", 0755);
if (!fismnt("/dev/shm")) if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm"))
mount("shm", "/dev/shm", "tmpfs", 0, NULL); mount("shm", "/dev/shm", "tmpfs", 0, NULL);
/* /*
@@ -341,151 +426,112 @@ int main(int argc, char* argv[])
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%"); mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
umask(022); umask(022);
/* Bootstrap conditions, needed for hooks */
cond_init();
/* /*
* Populate /dev and prepare for runtime events from kernel. * 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) { 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 */ /* Desktop and server distros usually have a variant of udev */
path = which("udevd"); udev = 1;
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
udev = 1;
/* Register udevd as a monitored service, started much later */ /* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[12345] %s -- Device event manager daemon", path); snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, NULL)) { if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path); _pe("Failed registering %s", path);
udev = 0; 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), ":2 [S] <svc%s> "
snprintf(cmd, sizeof(cmd), "%s --daemon", path); "udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
} }
}
if (path) {
free(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"); snprintf(cmd, sizeof(cmd), "%s -s", path);
if (udev && whichp("udevadm")) { free(path);
run("udevadm trigger --action=add --type=subsystems");
run("udevadm trigger --action=add --type=devices");
run("udevadm settle --timeout=120");
/* Tell temporary udevd to exit, we'll start a monitored instance later */ run_interactive(cmd, "Populating device tree");
run("udevadm control --exit");
} }
} }
/* /*
* Parse /etc/finit.conf and all *.conf in /etc/finit.d/ to * Start bundled watchdogd as soon as possible, if enabled
* figure out how to bootstrap the system.
*/ */
conf_parse_config(); if (which(FINIT_LIBPATH_ "/watchdogd"))
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd", global_rlimit, NULL);
/*
* Start built-in watchdog as soon as possible, if enabled
*/
watchdog(argv[0]);
/* Set hostname as soon as possible, for syslog et al. */
set_hostname(&hostname);
/* /*
* Mount filesystems * Mount filesystems
*/ */
if (!rescue) {
#ifdef REMOUNT_ROOTFS #ifdef REMOUNT_ROOTFS
run("mount -n -o remount,rw /"); run("mount -n -o remount,rw /");
#endif #endif
#ifdef SYSROOT #ifdef SYSROOT
mount(SYSROOT, "/", NULL, MS_MOVE, NULL); mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif #endif
}
/* Debian has this little script to copy generated rules while the system was read-only */ if (!rescue) {
if (fexist("/lib/udev/udev-finish")) _d("Root FS up, calling hooks ...");
run_interactive("/lib/udev/udev-finish", "Finalizing udev"); plugin_run_hooks(HOOK_ROOTFS_UP);
_d("Root FS up, calling hooks ..."); umask(0);
plugin_run_hooks(HOOK_ROOTFS_UP); if (run_interactive("mount -na", "Mounting filesystems"))
plugin_run_hooks(HOOK_MOUNT_ERROR);
umask(0); _d("Calling extra mount hook, after mount -a ...");
if (run_interactive("mount -na", "Mounting filesystems")) plugin_run_hooks(HOOK_MOUNT_POST);
plugin_run_hooks(HOOK_MOUNT_ERROR);
run("swapon -ea"); run("swapon -ea");
umask(0022); umask(0022);
}
/* Base FS up, enable standard SysV init signals */ /* Base FS up, enable standard SysV init signals */
sig_setup(&loop); sig_setup(&loop);
_d("Base FS up, calling hooks ..."); if (!rescue) {
plugin_run_hooks(HOOK_BASEFS_UP); _d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
}
/* /*
* Initalize state machine and start all bootstrap tasks * Set up inotify watcher for /etc/finit.d and read all .conf
* NOTE: no network available! * files to figure out how to bootstrap the system.
*/ */
sm_init(&sm); conf_monitor(&loop);
sm_step(&sm);
/* _d("Starting initctl API responder ...");
* Network stuff api_init(&loop);
*/
networking();
umask(022); umask(022);
/* Hooks that rely on loopback, or basic networking being up. */ _d("Starting the big state machine ...");
plugin_run_hooks(HOOK_NETWORK_UP); schedule_work(&crank);
_d("Starting bootstrap finalize timer ...");
schedule_work(&final);
/* /*
* Start all tasks/services in the configured runlevel * Enter main loop to monitor /dev/initctl and services
*/
service_runlevel(cfglevel);
/* Clean up bootstrap-only tasks/services that never started */
svc_prune_bootstrap();
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
/*
* 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)) {
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
service_reload_dynamic();
}
/* Hooks that should run at the very end */
plugin_run_hooks(HOOK_SYSTEM_UP);
/* Enable silent mode before starting TTYs */
log_silent();
/* Delayed start of TTYs at bootstrap */
tty_runlevel();
/* Start new initctl API responder */
api_init(&loop);
/*
* Enter main loop to monior /dev/initctl and services
*/ */
_d("Entering main loop ...");
return uev_run(&loop, 0); return uev_run(&loop, 0);
} }
+6 -2
View File
@@ -78,6 +78,9 @@
#define INIT_CMD_EMIT 15 #define INIT_CMD_EMIT 15
#define INIT_CMD_GET_RUNLEVEL 16 #define INIT_CMD_GET_RUNLEVEL 16
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */ #define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
#define INIT_CMD_SVC_ITER 129
#define INIT_CMD_SVC_QUERY 130
#define INIT_CMD_SVC_FIND 131
#define INIT_CMD_NACK 254 #define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255 #define INIT_CMD_ACK 255
@@ -89,16 +92,17 @@ struct init_request {
char data[368]; char data[368];
}; };
extern int wdogpid;
extern int runlevel; extern int runlevel;
extern int cfglevel; extern int cfglevel;
extern int prevlevel; extern int prevlevel;
extern int rescue;
extern int single;
extern int splash;
extern char *rcsd; extern char *rcsd;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
extern char *hostname; extern char *hostname;
extern char *runparts; extern char *runparts;
extern char *console;
extern uev_ctx_t *ctx; extern uev_ctx_t *ctx;
#endif /* FINIT_H_ */ #endif /* FINIT_H_ */
+63 -117
View File
@@ -64,42 +64,6 @@ static int readch(char *tty)
return ch1 & 0xFF; return ch1 & 0xFF;
} }
static void stty(int fd, speed_t speed)
{
struct termios term;
tcdrain(fd);
if (tcgetattr(fd, &term))
return;
cfsetispeed(&term, speed);
cfsetospeed(&term, speed);
tcsetattr(fd, TCSAFLUSH, &term);
tcflush(fd, TCIOFLUSH);
/* Disable modem specific flags */
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
term.c_cflag &= ~CRTSCTS;
term.c_cflag |= CLOCAL;
/* Timeouts, minimum chars and default flags */
term.c_cc[VTIME] = 0;
term.c_cc[VMIN] = 1;
term.c_iflag = ICRNL|IXON|IXOFF;
term.c_oflag = OPOST|ONLCR;
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
/* Reset special characters to defaults */
term.c_cc[VINTR] = CTRL('C');
term.c_cc[VQUIT] = CTRL('\\');
term.c_cc[VEOF] = CTRL('D');
term.c_cc[VEOL] = '\n';
term.c_cc[VKILL] = CTRL('U');
term.c_cc[VERASE] = CERASE;
tcsetattr(fd, TCSANOW, &term);
}
/* /*
* Parse and display a line from /etc/issue * Parse and display a line from /etc/issue
*/ */
@@ -162,7 +126,6 @@ static void do_issue(char *tty)
*/ */
uname(&uts); uname(&uts);
print("\n");
fp = fopen("/etc/issue", "r"); fp = fopen("/etc/issue", "r");
if (fp) { if (fp) {
while (fgets(buf, sizeof(buf), fp)) while (fgets(buf, sizeof(buf), fp))
@@ -244,72 +207,10 @@ static int do_login(char *name)
return 1; /* We shouldn't get here ... */ return 1; /* We shouldn't get here ... */
} }
static speed_t do_parse_speed(char *baud) int getty(char *tty, speed_t speed, char *term, char *user)
{
char *ptr;
size_t i;
unsigned long val;
struct { unsigned long val; speed_t speed; } v2s[] = {
{ 0, B0 },
{ 50, B50 },
{ 75, B75 },
{ 110, B110 },
{ 134, B134 },
{ 150, B150 },
{ 200, B200 },
{ 300, B300 },
{ 600, B600 },
{ 1200, B1200 },
{ 1800, B1800 },
{ 2400, B2400 },
{ 4800, B4800 },
{ 9600, B9600 },
{ 19200, B19200 },
{ 38400, B38400 },
{ 57600, B57600 },
{ 115200, B115200 },
{ 230400, B230400 },
{ 460800, B460800 },
{ 500000, B500000 },
{ 576000, B576000 },
{ 921600, B921600 },
{ 1000000, B1000000 },
{ 1152000, B1152000 },
{ 1500000, B1500000 },
{ 2000000, B2000000 },
{ 2500000, B2500000 },
{ 3000000, B3000000 },
{ 3500000, B3500000 },
{ 4000000, B4000000 },
};
errno = 0;
val = strtoul(baud, &ptr, 10);
if (errno || ptr == baud)
return B0;
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
if (v2s[i].val == val)
return v2s[i].speed;
}
return B0;
}
int getty(char *tty, char *baud, char *term, char *user)
{ {
int fd; int fd;
char name[30]; char name[30];
speed_t speed = B38400;
struct sigaction sa;
if (baud) {
speed = do_parse_speed(baud);
if (speed == B0) {
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
return 1;
}
}
/* Detach from initial controlling TTY */ /* Detach from initial controlling TTY */
vhangup(); vhangup();
@@ -325,32 +226,77 @@ int getty(char *tty, char *baud, char *term, char *user)
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
warn("Failed TIOCSCTTY"); warn("Failed TIOCSCTTY");
/* /* The getty process is responsible for the UTMP login record */
* Ignore a few signals, needed to prevent Ctrl-C at login:
* prompt and to prevent QUIT from dumping core.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
/* Set up TTY */
stty(fd, speed);
close(fd);
utmp_set_login(tty, NULL); utmp_set_login(tty, NULL);
if (term)
setenv("TERM", term, 1);
if (!user) if (!user)
do_getty(tty, name, sizeof(name)); do_getty(tty, name, sizeof(name));
else else
strlcpy(name, user, sizeof(name)); strlcpy(name, user, sizeof(name));
/* Set up TTY, re-enabling ISIG et al. */
stty(fd, speed);
close(fd);
if (term && term[0])
setenv("TERM", term, 1);
return do_login(name); 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: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+79
View File
@@ -184,6 +184,9 @@ void set_hostname(char **hostname)
{ {
FILE *fp; FILE *fp;
if (rescue)
goto done;
fp = fopen("/etc/hostname", "r"); fp = fopen("/etc/hostname", "r");
if (fp) { if (fp) {
struct stat st; struct stat st;
@@ -204,10 +207,86 @@ void set_hostname(char **hostname)
fclose(fp); fclose(fp);
} }
done:
if (*hostname) if (*hostname)
sethostname(*hostname, strlen(*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 #ifndef HAVE_GETFSENT
static lfile_t *fstab = NULL; static lfile_t *fstab = NULL;
+12 -5
View File
@@ -26,7 +26,10 @@
#define FINIT_HELPERS_H_ #define FINIT_HELPERS_H_
#include <stdarg.h> #include <stdarg.h>
#include <sys/resource.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
#include <lite/lite.h> #include <lite/lite.h>
#include "log.h" #include "log.h"
@@ -48,25 +51,29 @@ struct fstab *getfsent (void);
char *strip_line (char *line); char *strip_line (char *line);
int pid_alive (pid_t pid); int getty (char *tty, speed_t speed, char *term, char *user);
char *pid_get_name (pid_t pid, char *name, size_t len); int sh (char *tty);
int stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud);
void printv (const char *fmt, va_list ap); void printv (const char *fmt, va_list ap);
void print (int action, const char *fmt, ...); void print (int action, const char *fmt, ...);
void print_desc (char *action, char *desc); void print_desc (char *action, char *desc);
int print_result (int fail); int print_result (int fail);
int start_process (char *cmd, char *args[], int console);
int getuser (char *username, char **home); int getuser (char *username, char **home);
int getgroup (char *group); int getgroup (char *group);
void set_hostname (char **hostname); void set_hostname (char **hostname);
void networking (int updown);
int complete (char *cmd, int pid); int complete (char *cmd, int pid);
int run (char *cmd); int run (char *cmd);
int run_interactive (char *cmd, char *fmt, ...); int run_interactive (char *cmd, char *fmt, ...);
int exec_runtask (char *cmd, char *args[]); int exec_runtask (char *cmd, char *args[]);
pid_t run_getty (char *tty, char *speed, char *term, int noclear, int nowait, int console); 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, int console); 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); int run_parts (char *dir, char *cmd);
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid) static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
+82 -22
View File
@@ -43,12 +43,13 @@
} while (0); } while (0);
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */ /* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
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 cmsghdr *cmsg;
struct msghdr msgh;
char cmbuf[0x100];
memset(ifname, 0, len);
memset(&msgh, 0, sizeof(msgh)); memset(&msgh, 0, sizeof(msgh));
msgh.msg_control = cmbuf; msgh.msg_control = cmbuf;
msgh.msg_controllen = sizeof(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) if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
return -1; 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); 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) if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
continue; continue;
if_indextoname(ipi->ipi_ifindex, ifname); if_indextoname(ipi->ipi_ifindex, tmp);
strlcpy(ifname, tmp, len);
return 0; return 0;
} }
@@ -70,13 +73,31 @@ static int inetd_dgram_peek(int sd, char *ifname)
return -1; return -1;
} }
/* 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[IF_NAMESIZE + 1];
char buf[BUFSIZ];
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;
}
break;
}
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */ /* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
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 ifaddrs *ifaddr, *ifa;
struct sockaddr_in sin; struct sockaddr_in sin;
socklen_t len = sizeof(sin); socklen_t len = sizeof(sin);
memset(ifname, 0, ilen);
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len)) if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
return -1; return -1;
@@ -95,7 +116,7 @@ static int inetd_stream_peek(int sd, char *ifname)
iin = (struct sockaddr_in *)ifa->ifa_addr; iin = (struct sockaddr_in *)ifa->ifa_addr;
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) { if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE); strlcpy(ifname, ifa->ifa_name, ilen);
break; break;
} }
} }
@@ -105,11 +126,12 @@ static int inetd_stream_peek(int sd, char *ifname)
return 0; 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; 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) { if (svc->inetd.type == SOCK_STREAM) {
/* Open new client socket from server socket */ /* Open new client socket from server socket */
stdin = accept(stdin, NULL, NULL); stdin = accept(stdin, NULL, NULL);
@@ -120,19 +142,24 @@ static int get_stdin(svc_t *svc)
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port); _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 */ } else { /* SOCK_DGRAM */
inetd_dgram_peek(stdin, ifname); inetd_dgram_peek(stdin, ifname, sizeof(ifname));
} }
if (!inetd_is_allowed(&svc->inetd, ifname)) { if (!inetd_is_allowed(&svc->inetd, ifname)) {
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port); logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
if (svc->inetd.type == SOCK_STREAM) if (svc->inetd.type == SOCK_STREAM)
close(stdin); close(stdin);
else
inetd_dgram_drop(stdin, ifname);
return -1; return -1;
} }
/* Return ingress interface */
strlcpy(iifname, ifname, len);
return stdin; return stdin;
} }
@@ -140,10 +167,17 @@ static int get_stdin(svc_t *svc)
static void socket_cb(uev_t *w, void *arg, int events) static void socket_cb(uev_t *w, void *arg, int events)
{ {
svc_t *svc = (svc_t *)arg, *task; svc_t *svc = (svc_t *)arg, *task;
const char *conn = " connection";
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
int stdin; int stdin;
_d("Got event on %s socket ...", svc->cmd); _d("%s: Got socket event ...", svc->cmd);
stdin = get_stdin(svc); if (UEV_ERROR == events) {
logit(LOG_INFO, "%s: Socket error, aborting: %m", svc->cmd);
return;
}
stdin = get_stdin(svc, iifname, sizeof(iifname));
if (stdin < 0) { if (stdin < 0) {
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd); logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
return; return;
@@ -184,11 +218,14 @@ static void socket_cb(uev_t *w, void *arg, int events)
task->inetd.cmd = svc->inetd.cmd; task->inetd.cmd = svc->inetd.cmd;
task->inetd.type = svc->inetd.type; task->inetd.type = svc->inetd.type;
memcpy(task->rlimit, svc->rlimit, sizeof(task->rlimit));
memcpy(task->cond, svc->cond, sizeof(task->cond)); memcpy(task->cond, svc->cond, sizeof(task->cond));
memcpy(task->username, svc->username, sizeof(task->username)); memcpy(task->username, svc->username, sizeof(task->username));
memcpy(task->group, svc->group, sizeof(task->group)); memcpy(task->group, svc->group, sizeof(task->group));
memcpy(task->args, svc->args, sizeof(task->args)); 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; task->stdin_fd = stdin;
service_step(task); service_step(task);
@@ -202,10 +239,10 @@ static void socket_cb(uev_t *w, void *arg, int events)
*/ */
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name) int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
char pname[NI_MAXHOST]; char pname[NI_MAXHOST];
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) { for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
inetd_t *i = &svc->inetd; inetd_t *i = &svc->inetd;
if (!i->builtin || i->type != SOCK_DGRAM) if (!i->builtin || i->type != SOCK_DGRAM)
@@ -308,20 +345,43 @@ int inetd_start(inetd_t *inetd)
return 0; 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) void inetd_stop(inetd_t *inetd)
{ {
if (!inetd || !inetd->svc) {
_e("Invalid inetd, cannot stop it...");
return;
}
if (inetd->watcher.fd != -1) { if (inetd->watcher.fd != -1) {
_d("Stopping %s socket watcher ...", inetd->svc->cmd); _d("Stopping %s socket watcher ...", inetd->svc->cmd);
uev_io_stop(&inetd->watcher); 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)) { if (!svc_is_busy(inetd->svc)) {
_d("Shutting down inet socket %d ...", inetd->watcher.fd); _d("Shutting down inet socket %d ...", inetd->watcher.fd);
if (inetd->type == SOCK_STREAM)
shutdown(inetd->watcher.fd, SHUT_RDWR);
close(inetd->watcher.fd); close(inetd->watcher.fd);
inetd->watcher.fd = -1; inetd->watcher.fd = -1;
inetd_stop_children(inetd, 0);
} }
} }
} }
@@ -642,9 +702,9 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
svc_t *inetd_find_svc(char *path, char *service, char *proto) svc_t *inetd_find_svc(char *path, char *service, char *proto)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) { for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
if (strncmp(path, svc->cmd, strlen(svc->cmd))) if (strncmp(path, svc->cmd, strlen(svc->cmd)))
continue; continue;
+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); int inetd_start (inetd_t *inetd);
void inetd_stop (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_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
int inetd_del (inetd_t *inetd); int inetd_del (inetd_t *inetd);
+107 -152
View File
@@ -33,11 +33,9 @@
#include <time.h> #include <time.h>
#include <utmp.h> #include <utmp.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <lite/lite.h> #include <lite/lite.h>
#include "finit.h" #include "client.h"
#include "cond.h" #include "cond.h"
#include "serv.h" #include "serv.h"
#include "service.h" #include "service.h"
@@ -45,78 +43,30 @@
#define _PATH_COND _PATH_VARRUN "finit/cond/" #define _PATH_COND _PATH_VARRUN "finit/cond/"
typedef struct { struct command {
char *cmd; char *cmd;
int (*cb)(char *arg); int (*cb)(char *arg);
} command_t; };
int verbose = 0; int verbose = 0;
int runlevel = 0; int runlevel = 0;
static int runlevel_get(int *prevlevel)
static int do_send(struct init_request *rq, ssize_t len)
{ {
int sd, result = 255; int result;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
return -1;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
goto error;
if (write(sd, rq, len) != len)
goto error;
if (read(sd, rq, len) != len)
goto error;
result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit:
close(sd);
return result;
}
static int runlevel_get(void)
{
int sd, result = 255;
struct init_request rq; struct init_request rq;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
memset(&rq, 0, sizeof(rq)); memset(&rq, 0, sizeof(rq));
rq.cmd = INIT_CMD_GET_RUNLEVEL; rq.cmd = INIT_CMD_GET_RUNLEVEL;
rq.magic = INIT_MAGIC; rq.magic = INIT_MAGIC;
sd = socket(AF_UNIX, SOCK_STREAM, 0); result = client_send(&rq, sizeof(rq));
if (-1 == sd) if (!result) {
return -1; result = rq.runlevel;
if (prevlevel)
*prevlevel = rq.sleeptime;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
goto error;
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
goto error;
if (read(sd, &rq, sizeof(rq)) != sizeof(rq))
goto error;
result = rq.runlevel;
goto exit;
error:
perror("Failed communicating with finit");
result = -1;
exit:
close(sd);
return result; return result;
} }
@@ -127,7 +77,7 @@ static int toggle_debug(char *arg)
.cmd = INIT_CMD_DEBUG, .cmd = INIT_CMD_DEBUG,
}; };
return do_send(&rq, sizeof(rq)); return client_send(&rq, sizeof(rq));
} }
static int do_log(char *svc) static int do_log(char *svc)
@@ -146,6 +96,9 @@ static int do_log(char *svc)
return system(cmd); return system(cmd);
} }
/*
* XXX: Convert to read UTMP instead, like runlevel(8) does
*/
static int do_runlevel(char *arg) static int do_runlevel(char *arg)
{ {
struct init_request rq = { struct init_request rq = {
@@ -154,14 +107,25 @@ static int do_runlevel(char *arg)
.runlevel = (int)arg[0], .runlevel = (int)arg[0],
}; };
/* Not compatible with the SysV runlevel(8) command, it prints
* "PREVLEVEL RUNLEVEL", we just print the current runlevel. */
if (!rq.runlevel) { if (!rq.runlevel) {
printf("%d\n", runlevel_get()); char prev;
int runlevel, prevlevel;
runlevel = runlevel_get(&prevlevel);
if (255 == runlevel) {
printf("unknown\n");
return 0;
}
prev = prevlevel + '0';
if (prev <= '0' || prev > '9')
prev = 'N';
printf("%c %d\n", prev , runlevel);
return 0; return 0;
} }
return do_send(&rq, sizeof(rq)); return client_send(&rq, sizeof(rq));
} }
static int do_svc(int cmd, char *arg) static int do_svc(int cmd, char *arg)
@@ -173,27 +137,12 @@ static int do_svc(int cmd, char *arg)
strlcpy(rq.data, arg, sizeof(rq.data)); strlcpy(rq.data, arg, sizeof(rq.data));
return do_send(&rq, sizeof(rq)); return client_send(&rq, sizeof(rq));
} }
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); } static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); } static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
static int missing(char *job, int id)
{
char idstr[10] = "";
if (!job)
job = "";
if (id > 1)
snprintf(idstr, sizeof(idstr), ":%d", id);
if (verbose)
fprintf(stderr, "No such service %s%s\n", job, idstr);
return 1;
}
/* /*
* This is a wrapper for do_svc() that adds a simple sanity check of * This is a wrapper for do_svc() that adds a simple sanity check of
* the service(s) provided as argument. If a service does not exist * the service(s) provided as argument. If a service does not exist
@@ -201,28 +150,20 @@ static int missing(char *job, int id)
*/ */
static int do_startstop(int cmd, char *arg) static int do_startstop(int cmd, char *arg)
{ {
int ret; struct init_request rq = {
char *svcs; .magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_QUERY
};
if (!arg) { strlcpy(rq.data, arg, sizeof(rq.data));
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]", if (client_send(&rq, sizeof(rq))) {
fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data);
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]\n",
cmd == INIT_CMD_START_SVC ? "start" : cmd == INIT_CMD_START_SVC ? "start" :
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart")); (cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
return 1; return 1;
} }
svcs = strdup(arg);
if (!svcs) {
perror("Failed validating service(s) name");
return 1;
}
/* Include \0 in len, needed by parser */
ret = svc_parse_jobstr(svcs, strlen(svcs) + 1, NULL, missing);
free(svcs);
if (ret)
return ret;
return do_svc(cmd, arg); return do_svc(cmd, arg);
} }
@@ -265,6 +206,9 @@ static int do_cond_magic(char op, char *cond)
{ {
char event[368]; /* sizeof(init_request.data) */ char event[368]; /* sizeof(init_request.data) */
if (!cond || strlen(cond) < 1)
return 1;
event[0] = op; event[0] = op;
strlcpy(&event[1], cond, sizeof(event) - 1); strlcpy(&event[1], cond, sizeof(event) - 1);
@@ -281,7 +225,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
if (tflag != FTW_F) if (tflag != FTW_F)
return 0; return 0;
if (!strcmp(fpath, _PATH_COND"reconf")) if (!strcmp(fpath, _PATH_COND "reconf"))
return 0; return 0;
len = strlen(_PATH_COND); len = strlen(_PATH_COND);
@@ -309,7 +253,7 @@ static int do_cond_show(char *arg)
printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0); printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
if (!svc->cond[0]) if (!svc->cond[0])
continue; continue;
@@ -333,10 +277,10 @@ static int do_cond(char *cmd)
{ {
int c; int c;
char *arg; char *arg;
command_t command[] = { struct command command[] = {
{ "show", do_cond_show },
{ "set", do_cond_set }, { "set", do_cond_set },
{ "clear", do_cond_clear }, { "clear", do_cond_clear },
{ "show", do_cond_show },
{ "dump", do_cond_dump }, { "dump", do_cond_dump },
{ NULL, NULL } { NULL, NULL }
}; };
@@ -346,7 +290,7 @@ static int do_cond(char *cmd)
*arg++ = 0; *arg++ = 0;
for (c = 0; command[c].cmd; c++) { for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, cmd)) if (string_match(command[c].cmd, cmd))
return command[c].cb(arg); return command[c].cb(arg);
} }
@@ -431,7 +375,7 @@ static int show_version(char *arg)
char *runlevel_string(int runlevel, int levels) char *runlevel_string(int runlevel, int levels)
{ {
int i, pos = 1; int i, pos = 1;
static char lvl[20]; static char lvl[21];
memset(lvl, 0, sizeof(lvl)); memset(lvl, 0, sizeof(lvl));
lvl[0] = '['; lvl[0] = '[';
@@ -459,53 +403,49 @@ char *runlevel_string(int runlevel, int levels)
return lvl; return lvl;
} }
/* In verbose mode we skip the header and each service description. /*
* In verbose mode we skip the header and each service description.
* This in favor of having all info on one line so a machine can more * This in favor of having all info on one line so a machine can more
* easily parse it. */ * easily parse it.
*/
static int show_status(char *arg) static int show_status(char *arg)
{ {
svc_t *svc; svc_t *svc;
/* Fetch UTMP runlevel, needed for svc_status() call below */ /* Fetch UTMP runlevel, needed for svc_status() call below */
runlevel = runlevel_get(); runlevel = runlevel_get(NULL);
if (arg && arg[0]) { if (arg && arg[0]) {
int id = 1; long now = jiffies();
char *ptr, *next; char buf[42] = "N/A";
ptr = strchr(arg, ':');
if (ptr) {
*ptr++ = 0;
next = strchr(ptr, ' ');
if (next)
*next = 0;
id = atonum(ptr);
}
if (isdigit(arg[0]))
svc = svc_find_by_jobid(atonum(arg), id);
else
svc = svc_find_by_nameid(arg, id);
svc = client_svc_find(arg);
if (!svc) if (!svc)
return 1; return 1;
printf("%s\n", svc_status(svc)); printf("Service : %s\n", svc->cmd);
return 0; 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");
return do_log(svc->cmd);
} }
if (!verbose) if (!verbose)
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0); printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char jobid[10], args[512] = "", *lvls; char jobid[10], args[512] = "", *lvls;
if (svc_is_unique(svc)) // if (svc_is_unique(svc))
snprintf(jobid, sizeof(jobid), "%d", svc->job); // snprintf(jobid, sizeof(jobid), "%d", svc->job);
else // else
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id); snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
printf("%-5s %7s ", jobid, svc_status(svc)); printf("%-9s %7s ", jobid, svc_status(svc));
if (svc_is_inetd(svc)) if (svc_is_inetd(svc))
printf("inetd "); printf("inetd ");
else else
@@ -526,7 +466,7 @@ static int show_status(char *arg)
else else
name++; name++;
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc); printf("%-16.16s %-*.*s\n", name, adj, adj, svc->desc);
continue; continue;
} }
@@ -539,7 +479,7 @@ static int show_status(char *arg)
}; };
snprintf(rq.data, sizeof(rq.data), "%s", jobid); snprintf(rq.data, sizeof(rq.data), "%s", jobid);
if (do_send(&rq, sizeof(rq))) { if (client_send(&rq, sizeof(rq))) {
snprintf(args, sizeof(args), "Unknown inetd"); snprintf(args, sizeof(args), "Unknown inetd");
info = args; info = args;
} else { } else {
@@ -548,7 +488,7 @@ static int show_status(char *arg)
info = rq.data; info = rq.data;
info[len - 1] = 0; info[len - 1] = 0;
if (strcmp("internal", svc->cmd)) { if (!string_match("internal", svc->cmd)) {
char *ptr; char *ptr;
ptr = strchr(info, ' '); ptr = strchr(info, ' ');
@@ -579,9 +519,10 @@ static int show_status(char *arg)
static int usage(int rc) static int usage(int rc)
{ {
fprintf(stderr, fprintf(stderr,
"Usage: %s [OPTIONS] <COMMAND>\n" "Usage: %s [OPTIONS] [COMMAND]\n"
"\n" "\n"
"Options:\n" "Options:\n"
" -b, --batch Batch mode, no screen size probing\n"
" -v, --verbose Verbose output\n" " -v, --verbose Verbose output\n"
" -h, --help This help text\n" " -h, --help This help text\n"
"\n" "\n"
@@ -593,7 +534,9 @@ static int usage(int rc)
" list List all .conf in /etc/finit.d/\n" " list List all .conf in /etc/finit.d/\n"
" enable <CONF> Enable .conf in /etc/finit.d/available/\n" " enable <CONF> Enable .conf in /etc/finit.d/available/\n"
" disable <CONF> Disable .conf in /etc/finit.d/[enabled/]\n" " disable <CONF> Disable .conf in /etc/finit.d/[enabled/]\n"
" touch <CONF> Mark .conf in /etc/finit.d/ for reload\n"
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n" " reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
"\n" "\n"
" cond set <COND> Set (assert) condition => +COND\n" " cond set <COND> Set (assert) condition => +COND\n"
" cond clear <COND> Clear (deassert) condition => -COND\n" " cond clear <COND> Clear (deassert) condition => -COND\n"
@@ -604,7 +547,8 @@ static int usage(int rc)
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n" " start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n" " stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n" " restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
" status | show Show status of services\n" " status <JOB|NAME>[:ID] Show service status, by job# or name\n"
" status | show Show status of services, default command\n"
"\n" "\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n" " runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n" " reboot Reboot system\n"
@@ -624,8 +568,9 @@ static int do_help(char *arg)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int c; int interactive = 1, c;
command_t command[] = { char *cmd, arg[120];
struct command command[] = {
{ "debug", toggle_debug }, { "debug", toggle_debug },
{ "help", do_help }, { "help", do_help },
{ "version", show_version }, { "version", show_version },
@@ -633,6 +578,7 @@ int main(int argc, char *argv[])
{ "list", serv_list }, { "list", serv_list },
{ "enable", serv_enable }, { "enable", serv_enable },
{ "disable", serv_disable }, { "disable", serv_disable },
{ "touch", serv_touch },
{ "reload", do_reload }, { "reload", do_reload },
{ "cond", do_cond }, { "cond", do_cond },
@@ -653,6 +599,7 @@ int main(int argc, char *argv[])
{ NULL, NULL } { NULL, NULL }
}; };
struct option long_options[] = { struct option long_options[] = {
{"batch", 0, NULL, 'b'},
{"help", 0, NULL, 'h'}, {"help", 0, NULL, 'h'},
{"debug", 0, NULL, 'd'}, {"debug", 0, NULL, 'd'},
{"verbose", 0, NULL, 'v'}, {"verbose", 0, NULL, 'v'},
@@ -660,8 +607,12 @@ int main(int argc, char *argv[])
}; };
progname(argv[0]); 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) { switch(c) {
case 'b':
interactive = 0;
break;
case 'h': case 'h':
case '?': case '?':
return usage(0); return usage(0);
@@ -672,21 +623,25 @@ int main(int argc, char *argv[])
} }
} }
screen_init(); if (interactive)
if (optind < argc) { screen_init();
char *cmd = argv[optind++];
char arg[120] = "";
while (optind < argc) { if (optind >= argc)
strlcat(arg, argv[optind++], sizeof(arg)); return show_status(NULL);
if (optind < argc)
strlcat(arg, " ", sizeof(arg));
}
for (c = 0; command[c].cmd; c++) { memset(arg, 0, sizeof(arg));
if (!strcmp(command[c].cmd, cmd)) cmd = argv[optind++];
return command[c].cb(arg); while (optind < argc) {
} strlcat(arg, argv[optind++], sizeof(arg));
if (optind < argc)
strlcat(arg, " ", sizeof(arg));
}
for (c = 0; command[c].cmd; c++) {
if (!string_match(command[c].cmd, cmd))
continue;
return command[c].cb(arg);
} }
return usage(1); return usage(1);
+55 -29
View File
@@ -29,12 +29,22 @@
#include "finit.h" #include "finit.h"
#include "log.h" #include "log.h"
#include "util.h"
static int up = 0; static int up = 0;
static int debug = 0; static int debug = 0;
static int silent = SILENT_MODE; /* Completely silent, including boot */ static int silent = SILENT_MODE; /* Completely silent, including boot */
static int loglevel = LOG_NOTICE; 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) void log_init(int dbg)
{ {
if (dbg) if (dbg)
@@ -44,16 +54,27 @@ void log_init(int dbg)
loglevel = LOG_DEBUG; loglevel = LOG_DEBUG;
else else
loglevel = LOG_NOTICE; loglevel = LOG_NOTICE;
muffler();
} }
/* If we enabled terse mode at boot, restore to previous setting at shutdown */ /* If we enabled terse mode at boot, restore to previous setting at shutdown */
void log_exit(void) void log_exit(void)
{ {
silent = SILENT_MODE; muffler();
if (!silent) { if (!silent) {
sched_yield(); sched_yield();
fputs("\n", stderr); 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) void log_open(void)
@@ -63,7 +84,7 @@ void log_open(void)
closelog(); closelog();
if (debug) if (debug)
opts |= LOG_CONS | LOG_PERROR; opts |= LOG_PERROR; /* LOG_CONS | */
openlog("finit", opts, LOG_DAEMON); openlog("finit", opts, LOG_DAEMON);
setlogmask(LOG_UPTO(loglevel)); setlogmask(LOG_UPTO(loglevel));
@@ -91,12 +112,19 @@ void log_debug(void)
if (debug) { if (debug) {
silent = 0; silent = 0;
loglevel = LOG_DEBUG; loglevel = LOG_DEBUG;
screen_exit();
} else { } else {
silent = SILENT_MODE; silent = SILENT_MODE;
loglevel = LOG_NOTICE; loglevel = LOG_NOTICE;
screen_init();
} }
log_open(); log_open();
/* Also toggle silent mode (progress) */
log_silent();
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled"); logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
} }
@@ -105,45 +133,43 @@ int log_is_debug(void)
return debug; return debug;
} }
static void early_logit(int prio, const char *fmt, va_list ap)
{
FILE *fp;
if (!debug && prio > loglevel)
return;
fp = fopen("/dev/kmsg", "w");
if (fp) {
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
vfprintf(fp, fmt, ap);
fclose(fp);
if (debug)
vfprintf(stderr, fmt, ap);
} else {
vfprintf(stderr, fmt, ap);
}
}
/* /*
* Log to /dev/kmsg until syslogd has started, then openlog() * Log to /dev/kmsg until syslogd has started, then openlog()
* and continue logging as a regular daemon. * and continue logging as a regular daemon.
*/ */
void logit(int prio, const char *fmt, ...) void logit(int prio, const char *fmt, ...)
{ {
FILE *fp;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
if (!up) {
if (!fexist("/dev/log")) {
early_logit(prio, fmt, ap);
goto done;
}
log_open(); if (up || fexist("/dev/log")) {
if (!up)
log_open();
vsyslog(prio, fmt, ap);
goto done;
}
if (!debug && prio > loglevel)
goto done;
fp = fopen("/dev/kmsg", "w");
if (!fp) {
vfprintf(stderr, fmt, ap);
goto done;
}
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
vfprintf(fp, fmt, ap);
fclose(fp);
if (debug) {
va_end(ap);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
} }
vsyslog(prio, fmt, ap);
done: done:
va_end(ap); va_end(ap);
} }
+1 -1
View File
@@ -33,7 +33,7 @@
* errors and debug messages. For all other user messages, see logit() * errors and debug messages. For all other user messages, see logit()
* *
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages, * 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 _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%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-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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,11 +23,14 @@
*/ */
#include <errno.h> #include <errno.h>
#include <paths.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <lite/lite.h> #include <lite/lite.h>
#include "pid.h"
#include "svc.h"
#include "helpers.h" #include "helpers.h"
@@ -90,6 +93,117 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
return pname; 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: * Local Variables:
* indent-tabs-mode: t * 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:
*/
+27 -6
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit /* Plugin based services architecture for finit
* *
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2012-2017 Joachim Nilsson <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -30,10 +30,12 @@
#include <lite/queue.h> /* BSD sys/queue.h API */ #include <lite/queue.h> /* BSD sys/queue.h API */
#include "config.h" #include "config.h"
#include "cond.h"
#include "finit.h" #include "finit.h"
#include "private.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "private.h"
#include "service.h"
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0) #define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
#define SEARCH_PLUGIN(str) \ #define SEARCH_PLUGIN(str) \
@@ -106,6 +108,9 @@ int plugin_register(plugin_t *plugin)
/* Not called, at the moment plugins cannot be unloaded. */ /* Not called, at the moment plugins cannot be unloaded. */
int plugin_unregister(plugin_t *plugin) int plugin_unregister(plugin_t *plugin)
{ {
if (is_io_plugin(plugin))
uev_io_stop(&plugin->watcher);
#ifndef ENABLE_STATIC #ifndef ENABLE_STATIC
TAILQ_REMOVE(&plugins, plugin, link); TAILQ_REMOVE(&plugins, plugin, link);
@@ -163,6 +168,15 @@ plugin_t *plugin_find(char *name)
} }
/* Private daemon API *******************************************************/ /* Private daemon API *******************************************************/
#define CHOOSE(x, y) y
static const char *hook_cond[] = HOOK_TYPES;
#undef CHOOSE
const char *plugin_hook_str(hook_point_t no)
{
return hook_cond[no];
}
int plugin_exists(hook_point_t no) int plugin_exists(hook_point_t no)
{ {
plugin_t *p, *tmp; plugin_t *p, *tmp;
@@ -175,7 +189,7 @@ int plugin_exists(hook_point_t no)
return 0; 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) void plugin_run_hook(hook_point_t no, void *arg)
{ {
plugin_t *p, *tmp; plugin_t *p, *tmp;
@@ -186,6 +200,9 @@ void plugin_run_hook(hook_point_t no, void *arg)
p->hook[no].cb(arg ? arg : p->hook[no].arg); p->hook[no].cb(arg ? arg : p->hook[no].arg);
} }
} }
cond_set_oneshot(hook_cond[no]);
service_step_all(SVC_TYPE_RUNTASK);
} }
/* Regular hooks are called with the registered plugin's argument */ /* Regular hooks are called with the registered plugin's argument */
@@ -194,7 +211,11 @@ void plugin_run_hooks(hook_point_t no)
plugin_run_hook(no, NULL); plugin_run_hook(no, NULL);
} }
/* Generic libev I/O callback, looks up correct plugin and calls its callback */ /*
* Generic libev I/O callback, looks up correct plugin and calls its
* callback. libuEv might return UEV_ERROR in events, it is up to the
* plugin callback to handle this.
*/
static void generic_io_cb(uev_t *w, void *arg, int events) static void generic_io_cb(uev_t *w, void *arg, int events)
{ {
plugin_t *p = (plugin_t *)arg; plugin_t *p = (plugin_t *)arg;
@@ -349,11 +370,11 @@ static int load_plugins(char *path)
} }
#endif /* ENABLE_STATIC */ #endif /* ENABLE_STATIC */
int plugin_init(uev_ctx_t *ctx, char *path) int plugin_init(uev_ctx_t *ctx)
{ {
int fail = 1; int fail = 1;
if (!load_plugins(path)) if (!load_plugins(PLUGIN_PATH))
fail = init_plugins(ctx); fail = init_plugins(ctx);
return fail; return fail;
+44 -25
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit /* Plugin based services architecture for finit
* *
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2012-2017 Joachim Nilsson <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,13 +29,17 @@
#include "svc.h" #include "svc.h"
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
#define PLUGIN_PATH "/dev/null"
#endif
#define PLUGIN_DEP_MAX 10 #define PLUGIN_DEP_MAX 10
/*
* Event flags for I/O plugins
*/
#define PLUGIN_IO_ERROR UEV_ERROR
#define PLUGIN_IO_READ UEV_READ #define PLUGIN_IO_READ UEV_READ
#define PLUGIN_IO_WRITE UEV_WRITE #define PLUGIN_IO_WRITE UEV_WRITE
#define PLUGIN_IO_PRI UEV_PRI
#define PLUGIN_IO_HUP UEV_HUP
#define PLUGIN_IO_RDHUP UEV_RDHUP
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void) #define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void) #define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
@@ -43,28 +47,43 @@
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp) #define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
/* /*
* Predefined hook points for easier plugin debugging * 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.
*/ */
typedef enum { #define HOOK_TYPES { \
/* Bootstrap hooks */ /* Bootstrap hooks, runlevel [S] */ \
HOOK_BANNER = 0, CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
HOOK_ROOTFS_UP, CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
HOOK_MOUNT_ERROR, CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
HOOK_BASEFS_UP, CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
HOOK_NETWORK_UP, CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
HOOK_SVC_UP, CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
HOOK_SYSTEM_UP, CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
\
/* Runtime hooks, runlevel [S1-9] */ \
CHOOSE(HOOK_SVC_RECONF, "nop"), \
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
\
/* Shutdown hooks, runlevel [06] */ \
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
CHOOSE(HOOK_MAX_NUM, "nop") \
}
/* Runtime hooks */ #define CHOOSE(x, y) x
HOOK_SVC_RECONF, typedef enum HOOK_TYPES hook_point_t;
HOOK_SVC_LOST, #undef CHOOSE
HOOK_SVC_START,
HOOK_RUNLEVEL_CHANGE,
/* Shutdown hooks */
HOOK_SHUTDOWN,
HOOK_MAX_NUM
} hook_point_t;
/** /**
* plugin_t - Finit &plugin_t object * plugin_t - Finit &plugin_t object
+2 -6
View File
@@ -28,11 +28,6 @@
#include "svc.h" #include "svc.h"
#include "plugin.h" #include "plugin.h"
/*
* Main loop context
*/
uev_ctx_t *ctx;
int api_init (uev_ctx_t *ctx); int api_init (uev_ctx_t *ctx);
int api_exit (void); int api_exit (void);
@@ -40,11 +35,12 @@ int client (int argc, char *argv[]);
void service_monitor (pid_t lost); void service_monitor (pid_t lost);
const char *plugin_hook_str(hook_point_t no);
int plugin_exists (hook_point_t no); int plugin_exists (hook_point_t no);
void plugin_run_hook (hook_point_t no, void *arg); void plugin_run_hook (hook_point_t no, void *arg);
void plugin_run_hooks (hook_point_t no); void plugin_run_hooks (hook_point_t no);
int plugin_init (uev_ctx_t *ctx, char *path); int plugin_init (uev_ctx_t *ctx);
void plugin_exit (void); void plugin_exit (void);
#endif /* FINIT_PRIVATE_H_ */ #endif /* FINIT_PRIVATE_H_ */
+1 -1
View File
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
if (c) if (c)
err(1, "Failed signalling init to %s", msg); err(1, "Failed signalling init to %s", msg);
do_sleep(2); do_sleep(5);
return 0; return 0;
} }
+3
View File
@@ -0,0 +1,3 @@
runlevel 1
hostname rescue
tty [12345] @console noclear nologin
+69
View File
@@ -0,0 +1,69 @@
/* 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.
*/
#include "config.h"
#include "finit.h"
#include "schedule.h"
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
/*
* libuEv callback wrapper
*/
static void cb(uev_t *w, void *arg, int events)
{
struct wq *work = (struct wq *)arg;
if (UEV_ERROR == events) {
uev_timer_start(w);
return;
}
work->cb(work);
}
/*
* Place work on event queue
*/
int schedule_work(struct wq *work)
{
int msec;
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);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+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_ */
+34
View File
@@ -24,6 +24,7 @@
#include "config.h" #include "config.h"
#include <err.h> #include <err.h>
#include <fcntl.h> /* Definition of AT_* constants */
#include <glob.h> #include <glob.h>
#include <stdio.h> #include <stdio.h>
#include <lite/lite.h> #include <lite/lite.h>
@@ -130,6 +131,9 @@ int serv_enable(char *arg)
char link[80]; char link[80];
char path[80]; char path[80];
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) { if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg); snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr; arg = corr;
@@ -153,6 +157,9 @@ int serv_disable(char *arg)
char link[80]; char link[80];
struct stat st; struct stat st;
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) { if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg); snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr; arg = corr;
@@ -168,6 +175,33 @@ int serv_disable(char *arg)
return remove(link) != 0; return remove(link) != 0;
} }
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;
}
pushd(FINIT_RCSD);
if (!fexist(arg)) {
popd();
if (!strstr(arg, "finit.conf"))
errx(1, "Service %s is not enabled", arg);
arg = FINIT_CONF;
}
/* libite:touch() follows symlinks */
if (utimensat(AT_FDCWD, arg, NULL, AT_SYMLINK_NOFOLLOW))
err(1, "Failed marking %s for reload", arg);
return 0;
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+1
View File
@@ -27,6 +27,7 @@
int serv_list (char *arg); int serv_list (char *arg);
int serv_enable (char *arg); int serv_enable (char *arg);
int serv_disable (char *arg); int serv_disable (char *arg);
int serv_touch (char *arg);
#endif /* FINIT_SERV_H_ */ #endif /* FINIT_SERV_H_ */
+452 -167
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -28,14 +28,17 @@
#include "svc.h" #include "svc.h"
void service_runlevel (int newlevel); void service_runlevel (int newlevel);
int service_register (int type, char *line, struct timeval *mtime); int service_register (int type, char *line, struct rlimit rlimit[], char *file);
void service_unregister (svc_t *svc); void service_unregister (svc_t *svc);
int service_enabled (svc_t *svc);
void service_runtask_clean (void);
void service_reload_dynamic (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_step_all (int types);
void service_worker (void *unused);
int service_completed (void);
#endif /* FINIT_SERVICE_H_ */ #endif /* FINIT_SERVICE_H_ */
+60 -3
View File
@@ -27,6 +27,8 @@
* *
* SIGHUP * SIGHUP
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/ * 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 * SIGUSR1
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running * Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
@@ -81,6 +83,8 @@
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
extern svc_t *wdog;
/* /*
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT", * Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to * "=POWERDOWN", or "" to cancel shutdown, before requesting change to
@@ -131,8 +135,12 @@ void do_kill(int signo)
continue; continue;
if (fgets(file, sizeof(file), fp)) { if (fgets(file, sizeof(file), fp)) {
if (file[0] != '@') if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file);
else if (file[0] != '@')
kill(pid, signo); kill(pid, signo);
else
_d("Skipping %s ...", &file[1]);
} }
fclose(fp); fclose(fp);
} }
@@ -179,6 +187,11 @@ void do_shutdown(shutop_t op)
return; return;
} }
if (wdog) {
print(kill(wdog->pid, SIGPWR) == 1, "Advising watchdog, system going down");
do_sleep(2);
}
/* Unmount any tmpfs before unmounting swap ... */ /* Unmount any tmpfs before unmounting swap ... */
unmount_tmpfs(); unmount_tmpfs();
run("swapoff -e -a"); run("swapoff -e -a");
@@ -197,11 +210,11 @@ void do_shutdown(shutop_t op)
/* Reboot via watchdog or kernel, or shutdown? */ /* Reboot via watchdog or kernel, or shutdown? */
if (op == SHUT_REBOOT) { if (op == SHUT_REBOOT) {
if (wdogpid) { if (wdog) {
int timeout = 10; int timeout = 10;
/* Wait here until the WDT reboots, or timeout with fallback */ /* 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--) while (timeout--)
do_sleep(1); do_sleep(1);
} }
@@ -224,6 +237,15 @@ void do_shutdown(shutop_t op)
static void sighup_cb(uev_t *w, void *arg, int events) static void sighup_cb(uev_t *w, void *arg, int events)
{ {
_d("..."); _d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
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 */ /* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
service_reload_dynamic(); service_reload_dynamic();
} }
@@ -234,6 +256,11 @@ static void sighup_cb(uev_t *w, void *arg, int events)
static void sigint_cb(uev_t *w, void *arg, int events) static void sigint_cb(uev_t *w, void *arg, int events)
{ {
_d("..."); _d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); service_runlevel(6);
} }
@@ -244,6 +271,11 @@ static void sigint_cb(uev_t *w, void *arg, int events)
static void sigusr1_cb(uev_t *w, void *arg, int events) static void sigusr1_cb(uev_t *w, void *arg, int events)
{ {
_d("..."); _d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_HALT; halt = SHUT_HALT;
service_runlevel(0); service_runlevel(0);
} }
@@ -254,6 +286,11 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
static void sigusr2_cb(uev_t *w, void *arg, int events) static void sigusr2_cb(uev_t *w, void *arg, int events)
{ {
_d("..."); _d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_OFF; halt = SHUT_OFF;
service_runlevel(0); service_runlevel(0);
} }
@@ -264,6 +301,11 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
static void sigterm_cb(uev_t *w, void *arg, int events) static void sigterm_cb(uev_t *w, void *arg, int events)
{ {
_d("..."); _d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); service_runlevel(6);
} }
@@ -275,6 +317,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
{ {
pid_t pid; pid_t pid;
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
/* Reap all the children! */ /* Reap all the children! */
do { do {
pid = waitpid(-1, NULL, WNOHANG); pid = waitpid(-1, NULL, WNOHANG);
@@ -290,6 +337,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
*/ */
static void sigstop_cb(uev_t *w, void *arg, int events) static void sigstop_cb(uev_t *w, void *arg, int events)
{ {
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
touch(SYNC_STOPPED); touch(SYNC_STOPPED);
stopped++; stopped++;
} }
@@ -299,6 +351,11 @@ static void sigstop_cb(uev_t *w, void *arg, int events)
*/ */
static void sigcont_cb(uev_t *w, void *arg, int events) static void sigcont_cb(uev_t *w, void *arg, int events)
{ {
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
stopped = 0; stopped = 0;
erase(SYNC_STOPPED); erase(SYNC_STOPPED);
} }
+28 -14
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) void sm_set_runlevel(sm_t *sm, int newlevel)
{ {
sm->newlevel = newlevel; sm->newlevel = newlevel;
@@ -95,7 +111,8 @@ void sm_step(sm_t *sm)
restart: restart:
old_state = sm->state; old_state = sm->state;
_d("state: %s", sm_status(sm->state)); _d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d",
sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload);
switch (sm->state) { switch (sm->state) {
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
@@ -136,16 +153,16 @@ restart:
runlevel_set(prevlevel, runlevel); runlevel_set(prevlevel, runlevel);
/* Disable login in single-user mode as well as shutdown/reboot */ /* Disable login in single-user mode as well as shutdown/reboot */
if (runlevel == 1 || runlevel == 0 || runlevel == 6) nologin();
touch("/etc/nologin");
else
erase("/etc/nologin");
/* Make sure to (re)load all *.conf in /etc/finit.d/ */ /* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (runlevel != 0 && runlevel != 6) if (conf_any_change())
conf_reload_dynamic(); conf_reload();
_d("Stopping services services not allowed in new runlevel ..."); /* Reset once flag of runtasks */
service_runtask_clean();
_d("Stopping services not allowed in new runlevel ...");
sm->in_teardown = 1; sm->in_teardown = 1;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -167,7 +184,7 @@ restart:
_d("All services have been stoppped, calling runlevel change hooks ..."); _d("All services have been stoppped, calling runlevel change hooks ...");
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ 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; sm->in_teardown = 0;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -195,13 +212,13 @@ restart:
case SM_RELOAD_CHANGE_STATE: case SM_RELOAD_CHANGE_STATE:
/* First reload all *.conf in /etc/finit.d/ */ /* First reload all *.conf in /etc/finit.d/ */
conf_reload_dynamic(); conf_reload();
/* /*
* Then, mark all affected service conditions as in-flux and * Then, mark all affected service conditions as in-flux and
* let all affected services move to WAITING/HALTED * 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; sm->in_teardown = 1;
cond_reload(); cond_reload();
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
@@ -231,9 +248,6 @@ restart:
_d("Calling reconf hooks ..."); _d("Calling reconf hooks ...");
plugin_run_hooks(HOOK_SVC_RECONF); plugin_run_hooks(HOOK_SVC_RECONF);
/* Done, lift reconf condition */
cond_reassert(NULL);
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
_d("Reconfiguration done"); _d("Reconfiguration done");
+125
View File
@@ -0,0 +1,125 @@
/* Reset terminal line settings (stty sane)
*
* Copyright (c) 2016-2017 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <stdlib.h>
#include <unistd.h> /* _POSIX_VDISABLE */
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
speed_t stty_parse_speed(char *baud)
{
char *ptr;
size_t i;
unsigned long val;
struct { unsigned long val; speed_t speed; } v2s[] = {
{ 0, B0 },
{ 50, B50 },
{ 75, B75 },
{ 110, B110 },
{ 134, B134 },
{ 150, B150 },
{ 200, B200 },
{ 300, B300 },
{ 600, B600 },
{ 1200, B1200 },
{ 1800, B1800 },
{ 2400, B2400 },
{ 4800, B4800 },
{ 9600, B9600 },
{ 19200, B19200 },
{ 38400, B38400 },
{ 57600, B57600 },
{ 115200, B115200 },
{ 230400, B230400 },
{ 460800, B460800 },
{ 500000, B500000 },
{ 576000, B576000 },
{ 921600, B921600 },
{ 1000000, B1000000 },
{ 1152000, B1152000 },
{ 1500000, B1500000 },
{ 2000000, B2000000 },
{ 2500000, B2500000 },
{ 3000000, B3000000 },
{ 3500000, B3500000 },
{ 4000000, B4000000 },
};
errno = 0;
val = strtoul(baud, &ptr, 10);
if (errno || ptr == baud)
return B0;
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
if (v2s[i].val == val)
return v2s[i].speed;
}
return B0;
}
void stty(int fd, speed_t speed)
{
struct termios term;
tcdrain(fd);
if (tcgetattr(fd, &term))
return;
if (speed != B0) {
cfsetispeed(&term, speed);
cfsetospeed(&term, speed);
tcsetattr(fd, TCSAFLUSH, &term);
}
tcflush(fd, TCIOFLUSH);
/* Disable modem specific flags */
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
term.c_cflag &= ~CRTSCTS;
term.c_cflag |= CLOCAL;
/* Timeouts, minimum chars and default flags */
term.c_cc[VTIME] = 0;
term.c_cc[VMIN] = 1;
term.c_iflag = ICRNL|IXON|IXOFF;
term.c_oflag = OPOST|ONLCR;
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
/* Reset special characters to defaults */
term.c_cc[VINTR] = CTRL('C');
term.c_cc[VQUIT] = CTRL('\\');
term.c_cc[VEOF] = CTRL('D');
term.c_cc[VEOL] = '\n';
term.c_cc[VKILL] = CTRL('U');
term.c_cc[VERASE] = CERASE;
tcsetattr(fd, TCSANOW, &term);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+178 -238
View File
@@ -24,48 +24,48 @@
#include <err.h> #include <err.h>
#include <ctype.h> /* isdigit() */ #include <ctype.h> /* isdigit() */
#include <time.h>
#include <stdlib.h> #include <stdlib.h>
#include <strings.h> #include <strings.h>
#include <sys/time.h> #include <sys/time.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include "finit.h" #include "finit.h"
#include "svc.h" #include "svc.h"
#include "helpers.h" #include "helpers.h"
#include "pid.h"
#include "util.h" #include "util.h"
#include "cond.h"
#include "schedule.h"
/* Each svc_t needs a unique job# */ /* Each svc_t needs a unique job# */
static int jobcounter = 1; static int jobcounter = 1;
static svc_t *workaround = NULL; static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
static svc_t *__connect_shm(void) static void svc_gc(void *arg)
{ {
svc_t *list; struct timespec now;
struct wq *work = (struct wq *)arg;
svc_t *svc, *next;
list = finit_svc_connect(); clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
if (!list) { TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
if (workaround) char cond[MAX_COND_LEN];
return workaround; int msec;
/* Linux not built with CONFIG_SYSVIPC, or libc does not support shmat()/shmget() */ msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
if (ENOSYS == errno) (now.tv_nsec - svc->gc.tv_nsec) / 1000000;
warn("Kernel does support SYSV shmat() IPC, error %d", errno); if (msec < SVC_TERM_TIMEOUT)
continue;
/* Try to prevent PID 1 from aborting, issue #81 */ TAILQ_REMOVE(&gc_list, svc, link);
if (getpid() == 1) { cond_clear(mkcond(cond, sizeof(cond), svc->cmd));
warnx("Implementing PID 1 workaround, initctl tool will not work ..."); free(svc);
if (!workaround)
workaround = calloc(MAX_NUM_SVC, sizeof(svc_t));
list = workaround;
}
} }
if (!list) { if (!TAILQ_EMPTY(&gc_list))
warn("Failed connecting to shared memory, error %d", errno); schedule_work(work);
abort();
}
return list;
} }
/** /**
@@ -79,11 +79,12 @@ static svc_t *__connect_shm(void)
*/ */
svc_t *svc_new(char *cmd, int id, int type) svc_t *svc_new(char *cmd, int id, int type)
{ {
int i, job = -1; int job = -1;
svc_t *svc, *list = __connect_shm(); char *desc;
svc_t *svc, *iter = NULL;
/* Find first job n:o if registering multiple instances */ /* Find first job n:o if registering multiple instances */
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->cmd, cmd)) { if (!strcmp(svc->cmd, cmd)) {
job = svc->job; job = svc->job;
break; break;
@@ -92,36 +93,35 @@ svc_t *svc_new(char *cmd, int id, int type)
if (job == -1) if (job == -1)
job = jobcounter++; job = jobcounter++;
for (i = 0; i < MAX_NUM_SVC; i++) { svc = calloc(1, sizeof(*svc));
svc_t *svc = &list[i]; if (!svc)
return NULL;
if (svc->type == SVC_TYPE_FREE) { svc->type = type;
char *desc; svc->job = job;
svc->id = id;
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
memset(svc, 0, sizeof(*svc)); /* Default description, if missing */
svc->type = type; desc = strrchr(cmd, '/');
svc->job = job; if (desc)
svc->id = id; desc++;
strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); else
desc = cmd;
strlcpy(svc->desc, desc, sizeof(svc->desc));
/* Default description, if missing */ TAILQ_INSERT_TAIL(&svc_list, svc, link);
desc = rindex(cmd, '/');
if (desc)
desc++;
else
desc = cmd;
strlcpy(svc->desc, desc, sizeof(svc->desc));
return svc; return svc;
}
}
errno = ENOMEM;
return NULL;
} }
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 * @svc: Pointer to an &svc_t object
* *
* Returns: * Returns:
@@ -129,50 +129,57 @@ svc_t *svc_new(char *cmd, int id, int type)
*/ */
int svc_del(svc_t *svc) int svc_del(svc_t *svc)
{ {
svc->type = SVC_TYPE_FREE; TAILQ_REMOVE(&svc_list, svc, link);
TAILQ_INSERT_TAIL(&gc_list, svc, link);
clock_gettime(CLOCK_MONOTONIC_COARSE, &svc->gc);
schedule_work(&work);
return 0; return 0;
} }
/** /**
* svc_iterator - Naive iterator over all registered services. * svc_iterator - Naive iterator over all registered services.
* @first: Get first &svc_t object, or next until end. * @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
* *
* Returns: * Returns:
* The first &svc_t when @first is set, otherwise the next &svc_t until * An &svc_t pointer, or %NULL when no more entries can be found.
* the end when %NULL is returned.
*/ */
svc_t *svc_iterator(int first) svc_t *svc_iterator(svc_t **iter, int first)
{
static int i = 0;
svc_t *list = __connect_shm();
if (first)
i = 0;
while (i < MAX_NUM_SVC) {
svc_t *svc = &list[i++];
if (svc->type != SVC_TYPE_FREE)
return svc;
}
return NULL;
}
/**
* svc_inetd_iterator - Naive iterator over all registered inetd services.
* @first: Get first &svc_t object, or next until end.
*
* Returns:
* The first inetd &svc_t when @first is set, otherwise the next
* inetd &svc_t until the end when %NULL is returned.
*/
svc_t *svc_inetd_iterator(int first)
{ {
svc_t *svc; svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { if (!iter) {
errno = EINVAL;
return NULL;
}
if (first)
svc = TAILQ_FIRST(&svc_list);
else
svc = *iter;
if (svc)
*iter = TAILQ_NEXT(svc, link);
return svc;
}
/**
* svc_inetd_iterator - Naive iterator over all registered inetd services.
* @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
*
* Returns:
* The first inetd &svc_t when %NULL is given as argument, otherwise the
* next inetd &svc_t until the end when %NULL is returned.
*/
svc_t *svc_inetd_iterator(svc_t **iter, int first)
{
svc_t *svc;
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
if (svc_is_inetd(svc)) if (svc_is_inetd(svc))
return svc; return svc;
} }
@@ -181,42 +188,22 @@ svc_t *svc_inetd_iterator(int first)
} }
/**
* svc_dynamic_iterator - Naive iterator over all registered dynamic services.
* @first: Get first &svc_t object, or next until end.
*
* Returns:
* The first dynamically loaded &svc_t when @first is set, otherwise the
* next dynamically loaded &svc_t until the end when %NULL is returned.
*/
svc_t *svc_dynamic_iterator(int first)
{
svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
if (svc->mtime.tv_sec)
return svc;
}
return NULL;
}
/** /**
* svc_named_iterator - Iterates over all instances of a service. * svc_named_iterator - Iterates over all instances of a service.
* @first: Get first &svc_t object, or next until end. * @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
* @cmd: Service name to look for. * @cmd: Service name to look for.
* *
* Returns: * Returns:
* The first matching &svc_t when @first is set, otherwise the next * The first matching &svc_t when %NULL is given as argument, otherwise
* &svc_t instance with the same @cmd until the end when %NULL is * the next &svc_t with the same @cmd name until the end when %NULL is
* returned. * returned.
*/ */
svc_t *svc_named_iterator(int first, char *cmd) svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
{ {
svc_t *svc; svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
char *name = basename(svc->cmd); char *name = basename(svc->cmd);
if (!strncmp(name, cmd, strlen(name))) if (!strncmp(name, cmd, strlen(name)))
@@ -229,19 +216,20 @@ svc_t *svc_named_iterator(int first, char *cmd)
/** /**
* svc_job_iterator - Iterates over all instances of a service. * svc_job_iterator - Iterates over all instances of a service.
* @first: Get first &svc_t object, or next until end. * @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
* @job: Job to look for. * @job: Job to look for.
* *
* Returns: * Returns:
* The first matching &svc_t when @first is set, otherwise the next * The first matching &svc_t when %NULL is given as argument, otherwise
* &svc_t instance with the same @job until the end when %NULL is * the next &svc_t with the same @job ID until the end when %NULL is
* returned. * returned.
*/ */
svc_t *svc_job_iterator(int first, int job) svc_t *svc_job_iterator(svc_t **iter, int first, int job)
{ {
svc_t *svc; svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
if (svc->job == job) if (svc->job == job)
return svc; return svc;
} }
@@ -254,30 +242,14 @@ svc_t *svc_job_iterator(int first, int job)
* svc_foreach - Run a callback for each registered service * svc_foreach - Run a callback for each registered service
* @cb: Callback to run for each 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; svc_t *svc, *iter = NULL;
if (!cb) if (!cb)
return; return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
cb(svc);
}
/**
* svc_foreach_dynamic - Run a callback for each registered dynamic service
* @cb: Callback to run for each dynamic service
*/
void svc_foreach_dynamic(void (*cb)(svc_t *))
{
svc_t *svc;
if (!cb)
return;
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0))
cb(svc); cb(svc);
} }
@@ -287,14 +259,14 @@ void svc_foreach_dynamic(void (*cb)(svc_t *))
* @types: Mask of service types * @types: Mask of service types
* @cb: Callback to run for each matching type * @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; svc_t *svc, *iter = NULL;
if (!cb) if (!cb)
return; return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!(svc->type & types)) if (!(svc->type & types))
continue; continue;
@@ -312,9 +284,9 @@ void svc_foreach_type(int types, void (*cb)(svc_t *))
*/ */
svc_t *svc_stop_completed(void) svc_t *svc_stop_completed(void)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->state == SVC_STOPPING_STATE) if (svc->state == SVC_STOPPING_STATE)
return svc; return svc;
} }
@@ -331,9 +303,9 @@ svc_t *svc_stop_completed(void)
*/ */
svc_t *svc_find(char *cmd, int id) svc_t *svc_find(char *cmd, int id)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
return svc; return svc;
} }
@@ -350,9 +322,9 @@ svc_t *svc_find(char *cmd, int id)
*/ */
svc_t *svc_find_by_pid(pid_t pid) svc_t *svc_find_by_pid(pid_t pid)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid == pid) if (svc->pid == pid)
return svc; return svc;
} }
@@ -370,9 +342,9 @@ svc_t *svc_find_by_pid(pid_t pid)
*/ */
svc_t *svc_find_by_jobid(int job, int id) svc_t *svc_find_by_jobid(int job, int id)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->job == job && svc->id == id) if (svc->job == job && svc->id == id)
return svc; return svc;
} }
@@ -391,9 +363,9 @@ 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_nameid(char *name, int id)
{ {
char *ptr; char *ptr;
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
ptr = strrchr(svc->cmd, '/'); ptr = strrchr(svc->cmd, '/');
if (ptr) if (ptr)
ptr++; ptr++;
@@ -407,6 +379,27 @@ svc_t *svc_find_by_nameid(char *name, int id)
return NULL; 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. * svc_mark_dynamic - Mark dynamically loaded services for deletion.
* *
@@ -419,11 +412,15 @@ svc_t *svc_find_by_nameid(char *name, int id)
*/ */
void svc_mark_dynamic(void) void svc_mark_dynamic(void)
{ {
svc_t *svc = svc_dynamic_iterator(1); svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->protected)
continue;
if (svc_is_inetd_conn(svc))
continue;
while (svc) {
*((int *)&svc->dirty) = -1; *((int *)&svc->dirty) = -1;
svc = svc_dynamic_iterator(0);
} }
} }
@@ -437,17 +434,6 @@ void svc_mark_clean(svc_t *svc)
*((int *)&svc->dirty) = 0; *((int *)&svc->dirty) = 0;
} }
void svc_check_dirty(svc_t *svc, struct timeval *mtime)
{
if (mtime && timercmp(&svc->mtime, mtime, !=))
svc_mark_dirty(svc);
else
svc_mark_clean(svc);
svc->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
svc->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
}
/** /**
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d * svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
* @cb: Callback to run for each stale service * @cb: Callback to run for each stale service
@@ -457,14 +443,11 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime)
*/ */
void svc_clean_dynamic(void (*cb)(svc_t *)) void svc_clean_dynamic(void (*cb)(svc_t *))
{ {
svc_t *svc = svc_dynamic_iterator(1); svc_t *svc, *iter = NULL;
while (svc) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->dirty == -1 && cb) { if (svc->dirty == -1 && cb)
cb(svc); cb(svc);
svc_mark_clean(svc);
}
svc = svc_dynamic_iterator(0);
} }
} }
@@ -495,85 +478,44 @@ int svc_clean_bootstrap(svc_t *svc)
*/ */
void svc_prune_bootstrap(void) void svc_prune_bootstrap(void)
{ {
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc->pid) if (!svc->pid)
svc_clean_bootstrap(svc); svc_clean_bootstrap(svc);
} }
} }
char *svc_status(svc_t *svc) /**
* svc_enabled - Should the service run?
* @svc: Pointer to &svc_t object
*
* Returns:
* 1, if the service is allowed to run in the current runlevel and the
* user has not manually requested that this service should not run. 0
* otherwise.
*/
int svc_enabled(svc_t *svc)
{ {
switch (svc->state) { if (!svc)
case SVC_HALTED_STATE: return 0;
switch (svc->block) {
case SVC_BLOCK_NONE:
return "halted";
case SVC_BLOCK_MISSING: if (!svc_in_runlevel(svc, runlevel))
return "missing"; return 0;
case SVC_BLOCK_CRASHING: if (svc_is_removed(svc) || svc_is_blocked(svc))
return "crashed"; return 0;
case SVC_BLOCK_USER: return 1;
return "stopped";
case SVC_BLOCK_BUSY:
return "busy";
case SVC_BLOCK_RESTARTING:
return "restart";
}
case SVC_DONE_STATE:
return "done";
case SVC_STOPPING_STATE:
switch (svc->type) {
case SVC_TYPE_INETD_CONN:
case SVC_TYPE_RUN:
case SVC_TYPE_TASK:
return "active";
default:
return "stopping";
}
case SVC_WAITING_STATE:
return "waiting";
case SVC_READY_STATE:
return "ready";
case SVC_RUNNING_STATE:
return "running";
default:
return "UNKNOWN";
}
}
const char *svc_dirtystr(svc_t *svc)
{
if (svc_is_removed(svc))
return "removed";
if (svc_is_updated(svc))
return "updated";
if (svc_is_changed(svc))
return "UNKNOWN";
return "clean";
} }
/* Same base service, return unique ID */ /* Same base service, return unique ID */
int svc_next_id(char *cmd) int svc_next_id(char *cmd)
{ {
int id = 0; int id = 0;
svc_t *svc; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->cmd, cmd) && id < svc->id) if (!strcmp(svc->cmd, cmd) && id < svc->id)
id = svc->id; id = svc->id;
} }
@@ -583,12 +525,10 @@ int svc_next_id(char *cmd)
int svc_is_unique(svc_t *svc) int svc_is_unique(svc_t *svc)
{ {
svc_t *list = __connect_shm(); svc_t *s, *iter = NULL;
int i, unique = 1; int unique = 1;
for (i = 0; i < MAX_NUM_SVC; i++) {
svc_t *s = &list[i];
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
if (svc->type == SVC_TYPE_FREE) if (svc->type == SVC_TYPE_FREE)
continue; continue;
@@ -619,21 +559,21 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
token = strtok_r(input, " ", &pos); token = strtok_r(input, " ", &pos);
while (token) { while (token) {
int id = 1; int id = 1;
svc_t *svc; svc_t *svc, *iter = NULL;
char *ptr = strchr(token, ':'); char *ptr = strchr(token, ':');
if (isdigit(token[0])) { if (isdigit(token[0])) {
int job = atonum(token); int job = atonum(token);
if (!ptr) { if (!ptr) {
svc = svc_job_iterator(1, job); svc = svc_job_iterator(&iter, 1, job);
if (!svc && not_found) if (!svc && not_found)
result += not_found(NULL, job); result += not_found(NULL, job);
while (svc) { while (svc) {
if (found) if (found)
result += found(svc); result += found(svc);
svc = svc_job_iterator(0, job); svc = svc_job_iterator(&iter, 0, job);
} }
} else { } else {
*ptr++ = 0; *ptr++ = 0;
@@ -648,14 +588,14 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
} }
} else { } else {
if (!ptr) { if (!ptr) {
svc = svc_named_iterator(1, token); svc = svc_named_iterator(&iter, 1, token);
if (!svc && not_found) if (!svc && not_found)
result += not_found(token, id); result += not_found(token, id);
while (svc) { while (svc) {
if (found) if (found)
result += found(svc); result += found(svc);
svc = svc_named_iterator(0, token); svc = svc_named_iterator(&iter, 0, token);
} }
} else { } else {
*ptr++ = 0; *ptr++ = 0;
+125 -54
View File
@@ -26,9 +26,10 @@
#define FINIT_SVC_H_ #define FINIT_SVC_H_
#include <sys/ipc.h> /* IPC_CREAT */ #include <sys/ipc.h> /* IPC_CREAT */
#include <sys/shm.h> /* shmat() */ #include <sys/resource.h>
#include <sys/types.h> /* pid_t */ #include <sys/types.h> /* pid_t */
#include <lite/lite.h> #include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include "inetd.h" #include "inetd.h"
#include "helpers.h" #include "helpers.h"
@@ -65,15 +66,16 @@ typedef enum {
SVC_BLOCK_RESTARTING, SVC_BLOCK_RESTARTING,
} svc_block_t; } svc_block_t;
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
#define MAX_ARG_LEN 64 #define MAX_ARG_LEN 64
#define MAX_STR_LEN 64 #define MAX_STR_LEN 64
#define MAX_COND_LEN (MAX_ARG_LEN * 3) #define MAX_COND_LEN (MAX_ARG_LEN * 3)
#define MAX_USER_LEN 16 #define MAX_USER_LEN 16
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */ #define MAX_NUM_FDS 64 /* Max number of I/O plugins */
#define MAX_NUM_SVC 64 /* Enough? */
#define MAX_NUM_SVC_ARGS 32 #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 * Default enable for all services, can be stopped by means
* of issuing an initctl call. E.g. * of issuing an initctl call. E.g.
@@ -81,31 +83,45 @@ typedef enum {
* initctl <stop|start|restart> service * initctl <stop|start|restart> service
*/ */
typedef struct svc { typedef struct svc {
TAILQ_ENTRY(svc) link;
/* Instance specifics */ /* Instance specifics */
int job, id; /* JOB:ID */ int job, id; /* JOB:ID */
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
/* Service details */ /* Service details */
pid_t pid; 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, ... */ const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; svc_type_t type; /* Service, run, task, inetd, ... */
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */ int protected; /* Services like dbus-daemon & udev by Finit */
const int dirty; /* Set if old mtime != new mtime => reloaded, const int dirty; /* -1: removal, 0: unmodified, 1: modified */
* or -1 when marked for removal */
int starting; /* ... waiting for pidfile to be re-asserted */ int starting; /* ... waiting for pidfile to be re-asserted */
int runlevels; int runlevels;
int sighup; /* This service supports SIGHUP :) */ int sighup; /* This service supports SIGHUP :) */
svc_block_t block; /* Reason that this service is currently stopped */ svc_block_t block; /* Reason that this service is currently stopped */
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
/* Incremented for each restart by service monitor. */ /* Counters */
const unsigned int restart_counter; char once; /* run/task, (at least) once per runlevel */
const char restart_cnt; /* Incremented for each restart by service monitor. */
/* For inetd services */ /* For inetd services */
inetd_t inetd; inetd_t inetd;
int stdin_fd; int stdin_fd;
char iifname[IF_NAMESIZE + 1]; /* Ingress interface for connection */
/* Set for services we need to redirect stdout/stderr to syslog */ /* 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 */ /* Identity */
char username[MAX_USER_LEN]; char username[MAX_USER_LEN];
@@ -122,25 +138,11 @@ typedef struct svc {
*/ */
uev_t timer; uev_t timer;
void (*timer_cb)(struct svc *svc); void (*timer_cb)(struct svc *svc);
/* time at svc_del(), used by gc timer */
struct timespec gc;
} svc_t; } svc_t;
typedef struct svc_map svc_map_t;
/* Put services array in shm */
static inline svc_t *finit_svc_connect(void)
{
static void *ptr = (void *)-1;
if ((void *)-1 == ptr) {
ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC,
0600 | IPC_CREAT), NULL, 0);
if ((void *)-1 == ptr)
return NULL;
}
return (svc_t *)ptr;
}
svc_t *svc_new (char *cmd, int id, int type); svc_t *svc_new (char *cmd, int id, int type);
int svc_del (svc_t *svc); int svc_del (svc_t *svc);
@@ -148,59 +150,128 @@ svc_t *svc_find (char *cmd, int id);
svc_t *svc_find_by_pid (pid_t pid); svc_t *svc_find_by_pid (pid_t pid);
svc_t *svc_find_by_jobid (int job, int id); 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_nameid (char *name, int id);
svc_t *svc_find_by_pidfile (char *fn);
svc_t *svc_iterator (int first); svc_t *svc_iterator (svc_t **iter, int first);
svc_t *svc_inetd_iterator (int first); svc_t *svc_inetd_iterator (svc_t **iter, int first);
svc_t *svc_dynamic_iterator (int first); svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
svc_t *svc_named_iterator (int first, char *cmd); svc_t *svc_job_iterator (svc_t **iter, int first, int job);
svc_t *svc_job_iterator (int first, int job);
void svc_foreach (void (*cb)(svc_t *)); void svc_foreach (int (*cb)(svc_t *));
void svc_foreach_dynamic (void (*cb)(svc_t *)); void svc_foreach_type (int types, int (*cb)(svc_t *));
void svc_foreach_type (int types, void (*cb)(svc_t *));
svc_t *svc_stop_completed (void); svc_t *svc_stop_completed (void);
void svc_mark_dynamic (void); void svc_mark_dynamic (void);
void svc_check_dirty (svc_t *svc, struct timeval *mtime);
void svc_mark_dirty (svc_t *svc); void svc_mark_dirty (svc_t *svc);
void svc_mark_clean (svc_t *svc); void svc_mark_clean (svc_t *svc);
void svc_clean_dynamic (void (*cb)(svc_t *)); void svc_clean_dynamic (void (*cb)(svc_t *));
int svc_clean_bootstrap (svc_t *svc); int svc_clean_bootstrap (svc_t *svc);
void svc_prune_bootstrap (void); void svc_prune_bootstrap (void);
char *svc_status (svc_t *svc); int svc_enabled (svc_t *svc);
const char *svc_dirtystr (svc_t *svc);
int svc_next_id (char *cmd); int svc_next_id (char *cmd);
int svc_is_unique (svc_t *svc); int svc_is_unique (svc_t *svc);
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int)); int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int));
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); } static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; } static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline void svc_starting (svc_t *svc) { svc->starting = 1; } static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
static inline void svc_started (svc_t *svc) { svc->starting = 0; } static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; } static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime.tv_sec; }
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; } 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_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_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_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; } static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; } static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
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; }
#define svc_start(svc) svc_unblock(svc) #define svc_start(svc) svc_unblock(svc)
static inline void svc_stop (svc_t *svc) { svc->block = SVC_BLOCK_USER; } static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; } static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_BUSY; }
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; } static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; } static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; } 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) {
case SVC_BLOCK_NONE:
return "halted";
case SVC_BLOCK_MISSING:
return "missing";
case SVC_BLOCK_CRASHING:
return "crashed";
case SVC_BLOCK_USER:
return "stopped";
case SVC_BLOCK_BUSY:
return "busy";
case SVC_BLOCK_RESTARTING:
return "restart";
}
return "unknown";
case SVC_DONE_STATE:
return "done";
case SVC_STOPPING_STATE:
switch (svc->type) {
case SVC_TYPE_INETD_CONN:
case SVC_TYPE_RUN:
case SVC_TYPE_TASK:
return "active";
default:
return "stopping";
}
case SVC_WAITING_STATE:
return "waiting";
case SVC_READY_STATE:
return "ready";
case SVC_RUNNING_STATE:
return "running";
default:
return "UNKNOWN";
}
}
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))
return "updated";
if (svc_is_changed(svc))
return "UNKNOWN";
return "clean";
}
#endif /* FINIT_SVC_H_ */ #endif /* FINIT_SVC_H_ */
+159
View File
@@ -0,0 +1,159 @@
/* Simple telinit client
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <lite/lite.h>
#include "finit.h"
#include "util.h"
static int do_send(struct init_request *rq)
{
static int fd = -1;
int result = 255;
ssize_t len;
if (fd == -1) {
if (!fexist(FINIT_FIFO)) {
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
return 1;
}
fd = open(FINIT_FIFO, O_RDWR);
if (-1 == fd) {
perror("Failed opening " FINIT_FIFO);
return 1;
}
}
len = write(fd, rq, sizeof(*rq));
if (sizeof(*rq) == len)
result = 0;
return result;
}
static int do_reload(void)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RELOAD,
};
return do_send(&rq);
}
static int set_env(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SETENV,
};
if (runlevel == 0) {
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
return do_send(&rq);
}
return 0;
}
static int set_runlevel(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = runlevel,
};
return set_env(runlevel) || do_send(&rq);
}
/* telinit q | telinit <0-9> */
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n"
" -h, --help This help text\n"
" -V, --version Show Finit version\n\n"
"Commands:\n"
" 0 Power-off the system, same as initctl poweroff\n"
" 6 Reboot the system, same as initctl reboot\n"
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
" services in prev. runlevel that are not allowed in new.\n"
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
" sending SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", prognm);
return rc;
}
int client(int argc, char *argv[])
{
int c;
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'v'},
{NULL, 0, NULL, 0}
};
progname(argv[0]);
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
switch(c) {
case 'v':
return puts("v" VERSION) == EOF;
case 'h':
case '?':
return usage(0);
}
}
if (optind < argc) {
int req = (int)argv[optind][0];
if (isdigit(req))
return set_runlevel(req);
if (req == 'q' || req == 'Q')
return do_reload();
if (req == 's' || req == 'S')
return set_runlevel(1);
}
return usage(1);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+109 -84
View File
@@ -41,7 +41,7 @@
#ifdef FALLBACK_SHELL #ifdef FALLBACK_SHELL
static pid_t fallback = 0; static pid_t fallback = 0;
#endif #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) static char *canonicalize(char *tty)
{ {
@@ -72,48 +72,35 @@ static char *canonicalize(char *tty)
void tty_mark(void) void tty_mark(void)
{ {
tty_node_t *tty; struct tty *tty;
LIST_FOREACH(tty, &tty_list, link) { LIST_FOREACH(tty, &tty_list, link)
if (tty->mtime.tv_sec) tty->dirty = -1;
tty->dirty = -1;
}
}
void tty_check(tty_node_t *tty, struct timeval *mtime)
{
if (mtime && timercmp(&tty->mtime, mtime, !=))
tty->dirty = 1; /* Modified, restart */
else
tty->dirty = 0; /* Not modified */
/* Update mtime, if given */
tty->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
tty->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
} }
void tty_sweep(void) void tty_sweep(void)
{ {
tty_node_t *tty, *tmp; struct tty *tty, *tmp;
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) { LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
if (tty->mtime.tv_sec && tty->dirty) { if (!tty->dirty)
_d("TTY %s dirty, stopping ...", tty->data.name); continue;
tty_stop(&tty->data);
if (tty->dirty == -1) { _d("TTY %s dirty, stopping ...", tty->name);
_d("TTY %s removed, cleaning up.", tty->data.name); tty_stop(tty);
LIST_REMOVE(tty, link);
tty_unregister(tty); if (tty->dirty == -1) {
} _d("TTY %s removed, cleaning up.", tty->name);
tty_unregister(tty);
} }
} }
} }
/** /**
* tty_register - Register a getty on a device * tty_register - Register a getty on a device
* @line: Configuration, text after initial "tty" * @line: Configuration, text after initial "tty"
* @mtime: Modification time, to propagate to lower layers * @rlimit: Limits for this service/task/run/inetd, may be global limits
* @file: The file name TTY was loaded from
* *
* A Finit tty line can use the internal getty implementation or an * A Finit tty line can use the internal getty implementation or an
* external one, like the BusyBox getty for instance. This function * external one, like the BusyBox getty for instance. This function
@@ -131,14 +118,14 @@ void tty_sweep(void)
* Different getty implementations prefer the TTY device argument in * Different getty implementations prefer the TTY device argument in
* different order, so take care to investigate this first. * different order, so take care to investigate this first.
*/ */
int tty_register(char *line, struct timeval *mtime) int tty_register(char *line, struct rlimit rlimit[], char *file)
{ {
tty_node_t *entry; struct tty *entry;
int insert = 0, noclear = 0, nowait = 0;
size_t i, num = 0; 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 *dev = NULL, *baud = NULL;
char *runlevels = NULL, *term = NULL; char *runlevels = NULL, *term = NULL;
int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0;
if (!line) { if (!line) {
_e("Missing argument"); _e("Missing argument");
@@ -156,6 +143,8 @@ int tty_register(char *line, struct timeval *mtime)
noclear = 1; noclear = 1;
else if (!strcmp(tok, "nowait")) else if (!strcmp(tok, "nowait"))
nowait = 1; nowait = 1;
else if (!strcmp(tok, "nologin"))
nologin = 1;
else else
args[num++] = tok; args[num++] = tok;
@@ -168,6 +157,8 @@ int tty_register(char *line, struct timeval *mtime)
if (!dev && !cmd) { if (!dev && !cmd) {
if (args[i][0] == '[') if (args[i][0] == '[')
runlevels = line; runlevels = line;
if (!strcmp(args[i], "@console"))
dev = args[i];
if (!strncmp(args[i], "/dev", 4)) if (!strncmp(args[i], "/dev", 4))
dev = args[i]; dev = args[i];
if (!strncmp(args[i], "tty", 3)) if (!strncmp(args[i], "tty", 3))
@@ -213,6 +204,24 @@ int tty_register(char *line, struct timeval *mtime)
return errno = EINVAL; 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 */ /* Ensure all getty (built-in + external) are registered with absolute path */
dev = canonicalize(dev); dev = canonicalize(dev);
if (!dev) if (!dev)
@@ -231,12 +240,13 @@ int tty_register(char *line, struct timeval *mtime)
} }
} }
entry->data.name = dev; strlcpy(entry->name, dev, sizeof(entry->name));
entry->data.baud = baud ? strdup(baud) : NULL; strlcpy(entry->baud, baud ? baud : "", sizeof(entry->baud));
entry->data.term = term ? strdup(term) : NULL; strlcpy(entry->term, term ? term : "", sizeof(entry->term));
entry->data.noclear = noclear; entry->noclear = noclear;
entry->data.nowait = nowait; entry->nowait = nowait;
entry->data.runlevels = conf_parse_runlevels(runlevels); entry->nologin = nologin;
entry->runlevels = conf_parse_runlevels(runlevels);
/* External getty */ /* External getty */
if (cmd) { if (cmd) {
@@ -247,12 +257,12 @@ int tty_register(char *line, struct timeval *mtime)
tok = cmd; tok = cmd;
else else
tok++; tok++;
entry->data.cmd = cmd; entry->cmd = cmd;
args[1] = strdup(tok); args[1] = strdup(tok);
for (i = 1; i < num; i++) for (i = 1; i < num; i++)
entry->data.args[j++] = strdup(args[i]); entry->args[j++] = strdup(args[i]);
entry->data.args[++j] = NULL; entry->args[++j] = NULL;
} }
_d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s", _d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s",
@@ -261,33 +271,43 @@ int tty_register(char *line, struct timeval *mtime)
if (insert) if (insert)
LIST_INSERT_HEAD(&tty_list, entry, link); LIST_INSERT_HEAD(&tty_list, entry, link);
tty_check(entry, mtime); /* Register configured limits */
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
if (file && conf_changed(file))
entry->dirty = 1; /* Modified, restart */
else
entry->dirty = 0; /* Not modified */
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT "); _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; return 0;
} }
int tty_unregister(tty_node_t *tty) int tty_unregister(struct tty *tty)
{ {
if (!tty) { if (!tty) {
_e("Missing argument"); _e("Missing argument");
return errno = EINVAL; return errno = EINVAL;
} }
if (tty->data.name) LIST_REMOVE(tty, link);
free(tty->data.name);
if (tty->data.baud) if (tty->cmd) {
free(tty->data.baud);
if (tty->data.term)
free(tty->data.term);
if (tty->data.cmd) {
int i; int i;
free(tty->data.cmd); free(tty->cmd);
for (i = 0; i < TTY_MAX_ARGS; i++) { for (i = 0; i < TTY_MAX_ARGS; i++) {
if (tty->data.args[i]) if (tty->args[i])
free(tty->data.args[i]); free(tty->args[i]);
tty->data.args[i] = NULL; tty->args[i] = NULL;
} }
} }
free(tty); free(tty);
@@ -295,12 +315,12 @@ int tty_unregister(tty_node_t *tty)
return 0; 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) { LIST_FOREACH(entry, &tty_list, link) {
if (!strcmp(dev, entry->data.name)) if (!strcmp(dev, entry->name))
return entry; return entry;
} }
@@ -310,7 +330,7 @@ tty_node_t *tty_find(char *dev)
size_t tty_num(void) size_t tty_num(void)
{ {
size_t num = 0; size_t num = 0;
tty_node_t *entry; struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) LIST_FOREACH(entry, &tty_list, link)
num++; num++;
@@ -321,22 +341,22 @@ size_t tty_num(void)
size_t tty_num_active(void) size_t tty_num_active(void)
{ {
size_t num = 0; size_t num = 0;
tty_node_t *entry; struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) { LIST_FOREACH(entry, &tty_list, link) {
if (entry->data.pid) if (entry->pid)
num++; num++;
} }
return 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) { LIST_FOREACH(entry, &tty_list, link) {
if (entry->data.pid == pid) if (entry->pid == pid)
return entry; return entry;
} }
@@ -359,9 +379,8 @@ static int tty_exist(char *dev)
return result; return result;
} }
void tty_start(finit_tty_t *tty) void tty_start(struct tty *tty)
{ {
int is_console = 0;
char *dev; char *dev;
if (tty->pid) { if (tty->pid) {
@@ -375,21 +394,25 @@ void tty_start(finit_tty_t *tty)
return; return;
} }
if (console && !strcmp(dev, console))
is_console = 1;
if (tty_exist(dev)) { if (tty_exist(dev)) {
_d("%s: Not a valid TTY: %s", dev, strerror(errno)); _d("%s: Not a valid TTY: %s", dev, strerror(errno));
return; 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) if (!tty->cmd)
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, is_console); tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
else else
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, is_console); 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) if (!tty->pid)
return; return;
@@ -406,7 +429,7 @@ void tty_stop(finit_tty_t *tty)
tty->pid = 0; tty->pid = 0;
} }
int tty_enabled(finit_tty_t *tty) int tty_enabled(struct tty *tty)
{ {
if (!tty) if (!tty)
return 0; return 0;
@@ -454,12 +477,12 @@ int tty_fallback(pid_t lost)
return 0; return 0;
} }
static void tty_action(tty_node_t *tty) static void tty_action(struct tty *tty)
{ {
if (!tty_enabled(&tty->data)) if (!tty_enabled(tty))
tty_stop(&tty->data); tty_stop(tty);
else else
tty_start(&tty->data); tty_start(tty);
} }
/* /*
@@ -467,7 +490,7 @@ static void tty_action(tty_node_t *tty)
*/ */
int tty_respawn(pid_t pid) 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) if (!tty)
return tty_fallback(pid); return tty_fallback(pid);
@@ -476,7 +499,7 @@ int tty_respawn(pid_t pid)
utmp_set_dead(pid); utmp_set_dead(pid);
/* Clear PID to be able to respawn it. */ /* Clear PID to be able to respawn it. */
tty->data.pid = 0; tty->pid = 0;
tty_action(tty); tty_action(tty);
return 1; return 1;
@@ -487,15 +510,17 @@ int tty_respawn(pid_t pid)
*/ */
void tty_reload(char *dev) void tty_reload(char *dev)
{ {
tty_node_t *tty; struct tty *tty;
if (dev) { if (dev) {
tty = tty_find(dev); tty = tty_find(dev);
if (!tty) if (!tty) {
logit(LOG_WARNING, "No TTY registered for %s", dev); logit(LOG_WARNING, "No TTY registered for %s", dev);
else return;
tty_action(tty); }
tty_action(tty);
tty->dirty = 0;
return; return;
} }
@@ -510,7 +535,7 @@ void tty_reload(char *dev)
/* Start all TTYs that exist in the system and are allowed at this runlevel */ /* Start all TTYs that exist in the system and are allowed at this runlevel */
void tty_runlevel(void) void tty_runlevel(void)
{ {
tty_node_t *tty; struct tty *tty;
LIST_FOREACH(tty, &tty_list, link) LIST_FOREACH(tty, &tty_list, link)
tty_action(tty); tty_action(tty);
+20 -23
View File
@@ -26,51 +26,48 @@
#define FINIT_TTY_H_ #define FINIT_TTY_H_
#include <limits.h> #include <limits.h>
#include <sys/resource.h>
#include <lite/queue.h> /* BSD sys/queue.h API */ #include <lite/queue.h> /* BSD sys/queue.h API */
#define TTY_MAX_ARGS 16 #define TTY_MAX_ARGS 16
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1)) #define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
typedef struct { struct tty {
char *name; LIST_ENTRY(tty) link;
char *baud;
char *term; char name[42];
char baud[10];
char term[10];
int noclear; int noclear;
int nowait; int nowait;
int nologin;
int runlevels; int runlevels;
char *cmd; /* NULL when running built-in getty */ char *cmd; /* NULL when running built-in getty */
char *args[TTY_MAX_ARGS]; char *args[TTY_MAX_ARGS];
int pid; int pid;
} finit_tty_t;
typedef struct tty_node { /* Limits and scoping */
LIST_ENTRY(tty_node) link; struct rlimit rlimit[RLIMIT_NLIMITS];
finit_tty_t data;
/* XXX: Yes, TTYs should be refactored into a separate SVC type. */ /* Set if modified => reloaded, or -1 when marked for removal */
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */ int dirty;
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;
void tty_mark (void); void tty_mark (void);
void tty_check (tty_node_t *tty, struct timeval *mtime);
void tty_sweep (void); void tty_sweep (void);
int tty_register (char *line, struct timeval *mtime); 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 (void);
size_t tty_num_active (void); size_t tty_num_active (void);
tty_node_t *tty_find_by_pid (pid_t pid); struct tty *tty_find_by_pid (pid_t pid);
void tty_start (finit_tty_t *tty); void tty_start (struct tty *tty);
void tty_stop (finit_tty_t *tty); void tty_stop (struct tty *tty);
int tty_enabled (finit_tty_t *tty); int tty_enabled (struct tty *tty);
int tty_respawn (pid_t pid); int tty_respawn (pid_t pid);
void tty_reload (char *dev); void tty_reload (char *dev);
void tty_runlevel (void); void tty_runlevel (void);
+102 -1
View File
@@ -23,8 +23,12 @@
#include "config.h" #include "config.h"
#include <ctype.h> /* isprint() */ #include <ctype.h> /* isprint() */
#include <errno.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */
#include "util.h" #include "util.h"
int screen_rows = 24; int screen_rows = 24;
@@ -42,12 +46,92 @@ char *progname(char *arg0)
return prognm; 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) void do_sleep(unsigned int sec)
{ {
while ((sec = sleep(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 */ /* Allowed characters in job/id/name */
static int isallowed(int ch) static int isallowed(int ch)
{ {
@@ -67,17 +151,34 @@ char *sanitize(char *arg, size_t len)
return arg; return arg;
} }
if (i > 1 && arg[i] == 0) if (i > 0 && arg[i] == 0)
return arg; return arg;
return NULL; return NULL;
} }
/*
* Called at boot, and shutdown, to (re)initialize the
* screen size for print() et al.
*/
void screen_init(void) void screen_init(void)
{ {
if (!isatty(STDOUT_FILENO))
return;
initscr(&screen_rows, &screen_cols); 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: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+6
View File
@@ -39,10 +39,16 @@ extern char *prognm;
#include <lite/conio.h> #include <lite/conio.h>
char *progname (char *arg0); char *progname (char *arg0);
int strtobytes (char *arg);
void do_sleep (unsigned int sec); 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); char *sanitize (char *arg, size_t len);
void screen_init (void); void screen_init (void);
void screen_exit (void);
int screen_width (void); int screen_width (void);
#endif /* FINIT_UTIL_H_ */ #endif /* FINIT_UTIL_H_ */
+60 -68
View File
@@ -1,6 +1,6 @@
/* Built-in watchdog daemon /* 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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -9,74 +9,47 @@
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in all
* all copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* THE SOFTWARE. * SOFTWARE.
*/ */
#include <paths.h> #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 <signal.h>
#include <sys/un.h> #include <syslog.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include "finit.h"
#include "helpers.h"
#include "util.h"
#include "watchdog.h" #include "watchdog.h"
int running = 1; int running = 1;
int handover = 0; int handover = 0;
int shutdown = 0;
static int notify(void)
{
int sd, result = 255;
ssize_t len;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
struct init_request rq;
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
return -1;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
goto exit;
rq.cmd = INIT_CMD_WDOG_HELLO;
rq.magic = INIT_MAGIC;
rq.runlevel = getpid();
len = sizeof(rq);
if (write(sd, &rq, len) != len)
goto exit;
if (read(sd, &rq, len) != len)
goto exit;
result = 0;
exit:
close(sd);
return result;
}
static void sighandler(int signo) static void sighandler(int signo)
{ {
if (signo == SIGTERM) if (signo == SIGTERM)
handover = 1; handover = 1;
if (signo == SIGPWR)
shutdown = 1;
running = 0; running = 0;
} }
static int init(char *progname, char *devnode, int timeout) static int init(char *progname, char *devnode)
{ {
int fd; int fd;
@@ -84,49 +57,68 @@ static int init(char *progname, char *devnode, int timeout)
signal(SIGTERM, sighandler); signal(SIGTERM, sighandler);
signal(SIGPWR, 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); fd = open(devnode, O_WRONLY);
if (fd == -1) if (fd == -1)
return -1; return -1;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
return fd; return fd;
} }
static int loop(int fd) static int loop(int fd, int timeout)
{ {
int done = 0; int dummy = 0;
int period = timeout / 2;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
while (running) { while (running) {
do_sleep(1); ioctl(fd, WDIOC_KEEPALIVE, &dummy);
ioctl(fd, WDIOC_KEEPALIVE, &done); sleep(period);
if (!done && !notify())
done = 1;
} }
if (handover) /* 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); return !write(fd, "V", 1);
}
return 0; return 0;
} }
void watchdog(char *progname) int main(int argc, char *argv[])
{ {
if (fork() == 0) { int fd, ret;
int fd, ret;
fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT); fd = init(argv[0], WDT_DEVNODE);
if (fd == -1) { if (fd == -1) {
_pe("Failed connecting to watchdog %s", WDT_DEVNODE); if (ENOENT != errno)
_exit(1); syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
} return 1;
ret = loop(fd);
close(fd);
_exit(ret);
} }
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;
} }
/** /**
+3 -3
View File
@@ -24,12 +24,12 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#define WDT_DEVNODE "/dev/watchdog" #define WDT_DEVNODE "/dev/watchdog"
#define WDT_TIMEOUT 3 #define WDT_TIMEOUT 30
#ifdef BUILTIN_WATCHDOG #ifdef BUILTIN_WATCHDOG
void watchdog(char *progname); int watchdog(char *progname);
#else #else
#define watchdog(progname) #define watchdog(progname) 0
#endif /* BUILTIN_WATCHDOG */ #endif /* BUILTIN_WATCHDOG */
/** /**