Compare commits

...
64 Commits
Author SHA1 Message Date
Joachim Wiberg a61a8015d7 initctl: new command, list installed plugins
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-15 11:00:39 +02:00
Joachim Wiberg e2f070649d Fix wrong path to command in service after reload, may have changed
Service/task/run are today identified by name:id this means the path to
a command is not necessarily the basis for the identity.  Hence, an
svc_t can now be updated with new the correct path to a command.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:08:14 +02:00
Joachim Wiberg f08d375c36 Fix lingering cond in service after reload with no cond
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:08:14 +02:00
Joachim Wiberg 1c4bad5b73 initctl: only show log if PID != 0
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:08:14 +02:00
Joachim Wiberg 6c874895dc Fix #295: add reboot/shutdown/poweroff timeout -t SEC to initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:06:54 +02:00
Joachim Wiberg ef1a05425d Minor, reduce scope
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 08:16:50 +02:00
Joachim Wiberg fc3bad2160 Fix #284: search for plugins in /usr and /usr/local
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-07-17 22:08:05 +02:00
Joachim Wiberg 31ac933d64 Fix #294: shutdown --help mistakenly shuts down system
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-29 08:13:33 +02:00
Joachim Wiberg caa398816d Fix #250: support finit.cond=foo for cmdline <boot/foo> conditions
As a real life example, this can be useful for a mfgtest mode for the
device, wherein the bootloader can be set to finit.cond=test_mode or
finit.cond=user_mode and then specific services can start up in test
mode which wouldn't normally start.

Or even custom firmware update mode such as finit.cond=fw_update_mode
which performs some specific firmware update steps and doesn't launch
normal processes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-26 21:55:50 +02:00
Joachim Wiberg 0d57a507f3 doc: update SVC state machine with latest changes
In fbcd7d2 and 96257cc the state machine transitions were changed to
ensure that pre:scripts only run when conditions have been asserted, and
that manual:yes run/tasks end properly in 'DONE' as their siblilngs.

Also include SVG in repo for reference since that's the best export from
the original at draw.io

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-26 20:43:28 +02:00
Joachim Wiberg 96257cc5c7 Let manual:yes run/tasks also end in state 'done'
This is a follow-up to #274, in which Andy pointed out that ending up in
state 'stopped' was a bit confusing for manual:yes run/tasks.

With the following change we let all run/tasks end in state 'done', yet
still allow manual:yes ones to transition to 'ready' when the user calls
`initctl start foo`.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-25 15:41:34 +02:00
Joachim Wiberg fbcd7d2384 Fix #278: enforce conditions also for running pre: scripts
The whole point of pre: scripts is that they run just before the actual
run/task/service process, hence they need to be locked behind the same
conditions as the process.  Otherwise pre: scripts would be nothing more
than plain tasks.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-25 15:05:16 +02:00
Joachim Wiberg d9dbfa23c7 plugins: minor whitespace and coding style cleanup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-25 13:42:53 +02:00
Joachim Wiberg 91f077bd08 Merge pull request #293 from wkz/modprobe-uevent
Support for coldplugging more devices

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-23 16:55:06 +02:00
Tobias Waldekranz 5fb4a6ae8f plugins: modprobe: Support for coldplugging more devices
As it turns out, not all buses in Linux will add `modalias` attributes
to their devices in sysfs. One notable exception are MDIO buses. The
plugin's scan routine would thus not pick them up.

Fortunately, the information is always available in the device's
`uevent` attribute.

Therefore, default to looking for modaliases in the device's `uevent`
attribute instead, falling back to `modalias` only if `uevent` is not
availble (this should never happen).
2022-06-23 14:23:04 +02:00
Tobias Waldekranz e84333662e plugins: hotplug: Run after modprobe plugin, if available
Since the modprobe plugin (unsurpricingly) may cause new modules to be
loaded, it makes sense to to delay the initial device node scan until
we know that no mode nodes will show up within the next few
microseconds.
2022-06-23 14:19:30 +02:00
Joachim Wiberg ddb42fda70 Fix #289: minor spelling issue in build.md
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-21 22:28:00 +02:00
Joachim Wiberg 621eb21b42 test: regression test for issue #275
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-19 15:43:46 +02:00
Joachim Wiberg 6cc44a87c9 Fix #275: 'initctl status foo' should list all instances
This is a fix to a regression introduced in e51587c.  The idea is to
have 'initctl status foo' show all instances of foo, provided there are
more than one. E.g.

    root@anarchy:~# initctl -t status foo
    0    foo:1        stopped  [--234-----] hej knekt
    0    foo:2        stopped  [--234-----] hej snigel
    0    foo:3        stopped  [--234-----] hej kalas

And not foobar:

    0    foo:1        stopped  [--234-----] hej knekt
    0    foo:2        stopped  [--234-----] hej snigel
    0    foo:3        stopped  [--234-----] hej kalas
    0    foobar       stopped  [--234-----] hej foobar

If you type an exact match you can drill down like so:

    root@anarchy:~# initctl status foo:1
         Status : stopped (code=exited, status=0/SUCCESS, manual=yes)
       Identity : foo:1
    Description : hej knekt
    ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-19 15:39:01 +02:00
Joachim Wiberg 5f908cae49 Fix #274: allow manual:yes on sysv/service/run/task
Prior to this change only service stanzas respected the manual:yes
option, now it (should) work on any svc_t type.  Not tested on ttys.

The initctl tool has been given an extra manual=yes output for these
types of run/task/service entries, shown only when the manual option
is set.

Example:

root@anarchy:~# initctl status foo.sh
     Status : stopped (code=exited, status=0/SUCCESS, manual=yes)
   Identity : foo.sh                                ~~~~~~~~~~~~
Description : Hej foo
     Origin : /etc/finit.d/enabled/foo.conf
Environment :
Condition(s):
    Command : /root/foo.sh
   PID file : none
        PID : 0
       User : root
      Group : root
     Uptime : N/A
     Starts : 1
   Restarts : 0 (0/10)
  Runlevels : [--2345----]

Notice also the new `Starts : 1` which is a counter for the number of
starts in the current runlevel.  On runlevel change it is reset to 0.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-16 17:57:55 +02:00
Joachim Wiberg 6de67ed107 Fix #268: Merge branch 'dbus-fixes'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-15 06:35:30 +02:00
Andy Savage c129f06195 plugins: create missing d-bus directories
- Prefix plugin defines with DBUS_ to allow overriding at build-time
 - Create missing directories for modern d-bus

Signed-off-by: Andy Savage <andy@savage.hk>
2022-06-15 06:33:12 +02:00
Joachim Wiberg d11dc63bc2 Fix #269: add compulsory kernel symlinks in /dev
Add missing symlinks for compulsory and recommended /dev symlinks, as
specified in the Linux Administrator's Guide, section Linux Allocated
devices.

https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-14 21:49:19 +02:00
Joachim Wiberg d904ec1602 tty: add support for 'passenv' flag to /bin/login
This change makes it possible tell the bundled/built-in getty to call
/bin/login with the '-p' flag, to preserve any environment set in the
Finit .conf files.

Issue #286

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-13 21:20:16 +02:00
Joachim Wiberg 2a98b2f055 Minor, fix shadowing of variable in outer scope
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-13 21:18:22 +02:00
Joachim Wiberg 6286ede985 initctl: add optional TYPE argument to 'cond dump' command
Allow filtering of conditions in raw dump, i.e., the possibility to show
only conditions of a certain type.  The TYPE argument is not validated
in any way, so any prefix match is allowed, e.g.

   initctl cond dump service/s

Shows all conditions for services starting with 's'.

Fixes issue #272

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 23:37:03 +02:00
Joachim Wiberg 4a8ca787d3 Minor refactor, reuse MATCH_CMD() from conf.c in service_register()
When adding a new service option recently, I copy-pasted and forgot to
update the length argument to strncasecmp(), so it was time to fix that
once and for all.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 23:13:50 +02:00
Joachim Wiberg 561c88a868 Allow changing restart + oncrash settings of existing services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 22:48:12 +02:00
Joachim Wiberg f8dfcdf024 Fix #279: allow restart:always, restart:-1, of crashing services
This change allows endless restarts, similar to `respawn` but honors
`restart_sec`.  There is no upper limit on the number of times Finit
tries to restart a crashing service.  Same as `restart:-1`

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 22:48:12 +02:00
Joachim Wiberg f50b5a4ac6 Fix #285: initctl restart should start crashed service
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 22:24:27 +02:00
Joachim Wiberg 59525b80e3 Export finit/conf.h and finit/service.h for external plugins
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-06 10:38:19 +02:00
Joachim Wiberg 0ce028add1 Fix #283: too quick timeout at bootstrap of lingering tasks
This seemed odd at first, but it turns out we seem to have adjusted the
time scale for the bootstrap worker, so we were off by ... a factor 10.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-03 03:45:10 +02:00
Joachim Wiberg 75c5c85f07 test: new regression test for initctl restart self
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-30 06:59:14 +02:00
Joachim Wiberg ccb84ced2e Fix #280: allow calling initctl restart foo from within foo
Move the stop+start from initctl to the state machine by allowing
stopped tasks to restart once it has been collected.  This should
prevent finit from blocking, allowing it to handle other requests
while waiting for the service's PID to be collected.

Effectively, this will allow a service to call initctl to restart
itself, as reported in issue #280.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-30 06:54:27 +02:00
Joachim Wiberg 5ef669fb6c test: fix use-after-free by switching to strdupa()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-30 06:53:37 +02:00
Joachim Wiberg 96ff9c115c Follow-up to 9304024: skip . and ..
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-18 07:25:39 +02:00
Joachim Wiberg 83aa6abb1c Fix potential NULL ptr dereference, found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:37:06 +02:00
Joachim Wiberg 9304024841 Check return value of remove() when deleting full condition path
Found by Coverity.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:35:42 +02:00
Joachim Wiberg 0de5981948 initctl: minor, fix invalid format string for print_header()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:33:45 +02:00
Joachim Wiberg c39106906d Check return value from iwatch_init(), found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:28:04 +02:00
Joachim Wiberg 929a6e7aac test: fix minor memory leak in test daemon, found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:26:48 +02:00
Joachim Wiberg ceb4d04d23 Fix potential array overrun, found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:22:36 +02:00
Joachim Wiberg f483a8aeaa Update ChangeLog and bump version for v4.3 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 21:57:32 +02:00
Joachim Wiberg e3e220ce8e Fix links and update with info on new/existing features
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 21:55:38 +02:00
Joachim Wiberg fc653e4293 man: document initctl cond get
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 20:45:00 +02:00
Joachim Wiberg 94d55cb7dd initctl: allow 'cond get' read any type of condition
Fixes #265

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 11:52:02 +02:00
Joachim Wiberg 0e84ecf143 Minor refactor
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 11:44:24 +02:00
Joachim Wiberg 0e93dfd640 initctl: use standardized exit codes for all fatal errors
This change diversifies the exit codes used by initctl on fatal errors.
Previously only Std C codes 0 and 1 were used, with the initctl specific
255 for some cases.

Both the LSB init script codes and BSD sysexits.h codes are used.  See
util.c exitcodes[] for the full list.  The idea is to complement the
change in 9a76f08, issue #266, so that initctl can be used silently for
scripting purposes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 11:32:06 +02:00
Joachim Wiberg 4dff8237f1 initctl: fix fallback handling in log command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 11:01:58 +02:00
Joachim Wiberg 35400f2a8b Minor, drop unused header file
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 10:36:47 +02:00
Joachim Wiberg 9a76f0862c initctl: let -q silence all commands
This patch introduces ERR(), ERRX(), WARN(), and WARNX() as replacements
to their lower case counterparts.  These log macros respect the 'quiet'
flag to initctl, leaving only the return code as indicator to user.

Fixes #266

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 10:34:36 +02:00
Joachim Wiberg fb8a7caa99 initctl: simplify error output of start/stop/restart/signal
No need for the verbose output on error, simply state the cause of the
problem.  The user can issue `initctl -h` to check the syntax of the
command.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 10:30:49 +02:00
Joachim Wiberg f8bed5c7af Minor, fix local shadowing of scope global variable
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 09:38:05 +02:00
Joachim Wiberg da6fa482e7 Follow-up to 195155a, drop unused variable from refactor
... and it's obviously spelled "unlimited", *sigh*

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 09:38:05 +02:00
Joachim Wiberg fdf8e48c99 Minor, reduce scope of local variable
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-15 09:21:48 +02:00
Joachim Wiberg 195155a7f5 initctl: make log command unlimented
Drop `| tail -10` from 'log foo' command.  Let user call tail themselves
and only limit with tail on status command.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-13 13:53:38 +02:00
Joachim Wiberg 6934ba4dbc initctl: improve log digger in 'status foo' command
Grep in /var/log/syslog, or messages, for both the service identity (not
just svc->cmd) and also try to find [PID] for more relevant information.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-13 13:38:26 +02:00
Joachim Wiberg fdbd25c1b5 Use svc_ident() for more cases
- Drop basename() of svc->cmd from remaining code
  - Replace uses of svc->cmd with svc_ident(svc)
  - Use svc_ident() as default log tag

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-13 13:36:33 +02:00
Joachim Wiberg 467f1cca81 initctl: options alphabetically in usage text
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-10 08:11:37 +02:00
Joachim Wiberg d6fdf4a806 README: update initctl usage text
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-10 08:11:37 +02:00
Joachim Wiberg 4be9d97e61 README: make sure to mention auto-disable of cgroups
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-10 08:11:37 +02:00
Joachim Wiberg 021d011493 man: mention the UNIX pipe at least once in every man page
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-10 08:11:37 +02:00
Joachim Wiberg e988d38f71 initctl: minor, fix indentation in usage text
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-10 08:06:13 +02:00
Joachim Wiberg 8690fc732b Fix #263: document confusing initctl reload foo command
What a ride, apparently we support restarting run/tasks in the same
runlevel they have already started once in.  Still unsure if adding
support for reloading a particular Finit .conf file for a service
should be in the context of this command (it isn't now).  Maybe it
is better left as a future expansion when we switch to the new .conf
format?

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-10 08:03:36 +02:00
48 changed files with 1057 additions and 356 deletions
+16 -2
View File
@@ -4,7 +4,7 @@ Change Log
All relevant changes are documented in this file.
[4.3][UNRELEASED]
[4.3][] - 2022-05-15
--------------------
Critical bug fix release. If you run a 32-bit target with GLIBC 2.34
@@ -63,6 +63,14 @@ you *need* to upgrade!
* Document secret service option `respawn`, which bypasses the crash
semantics, allowing endless restarts
* Document secret `HOOK_BANNER`, the first hook point before the banner
* Document slightly confusing `initctl reload foo` command. It does
*not* reload the service's `.conf` file! Issue #263
* Log changes; all instances where previously the `basename cmd` of a
service was used to identify the service, now the proper `name:id` is
used instead. Meaning, a service without a custom `:ID` or `name:`
will display the same as before, but with any of those customizations
the name and name:id will now be shown. **Note:** this may affect any
log scrapers out there!
* New plugin: `hook-scripts`, allows [run-parts(8)][] style scripts to
run on any hook point. Contributed by Tobias Waldekranz
* `initctl` (`reboot`) falls back to `-f` when it detects it is in
@@ -75,6 +83,12 @@ you *need* to upgrade!
in lexicographic order and UNIX backup files (`foo.conf~`) are skipped
* The `name:id` tuple is now more consistently used in all log and debug
messages instead of the basename of the command
* Simplify error output of `initctl start/stop/restart/signal`, no more
extra usage help, just a plain error message
* Exit codes of `initctl` have changed to use LSB script standard and BSD
sysexits.h exit codes. As before, a non-zero exit is error or missing
* Add support for `initctl -q` to more commands: stop, start, restart,
reload, signal, etc.
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
@@ -93,7 +107,7 @@ you *need* to upgrade!
* Fix #233: initctl shows wrong status for run/task, by Sergio Morlans
and Ming Liu, Atlas Copco
* Fix #248: source `env:file` also in `pre:` and `post` scripts
* Fix #260: drop limit on device name in `Checking filesystem`...` output
* Fix #260: drop limit on device name in `Checking filesystem...` output
* Fix start/stop and monitoring (restart) of SysV init scripts and
forking services, see the updated documentation for details
* Fix call to `swapoff` at shutdown, does not support `-e` flag
+38 -12
View File
@@ -45,6 +45,10 @@ Features include:
* Cgroups v2, both configuration and monitoring in `initctl top`
* Plugin support for customization
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
* Integration with [watchdogd][] for full system supervision
* Logging to kernel ring buffer before `syslogd` has started, see the
recommended [sysklogd][] project for complete logging integration
and how to log to the kernel ring buffer from scripts using `logger`
Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the
@@ -52,12 +56,12 @@ on server and desktop systems as well. For working examples, see the
* [Void Linux](contrib/void/),
* [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/)
* [Debian GNU/Linux](contrib/debian/), also works on Ubuntu/Linux Mint
> **Note:** support for various Linux distributions does not mean Finit
> installs easily on all architectures. The bundled install scripts are
> examples for standard installations, tested on x86_64 systems. Custom
> setups, targeted at embedded systems, can be found in [myLinux][].
> examples for standard installations, tested on amd64 (x86_64) systems.
> Custom setups, for embedded systems, can be found in [myLinux][].
Example
@@ -73,7 +77,11 @@ the [initctl](#commands--status) tool.
host default
# Runlevel to start after bootstrap, 'S', default: 2
runlevel 2
#runlevel 2
# Support for setting global environment variables, using foo=bar syntax
# be careful though with variables like PATH, SHELL, LOGNAME, etc.
#PATH=/usr/bin:/bin:/usr/sbin:/sbin
# Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
@@ -102,6 +110,11 @@ service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
#task [S] /etc/init.d/kbd -- Preparing console
# Hidden from boot progress, using empty `--` description
#sysv [S] /etc/init.d/keyboard-setup --
#sysv [S] /etc/init.d/acpid --
#task [S] /etc/init.d/kbd --
# Run start scripts from this directory
# runparts /etc/start.d
@@ -268,8 +281,8 @@ unnecessary overhead, which can be removed at build-time using:
**Cgroups**
Finit supports cgroups v2 and comes with the following default groups
in which services and user sessions are placed in:
Finit supports cgroups v2 and comes with the following default groups in
which services and user sessions are placed in:
/sys/fs/cgroup
|-- init/ # cpu.weight:100
@@ -305,6 +318,10 @@ The `initctl` tool has three commands to help debug and optimize the
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
commands for details.
> **Note:** systems that do not support cgroups, specifically version 2,
> are automatically detected. On such systems the above functionality
> is disabled early at boot.
Runparts & /etc/rc.local
------------------------
@@ -414,24 +431,24 @@ services, called `initctl`. Unlike `telinit` the `initctl` tool does
not return until the given command has fully completed.
```
alpine:~# initctl help
Usage: initctl [OPTIONS] [COMMAND]
Options:
-b, --batch Batch mode, no screen size probing
-c, --create Create missing paths (and files) as needed
-f, --force Ignore missing files and arguments, never prompt
-h, --help This help text
-1, --once Only one lap in commands like 'top'
-p, --plain Use plain table headings, no ctrl chars
-q, --quiet Silent, only return status of command
-t, --no-heading Skip table headings
-v, --verbose Verbose output
-h, --help This help text
-V, --version Show program version
Commands:
debug Toggle Finit (daemon) debug
help This help text
version Show Finit version
version Show program version
ls | list List all .conf in /etc/finit.d
create <CONF> Create .conf in /etc/finit.d/available
@@ -444,15 +461,20 @@ Commands:
reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined condition +usr/COND
cond get <COND> Get status of user-defined condition, see $? and -v
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
cond status Show condition status, default cond command
cond dump Dump all conditions and their status
cond dump [TYPE] Dump all, or a type of, conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog
start <NAME>[:ID] Start service by name, with optional ID
stop <NAME>[:ID] Stop/Pause a running service by name
reload <NAME>[:ID] Reload service by name (SIGHUP or restart)
reload <NAME>[:ID] Reload service as if .conf changed (SIGHUP or restart)
This allows restart of run/tasks that have already run
Note: Finit .conf file(s) are *not* reloaded!
restart <NAME>[:ID] Restart (stop/start) service by name
signal <NAME>[:ID] <S> Send signal S to service by name, with optional ID
ident [NAME] Show matching identities for NAME, or all
status <NAME>[:ID] Show service status, by name
status Show status of services, default command
@@ -465,6 +487,8 @@ Commands:
halt Halt system
poweroff Halt and power off system
suspend Suspend system
utmp show Raw dump of UTMP/WTMP db
```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
@@ -603,7 +627,9 @@ and proposed extensions.
[Joachim Wiberg]: https://troglobit.com
[License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[GitHub]: https://github.com/troglobit/myLinux/
[myLinux]: https://github.com/troglobit/myLinux/
[watchdogd]: https://github.com/troglobit/watchdogd/
[sysklogd]: https://github.com/troglobit/sysklogd/
[GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
[Coverity Scan]: https://scan.coverity.com/projects/3545
+9 -1
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.3-rc2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/projects/finit/)
AC_INIT(Finit, 4.3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/projects/finit/)
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes])
@@ -151,6 +151,9 @@ AC_ARG_WITH(hook-scripts-path,
AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]),
[hook_scripts_path=$withval], [hook_scripts_path=yes])
AC_ARG_WITH(plugin-path,
AS_HELP_STRING([--with-plugin-path=DIR], [Search path for external plugins, default /usr/lib/finit/plugins:/usr/local/lib/finit/plugins]),
[plugin_path=$withval], [plugin_path=yes])
### Enable features ###########################################################################
@@ -251,6 +254,11 @@ AS_IF([test "x$enable_hook_scripts_plugin" = "xyes" -a "x$with_hook_scripts_path
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
AS_IF([test "x$with_plugin_path" != "xno"], [
AS_IF([test "x$plugin_path" = "xyes"], [plugin_path=/usr/lib/finit/plugins:/usr/local/lib/finit/plugins])
AC_EXPAND_DIR(plugin_path, "$plugin_path")
AC_DEFINE_UNQUOTED(EXTERNAL_PLUGIN_PATH, "$plugin_path", [Built-in default: /usr/lib/finit/plugins:/usr/local/lib/finit/plugins])])
# Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
+1 -1
View File
@@ -65,7 +65,7 @@ Below are a few of the main switches to configure:
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
* `--with-sulgin`: Enable bundled `sulogin` program. Default is to use the
* `--with-sulogin`: Enable bundled `sulogin` program. Default is to use the
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
login if the `root` user is disabled or has no password at all.
+11
View File
@@ -12,6 +12,17 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
* `debug`: Enable kernel debug. Debug messages are printed to the
console until Finit starts up, unless `loglevel=7` (below) is used.
* `finit.cond=foo[,bar[,baz]]`: set `<boot/foo>` condition, optionally
multiple conditions can be set using the same option, separated with a
comma. Alternatively, multiple `foo.cond=arg` can be given. Each will
result in a `<boot/arg>` condition being set to control the rest of the
system bootstrap.
Very useful for selecting different boot modes, e.g. manufacturing test,
firmware upgrade, or rescue mode.
> Note: `<boot/...>` conditions cannot be cleared with `initctl`!
* `finit.debug[=bool]`: Enable finit debug. This is operated
independently of the kernel `debug` setting. New as of Finit v4.
+11 -5
View File
@@ -46,11 +46,16 @@ Triggering
----------
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static
or one-shot conditions. They live in the `<usr/...` namespace and are
constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
- `netlink.so`: provides `<net/...>`
- `pidfile.so`: provides `<pid/...>`
- Cmdline `finit.cond=arg`: provides `<boot/arg`
See below for built-in conditions. Finit also supports user-defined
conditions, sometimes referred to as static or one-shot conditions.
They live in the `<usr/...` namespace and are constrained to a flat
hierarchy without any sub-directories, unlike the pidfile plugin, for
instance.
User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands:
@@ -118,6 +123,7 @@ Built-in conditions:
- `sys/pwr/fail`
- `sys/key/ctrlaltdel`
- `usr/foo`
- `boot/arg`
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
+2
View File
@@ -450,6 +450,8 @@ configurable with the following options:
a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used
* `restart:always` -- no upper limit on the number of times Finit
tries to restart a crashing service. Same as `restart:-1`
* `norestart` -- dont restart on failures, same as `restart:0`
* `respawn` -- bypasses the `restart` mechanism completely, allows
endless restarts. Useful in many use-cases, but not what `service`
Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 180 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 103 KiB

+4
View File
@@ -304,6 +304,10 @@ number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used
.It Cm restart:always
no upper limit on the number of times Finit tries to restart a crashing
service. Same as
.Cm restart:-1
.It Cm norestart
dont restart on failures, same as
.Cm restart:0
+50 -6
View File
@@ -24,9 +24,11 @@ This program follows the usual UNIX command line syntax, with long
options starting with two dashes (`-'). The options are as follows:
.Bl -tag -width Ds
.It Fl b, -batch
Batch mode, no screen size probing.
Batch mode, no screen size probing. Useful for scripting.
.It Fl c, -create
Create missing paths (and files) as needed. Useful with the edit command.
Create missing paths (and files) as needed. Useful with the
.Cm edit
command.
.It Fl f, -force
Ignore missing files and arguments, never prompt.
.It Fl h, -help
@@ -64,7 +66,16 @@ files in
Create
.Cm .conf
in
.Pa /etc/finit.d/available
.Pa /etc/finit.d/available .
.Pp
Piping the contents into
.Nm
works as one would expect:
.Bd -literal -offset indent
echo "task name:foo /tmp/foo.sh -- Foo logger" | initctl create foo
initctl enable foo
initctl reload
.Ed
.It Nm Ar delete Ar CONF
Delete
.Cm .conf
@@ -113,6 +124,20 @@ i.e., activates changes
.It Nm Ar cond set Ar COND
Set (assert) user-defined condition,
.Cm +usr/COND
.It Nm Ar cond get Ar COND
Get (quietly) the status of any condition. Defaults to user-defined
condions, but if a slash is detected, e.g.,
.Cm pid/foo
or
.Cm net/eth0/exist ,
then any condition can be read.
.Pp
The command is geared for scripting, check the exit code to get the
status of the condition, or give
.Nm
the
.Fl v
option for a more verbose output.
.It Nm Ar cond clr | clear Ar COND
Clear (deassert) user-defined condition,
.Cm -usr/COND
@@ -120,8 +145,8 @@ Clear (deassert) user-defined condition,
Show condition status, default
.Cm cond
command.
.It Nm Ar cond dump
Dump all conditions and their status
.It Nm Ar cond dump Op Cm TYPE
Dump all, or a given type of, conditions and their status
.It Nm Ar ident Op Cm NAME
Display indentities of all run/task/services, or only instances
matching
@@ -139,7 +164,24 @@ Start service by name, with optional ID, e.g.,
.It Nm Ar stop Cm NAME[:ID]
Stop/Pause a running service by name
.It Nm Ar reload Cm NAME[:ID]
Reload service by name (SIGHUP or restart)
Reload service as if its configuration had changed. Internally,
.Nm finit
marks the named service as "dirty" and turns the state machine,
resulting in a SIGHUP or restart of (at least) the named service.
Any dependant services are also restarted.
.Pp
.Sy Note:
no .conf file is reloaded with this variant of the command. Essentially
it is the same as calling
.Cm restart .
Except for two things:
.Bl -enum -offset indent -compact
.It
if the service supports HUP, it's signaled instead of stop-started, and
.It
it allows restarting run/task in the same runlevel -- which is otherwise
restricted
.El
.It Nm Ar signal Cm NAME:[ID] S
Send signal S to service, by name.
.Cm S
@@ -160,6 +202,8 @@ List cgroup config overview
List processes based on cgroups
.It Nm Ar top
Show top-like listing based on cgroups
.It Nm Ar plugins
List installed plugins
.It Nm Ar runlevel Op Ar 0-9
Show or set runlevel: 0 halt, 6 reboot
.It Nm Ar reboot
+42
View File
@@ -116,6 +116,45 @@ static void ln(const char *target, const char *linkpath)
_pe("Failed creating %s -> %s symlink", target, linkpath);
}
/* Kernel defines the following compulsory and recommended links
* https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links
*/
static void kernel_links(void)
{
struct {
char *tgt, *lnk;
int optional;
} k[] = {
{ "/proc/self/fd", "/dev/fd", 0 },
{ "fd/0", "/dev/stdin", 0 },
{ "fd/1", "/dev/stdout", 0 },
{ "fd/2", "/dev/stderr", 0 },
{ "socksys", "/dev/nfsd", 0 },
{ "null", "/dev/X0R", 0 },
{ "/proc/kcore", "/dev/core", 1 },
{ "ram0", "/dev/ramdisk", 1 },
{ "qft0", "/dev/ftape", 1 },
{ "video0", "/dev/bttv0", 1 },
{ "radio0", "/dev/radio", 1 },
};
char *fn, buf[80];
size_t i;
for (i = 0; i < NELEMS(k); i++) {
fn = k[i].tgt;
if (k[i].optional) {
if (fn[0] != '/') {
snprintf(buf, sizeof(buf), "/dev/%s", k[i].tgt);
fn = buf;
}
if (!fexist(fn))
continue;
}
ln(k[i].tgt, k[i].lnk);
}
}
/*
* Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP
*/
@@ -206,6 +245,9 @@ static void setup(void *arg)
if (whichp("restorecon"))
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
/* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */
kernel_links();
umask(prev);
}
+23 -13
View File
@@ -35,16 +35,20 @@
#include "service.h"
#include "conf.h"
#define DAEMON "dbus-daemon"
#define ARGS "--nofork --system --syslog-only"
#define DESC "D-Bus message bus daemon"
#define DBUS_DAEMON "dbus-daemon"
#define DBUS_ARGS "--nofork --system --syslog-only"
#define DBUS_DESC "D-Bus message bus daemon"
#ifndef DAEMONUSER
#define DAEMONUSER "messagebus"
#ifndef DBUS_DAEMONUSER
#define DBUS_DAEMONUSER "messagebus"
#endif
#ifndef DAEMONPIDFILE
#define DAEMONPIDFILE "/var/run/dbus/pid"
#ifndef DBUS_DAEMONGROUP
#define DBUS_DAEMONGROUP "messagebus"
#endif
#ifndef DBUS_DAEMONPIDFILE
#define DBUS_DAEMONPIDFILE "/var/run/dbus/pid"
#endif
static void setup(void *arg)
@@ -58,26 +62,32 @@ static void setup(void *arg)
return;
}
cmd = which(DAEMON);
cmd = which(DBUS_DAEMON);
if (!cmd) {
_d("Skipping plugin, %s is not installed.", DAEMON);
_d("Skipping plugin, %s is not installed.", DBUS_DAEMON);
return;
}
prev =umask(0);
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
_d("Creating D-Bus Required Directories ...");
mksubsys("/var/run/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
mksubsys("/var/run/lock/subsys", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
mksubsys("/var/lib/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
mksubsys("/tmp/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
/* Generate machine id for dbus */
if (whichp("dbus-uuidgen"))
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
/* Clean up from any previous pre-bootstrap run */
remove(DAEMONPIDFILE);
remove(DBUS_DAEMONPIDFILE);
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
DAEMONPIDFILE, DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
DBUS_DAEMONPIDFILE, DBUS_DAEMONUSER, DBUS_DAEMONGROUP, cmd, DBUS_ARGS, DBUS_DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON);
_pe("Failed registering %s", DBUS_DAEMON);
free(cmd);
umask(prev);
+1 -1
View File
@@ -95,7 +95,7 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc" },
.depends = { "bootmisc", "modprobe" },
};
PLUGIN_INIT(plugin_init)
+70 -10
View File
@@ -106,7 +106,7 @@ static void alias_remove(struct module *m)
TAILQ_REMOVE(&modules, m, link);
free(m->alias);
free(m);
}
}
static int alias_exist(char *alias)
{
@@ -120,27 +120,87 @@ static int alias_exist(char *alias)
return 0;
}
static int scan_alias(const char *path, const struct stat *st, int flag, struct FTW *unused)
static void alias_add_uniq(char *alias)
{
if (alias_exist(alias))
return;
alias_add(alias);
}
static FILE *maybe_fopen_alias(const char *file, const char *path)
{
const char *basename;
basename = rindex(path, '/');
if (!basename)
return NULL;
basename++;
if (strcmp(file, basename))
return NULL;
return fopen(path, "r");
}
static int scan_modalias(const char *path)
{
FILE *fp;
char buf[256];
FILE *fp;
if (fnmatch("*/modalias", path, 0))
return 0;
fp = fopen(path, "r");
fp = maybe_fopen_alias("modalias", path);
if (!fp)
return 0;
return 1;
if (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
if (!alias_exist(buf))
alias_add(buf);
alias_add_uniq(buf);
}
fclose(fp);
return 0;
}
static int scan_uevent(const char *path)
{
char buf[256];
FILE *fp;
fp = maybe_fopen_alias("uevent", path);
if (!fp)
return 1;
while (fgets(buf, sizeof(buf), fp)) {
if (strstr(buf, "MODALIAS=") != buf)
continue;
chomp(buf);
alias_add_uniq(buf + strlen("MODALIAS="));
break;
}
fclose(fp);
return 0;
}
static int scan_alias(const char *path, const struct stat *st,
int flag, struct FTW *unused)
{
/* Sanity check: is this a regular file we can read? */
if (!(S_ISREG(st->st_mode) && (st->st_mode & S_IRUSR)))
return 0;
if (!scan_uevent(path))
return 0;
scan_modalias(path);
return 0;
}
static void coldplug(void *arg)
{
struct module *m, *tmp;
+2 -2
View File
@@ -102,8 +102,8 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
service_forked(svc);
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
_d("Forking service %s (cmd %s) changed PID from %d to %d",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid);
svc->pid = pid;
/* Complement log in service.c for non-forking services */
+2 -1
View File
@@ -64,7 +64,8 @@ if LOGROTATE
finit_SOURCES += logrotate.c
endif
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.h \
plugin.h svc.h service.h
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
+55 -6
View File
@@ -45,8 +45,9 @@
#include "log.h"
#include "plugin.h"
#include "private.h"
#include "sig.h"
#include "schedule.h"
#include "service.h"
#include "sig.h"
#include "util.h"
extern svc_t *wdog;
@@ -89,8 +90,10 @@ static int restart(svc_t *svc, void *user_data)
if (!svc)
return 1;
svc_mark_dirty(svc);
svc_start(svc);
if (!svc_is_running(svc))
return start(svc, user_data);
service_stop(svc);
service_step(svc);
return 0;
@@ -225,10 +228,44 @@ static svc_t *do_find_byc(char *buf, size_t len)
return NULL;
}
static int do_reboot(int cmd, char *buf, size_t len)
static void bypass_shutdown(void *);
struct wq emergency = { .cb = bypass_shutdown };
static void bypass_shutdown(void *unused)
{
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
do_shutdown(halt);
}
static int do_reboot(int cmd, int timeout, char *buf, size_t len)
{
int rc = 1;
switch (cmd) {
case INIT_CMD_REBOOT:
halt = SHUT_REBOOT;
break;
case INIT_CMD_HALT:
halt = SHUT_HALT;
break;
case INIT_CMD_POWEROFF:
halt = SHUT_OFF;
break;
case INIT_CMD_SUSPEND:
break;
default:
return 255;
}
if (timeout > 0) {
emergency.delay = timeout * 1000;
schedule_work(&emergency);
}
switch (cmd) {
case INIT_CMD_REBOOT:
_d("reboot");
@@ -261,7 +298,7 @@ static int do_reboot(int cmd, char *buf, size_t len)
break;
default:
rc = 255;
/* Handled previously */
break;
}
@@ -319,6 +356,10 @@ static void api_cb(uev_t *w, void *arg, int events)
if (EAGAIN == errno)
break;
/* we get here when client restarts itself */
if (ECONNRESET == errno)
break;
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
}
@@ -388,6 +429,14 @@ static void api_cb(uev_t *w, void *arg, int events)
result = do_reload(rq.data, sizeof(rq.data));
break;
case INIT_CMD_GET_PLUGINS:
result = plugin_list(rq.data, sizeof(rq.data));
break;
case INIT_CMD_PLUGIN_DEPS:
result = plugin_deps(rq.data, sizeof(rq.data));
break;
case INIT_CMD_GET_RUNLEVEL:
_d("get runlevel");
rq.runlevel = runlevel;
@@ -398,7 +447,7 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_HALT:
case INIT_CMD_POWEROFF:
case INIT_CMD_SUSPEND:
result = do_reboot(rq.cmd, rq.data, sizeof(rq.data));
result = do_reboot(rq.cmd, rq.sleeptime, rq.data, sizeof(rq.data));
break;
case INIT_CMD_ACK:
+3
View File
@@ -511,6 +511,9 @@ void cgroup_init(uev_ctx_t *ctx)
/* prepare cgroup.events watcher */
fd = iwatch_init(&iw_cgroup);
if (fd == -1)
goto abort;
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
_pe("Failed setting up cgroup.events watcher");
iwatch_exit(&iw_cgroup);
+9 -8
View File
@@ -23,7 +23,6 @@
#include "config.h"
#include <err.h>
#include <dirent.h>
#include <stdio.h>
#include <signal.h>
@@ -244,7 +243,7 @@ static float cgroup_cpuload(struct cg *cg)
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
fp = fopen(fn, "r");
if (!fp)
err(1, "Cannot open %s", fn);
ERR(72, "Cannot open %s", fn);
while (fgets(buf, sizeof(buf), fp)) {
uint64_t curr;
@@ -280,13 +279,13 @@ static struct cg *append(char *path)
if (access(fn, F_OK)) {
/* older kernels, 4.19, don't have summary cpu.stat in root */
if (strcmp(path, FINIT_CGPATH))
warn("not a cgroup path with cpu controller, %s", path);
WARN("not a cgroup path with cpu controller, %s", path);
return NULL;
}
cg = calloc(1, sizeof(struct cg));
if (!cg)
err(1, "failed allocating struct cg");
ERR(71, "failed allocating struct cg");
cg->cg_path = strdup(path);
if (list)
@@ -296,7 +295,7 @@ static struct cg *append(char *path)
item.key = cg->cg_path;
item.data = cg;
if (!hsearch(item, ENTER))
err(1, "failed adding to hash table");
ERR(70, "failed adding to hash table");
return cg;
}
@@ -434,7 +433,6 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf))) {
char proc[ttcols];
int len;
switch (mode) {
case 1:
@@ -456,6 +454,8 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
if (plain) {
strlcat(row, proc, rplen);
} else {
int len;
strlcat(row, CDIM, rlen);
strlcat(row, proc, rlen);
@@ -600,7 +600,6 @@ int show_cgtop(char *arg)
uev_t timer, input, sigint, sigterm, sigquit;
char path[512];
uev_ctx_t ctx;
int flags;
if (!arg)
arg = FINIT_CGPATH;
@@ -610,7 +609,7 @@ int show_cgtop(char *arg)
}
if (!hcreate(ttrows + 25))
err(1, "failed creating hash table");
ERR(70, "failed creating hash table");
sysinfo(&si);
total_ram = si.totalram * si.mem_unit;
@@ -619,6 +618,8 @@ int show_cgtop(char *arg)
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
if (!ionce && !plain) {
int flags;
atexit(cleanup);
ttraw();
hidecursor();
+14 -19
View File
@@ -21,13 +21,13 @@
* THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "client.h"
#include "initctl.h"
static int sd = -1;
@@ -40,13 +40,13 @@ int client_connect(void)
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (-1 == sd) {
warn("Failed creating UNIX domain socket");
WARN("Failed creating UNIX domain socket");
return -1;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
if (errno != ENOENT)
warnx("Failed connecting to finit");
WARNX("Failed connecting to finit");
close(sd);
return -1;
}
@@ -72,22 +72,21 @@ int client_disconnect(void)
int client_send(struct init_request *rq, ssize_t len)
{
struct pollfd pfd = { 0 };
int sd, result = 255;
int result = 255;
int rc;
sd = client_connect();
if (-1 == sd)
if (client_connect() == -1)
return -1;
pfd.fd = sd;
pfd.events = POLLOUT;
if (poll(&pfd, 1, 2000) <= 0) {
warn("Timed out waiting for Finit, errno %d", errno);
WARN("Timed out waiting for Finit, errno %d", errno);
goto exit;
}
if (write(sd, rq, len) != len) {
warn("Failed communicating with Finit, errno %d", errno);
WARN("Failed communicating with Finit, errno %d", errno);
goto exit;
}
@@ -97,14 +96,14 @@ int client_send(struct init_request *rq, ssize_t len)
if (rc) {
if (errno == EINTR) /* shutdown/reboot */
goto exit;
warn("poll(), errno %d", errno);
WARN("poll(), errno %d", errno);
} else
warnx("Timed out waiting for reply from Finit.");
WARNX("Timed out waiting for reply from Finit.");
goto exit;
}
if (read(sd, rq, len) != len) {
warn("Failed reading reply from Finit, errno %d", errno);
WARN("Failed reading reply from Finit, errno %d", errno);
goto exit;
}
@@ -120,15 +119,13 @@ exit:
svc_t *client_svc_iterator(int first)
{
int sd = -1;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_ITER,
};
static svc_t svc;
sd = client_connect();
if (sd == -1)
if (client_connect() == -1)
return NULL;
if (first)
@@ -147,7 +144,7 @@ svc_t *client_svc_iterator(int first)
return &svc;
error:
perror("Failed communicating with finit");
WARN("Failed communicating with finit, error %d", errno);
client_disconnect();
sd = -1;
@@ -161,10 +158,8 @@ svc_t *do_cmd(int cmd, const char *arg)
.cmd = cmd,
};
static svc_t svc;
int sd = -1;
sd = client_connect();
if (sd == -1)
if (client_connect() == -1)
return NULL;
strlcpy(rq.data, arg, sizeof(rq.data));
@@ -180,7 +175,7 @@ svc_t *do_cmd(int cmd, const char *arg)
return &svc;
error:
client_disconnect();
perror("Failed communicating with finit");
WARN("Failed communicating with finit, error %d", errno);
return NULL;
}
+70 -2
View File
@@ -36,6 +36,67 @@
#include "pid.h"
#include "service.h"
struct cond_boot {
TAILQ_ENTRY(cond_boot) link;
char *name;
};
static TAILQ_HEAD(, cond_boot) cond_boot_list = TAILQ_HEAD_INITIALIZER(cond_boot_list);
/*
* Parse finit.cond=cond[,cond[,...]] from command line. It creates a
* temporary linked list of conds which are freed when finit reaches
* cond_init(), which calls cond_boot_strap() to realize the conditions
* in the file system as regular static conditions. Policy, however,
* dictates that these are read-only and should not be possible to
* manage from initctl.
*/
void cond_boot_parse(char *arg)
{
struct cond_boot *node;
if (!arg)
return;
node = malloc(sizeof(*node));
if (!node) {
_pe("Out of memory cannot track boot conditions");
return;
}
node->name = strdup(arg);
TAILQ_INSERT_HEAD(&cond_boot_list, node, link);
}
/*
* Convert all conditions read from the kernel command line (above) into
* proper finit conditions.
*/
static void cond_boot_strap(void)
{
struct cond_boot *node, *tmp;
TAILQ_FOREACH_SAFE(node, &cond_boot_list, link, tmp) {
TAILQ_REMOVE(&cond_boot_list, node, link);
if (node->name) {
char *ptr;
ptr = strtok(node->name, ",");
while (ptr) {
char cond[strlen(ptr) + 6];
snprintf(cond, sizeof(cond), "boot/%s", ptr);
cond_set_oneshot_noupdate(cond);
ptr = strtok(NULL, ",");
}
free(node->name);
}
free(node);
}
}
/*
* The service condition name is constructed from the 'pid/' prefix and
* the unique NAME:ID tuple that identify each process in Finit. Here
@@ -145,11 +206,17 @@ static void cond_delpath(const char *path)
return;
while ((d = readdir(dir))) {
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
paste(fn, sizeof(fn), path, d->d_name);
remove(fn);
if (remove(fn))
_pe("Failed removing condition path, file %s", fn);
}
closedir(dir);
unlink(path);
if (unlink(path) && errno != EISDIR)
_pe("Failed removing condition path %s", fn);
}
int cond_set_path(const char *path, enum cond_state next)
@@ -360,6 +427,7 @@ void cond_init(void)
}
cond_bump_reconf();
cond_boot_strap();
}
/**
+4 -3
View File
@@ -31,6 +31,7 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names);
void cond_boot_parse (char *arg);
int cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name);
@@ -38,9 +39,9 @@ void cond_set_oneshot (const char *name);
void cond_clear (const char *name);
void cond_reload (void);
int cond_set_noupdate (const char *name);
int cond_set_oneshot_noupdate(const char *name);
int cond_clear_noupdate(const char *name);
int cond_set_noupdate(const char *name);
int cond_set_oneshot_noupdate(const char *name);
int cond_clear_noupdate(const char *name);
void cond_reassert (const char *pat);
void cond_deassert (const char *pat);
+10 -5
View File
@@ -48,8 +48,6 @@
#include "util.h"
#define BOOTSTRAP (runlevel == 0)
#define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5;
@@ -115,6 +113,11 @@ static void parse_finit_opts(char *opt)
return;
}
if (string_compare(opt, "cond")) {
cond_boot_parse(arg);
return;
}
if (string_compare(opt, "status_style")) {
if (!arg) {
_e("status_style option requires an argument, skipping.");
@@ -439,8 +442,10 @@ void conf_parse_cond(svc_t *svc, char *cond)
if (svc_is_daemon(svc))
svc->sighup = 1;
if (!cond)
if (!cond) {
memset(svc->cond, 0, sizeof(svc->cond));
return;
}
/* First character must be '!' if SIGHUP is not supported. */
ptr = cond;
@@ -454,12 +459,12 @@ void conf_parse_cond(svc_t *svc, char *cond)
ptr[i] = 0;
if (i >= sizeof(svc->cond)) {
logit(LOG_WARNING, "Too long event list in declaration of %s: %s", svc->cmd, ptr);
logit(LOG_WARNING, "Too long event list in declaration of %s: %s", svc_ident(svc, NULL, 0), ptr);
return;
}
if (!strncmp(ptr, "svc/", 4)) {
logit(LOG_ERR, "Unsupported cond syntax for %s: <%s", svc->cmd, ptr);
logit(LOG_ERR, "Unsupported cond syntax for %s: <%s", svc_ident(svc, NULL, 0), ptr);
return;
}
+2 -2
View File
@@ -329,7 +329,7 @@ static int activate_console(int noclear, int nowait)
static const char clr[] = "\r\e[K";
static const char cup[] = "\e[A";
struct termios orig;
char c;
char ch;
int rc;
if (nowait || rescue)
@@ -351,7 +351,7 @@ static int activate_console(int noclear, int nowait)
dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, msg, strlen(msg));
while ((rc = read(STDIN_FILENO, &c, 1)) > 0 && c != '\r' && c != '\n')
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
continue;
/* On any error (likely EINTR), we avoid starting getty */
+4 -1
View File
@@ -122,6 +122,9 @@ static int sulogin(int do_reboot)
for (i = 0; i < NELEMS(cmd); i++) {
char *path = which(cmd[i]);
if (!path)
continue;
if (access(path, X_OK)) {
free(path);
continue;
@@ -574,7 +577,7 @@ static void bootstrap_worker(void *work)
.cb = finalize,
.delay = 10
};
static int cnt = 120;
static int cnt = 120 * 10; /* We run with 100ms period */
int level = cfglevel;
_d("Step all services ...");
+2 -2
View File
@@ -83,8 +83,8 @@
#define INIT_CMD_STOP_SVC 11
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
#define INIT_CMD_GET_PLUGINS 14 /* Fill data[] with loaded plugins */
#define INIT_CMD_PLUGIN_DEPS 15 /* Fill data[] with plugin deps */
#define INIT_CMD_GET_RUNLEVEL 16
#define INIT_CMD_REBOOT 20
#define INIT_CMD_HALT 21
+9 -2
View File
@@ -49,6 +49,7 @@
#endif
static long logname_len = 32; /* useradd(1) limit at 32 chars */
static int passenv = 0; /* Set /bin/login -p or not */
/*
* Read one character from stdin.
@@ -185,7 +186,10 @@ static int do_login(char *name)
{
struct stat st;
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
if (passenv)
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL);
else
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/*
* Failed to exec login, should not happen on normal systems.
@@ -252,11 +256,14 @@ int main(int argc, char *argv[])
char *tty, *speed = "0", *term = NULL;
int c;
while ((c = getopt(argc, argv, "h?")) != EOF) {
while ((c = getopt(argc, argv, "h?p")) != EOF) {
switch(c) {
case 'h':
case '?':
return usage(0);
case 'p':
passenv = 1;
break;
default:
return usage(1);
}
+180 -121
View File
@@ -23,7 +23,6 @@
#include "config.h"
#include <err.h>
#include <ftw.h>
#include <ctype.h>
#include <getopt.h>
@@ -39,6 +38,7 @@
# include <lite/lite.h>
#endif
#include "initctl.h"
#include "client.h"
#include "cond.h"
#include "serv.h"
@@ -64,6 +64,7 @@ int verbose = 0;
int plain = 0;
int quiet = 0;
int runlevel = 0;
int timeout = 0;
int cgrp = 0;
int utmp = 0;
@@ -154,12 +155,21 @@ static int toggle_debug(char *arg)
return client_send(&rq, sizeof(rq));
}
static int do_log(char *svc)
static int do_log(svc_t *svc, char *tail)
{
char *logfile = "/var/log/syslog";
const char *logfile = "/var/log/syslog";
pid_t pid;
char *nm;
if (!svc || !svc[0])
svc = "finit";
if (svc) {
nm = svc_ident(svc, NULL, 0);
pid = svc->pid;
if (!pid)
return 0; /* not running */
} else {
nm = "finit";
pid = 1;
}
if (!fexist(logfile)) {
logfile = "/var/log/messages";
@@ -167,7 +177,20 @@ static int do_log(char *svc)
return 0; /* bail out, maybe in container */
}
return systemf("cat %s | grep %s | tail -10", logfile, svc);
return systemf("cat %s | grep '\\[%d\\]\\|%s' %s", logfile, pid, nm, tail);
}
static int show_log(char *arg)
{
svc_t *svc = NULL;
if (arg) {
svc = client_svc_find(arg);
if (!svc)
ERRX(69, "no such task or service(s): %s", arg);
}
return do_log(svc, "");
}
static int do_runlevel(char *arg)
@@ -229,20 +252,11 @@ static int do_startstop(int cmd, char *arg)
};
if (!arg || !arg[0])
errx(1, "missing argument to %s", (cmd == INIT_CMD_START_SVC
? "start"
: (cmd == INIT_CMD_STOP_SVC
? "stop"
: "restart")));
ERRX(2, "missing command argument");
strlcpy(rq.data, arg, sizeof(rq.data));
if (client_send(&rq, sizeof(rq))) {
fprintf(stderr, "No such task or service(s): %s\n\n", arg);
fprintf(stderr, "Usage: initctl %s <NAME>[:ID]\n",
cmd == INIT_CMD_START_SVC ? "start" :
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
return 1;
}
if (client_send(&rq, sizeof(rq)))
ERRX(69, "no such task or service(s): %s", arg);
return do_svc(cmd, arg);
}
@@ -260,23 +274,10 @@ static int do_reload (char *arg)
static int do_restart(char *arg)
{
size_t retries = 3;
svc_t *svc;
if (do_startstop(INIT_CMD_RESTART_SVC, arg))
ERRX(7, "failed restarting %s", arg);
if (do_startstop(INIT_CMD_STOP_SVC, arg))
return 1;
while (retries-- > 0 && (svc = client_svc_find(arg))) {
if (!svc_is_running(svc))
break;
sleep(1);
}
if (retries == 0)
errx(1, "Failed stopping %s (restart)", arg);
return do_startstop(INIT_CMD_RESTART_SVC, arg);
return 0;
}
/**
@@ -298,27 +299,19 @@ int do_signal(int argc, char *argv[])
int signo;
if (argc != 2)
errx(1, "invalid number of arguments to signal");
ERRX(2, "invalid number of arguments to signal");
/* Validate service name */
strlcpy(rq.data, argv[0], sizeof(rq.data));
if (client_send(&rq, sizeof(rq))) {
fprintf(stderr, "No such task or service(s): %s\n\n", argv[0]);
goto show_usage;
}
if (client_send(&rq, sizeof(rq)))
ERRX(69, "no such task or service(s): %s", argv[0]);
/* Validate signal name (or number) */
signo = str2sig(argv[1]);
if (signo == -1) {
/* Not a signal name, is it an actual signal number? */
errno = 0;
signo = (int)strtol(argv[1], NULL, 10);
const char *errstr = NULL;
/* Was it a number? Was it a signo that finit recognizes? */
if (errno || !*(sig2str(signo))) {
fprintf(stderr, "Not a valid signal (or signo): %s\n", argv[1]);
goto show_usage;
}
signo = (int)strtonum(argv[1], 1, 31, &errstr);
if (errstr)
ERRX(65, "%s signal: %s", errstr, argv[1]);
}
/* Reuse runlevel for signal number. */
@@ -328,12 +321,9 @@ int do_signal(int argc, char *argv[])
strlcpy(rq.data, argv[0], sizeof(rq.data));
return client_send(&rq, sizeof(rq));
show_usage:
fprintf(stderr, "Usage: initctl signal <NAME>[:ID] <S>\n");
return 1;
}
char *dump_filter;
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{
const char *cond, *asserted;
@@ -348,6 +338,10 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
asserted = condstr(cond_get_path(fpath));
cond = &fpath[strlen(_PATH_COND)];
if (dump_filter && dump_filter[0] && strncmp(cond, dump_filter, strlen(dump_filter)))
return 0;
if (strncmp("pid/", cond, 4) == 0) {
svc_t *svc;
@@ -375,65 +369,68 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
static int do_cond_dump(char *arg)
{
col_widths();
if (heading) {
char title[80];
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
iw, "IDENT", "STATUS", "CONDITION");
print_header(title);
}
if (heading)
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
"STATUS", "CONDITION");
dump_filter = arg;
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
warnx("Failed parsing %s", _PATH_COND);
WARNX("Failed parsing %s", _PATH_COND);
return 1;
}
return 0;
}
static int do_cond_act(char *arg, int op)
typedef enum { COND_CLR, COND_SET, COND_GET } condop_t;
static int do_cond_act(char *arg, condop_t op)
{
char oneshot[256];
char path[256];
size_t off;
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
arg += strlen(COND_USR);
if (!arg || !arg[0])
errx(1, "Invalid condition (empty)");
if (strchr(arg, '/'))
errx(1, "Invalid condition (slashes)");
if (strchr(arg, '.'))
errx(1, "Invalid condition (periods)");
ERRX(2, "Invalid condition (empty)");
snprintf(oneshot, sizeof(oneshot), _PATH_CONDUSR "%s", arg);
/* allowed to read any condition, but not set/clr */
if (op != COND_GET) {
if (strchr(arg, '/'))
ERRX(2, "Invalid condition (slashes)");
if (strchr(arg, '.'))
ERRX(2, "Invalid condition (periods)");
}
if (strchr(arg, '/'))
snprintf(path, sizeof(path), _PATH_COND "%s", arg);
else
snprintf(path, sizeof(path), _PATH_CONDUSR "%s", arg);
off = strlen(_PATH_COND);
switch (op) {
case 2:
off = !fexist(oneshot);
case COND_GET:
off = !fexist(path);
if (verbose)
puts(off ? "off" : "on");
return off;
case 1:
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
err(1, "Failed asserting condition <%s>", &oneshot[off]);
case COND_SET:
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
ERR(73, "Failed asserting condition <%s>", &path[off]);
break;
case 0:
if (erase(oneshot) && errno != ENOENT)
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
break;
default:
errx(1, "Unsupported operation (%d)", op);
case COND_CLR:
if (erase(path) && errno != ENOENT)
ERR(73, "Failed deasserting condition <%s>", &path[off]);
break;
}
return 0;
}
static int do_cond_get(char *arg) { return do_cond_act(arg, 2); }
static int do_cond_set(char *arg) { return do_cond_act(arg, 1); }
static int do_cond_clr(char *arg) { return do_cond_act(arg, 0); }
static int do_cond_get(char *arg) { return do_cond_act(arg, COND_GET); }
static int do_cond_set(char *arg) { return do_cond_act(arg, COND_SET); }
static int do_cond_clr(char *arg) { return do_cond_act(arg, COND_CLR); }
static char *svc_cond(svc_t *svc, char *buf, size_t len)
{
@@ -492,13 +489,9 @@ static int do_cond_show(char *arg)
svc_t *svc;
col_widths();
if (heading) {
char title[80];
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
iw, "IDENT", "STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
print_header(title);
}
if (heading)
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
"STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
if (!svc->cond[0])
@@ -523,8 +516,9 @@ static int do_cond_show(char *arg)
static int do_cmd(int cmd)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = cmd,
.magic = INIT_MAGIC,
.cmd = cmd,
.sleeptime = timeout,
};
if (client_send(&rq, sizeof(rq))) {
@@ -611,6 +605,44 @@ static int show_version(char *arg)
return 0;
}
static int plugins_list(char *arg)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_GET_PLUGINS,
};
char buf[sizeof(rq.data)];
char *ptr;
int rc;
rc = client_send(&rq, sizeof(rq));
if (rc) {
if (rc == 255)
return 69;
else
ERRX(69, "No such command");
}
memcpy(buf, rq.data, sizeof(buf));
if (heading)
print_header("%-18s %s", "PLUGIN", "DEPENDENCIES");
ptr = strtok(buf, " ");
while (ptr) {
rq.cmd = INIT_CMD_PLUGIN_DEPS;
strlcpy(rq.data, ptr, sizeof(rq.data));
rc = client_send(&rq, sizeof(rq));
if (rc)
printf("%s\n", ptr);
else
printf("%-18s %s\n", ptr, rq.data);
ptr = strtok(NULL, " ");
}
return 0;
}
/**
* runlevel_string - Convert a bit encoded runlevel to .conf syntax
* @levels: Bit encoded runlevels
@@ -702,16 +734,17 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len)
return buf;
}
static char *exit_status(int status, char *buf, size_t len)
static char *exit_status(svc_t *svc, char *buf, size_t len)
{
int rc, sig;
rc = WEXITSTATUS(status);
sig = WTERMSIG(status);
rc = WEXITSTATUS(svc->status);
sig = WTERMSIG(svc->status);
if (WIFEXITED(status))
snprintf(buf, len, " (code=exited, status=%d%s)", rc, code2str(rc));
else if (WIFSIGNALED(status))
if (WIFEXITED(svc->status))
snprintf(buf, len, " (code=exited, status=%d%s%s)", rc, code2str(rc),
svc->manual ? ", manual=yes" : "");
else if (WIFSIGNALED(svc->status))
snprintf(buf, len, " (code=signal, status=%d%s)", sig, sig2str(sig));
return buf;
@@ -727,7 +760,7 @@ static char *status(svc_t *svc, int full)
s = svc_status(svc);
switch (svc->state) {
case SVC_HALTED_STATE:
exit_status(svc->status, ok, sizeof(ok));
exit_status(svc, ok, sizeof(ok));
color = "\e[1m";
break;
@@ -736,7 +769,7 @@ static char *status(svc_t *svc, int full)
break;
case SVC_DONE_STATE:
exit_status(svc->status, ok, sizeof(ok));
exit_status(svc, ok, sizeof(ok));
if (WIFEXITED(svc->status)) {
if (WEXITSTATUS(svc->status))
color = "\e[1;31m";
@@ -751,7 +784,7 @@ static char *status(svc_t *svc, int full)
break;
default:
exit_status(svc->status, ok, sizeof(ok));
exit_status(svc, ok, sizeof(ok));
color = "\e[1;33m";
break;
}
@@ -783,6 +816,28 @@ static void show_cgroup_tree(char *group, char *pfx)
cgroup_tree(path, pfx, 0, 0);
}
/*
* arg: 'foo' should match foo:1 foo:2, etc. but not foobar
* arg: 'foo:1' should only match foo:1
* arg: 'foo:' is allowed to fail, unsupported syntax atm
* arg: 'foo:*' is allowed to fail, unsupported syntax atm
*/
static int svc_compare(svc_t *svc, char *arg)
{
char ident[MAX_IDENT_LEN];
char *ptr;
svc_ident(svc, ident, sizeof(ident));
ptr = strchr(ident, ':');
if (ptr && !strchr(arg, ':'))
*ptr = 0;
if (!strcmp(ident, arg))
return 1;
return 0;
}
static int show_status(char *arg)
{
char ident[MAX_IDENT_LEN];
@@ -796,22 +851,16 @@ static int show_status(char *arg)
long now = jiffies();
char uptm[42] = "N/A";
char *pidfn = NULL;
int exact = 0;
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
svc_ident(svc, ident, sizeof(ident));
if (string_compare(ident, arg))
num++;
if (string_case_compare(ident, arg))
exact++;
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0))
num += svc_compare(svc, arg);
if (num > 1 && !exact)
if (num > 1)
break;
svc = client_svc_find(arg);
if (!svc)
return 255;
ERRX(69, "no such task or service(s): %s", arg);
if (quiet) {
if (svc_is_runtask(svc))
@@ -841,6 +890,8 @@ static int show_status(char *arg)
printf(" User : %s\n", svc->username);
printf(" Group : %s\n", svc->group);
printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
if (svc->manual)
printf(" Starts : %d\n", svc->once);
printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
if (cgrp && svc->pid > 1) {
@@ -864,7 +915,7 @@ static int show_status(char *arg)
no_cgroup:
printf("\n");
return do_log(svc->cmd);
return do_log(svc, "| tail -10");
}
col_widths();
@@ -878,14 +929,14 @@ static int show_status(char *arg)
else
strlcat(title, "COMMAND", sizeof(title));
print_header(title);
print_header("%s", title);
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char *lvls;
svc_ident(svc, ident, sizeof(ident));
if (num && !string_compare(ident, arg))
if (num && !svc_compare(svc, arg))
continue;
printf("%-*d ", pw, svc->pid);
@@ -974,13 +1025,13 @@ static int usage(int rc)
" -b, --batch Batch mode, no screen size probing\n"
" -c, --create Create missing paths (and files) as needed\n"
" -f, --force Ignore missing files and arguments, never prompt\n"
" -h, --help This help text\n"
" -1, --once Only one lap in commands like 'top'\n"
" -p, --plain Use plain table headings, no ctrl chars\n"
" -q, --quiet Silent, only return status of command\n"
" -t, --no-heading Skip table headings\n"
" -v, --verbose Verbose output\n"
" -V, --version Show program version\n"
" -h, --help This help text\n"
"\n"
"Commands:\n"
" debug Toggle Finit (daemon) debug\n"
@@ -1009,24 +1060,25 @@ static int usage(int rc)
" disable <CONF> Disable .conf in %s\n", ena);
if (has_rcsd)
fprintf(stderr,
" reload Reload *.conf in " FINIT_RCSD " (activate changes)\n");
" reload Reload *.conf in " FINIT_RCSD " (activate changes)\n");
else
fprintf(stderr,
" reload Reload " FINIT_CONF " (activate changes)\n");
fprintf(stderr,
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
"\n"
" cond set <COND> Set (assert) user-defined condition +usr/COND\n"
" cond get <COND> Get status of user-defined condition, see $? and -v\n"
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
" cond status Show condition status, default cond command\n"
" cond dump Dump all conditions and their status\n"
" cond dump [TYPE] Dump all, or a type of, conditions and their status\n"
"\n"
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
" start <NAME>[:ID] Start service by name, with optional ID\n"
" stop <NAME>[:ID] Stop/Pause a running service by name\n"
" reload <NAME>[:ID] Reload service by name (SIGHUP or restart)\n"
" reload <NAME>[:ID] Reload service as if .conf changed (SIGHUP or restart)\n"
" This allows restart of run/tasks that have already run\n"
" Note: Finit .conf file(s) are *not* reloaded!\n"
" restart <NAME>[:ID] Restart (stop/start) service by name\n"
" signal <NAME>[:ID] <S> Send signal S to service by name, with optional ID\n"
" ident [NAME] Show matching identities for NAME, or all\n"
@@ -1039,6 +1091,8 @@ static int usage(int rc)
" ps List processes based on cgroups\n"
" top Show top-like listing based on cgroups\n");
fprintf(stderr,
"\n"
" plugins List installed plugins\n"
"\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n"
@@ -1108,7 +1162,7 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
}
if (argv[0] && strlen(argv[0]) > 0)
errx(1, "No such command. See 'initctl help' for an overview of available commands.");
ERRX(3, "No such command. See 'initctl help' for an overview of available commands.");
return command[0].cb(NULL); /* default cmd */
}
@@ -1160,7 +1214,7 @@ int main(int argc, char *argv[])
{ "cond", cond, NULL, NULL, NULL },
{ "log", NULL, do_log, NULL, NULL },
{ "log", NULL, show_log, NULL, NULL },
{ "start", NULL, do_start, NULL, NULL },
{ "stop", NULL, do_stop, NULL, NULL },
{ "restart", NULL, do_restart, NULL, NULL },
@@ -1170,6 +1224,8 @@ int main(int argc, char *argv[])
{ "ps", NULL, show_cgps, &cgrp, NULL },
{ "top", NULL, show_cgtop, &cgrp, NULL },
{ "plugins", NULL, plugins_list, NULL, NULL },
{ "runlevel", NULL, do_runlevel, NULL, NULL },
{ "reboot", NULL, do_reboot, NULL, NULL },
{ "halt", NULL, do_halt, NULL, NULL },
@@ -1245,6 +1301,9 @@ void logit(int prio, const char *fmt, ...)
{
va_list ap;
if (quiet)
return;
va_start(ap, fmt);
if (prio <= LOG_ERR)
verrx(1, fmt, ap);
+8
View File
@@ -24,6 +24,8 @@
#ifndef FINIT_INITCTL_H_
#define FINIT_INITCTL_H_
#include <err.h>
#include "finit.h"
#include "util.h"
@@ -33,6 +35,12 @@ extern int ionce; /* initctl -1 */
extern int heading; /* initctl -t */
extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */
extern int quiet; /* initctl -q */
#define ERR(rc, fmt, args...) do { if (!quiet) err(rc, fmt, ##args); else exit(rc); } while (0)
#define ERRX(rc, fmt, args...) do { if (!quiet) errx(rc, fmt, ##args); else exit(rc); } while (0)
#define WARN(fmt, args...) do { if (!quiet) warn(fmt, ##args); } while (0)
#define WARNX(fmt, args...) do { if (!quiet) warnx(fmt, ##args); } while (0)
extern void print_header(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+1 -1
View File
@@ -49,7 +49,6 @@ static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
*/
int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
@@ -60,6 +59,7 @@ int logrotate(char *file, int num, off_t sz)
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
int cnt;
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
+62 -6
View File
@@ -260,8 +260,8 @@ int plugin_io_init(plugin_t *p)
/* Setup any I/O callbacks for plugins that use them */
static int init_plugins(uev_ctx_t *ctx)
{
int fail = 0;
plugin_t *p, *tmp;
int fail = 0;
PLUGIN_ITERATOR(p, tmp) {
if (plugin_io_init(p))
@@ -302,7 +302,7 @@ static int load_one(char *path, char *name)
noext = strcmp(name + strlen(name) - 3, ".so");
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
_d("Loading plugin %s ...", basename(sofile));
_d("Loading plugin %s ...", sofile);
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
if (!handle) {
_e("Failed loading plugin %s: %s", sofile, dlerror());
@@ -356,6 +356,8 @@ static int load_plugins(char *path)
dp = opendir(path);
if (!dp) {
if (errno == ENOENT)
return 0;
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 1;
}
@@ -381,14 +383,68 @@ static int load_plugins(char *path)
}
#endif /* ENABLE_STATIC */
int plugin_list(char *buf, size_t len)
{
#ifndef ENABLE_STATIC
plugin_t *p, *tmp;
buf[0] = 0;
PLUGIN_ITERATOR(p, tmp) {
if (buf[0])
strlcat(buf, " ", len);
strlcat(buf, p->name, len);
}
#else
buf[0] = 0;
#endif
return 0;
}
int plugin_deps(char *buf, size_t len)
{
#ifndef ENABLE_STATIC
plugin_t *p;
p = plugin_find(buf);
buf[0] = 0;
if (p) {
int i;
for (i = 0; i < PLUGIN_DEP_MAX; i++) {
if (!p->depends[i])
continue;
if (buf[0])
strlcat(buf, " ", len);
strlcat(buf, p->depends[i], len);
}
}
#else
buf[0] = 0;
#endif
return 0;
}
int plugin_init(uev_ctx_t *ctx)
{
int fail = 1;
load_plugins(PLUGIN_PATH);
if (!load_plugins(PLUGIN_PATH))
fail = init_plugins(ctx);
#ifdef EXTERNAL_PLUGIN_PATH
char *paths = strdup(EXTERNAL_PLUGIN_PATH);
char *path;
return fail;
if (paths) {
_d("Loading external plugins from %s ...", paths);
path = strtok(paths, ":");
while (path) {
load_plugins(path);
path = strtok(NULL, ":");
}
free(paths);
}
#endif
return init_plugins(ctx);
}
void plugin_exit(void)
+3
View File
@@ -146,6 +146,9 @@ typedef struct plugin {
/* Public plugin API */
int plugin_io_init (plugin_t *plugin);
int plugin_list (char *buf, size_t len);
int plugin_deps (char *buf, size_t len);
int plugin_register (plugin_t *plugin);
int plugin_unregister (plugin_t *plugin);
+3
View File
@@ -29,6 +29,9 @@
#include "plugin.h"
#define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */
#define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
extern int service_interval;
int api_init (uev_ctx_t *ctx);
+21 -9
View File
@@ -43,6 +43,8 @@ static cmd_t cmd = CMD_UNKNOWN;
static char *msg = NULL;
/* initctl API */
extern int timeout;
extern int do_reboot (char *arg);
extern int do_halt (char *arg);
extern int do_poweroff(char *arg);
@@ -103,12 +105,13 @@ static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
"Options:\n"
" --help This help text\n"
" -f, --force Force unsafe %s now, do not contact the init system.\n"
" --halt Halt system, regardless of how the command is called.\n"
" -h Halt or power off after shutdown.\n"
" -P, --poweroff Power-off system, regardless of how the command is called.\n"
" -r, --reboot Reboot system, regardless of how the command is called.\n"
" -?, --help This help text\n"
" -f, --force Force unsafe %s now, do not contact the init system.\n"
" --halt Halt system, regardless of how the command is called.\n"
" -h Halt or power off after shutdown.\n"
" -P, --poweroff Power-off system, regardless of how the command is called.\n"
" -r, --reboot Reboot system, regardless of how the command is called.\n"
" -t, --timeout=SEC Force reboot/shutdown after a given timeout\n"
"\n", prognm, msg);
return rc;
@@ -116,20 +119,22 @@ static int usage(int rc)
int reboot_main(int argc, char *argv[])
{
int c, force = 0;
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"help", 0, NULL, '?'},
{"force", 0, NULL, 'f'},
{"halt", 0, NULL, 'H'},
{"poweroff", 0, NULL, 'p'},
{"reboot", 0, NULL, 'r'},
{"timeout", 1, NULL, 't'},
{NULL, 0, NULL, 0}
};
int c, force = 0;
int tmp;
/* Initial command taken from program name */
transform(prognm);
while ((c = getopt_long(argc, argv, "h?fHPpr", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "h?fHPprt:", long_options, NULL)) != EOF) {
switch(c) {
case '?':
return usage(0);
@@ -152,6 +157,13 @@ int reboot_main(int argc, char *argv[])
cmd = CMD_REBOOT;
break;
case 't':
tmp = atoi(optarg);
if (tmp < 1)
errx(1, "Invalid forced timeout value.");
timeout = tmp;
break;
default:
return usage(1);
}
+28 -29
View File
@@ -23,7 +23,6 @@
#include "config.h"
#include <err.h>
#include <fcntl.h> /* Definition of AT_* constants */
#include <glob.h>
#include <stdio.h>
@@ -233,7 +232,7 @@ int serv_enable(char *arg)
int ena;
if (!arg || !arg[0]) {
warnx("missing argument to enable, may be one of:");
WARNX("missing argument to enable, may be one of:");
return serv_list("available");
}
@@ -243,18 +242,18 @@ int serv_enable(char *arg)
}
if (chdir(FINIT_RCSD))
err(1, "failed cd %s", FINIT_RCSD);
ERR(72, "failed cd %s", FINIT_RCSD);
if (icreate && mkdir("enabled", 0755) && EEXIST != errno)
err(1, "failed creating %s/enabled directory", FINIT_RCSD);
ERR(73, "failed creating %s/enabled directory", FINIT_RCSD);
ena = !chdir("enabled"); /* System *may* have enabled/ dir. */
snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg);
if (!fexist(path))
errx(1, "cannot find %s", conf(path, sizeof(path), arg, 0));
ERRX(72, "cannot find %s", conf(path, sizeof(path), arg, 0));
if (fexist(arg))
errx(1, "%s already enabled", arg);
ERRX(1, "%s already enabled", arg);
return symlink(path, arg) != 0;
}
@@ -265,7 +264,7 @@ int do_disable(char *arg, int check)
char corr[40];
if (!arg || !arg[0]) {
warnx("missing argument to disable, may be one of:");
WARNX("missing argument to disable, may be one of:");
return serv_list("enabled");
}
@@ -275,15 +274,15 @@ int do_disable(char *arg, int check)
}
if (chdir(FINIT_RCSD))
err(1, "failed cd %s", FINIT_RCSD);
ERR(72, "failed cd %s", FINIT_RCSD);
if (chdir("enabled")) /* System *may* have enabled/ dir. */
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
if (check && stat(arg, &st))
errx(1, "%s not (an) enabled (service).", arg);
ERRX(6, "%s not (an) enabled (service).", arg);
if (check && (st.st_mode & S_IFMT) == S_IFLNK)
errx(1, "cannot disable %s, not a symlink.", arg);
ERRX(1, "cannot disable %s, not a symlink.", arg);
return remove(arg) != 0;
}
@@ -299,16 +298,16 @@ int serv_touch(char *arg)
char *fn;
if (!arg || !arg[0]) {
warnx("missing argument to touch, may be one of:");
WARNX("missing argument to touch, may be one of:");
return serv_list("enabled");
}
fn = conf(path, sizeof(path), arg, 0);
if (!fexist(fn)) {
if (!strstr(arg, "finit.conf"))
errx(1, "%s not available.", arg);
ERRX(72, "%s not available.", arg);
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
ERRX(4, "%s is a built-in service.", arg);
strlcpy(path, FINIT_CONF, sizeof(path));
fn = path;
@@ -316,7 +315,7 @@ int serv_touch(char *arg)
/* libite:touch() follows symlinks */
if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW))
err(1, "failed marking %s for reload", fn);
ERR(71, "failed marking %s for reload", fn);
return 0;
}
@@ -329,9 +328,9 @@ int serv_show(char *arg)
fn = conf(path, sizeof(path), arg, 0);
if (!fexist(fn)) {
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
ERRX(4, "%s is a built-in service.", arg);
warnx("Cannot find %s", arg);
WARNX("Cannot find %s", arg);
return 1;
}
@@ -366,10 +365,10 @@ static int do_edit(char *arg, int creat)
fn = conf(path, sizeof(path), arg, creat);
if (!fexist(fn)) {
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
ERRX(4, "%s is a built-in service.", arg);
if (!creat) {
warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
WARNX("Cannot find %s, use -c flag, create command, or select one of:", arg);
return serv_list(NULL);
}
@@ -378,7 +377,7 @@ static int do_edit(char *arg, int creat)
copyfile(SAMPLE_CONF, fn, 0, 0);
#endif
} else if (creat)
warnx("the file %s already exists, falling back to edit.", fn);
WARNX("the file %s already exists, falling back to edit.", fn);
for (size_t i = 0; i < NELEMS(editor); i++) {
if (systemf("%s %s 2>/dev/null", editor[i], path))
@@ -407,10 +406,10 @@ int serv_creat(char *arg)
FILE *fp;
if (!arg || !arg[0])
errx(1, "missing argument to create");
ERRX(2, "missing argument to create");
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
ERRX(4, "%s is a built-in service.", arg);
/* Input from a pipe or a proper TTY? */
if (isatty(STDIN_FILENO))
@@ -419,16 +418,16 @@ int serv_creat(char *arg)
/* Open fn for writing from pipe */
fn = conf(buf, sizeof(buf), arg, 1);
if (!fn)
err(1, "failed creating conf %s", arg);
ERR(73, "failed creating conf %s", arg);
if (!icreate && fexist(fn)) {
warnx("%s already exists, skipping (use -c to override)", fn);
WARNX("%s already exists, skipping (use -c to override)", fn);
fn = "/dev/null";
}
fp = fopen(fn, "w");
if (!fp)
err(1, "failed opening %s for writing", fn);
ERR(73, "failed opening %s for writing", fn);
while (fgets(buf, sizeof(buf), stdin))
fputs(buf, fp);
@@ -442,24 +441,24 @@ int serv_delete(char *arg)
char *fn;
if (!arg || !arg[0]) {
warnx("missing argument to delete, may be one of:");
WARNX("missing argument to delete, may be one of:");
return serv_list("available");
}
fn = conf(buf, sizeof(buf), arg, 0);
if (!fn) {
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
errx(1, FINIT_RCSD " missing on system.");
ERRX(4, "%s is a built-in service.", arg);
ERRX(72, FINIT_RCSD " missing on system.");
}
if (!fexist(fn))
warnx("cannot find %s", fn);
WARNX("cannot find %s", fn);
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
do_disable(arg, 0);
if (remove(fn))
err(1, "Failed removing %s", fn);
ERR(1, "Failed removing %s", fn);
}
return 0;
+105 -77
View File
@@ -198,9 +198,10 @@ static int lredirect(svc_t *svc)
pid = fork();
if (pid == 0) {
int fds;
char *tag = basename(svc->cmd);
char *prio = "daemon.info";
char buf[MAX_IDENT_LEN];
char *tag;
int fds;
sched_yield();
@@ -213,6 +214,9 @@ static int lredirect(svc_t *svc)
/* Reset signals */
sig_unblock();
/* Default syslog identity name[:id] */
tag = svc_ident(svc, buf, sizeof(buf));
if (!whichp(_PATH_LOGIT)) {
logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name);
fallback_logger(tag, prio);
@@ -416,20 +420,19 @@ static pid_t service_fork(svc_t *svc)
/* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &svc->rlimit[i]) == -1)
logit(LOG_WARNING,
"%s: rlimit: Failed setting %s",
svc->cmd, rlim2str(i));
logit(LOG_WARNING, "%s: rlimit: failed setting %s",
svc_ident(svc, NULL, 0), rlim2str(i));
}
/* Set desired user+group */
if (gid >= 0) {
if (setgid(gid))
_pe("%s: failed setgid(%d)", svc->cmd, gid);
_pe("%s: failed setgid(%d)", svc_ident(svc, NULL, 0), gid);
}
if (uid >= 0) {
if (setuid(uid))
_pe("%s: failed setuid(%d)", svc->cmd, uid);
_pe("%s: failed setuid(%d)", svc_ident(svc, NULL, 0), uid);
/* Set default path for regular users */
if (uid > 0)
@@ -438,7 +441,7 @@ static pid_t service_fork(svc_t *svc)
setenv("HOME", home, 1);
if (chdir(home)) {
if (chdir("/"))
_pe("%s: failed chdir(%s) and chdir(/)", svc->cmd, home);
_pe("%s: failed chdir(%s) and chdir(/)", svc_ident(svc, NULL, 0), home);
}
}
}
@@ -475,14 +478,14 @@ static int service_start(svc_t *svc)
/* Don't try and start service if it doesn't exist. */
if (!whichp(svc->cmd)) {
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
logit(LOG_WARNING, "%s: missing %s or not in $PATH", svc_ident(svc, NULL, 0), svc->cmd);
svc_missing(svc);
return 1;
}
/* Unlike systemd we do not allow starting service if env is missing, unless - */
if (!svc_checkenv(svc)) {
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
logit(LOG_WARNING, "%s: missing %s env file %s", svc_ident(svc, NULL, 0), svc->cmd, svc->env);
svc_missing(svc);
return 1;
}
@@ -535,7 +538,7 @@ static int service_start(svc_t *svc)
int rc;
if ((rc = wordexp(svc->cmd, &we, 0))) {
_e("%s: failed wordexp(%s): %d", svc->cmd, svc->cmd, rc);
_e("%s: failed wordexp(%s): %d", svc_ident(svc, NULL, 0), svc->cmd, rc);
nomem:
wordfree(&we);
_exit(1);
@@ -563,19 +566,19 @@ static int service_start(svc_t *svc)
strlcat(str, arg, sizeof(str));
if ((rc = wordexp(str, &we, WRDE_APPEND))) {
_e("%s: failed wordexp(%s): %d", svc->cmd, str, rc);
_e("%s: failed wordexp(%s): %d", svc_ident(svc, NULL, 0), str, rc);
goto nomem;
}
}
if (we.we_wordc > MAX_NUM_SVC_ARGS) {
logit(LOG_ERR, "%s: too man args after expansion.", svc->cmd);
logit(LOG_ERR, "%s: too many args to %s after expansion.", svc_ident(svc, NULL, 0), svc->cmd);
goto nomem;
}
for (i = 0; i < we.we_wordc; i++) {
if (strlen(we.we_wordv[i]) >= sizeof(svc->args[i])) {
logit(LOG_ERR, "%s: expanded arg. '%s' too long", svc->cmd, we.we_wordv[i]);
logit(LOG_ERR, "%s: expanded %s arg. '%s' too long", svc_ident(svc, NULL, 0), svc->cmd, we.we_wordv[i]);
rc = WRDE_NOSPACE;
goto nomem;
}
@@ -684,7 +687,7 @@ static void service_kill(svc_t *svc)
if (svc->pid <= 1) {
/* Avoid killing ourselves or all processes ... */
_d("%s: Aborting SIGKILL, already terminated.", svc->cmd);
_d("%s: Aborting SIGKILL, already terminated.", svc_ident(svc, NULL, 0));
return;
}
@@ -714,7 +717,7 @@ static void service_cleanup(svc_t *svc)
fn = pid_file(svc);
if (fn && remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
basename(svc->cmd), fn);
svc_ident(svc, NULL, 0), fn);
/* No longer running, update books. */
if (svc_is_tty(svc) && svc->pid > 1)
@@ -728,10 +731,14 @@ static void service_cleanup(svc_t *svc)
* service_stop - Stop service
* @svc: Service to stop
*
* Called externally by initctl to perform stop/start (restart) of
* services. Internally it is used to bring a run/task/service to
* HALTED state.
*
* Returns:
* 0 if the service was successfully stopped. Non-zero otherwise.
*/
static int service_stop(svc_t *svc)
int service_stop(svc_t *svc)
{
char cmdline[CMD_SIZE] = "";
int do_progress = 1;
@@ -801,7 +808,7 @@ static int service_stop(svc_t *svc)
args[i++] = svc->cmd;
/* this handles, e.g., bridge-stop br0 stop */
for (j = 0; j < MAX_NUM_SVC_ARGS; j++) {
for (j = 0; j < MAX_NUM_SVC_ARGS - 2; j++) {
if (!strlen(svc->args[j]))
break;
args[i++] = svc->args[j];
@@ -857,7 +864,7 @@ static int service_restart(svc_t *svc)
return 1;
if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
_d("%s: bad PID %d for %s, SIGHUP", svc_ident(svc, NULL, 0), svc->pid, svc->cmd);
svc->start_time = svc->pid = 0;
return 1;
}
@@ -960,7 +967,7 @@ static void parse_env(svc_t *svc, char *env)
return;
if (strlen(env) >= sizeof(svc->env)) {
_e("%s: env file is too long (>%zu chars)", svc->cmd, sizeof(svc->env));
_e("%s: env file is too long (>%zu chars)", svc_ident(svc, NULL, 0), sizeof(svc->env));
return;
}
@@ -984,7 +991,7 @@ static void parse_cgroup(svc_t *svc, char *cgroup)
}
if (strlen(ptr) >= sizeof(svc->cgroup)) {
_e("%s: cgroup settings too long (>%zu chars)", svc->cmd, sizeof(svc->cgroup));
_e("%s: cgroup settings too long (>%zu chars)", svc_ident(svc, NULL, 0), sizeof(svc->cgroup));
return;
}
@@ -1009,7 +1016,7 @@ static void parse_killdelay(svc_t *svc, char *delay)
sec = strtonum(delay, 1, 60, &errstr);
if (errstr) {
_e("%s: killdelay %s is %s (1-60)", svc->cmd, delay, errstr);
_e("%s: killdelay %s is %s (1-60)", svc_ident(svc, NULL, 0), delay, errstr);
return;
}
@@ -1137,7 +1144,6 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
svc->args_dirty = (diff > 0);
}
/**
* service_register - Register service, task or run commands
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
@@ -1246,6 +1252,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
while (cmd) {
char *arg;
if (type == SVC_TYPE_TTY && cmd[0] == '@')
break; /* @console */
@@ -1257,44 +1265,48 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
cond = &cmd[1];
else if (cmd[0] == ':') /* :ID */
id = &cmd[1];
else if (!strncasecmp(cmd, "log", 3))
else if (MATCH_CMD(cmd, "log", arg))
log = cmd;
else if (!strncasecmp(cmd, "pid", 3))
else if (MATCH_CMD(cmd, "pid", arg))
pid = cmd;
else if (!strncasecmp(cmd, "name:", 5))
else if (MATCH_CMD(cmd, "name:", arg))
name = cmd;
else if (!strncasecmp(cmd, "type:forking", 10))
else if (MATCH_CMD(cmd, "type:forking", arg))
forking = 1;
else if (!strncasecmp(cmd, "manual:yes", 10))
else if (MATCH_CMD(cmd, "manual:yes", arg))
manual = 1;
else if (!strncasecmp(cmd, "restart:", 8))
restart_max = atoi(&cmd[8]);
else if (!strncasecmp(cmd, "restarttmo:", 11)) /* compat alias */
restart_tmo = atoi(&cmd[11]) * 1000;
else if (!strncasecmp(cmd, "restart_sec:", 12))
restart_tmo = atoi(&cmd[12]) * 1000;
else if (!strncasecmp(cmd, "norestart", 9))
else if (MATCH_CMD(cmd, "restart:", arg)) {
if (MATCH_CMD(arg, "always", arg))
restart_max = -1;
else
restart_max = atoi(arg);
}
else if (MATCH_CMD(cmd, "restarttmo:", arg)) /* compat alias */
restart_tmo = atoi(arg) * 1000;
else if (MATCH_CMD(cmd, "restart_sec:", arg))
restart_tmo = atoi(arg) * 1000;
else if (MATCH_CMD(cmd, "norestart", arg))
restart_max = 0;
else if (!strncasecmp(cmd, "oncrash:", 8)) {
if (!strncasecmp(&cmd[8], "reboot", 6))
else if (MATCH_CMD(cmd, "oncrash:", arg)) {
if (MATCH_CMD(arg, "reboot", arg))
oncrash_action = SVC_ONCRASH_REBOOT;
}
else if (!strncasecmp(cmd, "respawn", 7))
else if (MATCH_CMD(cmd, "respawn", arg))
respawn = 1;
else if (!strncasecmp(cmd, "halt:", 5))
halt = &cmd[5];
else if (!strncasecmp(cmd, "kill:", 5))
delay = &cmd[5];
else if (!strncasecmp(cmd, "pre:", 4))
pre_script = &cmd[4];
else if (!strncasecmp(cmd, "post:", 5))
post_script = &cmd[5];
else if (!strncasecmp(cmd, "env:", 4))
env = &cmd[4];
else if (!strncasecmp(cmd, "cgroup:", 7))
cgroup = &cmd[7]; /* only settings */
else if (!strncasecmp(cmd, "cgroup.", 7))
cgroup = &cmd[6]; /* with group */
else if (MATCH_CMD(cmd, "halt:", arg))
halt = arg;
else if (MATCH_CMD(cmd, "kill:", arg))
delay = arg;
else if (MATCH_CMD(cmd, "pre:", arg))
pre_script = arg;
else if (MATCH_CMD(cmd, "post:", arg))
post_script = arg;
else if (MATCH_CMD(cmd, "env:", arg))
env = arg;
else if (MATCH_CMD(cmd, "cgroup:", arg))
cgroup = arg; /* only settings */
else if (MATCH_CMD(cmd, "cgroup.", arg))
cgroup = arg; /* with group */
else
break;
@@ -1362,18 +1374,17 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
return errno = ENOMEM;
}
if (type == SVC_TYPE_SERVICE && manual)
if (manual)
svc_stop(svc);
svc->restart_max = restart_max;
svc->restart_tmo = restart_tmo;
svc->oncrash_action = oncrash_action;
} else {
_d("Found existing svc for %s name %s id %s type %d", cmd, name, id, type);
/* update type, may have changed from service -> task */
svc->type = type;
/* update path, may have changed on reload */
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
/* e.g., if missing cmd or env before */
if (!manual)
svc_unblock(svc);
@@ -1455,8 +1466,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
parse_env(svc, env);
if (file)
strlcpy(svc->file, file, sizeof(svc->file));
svc->manual = manual;
svc->respawn = respawn;
svc->forking = forking;
svc->restart_max = restart_max;
svc->restart_tmo = restart_tmo;
svc->oncrash_action = oncrash_action;
/* Decode any (optional) pid:/optional/path/to/file.pid */
if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
@@ -1752,7 +1767,7 @@ static void service_retry(svc_t *svc)
}
/* Peak instability index */
if (*restart_cnt >= svc->restart_max) {
if (svc->restart_max != -1 && *restart_cnt >= svc->restart_max) {
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
svc_ident(svc, NULL, 0));
svc_crashing(svc);
@@ -1874,18 +1889,15 @@ restart:
switch (svc->state) {
case SVC_HALTED_STATE:
if (enabled) {
if (svc_has_pre(svc)) {
svc_set_state(svc, SVC_SETUP_STATE);
service_pre_script(svc);
} else
svc_set_state(svc, SVC_READY_STATE);
}
if (enabled)
svc_set_state(svc, SVC_READY_STATE);
break;
case SVC_DONE_STATE:
if (svc_is_changed(svc))
svc_set_state(svc, SVC_HALTED_STATE);
if (svc_is_runtask(svc) && svc_is_manual(svc) && enabled)
svc_set_state(svc, SVC_READY_STATE);
break;
case SVC_STOPPING_STATE:
@@ -1909,6 +1921,8 @@ restart:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
if (svc->manual)
svc_stop(svc);
svc_set_state(svc, SVC_DONE_STATE);
break;
@@ -1926,7 +1940,7 @@ restart:
case SVC_SETUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_READY_STATE);
svc_set_state(svc, SVC_STARTING_STATE);
break;
case SVC_READY_STATE:
@@ -1937,22 +1951,36 @@ restart:
if (sm_is_in_teardown(&sm))
break;
err = service_start(svc);
if (err) {
if (svc_is_missing(svc)) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
(*restart_cnt)++;
if (svc_has_pre(svc)) {
svc_set_state(svc, SVC_SETUP_STATE);
service_pre_script(svc);
break;
}
/* Everything went fine, clean and set state */
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
svc_set_state(svc, SVC_STARTING_STATE);
}
break;
case SVC_STARTING_STATE:
if (!enabled) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
err = service_start(svc);
if (err) {
if (svc_is_missing(svc)) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
(*restart_cnt)++;
break;
}
/* Everything went fine, clean and set state */
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
break;
case SVC_RUNNING_STATE:
if (!enabled) {
service_stop(svc);
+1
View File
@@ -40,6 +40,7 @@ int service_timeout_cancel (svc_t *svc);
void service_forked (svc_t *svc);
int service_stop (svc_t *svc);
int service_step (svc_t *svc);
void service_step_all (int types);
void service_worker (void *unused);
+2 -2
View File
@@ -187,7 +187,7 @@ restart:
*/
svc = svc_stop_completed();
if (svc) {
_d("Waiting to collect %s(%d) ...", svc->cmd, svc->pid);
_d("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break;
}
@@ -240,7 +240,7 @@ restart:
*/
svc = svc_stop_completed();
if (svc) {
_d("Waiting to collect %s(%d) ...", svc->cmd, svc->pid);
_d("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break;
}
-1
View File
@@ -24,7 +24,6 @@
#include "config.h" /* Generated by configure script */
#include <err.h>
#include <ctype.h> /* isdigit() */
#include <time.h>
#include <signal.h>
+5 -2
View File
@@ -63,6 +63,7 @@ typedef enum {
SVC_SETUP_STATE, /* Running pre: script */
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
SVC_READY_STATE, /* Enabled but condition not satisfied */
SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */
SVC_RUNNING_STATE, /* Process running */
} svc_state_t;
@@ -121,7 +122,8 @@ typedef struct svc {
int status; /* From waitpid() when process is collected */
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; /* Service, run, task, ... */
int protect; /* Services like dbus-daemon & udev by Finit */
char protect; /* Services like dbus-daemon & udev by Finit */
char manual; /* run/task that require `initctl start foo` */
const int dirty; /* 0: unmodified, 1: modified */
const int removed;
int starting; /* ... waiting for pidfile to be re-asserted */
@@ -139,7 +141,7 @@ typedef struct svc {
/* Counters */
char once; /* run/task, (at least) once per runlevel */
unsigned int restart_tot; /* Total restarts ever, summarized, including `initctl restart` */
unsigned char restart_max; /* Maximum number of restarts allowed */
int restart_max; /* Maximum number of restarts allowed */
int restart_tmo; /* Time required for the service to start. */
unsigned char oncrash_action; /* Action to perform in crashed state. */
char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */
@@ -229,6 +231,7 @@ static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV
static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY == 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 && svc->forking; }
static inline int svc_is_manual (svc_t *svc) { return svc && svc->manual; }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
+11 -1
View File
@@ -154,6 +154,7 @@ char *tty_atcon(void)
int tty_parse_args(char *cmd, struct tty *tty)
{
char *dev = NULL;
int passenv = 0;
size_t i;
do {
@@ -165,6 +166,8 @@ int tty_parse_args(char *cmd, struct tty *tty)
tty->nologin = 1;
else if (!strcmp(cmd, "notty"))
tty->notty = 1; /* for board bringup */
else if (!strcmp(cmd, "passenv"))
passenv = 1;
else if (!strcmp(cmd, "rescue"))
tty->rescue = 1; /* for rescue shells */
else if (whichp(cmd)) /* in $PATH? */
@@ -228,8 +231,15 @@ int tty_parse_args(char *cmd, struct tty *tty)
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
/* Built-in getty now comes as standalone program */
if (!tty->cmd)
if (!tty->cmd) {
if (passenv && tty->num < NELEMS(tty->args)) {
for (i = tty->num; i > 0; i--)
tty->args[i] = tty->args[i - 1];
tty->args[0] = "-p";
tty->num++;
}
tty->cmd = _PATH_GETTY;
}
return 0;
}
+4
View File
@@ -23,7 +23,9 @@ EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
EXTRA_DIST += cond-start-task.sh
EXTRA_DIST += failing-sysv.sh
EXTRA_DIST += global-envs.sh
EXTRA_DIST += initctl-status-subset.sh
EXTRA_DIST += pre-post-serv.sh
EXTRA_DIST += restart-self.sh
EXTRA_DIST += runlevel.sh
EXTRA_DIST += start-stop-service.sh
EXTRA_DIST += start-stop-service-sub-config.sh
@@ -51,7 +53,9 @@ TESTS += add-remove-dynamic-service-sub-config.sh
TESTS += cond-start-task.sh
TESTS += failing-sysv.sh
TESTS += global-envs.sh
TESTS += initctl-status-subset.sh
TESTS += pre-post-serv.sh
TESTS += restart-self.sh
TESTS += runlevel.sh
TESTS += start-stop-service.sh
TESTS += start-stop-service-sub-config.sh
+2
View File
@@ -1,2 +1,4 @@
noinst_PROGRAMS = serv
serv_SOURCES = serv.c
serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
+39 -3
View File
@@ -21,7 +21,7 @@ static void verify_env(char *arg)
char *replica;
char *env;
key = replica = strdup(arg);
key = replica = strdupa(arg);
if (!replica)
err(1, "Failed duplicating arg %s", arg);
@@ -47,6 +47,28 @@ static void verify_noenv(char *key)
errx(1, "Error, key %s is set in environment to '%s'", key, val);
}
static void inc_restarts(void)
{
char buf[10];
int cnt = 0;
FILE *fp;
fp = fopen("/tmp/serv-restart.cnt", "r+");
if (!fp) {
fp = fopen("/tmp/serv-restart.cnt", "w");
if (!fp)
err(1, "failed creating restart counter file");
} else {
if (fgets(buf, sizeof(buf), fp))
cnt = atoi(buf);
}
cnt++;
fseek(fp, SEEK_SET, 0);
fprintf(fp, "%d", cnt);
fclose(fp);
}
static void sig(int signo)
{
warnx("We got signal %d ...", signo);
@@ -86,6 +108,7 @@ static int usage(int rc)
" -n Run in foreground\n"
" -p Create PID file despite running in foreground\n"
" -P FILE Create PID file using FILE\n"
" -r SVC Call initctl to restart service SVC (self)\n"
"\n"
"By default this program daemonizes itself to the background, and,\n"
"when it's done setting up its signal handler(s), creates a PID file\n"
@@ -100,10 +123,12 @@ int main(int argc, char *argv[])
{
int do_background = 1;
int do_pidfile = 1;
int do_restart = 0;
char *pidfn = NULL;
char cmd[80];
int c;
while ((c = getopt(argc, argv, "e:E:hnpP:")) != EOF) {
while ((c = getopt(argc, argv, "e:E:hnpP:r:")) != EOF) {
switch (c) {
case 'h':
return usage(0);
@@ -123,6 +148,10 @@ int main(int argc, char *argv[])
case 'P':
pidfn = optarg;
break;
case 'r':
snprintf(cmd, sizeof(cmd), "initctl restart %s", optarg);
do_restart = 1;
break;
default:
return usage(1);
}
@@ -142,8 +171,15 @@ int main(int argc, char *argv[])
pidfile(pidfn);
warnx("Entering while(1) loop");
while (1)
while (1) {
sleep(1);
if (do_restart) {
inc_restarts();
if (system(cmd))
return 1;
break;
}
}
warnx("Leaving ...");
return 0;
+57
View File
@@ -0,0 +1,57 @@
#!/bin/sh
# Verify that the status command 'initctl status foo' matches three
# instances of tasks called foo:1, foo:2, and foo:3. It should not
# match the fourth task called foobar. This is a regression test to
# ensure issue #275 doesn't happen again.
set -eu
TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown()
{
say "Test done $(date)"
texec rm -f "$FINIT_CONF"
}
test_init()
{
texec sh -c "echo '# Three foo and one bar enter Finit' > $FINIT_CONF"
}
test_add_one()
{
service=$1
say "Add service stanza '$service' to $FINIT_CONF ..."
texec sh -c "echo '$service' >> $FINIT_CONF"
}
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
test_init
test_add_one "task manual:yes name:foo :1 serv -- Foo #1"
test_add_one "task manual:yes name:foo :2 serv -- Foo #2"
test_add_one "task manual:yes name:foo :3 serv -- Foo #3"
test_add_one "task manual:yes name:foobar serv -- Foobar"
say 'Reload Finit'
#texec sh -c "initctl debug"
texec sh -c "initctl reload"
texec sh -c "initctl status"
#texec sh -c "initctl status"
#texec sh -c "ps"
#texec sh -c "initctl status serv"
assert_num_services 3 foo
assert_desc "Foobar" foobar
assert_desc "Foo #1" foo:1
+47
View File
@@ -0,0 +1,47 @@
#!/bin/sh
# Verify that a service can restart itself, issue #280
set -eu
TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
texec sh -c "mkdir -p /etc/default"
}
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF"
}
check_restarts()
{
assert "serv restarts" "$(texec cat "$1" | awk '{print $1;}')" -ge "$2"
}
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
say "Add stanza to $FINIT_CONF"
texec sh -c "echo 'service serv -np -r serv -- Restart self' > $FINIT_CONF"
say 'Reload Finit'
texec sh -c "initctl reload"
sleep 2
texec sh -c "initctl status serv"
texec sh -c "ps"
sleep 2
texec sh -c "initctl status serv"
texec sh -c "ps"
say 'Pending restarts by itself ...'
retry 'check_restarts /tmp/serv-restart.cnt 3' 20 1
return 0
+10
View File
@@ -31,6 +31,11 @@ assert_num_children()
assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1"
}
assert_num_services()
{
assert "$1 services are loaded" "$(texec initctl -t status "$2" | wc -l)" -eq "$1"
}
# shellcheck disable=SC2086
assert_new_pid()
{
@@ -42,6 +47,11 @@ assert_restarts()
assert "Finit has registered restarts" "$(texec initctl status "$2" | awk '/Restarts/{print $3;}')" -ge "$1"
}
assert_desc()
{
assert "Service description == $1" "$(texec initctl status "$2" | grep 'Description' | sed 's/Description : //')" = "$1"
}
# shellcheck disable=SC2086
assert_pidfile()
{