Compare commits

..
231 Commits
Author SHA1 Message Date
Joachim Nilsson 9d9b6b5201 Update ChangeLog and bump version for v3.2-rc3
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 18:06:25 +02:00
Joachim Nilsson c0b74c5e53 Fix minor build warning for --disable-inetd
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 18:06:25 +02:00
Joachim Nilsson 88a6864fd4 Relocate docs/ --> doc/, like most other projects have
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 17:53:36 +02:00
Joachim Nilsson 2b2c6aaf4e docs: Relocate GitHub specific files to .github/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 17:46:22 +02:00
Joachim Nilsson 8ed37f5fa0 Always append /ID to cond if run/task/service is declared with :ID
This patch is a follow-up to the idea introduced in 1d6737e, but this
time we make sure to always append the svc ID to conditions if their
.conf has been declared with :ID

Meaning, the following .conf:

    service :0 /sbin/teamd --config-file /etc/teamd-lag0.conf
    service :1 /sbin/teamd --config-file /etc/teamd-lag0.conf

results in the following two conditions:

    svc/sbin/teamd/0
    svc/sbin/teamd/1

With a custom 'name:foo':

    service name:lag :0 /sbin/teamd
    service name:lag :1 /sbin/teamd

the resulting conditions look even prettier:

    svc/lag/0
    svc/lag/1

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 15:22:29 +02:00
Joachim Nilsson 8fe4aad53e Make run/task/service :ID optional, default to zero/NULL
This patch makes the ':ID' field optional when registering non-inetd
run/taks/service etc.  It has always been optional in the .conf files,
but internally we defaulted to ':1'.

To open up for more useful conditions we now default to zero/NULL :ID,
meaning the user now *must* keep track of the :ID field if they have
multiple services with the same name.  This should already have been
an issue since such services would likely have conflicted with their
PID files (same name).  So it's unlikely to affect any user severely.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 15:21:28 +02:00
Joachim Nilsson 54e5fd2a78 initctl: Show :ID in process listing(s) only if non-zero
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 15:14:09 +02:00
Joachim Nilsson 1d6737eac1 Custom conditions include :ID; 'name:foo :bar' => svc/foo/bar
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 09:56:30 +02:00
Joachim Nilsson fa0c2f7050 Custom name:foo will now create condition svc/foo w/o path
When registering a service there is the option to set a custom PID file,
and even tell Finit to maintain that PID file in case the daemon doesn't

Until now there hasn't been a way to create custom conditions though ...

This patch leverages the new 'name:foo' argument to tas/run/services to
trigger condition 'svc/foo' (notice lack of path!) when a custom name is
set and either the default, or the custom, PID file is created.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-23 08:57:12 +02:00
Joachim Nilsson 70c0a38307 Provide description to built-in watchdog daemon
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 23:00:21 +02:00
Joachim Nilsson 217df432d6 Add support for 'console' as compat alias to '@console'
This patch makes it possible to declare tty's using the magic keyword 'console',
an alias for the already existing '@console' keyword, which automatically detect
valid console(s) from the console=foo kernel command line.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 22:35:32 +02:00
Joachim Nilsson cfa97253be Fix missing signal.h, required when building for uClibc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 22:34:19 +02:00
Joachim Nilsson 6462bac249 finit: /run: Follow-up to 7c8b0df7, use MS_ mount flags instead
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:28:48 +02:00
Joachim Nilsson b0a1d6bd61 Fix warning message when service doesn't exist: !. -> .
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson ee4768ba53 contrib: Let tty1 be available in runlevel 1 on Void and Alpine
The default runlevels are otherwise 2-5.  Usually, however, when
debugging a system you may want to have runlevel 1 (single user
level) available to log in.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson c7c0c723d6 Update TTY speed recommendations in README.md and Debian defaults
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson b7c1a2a198 Change tty default baud rate: 38400 -> 0, i.e. keep kernel default
This is a more sane default, and also what systems expect, and what
other getty implementations support.  I.e., unless specified, or set
to 0, the tty baud rate is zero -- which means do not change speed
at all.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson af45166814 src: Add critical debug messages for debugging condition handling
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson 1a881389b2 plugins: pidfile: Handle conditions for PID files in sub-directories
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson 6306c757a2 src: svc: Use realpath() to check if untangled file exists
The svc_find_by_pidfile() function may be called with "/run/./foo.pid",
or similar, which must be sanitized before being compared to the PID
file of each svc_t.

This patch introduce realpath() to untangle the paths and check if the
file actually exists.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson 3865ce765e src: svc: Handle case where multiple services have same PID file
Services may exist in multiple runlevels, i.e. one behavior at bootstrap
and another at runtime.  If the user does not set a specific PID file to
be used as condition finit can match the wrong svc.

This patch extends svc_find_by_pidfile() to also compare the contents of
the PID file with the PID of the registered svc_t.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 21:15:48 +02:00
Joachim Nilsson aa08221666 configure: Remove --enable-rw-rootfs detect using /etc/fstab instead
This patch removes the --enable-rw-rootfs build flag.  Like OpenRC we
now check /etc/fstab instad; if `/` is listed *and* doesn't have the
`ro` flag set, we remount / read-write.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-22 20:08:52 +02:00
Joachim Nilsson 7c8b0df720 finit: /run: Mount with nosuid,nodev,noexec for added security
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-15 19:28:23 +02:00
Joachim Nilsson cb802cd54e finit: devpts: Mount with recommended ptmxmode=0666
For details https://www.kernel.org/doc/Documentation/filesystems/devpts.txt

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2020-04-15 19:28:13 +02:00
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
Joachim Nilsson 5579ba16b1 Bump version for v3.2-rc1 pre-release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-16 08:58:56 +02:00
Joachim Nilsson 510b36645b Fix potential buffer overruns found by GCC 8.2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-10-15 22:56:23 +02:00
Joachim Nilsson 67cd88cd23 svc_status(): Fix potential fall-through in switch-case
Found by GCC 8.2

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-25 19:14:59 +01:00
Joachim Nilsson 06a5b43005 Outline plans for Finit 3.2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 18:28:28 +01:00
Joachim Nilsson 38939cc57e Travis-CI: Add static + disabled inetd build step
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 15:59:42 +01:00
Joachim Nilsson a3eadb72a7 Fix #98, handle --disable-inetd case in service_unregister()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 15:48:41 +01:00
89 changed files with 3152 additions and 1147 deletions
@@ -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>
Another good *counter* example [is this][rambling] ...
Code of Conduct
---------------
@@ -79,11 +76,4 @@ Bill & Ted; [be excellent to each other][conduct].
[github]: https://github.com/troglobit/finit/
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
[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
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
[conduct]: CODE-OF-CONDUCT.md
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [troglobit]
+13 -8
View File
@@ -13,10 +13,12 @@ compiler:
- 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:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig
- CFLAGS="-W -Wall -Wextra"
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
addons:
@@ -28,25 +30,28 @@ addons:
name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux"
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"
branch_pattern: dev
install:
- wget https://github.com/troglobit/libuev/releases/download/v2.1.0/libuev-2.1.0.tar.xz
- wget https://github.com/troglobit/libuev/releases/download/v2.2.0/libuev-2.2.0.tar.xz
- wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
- tar xf libuev-2.1.0.tar.xz
- tar xf libuev-2.2.0.tar.xz
- tar xf libite-2.0.1.tar.xz
- (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libuev-2.2.0 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip)
# Custom build script for the time being, no "make test" yet
script:
- ./autogen.sh
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp
- ./configure --prefix=/tmp --disable-inetd --enable-static
- make clean
- make V=1 -j5
- ./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
- make clean
- make -j5 V=1
- make V=1 -j5
- make install-strip
- tree /tmp
- ldd /tmp/sbin/finit
+125 -2
View File
@@ -4,6 +4,128 @@ Change Log
All relevant changes are documented in this file.
[3.2][UNRELEASED] - 2020-04-xx
------------------------------
Bug fix release, but also cgroups and a new progress!
https://twitter.com/b0rk/status/1214341831049252870?s=20
### 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-doc` and `--disable-contrib` to speed up
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`
* Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog
* Set `$SHELL`, like `$PATH`, to a sane default value, needed by BusyBox
* Finit no longer automatically reloads its `*.conf` files after running
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
* `initctl` without an argument or option now defaults to list services
* Convert built-in watchdog daemon to standalone mini watchdogd, issue #102
* Improved watchdog hand-over, now based on `svc_t` and not PID
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
not allowed in the runtime runlevel are unconditionally stopped
* Removed `HOOK_SVC_START` and `HOOK_SVC_LOST`, caused more problems
than they were worth. Users are encouraged to use accounting instead
* Skip displaying "Restarting ..." progress for bootstrap processes
* Added a simple work queue mechanism to queue up work at boot + runtime
- Postpone deletion of `svc_t` until any `SIGKILL` timer has elapsed
- As long as a stepped service changes state we queue another step all
event, because services may depend on each other
* Require new libuEv API: `uev_init1()` to reduce event cache so that
the kernel can invalidate deleted events before enqueing to userspace
* 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
* Mount devtpts with recommended `ptxmode=0666`
* Mount /run tmpfs with nosuid,nodev,noexec for added security
* Support for `console` as alias for `@console` in tty stanzas
* Drop `--enable-rw-roots` configure option, use `rw` for your `/`
partition in `/etc/fstab` instead to trigger remount at boot
* Drop default tty speed (38400) and use 0 (kernel default) instead
* Make `:ID` optional for real, use NULL/zero internally and only
force :ID (numbered) for inetd services, this allows ...
* Handle use-cases where multiple services share the same PID filem
and thus the same condition path, e.g. different instances for
different runlevels. Allow custom condition path with `name:foo`
syntax, creates conditions w/o a path, and ...
* Always append `:ID` qualifier to conditions if set for a service
### Fixes
* Fix #96: Start udevd as a proper service
* Ensure we track run commands as well as task/service, once per runlevel
* Fix #98: FTBFS with `--disable-inetd`
* Make sure to unblock UDP inetd services when connection terminates.
Regression introduced in v3.1
* Ensure run/tasks also go to stopping state on exit, like services,
otherwise it is unnecessarily hard to restart them
* Fix #99: Do not try to `SIGKILL` inetd services, they are not backed
by a PID. This caused a use after free issue crashing finit. Found
and fixed by Tobias Waldekranz, Westermo
* Fix missing OS/Finit title bug, adds leading newline before banner
* Remove "Failed connecting to watchdog ..." error message on systems
that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY,
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
Fixed by Jonas Johansson, Westermo
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
Fixed by Jonas Johansson, Westermo
* Fix #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
listening interface is changed. Only stop established connections
which are no longer allowed, i.e. do not touch already allowed
established connections. Fixed by Jonas Johansson, Westermo
* Fix #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
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
that have crashed. Found by Mattias Walström, Westermo
* Wait for bootstrap phase to complete before cleaning out any bootstrap
processes that have stopped, they may be restarted again
* Reassert condition when an unmodified run/task/service goes from
WAITING back to RUNNING again after a reconfiguration event.
Found and fixed by Jonas Johansson, Westermo
* Restore Ctrl-D and Ctrl-U support in built-in getty
* Remove service condition when service is deleted
* Fix C++ compilation issues, by Robert Andersson, Atlas Copco
* Build fixes for uClibc
* Provide service description for built-in watchdog daemon
[3.1][] - 2018-01-23
--------------------
@@ -76,7 +198,7 @@ This version requires at least libuEv v2.1.0 and libite v2.0.1
possibility of running a second stage mount command before files in
`/run` and similar are created
* Skip `gdbserver` when unleashing the grim reaper at shutdown
* Distribute and install `docs/` and `contrib/` directories
* Distribute and install `doc/` and `contrib/` directories
### Fixes
@@ -139,7 +261,7 @@ distributions.
* Ensure plugins always have a default name, file name
* Reorganization, move all source files to a `src/` sub-directory
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
distributions. See [docs/distro.md](docs/distro.md) for details
distributions. See [doc/distro.md](doc/distro.md) for details
* Remove `UNUSED()` macro, mentioned here because it may have been used
by external plugin developers. Set `-Wno-unused-parameter` instead
* New table headings in `initctl`, using `top` style inverted text
@@ -710,6 +832,7 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
[3.2]: https://github.com/troglobit/finit/compare/3.1...3.2
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
+8 -1
View File
@@ -1,8 +1,15 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = contrib docs plugins src
SUBDIRS = plugins src
doc_DATA = README.md LICENSE contrib/finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
if CONTRIB
SUBDIRS += contrib
endif
if DOC
SUBDIRS += doc
endif
# Compat hook
install-dev:
@make -C src install-pkgincludeHEADERS
+54 -42
View File
@@ -1,41 +1,41 @@
<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)
* [Features](#features)
* [Bootstrap](docs/bootstrap.md#bootstrap)
* [Bootstrap](doc/bootstrap.md#bootstrap)
* [Runlevels](#runlevels)
* [Syntax](docs/config.md#syntax)
* [Inetd](docs/inetd.md#inetd)
* [Syntax](doc/config.md#syntax)
* [Inetd](doc/inetd.md#inetd)
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
* [Hooks, Callbacks & Plugins](docs/plugins.md#hooks-callbacks--plugins)
* [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
* [Rebooting & Halting](#rebooting--halting)
* [Commands & Status](#commands--status)
* [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
* [Building](docs/build.md#building)
* [Running](docs/build.md#running)
* [Recovery](docs/build.md#recovery)
* [Debugging](docs/build.md#debugging)
* [Tips & Tricks with the kernel cmdline](doc/cmdline.md)
* [Building](doc/build.md#building)
* [Running](doc/build.md#running)
* [Recovery](doc/build.md#recovery)
* [Debugging](doc/build.md#debugging)
* [Requirements](#requirements)
* [Origin & References](#origin--references)
Introduction
------------
[![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
was reverse engineered from the [EeePC fastinit][] ten years ago by
[Claudio Matsuoka][] — "gaps filled with frog DNA …"
was reverse engineered from the [EeePC fastinit][] over ten years ago
by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section with tutorials for:
* [Alpine Linux](contrib/alpine/),
* [Debian GNU/Linux](contrib/debian/), and
* [Void Linux](contrib/void/)
* [Void Linux](contrib/void/),
* [Alpine Linux](contrib/alpine/), and
* [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/).
@@ -95,21 +95,21 @@ inetd ssh/tcp@*,!eth0 nowait [2345] /sbin/dropbear -i -R -F -- SSH service
# Run start scripts from this directory
# runparts /etc/start.d
# Virtual consoles run BusyBox getty
tty [12345] /sbin/getty -L 115200 /dev/tty1 linux
tty [12345] /sbin/getty -L 115200 /dev/tty2 linux
tty [12345] /sbin/getty -L 115200 /dev/tty3 linux
# Virtual consoles run BusyBox getty, keep kernel default speed
tty [12345] /sbin/getty -L 0 /dev/tty1 linux
tty [12345] /sbin/getty -L 0 /dev/tty2 linux
tty [12345] /sbin/getty -L 0 /dev/tty3 linux
# Use built-in getty for serial port and USB serial
tty [12345] /dev/ttyAMA0 115200 noclear nowait
tty [12345] /dev/ttyUSB0 115200 noclear
tty [12345] /dev/ttyAMA0 noclear nowait
tty [12345] /dev/ttyUSB0 noclear
# Just give me a shell, I need to debug this embedded system!
tty [12345] @console noclear nologin
```
The `service` stanza, as well as `task`, `run`, `inetd` and others are
described in full in [docs/config.md](docs/config.md). Here's a quick
described in full in [doc/config.md](doc/config.md). Here's a quick
overview of some of the most common components needed to start a UNIX
daemon:
@@ -130,7 +130,7 @@ advanced uses as well:
service /usr/sbin/sshd -D
Dependencies are handled using [conditions](docs/conditions.md). One of
Dependencies are handled using [conditions](doc/conditions.md). One of
the most common conditions is to wait for basic networking to become
available:
@@ -171,7 +171,7 @@ Start, monitor and restart services should they fail.
**Inetd**
Finit comes with a built-in [inetd server](docs/inetd.md). No need to
Finit comes with a built-in [inetd server](doc/inetd.md). No need to
maintain a separate config file for services that you want to start on
demand.
@@ -186,7 +186,7 @@ Built-in optional inetd services:
- discard [RFC863][]
- time (rdate) [RFC868][]
For more information, see [docs/inetd.md](docs/inetd.md).
For more information, see [doc/inetd.md](doc/inetd.md).
**Getty**
@@ -197,9 +197,9 @@ waits for user input before handing over to `/bin/login`, which is
responsible for handling the actual authentication.
```conf
tty [12345] /dev/tty1 38400 nowait linux
tty [12345] /dev/ttyAMA0 115200 noclear vt100
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
tty [12345] /dev/tty1 nowait linux
tty [12345] /dev/ttyAMA0 noclear vt100
tty [12345] /sbin/getty -L /dev/ttyAMA0 vt100
```
Users of embedded systems may want to enable automatic serial console
@@ -208,12 +208,12 @@ system uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit
figures it out by querying sysfs: `/sys/class/tty/console/active`.
```conf
tty [12345] @console 115200 linux noclear
tty [12345] @console linux noclear
```
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
latter is for skipping the login process entirely. For more information,
see [docs/config.md](docs/config.md#syntax).
see [doc/config.md](doc/config.md#syntax).
**Runlevels**
@@ -258,7 +258,7 @@ Extensions and functionality not purely related to what an `/sbin/init`
needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O.
For more information, see [docs/plugins.md](docs/plugins.md).
For more information, see [doc/plugins.md](doc/plugins.md).
**Progress**
@@ -277,7 +277,21 @@ append="init=/sbin/finit splash"
For a really nice boot, add `quiet` as well. This silences the output
from the kernel, leaving only warnings and errors. For other kernel
command line parameters, see [docs/build.md](docs/build.md#recovery).
command line parameters, see [doc/build.md](doc/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
@@ -299,8 +313,8 @@ Right after the runlevel change when all services have started properly,
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
If it exists and is an executable shell script Finit calls it at the very
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
in [docs/plugins.md](docs/plugins.md#hooks), and about the system bootstrap
in [docs/bootstrap.md](docs/bootstrap.md).
in [doc/plugins.md](doc/plugins.md#hooks), and about the system bootstrap
in [doc/bootstrap.md](doc/bootstrap.md).
Runlevels
@@ -378,7 +392,7 @@ section.
For compatibility reasons Finit listens to the same set of signals as
BusyBox init. This is not 100% compatible with SysV init, but clearly
the more common combination for Finit. For more details, see
[docs/signals.md](docs/signals.md).
[doc/signals.md](doc/signals.md).
Finit also listens to the classic SysV init FIFO, used by `telinit`.
Support for this is implemented by the `initctl.so` plugin. Hence,
@@ -429,8 +443,6 @@ Commands:
touch <CONF> Mark .conf in /etc/finit.d/ for reload
reload Reload *.conf in /etc/finit.d/ (activates changes)
cond set <COND> Set (assert) condition => +COND
cond clear <COND> Clear (deassert) condition => -COND
cond show Show condition status
cond dump Dump all conditions and their status
@@ -451,7 +463,7 @@ Commands:
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
file must be used to tell Finit to stop and start it on `reload` and
`runlevel` changes. If `<>` holds more [conditions](docs/conditions.md),
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
these will also affect how a service is maintained.
**Note:** even though it is possible to start services not belonging in
@@ -533,12 +545,12 @@ and proposed extensions.
[openrc]: http://www.gentoo.org/proj/en/base/openrc/
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[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
[Joachim Nilsson]: http://troglobit.com
[GitHub]: https://github.com/troglobit/finit
[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 Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545
+55 -18
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 3.1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AC_INIT(Finit, 3.2-rc3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
AM_SILENT_RULES([yes])
@@ -9,7 +9,7 @@ AC_CONFIG_FILES([Makefile
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
docs/Makefile
doc/Makefile
plugins/Makefile
src/Makefile])
@@ -40,8 +40,11 @@ AC_TYPE_UINT8_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
# Check for pkg-config first, warn if it's not installed
PKG_PROG_PKG_CONFIG
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 2.1.0])
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
# Check for configured Finit features
@@ -54,14 +57,18 @@ AC_ARG_ENABLE(fallback_shell,
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
enable_fallback_shell=no])
AC_ARG_ENABLE(rw-rootfs,
AS_HELP_STRING([--enable-rw-rootfs], [Remount / R/W at boot, usually not for embedded]),,[
enable_rw_rootfs=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(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])
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,
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
enable_watchdog=no])
@@ -74,10 +81,22 @@ AC_ARG_ENABLE(inetd,
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
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,
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
enable_logrotate=yes])
AC_ARG_ENABLE(doc,
AS_HELP_STRING([--disable-doc], [Disable build and install of doc/ section]),,[
enable_doc=yes])
AC_ARG_ENABLE(contrib,
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
enable_contrib=yes])
# Check for extra plugins to enable
enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
@@ -87,7 +106,7 @@ AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864])
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!])
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
@@ -129,7 +148,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]),
[random_seed=$withval], [random_seed=/var/lib/misc/random-seed])
### Enable features ###########################################################################
# Create config.h from selected features and fallback defautls
@@ -139,13 +157,21 @@ AS_IF([test "x$enable_emergency_shell" = "xyes"], [
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
AS_IF([test "x$enable_rw_rootfs" = "xyes"], [
AC_DEFINE(REMOUNT_ROOTFS, 1, [Remount / as rw at boot])])
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_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"], [
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
@@ -158,6 +184,9 @@ AS_IF([test "x$enable_inetd" != "xno"], [
enable_inetd="yes"
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"], [
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
@@ -216,11 +245,16 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
# Override default libdir, used for plugins and rescue.conf
#pkglibdir=$libdir/finit
#AC_SUBST(pkglibdir)
#AC_EXPAND_DIR(pkglibdir, "$libdir/finit")
pkglibdir=$libdir/finit
AC_SUBST(pkglibexecdir)
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path])
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
AC_SUBST(plugin_path)
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
@@ -252,13 +286,16 @@ Behavior:
Plugins...............: $plugins
Optional features:
Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib
Built-in inetd........: $enable_inetd
Built-in watchdogd....: $enable_watchdog
Built-in logrotate....: $enable_logrotate
Scripting tool logit..: $enable_logit
Emergency shell.......: $enable_emergency_shell
Fallback shell........: $enable_fallback_shell
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 hostname......: $hostname
Default runlevel......: $runlevel
+1 -1
View File
@@ -14,7 +14,7 @@ fi
# The plugins are optional, but you may need D-Bus and X11 if you want
# to run X-Window, the other configure flags are however required.
PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--enable-rw-rootfs --enable-progress \
--enable-progress \
--enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --enable-inetd-echo-plugin \
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
+1 -1
View File
@@ -4,7 +4,7 @@ d_LINKS = dropbear.conf getty.conf keymap.conf modules.conf klogd.conf \
syslogd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
+3 -3
View File
@@ -1,4 +1,4 @@
# Consoles to start getty on when system is up
tty /dev/tty1 noclear nowait
tty /dev/tty2 noclear nowait
tty /dev/tty3 noclear nowait
tty [12345] /dev/tty1 noclear nowait
tty [2345] /dev/tty2 noclear nowait
tty [2345] /dev/tty3 noclear nowait
+1 -1
View File
@@ -22,7 +22,7 @@ echo
# The plugins are optional, but you may need D-Bus and X11 if you want
# to run X-Window, the other configure flags are however required.
./configure \
--enable-rw-rootfs --enable-progress \
--enable-progress \
--enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --enable-inetd-echo-plugin \
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
+1 -1
View File
@@ -4,7 +4,7 @@ d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
modules.conf rsyslogd.conf sshd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
+3 -3
View File
@@ -1,4 +1,4 @@
# Consoles to start getty on when system is up
tty [12345] /dev/tty1 115200 vt100 noclear nowait
tty [2345] /dev/tty2 38400 linux noclear nowait
tty [2345] /dev/tty3 38400 linux noclear nowait
tty [12345] /dev/tty1 linux noclear nowait
tty [2345] /dev/tty2 linux noclear nowait
tty [2345] /dev/tty3 linux noclear nowait
+1 -1
View File
@@ -14,7 +14,7 @@ fi
# The plugins are optional, but you may need D-Bus and X11 if you want
# to run X-Window, the other configure flags are however required.
PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--enable-rw-rootfs --enable-progress \
--enable-progress \
--enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --enable-inetd-echo-plugin \
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
+1 -1
View File
@@ -3,7 +3,7 @@ docsdir := @docdir@/contrib/void/finit.d
d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
+3 -3
View File
@@ -1,4 +1,4 @@
# Consoles to start Finit built-in getty on when system is up
tty /dev/tty1 noclear nowait
tty /dev/tty2 noclear nowait
tty /dev/tty3 noclear nowait
tty [12345] /dev/tty1 noclear nowait
tty [2345] /dev/tty2 noclear nowait
tty [2345] /dev/tty3 noclear nowait
View File
+1 -1
View File
@@ -1,4 +1,4 @@
docsdir := @docdir@/docs
docsdir := @docdir@/doc
docs_DATA = config.md conditions.md distro.md inetd.md plugins.md \
service.md signals.md svc-machine.png cmdline.md
EXTRA_DIST = $(docs_DATA)
+1
View File
@@ -0,0 +1 @@
config.md
+18 -23
View File
@@ -18,16 +18,28 @@ after a reconfiguration (`initctl reload`), the Finit dependency system
can also be used.
General
-------
Near Future
-----------
* Add support for timed shutdown in Finit, including cancelled shutdown,
possibly using an at-job, see [crond section](#Crond) below
* 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
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Cron/At support, see below
* Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ...
General
-------
* Add support for timed shutdown in Finit, including cancelled shutdown,
possibly using an at-job, see [crond section](#Crond) below
* Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json`
* [Solaris SMF][] (Service Management Facility) has some interesting
@@ -48,6 +60,8 @@ General
now inotify listeners should be automatically created (and 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
----
@@ -103,22 +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
re-reading `/etc/finit.conf`
Documentation
-------------
* Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ...
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
how close Finit3 still is to the original easy-to-use Finit0 :)
[libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so
[Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+1 -1
View File
@@ -12,7 +12,7 @@ Bootstrap
9. Parse `/etc/finit.conf` and all `/etc/finit.d/*.conf` files
10. Start built-in watchdog, if enabled
11. Set hostname
12. Pivot root, or remount `/` read-write, depending on system type
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
14. Mount all file systems listed in `/etc/fstab` and swap, if available
15. Enable SysV init signals
+1 -8
View File
@@ -40,11 +40,6 @@ Below are a few of the main switches to configure:
* `--disable-inetd`: Disable the built-in inetd server.
* `--enable-rw-rootfs`: Most desktop and server systems boot with the
root file stystem read-only. With this setting Finit will remount it
as read-write early at boot so the `bootmisc.so` plugin can run.
Usually not needed on embedded systems.
* `--enable-static`: Build Finit statically. The plugins will be
built-ins (.o files) and all external libraries, except the C library
will be linked statically.
@@ -53,8 +48,6 @@ Below are a few of the main switches to configure:
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
* `--enable-lost-plugin`: Enable noisy example plugin for `HOOK_SVC_LOST`.
* `--enable-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin.
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
@@ -75,7 +68,7 @@ First, unpack the archive:
Then configure, build and install:
```shell
$ ./configure --enable-rw-rootfs --enable-inetd-echo-plugin \
$ ./configure --enable-inetd-echo-plugin \
--enable-inetd-chargen-plugin --enable-inetd-daytime-plugin \
--enable-inetd-discard-plugin --enable-inetd-time-plugin \
--with-heading="Alpine Linux 3.4" --with-hostname=alpine
View File
+28 -17
View File
@@ -47,17 +47,7 @@ service's PID file being created.
Triggering
----------
Conditions are triggered by built-in plugins, but can for debugging
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 can only be triggered by built-in plugins.
Conditions retain their current state until the next reconfiguration or
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.
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
---------
@@ -133,12 +142,13 @@ To fake interface `vlan1` suddenly appearing, and test what happens to
```shell
~ # 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
from `initctl cond show` again. (The client will likely have failed to
start, but at least the condition is now satisfied.)
from `initctl cond show` again. The client will likely have failed to
start, but at least the condition is now satisfied.
There is also the `initctl cond dump` command, which dumps all known
conditions and their current status.
@@ -147,9 +157,10 @@ conditions and their current status.
Internals
---------
Conditions are implemented as simple files in the file system, in the
`/var/run/finit/cond/` sub-directory. Use the `initctl cond set/clear`
commands to add/remove files in this hierarchy.
As shown previously, conditions are implemented as simple files in the
file system, in the `/var/run/finit/cond/` sub-directory. The files
are created, updated, and removed by condition plugins. To debug them,
see the previous section.
A condition is always in one of three states:
+78 -17
View File
@@ -93,7 +93,16 @@ Syntax
case the contents of that file is used.
* `module <MODULE> [ARGS]`
Load a kernel module, with optional arguments
Load a kernel module, with optional arguments. 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>`
Script or program to bring up networking, with optional arguments
@@ -143,11 +152,36 @@ Syntax
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, or daemon, to be monitored and automatically restarted if it
exits prematurely. Please note that you often need to provide a
`--foreground` or `--no-background` argument to most daemons to
prevent them from forking off a sub-process in the background.
exits prematurely.
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
service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon
@@ -169,21 +203,42 @@ Syntax
pid[:[/path/to/]filename[.pid]]
For example, by adding `pid:/run/foo.pid` to the service `bar` that
PID file will, not only be created and removed automatically, but also
be used by the Finit condition subsystem. So another service/run/task
can depend on `<svc/foo>`.
For example, by adding `pid:/run/foo.pid` to the service `/sbin/bar`
that PID file will, not only be created and removed automatically, but
also be used by the Finit condition subsystem. So a service/run/task
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
can inform Finit of this by simplly prepending an `!` to the argument.
However, if a service `bar` *does* create a PID file, using `foo.pid`,
we can inform Finit of this by prepending an `!`:
pid:!/run/foo.pid
Here Finit will *not* create/remove/touch the PID file, only use it
for the condition handling instead of the default PID file name.
For a detailed description of conditions, and how to debug them, see
the [Finit Conditions](conditions.md) document.
> For a detailed description of conditions, and how to debug them, see
> 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`
Launch a daemon when a client initiates a connection on an Internet
@@ -246,8 +301,9 @@ Syntax
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
The first variant of this option uses the built-in getty on the given
TTY device DEV, in the given runlevels. The DEV may be the special
keyword `@console`, which is very useful on embedded systems. Default
baud rate is `38400`.
keyword `@console`, or `console`, useful on embedded systems.
The default baud rate is 0, i.e., keep kernel default.
**Example:**
```conf
@@ -285,11 +341,12 @@ Syntax
Embedded systems may want to enable automatic `DEV` by supplying the
special `@console` device. This works regardless weather the system
uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
it out by querying sysfs: `/sys/class/tty/console/active`.
it out by querying sysfs: `/sys/class/tty/console/active`. The speed
can be omitted to keep the kernel default.
**Example:**
```conf
tty [12345] @console 115200 noclear vt220
tty [12345] @console noclear vt220
```
On really bare bones systems Finit offers a fallback shell, which
@@ -338,14 +395,18 @@ syslog using the native `logit` tool. The full syntax is:
log:/path/to/file
log:prio:facility.level,tag:ident
log:console
log:null
log
Default `prio` is `daemon.info` and default `tag` is the basename of the
service or run/task command.
Log rotation is controlled using the global `log` setting.
**Example:**
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document.
View File
View File
+2 -17
View File
@@ -42,13 +42,11 @@ For your convenience a set of *optional* plugins are available:
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
* *hwclock.so*: Restore and save system clock from/to RTC on
startup/shutdown.
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
* *lost.so*: Very simple `HOOK_SVC_LOST` example. Logs process ID and
name to syslog. _Optional plugin._
* *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
interfaces. These events are then sent to the Finit service monitor
@@ -102,19 +100,6 @@ Hooks
all modified/removed services have been stopped. When the hook has
completed, Finit continues to start all modified and new services.
* `HOOK_SVC_LOST`: Called when a process is lost. When reconfiguring
services at runtime this hook may be called a lot. However, it may be
a quite useful hook to monitor a system post bootstrap when no, or
few, services are expected to exit. A default plugin `lost.so` is
available in the `plugins/` subdirectory as an example.
**NOTE:** This hook callback gets the lost PID as argument.
* `HOOK_SVC_START`: Like `HOOK_SVC_LOST`, but called when a process is
started. Same caveats apply.
**NOTE:** This hook callback gets the new PID as argument.
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
change. The hook is called when services not matching the new
runlevel have been been stopped. When the hook has completed, Finit
View File
View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

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

+6 -6
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC
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
libplug_la_SOURCES += alsa-utils.c
@@ -39,8 +39,8 @@ libplug_la_SOURCES += time.c
endif
endif
if BUILD_LOST_PLUGIN
libplug_la_SOURCES += lost.c
if BUILD_MODULES_LOAD_PLUGIN
libplug_la_SOURCES += modules-load.c
endif
if BUILD_NETLINK_PLUGIN
@@ -52,7 +52,7 @@ libplug_la_SOURCES += resolvconf.c
endif
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
pkglib_LTLIBRARIES += alsa-utils.la
@@ -84,8 +84,8 @@ pkglib_LTLIBRARIES += time.la
endif
endif
if BUILD_LOST_PLUGIN
pkglib_LTLIBRARIES += lost.la
if BUILD_MODULES_LOAD_PLUGIN
pkglib_LTLIBRARIES += modules-load.la
endif
if BUILD_NETLINK_PLUGIN
+2 -2
View File
@@ -38,7 +38,7 @@
static void setup(void *arg)
{
char *cmd;
char line[80];
char line[256];
cmd = which(DAEMON);
if (!cmd) {
@@ -58,7 +58,7 @@ static void setup(void *arg)
erase("/var/run/dbus/pid");
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345] %s %s -- %s", cmd, ARGS, DESC);
snprintf(line, sizeof(line), "[S12345] 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))
_pe("Failed registering %s", DAEMON);
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:
*/
+241 -62
View File
@@ -1,6 +1,7 @@
/* Simple pidfile event monitor for the Finit condition engine
*
* 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
* of this software and associated documentation files (the "Software"), to deal
@@ -21,99 +22,277 @@
* THE SOFTWARE.
*/
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include <lite/queue.h>
#include <sys/inotify.h>
#include "finit.h"
#include "cond.h"
#include "helpers.h"
#include "pid.h"
#include "plugin.h"
#include "service.h"
struct context {
struct wd_entry {
TAILQ_ENTRY(wd_entry) link;
char *path;
int fd;
int wd;
};
static char *mkcond(char *buf, size_t len, char *nm)
struct context {
int fd;
TAILQ_HEAD(, wd_entry) wd_list;
};
static struct context pidfile_ctx;
static int watcher_add(struct context *ctx, char *path)
{
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
return buf;
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 *dir, char *name, uint32_t mask)
{
char cond[MAX_COND_LEN];
char fn[PATH_MAX];
svc_t *svc;
_d("Got dir: %s, name: %s, mask: %08x", dir, name, mask);
snprintf(fn, sizeof(fn), "%s/%s", dir, name);
svc = svc_find_by_pidfile(fn);
if (!svc) {
_d("pidfile: No matching svc for %s", fn);
return;
}
_d("pidfile: Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
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(dir, 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 char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
static char cond[MAX_COND_LEN];
struct inotify_event *ev;
ssize_t sz, len;
svc_t *svc;
ssize_t sz, off;
size_t buflen = 8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1;
char *buf;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
buf= malloc(buflen);
if (!buf) {
_pe("Failed allocating buffer for inotify events");
return;
}
ev_buf[sz] = 0;
for (ev = (void *)ev_buf;
sz > (ssize_t)sizeof(*ev);
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
if (!ev->mask || !strstr(ev->name, ".pid"))
continue;
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);
_d("pidfile: Entering ... reading %zu bytes into ev_buf[]", buflen - 1);
sz = read(fd, buf, buflen - 1);
if (sz <= 0) {
_pe("invalid inotify event");
goto done;
}
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(wde->path, ev->name, ev->mask);
}
done:
free(buf);
}
/*
* Assert condition only if the service is running, but not if it's
* recently been changed or while it's starting up.
* This function is called after `initctl reload` to reassert conditions
* for services that have not been changed.
*
* We must wait for the service to create/touch its pidfile.
* We reassert the run/task/service's condition only if it is running,
* but not if it's recently been changed or while it's starting up.
*/
static void pidfile_reconf(void *arg)
{
static char cond[MAX_COND_LEN];
svc_t *svc, *iter = NULL;
int restart = 0;
do {
restart = 0;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->state != SVC_RUNNING_STATE)
continue;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
mkcond(cond, sizeof(cond), svc->cmd);
if (svc->state == SVC_RUNNING_STATE &&
!svc_is_changed(svc) &&
!svc_is_starting(svc) &&
cond_get(cond) != COND_ON) {
cond_set_path(cond_path(cond), COND_ON);
restart = 1;
}
}
if (svc_is_changed(svc) || svc_is_starting(svc))
continue;
if (restart)
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
mkcond(svc, cond, sizeof(cond));
if (cond_get(cond) == COND_ON)
continue;
} while (restart);
cond_set_path(cond_path(cond), COND_ON);
}
/*
* This will call service_step(), which in turn will schedule itself
* as long as stepped services change state. Services going from
* WAITING to RUNNING will reassert their conditions in that loop,
* which in turn may unlock other services, and so on.
*/
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
}
static void pidfile_init(void *arg)
{
char *path;
struct context *ctx = arg;
/*
* The bootmisc plugin is responsible for setting up /var/run.
@@ -126,15 +305,9 @@ static void pidfile_init(void *arg)
return;
}
ctx->wd = inotify_add_watch(ctx->fd, path, IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
free(path);
if (ctx->wd < 0) {
_pe("inotify_add_watch()");
close(ctx->fd);
return;
}
TAILQ_INIT(&pidfile_ctx.wd_list);
if (watcher_add(&pidfile_ctx, path))
close(pidfile_ctx.fd);
_d("pidfile monitor active");
}
@@ -146,7 +319,7 @@ static struct context pidfile_ctx;
*/
static plugin_t plugin = {
.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 },
.io = {
.cb = pidfile_callback,
@@ -169,7 +342,13 @@ PLUGIN_INIT(plugin_init)
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);
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:
*/
+5 -5
View File
@@ -57,7 +57,7 @@ static void watcher(void *arg, int fd, int events)
{
int len = 0;
char buf[EVENT_SIZE], name[42];
tty_node_t *entry;
struct tty *entry;
struct inotify_event *notified = (struct inotify_event *)buf;
while ((len = read(fd, buf, sizeof(buf)))) {
@@ -72,11 +72,11 @@ static void watcher(void *arg, int fd, int events)
snprintf(name, sizeof(name), "/dev/%s", notified->name);
entry = tty_find(name);
if (entry && tty_enabled(&entry->data)) {
if (entry && tty_enabled(entry)) {
if (notified->mask & IN_CREATE)
tty_start(&entry->data);
else if (entry->data.pid)
tty_stop(&entry->data);
tty_start(entry);
else if (entry->pid)
tty_stop(entry);
}
}
}
+1
View File
@@ -3,5 +3,6 @@ finit
initctl
logit
reboot
watchdogd
Makefile
Makefile.in
+16 -6
View File
@@ -1,4 +1,4 @@
EXTRA_DIST = rescue.conf
EXTRA_DIST = rescue.conf cgreaper.sh
AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC
@@ -8,13 +8,19 @@ else
AM_LDFLAGS = -export-dynamic
endif
bin_PROGRAMS = logit
sbin_PROGRAMS = finit initctl reboot
pkglibexec_SCRIPTS = cgreaper.sh
if WATCHDOGD
pkglibexec_PROGRAMS = watchdogd
endif
if LOGIT
bin_PROGRAMS = logit
logit_SOURCES = logit.c
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 \
telinit.c \
conf.c conf.h \
@@ -25,6 +31,7 @@ finit_SOURCES = api.c \
mdadm.c mount.c \
pid.c pid.h \
plugin.c plugin.h private.h \
schedule.c schedule.h \
service.c service.h \
sig.c sig.h \
sm.c sm.h \
@@ -36,9 +43,6 @@ pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h log.h plugin.h svc.h
if INETD
finit_SOURCES += inetd.c inetd.h
endif
if WATCHDOGD
finit_SOURCES += watchdog.c watchdog.h
endif
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
@@ -63,6 +67,12 @@ reboot_LDADD = $(lite_LIBS)
INIT_LNKS = telinit init
REBOOT_LNKS = shutdown halt poweroff suspend
if WATCHDOGD
watchdogd_SOURCES = watchdog.c watchdog.h
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
#log.c log.h
endif
# Hook in install to add finit and reboot symlink(s)
install-exec-hook:
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
+62 -71
View File
@@ -44,6 +44,7 @@
#include "service.h"
#include "util.h"
extern svc_t *wdog;
static uev_t api_watcher;
static int call(int (*action)(svc_t *), char *buf, size_t len)
@@ -51,7 +52,7 @@ static int call(int (*action)(svc_t *), char *buf, size_t len)
return svc_parse_jobstr(buf, len, action, NULL);
}
static int service_stop(svc_t *svc)
static int stop(svc_t *svc)
{
if (!svc)
return 1;
@@ -62,7 +63,7 @@ static int service_stop(svc_t *svc)
return 0;
}
static int service_start(svc_t *svc)
static int start(svc_t *svc)
{
if (!svc)
return 1;
@@ -73,43 +74,45 @@ static int service_start(svc_t *svc)
return 0;
}
static int service_restart(svc_t *svc)
static int restart(svc_t *svc)
{
if (!svc)
return 1;
if (svc_is_blocked(svc))
svc_start(svc);
svc_mark_dirty(svc);
service_step(svc);
return 0;
}
static int do_start (char *buf, size_t len) { return call(service_start, buf, len); }
static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); }
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
static char query_buf[368];
static int missing(char *job, int id)
static int missing(char *job, char *id)
{
char buf[20];
char idstr[10] = "";
if (!job)
job = "";
if (id > 1)
snprintf(idstr, sizeof(idstr), ":%d", id);
if (!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));
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));
if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) {
memcpy(rq->data, query_buf, sizeof(rq->data));
if (svc_parse_jobstr(buf, len, NULL, missing)) {
memcpy(buf, query_buf, len);
return 1;
}
@@ -118,8 +121,9 @@ static int do_query(struct init_request *rq, size_t len)
static svc_t *do_find(char *buf, size_t len)
{
int id = 1;
char *id = NULL;
char *ptr, *input;
svc_t *iter = NULL;
input = sanitize(buf, len);
if (!input)
@@ -128,11 +132,28 @@ static svc_t *do_find(char *buf, size_t len)
ptr = strchr(input, ':');
if (ptr) {
*ptr++ = 0;
id = atonum(ptr);
id = ptr;
}
if (isdigit(input[0]))
return svc_find_by_jobid(atonum(input), id);
if (isdigit(input[0])) {
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);
}
@@ -160,47 +181,6 @@ ev_t ev_list[] = {
{ 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)
{
svc_t empty;
@@ -295,31 +275,30 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_START_SVC:
_d("start %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_start(rq.data, sizeof(rq.data));
break;
case INIT_CMD_STOP_SVC:
_d("stop %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_stop(rq.data, sizeof(rq.data));
break;
case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_restart(rq.data, sizeof(rq.data));
break;
#ifdef INETD_ENABLED
case INIT_CMD_QUERY_INETD:
_d("query inetd");
strterm(rq.data, sizeof(rq.data));
result = do_query_inetd(rq.data, sizeof(rq.data));
break;
#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:
_d("get runlevel");
rq.runlevel = runlevel;
@@ -337,13 +316,23 @@ static void api_cb(uev_t *w, void *arg, int events)
break;
}
if (wdogpid > 0 && wdogpid != rq.runlevel) {
_d("Sending SIGTERM to %d", wdogpid);
kill(wdogpid, SIGTERM);
do_sleep(1);
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
svc = svc_find_by_pid(rq.runlevel);
if (!svc) {
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
break;
}
_d("wdog was %d, now %d is in charge", wdogpid, rq.runlevel);
wdogpid = rq.runlevel;
/* Disable and allow Finit to collect bundled watchdog */
if (wdog) {
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
stop(wdog);
if (wdog->protect) {
wdog->protect = 0;
wdog->runlevels = 0;
}
}
wdog = svc;
break;
case INIT_CMD_SVC_ITER:
@@ -359,11 +348,13 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_SVC_QUERY:
_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;
case INIT_CMD_SVC_FIND:
_d("svc find: %s", rq.data);
strterm(rq.data, sizeof(rq.data));
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
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:
*/
+32
View File
@@ -0,0 +1,32 @@
/* 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.
*/
#ifndef FINIT_CGROUP_H_
#define FINIT_CGROUP_H_
void cgroup_init (void);
int cgroup_user (char *name);
int cgroup_service (char *cmd, char *id, int pid);
#endif /* FINIT_CGROUP_H_ */
+1 -1
View File
@@ -125,7 +125,7 @@ error:
return NULL;
}
svc_t *client_svc_find(char *arg)
svc_t *client_svc_find(const char *arg)
{
int sd = -1;
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);
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_ */
+89 -4
View File
@@ -31,12 +31,96 @@
#include "pid.h"
#include "service.h"
static int cond_set_gen(const char *path, unsigned int gen)
/*
* 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, *nm;
strlcpy(path, svc->cmd, sizeof(path));
ptr = rindex(path, '/');
if (ptr)
*ptr++ = 0;
else
path[0] = 0;
/* Figure out default name used when registering service */
if (ptr)
nm = ptr;
else
nm = svc->cmd;
pidfile = pid_file(svc);
ptr = strstr(pidfile, "run");
if (ptr)
ptr += 3;
else
ptr = rindex(pidfile, '/');
/* Custom name:foo declaration found => svc/foo instead of /svc/bin/path/pidfile-.pid */
if (strcmp(nm, svc->name)) {
snprintf(buf, len, "svc/%s", svc->name);
_d("Composed condition from svc->name %s => %s", svc->name, buf);
} else {
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;
/* Always append /ID if service is declared with :ID */
if (svc->id[0]) {
strlcat(buf, "/", len);
strlcat(buf, svc->id, len);
}
_d("Creating condition => %s", buf);
return buf;
}
static int cond_set_gen(const char *file, unsigned int gen)
{
char *ptr, path[256];
FILE *fp;
int ret;
fp = fopen(path, "w");
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "w");
if (!fp)
return -1;
@@ -124,6 +208,7 @@ static int svc_has_cond(svc_t *svc)
case SVC_TYPE_SERVICE:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
return 1;
default:
@@ -148,7 +233,7 @@ static void cond_update(const char *name)
}
void cond_set(const char *name)
{
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
@@ -167,7 +252,7 @@ void cond_set_oneshot(const char *name)
return;
path = cond_path(name);
_d("s => %s", name, path);
_d("%s => %s", name, path);
if (cond_checkpath(path))
return;
+11 -8
View File
@@ -29,7 +29,6 @@
#include "pid.h"
#include "service.h"
const char *condstr(enum cond_state s)
{
static const char *strs[] = {
@@ -43,21 +42,25 @@ const char *condstr(enum cond_state s)
const char *cond_path(const char *name)
{
static char path[256];
char tmp[MAX_ARG_LEN];
static char file[MAX_ARG_LEN];
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 *path)
unsigned int cond_get_gen(const char *file)
{
unsigned int gen;
char *ptr, path[256];
FILE *fp;
int ret;
fp = fopen(path, "r");
/* /var/run --> /run symlink may not exist (yet) */
ptr = pid_runpath(file, path, sizeof(path));
fp = fopen(ptr, "r");
if (!fp)
return 0;
@@ -89,9 +92,9 @@ enum cond_state cond_get(const char *name)
enum cond_state cond_get_agg(const char *names)
{
char *cond;
static char conds[MAX_COND_LEN];
enum cond_state s = COND_ON;
static char conds[MAX_ARG_LEN];
char *cond;
if (!names)
return COND_ON;
@@ -105,8 +108,8 @@ enum cond_state cond_get_agg(const char *names)
int cond_affects(const char *name, const char *names)
{
static char conds[MAX_COND_LEN];
char *cond;
static char conds[MAX_ARG_LEN];
if (!name || !names)
return 0;
+2
View File
@@ -2,6 +2,7 @@
#define FINIT_COND_H_
#include <paths.h>
#include <svc.h>
#define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
@@ -13,6 +14,7 @@ typedef enum cond_state {
COND_ON
} cond_state_t;
char *mkcond (svc_t *svc, char *buf, size_t len);
const char *condstr (enum cond_state s);
const char *cond_path (const char *name);
unsigned int cond_get_gen (const char *path);
+29 -20
View File
@@ -30,6 +30,7 @@
#include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include <sys/time.h>
#include <glob.h>
#include "finit.h"
#include "cond.h"
@@ -52,7 +53,7 @@ struct conf_change {
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 int parse_conf(char *file);
@@ -267,7 +268,7 @@ char *rlim2str(int rlim)
char *lim2str(struct rlimit *rlim)
{
char tmp[20];
char tmp[25];
static char buf[42];
memset(buf, 0, sizeof(buf));
@@ -463,6 +464,12 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
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 */
if (MATCH_CMD(line, "inetd ", x)) {
#ifdef INETD_ENABLED
@@ -577,8 +584,8 @@ static int parse_conf(char *file)
*/
int conf_reload(void)
{
int i, num;
struct dirent **e;
size_t i;
glob_t gl;
/* Mark and sweep */
svc_mark_dynamic();
@@ -601,20 +608,14 @@ int conf_reload(void)
parse_conf(FINIT_CONF);
/* Next, read all *.conf in /etc/finit.d/ */
num = scandir(rcsd, &e, NULL, alphasort);
if (num < 0) {
_d("Skipping %s, no files found ...", rcsd);
goto done;
}
glob("/etc/finit.d/*.conf", 0, NULL, &gl);
glob("/etc/finit.d/enabled/*.conf", GLOB_APPEND, NULL, &gl);
for (i = 0; i < num; i++) {
char *name = e[i]->d_name;
char path[LINE_SIZE];
for (i = 0; i < gl.gl_pathc; i++) {
char *path = gl.gl_pathv[i];
size_t len;
struct stat st;
snprintf(path, sizeof(path), "%s/%s", rcsd, name);
/* Check that it's an actual file ... beyond any symlinks */
if (lstat(path, &st)) {
_d("Skipping %s, cannot access: %s", path, strerror(errno));
@@ -650,9 +651,7 @@ int conf_reload(void)
parse_conf_dynamic(path);
}
while (num--)
free(e[num]);
free(e);
globfree(&gl);
done:
/* Drop record of all .conf changes */
@@ -706,6 +705,8 @@ static int do_change(char *name, uint32_t mask)
{
struct conf_change *node;
_d("Change detected for %s, mask 0x%08x", name, mask);
node = conf_find(name);
if (mask & (IN_DELETE | IN_MOVED_FROM)) {
drop_change(node);
@@ -727,6 +728,7 @@ static int do_change(char *name, uint32_t mask)
return 1;
}
_d("Event registered for %s, mask 0x%x", name, mask);
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
return 0;
@@ -782,6 +784,11 @@ static void conf_cb(uev_t *w, void *arg, int events)
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)
@@ -834,6 +841,7 @@ static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
close(fd);
return 1;
}
_d("Set up inotify watcher for %s ...", path);
return 0;
}
@@ -856,8 +864,9 @@ int conf_monitor(uev_ctx_t *ctx)
* changes to either symlink or target.
*/
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW);
rc += add_watcher(ctx, &w3, FINIT_CONF, 0);
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available/", IN_DONT_FOLLOW);
rc += add_watcher(ctx, &w3, FINIT_RCSD "/enabled/", 0);
rc += add_watcher(ctx, &w4, FINIT_CONF, 0);
return rc + conf_reload();
}
@@ -868,7 +877,7 @@ int conf_monitor(uev_ctx_t *ctx)
int conf_init(void)
{
hostname = strdup(DEFHOST);
w1.fd = w2.fd = w3.fd = -1;
w1.fd = w2.fd = w3.fd = w4.fd = -1;
return conf_monitor(NULL);
}
+40 -37
View File
@@ -231,6 +231,7 @@ int exec_runtask(char *cmd, char *args[])
strlcat(buf, args[i], sizeof(buf));
}
logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf);
_d("Calling %s %s", _PATH_BSHELL, buf);
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 termios term;
char name[80];
/*
* 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 */
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)
{
struct termios orig;
int ret = 0;
struct termios term;
if (!noclear)
(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;
/* Disable ECHO, XON/OFF while waiting for <CR> */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_iflag &= ~(IXON|IXOFF);
term.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
if (!tcgetattr(STDIN_FILENO, &orig)) {
struct termios c = orig;
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)) {
char c;
static const char clr[] = "\r\e[2K";
static const char cup[] = "\e[A";
static const char msg[] = "\nPlease press Enter to activate this console.";
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, msg, strlen(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
continue;
if (fexist(SYNC_STOPPED))
continue;
(void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, cup, strlen(cup));
ret = 1;
break;
}
/* Restore ECHO, XON/OFF */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_iflag |= IXON|IXOFF;
term.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
/* Restore TTY */
if (tcsetattr(STDIN_FILENO, TCSANOW, &orig) == -1)
ret = 0;
return ret;
}
@@ -361,15 +369,12 @@ pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, stru
pid = fork();
if (!pid) {
speed_t speed = B38400;
speed_t speed;
if (baud) {
speed = stty_parse_speed(baud);
if (B0 == speed)
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
}
speed = stty_parse_speed(baud);
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
prepare_tty(tty, speed, "tty", rlimit);
prepare_tty(tty, speed, "finit-getty", rlimit);
if (activate_console(noclear, nowait))
_exit(getty(tty, speed, term, NULL));
}
@@ -407,6 +412,7 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, st
dup2(fd, STDERR_FILENO);
/* Dunno speed, tell stty() to not mess with it */
logit(LOG_INFO, "Starting external getty on %s, speed %u", tty, B0);
prepare_tty(tty, B0, "getty", rlimit);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
@@ -449,12 +455,16 @@ int run_parts(char *dir, char *cmd)
}
for (i = 0; i < num; i++) {
int j = 0;
pid_t pid = 0;
struct stat st;
char *args[NUM_ARGS];
char path[512];
char *argv[4] = {
"sh",
"-c",
path,
NULL
};
char *name = e[i]->d_name;
char path[LINE_SIZE];
pid_t pid = 0;
snprintf(path, sizeof(path), "%s/%s", dir, name);
if (stat(path, &st)) {
@@ -467,29 +477,22 @@ int run_parts(char *dir, char *cmd)
continue;
}
/* Fill in args[], starting with full path to executable */
args[j++] = path;
/* If the callee didn't supply a run_parts() argument */
if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */
_d("Checking if %s is a sysvinit startstop script ...", name);
if (name[0] == 'S' && isdigit(name[1])) {
args[j++] = "start";
} else if (name[0] == 'K' && isdigit(name[1])) {
args[j++] = "stop";
}
if (name[0] == 'S' && isdigit(name[1]))
strlcat(path, " start", sizeof(path));
else if (name[0] == 'K' && isdigit(name[1]))
strlcat(path, " stop", sizeof(path));
} else {
args[j++] = cmd;
strlcat(path, cmd, sizeof(path));
}
args[j++] = NULL;
pid = fork();
if (!pid) {
_d("Calling %s ...", path);
sig_unblock();
execv(path, args);
exit(0);
return execvp(_PATH_BSHELL, argv);
}
complete(path, pid);
+163 -85
View File
@@ -36,6 +36,7 @@
#include <lite/lite.h>
#include "finit.h"
#include "cgroup.h"
#include "cond.h"
#include "conf.h"
#include "helpers.h"
@@ -47,9 +48,8 @@
#include "tty.h"
#include "util.h"
#include "utmp-api.h"
#include "watchdog.h"
#include "schedule.h"
int wdogpid = 0; /* No watchdog by default */
int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int prevlevel = -1;
@@ -63,15 +63,16 @@ char *rcsd = FINIT_RCSD;
char *runparts = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
svc_t *wdog = NULL; /* No watchdog by default */
static int udev = 0; /* Runtime detection of udev */
/*
* Show user configured banner before service bootstrap progress
*/
static void banner(void)
{
char *buf = INIT_HEADING;
char separator[SCREEN_WIDTH];
if (plugin_exists(HOOK_BANNER)) {
plugin_run_hooks(HOOK_BANNER);
return;
@@ -80,8 +81,7 @@ static void banner(void)
if (log_is_silent())
return;
memset(separator, '=', sizeof(separator));
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
print_banner(INIT_HEADING);
}
static int ismnt(char *file, char *dir)
@@ -214,15 +214,20 @@ static void emergency_shell(void)
*/
static void finalize(void)
{
svc_t *svc;
/*
* Track bundled watchdogd in case a better one turns up
*/
svc = svc_find(FINIT_LIBPATH_ "/watchdogd", "1");
if (svc)
wdog = svc;
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue) {
_d("Running startup scripts in %s ...", runparts);
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
if (conf_any_change())
service_reload_dynamic();
}
/*
* Start all tasks/services in the configured runlevel
@@ -240,11 +245,8 @@ static void finalize(void)
service_step_all(SVC_TYPE_ANY);
/* Convenient SysV compat for when you just don't care ... */
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue) {
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
if (conf_any_change())
service_reload_dynamic();
}
/* Hooks that should run at the very end */
_d("Calling all system up hooks ...");
@@ -260,13 +262,61 @@ static void finalize(void)
tty_runlevel();
}
int main(int argc, char* argv[])
/*
* Start cranking the big state machine
*/
static void crank_worker(void *unused)
{
/*
* Initalize state machine and start all bootstrap tasks
* NOTE: no network available!
*/
sm_init(&sm);
sm_step(&sm);
/* Debian has this little script to copy generated rules while the system was read-only */
if (udev && fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
}
/*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
* allowed to complete their work in [S], or timeout, before we call
* finalize(), should not take more than 120 sec.
*/
static void final_worker(void *work)
{
static int cnt = 120;
_d("Step all services ...");
service_step_all(SVC_TYPE_ANY);
if (cnt-- > 0 && !service_completed()) {
_d("Not all bootstrap run/tasks have completed yet ... %d", cnt);
schedule_work(work);
return;
}
if (cnt > 0)
_d("All run/task have completed, resuming bootstrap.");
else
_d("Timeout, resuming bootstrap.");
finalize();
}
int main(int argc, char *argv[])
{
struct wq crank = {
.cb = crank_worker
};
struct wq final = {
.cb = final_worker,
.delay = 1000
};
uev_ctx_t loop;
char *path;
char cmd[256];
int udev = 0;
uev_t timer; /* Bootstrap timer, on timeout call finalize() */
uev_ctx_t loop;
/*
* finit/init/telinit client tool uses /dev/initctl pipe
@@ -275,27 +325,48 @@ int main(int argc, char* argv[])
if (getpid() != 1)
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.
*/
uev_init(&loop);
uev_init1(&loop, 1);
ctx = &loop;
/*
* Set the PATH early to something sane
* Set PATH and SHELL early to something sane
*/
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
/*
* Mount base file system, kernel is assumed to run devtmpfs for /dev
*/
chdir("/");
umask(0);
mount("none", "/proc", "proc", 0, NULL);
mount("none", "/sys", "sysfs", 0, NULL);
if (mount("none", "/proc", "proc", 0, NULL))
_pe("Failed mounting /proc");
if (mount("none", "/sys", "sysfs", 0, NULL))
_pe("Failed mounting /sysfs");
if (fisdir("/proc/bus/usb"))
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
/*
* Initialize default control groups, if available
*/
cgroup_init();
/*
* Parse kernel command line (debug, rescue, splash, etc.)
* Also calls log_init() to set correct log level
@@ -350,9 +421,9 @@ int main(int argc, char* argv[])
else if (whichp("udevadm"))
run_interactive("udevadm info --cleanup-db", "Cleaning up udev db");
/* Some systems use /dev/pts */
/* Modern systems use /dev/pts */
makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620,ptmxmode=0666");
/*
* Some systems rely on us to both create /dev/shm and, to mount
@@ -362,84 +433,99 @@ int main(int argc, char* argv[])
*/
makedir("/dev/shm", 0755);
if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm"))
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
/*
* New tmpfs based /run for volatile runtime data
* For details, see http://lwn.net/Articles/436012/
*/
if (fisdir("/run") && !fismnt("/run"))
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
mount("tmpfs", "/run", "tmpfs", MS_NODEV | MS_NOSUID | MS_NOEXEC, "mode=0755,size=10%");
umask(022);
/* Bootstrap conditions, needed for hooks */
cond_init();
/*
* Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system.
*/
path = which("mdev");
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (log_is_debug())
touch("/dev/mdev.log");
snprintf(cmd, sizeof(cmd), "%s -s", path);
} else {
/* Desktop and server distros usually have a variant of udev */
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
udev = 1;
udev = 1;
/* Register udevd as a monitored service, started much later */
snprintf(cmd, sizeof(cmd), "[12345] %s -- Device event manager daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
udev = 0;
}
/* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
udev = 0;
} else {
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
"udevadm trigger -c add -t devices "
"-- Requesting device events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
/* Start a temporary udevd instance to populate /dev */
snprintf(cmd, sizeof(cmd), "%s --daemon", path);
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
"udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events", path);
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
}
}
if (path) {
free(path);
} else {
path = which("mdev");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (log_is_debug())
touch("/dev/mdev.log");
run_interactive(cmd, "Populating device tree");
if (udev && whichp("udevadm")) {
run("udevadm trigger --action=add --type=subsystems");
run("udevadm trigger --action=add --type=devices");
run("udevadm settle --timeout=120");
snprintf(cmd, sizeof(cmd), "%s -s", path);
free(path);
/* Tell temporary udevd to exit, we'll start a monitored instance later */
run("udevadm control --exit");
run_interactive(cmd, "Populating device tree");
}
}
/*
* Start built-in watchdog as soon as possible, if enabled
* Start bundled watchdogd as soon as possible, if enabled
*/
wdogpid = watchdog(argv[0]);
if (which(FINIT_LIBPATH_ "/watchdogd"))
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
/*
* Mount filesystems
*/
if (!rescue) {
#ifdef REMOUNT_ROOTFS
run("mount -n -o remount,rw /");
#endif
#ifdef SYSROOT
#ifndef SYSROOT
/*
* Remount / read-write if it exists in fstab is not 'ro'.
* This is what the Debian sysv initscripts does.
*/
if (setfsent()) {
struct fstab *fs;
while ((fs = getfsent())) {
if (strcmp(fs->fs_file, "/"))
continue;
if (strcmp(fs->fs_type, "ro"))
run_interactive("mount -n -o remount,rw /", "Remounting / as read-write");
break;
}
endfsent();
}
#else
/*
* XXX: Untested, in the initramfs age we should
* probably use switch_root instead.
*/
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif
}
/* Debian has this little script to copy generated rules while the system was read-only */
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
/* Bootstrap conditions, needed for hooks */
cond_init();
if (!rescue) {
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
@@ -469,26 +555,18 @@ int main(int argc, char* argv[])
*/
conf_monitor(&loop);
/*
* Initalize state machine and start all bootstrap tasks
* NOTE: no network available!
*/
sm_init(&sm);
sm_step(&sm);
/* Start new initctl API responder */
_d("Starting initctl API responder ...");
api_init(&loop);
umask(022);
/*
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
* [S], or timeout, before calling finalize()
*/
_d("Starting the big state machine ...");
schedule_work(&crank);
_d("Starting bootstrap finalize timer ...");
uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000);
schedule_work(&final);
/*
* Enter main loop to monior /dev/initctl and services
* Enter main loop to monitor /dev/initctl and services
*/
_d("Entering main loop ...");
return uev_run(&loop, 0);
+2 -3
View File
@@ -45,7 +45,6 @@
#define _PATH_VARRUN "/var/run/"
#endif
#define CMD_SIZE 256
#define LINE_SIZE 1024
#define BUF_SIZE 4096
@@ -75,7 +74,7 @@
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
#define INIT_CMD_QUERY_INETD 14
#define INIT_CMD_EMIT 15
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
#define INIT_CMD_GET_RUNLEVEL 16
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
#define INIT_CMD_SVC_ITER 129
@@ -84,6 +83,7 @@
#define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255
/* Traditionally aligned on 384 bytes */
struct init_request {
int magic; /* Magic number */
int cmd; /* What kind of request */
@@ -92,7 +92,6 @@ struct init_request {
char data[368];
};
extern int wdogpid;
extern int runlevel;
extern int cfglevel;
extern int prevlevel;
+10 -6
View File
@@ -32,6 +32,7 @@
#include <termios.h>
#include "finit.h"
#include "cgroup.h"
#include "helpers.h"
#include "sig.h"
#include "utmp-api.h"
@@ -192,6 +193,7 @@ static int do_login(char *name)
{
struct stat st;
cgroup_user(name);
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 fd;
const char cln[] = "\r\e[2K\n";
char name[30];
int fd;
/* Detach from initial controlling TTY */
vhangup();
@@ -222,10 +225,15 @@ int getty(char *tty, speed_t speed, char *term, char *user)
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
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 */
utmp_set_login(tty, NULL);
if (!user)
@@ -233,11 +241,7 @@ int getty(char *tty, speed_t speed, char *term, char *user)
else
strlcpy(name, user, sizeof(name));
/* Set up TTY, re-enabling ISIG et al. */
stty(fd, speed);
close(fd);
if (term)
if (term && term[0])
setenv("TERM", term, 1);
return do_login(name);
+132 -32
View File
@@ -39,6 +39,30 @@
#include "util.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)
{
@@ -57,6 +81,81 @@ char *strip_line(char *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)
{
#if defined(CONFIG_PRINTK_TIME)
@@ -77,6 +176,26 @@ static size_t print_timestamp(char *buf, size_t len)
#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)
{
char buf[SCREEN_WIDTH];
@@ -91,51 +210,32 @@ void printv(const char *fmt, va_list ap)
len = print_timestamp(buf, sizeof(buf));
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
len = strlen(buf);
buf[len++] = ' ';
for (size_t i = len; i < (sizeof(buf) - 8); i++)
buf[i] = '.'; /* pad with dots. */
fprintf(stderr, "\r%s ", buf);
if (progress_style == 1)
fprintf(stderr, "\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
else
fprintf(stderr, "\r\e[2K%s%s", status(3), 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())
return;
if (fmt) {
va_list ap;
va_start(ap, fmt);
printv(fmt, ap);
va_end(ap);
}
switch (action) {
case -1:
break;
if (rc < 0)
return;
case 0:
write(STDERR_FILENO, success, sizeof(success));
break;
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;
}
if (progress_style == 1)
fprintf(stderr, "%s\n", status(rc));
else
fprintf(stderr, ".\r%s\n", status(rc));
}
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);
speed_t stty_parse_speed(char *baud);
void print_banner (const char *heading);
void printv (const char *fmt, va_list ap);
void print (int action, const char *fmt, ...);
void print_desc (char *action, char *desc);
+46 -15
View File
@@ -43,12 +43,13 @@
} while (0);
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
static int inetd_dgram_peek(int sd, char *ifname)
static int inetd_dgram_peek(int sd, char *ifname, size_t len)
{
char cmbuf[0x100];
struct msghdr msgh;
struct cmsghdr *cmsg;
struct msghdr msgh;
char cmbuf[0x100];
memset(ifname, 0, len);
memset(&msgh, 0, sizeof(msgh));
msgh.msg_control = cmbuf;
msgh.msg_controllen = sizeof(cmbuf);
@@ -56,13 +57,15 @@ static int inetd_dgram_peek(int sd, char *ifname)
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
return -1;
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
char tmp[IF_NAMESIZE + 1] = { 0 };
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
continue;
if_indextoname(ipi->ipi_ifindex, ifname);
if_indextoname(ipi->ipi_ifindex, tmp);
strlcpy(ifname, tmp, len);
return 0;
}
@@ -73,11 +76,11 @@ static int inetd_dgram_peek(int sd, char *ifname)
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
static void inetd_dgram_drop(int sd, const char *ifname)
{
char pkt_interface[17];
char pkt_interface[IF_NAMESIZE + 1];
char buf[BUFSIZ];
while (1) {
inetd_dgram_peek(sd, pkt_interface);
inetd_dgram_peek(sd, pkt_interface, sizeof(pkt_interface));
if (string_compare(pkt_interface, ifname)) {
if (recv(sd, buf, sizeof(buf), 0) < 0)
break;
@@ -88,12 +91,13 @@ static void inetd_dgram_drop(int sd, const char *ifname)
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
static int inetd_stream_peek(int sd, char *ifname)
static int inetd_stream_peek(int sd, char *ifname, size_t ilen)
{
struct ifaddrs *ifaddr, *ifa;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
memset(ifname, 0, ilen);
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
return -1;
@@ -112,7 +116,7 @@ static int inetd_stream_peek(int sd, char *ifname)
iin = (struct sockaddr_in *)ifa->ifa_addr;
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE);
strlcpy(ifname, ifa->ifa_name, ilen);
break;
}
}
@@ -122,11 +126,12 @@ static int inetd_stream_peek(int sd, char *ifname)
return 0;
}
static int get_stdin(svc_t *svc)
static int get_stdin(svc_t *svc, char *iifname, size_t len)
{
int stdin = svc->inetd.watcher.fd;
char ifname[IF_NAMESIZE] = "UNKNOWN";
char ifname[IF_NAMESIZE + 1] = "UNKNOWN";
memset(iifname, 0, len);
if (svc->inetd.type == SOCK_STREAM) {
/* Open new client socket from server socket */
stdin = accept(stdin, NULL, NULL);
@@ -137,9 +142,9 @@ static int get_stdin(svc_t *svc)
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
inetd_stream_peek(stdin, ifname);
inetd_stream_peek(stdin, ifname, sizeof(ifname));
} else { /* SOCK_DGRAM */
inetd_dgram_peek(stdin, ifname);
inetd_dgram_peek(stdin, ifname, sizeof(ifname));
}
if (!inetd_is_allowed(&svc->inetd, ifname)) {
@@ -152,6 +157,9 @@ static int get_stdin(svc_t *svc)
return -1;
}
/* Return ingress interface */
strlcpy(iifname, ifname, len);
return stdin;
}
@@ -160,6 +168,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
{
svc_t *svc = (svc_t *)arg, *task;
const char *conn = " connection";
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
char id[MAX_ID_LEN];
int stdin;
_d("%s: Got socket event ...", svc->cmd);
@@ -168,7 +178,7 @@ static void socket_cb(uev_t *w, void *arg, int events)
return;
}
stdin = get_stdin(svc);
stdin = get_stdin(svc, iifname, sizeof(iifname));
if (stdin < 0) {
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
return;
@@ -185,7 +195,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
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) {
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
if (svc->inetd.type == SOCK_STREAM)
@@ -216,6 +227,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
memcpy(task->args, svc->args, sizeof(task->args));
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
strlcat(task->desc, conn, sizeof(task->desc));
strlcpy(task->iifname, iifname, sizeof(task->iifname));
strlcpy(task->name, svc->name, sizeof(task->name));
task->stdin_fd = stdin;
service_step(task);
@@ -335,6 +348,22 @@ int inetd_start(inetd_t *inetd)
return 0;
}
void inetd_stop_children(inetd_t *inetd, int check_allowed)
{
svc_t *svc, *iter = NULL;
svc = svc_job_iterator(&iter, 1, inetd->svc->job);
while (svc) {
if (!svc_is_inetd(svc)) {
if (!check_allowed || !inetd_is_allowed(inetd, svc->iifname)) {
svc_stop(svc);
service_step(svc);
}
}
svc = svc_job_iterator(&iter, 0, inetd->svc->job);
}
}
void inetd_stop(inetd_t *inetd)
{
if (!inetd || !inetd->svc) {
@@ -354,6 +383,8 @@ void inetd_stop(inetd_t *inetd)
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
close(inetd->watcher.fd);
inetd->watcher.fd = -1;
inetd_stop_children(inetd, 0);
}
}
}
+1
View File
@@ -58,6 +58,7 @@ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name);
int inetd_start (inetd_t *inetd);
void inetd_stop (inetd_t *inetd);
void inetd_stop_children (inetd_t *inetd, int check_allowed);
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
int inetd_del (inetd_t *inetd);
+101 -40
View File
@@ -140,7 +140,6 @@ static int do_svc(int cmd, char *arg)
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); }
/*
@@ -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 char conds[MAX_ARG_LEN];
static char conds[MAX_COND_LEN];
char *cond;
strlcpy(conds, _conds, sizeof(conds));
@@ -202,22 +201,6 @@ static void show_cond_one(const char *_conds)
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)
{
int len;
@@ -279,8 +262,6 @@ static int do_cond(char *cmd)
char *arg;
struct command command[] = {
{ "show", do_cond_show },
{ "set", do_cond_set },
{ "clear", do_cond_clear },
{ "dump", do_cond_dump },
{ NULL, NULL }
};
@@ -435,17 +416,17 @@ static int show_status(char *arg)
}
if (!verbose)
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char jobid[10], args[512] = "", *lvls;
char jobid[20], args[512] = "", *lvls;
// if (svc_is_unique(svc))
// snprintf(jobid, sizeof(jobid), "%d", svc->job);
// else
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
if (!svc->id[0])
snprintf(jobid, sizeof(jobid), "%d", svc->job);
else
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))
printf("inetd ");
else
@@ -459,14 +440,7 @@ static int show_status(char *arg)
if (!verbose) {
int adj = screen_cols - 60;
char *name = strrchr(svc->cmd, '/');
if (!name)
name = svc->cmd;
else
name++;
printf("%-16.16s %-*.*s\n", name, adj, adj, svc->desc);
printf("%-16.16s %-*.*s\n", svc->name, adj, adj, svc->desc);
continue;
}
@@ -516,12 +490,78 @@ static int show_status(char *arg)
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)
{
fprintf(stderr,
"Usage: %s [OPTIONS] [COMMAND]\n"
"\n"
"Options:\n"
" -b, --batch Batch mode, no screen size probing\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n"
"\n"
@@ -537,8 +577,6 @@ static int usage(int rc)
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\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 dump Dump all conditions and their status\n"
"\n"
@@ -549,6 +587,8 @@ static int usage(int rc)
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
" status | show Show status of services, default command\n"
"\n"
" ps List processes based on cgroups\n"
"\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n"
" halt Halt system\n"
@@ -567,7 +607,7 @@ static int do_help(char *arg)
int main(int argc, char *argv[])
{
int c;
int interactive = 1, c;
char *cmd, arg[120];
struct command command[] = {
{ "debug", toggle_debug },
@@ -589,6 +629,8 @@ int main(int argc, char *argv[])
{ "status", show_status },
{ "show", show_status }, /* Convenience alias */
{ "ps", show_cgroup },
{ "runlevel", do_runlevel },
{ "reboot", do_reboot },
{ "halt", do_halt },
@@ -598,6 +640,7 @@ int main(int argc, char *argv[])
{ NULL, NULL }
};
struct option long_options[] = {
{"batch", 0, NULL, 'b'},
{"help", 0, NULL, 'h'},
{"debug", 0, NULL, 'd'},
{"verbose", 0, NULL, 'v'},
@@ -605,8 +648,12 @@ int main(int argc, char *argv[])
};
progname(argv[0]);
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "bh?v", long_options, NULL)) != EOF) {
switch(c) {
case 'b':
interactive = 0;
break;
case 'h':
case '?':
return usage(0);
@@ -617,7 +664,8 @@ int main(int argc, char *argv[])
}
}
screen_init();
if (interactive)
screen_init();
if (optind >= argc)
return show_status(NULL);
@@ -640,6 +688,19 @@ int main(int argc, char *argv[])
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:
* indent-tabs-mode: t
+1 -3
View File
@@ -62,10 +62,8 @@ void log_init(int dbg)
void log_exit(void)
{
muffler();
if (!silent) {
if (!silent)
sched_yield();
fputs("\n", stderr);
}
/*
* Unless in debug mode at shutdown, Reinitialize screen,
+5
View File
@@ -26,6 +26,11 @@
#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() <--
* ~~~~~~~~~~~
+21 -2
View File
@@ -45,7 +45,7 @@ int pid_alive(pid_t pid)
{
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);
}
@@ -95,8 +95,8 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
char *pid_file(svc_t *svc)
{
static char path[256];
char fn[MAX_ARG_LEN];
static char path[MAX_ARG_LEN];
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));
}
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)
{
FILE *fp;
+17 -3
View File
@@ -31,13 +31,25 @@ int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc);
pid_t pid_file_read (const char *fn);
int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg);
static inline char *pid_runpath(char *file, char *path, size_t len)
/**
* pid_runpath - Adjust /var/run --> /run path depending on system
* @file: Path to file in /run/path or /var/run/path
* @path: Pointer to buffer to write correct path
* @len: Length, in bytes, of @path buffer
*
* Returns:
* Always returns a valid pointer, which can be either @path with a /run
* or /var/run prefix to @file, or it may be @file if the prefix is OK.
*/
static inline char *pid_runpath(const char *file, char *path, size_t len)
{
static int unknown = 1;
static char *prefix = "/var/run";
static int unknown = 1;
int rc;
if (unknown) {
if (fisdir("/run"))
@@ -50,7 +62,9 @@ static inline char *pid_runpath(char *file, char *path, size_t len)
else if (!strncmp(file, "/run/", 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;
}
+1 -16
View File
@@ -189,19 +189,11 @@ int plugin_exists(hook_point_t no)
return 0;
}
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
/* Some hooks are called with a fixed argument */
void plugin_run_hook(hook_point_t no, void *arg)
{
static int last = -1;
plugin_t *p, *tmp;
/*
* End recursion: any plugin hook => start service => SVC start
* hook => start service ... err ... wait a second
*/
if (HOOK_SVC_START == last)
return;
PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) {
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
@@ -209,15 +201,8 @@ void plugin_run_hook(hook_point_t no, void *arg)
}
}
/* Guard against infinite recursion */
if (HOOK_SVC_START == no)
last = no;
cond_set_oneshot(hook_cond[no]);
service_step_all(SVC_TYPE_RUNTASK);
if (HOOK_SVC_START == no)
last = -1;
}
/* Regular hooks are called with the registered plugin's argument */
-4
View File
@@ -56,8 +56,6 @@
* according to the following scheme:
*
* - HOOK_SVC_RECONF :: action/svc/reconf
* - HOOK_SVC_LOST :: action/svc/lost
* - HOOK_SVC_START :: action/svc/start
* - HOOK_RUNLEVEL_CHANGE :: action/sys/runlevel
*
* However, the implementation did not turn out to be stable enough for
@@ -76,8 +74,6 @@
\
/* Runtime hooks, runlevel [S1-9] */ \
CHOOSE(HOOK_SVC_RECONF, "nop"), \
CHOOSE(HOOK_SVC_LOST, "nop"), \
CHOOSE(HOOK_SVC_START, "nop"), \
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
\
/* Shutdown hooks, runlevel [06] */ \
+1 -6
View File
@@ -28,17 +28,12 @@
#include "svc.h"
#include "plugin.h"
/*
* Main loop context
*/
uev_ctx_t *ctx;
int api_init (uev_ctx_t *ctx);
int api_exit (void);
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);
int plugin_exists (hook_point_t no);
+30 -21
View File
@@ -1,6 +1,6 @@
/* Example HOOK_SVC_LOST plugin, very noisy do not use as-is!
/* Work queue helper functions
*
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,35 +21,44 @@
* THE SOFTWARE.
*/
#include "config.h"
#include "finit.h"
#include "svc.h"
#include "plugin.h"
#include "schedule.h"
static void lost_pid(void *arg)
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
/*
* libuEv callback wrapper
*/
static void cb(uev_t *w, void *arg, int events)
{
pid_t pid = (uintptr_t)arg;
svc_t *svc;
struct wq *work = (struct wq *)arg;
svc = svc_find_by_pid(pid);
if (!svc)
if (UEV_ERROR == events) {
uev_timer_start(w);
return;
}
logit(LOG_INFO, "Lost PID %u, svc %s", pid, svc->cmd);
work->cb(work);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_SVC_LOST] = { .cb = lost_pid },
};
PLUGIN_INIT(plugin_init)
/*
* Place work on event queue
*/
int schedule_work(struct wq *work)
{
plugin_register(&plugin);
}
int msec;
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
if (!work)
return errno = EINVAL;
msec = work->delay;
if (work->init != SC_INIT) {
work->init = SC_INIT;
return uev_timer_init(ctx, &work->watcher, cb, work, msec, 0);
}
return uev_timer_set(&work->watcher, msec, 0);
}
/**
+12 -44
View File
@@ -1,6 +1,6 @@
/* Save and restore RTC using hwclock
/* Work queue helper functions
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,49 +21,17 @@
* THE SOFTWARE.
*/
#include <lite/lite.h>
#ifndef FINIT_SCHEDULE_H_
#define FINIT_SCHEDULE_H_
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
static void save(void *arg)
{
_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)
{
_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
}
struct wq {
uev_t watcher;
int init;
int delay; /* msec delay before starting work */
void (*cb)(void *);
void *arg;
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
int schedule_work(struct wq *work);
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
#endif /* FINIT_SCHEDULE_H_ */
+19 -7
View File
@@ -120,7 +120,7 @@ int serv_list(char *arg)
if (fisdir(enabled))
do_list("ENABLED ", enabled);
if (fisdir(FINIT_RCSD))
do_list("ENABLED ", FINIT_RCSD);
do_list("STATIC ", FINIT_RCSD);
return 0;
}
@@ -128,8 +128,11 @@ int serv_list(char *arg)
int serv_enable(char *arg)
{
char corr[40];
char link[80];
char path[80];
char link[256];
char path[256];
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
@@ -137,11 +140,14 @@ int serv_enable(char *arg)
}
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))
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))
errx(1, "%s already exists", link);
@@ -151,15 +157,18 @@ int serv_enable(char *arg)
int serv_disable(char *arg)
{
char corr[40];
char link[80];
char link[256];
struct stat st;
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
}
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
snprintf(link, sizeof(link), "%s/%s", enabled, arg);
if (stat(link, &st))
err(1, "Cannot find %s", link);
@@ -173,6 +182,9 @@ int serv_touch(char *arg)
{
char corr[40];
if (!arg || !arg[0])
return serv_list(NULL);
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
+491 -201
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -36,8 +36,9 @@ void service_reload_dynamic (void);
int service_step (svc_t *svc);
void service_step_all (int types);
void service_worker (void *unused);
void service_bootstrap_cb (uev_t *w, void *arg, int events);
int service_completed (void);
#endif /* FINIT_SERVICE_H_ */
+90 -6
View File
@@ -83,6 +83,8 @@
#include "util.h"
#include "utmp-api.h"
extern svc_t *wdog;
/*
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
@@ -96,6 +98,55 @@ static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
static uev_t sigchld_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 unmount_tmpfs(void);
void unmount_regular(void);
@@ -185,8 +236,8 @@ void do_shutdown(shutop_t op)
return;
}
if (wdogpid) {
print(kill(wdogpid, SIGPWR) == 1, "Advising watchdog, system going down");
if (wdog) {
print(kill(wdog->pid, SIGPWR) == 1, "Advising watchdog, system going down");
do_sleep(2);
}
@@ -208,11 +259,11 @@ void do_shutdown(shutop_t op)
/* Reboot via watchdog or kernel, or shutdown? */
if (op == SHUT_REBOOT) {
if (wdogpid) {
if (wdog) {
int timeout = 10;
/* Wait here until the WDT reboots, or timeout with fallback */
print(kill(wdogpid, SIGTERM) == 1, "Pending watchdog reboot");
print(kill(wdog->pid, SIGTERM) == 1, "Pending watchdog reboot");
while (timeout--)
do_sleep(1);
}
@@ -314,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)
{
pid_t pid;
int status;
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
@@ -322,10 +374,10 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
/* Reap all the children! */
do {
pid = waitpid(-1, NULL, WNOHANG);
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0) {
_d("Collected child %d", pid);
service_monitor(pid);
service_monitor(pid, status);
}
} while (pid > 0);
}
@@ -366,6 +418,38 @@ int sig_stopped(void)
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
*/
+3
View File
@@ -70,10 +70,13 @@ extern shutop_t halt;
void do_shutdown (shutop_t op);
int sig_stopped (void);
int sig_num (const char *name);
void sig_init (void);
void sig_unblock (void);
void sig_setup (uev_ctx_t *ctx);
const char *sig_name(int signo);
#endif /* FINIT_SIG_H_ */
/**
+22 -8
View File
@@ -72,6 +72,22 @@ static char *sm_status(sm_state_t state)
}
}
/*
* Disable login in single user mode and shutdown/reboot
*
* Re-enable only when going from these runlevels, this way a user can
* manage /etc/nologin manually within the other runlevels without us
* pulling the rug from under their feet.
*/
static void nologin(void)
{
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
touch("/etc/nologin");
if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6)
erase("/etc/nologin");
}
void sm_set_runlevel(sm_t *sm, int newlevel)
{
sm->newlevel = newlevel;
@@ -101,7 +117,7 @@ restart:
switch (sm->state) {
case SM_BOOTSTRAP_STATE:
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE);
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE);
sm->state = SM_RUNNING_STATE;
break;
@@ -134,13 +150,11 @@ restart:
}
_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);
/* Disable login in single-user mode as well as shutdown/reboot */
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
touch("/etc/nologin");
else
erase("/etc/nologin");
nologin();
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
@@ -149,7 +163,7 @@ restart:
/* Reset once flag of runtasks */
service_runtask_clean();
_d("Stopping services services not allowed in new runlevel ...");
_d("Stopping services not allowed in new runlevel ...");
sm->in_teardown = 1;
service_step_all(SVC_TYPE_ANY);
@@ -171,7 +185,7 @@ restart:
_d("All services have been stoppped, calling runlevel change hooks ...");
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
_d("Starting services services new to this runlevel ...");
_d("Starting services new to this runlevel ...");
sm->in_teardown = 0;
service_step_all(SVC_TYPE_ANY);
@@ -205,7 +219,7 @@ restart:
* Then, mark all affected service conditions as in-flux and
* let all affected services move to WAITING/HALTED
*/
_d("Stopping services services not allowed after reconf ...");
_d("Stopping services not allowed after reconf ...");
sm->in_teardown = 1;
cond_reload();
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
+12 -2
View File
@@ -27,6 +27,7 @@
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
/* B0 means; keep kernel default */
speed_t stty_parse_speed(char *baud)
{
char *ptr;
@@ -66,6 +67,9 @@ speed_t stty_parse_speed(char *baud)
{ 4000000, B4000000 },
};
if (!baud || !baud[0])
return B0;
errno = 0;
val = strtoul(baud, &ptr, 10);
if (errno || ptr == baud)
@@ -95,7 +99,7 @@ void stty(int fd, speed_t speed)
tcflush(fd, TCIOFLUSH);
/* 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 |= CLOCAL;
@@ -104,7 +108,7 @@ void stty(int fd, speed_t speed)
term.c_cc[VMIN] = 1;
term.c_iflag = ICRNL|IXON|IXOFF;
term.c_oflag = OPOST|ONLCR;
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
term.c_cflag |= CS8|CREAD|HUPCL;
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
/* Reset special characters to defaults */
@@ -115,6 +119,12 @@ void stty(int fd, speed_t speed)
term.c_cc[VKILL] = CTRL('U');
term.c_cc[VERASE] = CERASE;
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);
}
/**
+172 -53
View File
@@ -24,6 +24,8 @@
#include <err.h>
#include <ctype.h> /* isdigit() */
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/time.h>
@@ -34,10 +36,39 @@
#include "helpers.h"
#include "pid.h"
#include "util.h"
#include "cond.h"
#include "schedule.h"
/* Each svc_t needs a unique job# */
static int jobcounter = 1;
static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
static void svc_gc(void *arg)
{
struct timespec now;
struct wq *work = (struct wq *)arg;
svc_t *svc, *next;
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
char cond[MAX_COND_LEN];
int msec;
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
(now.tv_nsec - svc->gc.tv_nsec) / 1000000;
if (msec < SVC_TERM_TIMEOUT)
continue;
TAILQ_REMOVE(&gc_list, svc, link);
_d("Cleaning out %s, clearing any conditions ...", svc->name);
cond_clear(mkcond(svc, cond, sizeof(cond)));
free(svc);
}
if (!TAILQ_EMPTY(&gc_list))
schedule_work(work);
}
/**
* svc_new - Create a new service
@@ -48,10 +79,9 @@ static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
* Returns:
* 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;
char *desc;
svc_t *svc, *iter = NULL;
/* Find first job n:o if registering multiple instances */
@@ -70,24 +100,31 @@ svc_t *svc_new(char *cmd, int id, int type)
svc->type = type;
svc->job = job;
svc->id = id;
if (id && id[0])
strlcpy(svc->id, id, sizeof(svc->id));
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
/* Default description, if missing */
desc = strrchr(cmd, '/');
if (desc)
desc++;
else
desc = cmd;
strlcpy(svc->desc, desc, sizeof(svc->desc));
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
/* Default HALT signal to send */
svc->sighalt = SIGTERM;
/* Default delay between SIGTERM and SIGKILL */
svc->killdelay = SVC_TERM_TIMEOUT;
TAILQ_INSERT_TAIL(&svc_list, svc, link);
return svc;
}
static struct wq work = {
.cb = svc_gc,
.delay = SVC_TERM_TIMEOUT
};
/**
* svc_del - Delete a service object
* svc_del - Mark a service object for deletion
* @svc: Pointer to an &svc_t object
*
* Returns:
@@ -96,8 +133,10 @@ svc_t *svc_new(char *cmd, int id, int type)
int svc_del(svc_t *svc)
{
TAILQ_REMOVE(&svc_list, svc, link);
memset(svc, 0, sizeof(*svc));
free(svc);
TAILQ_INSERT_TAIL(&gc_list, svc, link);
clock_gettime(CLOCK_MONOTONIC_COARSE, &svc->gc);
schedule_work(&work);
return 0;
}
@@ -168,7 +207,7 @@ svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
svc_t *svc;
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)))
return svc;
@@ -265,12 +304,12 @@ svc_t *svc_stop_completed(void)
* Returns:
* 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;
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;
}
@@ -304,12 +343,12 @@ svc_t *svc_find_by_pid(pid_t pid)
* Returns:
* 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;
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;
}
@@ -324,19 +363,12 @@ svc_t *svc_find_by_jobid(int job, int id)
* Returns:
* 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;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
ptr = strrchr(svc->cmd, '/');
if (ptr)
ptr++;
else
ptr = svc->cmd;
if (svc->id == id && !strcmp(name, ptr))
if (!strcmp(svc->id, id) && !strcmp(name, svc->name))
return svc;
}
@@ -347,18 +379,49 @@ svc_t *svc_find_by_nameid(char *name, int id)
* svc_find_by_plidfile - Find an service object by its PID file
* @fn: PID file, can be absolute path or relative to /run
*
* This function is primarily used by the pidfile plugin to track the
* conditions and dependencies of services.
*
* Note, the PID file is assumed to exist so the contents can be
* compared to the the PID of a located service. There may be several
* services, in many runlevels, that use the same name for the PID file.
* So we won't know for sure until the PID is compared.
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find_by_pidfile(char *fn)
{
svc_t *svc, *iter = NULL;
char adjpath[strlen(fn) + 10]; /* + sizeof("/var/run/") */
char fnpath[PATH_MAX];
pid_runpath(fn, adjpath, sizeof(adjpath));
if (!realpath(adjpath, fnpath)) {
_pe("adjpath: %s, errno %d", adjpath, errno);
return NULL;
}
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char path[MAX_ARG_LEN];
char path[PATH_MAX];
char *pidfn;
pid_t pid;
if (string_compare(pid_runpath(fn, path, sizeof(path)), pid_file(svc)))
return svc;
pidfn = pid_file(svc);
if (!pidfn || !realpath(pidfn, path)) {
if (errno != ENOENT)
_pe("pidfn: %s, errno %d", pidfn ?: "<NULL>", errno);
continue;
}
if (!string_compare(fnpath, path))
continue;
pid = pid_file_read(path);
if (svc->pid != pid)
continue;
return svc;
}
return NULL;
@@ -379,7 +442,9 @@ void svc_mark_dynamic(void)
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->protected)
if (svc->protect)
continue;
if (svc_is_inetd_conn(svc))
continue;
*((int *)&svc->dirty) = -1;
@@ -471,18 +536,27 @@ int svc_enabled(svc_t *svc)
return 1;
}
/* Same base service, return unique ID */
int svc_next_id(char *cmd)
/*
* Same base service, return unique ID as an integer
* Note: intended for use with INETD services.
*/
int svc_next_id_int(char *cmd)
{
int id = 0;
int n = 1;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->cmd, cmd) && id < svc->id)
id = svc->id;
char id[MAX_ID_LEN];
snprintf(id, sizeof(id), "%d", n);
if (!strcmp(svc->cmd, cmd) && strcmp(svc->id, id))
return n;
n++;
}
return id + 1;
return 1;
}
int svc_is_unique(svc_t *svc)
@@ -506,31 +580,76 @@ int svc_is_unique(svc_t *svc)
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)
*/
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;
char *input, *token, *pos;
input = sanitize(str, len);
if (!input)
return -1;
token = strtok_r(input, " ", &pos);
while (token) {
int id = 1;
_d("Got str:'%s'", str);
input = tokstr(str, len);
while (input) {
char *id = 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])) {
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) {
svc = svc_job_iterator(&iter, 1, job);
if (!svc && not_found)
result += not_found(NULL, job);
result += not_found(NULL, token);
while (svc) {
if (found)
@@ -539,8 +658,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
}
} else {
*ptr++ = 0;
id = atonum(ptr);
job = atonum(token);
id = ptr;
svc = svc_find_by_jobid(job, id);
if (!svc && not_found)
@@ -561,7 +679,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
}
} else {
*ptr++ = 0;
id = atonum(ptr);
id = ptr;
svc = svc_find_by_nameid(token, id);
if (!svc && not_found)
@@ -571,7 +689,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;
+48 -23
View File
@@ -43,10 +43,11 @@ typedef enum {
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
SVC_TYPE_INETD = 8, /* Classic inetd service */
SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
} svc_type_t;
#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 {
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
@@ -66,12 +67,16 @@ typedef enum {
SVC_BLOCK_RESTARTING,
} svc_block_t;
#define MAX_ID_LEN 16
#define MAX_ARG_LEN 64
#define MAX_STR_LEN 64
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
#define MAX_USER_LEN 16
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
#define MAX_NUM_SVC_ARGS 32
#define MAX_NUM_SVC_ARGS 64
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
#define SVC_TERM_TIMEOUT 3000
/*
* Default enable for all services, can be stopped by means
@@ -83,24 +88,30 @@ typedef struct svc {
TAILQ_ENTRY(svc) link;
/* Instance specifics */
int job, id; /* JOB:ID */
int job; /* JOB: */
char id[MAX_ID_LEN]; /* :ID */
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
/* Service details */
pid_t pid;
char pidfile[MAX_ARG_LEN];
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
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() */
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, ... */
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 */
int starting; /* ... waiting for pidfile to be re-asserted */
int runlevels;
int sighup; /* This service supports SIGHUP :) */
svc_block_t block; /* Reason that this service is currently stopped */
char cond[MAX_COND_LEN];
char name[MAX_ARG_LEN];
/* Counters */
char once; /* run/task, (at least) once per runlevel */
@@ -109,11 +120,13 @@ typedef struct svc {
/* For inetd services */
inetd_t inetd;
int stdin_fd;
char iifname[IF_NAMESIZE + 1]; /* Ingress interface for connection */
/* Set for services we need to redirect stdout/stderr to syslog */
struct {
char enabled;
char null;
char console;
char file[64];
char prio[20];
char ident[20];
@@ -134,15 +147,18 @@ typedef struct svc {
*/
uev_t timer;
void (*timer_cb)(struct svc *svc);
/* time at svc_del(), used by gc timer */
struct timespec gc;
} svc_t;
svc_t *svc_new (char *cmd, int id, int type);
svc_t *svc_new (char *cmd, char *id, int type);
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_jobid (int job, int id);
svc_t *svc_find_by_nameid (char *name, int id);
svc_t *svc_find_by_jobid (int job, char *id);
svc_t *svc_find_by_nameid (char *name, char *id);
svc_t *svc_find_by_pidfile (char *fn);
svc_t *svc_iterator (svc_t **iter, int first);
@@ -163,40 +179,45 @@ int svc_clean_bootstrap (svc_t *svc);
void svc_prune_bootstrap (void);
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_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_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_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_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_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
#define svc_start(svc) svc_unblock(svc)
static inline void svc_stop (svc_t *svc) { svc->block = SVC_BLOCK_USER; }
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; }
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; }
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_BUSY; }
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
static inline char *svc_status(svc_t *svc)
{
if (!svc)
return "Unknown";
switch (svc->state) {
case SVC_HALTED_STATE:
switch (svc->block) {
@@ -218,6 +239,7 @@ static inline char *svc_status(svc_t *svc)
case SVC_BLOCK_RESTARTING:
return "restart";
}
return "unknown";
case SVC_DONE_STATE:
return "done";
@@ -249,6 +271,9 @@ static inline char *svc_status(svc_t *svc)
static inline const char *svc_dirtystr(svc_t *svc)
{
if (!svc)
return "Unknown";
if (svc_is_removed(svc))
return "removed";
if (svc_is_updated(svc))
+103 -92
View File
@@ -41,32 +41,16 @@
#ifdef FALLBACK_SHELL
static pid_t fallback = 0;
#endif
static LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER();
static char *canonicalize(char *tty)
{
char buf[42];
struct stat st;
static char path[80];
if (!tty)
return NULL;
/* Auto-detect serial console, for embedded devices mostly */
if (!strcmp(tty, "@console")) {
FILE *fp;
fp = fopen("/sys/class/tty/console/active", "r");
if (!fp) {
_e("Cannot find system console, is sysfs not mounted?");
return NULL;
}
if (fgets(buf, sizeof(buf), fp))
tty = chomp(buf);
fclose(fp);
}
strlcpy(path, tty, sizeof(path));
if (stat(path, &st)) {
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
@@ -88,7 +72,7 @@ static char *canonicalize(char *tty)
void tty_mark(void)
{
tty_node_t *tty;
struct tty *tty;
LIST_FOREACH(tty, &tty_list, link)
tty->dirty = -1;
@@ -96,17 +80,17 @@ void tty_mark(void)
void tty_sweep(void)
{
tty_node_t *tty, *tmp;
struct tty *tty, *tmp;
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
if (!tty->dirty)
continue;
_d("TTY %s dirty, stopping ...", tty->data.name);
tty_stop(&tty->data);
_d("TTY %s dirty, stopping ...", tty->name);
tty_stop(tty);
if (tty->dirty == -1) {
_d("TTY %s removed, cleaning up.", tty->data.name);
_d("TTY %s removed, cleaning up.", tty->name);
tty_unregister(tty);
}
}
@@ -136,12 +120,12 @@ void tty_sweep(void)
*/
int tty_register(char *line, struct rlimit rlimit[], char *file)
{
tty_node_t *entry;
int insert = 0, noclear = 0, nowait = 0, nologin = 0;
struct tty *entry;
size_t i, num = 0;
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS];
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS], buf[256];
char *dev = NULL, *baud = NULL;
char *runlevels = NULL, *term = NULL;
int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0;
if (!line) {
_e("Missing argument");
@@ -169,25 +153,29 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
/* Iterate over all args */
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] == '[')
runlevels = line;
if (!strcmp(args[i], "@console"))
if (!strcmp(args[i], "@console") || !strcmp(args[i], "console"))
dev = args[i];
if (!strncmp(args[i], "/dev", 4))
dev = args[i];
if (!strncmp(args[i], "tty", 3))
dev = args[i];
if (!access(args[i], X_OK))
cmd = strdup(args[i]);
cmd = args[i];
/* The first arg must be one of the above */
continue;
}
/* Built-in getty args */
if (dev) {
if (!cmd && dev) {
if (isdigit(args[i][0])) {
baud = args[i];
continue;
@@ -200,51 +188,63 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
if (i + 1 == num)
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) {
error:
_e("Incomplete or non-existing TTY device given, cannot register.");
if (cmd)
free(cmd);
return errno = EINVAL;
}
/* Auto-detect serial console, for embedded devices mostly */
if (!strcmp(dev, "@console") || !strcmp(dev, "console")) {
FILE *fp;
fp = fopen("/sys/class/tty/console/active", "r");
if (!fp) {
_e("Cannot find system console, is sysfs not mounted?");
return errno = ENOENT;
}
atcon = 1;
if (fgets(buf, sizeof(buf), fp))
dev = strtok(chomp(buf), " \t");
fclose(fp);
}
again:
/* Ensure all getty (built-in + external) are registered with absolute path */
dev = canonicalize(dev);
if (!dev)
goto error;
dev = strdup(dev);
entry = tty_find(dev);
if (!entry) {
insert = 1;
entry = calloc(1, sizeof(*entry));
if (!entry) {
free(dev);
if (cmd)
free(cmd);
if (!entry)
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;
}
entry->data.name = dev;
entry->data.baud = baud ? strdup(baud) : NULL;
entry->data.term = term ? strdup(term) : NULL;
entry->data.noclear = noclear;
entry->data.nowait = nowait;
entry->data.nologin = nologin;
entry->data.runlevels = conf_parse_runlevels(runlevels);
strlcpy(entry->name, dev, sizeof(entry->name));
strlcpy(entry->baud, baud ? baud : "", sizeof(entry->baud));
strlcpy(entry->term, term ? term : "", sizeof(entry->term));
entry->noclear = noclear;
entry->nowait = nowait;
entry->nologin = nologin;
entry->runlevels = conf_parse_runlevels(runlevels);
/* External getty */
if (cmd) {
@@ -255,12 +255,19 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
tok = cmd;
else
tok++;
entry->data.cmd = cmd;
entry->cmd = strdup(cmd);
args[1] = strdup(tok);
for (i = 1; i < num; i++)
entry->data.args[j++] = strdup(args[i]);
entry->data.args[++j] = NULL;
for (i = 1; i < num; i++) {
char *arg = args[i];
/* Replace @console with actual device */
if (arg && (!strcmp(arg, "@console") || !strcmp(arg, "console")))
arg = dev;
if (arg)
entry->args[j++] = strdup(arg);
}
entry->args[++j] = NULL;
}
_d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s",
@@ -270,7 +277,7 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
LIST_INSERT_HEAD(&tty_list, entry, link);
/* Register configured limits */
memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit));
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
if (file && conf_changed(file))
entry->dirty = 1; /* Modified, restart */
@@ -278,10 +285,18 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
entry->dirty = 0; /* Not modified */
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
if (atcon) {
dev = strtok(NULL, " \t");
if (dev) {
_d("Found another @console: %s", dev);
goto again;
}
}
return 0;
}
int tty_unregister(tty_node_t *tty)
int tty_unregister(struct tty *tty)
{
if (!tty) {
_e("Missing argument");
@@ -290,20 +305,14 @@ int tty_unregister(tty_node_t *tty)
LIST_REMOVE(tty, link);
if (tty->data.name)
free(tty->data.name);
if (tty->data.baud)
free(tty->data.baud);
if (tty->data.term)
free(tty->data.term);
if (tty->data.cmd) {
if (tty->cmd) {
int i;
free(tty->data.cmd);
free(tty->cmd);
for (i = 0; i < TTY_MAX_ARGS; i++) {
if (tty->data.args[i])
free(tty->data.args[i]);
tty->data.args[i] = NULL;
if (tty->args[i])
free(tty->args[i]);
tty->args[i] = NULL;
}
}
free(tty);
@@ -311,12 +320,12 @@ int tty_unregister(tty_node_t *tty)
return 0;
}
tty_node_t *tty_find(char *dev)
struct tty *tty_find(char *dev)
{
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) {
if (!strcmp(dev, entry->data.name))
if (!strcmp(dev, entry->name))
return entry;
}
@@ -326,7 +335,7 @@ tty_node_t *tty_find(char *dev)
size_t tty_num(void)
{
size_t num = 0;
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link)
num++;
@@ -337,22 +346,22 @@ size_t tty_num(void)
size_t tty_num_active(void)
{
size_t num = 0;
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) {
if (entry->data.pid)
if (entry->pid)
num++;
}
return num;
}
tty_node_t *tty_find_by_pid(pid_t pid)
struct tty *tty_find_by_pid(pid_t pid)
{
tty_node_t *entry;
struct tty *entry;
LIST_FOREACH(entry, &tty_list, link) {
if (entry->data.pid == pid)
if (entry->pid == pid)
return entry;
}
@@ -375,7 +384,7 @@ static int tty_exist(char *dev)
return result;
}
void tty_start(finit_tty_t *tty)
void tty_start(struct tty *tty)
{
char *dev;
@@ -396,17 +405,19 @@ void tty_start(finit_tty_t *tty)
}
if (tty->nologin) {
_d("%s: Starting /bin/sh ...", dev);
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
return;
}
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : "");
if (!tty->cmd)
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
else
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
}
void tty_stop(finit_tty_t *tty)
void tty_stop(struct tty *tty)
{
if (!tty->pid)
return;
@@ -423,7 +434,7 @@ void tty_stop(finit_tty_t *tty)
tty->pid = 0;
}
int tty_enabled(finit_tty_t *tty)
int tty_enabled(struct tty *tty)
{
if (!tty)
return 0;
@@ -471,12 +482,12 @@ int tty_fallback(pid_t lost)
return 0;
}
static void tty_action(tty_node_t *tty)
static void tty_action(struct tty *tty)
{
if (!tty_enabled(&tty->data))
tty_stop(&tty->data);
if (!tty_enabled(tty))
tty_stop(tty);
else
tty_start(&tty->data);
tty_start(tty);
}
/*
@@ -484,7 +495,7 @@ static void tty_action(tty_node_t *tty)
*/
int tty_respawn(pid_t pid)
{
tty_node_t *tty = tty_find_by_pid(pid);
struct tty *tty = tty_find_by_pid(pid);
if (!tty)
return tty_fallback(pid);
@@ -493,7 +504,7 @@ int tty_respawn(pid_t pid)
utmp_set_dead(pid);
/* Clear PID to be able to respawn it. */
tty->data.pid = 0;
tty->pid = 0;
tty_action(tty);
return 1;
@@ -504,7 +515,7 @@ int tty_respawn(pid_t pid)
*/
void tty_reload(char *dev)
{
tty_node_t *tty;
struct tty *tty;
if (dev) {
tty = tty_find(dev);
@@ -529,7 +540,7 @@ void tty_reload(char *dev)
/* Start all TTYs that exist in the system and are allowed at this runlevel */
void tty_runlevel(void)
{
tty_node_t *tty;
struct tty *tty;
LIST_FOREACH(tty, &tty_list, link)
tty_action(tty);
+15 -21
View File
@@ -32,10 +32,12 @@
#define TTY_MAX_ARGS 16
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
typedef struct {
char *name;
char *baud;
char *term;
struct tty {
LIST_ENTRY(tty) link;
char name[42];
char baud[10];
char term[10];
int noclear;
int nowait;
int nologin;
@@ -48,32 +50,24 @@ typedef struct {
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
} finit_tty_t;
typedef struct tty_node {
LIST_ENTRY(tty_node) link;
finit_tty_t data;
/* XXX: Yes, TTYs should be refactored into a separate SVC type. */
int dirty; /* Set if old mtime != new mtime => reloaded,
* or -1 when marked for removal */
} tty_node_t;
//extern LIST_HEAD(, tty_node) tty_list;
/* Set if modified => reloaded, or -1 when marked for removal */
int dirty;
};
void tty_mark (void);
void tty_sweep (void);
int tty_register (char *line, struct rlimit rlimit[], char *file);
int tty_unregister (tty_node_t *tty);
int tty_unregister (struct tty *tty);
tty_node_t *tty_find (char *dev);
struct tty *tty_find (char *dev);
size_t tty_num (void);
size_t tty_num_active (void);
tty_node_t *tty_find_by_pid (pid_t pid);
void tty_start (finit_tty_t *tty);
void tty_stop (finit_tty_t *tty);
int tty_enabled (finit_tty_t *tty);
struct tty *tty_find_by_pid (pid_t pid);
void tty_start (struct tty *tty);
void tty_stop (struct tty *tty);
int tty_enabled (struct tty *tty);
int tty_respawn (pid_t pid);
void tty_reload (char *dev);
void tty_runlevel (void);
+51 -18
View File
@@ -24,8 +24,11 @@
#include "config.h"
#include <ctype.h> /* isprint() */
#include <errno.h>
#include <regex.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */
@@ -46,6 +49,32 @@ char *progname(char *arg0)
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 mod = 0, bytes;
@@ -77,7 +106,11 @@ int strtobytes(char *arg)
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;
}
/* Allowed characters in job/id/name */
static int isallowed(int ch)
{
return isprint(ch);
}
/* Sanitize user input, make sure to NUL terminate. */
/*
* Verify string argument is NUL terminated
* Verify string is a JOB[:ID], JOB and ID
* can both be string or number, or combo.
*/
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)
i++;
if (strlen(arg) > len)
return NULL;
if (i + 1 < len) {
arg[i + 1] = 0;
return arg;
}
if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED))
return NULL;
if (i > 0 && arg[i] == 0)
return arg;
rc = regexec(&preg, arg, 0, NULL, 0);
regfree(&preg);
if (rc)
return NULL;
return NULL;
return arg;
}
/*
+7
View File
@@ -39,6 +39,7 @@ extern char *prognm;
#include <lite/conio.h>
char *progname (char *arg0);
int echo (char *file, int append, char *fmt, ...);
int strtobytes (char *arg);
void do_sleep (unsigned int sec);
@@ -51,6 +52,12 @@ void screen_init (void);
void screen_exit (void);
int screen_width (void);
static inline char *strterm(char *str, size_t len)
{
str[len - 1] = 0;
return str;
}
#endif /* FINIT_UTIL_H_ */
/**
+37 -31
View File
@@ -1,6 +1,6 @@
/* Built-in watchdog daemon
*
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2016-2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -9,24 +9,30 @@
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
#include "finit.h"
#include "log.h"
#include "watchdog.h"
int running = 1;
@@ -51,6 +57,9 @@ static int init(char *progname, char *devnode)
signal(SIGTERM, sighandler);
signal(SIGPWR, sighandler);
openlog(&progname[1], LOG_CONS | LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "Finit v%s basic watchdogd starting ...", VERSION);
fd = open(devnode, O_WRONLY);
if (fd == -1)
return -1;
@@ -78,6 +87,7 @@ static int loop(int fd, int timeout)
/* External watchdogd wants to take over ... */
if (handover) {
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
return !write(fd, "V", 1);
}
@@ -85,34 +95,30 @@ static int loop(int fd, int timeout)
return 0;
}
int watchdog(char *progname)
int main(int argc, char *argv[])
{
int pid;
int fd, ret;
pid = fork();
if (pid == 0) {
int fd, ret;
fd = init(progname, WDT_DEVNODE);
if (fd == -1) {
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
_exit(1);
}
ret = loop(fd, WDT_TIMEOUT);
while (!handover) {
/* Waiting for SIGTERM ... */
sleep(1);
/* Set lowest possible timeout on SIGTERM */
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
}
close(fd);
_exit(ret);
fd = init(argv[0], WDT_DEVNODE);
if (fd == -1) {
if (ENOENT != errno)
syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
return 1;
}
return pid;
ret = loop(fd, WDT_TIMEOUT);
while (!handover) {
syslog(LOG_ALERT, "System going down ...");
/* Waiting for SIGTERM ... */
sleep(1);
/* Set lowest possible timeout on SIGTERM */
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
}
close(fd);
return ret;
}
/**