Compare commits

...
121 Commits
Author SHA1 Message Date
Joachim Nilsson 8379b4927b Fix GCC truncation warnings, too small buffers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-12 00:33:52 +02:00
Joachim Nilsson 6a7f48970c Add cgreaper.sh to dist
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-12 00:29:39 +02:00
Joachim Nilsson d797754ca8 Update ChangeLog and bump version for v3.2-rc2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-11 23:54:26 +02:00
Joachim Nilsson 770a79fa89 Fix #120: Redirect stdin to /dev/null for services by default
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-11 22:24:33 +02:00
Joachim Nilsson ff24ee3553 Fix #124: reap all children in same process group of dying parent
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-11 21:55:40 +02:00
Joachim Nilsson 25c194ef93 Create unique service cgroup based on name:id
We want to have unique cgroups per instance.  I.e., a DHCP client for
eth0 should have its own cgroup separate from a DHCP client for eth1.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-11 21:50:38 +02:00
Joachim Nilsson 91a9c83928 Send signal to every process in the same process group
When stopping a run/task/service we must send SIGTERM/KILL to all every
process in the same process group.  Otherwise we may end up with stray
processes like 'logit' when log redirection is enabled.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-08 18:00:48 +02:00
Joachim Nilsson 9b022ed7a4 Create FUNDING.yml 2020-04-04 13:56:36 +02:00
Joachim Nilsson ca3fc057f2 configure: Change semantics of 33bb6ed, logit enabled by default
The logit tool is not just used by scripts and such, when logger is
absent.  It is also used by Finit internally to redirect logging of
services to syslog and/or log file, with log rotation.

This patch reverts the default to enabled.  Someone explicitly has
to disable logit now to get less functionality.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-30 11:56:39 +02:00
Joachim Nilsson bf6e599584 Handle log redirect to syslog when logit is disabled
`--disable-logit` at configure time means redirecting stdout from a
service to a file is completely disabled.  Fallback to `logger` can
not be guaranteed to provide log file rotation, so all logs will be
sent to syslog instead.  In case logger is also missing, a simple
redirect using fork() + syslog() is used as fallback.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-30 11:53:43 +02:00
Joachim Nilsson 89c7171223 Refactor run/task/service stdout redirection after setsid() addition
This patch refactors redirect_null() and adds support for redirecting
stdout to console when 'log:console' is enabled.  This was previously
sort of the default, but with the addition of the long-sought-after
setsid() call we needed to change things.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-30 10:11:29 +02:00
Joachim Nilsson c41f3f4a06 Call setsid() by default to detach from controlling terminal
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-29 17:39:35 +02:00
Yaroslav Syrytsia 0a54a9624a Fix #123: Regression in sysv start/stop scripts
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-26 05:16:52 +01:00
Joachim Nilsson 1e238eec61 Increase MAX number of arguments to a service 32 -> 64
Usually a wrapper script is recommended, but for some use-cases it
is more efficient to just be able to use more command line args.

Example application that needs this; udhcpc

Note, we should refactor this to use a dynamically allocated array
instead of this static one.  We relied heavily on static arrays in
Finit 1, when everything had to fit i shm.  This is just legacy.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-19 07:50:36 +01:00
Joachim Nilsson 4163527006 Fix run-parts regression introduced in 9a98bff2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-09 14:29:04 +01:00
Joachim Nilsson f06e17ccc6 initctl: Widen status column a bit to fit 'stopping' state
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-03-05 17:51:52 +01:00
Joachim Nilsson fe41b9c4b8 Revert "plugins: pidfile: Simplify and clean up developer debug messages"
This reverts commit 69016bb8f5.
2020-03-02 07:22:15 +01:00
Joachim Nilsson 7cf3ef80db Revert "Travic-CI: Disable clang temporarily for Coverity run"
This reverts commit 27c839bc92.
2020-02-29 17:10:28 +01:00
Joachim Nilsson 69016bb8f5 plugins: pidfile: Simplify and clean up developer debug messages
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-29 17:04:27 +01:00
Joachim Nilsson f7d5b588c9 plugins: pidfile: Factor out directory handling from callback
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-29 16:52:51 +01:00
Joachim Nilsson 0ad99ce054 Fix use-after-close in service.c:redirect(), found by Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-29 16:42:53 +01:00
Joachim Nilsson 1176961e7b plugins: pidfile: Fix memory leak in pidfile_callback()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-29 16:40:06 +01:00
Joachim Nilsson 27c839bc92 Travic-CI: Disable clang temporarily for Coverity run
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-29 16:32:05 +01:00
Joachim Nilsson 6149e93aca docs: Update documentation on svc conditions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-28 17:16:24 +01:00
Joachim Nilsson 4e7baa807e Fix #109: Declare PID file path to dbus-daemon in dbus plugin
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-28 16:50:57 +01:00
Joachim Nilsson 54cfa74042 Fix #109: Support PID files in subdirectories to /var/run
Services, like dbus and teamd for instance, may create their PID files
in a subdirectory of /var/run (today often /run). E.g.,

   - /var/run/teamd/a1.pid    -- For aggregate A1
   - /var/run/dbus/pid
   - /var/run/lxc/foo.pid     -- For container foo

This patch adds support for dynamically adding inotify watchers to any
new subdirectory created in /var/run (discarding too deep directories).

To match services in this directory the run/task/service/sysv stanza
must contain the pid:!/path/to/pidfile.pid syntax.  This pid file name
is also used to create the condition this service asserts using the
following formula:

   svc/ + <dirname of service> + <subdir and file without .pid>

E.g., the case of teamd (above) gives condition 'svc/usr/bin/teamd/a1'

The special case of dbus is interesting, since it may not be a special
case, but rather the norm for services using a subdirectory.  It is
handled as follows; when a new subdirectory is detected, the directory
is scanned for files matching *.pid.  Matching files follow the teamd
case.  The directory is also scanned for 'pid', which then gives us the
condition 'svc/usr/bin/dbus'

One last example, illustrated by lxc-start, where we want to track the
condition for the LXC container foo.  The service stanza:

   service pid:!/run/lxc/foo.pid lxc-start -n foo -F -p /run/lxc/foo.pid -- Container foo

This command has no leading path so the condition is composed entirely
from the PID file location:

   svc/  + '' + lxc/foo => svc/lxc/foo

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-28 16:39:53 +01:00
Joachim Nilsson 97ce0aa519 Rename local variable shadowing variable in outer scope
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-28 14:36:14 +01:00
Joachim Nilsson e72b61c9c9 Minor, fix dead EeePC fastinit link and adjust distro ordering
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-27 23:08:20 +01:00
Joachim Nilsson b66069278f Support for a custom kill:DELAY, default 3 sec
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 18:49:47 +01:00
Joachim Nilsson 1192506d43 Support for custom halt:signal, default SIGTERM
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 18:28:02 +01:00
Joachim Nilsson decdc1560a Support for monitoring forking services/sysv daemons
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 14:10:47 +01:00
Joachim Nilsson f6372de6a9 New function pid_file_read(), returns PID from a pid file
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 11:40:44 +01:00
Joachim Nilsson 9052248127 Check for an actual /proc file, not directory, in pid_alive()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 11:40:18 +01:00
Joachim Nilsson 6ab9c8fad6 Allow services to be started without absolute path, trust $PATH
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 11:39:17 +01:00
Joachim Nilsson 619bd03551 Drop old comment related to service plugins (now removed)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 11:38:50 +01:00
Joachim Nilsson e301877b16 Follow-up to 12d14aa, configurable auto-reload of .conf files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-26 11:13:06 +01:00
Joachim Nilsson 3b67afe480 Change semantics for SysV start/stop scripts
If a start/stop script returns OK we classify it as 'started' and leave
it in running state, despite having collected its PID.  This way we can
track what scripts need to be called with 'stop' when changing to a
runlevel they are not declared for.

Also, clean up related debug messages from earlier commit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-05 12:35:15 +01:00
Joachim Nilsson 9a9a9f4bfe Remove old blocking (!) waitpid() call in service_start()
Remnant from when processes were collected in service_start(), now all
handled by service_monitor() from SIGCHLD and service_step().

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-05 12:32:04 +01:00
Joachim Nilsson f38299bfca Redirect stdout/stderr according to .conf when stopping SysV service
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-05 12:31:21 +01:00
Joachim Nilsson 99901782ff Factor out stdout/stderr redirection to from service_start()
This new function can then be used also by service_stop(), e.g. for SysV
start/stop scripts.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-05 12:30:00 +01:00
Joachim Nilsson 912a336997 Only show [ OK ] startup/status if run tasks exit normally
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-05 07:52:36 +01:00
Joachim Nilsson c3eca9fe8f Save process exit status to svc_t
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-02-05 07:23:06 +01:00
Joachim Nilsson 225510a270 Remove gratuitous newline when finit stops its own log service
This patch fixes an annoying thing when typing reboot/shutdown/poweroff
that caused an extra newline to be printed before displaying services
stopping.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-30 18:29:45 +01:00
Joachim Nilsson 4389fbd8bc Merge pull request #122 from jackuess/fix-inaccurate-sleep
Switch to using nanosleep in favour of sleep
2020-01-30 18:01:46 +01:00
Jacques de Laval 920382518f Switch to using nanosleep in favour of sleep
The remaining sleep time returned by sleep is not accurate enough to be
used as a means to achieve "signal safe"-sleep. nanosleep can be used
in similar fashion instead and is granular enough for our purposes.

This issue was identified during reboot, when Finit is bombarded with
SIGCHLD, aborting the sleep that we do to give daemons a chance to shut
down gracefully. The total delay ended up being less than a second when
in fact two seconds were the intended delay.
2020-01-30 15:39:46 +01:00
Joachim Nilsson 997a699b7e Add more debug logs for /etc/finit.d*/ inotify and exec_runtask()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-22 15:48:56 +01:00
Joachim Nilsson 21db27ee75 Minor, coding style cleanup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-22 15:48:42 +01:00
Joachim Nilsson fa0c63070c Add support for starting/stopping SysV init scripts
This patch adds initial support for starting a SysV init script in a
runlevel, and stopping it when leaving a runlevel.

    sysv [LVLS] <COND> /path/to/script.sh -- Optional Description

The SysV /etc/inittab file, which may use /etc/init.d, or /etc/rcN.d,
is still not supported.  A separate plugin would be required for this,
see the documentation for more information.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-22 15:45:08 +01:00
Joachim Nilsson 9bfc9092b8 Document new mdules-load and modprobe plugins
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-15 22:53:24 +01:00
Joachim Nilsson 1c6464a7a8 Add support for cold plugging devices, auto-loading of modules
This patch adds a new plugin to Finit called modprobe.  It probes in
/sys/devices after modalias files and then calls 'modprobe -ab' for
each alias listed.

For hot-plugging we recommend adding the following to /etc/mdev.conf

  $MODALIAS=.*	root:root	0660	@modprobe -b "$MODALIAS"

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-12 20:00:47 +01:00
Joachim Nilsson 51adba4387 Slight update, we won't be releasing in 2018 ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-08 12:08:24 +01:00
Joachim Nilsson bb1aed1010 initctl: Follow-up to 6a5f439, don't fail if directory already exists
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-08 12:06:41 +01:00
Joachim Nilsson cf4a1332f9 Revert "Travis-CI: Disable clang for Coverity Scan run"
This reverts commit fc37b854ae.
2020-01-07 20:46:49 +01:00
Joachim Nilsson d70fb85aaf Make MIT badge more readable, teal is also a nice color
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 20:45:53 +01:00
Joachim Nilsson fc37b854ae Travis-CI: Disable clang for Coverity Scan run
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:55:03 +01:00
Jonas Holmberg ca210f5431 Add support for logging security related events
This patch introduces the LOG_CONSOLE syslog facility for logging common
events.  In industrial applications aiming for IEC 62443 compliance the
following events are central for system observability:

- Change of runlevel - i.e., starting up, shutting down, upgrade, etc.
  Facility: console, severity: notice
- Service starting
  Facility: console, severity: notice
- Service restarting
  Facility: console, severity: notice
- Service stopping
  Facility: console, severity: notice
- Service failed to start
  Facility: console, severity: warning

The use of facility console for this makes it easier to filter out when
forwarding syslog messages from an embedded system to a remote log sink.
Otherwise messages of facility daemon would be used, which include a lot
more, and mostly irrelevant, information.

Signed-off-by: Jonas Holmberg <jonas.holmberg@westermo.se>
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:38:21 +01:00
Jonas Holmberg a19a3c774e Fix issue where svc_find() finds the wrong service
This issue may arise in situations where the name of the service being
compared is a sub-string of the service we want to see if it exist.

Example:
  - Two different services: teamd, teamdagentd
  - They have the same id, 1 being the default if none is specified
  - The service with the shortest name is created first

While checking if the "teamdagentd" service exists we eventually compare
it to the "teamd" service.  Prior to this fix we only compare the names
names with the length of the existing service.  Hence, teamdagentd will
seem to be the same as teamd since the first part of the string is equal
to the existing.

Signed-off-by: Jonas Holmberg <jonas.holmberg@westermo.se>
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:23:53 +01:00
Joachim Nilsson a4f1330eae Simplify, no need to check svc->id, is always a valid string
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:21:46 +01:00
Joachim Nilsson 38d768ac9e Travis-CI: Enable all -Wfoo CFLAGS globally and build logit in all
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:07:27 +01:00
Joachim Nilsson 36b14f9ff7 Travis-CI: Move environment variable PKG_CONFIG_PATH to env:->global:
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:06:55 +01:00
Joachim Nilsson bdbd2afc40 Travis-CI: Re-enable clang builds after Coverity Scan run
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:05:26 +01:00
Joachim Nilsson 33bb6ed5ac Add --enable-logit to configure, disabled by default
Since sysklogd now ships an excellent enhanced logger tool there is no
need for Finit to provide its own tool for the same purpose by default.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 13:02:06 +01:00
Joachim Nilsson 212083b922 Introduce strterm(), hard-add terminating '\0' at end of buffer
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 03:02:39 +01:00
Joachim Nilsson 55c49a0bf0 Sanitize job[:id] tuple read from UNIX socket using a regex
This is a really tight check for a single "job[:id]" tuple, not much
escapes it, Coverity should be a lot more happy about our addressing
CWE-20 with this one.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 02:51:15 +01:00
Joachim Nilsson b369e1cfa2 Refactor svc_parse_jobstr(), use tokstr() instead of strtok_r()
This is a safer version of strtok() that makes sure we do not step
outside of string bounds.  Coverity complained about this, but was
likely more concerned with the lacking sanitize() and trusting data
read from a UNIX domain socket ...

Note: not only is tokstr() not re-entrant, it is also hard-coded
      to only check for whitespace as token separator.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 02:51:15 +01:00
Joachim Nilsson cfefe23275 Fix another GCC 9 string truncation warning
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 02:51:15 +01:00
Joachim Nilsson 338c358a75 Defensive programming, check bounds of loop variable first
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 02:51:15 +01:00
Joachim Nilsson fe017642de Refactor do_query() to same calling convention used elsewhere
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 02:51:15 +01:00
Joachim Nilsson 8d271ac5b8 Fix GCC 9 string trunction warning
- Increase size of resulting path buffer, unlikely a real problem
- Check return value from snprintf() to detect errors and truncation
- Add logit() function wrapper to initctl, maps to _e()/_d()/_pe()

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 01:03:57 +01:00
Joachim Nilsson 744719219e initctl: Remove experimental cond set/clear commands
The commands are both unsafe (insecure) and send the wrong message,
that initctl can be used to script conditions.  Only plugins are
officially supported to manage conditions.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-07 00:35:13 +01:00
Joachim Nilsson 7bf2fcb6e3 Fix Coverity CID 265222, guard init_request::data member
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 23:56:59 +01:00
Joachim Nilsson ae9ed20d94 Revert "Attempt to fix Coverity CID 265222"
This reverts commit 238f63c0d3.
2020-01-06 23:54:32 +01:00
Joachim Nilsson a5c9d0a3f4 initctl: Fix file descriptor leak, found by Coverity
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 22:04:59 +01:00
Joachim Nilsson a8c071c5ef Fix memory leak, call strdup() as late as possible, found by Coverity
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 22:03:01 +01:00
Joachim Nilsson 238f63c0d3 Attempt to fix Coverity CID 265222
When reading data from (presumably) initctl over the UNIX domain socket
we might actually be receiving corrupt data from a malicious attacker.

This patch is an attempt at, the very least, guarding against C string
functions reading out of bounds due to non-NULL terminated strings.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 21:58:03 +01:00
Joachim Nilsson 41647a814c Handle improbable case were dev may be NULL, found by Coverity
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 21:46:44 +01:00
Joachim Nilsson 765ef63274 Handle mkdir() error in cgroups, don't fail if already exists
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 21:41:43 +01:00
Joachim Nilsson 6a5f4398a2 initctl: Check mkdir() for error and give helpful message on fail
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 21:37:07 +01:00
Joachim Nilsson 7fc515678f Check if svc ID is non-empty before strcmp(), found by Coverity
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 21:31:04 +01:00
Joachim Nilsson e410a24d13 Fix %s i _d() message
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 21:26:51 +01:00
Joachim Nilsson 397f67d03a Clear current line before starting built-in getty
When logging out from the shell, e.g. with Ctrl-D, artifacts from that
session may remain on the current line.  This patch makes sure to clear
up that line before issuing a newline and starting the built-in getty.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 19:37:00 +01:00
Joachim Nilsson b4b9111b33 Enable line wrap in TTY, in case was disabled previously
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 19:26:56 +01:00
Joachim Nilsson c19489d6c3 Add support for @console to external getty in tty_register()
- Refactor code to figure out device so it works for both the built-in
  and external getty code paths
- Fix memory leak when user edits an existing tty device
- Replace @console in argument list to external getty

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 19:05:20 +01:00
Joachim Nilsson 00a2840853 Allow Ctrl-U, Ctrl-D again in built-in getty
Many users found it very frustrating that it was impossible to use
standard commands like Ctrl-U and Ctrl-D at the login: prompt.  It
was never the intention to hamper users for no reason like that.

Also, compensate for cursor-up in "Please press Enter" wait-handler.
Curiously, this makes everything very neat looking also when having
'nowait' enabled for the tty.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 19:01:53 +01:00
Joachim Nilsson d1718d338e Set proper RAW mode for "Please press Enter to activate ..."
Also, while leading with a \n for the activation text and clearing the
line before getty is called, some getty implementations, e.g. BusyBox,
write an extra \n before printing /etc/issue.  For perfectionists that
want a single newline separating started services and the first line
of /etc/issue we now not only clear the line, we also move the cursor
one line up before handing over to getty.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 18:58:08 +01:00
Joachim Nilsson 58ade5d74e Allow built-in getty to show in ps as 'tty DEV'
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 18:52:40 +01:00
Joachim Nilsson c391e04983 Hide command line arguments from ps, for forked children like getty
The built-in gety in Finit forks but does not execv().  To prevent
confusion on the part of the user, this patch hides all command line
arguments to PID 1 so it won't show up in ps listings of children.

Finit currently reads its arguments from the kernel /proc/cmdline,
it has hard-coded paths to /etc/finit.conf etc.  A future version
of Finit may take command line params, in which case this patch
should be moved after any getopt() handling.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-06 18:49:14 +01:00
Joachim Nilsson 85fe613b30 Account for 3-4 byte wide unicode characters in heading
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-05 16:10:41 +01:00
Joachim Nilsson 880ad98dbe Workaround markdown parser
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-04 11:49:50 +01:00
Joachim Nilsson b1a88df9fc Pad heading in modern style using ═══════════════════
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-04 11:40:09 +01:00
Joachim Nilsson aaf7888411 Move badges to top row
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-03 14:27:45 +01:00
Joachim Nilsson 6edb01a72b Update ingress, time passes oh so quickly ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-03 14:27:41 +01:00
Joachim Nilsson 636f6678ed Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-01-02 14:59:32 +01:00
Joachim Nilsson 3b62f46599 TODO: Remember to collect siginfo_t on SIGCHLD
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-12-12 17:57:09 +01:00
Joachim Nilsson 49f0d94b95 Drop reference to non-existing site, refers to *good* example now
Also, drop the Emacs hint, not needed anymore.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-11-04 06:09:59 +01:00
Joachim Nilsson abccb26a83 Check for pkg-config in configure before looking for deps
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-07-04 22:26:08 +02:00
Joachim Nilsson 0848b9b07a Minor, replace cgroup hard-coded path with path from configure
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-05-14 10:15:10 +02:00
Joachim Nilsson c64188bfc5 plugins: rtc: Simplify failed restore from RTC warning
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-04-29 18:20:38 +02:00
Joachim Nilsson 5e789401d4 Rename hwclock.so plugin to rtc.so since it's now stand-alone
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-04-05 21:35:13 +02:00
Joachim Nilsson 744fdd654f plugins: hwclock: Native ioctl support for RTC, skip hwclock tool
The BusyBox hwclock is really bad at handling, or rather displaying,
errors properly.  When the kernel signals EINVAL, or ENOENT, it just
shows that message on stderr.

This patch implements proper RTC load/store at boot/halt with quite
some improved error handling suitable for embedded systems.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-04-05 21:27:39 +02:00
Joachim Nilsson f51fde4d46 Initial support for cgroups
Similar to how systemd creates cgroups for all services, for purposes of
tracking, Finit now does the same.  We also mount all available cgroups
in the /sys/fs/cgroup namespace.

This patch adds support for grouping processes in logical cgroups, like
systemd, and an `initctl ps` command to list the hieararchy.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-02-05 18:35:36 +01:00
Joachim Nilsson 85261c9acd initctl: Create 'enabled' services directory if it doesn't exist
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-02-05 12:42:13 +01:00
Joachim Nilsson 6a1082a947 New helper function: echo(), like shell echo but for C
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-02-05 10:33:00 +01:00
Joachim Nilsson 2094d2a53a Warn if Finit fail to mount /proc and /sys
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-02-05 10:31:36 +01:00
Joachim Nilsson d7da177742 Relocate images to img/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-01-27 10:56:39 +01:00
Joachim Nilsson 170e5aa3fd docs: TODO: Drop simple conf item, contrib/finit.conf exists
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-01-27 10:54:47 +01:00
Joachim Nilsson f8ef8a1616 Merge pull request #115 from jonasj76/id-as-string
Add support to specify service ID as a string
2018-12-11 21:12:50 +01:00
Jonas Johansson f5f1a55747 Add support to specify service ID as a string
Signed-off-by: Jonas Johansson <jonasj76@gmail.com>
2018-12-11 16:43:05 +01:00
Joachim Nilsson a36aade7f4 Merge pull request #113 from srand/master
Bugfixes and features
2018-10-23 07:15:20 +02:00
Robert Andersson 76fc72770e pidfile: monitor renamed files, e.g. rsyslogd.pid.tmp
Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:53:57 +02:00
Robert Andersson b64dbbf8d0 fix condition string lengths
Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Robert Andersson d371b85c47 fix C++ compilation issues
Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Robert Andersson 053304012f Add option to log service/task output to the console
Example:
    service log:console [2] /usr/sbin/udhcpd -f -- DHCP Server

Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Robert Andersson 12d14aae84 fix service enable/disable support
Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Robert Andersson 6a7f115781 Add config option for manually started services
Example:
    service manual:yes [2] /usr/sbin/lighttpd -- HTTP Server

Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Robert Andersson 423c8d41a7 modules-load: add task name config
Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Robert Andersson f2cab53e55 Add service/task name config option
Allows the service/task name derived from the binary to be
overridden with a custom name.

Example:
    service name:dhcpd [2] /usr/sbin/udhcpd -f -- DHCP Server

Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Robert Andersson 33fd6f0049 Add modules-load.d plugin
This plugin scans /etc/modules-load.d for modules to modprobe.

Signed-off-by: Robert Andersson <robert.m.andersson@se.atlascopco.com>
2018-10-22 12:51:58 +02:00
Joachim Nilsson e83dcafb02 stty(): Restore cursor as well, if it was hidden previously
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-19 17:52:00 +02:00
Joachim Nilsson 9a98bff23f Run all runparts scripts using /bin/sh -c foo
This patch is an attempt to address an issue with runparts scripts found
by Magnus Malm, @magnusmalm.  Any script in:

    runparts /foo/bar.d/

were required to have the #!/bin/sh she-bang, otherwise Finit would not
start them.  He tested with the standard run-parts tool, which had no
such restrictions.

The patch not only makes sure to start scripts using `/bin/sh -c`

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-18 22:19:51 +02:00
Joachim Nilsson 1a2f7d162b Helpful symlink to config.md
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-17 14:12:04 +02:00
58 changed files with 2218 additions and 631 deletions
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [troglobit]
+8 -6
View File
@@ -9,14 +9,16 @@ 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
# The secure: declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
env: env:
global: global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - PKG_CONFIG_PATH=/tmp/lib/pkgconfig
# via the "travis encrypt" command using the project repo's public key - CFLAGS="-W -Wall -Wextra"
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4=" - secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
addons: addons:
@@ -28,7 +30,7 @@ addons:
name: "troglobit/finit" name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux" description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp" build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
build_command: "make" build_command: "make"
branch_pattern: dev branch_pattern: dev
@@ -43,10 +45,10 @@ install:
# 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 --disable-inetd --enable-static - ./configure --prefix=/tmp --disable-inetd --enable-static
- make clean - make clean
- make V=1 -j5 - 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 - ./configure --prefix=/tmp --enable-inetd-echo-plugin --enable-inetd-time-plugin --enable-x11-common-plugin --enable-inetd-chargen-plugin --enable-fallback-shell --enable-watchdog --enable-logit
- cat config.log - cat config.log
- make clean - make clean
- make V=1 -j5 - make V=1 -j5
+44 -5
View File
@@ -4,14 +4,25 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[3.2][UNRELEASED] - 2018-10-xx [3.2][UNRELEASED] - 2020-04-xx
------------------------------ ------------------------------
Bug fix release. Bug fix release, but also cgroups and a new progress!
https://twitter.com/b0rk/status/1214341831049252870?s=20
### Changes ### Changes
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
* Support for `sysv` start/stop scripts as well as monitoring forking
services, stared using `sysv` or `service` stanza
* Support for custom `kill:DELAY`, default 3 sec.
* Support for custom `halt:SIGNAL`, default SIGTERM
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
useful with new `sysv` or `service` which fork to background
* Support starting run/task/services without absolute path, trust `$PATH`
* Add support for `--disable-docs` and `--disable-contrib` to speed up * Add support for `--disable-docs` and `--disable-contrib` to speed up
builds and work around issue with massively parallel builds builds and work around issue with massively parallel builds
* Add support for `@console` also for external getty
* Add `-b`, batch mode, for non-interactive use to `initctl` * Add `-b`, batch mode, for non-interactive use to `initctl`
* Prefer udev to handle `/dev/` if mdev is also available * Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog * Redirect dbus daemon output to syslog
@@ -19,7 +30,7 @@ Bug fix release.
* Finit no longer automatically reloads its `*.conf` files after running * Finit no longer automatically reloads its `*.conf` files after running
`/etc/rc.local` or run-parts. Use `initctl reload` instead. `/etc/rc.local` or run-parts. Use `initctl reload` instead.
* `initctl` without an argument or option now defaults to list services * `initctl` without an argument or option now defaults to list services
* Converted built-in watchdog daemon to standalone mini watchdogd * Convert built-in watchdog daemon to standalone mini watchdogd, issue #102
* Improved watchdog hand-over, now based on `svc_t` and not PID * Improved watchdog hand-over, now based on `svc_t` and not PID
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services * Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
not allowed in the runtime runlevel are unconditionally stopped not allowed in the runtime runlevel are unconditionally stopped
@@ -32,7 +43,22 @@ Bug fix release.
event, because services may depend on each other event, because services may depend on each other
* Require new libuEv API: `uev_init1()` to reduce event cache so that * Require new libuEv API: `uev_init1()` to reduce event cache so that
the kernel can invalidate deleted events before enqueing to userspace the kernel can invalidate deleted events before enqueing to userspace
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
from the `hwclock` tool. Note: the kernel can also be set to load
and store RTC to/from system clock at boot/halt as well, issue #110
* New plugin to support cold plugging devices, auto-loading of modules
at boot. Detects required modules by reading `/sys/devices/*`
* New plugin for `/etc/modules-load.d/` by Robert Andersson, Atlas Copco
* New `name:foo` support for services, by Robert Andersson, Atlas Copco
* New `manual:yes` support for services, by Robert Andersson, Atlas Copco
* New `log:console` support for services, by Robert Andersson, Atlas Copco
* Support for `:ID` as a string, by Jonas Johansson, Westermo
* Support for auto-reload, instead of having to do `initctl reload`,
when a service configuration has changed. Disabled by default, but
can be enabled with `./configure --enable-auto-reload`
* Support for logging security related events, e.g., runlevel change,
star/stop or failure to start services, by Jonas Holmberg, Westermo
### Fixes ### Fixes
* Fix #96: Start udevd as a proper service * Fix #96: Start udevd as a proper service
@@ -50,18 +76,29 @@ Bug fix release.
that do not have a watchdog that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does * Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access not yet exist (symlink to `/run`). Added compat layer for access
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY * 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 #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
* Fix #106: Don't mark inetd connections for deletion at .conf reload. * Fix #106: Don't mark inetd connections for deletion at .conf reload.
Fixed by Jonas Johansson, Westermo Fixed by Jonas Johansson, Westermo
* Fix #107: Stop spawned inetd conncections when stopping inetd service. * Fix #107: Stop spawned inetd conncections when stopping inetd service.
Fixed by Jonas Johansson, Westermo Fixed by Jonas Johansson, Westermo
* Fix #109: Support for PID files in sub-directories to `/var/run`
* Handle rename of PID files, by Robert Andersson, Atlas Copco
* Fix #111: Only restart inetd services when necessary. E.g., if the * Fix #111: Only restart inetd services when necessary. E.g., if the
listening interface is changed. Only stop established connections listening interface is changed. Only stop established connections
which are no longer allowed, i.e. do not touch already allowed which are no longer allowed, i.e. do not touch already allowed
established connections. Fixed by Jonas Johansson, Westermo established connections. Fixed by Jonas Johansson, Westermo
* Fix #120: Redirect `stdin` to `/dev/null` for services by default
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
fixed by Jacques de Laval, Westermo
* Fix #124: Lingering processes in process group when session leader
exits. E.g., lingering `logit` processes when parent dies
* Fix: update inetd service args on config change. Found and fixed by * Fix: update inetd service args on config change. Found and fixed by
Petrus Hellgren, Westermo Petrus Hellgren, Westermo
* Fix service name matching, e.g. for condition handling, may match with
wrong service, by Jonas Holmberg, Westermo
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard
run-parts tool. Found by Magnus Malm, Westermo
* Fix `initctl [start | restart]`, should behave the same for services * Fix `initctl [start | restart]`, should behave the same for services
that have crashed. Found by Mattias Walström, Westermo that have crashed. Found by Mattias Walström, Westermo
* Wait for bootstrap phase to complete before cleaning out any bootstrap * Wait for bootstrap phase to complete before cleaning out any bootstrap
@@ -69,7 +106,9 @@ Bug fix release.
* Reassert condition when an unmodified run/task/service goes from * Reassert condition when an unmodified run/task/service goes from
WAITING back to RUNNING again after a reconfiguration event. WAITING back to RUNNING again after a reconfiguration event.
Found and fixed by Jonas Johansson, Westermo Found and fixed by Jonas Johansson, Westermo
* Restore Ctrl-D and Ctrl-U support in built-in getty
* Remove service condition when service is deleted * Remove service condition when service is deleted
* Fix C++ compilation issues, by Robert Andersson, Atlas Copco
[3.1][] - 2018-01-23 [3.1][] - 2018-01-23
+24 -12
View File
@@ -1,4 +1,5 @@
<img align="right" src="images/finit3.png" alt="Finit: Fast Init"> [![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
* [Introduction](#introduction) * [Introduction](#introduction)
* [Features](#features) * [Features](#features)
@@ -21,21 +22,20 @@
Introduction Introduction
------------ ------------
[![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
Finit is a simple alternative to [SysV init][4] and [systemd][6]. It Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
was reverse engineered from the [EeePC fastinit][] ten years ago by was reverse engineered from the [EeePC fastinit][] over ten years ago
[Claudio Matsuoka][] — "gaps filled with frog DNA …" by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
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 with tutorials for: [contrib/](contrib/) section with tutorials for:
* [Alpine Linux](contrib/alpine/), * [Void Linux](contrib/void/),
* [Debian GNU/Linux](contrib/debian/), and * [Alpine Linux](contrib/alpine/), and
* [Void Linux](contrib/void/) * [Debian GNU/Linux](contrib/debian/)
![Alpine screenshot](images/alpine-screenshot.png) ![Alpine screenshot](img/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/). The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
@@ -280,6 +280,20 @@ from the kernel, leaving only warnings and errors. For other kernel
command line parameters, see [docs/build.md](docs/build.md#recovery). command line parameters, see [docs/build.md](docs/build.md#recovery).
**Automatic Reload**
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
registering any changes to `.conf` files. To activate a change the user
must call `initctl reload`, which reloads all modified files, stops any
removed services, starts new ones, and restarts any modified ones, with
SIGHUP if the process supports it.
For some use-cases the extra step of calling `initctl reload` creates an
unnecessary overhead, which can be removed at build-time using:
configure --enable-auto-reload
Runparts & /etc/rc.local Runparts & /etc/rc.local
------------------------ ------------------------
@@ -429,8 +443,6 @@ Commands:
touch <CONF> Mark .conf in /etc/finit.d/ for reload touch <CONF> Mark .conf in /etc/finit.d/ for reload
reload Reload *.conf in /etc/finit.d/ (activates changes) reload Reload *.conf in /etc/finit.d/ (activates changes)
cond set <COND> Set (assert) 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
@@ -533,12 +545,12 @@ and proposed extensions.
[openrc]: http://www.gentoo.org/proj/en/base/openrc/ [openrc]: http://www.gentoo.org/proj/en/base/openrc/
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[original finit]: http://helllabs.org/finit/ [original finit]: http://helllabs.org/finit/
[EeePC fastinit]: http://wiki.eeeuser.com/boot_process:the_boot_process [EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
[Claudio Matsuoka]: https://github.com/cmatsuoka [Claudio Matsuoka]: https://github.com/cmatsuoka
[Joachim Nilsson]: http://troglobit.com [Joachim Nilsson]: http://troglobit.com
[GitHub]: https://github.com/troglobit/finit [GitHub]: https://github.com/troglobit/finit
[License]: https://en.wikipedia.org/wiki/MIT_License [License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-yellow.svg [License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[Travis]: https://travis-ci.org/troglobit/finit [Travis]: https://travis-ci.org/troglobit/finit
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master [Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545 [Coverity Scan]: https://scan.coverity.com/projects/3545
+38 -6
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 3.2-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html) AC_INIT(Finit, 3.2-rc2, 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])
@@ -40,6 +40,9 @@ AC_TYPE_UINT8_T
AC_TYPE_UINT16_T AC_TYPE_UINT16_T
AC_TYPE_UINT32_T AC_TYPE_UINT32_T
# Check for pkg-config first, warn if it's not installed
PKG_PROG_PKG_CONFIG
# Check for required libraries # Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0]) PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.0.1]) PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
@@ -54,14 +57,22 @@ AC_ARG_ENABLE(fallback_shell,
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[ AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
enable_fallback_shell=no]) enable_fallback_shell=no])
AC_ARG_ENABLE(auto_reload,
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
enable_auto_reload=no])
AC_ARG_ENABLE(rw-rootfs, AC_ARG_ENABLE(rw-rootfs,
AS_HELP_STRING([--enable-rw-rootfs], [Remount / R/W at boot, usually not for embedded]),,[ AS_HELP_STRING([--enable-rw-rootfs], [Remount / R/W at boot, usually not for embedded]),,[
enable_rw_rootfs=no]) enable_rw_rootfs=no])
AC_ARG_ENABLE(progress, AC_ARG_ENABLE(progress,
AS_HELP_STRING([--enable-progress], [Pre Finit v3 bootup progress and heading]),,[ AS_HELP_STRING([--enable-progress], [Modern progress display at bootup]),,[
enable_progress=no]) enable_progress=no])
AC_ARG_ENABLE(progress-classic,
AS_HELP_STRING([--enable-progress-classic], [Pre Finit v3 bootup progress and heading]),,[
enable_progress_classic=no])
AC_ARG_ENABLE(watchdog, 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])
@@ -74,6 +85,10 @@ 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])
AC_ARG_ENABLE(logit,
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
enable_logit=yes])
AC_ARG_ENABLE(logrotate, 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])
@@ -95,6 +110,7 @@ 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([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
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.])
@@ -136,7 +152,6 @@ AC_ARG_WITH(random-seed,
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]), AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
[random_seed=$withval], [random_seed=/var/lib/misc/random-seed]) [random_seed=$withval], [random_seed=/var/lib/misc/random-seed])
### Enable features ########################################################################### ### Enable features ###########################################################################
# Create config.h from selected features and fallback defautls # Create config.h from selected features and fallback defautls
@@ -146,13 +161,24 @@ AS_IF([test "x$enable_emergency_shell" = "xyes"], [
AS_IF([test "x$enable_fallback_shell" = "xyes"], [ AS_IF([test "x$enable_fallback_shell" = "xyes"], [
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])]) AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
AS_IF([test "x$enable_auto_reload" = "xyes"], [
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
AS_IF([test "x$enable_rw_rootfs" = "xyes"], [ AS_IF([test "x$enable_rw_rootfs" = "xyes"], [
AC_DEFINE(REMOUNT_ROOTFS, 1, [Remount / as rw at boot])]) AC_DEFINE(REMOUNT_ROOTFS, 1, [Remount / as rw at boot])])
AS_IF([test "x$enable_progress" = "xyes"], [ AS_IF([test "x$enable_progress" = "xyes"], [
AC_DEFINE(SILENT_MODE, 0, [Pre Finit v3 bootup messages and heading]) AC_DEFINE(PROGRESS_STYLE, 2, [Modern bootup messages, systemd inspired])])
AS_IF([test "x$enable_progress_classic" = "xyes"], [
enable_progress=no
AC_DEFINE(PROGRESS_STYLE, 1, [Pre Finit v3 bootup messages and heading])])
AS_IF([test "x$enable_progress_classic" = "xyes" -o "x$enable_progress" = "xyes"], [
AC_DEFINE(SILENT_MODE, 0, [Display bootup progress, see above for style])
], [ ], [
AC_DEFINE(SILENT_MODE, 1, [Completely silent until login prompt])]) AC_DEFINE(PROGRESS_STYLE, 0, [Default progress style])
AC_DEFINE(SILENT_MODE, 1, [Completely silent until login prompt (default)])])
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])])
@@ -165,6 +191,9 @@ AS_IF([test "x$enable_inetd" != "xno"], [
enable_inetd="yes" enable_inetd="yes"
AC_DEFINE(INETD_ENABLED, 1, [Enable built-in inetd])]) AC_DEFINE(INETD_ENABLED, 1, [Enable built-in inetd])])
AS_IF([test "x$enable_logit" != "xno"], [
AC_DEFINE(LOGIT_ENABLED, 1, [Enable logit tool, used for log redirection by Finit])])
AS_IF([test "x$enable_logrotate" != "xno"], [ AS_IF([test "x$enable_logrotate" != "xno"], [
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])]) AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
@@ -223,6 +252,7 @@ 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(LOGIT, [test "x$enable_logit" = "xyes"])
AM_CONDITIONAL(DOCS, [test "x$enable_docs" = "xyes"]) AM_CONDITIONAL(DOCS, [test "x$enable_docs" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"]) AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
@@ -268,10 +298,12 @@ Optional features:
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
Scripting tool logit..: $enable_logit
Emergency shell.......: $enable_emergency_shell Emergency shell.......: $enable_emergency_shell
Fallback shell........: $enable_fallback_shell Fallback shell........: $enable_fallback_shell
Remount / RW at boot..: $enable_rw_rootfs Remount / RW at boot..: $enable_rw_rootfs
Traditional progress..: $enable_progress Modern progress.......: $enable_progress
Classic progress......: $enable_progress_classic
Default console dev...: $console Default console dev...: $console
Default hostname......: $hostname Default hostname......: $hostname
Default runlevel......: $runlevel Default runlevel......: $runlevel
-10
View File
@@ -66,9 +66,6 @@ how `git commit -s` is used to automatically add a `Signed-off-by`:
Signed-off-by: Jane Doe <jane.doe@example.com> Signed-off-by: Jane Doe <jane.doe@example.com>
Another good *counter* example [is this][rambling] ...
Code of Conduct Code of Conduct
--------------- ---------------
@@ -79,11 +76,4 @@ Bill & Ted; [be excellent to each other][conduct].
[github]: https://github.com/troglobit/finit/ [github]: https://github.com/troglobit/finit/
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
[gitbook]: https://git-scm.com/book/ch5-2.html [gitbook]: https://git-scm.com/book/ch5-2.html
[rambling]: http://stopwritingramblingcommitmessages.com/
[conduct]: https://github.com/troglobit/finit/blob/master/CODE-OF-CONDUCT.md [conduct]: https://github.com/troglobit/finit/blob/master/CODE-OF-CONDUCT.md
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+1
View File
@@ -0,0 +1 @@
config.md
+5 -4
View File
@@ -21,6 +21,9 @@ can also be used.
Near Future Near Future
----------- -----------
* When a process dies, and Finit does not restart it, we should collect
a `siginfo_t` from the SIGCHLD signal and supply to the user in case
`initctl status <process>` is called to debug the issue.
* Merge UDP redirect service uredir as an UDP inetd variant * Merge UDP redirect service uredir as an UDP inetd variant
* 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
@@ -30,8 +33,6 @@ Near Future
* Cron/At support, see below * Cron/At support, see below
* Write man pages for finit and `finit.conf`, steal from the excellent * Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ... `pimd` man pages ...
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
how close Finit3 still is to the original easy-to-use Finit0 :)
General General
@@ -59,6 +60,8 @@ General
now inotify listeners should be automatically created (and removed) now inotify listeners should be automatically created (and removed)
when a subdirectory is added (or removed). when a subdirectory is added (or removed).
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
Init Init
---- ----
@@ -114,5 +117,3 @@ could support the basic functionality of the `at` command directly from
the shell -- that way setting up one-time jobs would not entail the shell -- that way setting up one-time jobs would not entail
re-reading `/etc/finit.conf` re-reading `/etc/finit.conf`
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
+28 -17
View File
@@ -47,17 +47,7 @@ service's PID file being created.
Triggering Triggering
---------- ----------
Conditions are triggered by built-in plugins, but can for debugging Conditions can only be triggered by built-in plugins.
purposes also be controlled using the `cond set` and `cond clear`
sub-commands to the `initctl` control tool:
* `initctl cond set your/cond/here`
To set a condition
* `initctl cond clear your/cond/here`
To clear a condition
Conditions retain their current state until the next reconfiguration or Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the runlevel change. At that point all set conditions transition into the
@@ -102,6 +92,25 @@ Built-in conditions:
difference is that `running` tells if the NIC has link. difference is that `running` tells if the NIC has link.
Composition
-----------
The `svc/` conditions can be quite tricky to understand. They are
generated by the Finit `pidfile.so` plugin and are composed from the
daemon's path and the pidfile name (and path).
| **service** | **pid:** | **pidfile path** | **svc condition** |
|--------------------------------------|-----------------|------------------|-------------------|
| /sbin/foo | | /run/foo.pid | svc/sbin/foo |
| /sbin/bar -p /run/baz.pid | pid:baz | /run/bas.pid | svc/sbin/baz |
| lxc-start -n foo -p /run/lxc/foo.pid | pid:lxc/foo.pid | /run/lxc/foo.pid | svc/lxc/foo |
| /usr/bin/dbus-daemon | pid:dbus/pid | /run/dbus/pid | svc/usr/bin/dbus |
Omitting the `pid:` to run/task/service/sysv stanza means Finit will
guess the pidfile path based on `/run/` and the basename(1) of the
program.
Debugging Debugging
--------- ---------
@@ -133,12 +142,13 @@ To fake interface `vlan1` suddenly appearing, and test what happens to
```shell ```shell
~ # initctl debug ~ # initctl debug
~ # initctl cond set net/vlan1/exist ~ # mkdir -p /var/run/finit/cond/net/vlan1
~ # cp /var/run/finit/cond /var/run/finit/cond/net/vlan1/exist
``` ```
Then watch the console for the debug messages and then check the output Then watch the console for the debug messages and then check the output
from `initctl cond show` again. (The client will likely have failed to from `initctl cond show` again. The client will likely have failed to
start, but at least the condition is now satisfied.) start, but at least the condition is now satisfied.
There is also the `initctl cond dump` command, which dumps all known There is also the `initctl cond dump` command, which dumps all known
conditions and their current status. conditions and their current status.
@@ -147,9 +157,10 @@ conditions and their current status.
Internals Internals
--------- ---------
Conditions are implemented as simple files in the file system, in the As shown previously, conditions are implemented as simple files in the
`/var/run/finit/cond/` sub-directory. Use the `initctl cond set/clear` file system, in the `/var/run/finit/cond/` sub-directory. The files
commands to add/remove files in this hierarchy. are created, updated, and removed by condition plugins. To debug them,
see the previous section.
A condition is always in one of three states: A condition is always in one of three states:
+68 -12
View File
@@ -93,7 +93,16 @@ Syntax
case the contents of that file is used. case the contents of that file is used.
* `module <MODULE> [ARGS]` * `module <MODULE> [ARGS]`
Load a kernel module, with optional arguments Load a kernel module, with optional arguments. Similar to `insmod`
command line tool.
Note, there is both a `modules-load.so` and a `modprobe.so` plugin
that can handle module loading better. The former supports loading
from `/etc/modules-load.d/`, and the latter uses kernel modinfo to
automatically load (or coldplug) every required module. For hotplug
we recommend the BusyBox mdev tool, add to `/etc/mdev.conf`:
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
* `network <PATH>` * `network <PATH>`
Script or program to bring up networking, with optional arguments Script or program to bring up networking, with optional arguments
@@ -143,11 +152,36 @@ Syntax
task [s] echo "foo" | cat >/tmp/bar task [s] echo "foo" | cat >/tmp/bar
``` ```
* `sysv [LVLS] <COND> /path/to/init-script -- Optional description`__
Similar to `task` is the `sysv` stanza, which can be used to call SysV
style scripts. The primary intention for this command is to be able to
re-use much of existing setup and init scripts in Linux distributions.
When entering an allowed runlevel, Finit calls `init-script start`,
when entering a disallowed runlevel, Finit calls `init-script stop`,
and if the Finit .conf, where `sysv` stanza is declared, is modified,
Finit calls `init-script restart` on `initctl reload`. Similar to
how `service` stanzas work.
Forking services started with `sysv` scripts can be monitored by Finit
by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
* `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description` * `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Please note that you often need to provide a exits prematurely.
`--foreground` or `--no-background` argument to most daemons to
prevent them from forking off a sub-process in the background. For daemons that support it, we recommend appending `--foreground`, or
`--no-background`, argument to prevent them from forking off a
sub-process in the background. This is the most reliable way to
monitor a service.
However, not all daemons support running in the foreground, or they
may start logging to the foreground as well, these are called forking
services and are supported using the same syntax as forking `sysv`
services, using the `pid:!/path/to/pidfile.pid` syntax.
In the case of `ospfd` (below), we omit the `-d` flag to prevent it
from forking (daemonizing):
```shell ```shell
service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon
@@ -169,21 +203,42 @@ Syntax
pid[:[/path/to/]filename[.pid]] pid[:[/path/to/]filename[.pid]]
For example, by adding `pid:/run/foo.pid` to the service `bar` that For example, by adding `pid:/run/foo.pid` to the service `/sbin/bar`
PID file will, not only be created and removed automatically, but also that PID file will, not only be created and removed automatically, but
be used by the Finit condition subsystem. So another service/run/task also be used by the Finit condition subsystem. So a service/run/task
can depend on `<svc/foo>`. can depend on `<svc/sbin/foo>`, notice the composition of conditions.
If a service `bar` *does* maintain a PID file, but using `foo.pid`, we However, if a service `bar` *does* create a PID file, using `foo.pid`,
can inform Finit of this by simplly prepending an `!` to the argument. we can inform Finit of this by prepending an `!`:
pid:!/run/foo.pid pid:!/run/foo.pid
Here Finit will *not* create/remove/touch the PID file, only use it 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 the condition handling instead of the default PID file name.
For a detailed description of conditions, and how to debug them, see > For a detailed description of conditions, and how to debug them, see
the [Finit Conditions](conditions.md) document. > the [Finit Conditions](conditions.md) document.
If a service should not be automatically started, it can be configured
as manual with the optional `manual` argument. The service can then be
started at any time by running `initctl start <service>`.
manual:yes
The name of a service, shown by the `initctl` tool, defaults to the
basename of the service executable. It can be changed with the
optional `name` argument:
name:<service-name>
When stopping a service (run/task/sysv/service), either manually or
when moving to another runlevel, Finit starts by sending `SIGTERM`, to
allow the process to shut down gracefully. If the process has not
been collected within 3 seconds, Finit sends `SIGKILL`. To halt the
process using a different signal, use the option `halt:SIGNAL`, e.g.,
`halt:SIGPWR`. To change the delay between your halt signal and KILL,
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
sending `SIGKILL`.
* `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
@@ -338,6 +393,7 @@ syslog using the native `logit` tool. The full syntax is:
log:/path/to/file log:/path/to/file
log:prio:facility.level,tag:ident log:prio:facility.level,tag:ident
log:console
log:null log:null
log log
+3 -2
View File
@@ -42,11 +42,12 @@ For your convenience a set of *optional* plugins are available:
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below. * *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
* *hwclock.so*: Restore and save system clock from/to RTC on * *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
startup/shutdown.
* *initctl.so*: Extends finit with a traditional `initctl` functionality. * *initctl.so*: Extends finit with a traditional `initctl` functionality.
* *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
* *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

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

+10 -2
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC if STATIC
noinst_LTLIBRARIES = libplug.la noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c hwclock.c initctl.c pidfile.c procps.c tty.c urandom.c libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c initctl.c pidfile.c procps.c tty.c urandom.c
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c libplug_la_SOURCES += alsa-utils.c
@@ -39,6 +39,10 @@ libplug_la_SOURCES += time.c
endif endif
endif endif
if BUILD_MODULES_LOAD_PLUGIN
libplug_la_SOURCES += modules-load.c
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
libplug_la_SOURCES += netlink.c libplug_la_SOURCES += netlink.c
endif endif
@@ -48,7 +52,7 @@ libplug_la_SOURCES += resolvconf.c
endif endif
else else
pkglib_LTLIBRARIES = bootmisc.la hwclock.la initctl.la pidfile.la procps.la tty.la urandom.la pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la initctl.la pidfile.la procps.la tty.la urandom.la
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la pkglib_LTLIBRARIES += alsa-utils.la
@@ -80,6 +84,10 @@ pkglib_LTLIBRARIES += time.la
endif endif
endif endif
if BUILD_MODULES_LOAD_PLUGIN
pkglib_LTLIBRARIES += modules-load.la
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
pkglib_LTLIBRARIES += netlink.la pkglib_LTLIBRARIES += netlink.la
endif endif
+1 -1
View File
@@ -58,7 +58,7 @@ 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] log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC); snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, 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);
+176
View File
@@ -0,0 +1,176 @@
/* Coldplug modules using modalias magic
*
* Copyright (c) 2020 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.
*/
/*
* Cold plug magic taken from Buildroot
* https://github.com/buildroot/buildroot/commit/b4fc5a180c81689a982d5c595844331684c14f51
*
* The plugin basically does this, only in C:
*
* system("find /sys/devices -name modalias -print0"
* " | xargs -0 sort -u -z"
* " | xargs -0 modprobe -abq");
*
* Note: BusyBox must *not* be built with CONFIG_MODPROBE_SMALL
*/
#include <fnmatch.h>
#include <ftw.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */
#include <sys/types.h>
#include <lite/lite.h>
#include "config.h"
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
struct module {
TAILQ_ENTRY(module) link;
char *alias;
};
static TAILQ_HEAD(, module) modules = TAILQ_HEAD_INITIALIZER(modules);
static int modprobe(char *alias)
{
char *args[] = {
"modprobe",
"-abq",
alias,
NULL,
};
pid_t pid;
pid = fork();
switch (pid) {
case -1:
_pe("Failed forking modprobe child");
return 1;
case 0:
execvp(args[0], args);
break;
default:
if (!complete(args[0], pid))
_d("Successful modprobe of %s", alias);
break;
}
return 0;
}
static void alias_add(char *alias)
{
struct module *m;
m = malloc(sizeof(*m));
if (!m)
return;
m->alias = strdup(alias);
if (!m->alias) {
free(m);
return;
}
TAILQ_INSERT_TAIL(&modules, m, link);
}
static void alias_remove(struct module *m)
{
TAILQ_REMOVE(&modules, m, link);
free(m->alias);
free(m);
}
static int alias_exist(char *alias)
{
struct module *m;
TAILQ_FOREACH(m, &modules, link) {
if (!strcmp(m->alias, alias))
return 1;
}
return 0;
}
static int scan_alias(const char *path, const struct stat *st, int flag, struct FTW *unused)
{
FILE *fp;
char buf[256];
if (fnmatch("*/modalias", path, 0))
return 0;
fp = fopen(path, "r");
if (!fp)
return 0;
if (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
if (!alias_exist(buf))
alias_add(buf);
}
fclose(fp);
return 0;
}
static void coldplug(void *arg)
{
struct module *m, *tmp;
int rc = 0;
print_desc("Cold plugging system", NULL);
nftw("/sys/devices", scan_alias, 200, FTW_DEPTH);
TAILQ_FOREACH_SAFE(m, &modules, link, tmp) {
rc += modprobe(m->alias);
alias_remove(m);
}
print_result(rc);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = coldplug },
.depends = { "bootmisc", }
};
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:
*/
+107
View File
@@ -0,0 +1,107 @@
/* Scans /etc/modules-load.d for modules to load.
*
* Copyright (c) 2018 Robert Andersson <robert.m.andersson@se.atlascopco.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <lite/lite.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "service.h"
#include "conf.h"
#define MODULES_LOAD_PATH "/etc/modules-load.d"
#define SERVICE_LINE \
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
static void load(void *arg)
{
FILE *fp;
DIR *dirp;
struct dirent *direntp;
char line[256];
int index = 1;
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
dirp = opendir(MODULES_LOAD_PATH);
if (!dirp) return;
while ((direntp = readdir(dirp)))
{
char module_path[PATH_MAX];
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, direntp->d_name, sizeof(module_path));
fp = fopen(module_path, "r");
if (!fp) continue;
while (fgets(line, sizeof(line), fp)) {
char cmd[CMD_SIZE], *mod, *args;
mod = strip_line(line);
if (!*mod) continue;
mod = chomp(mod);
if (!mod || !*mod) continue;
mod = strtok_r(mod, " ", &args);
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
index, mod, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
index++;
}
fclose(fp);
}
closedir(dirp);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = load
},
};
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:
*/
+219 -40
View File
@@ -1,6 +1,7 @@
/* Simple pidfile event monitor for the Finit condition engine /* Simple pidfile event monitor for the Finit condition engine
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2020 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,55 +22,234 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <glob.h>
#include <limits.h> #include <limits.h>
#include <paths.h> #include <paths.h>
#include <lite/queue.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "helpers.h" #include "helpers.h"
#include "pid.h"
#include "plugin.h" #include "plugin.h"
#include "service.h" #include "service.h"
struct wd_entry {
TAILQ_ENTRY(wd_entry) link;
char *path;
int fd;
};
struct context { struct context {
int fd; int fd;
int wd; TAILQ_HEAD(, wd_entry) wd_list;
}; };
static struct context pidfile_ctx;
static int watcher_add(struct context *ctx, char *path)
{
struct wd_entry *wd;
uint32_t mask = IN_ONLYDIR | IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO;
char *ptr;
int fd;
_d("pidfile: Adding new watcher for path %s", path);
ptr = strstr(path, "run/");
if (ptr) {
char *slash;
ptr += 4;
slash = strchr(ptr, '/');
if (slash) {
ptr = slash++;
slash = strchr(ptr, '/');
if (slash) {
_d("pidfile: Path too deep, skipping.");
return -1;
}
}
}
fd = inotify_add_watch(ctx->fd, path, mask);
if (fd < 0) {
_pe("inotify_add_watch()");
return -1;
}
wd = malloc(sizeof(struct wd_entry));
if (!wd) {
_pe("Failed allocating new `struct wd_entry`");
inotify_rm_watch(ctx->fd, fd);
return -1;
}
wd->path = path;
wd->fd = fd;
TAILQ_INSERT_HEAD(&ctx->wd_list, wd, link);
return 0;
}
static int watcher_del(struct context *ctx, struct wd_entry *wde)
{
_d("pidfile: Removing watcher for removed path %s", wde->path);
TAILQ_REMOVE(&ctx->wd_list, wde, link);
inotify_rm_watch(ctx->fd, wde->fd);
free(wde->path);
free(wde);
return 0;
}
static void update_conds(char *name, uint32_t mask)
{
svc_t *svc;
char cond[MAX_COND_LEN];
_d("name: %s", name);
svc = svc_find_by_pidfile(name);
if (!svc) {
_d("pidfile: No matching svc for %s", name);
return;
}
_d("Event %s for svc %s", name, svc->cmd);
mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
svc_started(svc);
if (svc_is_forking(svc)) {
pid_t pid;
pid = pid_file_read(pid_file(svc));
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
}
cond_set(cond);
} else if (mask & IN_DELETE)
cond_clear(cond);
}
/* synthesize events in case of new run dirs */
static void scan_for_pidfiles(struct context *ctx, char *dir, int len)
{
glob_t gl;
size_t i;
char path[len + 6];
int rc;
snprintf(path, sizeof(path), "%s/*.pid", dir);
rc = glob(path, GLOB_NOSORT, NULL, &gl);
if (rc && rc != GLOB_NOMATCH)
return;
snprintf(path, sizeof(path), "%s/pid", dir);
rc = glob(path, GLOB_NOSORT | GLOB_APPEND, NULL, &gl);
if (rc && rc != GLOB_NOMATCH)
return;
for (i = 0; i < gl.gl_pathc; i++) {
_d("scan found %s", gl.gl_pathv[i]);
update_conds(gl.gl_pathv[i], IN_CREATE);
}
globfree(&gl);
}
static void handle_dir(struct context *ctx, struct wd_entry *wde, char *name, int mask)
{
char *path;
int plen;
int exist = 0;
plen = snprintf(NULL, 0, "%s/%s", wde->path, name) + 1;
path = malloc(plen);
if (!path) {
_pe("Failed allocating path buffer for watcher");
return;
}
snprintf(path, plen, "%s/%s", wde->path, name);
_d("pidfile: path is %s", path);
TAILQ_FOREACH(wde, &ctx->wd_list, link) {
if (!strcmp(wde->path, path)) {
exist = 1;
break;
}
}
if (mask & IN_CREATE) {
if (!exist) {
int rc = watcher_add(ctx, path);
scan_for_pidfiles(ctx, path, plen);
if (!rc)
return;
}
} else if (mask & IN_DELETE) {
if (exist)
watcher_del(&pidfile_ctx, wde);
}
free(path);
}
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 cond[MAX_COND_LEN];
struct inotify_event *ev; struct inotify_event *ev;
ssize_t sz, len; ssize_t sz, off;
svc_t *svc; size_t buflen = 8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1;
char *buf;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); buf= malloc(buflen);
if (sz <= 0) { if (!buf) {
_pe("invalid inotify event"); _pe("Failed allocating buffer for inotify events");
return; return;
} }
ev_buf[sz] = 0;
for (ev = (void *)ev_buf; _d("pidfile: Entering ... reading %zu bytes into ev_buf[]", buflen - 1);
sz > (ssize_t)sizeof(*ev); sz = read(fd, buf, buflen - 1);
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) { if (sz <= 0) {
if (!ev->mask || !strstr(ev->name, ".pid")) _pe("invalid inotify event");
continue; goto done;
svc = svc_find_by_pidfile(ev->name);
if (!svc)
continue;
mkcond(cond, sizeof(cond), svc->cmd);
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
svc_started(svc);
cond_set(cond);
} else if (ev->mask & IN_DELETE)
cond_clear(cond);
} }
buf[sz] = 0;
_d("pidfile: Read %zd bytes, processing ...", sz);
off = 0;
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
struct wd_entry *wde;
ev = (struct inotify_event *)&buf[off];
_d("pidfile: path %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
/* Find base path for this event */
TAILQ_FOREACH(wde, &pidfile_ctx.wd_list, link) {
if (wde->fd == ev->wd)
break;
}
if (ev->mask & IN_ISDIR) {
handle_dir(&pidfile_ctx, wde, ev->name, ev->mask);
continue;
}
if (ev->mask & IN_DELETE) {
_d("pidfile %s/%s removed ...", wde->path, ev->name);
continue;
}
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
update_conds(ev->name, ev->mask);
}
done:
free(buf);
} }
/* /*
@@ -91,7 +271,7 @@ static void pidfile_reconf(void *arg)
if (svc_is_changed(svc) || svc_is_starting(svc)) if (svc_is_changed(svc) || svc_is_starting(svc))
continue; continue;
mkcond(cond, sizeof(cond), svc->cmd); mkcond(svc, cond, sizeof(cond));
if (cond_get(cond) == COND_ON) if (cond_get(cond) == COND_ON)
continue; continue;
@@ -110,7 +290,6 @@ static void pidfile_reconf(void *arg)
static void pidfile_init(void *arg) static void pidfile_init(void *arg)
{ {
char *path; char *path;
struct context *ctx = arg;
/* /*
* The bootmisc plugin is responsible for setting up /var/run. * The bootmisc plugin is responsible for setting up /var/run.
@@ -123,15 +302,9 @@ static void pidfile_init(void *arg)
return; return;
} }
ctx->wd = inotify_add_watch(ctx->fd, path, IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY); TAILQ_INIT(&pidfile_ctx.wd_list);
free(path); if (watcher_add(&pidfile_ctx, path))
close(pidfile_ctx.fd);
if (ctx->wd < 0) {
_pe("inotify_add_watch()");
close(ctx->fd);
return;
}
_d("pidfile monitor active"); _d("pidfile monitor active");
} }
@@ -143,7 +316,7 @@ static struct context pidfile_ctx;
*/ */
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .arg = &pidfile_ctx, .cb = pidfile_init }, .hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf }, .hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
.io = { .io = {
.cb = pidfile_callback, .cb = pidfile_callback,
@@ -166,7 +339,13 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(plugin_exit)
{ {
inotify_rm_watch(pidfile_ctx.fd, pidfile_ctx.wd); struct wd_entry *wde, *tmp;
TAILQ_FOREACH_SAFE(wde, &pidfile_ctx.wd_list, link, tmp) {
inotify_rm_watch(pidfile_ctx.fd, wde->fd);
free(wde->path);
free(wde);
}
close(pidfile_ctx.fd); close(pidfile_ctx.fd);
plugin_unregister(&plugin); plugin_unregister(&plugin);
+160
View File
@@ -0,0 +1,160 @@
/* Save and restore RTC using hwclock
*
* Copyright (c) 2012-2019 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 <time.h>
#include <sys/ioctl.h>
#include <linux/rtc.h>
#include <lite/lite.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
/* Kernel RTC driver validates against this date for sanity check */
#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */
static int rtc_open(void)
{
char *alt[] = {
"/dev/rtc",
"/dev/rtc0",
"/dev/misc/rtc"
};
size_t i;
int fd;
for (i = 0; i < NELEMS(alt); i++) {
fd = open(alt[i], O_RDONLY);
if (fd < 0)
continue;
return fd;
}
return -1;
}
static void rtc_save(void *arg)
{
struct timeval tv = { 0 };
struct tm tm = { 0 };
int fd, rc = 0;
fd = rtc_open();
if (fd < 0)
return;
rc = gettimeofday(&tv, NULL);
if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
print_desc(NULL, "System clock invalid, not saving to RTC");
invalid:
logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC");
rc = 2;
goto out;
}
print_desc(NULL, "Saving system time (UTC) to RTC");
gmtime_r(&tv.tv_sec, &tm);
if (ioctl(fd, RTC_SET_TIME, &tm) < 0) {
if (EINVAL == errno)
goto invalid;
rc = 1;
goto out;
}
out:
print(rc, NULL);
close(fd);
}
static void rtc_restore(void *arg)
{
struct timeval tv = { 0 };
struct tm tm = { 0 };
int fd, rc = 0;
fd = rtc_open();
if (fd < 0)
return;
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
char msg[120];
snprintf(msg, sizeof(msg), "Failed restoring system clock, %s",
EINVAL == errno ? "RTC time is too old" :
ENOENT == errno ? "RTC has no saved time" : "see log for details");
print_desc(NULL, msg);
invalid:
logit(LOG_ERR, "Failed restoring system clock from RTC.");
if (EINVAL == errno)
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)");
else if (ENOENT == errno)
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
else
logit(LOG_ERR, "RTC error code %d: %m", errno);
rc = 2;
goto out;
}
print_desc(NULL, "Restoring system clock (UTC) from RTC");
tv.tv_sec = mktime(&tm);
if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
errno = EINVAL;
goto invalid;
}
if (settimeofday(&tv, NULL) == -1)
rc = 1;
out:
print(rc, NULL);
close(fd);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = rtc_restore
},
.hook[HOOK_SHUTDOWN] = {
.cb = rtc_save
}
};
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:
*/
+6 -3
View File
@@ -1,4 +1,4 @@
EXTRA_DIST = rescue.conf EXTRA_DIST = rescue.conf cgreaper.sh
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
@@ -8,16 +8,19 @@ else
AM_LDFLAGS = -export-dynamic AM_LDFLAGS = -export-dynamic
endif endif
bin_PROGRAMS = logit
sbin_PROGRAMS = finit initctl reboot sbin_PROGRAMS = finit initctl reboot
pkglibexec_SCRIPTS = cgreaper.sh
if WATCHDOGD if WATCHDOGD
pkglibexec_PROGRAMS = watchdogd pkglibexec_PROGRAMS = watchdogd
endif endif
if LOGIT
bin_PROGRAMS = logit
logit_SOURCES = logit.c logit_SOURCES = logit.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
endif
finit_SOURCES = api.c \ finit_SOURCES = api.c cgroup.c cgroup.h \
cond.c cond-w.c cond.h \ cond.c cond-w.c cond.h \
telinit.c \ telinit.c \
conf.c conf.h \ conf.c conf.h \
+38 -61
View File
@@ -93,27 +93,26 @@ static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); } static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
static char query_buf[368]; static char query_buf[368];
static int missing(char *job, int id) static int missing(char *job, char *id)
{ {
char buf[20]; char buf[20];
char idstr[13] = "";
if (!job) if (!job)
job = ""; job = "";
if (id > 1) if (!id)
snprintf(idstr, sizeof(idstr), ":%d", id); id = "";
snprintf(buf, sizeof(buf), "%s%s ", job, idstr); snprintf(buf, sizeof(buf), "%s:%s ", job, id);
strlcat(query_buf, buf, sizeof(query_buf)); strlcat(query_buf, buf, sizeof(query_buf));
return 1; return 1;
} }
static int do_query(struct init_request *rq, size_t len) static int do_query(char *buf, size_t len)
{ {
memset(query_buf, 0, sizeof(query_buf)); memset(query_buf, 0, sizeof(query_buf));
if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) { if (svc_parse_jobstr(buf, len, NULL, missing)) {
memcpy(rq->data, query_buf, sizeof(rq->data)); memcpy(buf, query_buf, len);
return 1; return 1;
} }
@@ -122,8 +121,9 @@ static int do_query(struct init_request *rq, size_t len)
static svc_t *do_find(char *buf, size_t len) static svc_t *do_find(char *buf, size_t len)
{ {
int id = 1; char *id = NULL;
char *ptr, *input; char *ptr, *input;
svc_t *iter = NULL;
input = sanitize(buf, len); input = sanitize(buf, len);
if (!input) if (!input)
@@ -132,11 +132,28 @@ static svc_t *do_find(char *buf, size_t len)
ptr = strchr(input, ':'); ptr = strchr(input, ':');
if (ptr) { if (ptr) {
*ptr++ = 0; *ptr++ = 0;
id = atonum(ptr); id = ptr;
} }
if (isdigit(input[0])) if (isdigit(input[0])) {
return svc_find_by_jobid(atonum(input), id); char *ep;
long job = 0;
errno = 0;
job = strtol(input, &ep, 10);
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
(errno != 0 && job == 0) ||
(input == ep))
return NULL;
if (!id)
return svc_job_iterator(&iter, 1, job);
return svc_find_by_jobid(job, id);
}
if (!id)
return svc_named_iterator(&iter, 1, input);
return svc_find_by_nameid(input, id); return svc_find_by_nameid(input, id);
} }
@@ -164,47 +181,6 @@ ev_t ev_list[] = {
{ NULL, NULL } { NULL, NULL }
}; };
static int do_handle_event(char *event)
{
int i;
for (i = 0; ev_list[i].event; i++) {
ev_t *e = &ev_list[i];
size_t len = MAX(strlen(e->event), strlen(event));
if (!strncasecmp(e->event, event, len)) {
e->cb();
return 0;
}
}
if (event[0] == '-')
cond_clear(&event[1]);
else if (event[0] == '+')
cond_set(&event[1]);
else
cond_set(event);
return 0;
}
static int do_handle_emit(char *buf, size_t len)
{
int result = 0;
char *input, *event, *pos;
input = sanitize(buf, len);
if (!input)
return -1;
event = strtok_r(input, " ", &pos);
while (event) {
result += do_handle_event(event);
event = strtok_r(NULL, " ", &pos);
}
return result;
}
static void send_svc(int sd, svc_t *svc) static void send_svc(int sd, svc_t *svc)
{ {
svc_t empty; svc_t empty;
@@ -299,31 +275,30 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_START_SVC: case INIT_CMD_START_SVC:
_d("start %s", rq.data); _d("start %s", rq.data);
strterm(rq.data, sizeof(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); _d("stop %s", rq.data);
strterm(rq.data, sizeof(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); _d("restart %s", rq.data);
strterm(rq.data, sizeof(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"); _d("query inetd");
strterm(rq.data, sizeof(rq.data));
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:
_d("emit %s", rq.data);
result = do_handle_emit(rq.data, sizeof(rq.data));
break;
case INIT_CMD_GET_RUNLEVEL: case INIT_CMD_GET_RUNLEVEL:
_d("get runlevel"); _d("get runlevel");
rq.runlevel = runlevel; rq.runlevel = runlevel;
@@ -352,8 +327,8 @@ static void api_cb(uev_t *w, void *arg, int events)
if (wdog) { if (wdog) {
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid); logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
stop(wdog); stop(wdog);
if (wdog->protected) { if (wdog->protect) {
wdog->protected = 0; wdog->protect = 0;
wdog->runlevels = 0; wdog->runlevels = 0;
} }
} }
@@ -373,11 +348,13 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_SVC_QUERY: case INIT_CMD_SVC_QUERY:
_d("svc query: %s", rq.data); _d("svc query: %s", rq.data);
result = do_query(&rq, len); strterm(rq.data, sizeof(rq.data));
result = do_query(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_SVC_FIND: case INIT_CMD_SVC_FIND:
_d("svc find: %s", rq.data); _d("svc find: %s", rq.data);
strterm(rq.data, sizeof(rq.data));
send_svc(sd, do_find(rq.data, sizeof(rq.data))); send_svc(sd, do_find(rq.data, sizeof(rq.data)));
goto leave; goto leave;
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
# Called by kernel when last child in cgroup exists
BASE=/sys/fs/cgroup/finit
PROC=`basename $1`
DIR="$BASE$1"
logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
rmdir $DIR
exit 0
+140
View File
@@ -0,0 +1,140 @@
/* Finit control group support functions
*
* Copyright (c) 2019 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 <stdio.h>
#include <string.h>
#include <lite/lite.h>
#include <sys/mount.h>
#include "log.h"
#include "util.h"
static int cg_init = 0;
/*
* Called by Finit at early boot to mount initial cgroups
*/
void cgroup_init(void)
{
FILE *fp;
char buf[80];
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
fp = fopen("/proc/cgroups", "r");
if (!fp) {
_d("No cgroup support");
return;
}
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
_d("Failed mounting cgroups");
goto fail;
}
/* Skip first line, header */
fgets(buf, sizeof(buf), fp);
while (fgets(buf, sizeof(buf), fp)) {
char *cgroup;
char rc[80];
cgroup = strtok(buf, "\t ");
if (!cgroup)
continue;
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
if (mkdir(rc, 0755) && EEXIST != errno)
continue;
if (mount("cgroup", rc, "cgroup", opts, cgroup))
_d("Failed mounting %s cgroup on %s", cgroup, rc);
}
/* Default cgroups for process monitoring */
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
goto fail;
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
_pe("Failed mounting Finit cgroup hierarchy");
goto fail;
}
/* Set up reaper and enable callbacks */
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
/* Default groups, PID 1, services, and user/login processes */
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/user", 0755) && EEXIST != errno)
goto fail;
/* Move ourselves to init */
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
/* We have signal, main screen turn on! */
cg_init = 1;
fail:
fclose(fp);
}
static int move_pid(char *group, char *name, char *id, int pid)
{
char path[256];
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
if (mkdir(path, 0755) && errno != EEXIST)
return 1;
strlcat(path, "/cgroup.procs", sizeof(path));
return echo(path, 0, "%d", pid);
}
int cgroup_user(char *name)
{
if (!cg_init)
return 0;
return move_pid("finit/user", name, "0", getpid());
}
int cgroup_service(char *nm, char *id, int pid)
{
if (!cg_init)
return 0;
if (pid <= 0) {
errno = EINVAL;
return 1;
}
return move_pid("finit/system", nm, id, pid);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+8 -45
View File
@@ -1,6 +1,6 @@
/* Save and restore RTC using hwclock /* Finit control group support functions
* *
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2019 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,49 +21,12 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <lite/lite.h> #ifndef FINIT_CGROUP_H_
#define FINIT_CGROUP_H_
#include "finit.h" void cgroup_init (void);
#include "helpers.h"
#include "plugin.h"
static void save(void *arg) int cgroup_user (char *name);
{ int cgroup_service (char *cmd, char *id, int pid);
_d("Saving system clock to RTC ...");
/* -w,--systohc, -u,--utc */
run_interactive("hwclock -w -u", "Saving system time (UTC) to RTC");
}
static void restore(void *arg) #endif /* FINIT_CGROUP_H_ */
{
_d("Restoring system clock from RTC ...");
/* -s,--hctosys, -u,--utc */
run_interactive("hwclock -s -u", "Restoring system clock (UTC) from RTC");
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = restore
},
.hook[HOOK_SHUTDOWN] = {
.cb = save
}
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
View File
@@ -125,7 +125,7 @@ error:
return NULL; return NULL;
} }
svc_t *client_svc_find(char *arg) svc_t *client_svc_find(const char *arg)
{ {
int sd = -1; int sd = -1;
struct init_request rq = { struct init_request rq = {
+1 -1
View File
@@ -32,6 +32,6 @@ int client_disconnect (void);
int client_send (struct init_request *rq, ssize_t len); int client_send (struct init_request *rq, ssize_t len);
svc_t *client_svc_iterator (int first); svc_t *client_svc_iterator (int first);
svc_t *client_svc_find (char *arg); svc_t *client_svc_find (const char *arg);
#endif /* FINIT_CLIENT_H_ */ #endif /* FINIT_CLIENT_H_ */
+64 -1
View File
@@ -31,6 +31,68 @@
#include "pid.h" #include "pid.h"
#include "service.h" #include "service.h"
/*
* The service condition name is constructed from the 'svc/' prefix, the
* dirname of the svc->cmd, e.g., '/usr/bin/teamd' => 'usr/bin', and the
* service's pid:filename, including an optional subdirectory, without
* the .pid extension.
*
* The following example uses the team (aggregate) service:
*
* service pid:!/run/teamd/a1.pid /usr/bin/teamd -f /etc/teamd/a1.conf
*
* => 'svc/' + 'usr/bin' + 'teamd/a1' => svc/usr/bin/teamd/a1
*
* The next example uses the dbus-daemon:
*
* service pid:!/run/dbus/pid /usr/bin/dbus-daemon
*
* => 'svc/' + 'usr/bin' + 'dbus' => svc/usr/bin/dbus
*
* The last example uses lxc-start to start container foo:
*
* service pid:!/run/lxc/foo.pid lxc-start -n foo -F -p /run/lxc/foo.pid -- Container foo
*
* => 'svc/' + '' + 'lxc/foo' => svc/lxc/foo
*/
char *mkcond(svc_t *svc, char *buf, size_t len)
{
char path[256];
char *pidfile;
char *ptr;
strlcpy(path, svc->cmd, sizeof(path));
ptr = rindex(path, '/');
if (ptr)
*ptr = 0;
else
path[0] = 0;
pidfile = pid_file(svc);
ptr = strstr(pidfile, "run");
if (ptr)
ptr += 3;
else
ptr = rindex(pidfile, '/');
snprintf(buf, len, "svc%s%s%s", path[0] != 0 && path[0] != '/' ? "/" : "", path, ptr);
_d("Composed condition from cmd %s (path %s) and pidfile %s => %s", svc->cmd, path, ptr, buf);
/* Case: /var/run/dbus/pid */
ptr = strstr(buf, "/pid");
if (ptr && !strcmp(ptr, "/pid"))
*ptr = 0;
/* Case /var/run/teamd/a1.pid */
ptr = strstr(buf, ".pid");
if (ptr && !strcmp(ptr, ".pid"))
*ptr = 0;
_d("Creating condition => %s", buf);
return buf;
}
static int cond_set_gen(const char *file, unsigned int gen) static int cond_set_gen(const char *file, unsigned int gen)
{ {
char *ptr, path[256]; char *ptr, path[256];
@@ -128,6 +190,7 @@ static int svc_has_cond(svc_t *svc)
case SVC_TYPE_SERVICE: case SVC_TYPE_SERVICE:
case SVC_TYPE_TASK: case SVC_TYPE_TASK:
case SVC_TYPE_RUN: case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
return 1; return 1;
default: default:
@@ -171,7 +234,7 @@ void cond_set_oneshot(const char *name)
return; return;
path = cond_path(name); path = cond_path(name);
_d("s => %s", name, path); _d("%s => %s", name, path);
if (cond_checkpath(path)) if (cond_checkpath(path))
return; return;
+6 -13
View File
@@ -29,13 +29,6 @@
#include "pid.h" #include "pid.h"
#include "service.h" #include "service.h"
char *mkcond(char *buf, size_t len, char *nm)
{
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
return buf;
}
const char *condstr(enum cond_state s) const char *condstr(enum cond_state s)
{ {
static const char *strs[] = { static const char *strs[] = {
@@ -49,18 +42,18 @@ const char *condstr(enum cond_state s)
const char *cond_path(const char *name) const char *cond_path(const char *name)
{ {
static char path[256];
char tmp[MAX_ARG_LEN]; char tmp[MAX_ARG_LEN];
static char file[MAX_ARG_LEN];
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name); snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
return pid_runpath(tmp, file, sizeof(file)); return pid_runpath(tmp, path, sizeof(path));
} }
unsigned int cond_get_gen(const char *file) unsigned int cond_get_gen(const char *file)
{ {
char *ptr, path[256];
unsigned int gen; unsigned int gen;
char *ptr, path[256];
FILE *fp; FILE *fp;
int ret; int ret;
@@ -99,9 +92,9 @@ 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)
{ {
char *cond; static char conds[MAX_COND_LEN];
enum cond_state s = COND_ON; enum cond_state s = COND_ON;
static char conds[MAX_ARG_LEN]; char *cond;
if (!names) if (!names)
return COND_ON; return COND_ON;
@@ -115,8 +108,8 @@ 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)
{ {
static char conds[MAX_COND_LEN];
char *cond; char *cond;
static char conds[MAX_ARG_LEN];
if (!name || !names) if (!name || !names)
return 0; return 0;
+2 -1
View File
@@ -2,6 +2,7 @@
#define FINIT_COND_H_ #define FINIT_COND_H_
#include <paths.h> #include <paths.h>
#include <svc.h>
#define COND_DIR "finit/cond" #define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR #define COND_PATH _PATH_VARRUN COND_DIR
@@ -13,7 +14,7 @@ typedef enum cond_state {
COND_ON COND_ON
} cond_state_t; } cond_state_t;
char *mkcond (char *buf, size_t len, char *nm); char *mkcond (svc_t *svc, char *buf, size_t len);
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); unsigned int cond_get_gen (const char *path);
+27 -19
View File
@@ -30,6 +30,7 @@
#include <lite/lite.h> #include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */ #include <lite/queue.h> /* BSD sys/queue.h API */
#include <sys/time.h> #include <sys/time.h>
#include <glob.h>
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
@@ -52,7 +53,7 @@ struct conf_change {
char *name; char *name;
}; };
static uev_t w1, w2, w3; static uev_t w1, w2, w3, w4;
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list); static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
static int parse_conf(char *file); static int parse_conf(char *file);
@@ -463,6 +464,12 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
return; return;
} }
/* Similar to task but is treated like a SysV init script */
if (MATCH_CMD(line, "sysv ", x)) {
service_register(SVC_TYPE_SYSV, x, rlimit, file);
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
@@ -577,8 +584,8 @@ static int parse_conf(char *file)
*/ */
int conf_reload(void) int conf_reload(void)
{ {
int i, num; size_t i;
struct dirent **e; glob_t gl;
/* Mark and sweep */ /* Mark and sweep */
svc_mark_dynamic(); svc_mark_dynamic();
@@ -601,20 +608,14 @@ int conf_reload(void)
parse_conf(FINIT_CONF); parse_conf(FINIT_CONF);
/* Next, read all *.conf in /etc/finit.d/ */ /* Next, read all *.conf in /etc/finit.d/ */
num = scandir(rcsd, &e, NULL, alphasort); glob("/etc/finit.d/*.conf", 0, NULL, &gl);
if (num < 0) { glob("/etc/finit.d/enabled/*.conf", GLOB_APPEND, NULL, &gl);
_d("Skipping %s, no files found ...", rcsd);
goto done;
}
for (i = 0; i < num; i++) { for (i = 0; i < gl.gl_pathc; i++) {
char *name = e[i]->d_name; char *path = gl.gl_pathv[i];
char path[LINE_SIZE];
size_t len; size_t len;
struct stat st; struct stat st;
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)) {
_d("Skipping %s, cannot access: %s", path, strerror(errno)); _d("Skipping %s, cannot access: %s", path, strerror(errno));
@@ -650,9 +651,7 @@ int conf_reload(void)
parse_conf_dynamic(path); parse_conf_dynamic(path);
} }
while (num--) globfree(&gl);
free(e[num]);
free(e);
done: done:
/* Drop record of all .conf changes */ /* Drop record of all .conf changes */
@@ -706,6 +705,8 @@ static int do_change(char *name, uint32_t mask)
{ {
struct conf_change *node; struct conf_change *node;
_d("Change detected for %s, mask 0x%08x", name, mask);
node = conf_find(name); node = conf_find(name);
if (mask & (IN_DELETE | IN_MOVED_FROM)) { if (mask & (IN_DELETE | IN_MOVED_FROM)) {
drop_change(node); drop_change(node);
@@ -783,6 +784,11 @@ static void conf_cb(uev_t *w, void *arg, int events)
break; break;
} }
} }
#ifdef ENABLE_AUTO_RELOAD
if (conf_any_change())
service_reload_dynamic();
#endif
} }
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt) static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
@@ -835,6 +841,7 @@ static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
close(fd); close(fd);
return 1; return 1;
} }
_d("Set up inotify watcher for %s ...", path);
return 0; return 0;
} }
@@ -857,8 +864,9 @@ int conf_monitor(uev_ctx_t *ctx)
* changes to either symlink or target. * changes to either symlink or target.
*/ */
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0); rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW); rc += add_watcher(ctx, &w2, FINIT_RCSD "/available/", IN_DONT_FOLLOW);
rc += add_watcher(ctx, &w3, FINIT_CONF, 0); rc += add_watcher(ctx, &w3, FINIT_RCSD "/enabled/", 0);
rc += add_watcher(ctx, &w4, FINIT_CONF, 0);
return rc + conf_reload(); return rc + conf_reload();
} }
@@ -869,7 +877,7 @@ int conf_monitor(uev_ctx_t *ctx)
int conf_init(void) int conf_init(void)
{ {
hostname = strdup(DEFHOST); hostname = strdup(DEFHOST);
w1.fd = w2.fd = w3.fd = -1; w1.fd = w2.fd = w3.fd = w4.fd = -1;
return conf_monitor(NULL); return conf_monitor(NULL);
} }
+36 -31
View File
@@ -231,6 +231,7 @@ int exec_runtask(char *cmd, char *args[])
strlcat(buf, args[i], sizeof(buf)); strlcat(buf, args[i], sizeof(buf));
} }
logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf); logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf);
_d("Calling %s %s", _PATH_BSHELL, buf);
return execvp(_PATH_BSHELL, argv); return execvp(_PATH_BSHELL, argv);
} }
@@ -239,6 +240,7 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
{ {
struct sigaction sa; struct sigaction sa;
struct termios term; struct termios term;
char name[80];
/* /*
* Reset to sane defaults in case of messup from prev. session * Reset to sane defaults in case of messup from prev. session
@@ -282,13 +284,16 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
/* Finit is responsible for the UTMP INIT_PROCESS record */ /* Finit is responsible for the UTMP INIT_PROCESS record */
utmp_set_init(tty, 0); utmp_set_init(tty, 0);
prctl(PR_SET_NAME, procname, 0, 0, 0); if (!strncmp("/dev/", tty, 5))
tty += 5;
snprintf(name, sizeof(name), "%s %s", procname, tty);
prctl(PR_SET_NAME, name, 0, 0, 0);
} }
static int activate_console(int noclear, int nowait) static int activate_console(int noclear, int nowait)
{ {
struct termios orig;
int ret = 0; 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);
@@ -297,15 +302,20 @@ static int activate_console(int noclear, int nowait)
return 1; return 1;
/* Disable ECHO, XON/OFF while waiting for <CR> */ /* Disable ECHO, XON/OFF while waiting for <CR> */
if (!tcgetattr(STDIN_FILENO, &term)) { if (!tcgetattr(STDIN_FILENO, &orig)) {
term.c_iflag &= ~(IXON|IXOFF); struct termios c = orig;
term.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term); c.c_iflag &= ~(BRKINT|ICRNL|INPCK|ISTRIP|IXON|IXOFF);
c.c_oflag &= ~(OPOST);
c.c_cflag |= (CS8);
c.c_lflag &= ~(ECHO|ICANON|IEXTEN|ISIG);
tcsetattr(STDIN_FILENO, TCSANOW, &c);
} }
while (!fexist(SYNC_SHUTDOWN)) { while (!fexist(SYNC_SHUTDOWN)) {
char c; char c;
static const char clr[] = "\r\e[2K"; static const char clr[] = "\r\e[2K";
static const char cup[] = "\e[A";
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)) {
@@ -315,23 +325,21 @@ static int activate_console(int noclear, int nowait)
(void)write(STDERR_FILENO, clr, strlen(clr)); (void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, msg, strlen(msg)); (void)write(STDERR_FILENO, msg, strlen(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n') while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
continue; continue;
if (fexist(SYNC_STOPPED)) if (fexist(SYNC_STOPPED))
continue; continue;
(void)write(STDERR_FILENO, clr, strlen(clr)); (void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, cup, strlen(cup));
ret = 1; ret = 1;
break; break;
} }
/* Restore ECHO, XON/OFF */ /* Restore TTY */
if (!tcgetattr(STDIN_FILENO, &term)) { if (tcsetattr(STDIN_FILENO, TCSANOW, &orig) == -1)
term.c_iflag |= IXON|IXOFF; ret = 0;
term.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
return ret; return ret;
} }
@@ -369,7 +377,7 @@ pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, stru
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud); logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
} }
prepare_tty(tty, speed, "finit-getty", rlimit); prepare_tty(tty, speed, "tty", rlimit);
if (activate_console(noclear, nowait)) if (activate_console(noclear, nowait))
_exit(getty(tty, speed, term, NULL)); _exit(getty(tty, speed, term, NULL));
} }
@@ -449,12 +457,16 @@ int run_parts(char *dir, char *cmd)
} }
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
int j = 0;
pid_t pid = 0;
struct stat st; struct stat st;
char *args[NUM_ARGS]; char path[512];
char *argv[4] = {
"sh",
"-c",
path,
NULL
};
char *name = e[i]->d_name; char *name = e[i]->d_name;
char path[LINE_SIZE]; pid_t pid = 0;
snprintf(path, sizeof(path), "%s/%s", dir, name); snprintf(path, sizeof(path), "%s/%s", dir, name);
if (stat(path, &st)) { if (stat(path, &st)) {
@@ -467,29 +479,22 @@ int run_parts(char *dir, char *cmd)
continue; continue;
} }
/* Fill in args[], starting with full path to executable */
args[j++] = path;
/* If the callee didn't supply a run_parts() argument */ /* If the callee didn't supply a run_parts() argument */
if (!cmd) { if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */ /* Check if S<NUM>service or K<NUM>service notation is used */
_d("Checking if %s is a sysvinit startstop script ...", name); _d("Checking if %s is a sysvinit startstop script ...", name);
if (name[0] == 'S' && isdigit(name[1])) { if (name[0] == 'S' && isdigit(name[1]))
args[j++] = "start"; strlcat(path, " start", sizeof(path));
} else if (name[0] == 'K' && isdigit(name[1])) { else if (name[0] == 'K' && isdigit(name[1]))
args[j++] = "stop"; strlcat(path, " stop", sizeof(path));
}
} else { } else {
args[j++] = cmd; strlcat(path, cmd, sizeof(path));
} }
args[j++] = NULL;
pid = fork(); pid = fork();
if (!pid) { if (!pid) {
_d("Calling %s ...", path);
sig_unblock(); sig_unblock();
execv(path, args); return execvp(_PATH_BSHELL, argv);
exit(0);
} }
complete(path, pid); complete(path, pid);
+26 -9
View File
@@ -36,6 +36,7 @@
#include <lite/lite.h> #include <lite/lite.h>
#include "finit.h" #include "finit.h"
#include "cgroup.h"
#include "cond.h" #include "cond.h"
#include "conf.h" #include "conf.h"
#include "helpers.h" #include "helpers.h"
@@ -72,9 +73,6 @@ static int udev = 0; /* Runtime detection of udev */
*/ */
static void banner(void) static void banner(void)
{ {
char *buf = INIT_HEADING;
char separator[SCREEN_WIDTH];
if (plugin_exists(HOOK_BANNER)) { if (plugin_exists(HOOK_BANNER)) {
plugin_run_hooks(HOOK_BANNER); plugin_run_hooks(HOOK_BANNER);
return; return;
@@ -83,8 +81,7 @@ static void banner(void)
if (log_is_silent()) if (log_is_silent())
return; return;
memset(separator, '=', sizeof(separator)); print_banner(INIT_HEADING);
fprintf(stderr, "\n\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
} }
static int ismnt(char *file, char *dir) static int ismnt(char *file, char *dir)
@@ -222,7 +219,7 @@ static void finalize(void)
/* /*
* Track bundled watchdogd in case a better one turns up * Track bundled watchdogd in case a better one turns up
*/ */
svc = svc_find(FINIT_LIBPATH_ "/watchdogd", 1); svc = svc_find(FINIT_LIBPATH_ "/watchdogd", "1");
if (svc) if (svc)
wdog = svc; wdog = svc;
@@ -308,7 +305,7 @@ static void final_worker(void *work)
finalize(); finalize();
} }
int main(int argc, char* argv[]) int main(int argc, char *argv[])
{ {
struct wq crank = { struct wq crank = {
.cb = crank_worker .cb = crank_worker
@@ -328,6 +325,19 @@ int main(int argc, char* argv[])
if (getpid() != 1) if (getpid() != 1)
return client(argc, argv); return client(argc, argv);
/*
* Hide command line arguments from ps (in particular for
* forked children that don't execv()). This is an ugly
* hack that only works on Linux.
* https://web.archive.org/web/20110227041321/http://netsplit.com/2007/01/10/hiding-arguments-from-ps/
*/
if (argc > 1) {
char *arg_end;
arg_end = argv[argc-1] + strlen (argv[argc-1]);
*arg_end = ' ';
}
/* /*
* Initalize event context. * Initalize event context.
*/ */
@@ -345,11 +355,18 @@ int main(int argc, char* argv[])
*/ */
chdir("/"); chdir("/");
umask(0); umask(0);
mount("none", "/proc", "proc", 0, NULL); if (mount("none", "/proc", "proc", 0, NULL))
mount("none", "/sys", "sysfs", 0, NULL); _pe("Failed mounting /proc");
if (mount("none", "/sys", "sysfs", 0, NULL))
_pe("Failed mounting /sysfs");
if (fisdir("/proc/bus/usb")) if (fisdir("/proc/bus/usb"))
mount("none", "/proc/bus/usb", "usbfs", 0, NULL); mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
/*
* Initialize default control groups, if available
*/
cgroup_init();
/* /*
* Parse kernel command line (debug, rescue, splash, etc.) * Parse kernel command line (debug, rescue, splash, etc.)
* Also calls log_init() to set correct log level * Also calls log_init() to set correct log level
+2 -2
View File
@@ -45,7 +45,6 @@
#define _PATH_VARRUN "/var/run/" #define _PATH_VARRUN "/var/run/"
#endif #endif
#define CMD_SIZE 256 #define CMD_SIZE 256
#define LINE_SIZE 1024 #define LINE_SIZE 1024
#define BUF_SIZE 4096 #define BUF_SIZE 4096
@@ -75,7 +74,7 @@
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */ #define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */ #define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
#define INIT_CMD_QUERY_INETD 14 #define INIT_CMD_QUERY_INETD 14
#define INIT_CMD_EMIT 15 #define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
#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_ITER 129
@@ -84,6 +83,7 @@
#define INIT_CMD_NACK 254 #define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255 #define INIT_CMD_ACK 255
/* Traditionally aligned on 384 bytes */
struct init_request { struct init_request {
int magic; /* Magic number */ int magic; /* Magic number */
int cmd; /* What kind of request */ int cmd; /* What kind of request */
+9 -5
View File
@@ -32,6 +32,7 @@
#include <termios.h> #include <termios.h>
#include "finit.h" #include "finit.h"
#include "cgroup.h"
#include "helpers.h" #include "helpers.h"
#include "sig.h" #include "sig.h"
#include "utmp-api.h" #include "utmp-api.h"
@@ -192,6 +193,7 @@ static int do_login(char *name)
{ {
struct stat st; struct stat st;
cgroup_user(name);
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL); execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/* /*
@@ -209,8 +211,9 @@ static int do_login(char *name)
int getty(char *tty, speed_t speed, char *term, char *user) int getty(char *tty, speed_t speed, char *term, char *user)
{ {
int fd; const char cln[] = "\r\e[2K\n";
char name[30]; char name[30];
int fd;
/* Detach from initial controlling TTY */ /* Detach from initial controlling TTY */
vhangup(); vhangup();
@@ -222,10 +225,15 @@ int getty(char *tty, speed_t speed, char *term, char *user)
dup2(fd, STDIN_FILENO); dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO); dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
close(fd);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
warn("Failed TIOCSCTTY"); warn("Failed TIOCSCTTY");
/* Set up TTY, re-enabling ISIG et al. */
stty(STDIN_FILENO, speed);
(void)write(STDOUT_FILENO, cln, strlen(cln));
/* The getty process is responsible for the UTMP login record */ /* The getty process is responsible for the UTMP login record */
utmp_set_login(tty, NULL); utmp_set_login(tty, NULL);
if (!user) if (!user)
@@ -233,10 +241,6 @@ int getty(char *tty, speed_t speed, char *term, char *user)
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]) if (term && term[0])
setenv("TERM", term, 1); setenv("TERM", term, 1);
+132 -32
View File
@@ -39,6 +39,30 @@
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
static int progress_style = PROGRESS_STYLE;
/*
* Note: the pending status (⋯) must be last item. Also, ⋯ is not
* off-by-one, it's a multi-byte character.
*/
#define STATUS_CLASS { \
CHOOSE(" OK ", " OK ", "\e[1;32m"), /* Green */ \
CHOOSE("FAIL", "FAIL", "\e[1;31m"), /* Red */ \
CHOOSE("WARN", "WARN", "\e[1;31m"), /* Red */ \
CHOOSE(" \\/ ", " ⋯ ", "\e[1;33m"), /* Yellow */ \
}
#define CHOOSE(x,y,z) x
static const char *status1[] = STATUS_CLASS;
#undef CHOOSE
#define CHOOSE(x,y,z) y
static const char *status2[] = STATUS_CLASS;
#undef CHOOSE
#define CHOOSE(x,y,z) z
static const char *color[] = STATUS_CLASS;
char *strip_line(char *line) char *strip_line(char *line)
{ {
@@ -57,6 +81,81 @@ char *strip_line(char *line)
return line; return line;
} }
/*
* Return screen length of string, not counting escape chars, and
* accounting for unicode characters as only one screen byte wide
*/
size_t slen(char *string)
{
unsigned char *buf = (unsigned char *)string;
size_t len = 0;
while (*buf) {
/* Skip ANSI escape sequences */
if (*buf == '\e') {
while (*buf && !isalpha(*buf))
buf++;
continue;
}
/* Skip 3-byte unicode */
if (*buf == 0xe2) {
for (int cnt = 3; *buf && cnt >= 0; cnt--)
buf++;
continue;
}
len++;
buf++;
}
return len;
}
/*
* ch may be an ascii or unicode character
*/
static char *pad(char *buf, size_t len, char *ch, size_t width)
{
size_t i = slen(buf);
strlcat(buf, " ", len);
width -= 8; /* Skip leading '[ STAT ]' */
while (i < width) {
strlcat(buf, ch, len);
i++;
}
return buf;
}
void print_banner(const char *heading)
{
char buf[4 * SCREEN_WIDTH];
memset(buf, 0, sizeof(buf));
strlcat(buf, "\r\e[2K", sizeof(buf));
if (progress_style == 1) {
strlcat(buf, "\e[1m", sizeof(buf));
strlcat(buf, heading, sizeof(buf));
pad(buf, sizeof(buf), "=", SCREEN_WIDTH - 2);
} else {
size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH;
strlcat(buf, "\e[1;31m⏺ \e[1;33m⏺ \e[1;32m⏺ \e[0m\e[1m ", sizeof(buf));
strlcat(buf, heading, sizeof(buf));
/*
* Padding with full-width '═' sign from unicode,
* we could also use '―' or something else.
*/
pad(buf, sizeof(buf), "═", wmax);
}
strlcat(buf, "\e[0m\n", sizeof(buf));
(void)write(STDERR_FILENO, buf, strlen(buf));
}
static size_t print_timestamp(char *buf, size_t len) static size_t print_timestamp(char *buf, size_t len)
{ {
#if defined(CONFIG_PRINTK_TIME) #if defined(CONFIG_PRINTK_TIME)
@@ -77,6 +176,26 @@ static size_t print_timestamp(char *buf, size_t len)
#endif #endif
} }
static char *status(int rc)
{
static char buf[64];
if (rc < 0 || rc >= (int)NELEMS(status1))
rc = NELEMS(status1) - 1; /* Default to "⋯" (pending) */
if (progress_style == 1) {
int hl = 1;
if (rc == 1 || rc == 2)
hl = 7;
snprintf(buf, sizeof(buf), "\e[%dm[%s]\e[0m", hl, status1[rc]);
} else
snprintf(buf, sizeof(buf), "\e[1m[%s%s\e[0m\e[1m]\e[0m ", color[rc], status2[rc]);
return buf;
}
void printv(const char *fmt, va_list ap) void printv(const char *fmt, va_list ap)
{ {
char buf[SCREEN_WIDTH]; char buf[SCREEN_WIDTH];
@@ -91,51 +210,32 @@ void printv(const char *fmt, va_list ap)
len = print_timestamp(buf, sizeof(buf)); len = print_timestamp(buf, sizeof(buf));
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap); vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
len = strlen(buf); if (progress_style == 1)
buf[len++] = ' '; fprintf(stderr, "\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
for (size_t i = len; i < (sizeof(buf) - 8); i++) else
buf[i] = '.'; /* pad with dots. */ fprintf(stderr, "\r\e[2K%s%s", status(3), buf);
fprintf(stderr, "\r%s ", buf);
} }
void print(int action, const char *fmt, ...) void print(int rc, const char *fmt, ...)
{ {
va_list ap;
const char success[] = "\e[1m[ OK ]\e[0m\n";
const char failure[] = "\e[7m[FAIL]\e[0m\n";
const char warning[] = "\e[7m[WARN]\e[0m\n";
const char pending[] = "\e[1m[ \\/ ]\e[0m\n";
if (log_is_silent()) if (log_is_silent())
return; return;
if (fmt) { if (fmt) {
va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
printv(fmt, ap); printv(fmt, ap);
va_end(ap); va_end(ap);
} }
switch (action) { if (rc < 0)
case -1: return;
break;
case 0: if (progress_style == 1)
write(STDERR_FILENO, success, sizeof(success)); fprintf(stderr, "%s\n", status(rc));
break; else
fprintf(stderr, ".\r%s\n", status(rc));
case 1:
write(STDERR_FILENO, failure, sizeof(failure));
break;
case 2:
write(STDERR_FILENO, warning, sizeof(warning));
break;
default:
write(STDERR_FILENO, pending, sizeof(pending));
break;
}
} }
void print_desc(char *action, char *desc) void print_desc(char *action, char *desc)
+1
View File
@@ -57,6 +57,7 @@ int sh (char *tty);
int stty (int fd, speed_t speed); int stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud); speed_t stty_parse_speed(char *baud);
void print_banner (const char *heading);
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);
+4 -1
View File
@@ -169,6 +169,7 @@ 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"; const char *conn = " connection";
char iifname[IF_NAMESIZE + 1] = "UNKNOWN"; char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
char id[MAX_ID_LEN];
int stdin; int stdin;
_d("%s: Got socket event ...", svc->cmd); _d("%s: Got socket event ...", svc->cmd);
@@ -194,7 +195,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
return; return;
} }
task = svc_new(svc->cmd, svc->inetd.next_id++, SVC_TYPE_INETD_CONN); snprintf(id, sizeof(id), "%d", svc->inetd.next_id++);
task = svc_new(svc->cmd, id, SVC_TYPE_INETD_CONN);
if (!task) { if (!task) {
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd); logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
if (svc->inetd.type == SOCK_STREAM) if (svc->inetd.type == SOCK_STREAM)
@@ -226,6 +228,7 @@ static void socket_cb(uev_t *w, void *arg, int events)
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn)); strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
strlcat(task->desc, conn, sizeof(task->desc)); strlcat(task->desc, conn, sizeof(task->desc));
strlcpy(task->iifname, iifname, sizeof(task->iifname)); strlcpy(task->iifname, iifname, sizeof(task->iifname));
strlcpy(task->name, svc->name, sizeof(task->name));
task->stdin_fd = stdin; task->stdin_fd = stdin;
service_step(task); service_step(task);
+88 -34
View File
@@ -140,7 +140,6 @@ static int do_svc(int cmd, char *arg)
return client_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_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); } static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
/* /*
@@ -173,7 +172,7 @@ static int do_restart(char *arg) { return do_startstop(INIT_CMD_RESTART_SVC, arg
static void show_cond_one(const char *_conds) static void show_cond_one(const char *_conds)
{ {
static char conds[MAX_ARG_LEN]; static char conds[MAX_COND_LEN];
char *cond; char *cond;
strlcpy(conds, _conds, sizeof(conds)); strlcpy(conds, _conds, sizeof(conds));
@@ -202,22 +201,6 @@ static void show_cond_one(const char *_conds)
putchar('>'); putchar('>');
} }
static int do_cond_magic(char op, char *cond)
{
char event[368]; /* sizeof(init_request.data) */
if (!cond || strlen(cond) < 1)
return 1;
event[0] = op;
strlcpy(&event[1], cond, sizeof(event) - 1);
return do_emit(event);
}
static int do_cond_set (char *cond) { return do_cond_magic('+', cond); }
static int do_cond_clear(char *cond) { return do_cond_magic('-', cond); }
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{ {
int len; int len;
@@ -279,8 +262,6 @@ static int do_cond(char *cmd)
char *arg; char *arg;
struct command command[] = { struct command command[] = {
{ "show", do_cond_show }, { "show", do_cond_show },
{ "set", do_cond_set },
{ "clear", do_cond_clear },
{ "dump", do_cond_dump }, { "dump", do_cond_dump },
{ NULL, NULL } { NULL, NULL }
}; };
@@ -435,17 +416,17 @@ static int show_status(char *arg)
} }
if (!verbose) if (!verbose)
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0); printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) { for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char jobid[10], args[512] = "", *lvls; char jobid[20], 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:%s", svc->job, svc->id);
printf("%-9s %7s ", jobid, svc_status(svc)); printf("%-9s %8s ", jobid, svc_status(svc));
if (svc_is_inetd(svc)) if (svc_is_inetd(svc))
printf("inetd "); printf("inetd ");
else else
@@ -459,14 +440,7 @@ static int show_status(char *arg)
if (!verbose) { if (!verbose) {
int adj = screen_cols - 60; int adj = screen_cols - 60;
char *name = strrchr(svc->cmd, '/'); printf("%-16.16s %-*.*s\n", svc->name, adj, adj, svc->desc);
if (!name)
name = svc->cmd;
else
name++;
printf("%-16.16s %-*.*s\n", name, adj, adj, svc->desc);
continue; continue;
} }
@@ -516,6 +490,71 @@ static int show_status(char *arg)
return 0; return 0;
} }
static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{
FILE *fp;
char *group;
char path[256];
char buf[256];
int user = 0;
int num = 0;
if (tflag == FTW_F)
return 0;
snprintf(path, sizeof(path), "%s/cgroup.procs", fpath);
fp = fopen(path, "r");
if (!fp)
return 0;
if (strstr(fpath, "finit/user"))
user = 1;
group = strrchr(fpath, '/');
if (!group)
goto err;
group++;
while (fgets(buf, sizeof(buf), fp)) {
FILE *cfp;
int pid;
if (num == 0)
printf("%c :- %s/", user ? ' ' : '|', group);
num++;
pid = atoi(chomp(buf));
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
cfp = fopen(path, "r");
if (!cfp)
continue;
fgets(buf, sizeof(buf), cfp);
fclose(cfp);
printf("\n%c :- %d %s", user ? ' ' : '|', pid, buf);
}
err:
fclose(fp);
if (num)
printf("\r%c\n", user ? ' ' : '|');
return 0;
}
static int show_cgroup(char *arg)
{
puts("finit/");
puts("|- init/");
nftw("/sys/fs/cgroup/finit/init", dump_cgroup, 20, 0);
puts("|- system/");
nftw("/sys/fs/cgroup/finit/system", dump_cgroup, 20, 0);
puts("`- user/");
nftw("/sys/fs/cgroup/finit/user", dump_cgroup, 20, 0);
return 0;
}
static int usage(int rc) static int usage(int rc)
{ {
fprintf(stderr, fprintf(stderr,
@@ -538,8 +577,6 @@ static int usage(int rc)
" 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" // " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
"\n" "\n"
" cond set <COND> Set (assert) condition => +COND\n"
" cond clear <COND> Clear (deassert) condition => -COND\n"
" cond show Show condition status\n" " cond show Show condition status\n"
" cond dump Dump all conditions and their status\n" " cond dump Dump all conditions and their status\n"
"\n" "\n"
@@ -550,6 +587,8 @@ static int usage(int rc)
" status <JOB|NAME>[:ID] Show service status, by job# or name\n" " status <JOB|NAME>[:ID] Show service status, by job# or name\n"
" status | show Show status of services, default command\n" " status | show Show status of services, default command\n"
"\n" "\n"
" ps List processes based on cgroups\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"
" halt Halt system\n" " halt Halt system\n"
@@ -590,6 +629,8 @@ int main(int argc, char *argv[])
{ "status", show_status }, { "status", show_status },
{ "show", show_status }, /* Convenience alias */ { "show", show_status }, /* Convenience alias */
{ "ps", show_cgroup },
{ "runlevel", do_runlevel }, { "runlevel", do_runlevel },
{ "reboot", do_reboot }, { "reboot", do_reboot },
{ "halt", do_halt }, { "halt", do_halt },
@@ -647,6 +688,19 @@ int main(int argc, char *argv[])
return usage(1); return usage(1);
} }
void logit(int prio, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (prio <= LOG_ERR)
verrx(1, fmt, ap);
else
vwarnx(fmt, ap);
va_end(ap);
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+1 -3
View File
@@ -62,10 +62,8 @@ void log_init(int dbg)
void log_exit(void) void log_exit(void)
{ {
muffler(); muffler();
if (!silent) { if (!silent)
sched_yield(); sched_yield();
fputs("\n", stderr);
}
/* /*
* Unless in debug mode at shutdown, Reinitialize screen, * Unless in debug mode at shutdown, Reinitialize screen,
+5
View File
@@ -26,6 +26,11 @@
#include <syslog.h> #include <syslog.h>
/* Local facility, unused in GNU but available in FreeBSD or sysklogd >= 2.0 */
#ifndef LOG_CONSOLE
#define LOG_CONSOLE (14<<3)
#endif
/* /*
* Developer error and debug messages, otherwise --> use logit() <-- * Developer error and debug messages, otherwise --> use logit() <--
* ~~~~~~~~~~~ * ~~~~~~~~~~~
+21 -2
View File
@@ -45,7 +45,7 @@ int pid_alive(pid_t pid)
{ {
char name[24]; /* Enough for max pid_t */ char name[24]; /* Enough for max pid_t */
snprintf(name, sizeof(name), "/proc/%d", pid); snprintf(name, sizeof(name), "/proc/%d/status", pid);
return fexist(name); return fexist(name);
} }
@@ -95,8 +95,8 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
char *pid_file(svc_t *svc) char *pid_file(svc_t *svc)
{ {
static char path[256];
char fn[MAX_ARG_LEN]; char fn[MAX_ARG_LEN];
static char path[MAX_ARG_LEN];
if (svc->pidfile[0]) { if (svc->pidfile[0]) {
if (svc->pidfile[0] == '!') if (svc->pidfile[0] == '!')
@@ -109,6 +109,25 @@ char *pid_file(svc_t *svc)
return pid_runpath(fn, path, sizeof(path)); return pid_runpath(fn, path, sizeof(path));
} }
pid_t pid_file_read(const char *fn)
{
pid_t pid = 0;
FILE *fp;
char buf[42];
fp = fopen(fn, "r");
if (!fp)
return -1;
if (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
pid = atoi(buf);
}
fclose(fp);
return pid;
}
int pid_file_create(svc_t *svc) int pid_file_create(svc_t *svc)
{ {
FILE *fp; FILE *fp;
+6 -2
View File
@@ -31,6 +31,7 @@ int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len); char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc); char *pid_file (svc_t *svc);
pid_t pid_file_read (const char *fn);
int pid_file_create (svc_t *svc); int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg); int pid_file_parse (svc_t *svc, char *arg);
@@ -46,8 +47,9 @@ int pid_file_parse (svc_t *svc, char *arg);
*/ */
static inline char *pid_runpath(const char *file, char *path, size_t len) static inline char *pid_runpath(const char *file, char *path, size_t len)
{ {
static int unknown = 1;
static char *prefix = "/var/run"; static char *prefix = "/var/run";
static int unknown = 1;
int rc;
if (unknown) { if (unknown) {
if (fisdir("/run")) if (fisdir("/run"))
@@ -60,7 +62,9 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
else if (!strncmp(file, "/run/", 5)) else if (!strncmp(file, "/run/", 5))
file += 5; file += 5;
snprintf(path, len, "%s/%s", prefix, file); rc = snprintf(path, len, "%s/%s", prefix, file);
if (rc < 0 || (size_t)rc >= len)
_e("File path '%s' truncated, should end with '%s'", path, file);
return path; return path;
} }
+1 -1
View File
@@ -33,7 +33,7 @@ int api_exit (void);
int client (int argc, char *argv[]); int client (int argc, char *argv[]);
void service_monitor (pid_t lost); void service_monitor (pid_t lost, int status);
const char *plugin_hook_str(hook_point_t no); const char *plugin_hook_str(hook_point_t no);
int plugin_exists (hook_point_t no); int plugin_exists (hook_point_t no);
+10 -7
View File
@@ -120,7 +120,7 @@ int serv_list(char *arg)
if (fisdir(enabled)) if (fisdir(enabled))
do_list("ENABLED ", enabled); do_list("ENABLED ", enabled);
if (fisdir(FINIT_RCSD)) if (fisdir(FINIT_RCSD))
do_list("ENABLED ", FINIT_RCSD); do_list("STATIC ", FINIT_RCSD);
return 0; return 0;
} }
@@ -128,8 +128,8 @@ int serv_list(char *arg)
int serv_enable(char *arg) int serv_enable(char *arg)
{ {
char corr[40]; char corr[40];
char link[80]; char link[256];
char path[80]; char path[256];
if (!arg || !arg[0]) if (!arg || !arg[0])
return serv_list(NULL); return serv_list(NULL);
@@ -140,11 +140,14 @@ int serv_enable(char *arg)
} }
pushd(FINIT_RCSD); pushd(FINIT_RCSD);
snprintf(path, sizeof(path), "available/%s", arg); if (mkdir("enabled", 0755) && EEXIST != errno)
err(1, "Failed creating %s/enabled directory", FINIT_RCSD);
snprintf(path, sizeof(path), "%s/%s", available, arg);
if (!fexist(path)) if (!fexist(path))
errx(1, "Cannot find %s", path); errx(1, "Cannot find %s", path);
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg); snprintf(link, sizeof(link), "%s/%s", enabled, arg);
if (fexist(link)) if (fexist(link))
errx(1, "%s already exists", link); errx(1, "%s already exists", link);
@@ -154,7 +157,7 @@ int serv_enable(char *arg)
int serv_disable(char *arg) int serv_disable(char *arg)
{ {
char corr[40]; char corr[40];
char link[80]; char link[256];
struct stat st; struct stat st;
if (!arg || !arg[0]) if (!arg || !arg[0])
@@ -165,7 +168,7 @@ int serv_disable(char *arg)
arg = corr; arg = corr;
} }
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg); snprintf(link, sizeof(link), "%s/%s", enabled, arg);
if (stat(link, &st)) if (stat(link, &st))
err(1, "Cannot find %s", link); err(1, "Cannot find %s", link);
+377 -132
View File
@@ -32,6 +32,7 @@
#include <net/if.h> #include <net/if.h>
#include <lite/lite.h> #include <lite/lite.h>
#include "cgroup.h"
#include "conf.h" #include "conf.h"
#include "cond.h" #include "cond.h"
#include "finit.h" #include "finit.h"
@@ -115,16 +116,166 @@ static int service_timeout_cancel(svc_t *svc)
return err; return err;
} }
static void redirect_null(void) /*
* Redirect stdin to /dev/null => all reads by process = EOF
* https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Logging%20and%20Standard%20Input/Output
*/
static int stdin_redirect(void)
{ {
FILE *fp; int fd;
fp = fopen("/dev/null", "w"); fd = open("/dev/null", O_RDONLY | O_APPEND);
if (fp) { if (-1 != fd) {
dup2(fileno(fp), STDOUT_FILENO); dup2(fd, STDIN_FILENO);
dup2(fileno(fp), STDERR_FILENO); return close(fd);
fclose(fp);
} }
return -1;
}
/*
* Redirect output to a file, e.g., /dev/null, or /dev/console
*/
static int fredirect(const char *file)
{
int fd;
fd = open(file, O_WRONLY | O_APPEND);
if (-1 != fd) {
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
return close(fd);
}
return -1;
}
#ifndef LOGIT_ENABLED
/*
* Fallback in case we don't even have logger on the system.
* XXX: we should parse 'prio' here to get facility.level
*/
static void fallback_logger(char *ident, char *prio)
{
int facility = LOG_DAEMON;
int level = LOG_NOTICE;
char buf[256];
openlog(ident, LOG_NOWAIT | LOG_PID, facility);
while ((fgets(buf, sizeof(buf), stdin)))
syslog(level, "%s", buf);
closelog();
}
#endif
/*
* Redirect output to syslog using the command line logit tool
*/
static int lredirect(svc_t *svc)
{
pid_t pid;
int fd;
/*
* Open PTY to connect to logger. A pty isn't buffered
* like a pipe, and it eats newlines so they aren't logged
*/
fd = posix_openpt(O_RDWR);
if (fd == -1) {
svc->log.enabled = 0;
return -1;
}
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
close(fd);
svc->log.enabled = 0;
return -1;
}
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
pid = fork();
if (pid == 0) {
int fds;
char *tag = basename(svc->cmd);
char *prio = "daemon.info";
fds = open(ptsname(fd), O_RDONLY);
close(fd);
if (fds == -1)
_exit(0);
dup2(fds, STDIN_FILENO);
/* Reset signals */
sig_unblock();
if (svc->log.file[0] == '/') {
#ifdef LOGIT_ENABLED
char sz[20], num[3];
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
snprintf(num, sizeof(num), "%d", logfile_count_max);
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
_exit(0);
#else
logit(LOG_INFO, "logit disabled, logging %s to syslog instead", svc->name);
#endif
}
if (svc->log.ident[0])
tag = svc->log.ident;
if (svc->log.prio[0])
prio = svc->log.prio;
#ifdef LOGIT_ENABLED
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
#else
if (whichp("logger"))
execlp("logger", "logger", "-t", tag, "-p", prio, NULL);
fallback_logger(tag, prio);
#endif
_exit(0);
}
return close(fd);
}
/*
* Handle redirection of process output, if enabled
*/
static int redirect(svc_t *svc)
{
/* Redirect inetd socket to stdin for connection */
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc)) {
dup2(svc->stdin_fd, STDIN_FILENO);
close(svc->stdin_fd);
dup2(STDIN_FILENO, STDOUT_FILENO);
dup2(STDIN_FILENO, STDERR_FILENO);
return 0;
}
#endif
stdin_redirect();
if (svc->log.enabled) {
if (svc->log.null)
return fredirect("/dev/null");
if (svc->log.console)
return fredirect(CONSOLE);
return lredirect(svc);
} else if (log_is_debug())
return fredirect(CONSOLE);
#ifdef REDIRECT_OUTPUT
else
return fredirect("/dev/null");
#endif
return 0;
} }
static int is_norespawn(void) static int is_norespawn(void)
@@ -139,7 +290,7 @@ static int is_norespawn(void)
* @svc: Service to start * @svc: Service to start
* *
* Returns: * Returns:
* 0 if the service was successfully started. Non-zero otherwise. * 0 if the service was successfully started. Non-zero otherwise.
*/ */
static int service_start(svc_t *svc) static int service_start(svc_t *svc)
{ {
@@ -165,12 +316,15 @@ static int service_start(svc_t *svc)
if (svc_is_inetd(svc)) if (svc_is_inetd(svc))
return inetd_start(&svc->inetd); return inetd_start(&svc->inetd);
#endif #endif
if (svc_is_sysv(svc)) {
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
}
if (!svc->desc[0]) if (!svc->desc[0])
do_progress = 0; do_progress = 0;
if (do_progress) { if (do_progress) {
if (svc_is_daemon(svc)) if (svc_is_daemon(svc) || svc_is_sysv(svc))
print_desc("Starting ", svc->desc); print_desc("Starting ", svc->desc);
else else
print_desc("", svc->desc); print_desc("", svc->desc);
@@ -185,6 +339,8 @@ static int service_start(svc_t *svc)
sigprocmask(SIG_BLOCK, &nmask, &omask); sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = fork(); pid = fork();
cgroup_service(svc->name, svc->id, pid);
if (pid == 0) { if (pid == 0) {
int status; int status;
char *home = NULL; char *home = NULL;
@@ -221,99 +377,31 @@ static int service_start(svc_t *svc)
} }
} }
/* Serve copy of args to process in case it modifies them. */ if (!svc_is_sysv(svc)) {
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++) for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
args[i] = svc->args[i]; args[i] = svc->args[i];
} else {
i = 0;
args[i++] = svc->cmd;
args[i++] = "start";
}
args[i] = NULL; args[i] = NULL;
/* Redirect inetd socket to stdin for connection */ /*
#ifdef INETD_ENABLED * The setsid() call is the most humble of all in this
if (svc_is_inetd_conn(svc)) { * function. It takes care to detach the process from
dup2(svc->stdin_fd, STDIN_FILENO); * its controlling terminal, preventing daemons from
close(svc->stdin_fd); * leaking to the console, and allowing us to run such
dup2(STDIN_FILENO, STDOUT_FILENO); * programs like `lxc-start -F` in the foreground to
dup2(STDIN_FILENO, STDERR_FILENO); * properly monitor them.
} else *
#endif * If you find yourself here wanting to fix the output
* to the console at boot, for debugging or similar,
* have a look at redirect() and log.console instead.
*/
setsid();
if (svc->log.enabled) { redirect(svc);
int fd;
if (svc->log.null) {
redirect_null();
goto logit_done;
}
/*
* Open PTY to connect to logger. A pty isn't buffered
* like a pipe, and it eats newlines so they aren't logged
*/
fd = posix_openpt(O_RDWR);
if (fd == -1) {
svc->log.enabled = 0;
goto logit_done;
}
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
close(fd);
svc->log.enabled = 0;
goto logit_done;
}
/* SIGCHLD is still blocked for grantpt() and fork() */
sigprocmask(SIG_BLOCK, &nmask, NULL);
pid = fork();
if (pid == 0) {
int fds;
char *tag = basename(svc->cmd);
char *prio = "daemon.info";
fds = open(ptsname(fd), O_RDONLY);
close(fd);
if (fds == -1)
_exit(0);
dup2(fds, STDIN_FILENO);
/* Reset signals */
sig_unblock();
if (svc->log.file[0] == '/') {
char sz[20], num[3];
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
snprintf(num, sizeof(num), "%d", logfile_count_max);
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
_exit(0);
}
if (svc->log.ident[0])
tag = svc->log.ident;
if (svc->log.prio[0])
prio = svc->log.prio;
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
_exit(0);
}
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
} else if (log_is_debug()) {
int fd;
fd = open(CONSOLE, O_WRONLY | O_APPEND);
if (-1 != fd) {
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
}
}
#ifdef REDIRECT_OUTPUT
else
redirect_null();
#endif
logit_done:
sig_unblock(); sig_unblock();
if (svc->inetd.cmd) if (svc->inetd.cmd)
@@ -321,7 +409,7 @@ static int service_start(svc_t *svc)
else if (svc_is_runtask(svc)) else if (svc_is_runtask(svc))
status = exec_runtask(svc->cmd, args); status = exec_runtask(svc->cmd, args);
else else
status = execv(svc->cmd, args); status = execvp(svc->cmd, args);
#ifdef INETD_ENABLED #ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc)) { if (svc_is_inetd_conn(svc)) {
@@ -332,28 +420,33 @@ static int service_start(svc_t *svc)
} }
} else } else
#endif #endif
if (svc->log.enabled && !svc->log.null)
waitpid(pid, NULL, 0);
_exit(status); _exit(status);
} else if (log_is_debug()) { } else if (log_is_debug()) {
char buf[CMD_SIZE] = ""; char buf[CMD_SIZE] = "";
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++) { for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++) {
char arg[MAX_ARG_LEN]; char arg[MAX_ARG_LEN + 1];
snprintf(arg, sizeof(arg), "%s ", svc->args[i]); snprintf(arg, sizeof(arg), "%s ", svc->args[i]);
if (strlen(arg) < (sizeof(buf) - strlen(buf))) if (strlen(arg) < (sizeof(buf) - strlen(buf)))
strcat(buf, arg); strlcat(buf, arg, sizeof(buf));
} }
_d("Starting %s: %s", svc->cmd, buf); _d("Starting %s: %s", svc->cmd, buf);
} }
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s:%s, PID: %d",
basename(svc->cmd), svc->id, pid);
svc->pid = pid; svc->pid = pid;
svc->start_time = jiffies(); svc->start_time = jiffies();
switch (svc->type) { switch (svc->type) {
case SVC_TYPE_RUN: case SVC_TYPE_RUN:
result = WEXITSTATUS(complete(svc->cmd, pid)); svc->status = complete(svc->cmd, pid);
if (WIFEXITED(svc->status) && !WEXITSTATUS(svc->status))
result = 0;
else
result = 1;
svc->start_time = svc->pid = 0; svc->start_time = svc->pid = 0;
svc->once++; svc->once++;
svc_set_state(svc, SVC_STOPPING_STATE); svc_set_state(svc, SVC_STOPPING_STATE);
@@ -398,10 +491,12 @@ static void service_kill(svc_t *svc)
} }
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid); _d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s:%s, PID: %d, sending SIGKILL ...",
basename(svc->cmd), svc->id, svc->pid);
if (runlevel != 1) if (runlevel != 1)
print_desc("Killing ", svc->desc); print_desc("Killing ", svc->desc);
kill(svc->pid, SIGKILL); kill(-svc->pid, SIGKILL);
/* Let SIGKILLs stand out, show result as [WARN] */ /* Let SIGKILLs stand out, show result as [WARN] */
if (runlevel != 1) if (runlevel != 1)
@@ -443,16 +538,63 @@ static int service_stop(svc_t *svc)
#endif #endif
service_timeout_cancel(svc); service_timeout_cancel(svc);
if (svc->pid <= 1) if (!svc_is_sysv(svc)) {
return 1; if (svc->pid <= 1)
return 1;
_d("Sending %s to pid:%d name:%s", sig_name(svc->sighalt),
svc->pid, pid_get_name(svc->pid, NULL, 0));
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s:%s, PID: %d, sending %s ...",
basename(svc->cmd), svc->id, svc->pid, sig_name(svc->sighalt));
} else {
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s stop' ...", svc->cmd);
}
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
svc_set_state(svc, SVC_STOPPING_STATE); svc_set_state(svc, SVC_STOPPING_STATE);
if (runlevel != 1) if (runlevel != 1)
print_desc("Stopping ", svc->desc); print_desc("Stopping ", svc->desc);
res = kill(svc->pid, SIGTERM); if (!svc_is_sysv(svc)) {
if (svc->pid <= 1)
goto cleanup;
res = kill(-svc->pid, svc->sighalt);
/* PID lost or forking process never really started */
if (res == -1 && ESRCH == errno) {
char *fn;
/* XXX: Same clenup as done by service_monitor(), refactor */
cleanup:
fn = pid_file(svc);
if (remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
basename(svc->cmd), fn);
/* No longer running, update books. */
svc->start_time = svc->pid = 0;
}
} else {
char *args[] = { svc->cmd, "stop", NULL };
pid_t pid;
pid = fork();
switch (pid) {
case 0:
redirect(svc);
exec_runtask(svc->cmd, args);
_exit(0);
break;
case -1:
_pe("Failed fork() to call sysv script '%s stop'", svc->cmd);
res = 1;
break;
default:
res = WEXITSTATUS(complete(svc->cmd, pid));
break;
}
}
if (runlevel != 1) if (runlevel != 1)
print_result(res); print_result(res);
@@ -466,7 +608,7 @@ static int service_stop(svc_t *svc)
* *
* This function does some basic checks of the runtime state of Finit * This function does some basic checks of the runtime state of Finit
* and a sanity check of the @svc before sending %SIGHUP. * and a sanity check of the @svc before sending %SIGHUP.
* *
* Returns: * Returns:
* POSIX OK(0) or non-zero on error. * POSIX OK(0) or non-zero on error.
*/ */
@@ -496,6 +638,8 @@ static int service_restart(svc_t *svc)
print_desc("Restarting ", svc->desc); print_desc("Restarting ", svc->desc);
_d("Sending SIGHUP to PID %d", svc->pid); _d("Sending SIGHUP to PID %d", svc->pid);
logit(LOG_CONSOLE | LOG_NOTICE, "Restarting %s:%s, PID: %d, sending SIGHUP ...",
basename(svc->cmd), svc->id, svc->pid);
rc = kill(svc->pid, SIGHUP); rc = kill(svc->pid, SIGHUP);
/* Declare we're waiting for svc to re-assert/touch its pidfile */ /* Declare we're waiting for svc to re-assert/touch its pidfile */
@@ -558,6 +702,8 @@ static void parse_log(svc_t *svc, char *arg)
svc->log.enabled = 1; svc->log.enabled = 1;
else if (!strcmp(tok, "null") || !strcmp(tok, "/dev/null")) else if (!strcmp(tok, "null") || !strcmp(tok, "/dev/null"))
svc->log.null = 1; svc->log.null = 1;
else if (!strcmp(tok, "console") || !strcmp(tok, "/dev/console"))
svc->log.console = 1;
else if (tok[0] == '/') else if (tok[0] == '/')
strlcpy(svc->log.file, tok, sizeof(svc->log.file)); strlcpy(svc->log.file, tok, sizeof(svc->log.file));
else if (!strcmp(tok, "priority") || !strcmp(tok, "prio")) else if (!strcmp(tok, "priority") || !strcmp(tok, "prio"))
@@ -569,6 +715,49 @@ static void parse_log(svc_t *svc, char *arg)
} }
} }
static void parse_sighalt(svc_t *svc, char *arg)
{
int signo;
signo = sig_num(arg);
if (signo == -1)
return;
svc->sighalt = signo;
}
static void parse_killdelay(svc_t *svc, char *delay)
{
const char *errstr;
long long sec;
sec = strtonum(delay, 1, 60, &errstr);
if (errstr) {
_e("%s: killdelay %s is %s (1-60)", svc->cmd, delay, errstr);
return;
}
/* convert to msec */
svc->killdelay = (int)(sec * 1000);
}
/*
* name:<name>
*/
static void parse_name(svc_t *svc, char *arg)
{
char *name = NULL;
if (arg && !strncasecmp(arg, "name:", 5)) {
name = arg + 5;
} else {
name = strrchr(svc->cmd, '/');
name = name ? name + 1 : svc->cmd;
}
strlcpy(svc->name, name, sizeof(svc->name));
}
/** /**
* parse_cmdline_args - Update the command line args in the svc struct * parse_cmdline_args - Update the command line args in the svc struct
* *
@@ -584,13 +773,17 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
strlcpy(svc->args[0], cmd, sizeof(svc->args[0])); strlcpy(svc->args[0], cmd, sizeof(svc->args[0]));
/* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args /*
* array to be zero-terminated. */ * Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
for (i=1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS-1); i++) * array to be zero-terminated.
*/
for (i = 1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1); i++)
strlcpy(svc->args[i], arg, sizeof(svc->args[0])); strlcpy(svc->args[i], arg, sizeof(svc->args[0]));
/* clear remaining args in case they were set earlier. /*
* This also zero-terminates the args array.*/ * Clear remaining args in case they were set earlier.
* This also zero-terminates the args array.
*/
for (; i < MAX_NUM_SVC_ARGS; i++) for (; i < MAX_NUM_SVC_ARGS; i++)
svc->args[i][0] = 0; svc->args[i][0] = 0;
} }
@@ -650,15 +843,18 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
*/ */
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
{ {
int id = -1; char id_str[MAX_ID_LEN];
#ifdef INETD_ENABLED #ifdef INETD_ENABLED
int forking = 0; int forking = 0;
#endif #endif
int levels = 0; int levels = 0;
int manual = 0;
char *line; char *line;
char *id = NULL;
char *username = NULL, *log = NULL, *pid = NULL; char *username = NULL, *log = NULL, *pid = NULL;
char *service = NULL, *proto = NULL, *ifaces = NULL; char *service = NULL, *proto = NULL, *ifaces = NULL;
char *cmd, *desc, *runlevels = NULL, *cond = NULL; char *cmd, *desc, *runlevels = NULL, *cond = NULL;
char *name = NULL, *halt = NULL, *delay = NULL;
svc_t *svc; svc_t *svc;
plugin_t *plugin = NULL; plugin_t *plugin = NULL;
@@ -705,7 +901,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else if (cmd[0] == '<') /* <[!][cond][,cond..]> */ else if (cmd[0] == '<') /* <[!][cond][,cond..]> */
cond = &cmd[1]; cond = &cmd[1];
else if (cmd[0] == ':') /* :ID */ else if (cmd[0] == ':') /* :ID */
id = atoi(&cmd[1]); id = &cmd[1];
#ifdef INETD_ENABLED #ifdef INETD_ENABLED
else if (!strncasecmp(cmd, "nowait", 6)) else if (!strncasecmp(cmd, "nowait", 6))
forking = 1; forking = 1;
@@ -716,6 +912,14 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
log = cmd; log = cmd;
else if (!strncasecmp(cmd, "pid", 3)) else if (!strncasecmp(cmd, "pid", 3))
pid = cmd; pid = cmd;
else if (!strncasecmp(cmd, "name:", 5))
name = cmd;
else if (!strncasecmp(cmd, "manual:yes", 10))
manual = 1;
else if (!strncasecmp(cmd, "halt:", 5))
halt = &cmd[5];
else if (!strncasecmp(cmd, "kill:", 5))
delay = &cmd[5];
else if (cmd[0] != '/' && strchr(cmd, '/')) else if (cmd[0] != '/' && strchr(cmd, '/'))
service = cmd; /* inetd service/proto */ service = cmd; /* inetd service/proto */
else else
@@ -773,24 +977,34 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
parse_cmdline_args(svc, cmd); parse_cmdline_args(svc, cmd);
goto inetd_setup; goto inetd_setup;
} }
if (id <= 0) if (!id) {
id = svc_next_id(cmd); int n = svc_next_id_int(cmd);
if (n) {
snprintf(id_str, sizeof(id_str), "%d", n);
id = id_str;
}
}
} }
recreate: recreate:
#endif #endif
if (id <= 0) if (!id)
id = 1; id = "1";
svc = svc_find(cmd, id); svc = svc_find(cmd, id);
if (!svc) { if (!svc) {
_d("Creating new svc for %s id #%d type %d", cmd, id, type); _d("Creating new svc for %s id #%s type %d", cmd, id, type);
svc = svc_new(cmd, id, type); svc = svc_new(cmd, id, type);
if (!svc) { if (!svc) {
_e("Out of memory, cannot register service %s", cmd); _e("Out of memory, cannot register service %s", cmd);
free(line); free(line);
return errno = ENOMEM; return errno = ENOMEM;
} }
if (type == SVC_TYPE_SERVICE && manual) {
svc_stop(svc);
}
} }
#ifdef INETD_ENABLED #ifdef INETD_ENABLED
else { else {
@@ -831,6 +1045,11 @@ recreate:
conf_parse_cond(svc, cond); conf_parse_cond(svc, cond);
parse_name(svc, name);
if (halt)
parse_sighalt(svc, halt);
if (delay)
parse_killdelay(svc, delay);
if (log) if (log)
parse_log(svc, log); parse_log(svc, log);
if (desc) if (desc)
@@ -875,7 +1094,7 @@ recreate:
svc_mark_clean(svc); svc_mark_clean(svc);
if (!file) if (!file)
svc->protected = 1; svc->protect = 1;
/* Free duped line, from above */ /* Free duped line, from above */
free(line); free(line);
@@ -925,7 +1144,7 @@ void service_unregister(svc_t *svc)
svc_del(svc); svc_del(svc);
} }
void service_monitor(pid_t lost) void service_monitor(pid_t lost, int status)
{ {
svc_t *svc; svc_t *svc;
@@ -941,7 +1160,13 @@ void service_monitor(pid_t lost)
return; return;
} }
_d("collected %s(%d)", svc->cmd, lost); _d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
svc->status = status;
/* Forking sysv/services declare themselves with pid:!/path/to/pid.file */
if (svc_is_starting(svc) && svc_is_forking(svc))
return;
/* Try removing PID file (in case service does not clean up after itself) */ /* Try removing PID file (in case service does not clean up after itself) */
if (svc_is_daemon(svc)) { if (svc_is_daemon(svc)) {
@@ -950,8 +1175,16 @@ void service_monitor(pid_t lost)
fn = pid_file(svc); fn = pid_file(svc);
if (remove(fn) && errno != ENOENT) if (remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn); logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn);
} else if (svc_is_runtask(svc)) {
if (WIFEXITED(status) && !WEXITSTATUS(status))
svc->started = 1;
else
svc->started = 0;
} }
/* Terminate any children in the same proess group, e.g. logit */
kill(-svc->pid, SIGTERM);
/* No longer running, update books. */ /* No longer running, update books. */
svc->start_time = svc->pid = 0; svc->start_time = svc->pid = 0;
@@ -979,7 +1212,8 @@ static void service_retry(svc_t *svc)
} }
if (*restart_cnt >= RESPAWN_MAX) { if (*restart_cnt >= RESPAWN_MAX) {
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->cmd); logit(LOG_CONSOLE | LOG_WARNING, "Service %s:%s keeps crashing, not restarting.",
basename(svc->cmd), svc->id);
svc_crashing(svc); svc_crashing(svc);
*restart_cnt = 0; *restart_cnt = 0;
service_step(svc); service_step(svc);
@@ -989,6 +1223,8 @@ static void service_retry(svc_t *svc)
(*restart_cnt)++; (*restart_cnt)++;
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt); _d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
logit(LOG_CONSOLE | LOG_WARNING, "Service %s:%s died, restarting (%d/%d)",
basename(svc->cmd), svc->id, *restart_cnt, RESPAWN_MAX);
svc_unblock(svc); svc_unblock(svc);
service_step(svc); service_step(svc);
@@ -1005,8 +1241,10 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */ /* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if ((*state == SVC_STOPPING_STATE) && !svc_is_inetd(svc)) { if ((*state == SVC_STOPPING_STATE) && !svc_is_inetd(svc)) {
_d("%s is stopping, wait %d sec before sending SIGKILL ...",
svc->cmd, svc->killdelay / 1000);
service_timeout_cancel(svc); service_timeout_cancel(svc);
service_timeout_after(svc, SVC_TERM_TIMEOUT, service_kill); service_timeout_after(svc, svc->killdelay, service_kill);
} }
} }
@@ -1052,6 +1290,7 @@ restart:
case SVC_STOPPING_STATE: case SVC_STOPPING_STATE:
if (!svc->pid) { if (!svc->pid) {
/* PID was collected normally, no need to kill it */ /* PID was collected normally, no need to kill it */
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
service_timeout_cancel(svc); service_timeout_cancel(svc);
switch (svc->type) { switch (svc->type) {
@@ -1063,6 +1302,7 @@ restart:
case SVC_TYPE_INETD_CONN: case SVC_TYPE_INETD_CONN:
case SVC_TYPE_TASK: case SVC_TYPE_TASK:
case SVC_TYPE_RUN: case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
svc_set_state(svc, SVC_DONE_STATE); svc_set_state(svc, SVC_DONE_STATE);
break; break;
@@ -1121,7 +1361,12 @@ restart:
} }
if (svc_is_runtask(svc)) { if (svc_is_runtask(svc)) {
svc_set_state(svc, SVC_STOPPING_STATE); if (svc_is_sysv(svc)) {
if (!svc->started)
svc_set_state(svc, SVC_STOPPING_STATE);
} else {
svc_set_state(svc, SVC_STOPPING_STATE);
}
svc->once++; svc->once++;
break; break;
} }
@@ -1179,10 +1424,10 @@ restart:
svc_set_state(svc, SVC_RUNNING_STATE); svc_set_state(svc, SVC_RUNNING_STATE);
/* Reassert condition if we go from waiting and no change */ /* Reassert condition if we go from waiting and no change */
if (!svc_is_changed(svc)) { if (!svc_is_changed(svc)) {
char cond[MAX_COND_LEN]; char name[MAX_COND_LEN];
mkcond(cond, sizeof(cond), svc->cmd); mkcond(svc, name, sizeof(name));
cond_set_path(cond_path(cond), COND_ON); cond_set_path(cond_path(name), COND_ON);
} }
break; break;
+84 -2
View File
@@ -98,6 +98,55 @@ static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
static uev_t sigchld_watcher; static uev_t sigchld_watcher;
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher; static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
static struct sigmap {
int num;
char *name;
} signames[] = {
/* ISO C99 signals. */
{ SIGINT, "SIGINT" },
{ SIGILL, "SIGILL" },
{ SIGABRT, "SIGABRT" },
{ SIGFPE, "SIGFPE" },
{ SIGSEGV, "SIGSEGV" },
{ SIGTERM, "SIGTERM" },
/* Historical signals specified by POSIX. */
{ SIGHUP, "SIGHUP" },
{ SIGQUIT, "SIGQUIT" },
{ SIGTRAP, "SIGTRAP" },
{ SIGKILL, "SIGKILL" },
{ SIGBUS, "SIGBUS" },
{ SIGSYS, "SIGSYS" },
{ SIGPIPE, "SIGPIPE" },
{ SIGALRM, "SIGALRM" },
/* New(er) POSIX signals (1003.1-2008, 1003.1-2013). */
{ SIGURG, "SIGURG" },
{ SIGSTOP, "SIGSTOP" },
{ SIGTSTP, "SIGTSTP" },
{ SIGCONT, "SIGCONT" },
{ SIGCHLD, "SIGCHLD" },
{ SIGTTIN, "SIGTTIN" },
{ SIGTTOU, "SIGTTOU" },
{ SIGPOLL, "SIGPOLL" },
{ SIGXCPU, "SIGXCPU" },
{ SIGXFSZ, "SIGXFSZ" },
{ SIGVTALRM, "SIGVTALRM" },
{ SIGPROF, "SIGPROF" },
{ SIGUSR1, "SIGUSR1" },
{ SIGUSR2, "SIGUSR2" },
/* Nonstandard signals found in all modern POSIX systems
(including both BSD and Linux). */
{ SIGWINCH, "SIGWINCH" },
{ SIGSTKFLT, "SIGSTKFLT" },
{ SIGPWR, "SIGPWR" },
/* Archaic names for compatibility. */
{ SIGIO, "SIGIO" },
{ SIGIOT, "SIGIOT" },
{ SIGCLD, "SIGCLD" },
};
void mdadm_wait(void); void mdadm_wait(void);
void unmount_tmpfs(void); void unmount_tmpfs(void);
void unmount_regular(void); void unmount_regular(void);
@@ -316,6 +365,7 @@ static void sigterm_cb(uev_t *w, void *arg, int events)
static void sigchld_cb(uev_t *w, void *arg, int events) static void sigchld_cb(uev_t *w, void *arg, int events)
{ {
pid_t pid; pid_t pid;
int status;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); _e("Unrecoverable error in signal watcher");
@@ -324,10 +374,10 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
/* Reap all the children! */ /* Reap all the children! */
do { do {
pid = waitpid(-1, NULL, WNOHANG); pid = waitpid(-1, &status, WNOHANG);
if (pid > 0) { if (pid > 0) {
_d("Collected child %d", pid); _d("Collected child %d", pid);
service_monitor(pid); service_monitor(pid, status);
} }
} while (pid > 0); } while (pid > 0);
} }
@@ -368,6 +418,38 @@ int sig_stopped(void)
return stopped; return stopped;
} }
/*
* Convert SIGFOO to a number, if it exists
*/
int sig_num(const char *name)
{
size_t i, offset = 0;
if (strncasecmp(name, "SIG", 3))
offset = 3;
for (i = 0; i < NELEMS(signames); i++) {
if (strcasecmp(name, &signames[i].name[offset]))
continue;
return signames[i].num;
}
return -1;
}
const char *sig_name(int signo)
{
size_t i;
for (i = 0; i < NELEMS(signames); i++) {
if (signames[i].num == signo)
return signames[i].name;
}
return "SIGUNKOWN";
}
/* /*
* Inital signal setup - ignore everything but SIGCHLD until we're capable of responding * Inital signal setup - ignore everything but SIGCHLD until we're capable of responding
*/ */
+3
View File
@@ -70,10 +70,13 @@ extern shutop_t halt;
void do_shutdown (shutop_t op); void do_shutdown (shutop_t op);
int sig_stopped (void); int sig_stopped (void);
int sig_num (const char *name);
void sig_init (void); void sig_init (void);
void sig_unblock (void); void sig_unblock (void);
void sig_setup (uev_ctx_t *ctx); void sig_setup (uev_ctx_t *ctx);
const char *sig_name(int signo);
#endif /* FINIT_SIG_H_ */ #endif /* FINIT_SIG_H_ */
/** /**
+2 -1
View File
@@ -117,7 +117,7 @@ restart:
switch (sm->state) { switch (sm->state) {
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF); _d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE); service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE);
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
break; break;
@@ -150,6 +150,7 @@ restart:
} }
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel); _d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %d", INIT_HEADING, runlevel);
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 */
+8 -2
View File
@@ -95,7 +95,7 @@ void stty(int fd, speed_t speed)
tcflush(fd, TCIOFLUSH); tcflush(fd, TCIOFLUSH);
/* Disable modem specific flags */ /* Disable modem specific flags */
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD); term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD|CBAUDEX);
term.c_cflag &= ~CRTSCTS; term.c_cflag &= ~CRTSCTS;
term.c_cflag |= CLOCAL; term.c_cflag |= CLOCAL;
@@ -104,7 +104,7 @@ void stty(int fd, speed_t speed)
term.c_cc[VMIN] = 1; term.c_cc[VMIN] = 1;
term.c_iflag = ICRNL|IXON|IXOFF; term.c_iflag = ICRNL|IXON|IXOFF;
term.c_oflag = OPOST|ONLCR; term.c_oflag = OPOST|ONLCR;
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX; term.c_cflag |= CS8|CREAD|HUPCL;
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE; term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
/* Reset special characters to defaults */ /* Reset special characters to defaults */
@@ -115,6 +115,12 @@ void stty(int fd, speed_t speed)
term.c_cc[VKILL] = CTRL('U'); term.c_cc[VKILL] = CTRL('U');
term.c_cc[VERASE] = CERASE; term.c_cc[VERASE] = CERASE;
tcsetattr(fd, TCSANOW, &term); tcsetattr(fd, TCSANOW, &term);
/* Show cursor again, if it was hidden previously */
write(fd, "\033[?25h", 6);
/* Enable line wrap, if disabled previously */
write(fd, "\033[7h", 4);
} }
/** /**
+91 -47
View File
@@ -60,7 +60,7 @@ static void svc_gc(void *arg)
continue; continue;
TAILQ_REMOVE(&gc_list, svc, link); TAILQ_REMOVE(&gc_list, svc, link);
cond_clear(mkcond(cond, sizeof(cond), svc->cmd)); cond_clear(mkcond(svc, cond, sizeof(cond)));
free(svc); free(svc);
} }
@@ -77,10 +77,9 @@ static void svc_gc(void *arg)
* Returns: * Returns:
* A pointer to a new &svc_t object, or %NULL if out of empty slots. * A pointer to a new &svc_t object, or %NULL if out of empty slots.
*/ */
svc_t *svc_new(char *cmd, int id, int type) svc_t *svc_new(char *cmd, char *id, int type)
{ {
int job = -1; int job = -1;
char *desc;
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
/* Find first job n:o if registering multiple instances */ /* Find first job n:o if registering multiple instances */
@@ -99,16 +98,17 @@ svc_t *svc_new(char *cmd, int id, int type)
svc->type = type; svc->type = type;
svc->job = job; svc->job = job;
svc->id = id; strlcpy(svc->id, id, sizeof(svc->id));
strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
/* Default description, if missing */ /* Default description, if missing */
desc = strrchr(cmd, '/'); strlcpy(svc->desc, svc->name, sizeof(svc->desc));
if (desc)
desc++; /* Default HALT signal to send */
else svc->sighalt = SIGTERM;
desc = cmd;
strlcpy(svc->desc, desc, sizeof(svc->desc)); /* Default delay between SIGTERM and SIGKILL */
svc->killdelay = SVC_TERM_TIMEOUT;
TAILQ_INSERT_TAIL(&svc_list, svc, link); TAILQ_INSERT_TAIL(&svc_list, svc, link);
@@ -204,7 +204,7 @@ svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
svc_t *svc; svc_t *svc;
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
char *name = basename(svc->cmd); char *name = svc->name;
if (!strncmp(name, cmd, strlen(name))) if (!strncmp(name, cmd, strlen(name)))
return svc; return svc;
@@ -301,12 +301,12 @@ svc_t *svc_stop_completed(void)
* Returns: * Returns:
* A pointer to an &svc_t object, or %NULL if not found. * A pointer to an &svc_t object, or %NULL if not found.
*/ */
svc_t *svc_find(char *cmd, int id) svc_t *svc_find(char *cmd, char *id)
{ {
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) if (!strcmp(svc->cmd, cmd) && !strcmp(svc->id, id))
return svc; return svc;
} }
@@ -340,12 +340,12 @@ svc_t *svc_find_by_pid(pid_t pid)
* Returns: * Returns:
* A pointer to an &svc_t object, or %NULL if not found. * A pointer to an &svc_t object, or %NULL if not found.
*/ */
svc_t *svc_find_by_jobid(int job, int id) svc_t *svc_find_by_jobid(int job, char *id)
{ {
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->job == job && svc->id == id) if (svc->job == job && !strcmp(svc->id, id))
return svc; return svc;
} }
@@ -360,19 +360,12 @@ svc_t *svc_find_by_jobid(int job, int id)
* Returns: * Returns:
* A pointer to an &svc_t object, or %NULL if not found. * A pointer to an &svc_t object, or %NULL if not found.
*/ */
svc_t *svc_find_by_nameid(char *name, int id) svc_t *svc_find_by_nameid(char *name, char *id)
{ {
char *ptr;
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
ptr = strrchr(svc->cmd, '/'); if (!strcmp(svc->id, id) && !strcmp(name, svc->name))
if (ptr)
ptr++;
else
ptr = svc->cmd;
if (svc->id == id && !strcmp(name, ptr))
return svc; return svc;
} }
@@ -415,7 +408,7 @@ void svc_mark_dynamic(void)
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->protected) if (svc->protect)
continue; continue;
if (svc_is_inetd_conn(svc)) if (svc_is_inetd_conn(svc))
continue; continue;
@@ -509,18 +502,24 @@ int svc_enabled(svc_t *svc)
return 1; return 1;
} }
/* Same base service, return unique ID */ /* Same base service, return unique ID as an integer */
int svc_next_id(char *cmd) int svc_next_id_int(char *cmd)
{ {
int id = 0; int n = 1;
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->cmd, cmd) && id < svc->id) char id[MAX_ID_LEN];
id = svc->id;
snprintf(id, sizeof(id), "%d", n);
if (!strcmp(svc->cmd, cmd) && strcmp(svc->id, id))
return n;
n++;
} }
return id + 1; return 0;
} }
int svc_is_unique(svc_t *svc) int svc_is_unique(svc_t *svc)
@@ -544,31 +543,76 @@ int svc_is_unique(svc_t *svc)
return unique; return unique;
} }
/* break up "job:id job:id job:id ..." into several "job:id" tokens */
static char *tokstr(char *str, size_t len)
{
static char *cur = NULL;
static size_t pos = 0;
char *token;
if (!cur && !str)
return NULL;
if (str) {
cur = str;
pos = 0;
}
if (pos >= len)
return NULL;
token = &cur[pos];
while (pos < len) {
if (isspace(cur[pos])) {
cur[pos++] = 0;
break;
}
pos++;
}
return token;
}
/* /*
* Used by api.c (to start/stop/restart) and initctl.c (for input validation) * Used by api.c (to start/stop/restart) and initctl.c (for input validation)
*/ */
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 *, char *))
{ {
char *input, *token;
int result = 0; int result = 0;
char *input, *token, *pos;
input = sanitize(str, len); _d("Got str:'%s'", str);
if (!input) input = tokstr(str, len);
return -1; while (input) {
char *id = NULL;
token = strtok_r(input, " ", &pos);
while (token) {
int id = 1;
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
char *ptr = strchr(token, ':'); char *ptr;
_d("Got token:'%s'", input);
token = sanitize(input, len);
if (!token) {
_d("Sanitation of token:'%s' failed", input);
goto next;
}
ptr = strchr(token, ':');
if (isdigit(token[0])) { if (isdigit(token[0])) {
int job = atonum(token); char *ep;
long job = 0;
errno = 0;
job = strtol(token, &ep, 10);
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
(errno != 0 && job == 0) ||
(token == ep)) {
result++;
continue;
}
if (!ptr) { if (!ptr) {
svc = svc_job_iterator(&iter, 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, token);
while (svc) { while (svc) {
if (found) if (found)
@@ -577,8 +621,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
} }
} else { } else {
*ptr++ = 0; *ptr++ = 0;
id = atonum(ptr); id = ptr;
job = atonum(token);
svc = svc_find_by_jobid(job, id); svc = svc_find_by_jobid(job, id);
if (!svc && not_found) if (!svc && not_found)
@@ -599,7 +642,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
} }
} else { } else {
*ptr++ = 0; *ptr++ = 0;
id = atonum(ptr); id = ptr;
svc = svc_find_by_nameid(token, id); svc = svc_find_by_nameid(token, id);
if (!svc && not_found) if (!svc && not_found)
@@ -609,7 +652,8 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
} }
} }
token = strtok_r(NULL, " ", &pos); next:
input = tokstr(NULL, len);
} }
return result; return result;
+24 -13
View File
@@ -43,10 +43,11 @@ typedef enum {
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */ SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
SVC_TYPE_INETD = 8, /* Classic inetd service */ SVC_TYPE_INETD = 8, /* Classic inetd service */
SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */ SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
} svc_type_t; } svc_type_t;
#define SVC_TYPE_ANY (-1) #define SVC_TYPE_ANY (-1)
#define SVC_TYPE_RUNTASK (6) #define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV)
typedef enum { typedef enum {
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */ SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
@@ -66,14 +67,15 @@ typedef enum {
SVC_BLOCK_RESTARTING, SVC_BLOCK_RESTARTING,
} svc_block_t; } svc_block_t;
#define MAX_ID_LEN 16
#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_ARGS 32 #define MAX_NUM_SVC_ARGS 64
/* Time after SIGTERM that we SIGKILL stopping processes */ /* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
#define SVC_TERM_TIMEOUT 3000 #define SVC_TERM_TIMEOUT 3000
/* /*
@@ -86,24 +88,30 @@ typedef struct svc {
TAILQ_ENTRY(svc) link; TAILQ_ENTRY(svc) link;
/* Instance specifics */ /* Instance specifics */
int job, id; /* JOB:ID */ int job; /* JOB: */
char id[MAX_ID_LEN]; /* :ID */
/* Limits and scoping */ /* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS]; struct rlimit rlimit[RLIMIT_NLIMITS];
/* Service details */ /* Service details */
pid_t pid; int sighalt; /* Signal to stop prorcess, default: SIGTERM */
char pidfile[MAX_ARG_LEN]; int killdelay; /* Delay in msec before sending SIGKILL */
pid_t pid;
char pidfile[256];
long start_time; /* Start time, as seconds since boot, from sysinfo() */ long start_time; /* Start time, as seconds since boot, from sysinfo() */
int started; /* Set for run/task/sysv to track if started */
int status; /* From waitpid() when process is collected */
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */ const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; /* Service, run, task, inetd, ... */ svc_type_t type; /* Service, run, task, inetd, ... */
int protected; /* Services like dbus-daemon & udev by Finit */ int protect; /* Services like dbus-daemon & udev by Finit */
const int dirty; /* -1: removal, 0: unmodified, 1: modified */ const int dirty; /* -1: removal, 0: unmodified, 1: modified */
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];
char name[MAX_ARG_LEN];
/* Counters */ /* Counters */
char once; /* run/task, (at least) once per runlevel */ char once; /* run/task, (at least) once per runlevel */
@@ -118,6 +126,7 @@ typedef struct svc {
struct { struct {
char enabled; char enabled;
char null; char null;
char console;
char file[64]; char file[64];
char prio[20]; char prio[20];
char ident[20]; char ident[20];
@@ -143,13 +152,13 @@ typedef struct svc {
struct timespec gc; struct timespec gc;
} svc_t; } svc_t;
svc_t *svc_new (char *cmd, int id, int type); svc_t *svc_new (char *cmd, char *id, int type);
int svc_del (svc_t *svc); int svc_del (svc_t *svc);
svc_t *svc_find (char *cmd, int id); svc_t *svc_find (char *cmd, char *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, char *id);
svc_t *svc_find_by_nameid (char *name, int id); svc_t *svc_find_by_nameid (char *name, char *id);
svc_t *svc_find_by_pidfile (char *fn); svc_t *svc_find_by_pidfile (char *fn);
svc_t *svc_iterator (svc_t **iter, int first); svc_t *svc_iterator (svc_t **iter, int first);
@@ -170,15 +179,17 @@ int svc_clean_bootstrap (svc_t *svc);
void svc_prune_bootstrap (void); void svc_prune_bootstrap (void);
int svc_enabled (svc_t *svc); int svc_enabled (svc_t *svc);
int svc_next_id (char *cmd); int svc_next_id_int (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 *, char *));
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; } 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_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_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); } static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; }
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; }
+30 -25
View File
@@ -153,8 +153,12 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
/* Iterate over all args */ /* Iterate over all args */
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
/* First, figure out if built-in or external */ /*
if (!dev && !cmd) { * First, figure out if built-in or external getty
* tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in
* tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external
*/
if ((!cmd && !dev) || (cmd && !dev)) {
if (args[i][0] == '[') if (args[i][0] == '[')
runlevels = line; runlevels = line;
if (!strcmp(args[i], "@console")) if (!strcmp(args[i], "@console"))
@@ -164,14 +168,14 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
if (!strncmp(args[i], "tty", 3)) if (!strncmp(args[i], "tty", 3))
dev = args[i]; dev = args[i];
if (!access(args[i], X_OK)) if (!access(args[i], X_OK))
cmd = strdup(args[i]); cmd = args[i];
/* The first arg must be one of the above */ /* The first arg must be one of the above */
continue; continue;
} }
/* Built-in getty args */ /* Built-in getty args */
if (dev) { if (!cmd && dev) {
if (isdigit(args[i][0])) { if (isdigit(args[i][0])) {
baud = args[i]; baud = args[i];
continue; continue;
@@ -184,23 +188,11 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
if (i + 1 == num) if (i + 1 == num)
term = args[i]; term = args[i];
} }
/* External getty, figure out the device */
if (cmd) {
if (!strncmp(args[i], "/dev", 4))
dev = args[i];
if (!strncmp(args[i], "tty", 3))
dev = args[i];
}
} }
if (!dev) { if (!dev) {
error: error:
_e("Incomplete or non-existing TTY device given, cannot register."); _e("Incomplete or non-existing TTY device given, cannot register.");
if (cmd)
free(cmd);
return errno = EINVAL; return errno = EINVAL;
} }
@@ -226,18 +218,24 @@ again:
dev = canonicalize(dev); dev = canonicalize(dev);
if (!dev) if (!dev)
goto error; goto error;
dev = strdup(dev);
entry = tty_find(dev); entry = tty_find(dev);
if (!entry) { if (!entry) {
insert = 1;
entry = calloc(1, sizeof(*entry)); entry = calloc(1, sizeof(*entry));
if (!entry) { if (!entry)
free(dev);
if (cmd)
free(cmd);
return errno = ENOMEM; return errno = ENOMEM;
insert = 1;
} else {
if (entry->cmd) {
free(entry->cmd);
entry->cmd = NULL;
for (i = 0; i < TTY_MAX_ARGS; i++) {
if (entry->args[i])
free(entry->args[i]);
entry->args[i] = NULL;
}
} }
insert = 0;
} }
strlcpy(entry->name, dev, sizeof(entry->name)); strlcpy(entry->name, dev, sizeof(entry->name));
@@ -257,11 +255,18 @@ again:
tok = cmd; tok = cmd;
else else
tok++; tok++;
entry->cmd = cmd; entry->cmd = strdup(cmd);
args[1] = strdup(tok); args[1] = strdup(tok);
for (i = 1; i < num; i++) for (i = 1; i < num; i++) {
entry->args[j++] = strdup(args[i]); char *arg = args[i];
/* Replace @console with actual device */
if (arg && !strcmp(arg, "@console"))
arg = dev;
if (arg)
entry->args[j++] = strdup(arg);
}
entry->args[++j] = NULL; entry->args[++j] = NULL;
} }
+51 -18
View File
@@ -24,8 +24,11 @@
#include "config.h" #include "config.h"
#include <ctype.h> /* isprint() */ #include <ctype.h> /* isprint() */
#include <errno.h> #include <errno.h>
#include <regex.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <sys/sysinfo.h> /* sysinfo() */ #include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */ #include <lite/lite.h> /* strlcat() */
@@ -46,6 +49,32 @@ char *progname(char *arg0)
return prognm; return prognm;
} }
int echo(char *file, int append, char *fmt, ...)
{
va_list ap;
FILE *fp;
if (!file)
fp = stdout;
else
fp = fopen(file, append ? "a" : "w");
if (!fp)
return -1;
if (fmt) {
va_start(ap, fmt);
vfprintf(fp, fmt, ap);
va_end(ap);
}
/* echo(1) always adds a newline */
fprintf(fp, "\n");
if (file)
fclose(fp);
return 0;
}
int strtobytes(char *arg) int strtobytes(char *arg)
{ {
int mod = 0, bytes; int mod = 0, bytes;
@@ -77,7 +106,11 @@ int strtobytes(char *arg)
void do_sleep(unsigned int sec) void do_sleep(unsigned int sec)
{ {
while ((sec = sleep(sec))) struct timespec requested_sleep = {
.tv_sec = sec,
.tv_nsec = 0,
};
while (nanosleep(&requested_sleep, &requested_sleep))
; ;
} }
@@ -132,29 +165,29 @@ char *uptime(long secs, char *buf, size_t len)
return buf; return buf;
} }
/* Allowed characters in job/id/name */ /*
static int isallowed(int ch) * Verify string argument is NUL terminated
{ * Verify string is a JOB[:ID], JOB and ID
return isprint(ch); * can both be string or number, or combo.
} */
/* Sanitize user input, make sure to NUL terminate. */
char *sanitize(char *arg, size_t len) char *sanitize(char *arg, size_t len)
{ {
size_t i = 0; const char *regex = "[a-z0-9_]+[:]?[a-z0-9_]*";
regex_t preg;
int rc;
while (isallowed(arg[i]) && i < len) if (strlen(arg) > len)
i++; return NULL;
if (i + 1 < len) { if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED))
arg[i + 1] = 0; return NULL;
return arg;
}
if (i > 0 && arg[i] == 0) rc = regexec(&preg, arg, 0, NULL, 0);
return arg; regfree(&preg);
if (rc)
return NULL;
return NULL; return arg;
} }
/* /*
+7
View File
@@ -39,6 +39,7 @@ extern char *prognm;
#include <lite/conio.h> #include <lite/conio.h>
char *progname (char *arg0); char *progname (char *arg0);
int echo (char *file, int append, char *fmt, ...);
int strtobytes (char *arg); int strtobytes (char *arg);
void do_sleep (unsigned int sec); void do_sleep (unsigned int sec);
@@ -51,6 +52,12 @@ void screen_init (void);
void screen_exit (void); void screen_exit (void);
int screen_width (void); int screen_width (void);
static inline char *strterm(char *str, size_t len)
{
str[len - 1] = 0;
return str;
}
#endif /* FINIT_UTIL_H_ */ #endif /* FINIT_UTIL_H_ */
/** /**