mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a61a8015d7 | ||
|
|
e2f070649d | ||
|
|
f08d375c36 | ||
|
|
1c4bad5b73 | ||
|
|
6c874895dc | ||
|
|
ef1a05425d | ||
|
|
fc3bad2160 | ||
|
|
31ac933d64 | ||
|
|
caa398816d | ||
|
|
0d57a507f3 | ||
|
|
96257cc5c7 | ||
|
|
fbcd7d2384 | ||
|
|
d9dbfa23c7 | ||
|
|
91f077bd08 | ||
|
|
5fb4a6ae8f | ||
|
|
e84333662e | ||
|
|
ddb42fda70 | ||
|
|
621eb21b42 | ||
|
|
6cc44a87c9 | ||
|
|
5f908cae49 | ||
|
|
6de67ed107 | ||
|
|
c129f06195 | ||
|
|
d11dc63bc2 | ||
|
|
d904ec1602 | ||
|
|
2a98b2f055 | ||
|
|
6286ede985 | ||
|
|
4a8ca787d3 | ||
|
|
561c88a868 | ||
|
|
f8dfcdf024 | ||
|
|
f50b5a4ac6 | ||
|
|
59525b80e3 | ||
|
|
0ce028add1 | ||
|
|
75c5c85f07 | ||
|
|
ccb84ced2e | ||
|
|
5ef669fb6c | ||
|
|
96ff9c115c | ||
|
|
83aa6abb1c | ||
|
|
9304024841 | ||
|
|
0de5981948 | ||
|
|
c39106906d | ||
|
|
929a6e7aac | ||
|
|
ceb4d04d23 | ||
|
|
f483a8aeaa | ||
|
|
e3e220ce8e | ||
|
|
fc653e4293 | ||
|
|
94d55cb7dd | ||
|
|
0e84ecf143 | ||
|
|
0e93dfd640 | ||
|
|
4dff8237f1 | ||
|
|
35400f2a8b | ||
|
|
9a76f0862c | ||
|
|
fb8a7caa99 | ||
|
|
f8bed5c7af | ||
|
|
da6fa482e7 | ||
|
|
fdf8e48c99 | ||
|
|
195155a7f5 | ||
|
|
6934ba4dbc | ||
|
|
fdbd25c1b5 | ||
|
|
467f1cca81 | ||
|
|
d6fdf4a806 | ||
|
|
4be9d97e61 | ||
|
|
021d011493 | ||
|
|
e988d38f71 | ||
|
|
8690fc732b |
+16
-2
@@ -4,7 +4,7 @@ Change Log
|
|||||||
All relevant changes are documented in this file.
|
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
|
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
|
* Document secret service option `respawn`, which bypasses the crash
|
||||||
semantics, allowing endless restarts
|
semantics, allowing endless restarts
|
||||||
* Document secret `HOOK_BANNER`, the first hook point before the banner
|
* 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
|
* New plugin: `hook-scripts`, allows [run-parts(8)][] style scripts to
|
||||||
run on any hook point. Contributed by Tobias Waldekranz
|
run on any hook point. Contributed by Tobias Waldekranz
|
||||||
* `initctl` (`reboot`) falls back to `-f` when it detects it is in
|
* `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
|
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
|
* The `name:id` tuple is now more consistently used in all log and debug
|
||||||
messages instead of the basename of the command
|
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
|
[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
|
* Fix #233: initctl shows wrong status for run/task, by Sergio Morlans
|
||||||
and Ming Liu, Atlas Copco
|
and Ming Liu, Atlas Copco
|
||||||
* Fix #248: source `env:file` also in `pre:` and `post` scripts
|
* 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
|
* Fix start/stop and monitoring (restart) of SysV init scripts and
|
||||||
forking services, see the updated documentation for details
|
forking services, see the updated documentation for details
|
||||||
* Fix call to `swapoff` at shutdown, does not support `-e` flag
|
* Fix call to `swapoff` at shutdown, does not support `-e` flag
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ Features include:
|
|||||||
* Cgroups v2, both configuration and monitoring in `initctl top`
|
* Cgroups v2, both configuration and monitoring in `initctl top`
|
||||||
* Plugin support for customization
|
* Plugin support for customization
|
||||||
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
|
* 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
|
Focus is on small and embedded systems, although Finit is fully usable
|
||||||
on server and desktop systems as well. For working examples, see the
|
on server and desktop systems as well. For working examples, see the
|
||||||
@@ -52,12 +56,12 @@ on server and desktop systems as well. For working examples, see the
|
|||||||
|
|
||||||
* [Void Linux](contrib/void/),
|
* [Void Linux](contrib/void/),
|
||||||
* [Alpine Linux](contrib/alpine/), and
|
* [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
|
> **Note:** support for various Linux distributions does not mean Finit
|
||||||
> installs easily on all architectures. The bundled install scripts are
|
> installs easily on all architectures. The bundled install scripts are
|
||||||
> examples for standard installations, tested on x86_64 systems. Custom
|
> examples for standard installations, tested on amd64 (x86_64) systems.
|
||||||
> setups, targeted at embedded systems, can be found in [myLinux][].
|
> Custom setups, for embedded systems, can be found in [myLinux][].
|
||||||
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
@@ -73,7 +77,11 @@ the [initctl](#commands--status) tool.
|
|||||||
host default
|
host default
|
||||||
|
|
||||||
# Runlevel to start after bootstrap, 'S', default: 2
|
# 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:
|
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||||
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
# log => 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
|
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
|
||||||
#task [S] /etc/init.d/kbd -- Preparing console
|
#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
|
# Run start scripts from this directory
|
||||||
# runparts /etc/start.d
|
# runparts /etc/start.d
|
||||||
|
|
||||||
@@ -268,8 +281,8 @@ unnecessary overhead, which can be removed at build-time using:
|
|||||||
|
|
||||||
**Cgroups**
|
**Cgroups**
|
||||||
|
|
||||||
Finit supports cgroups v2 and comes with the following default groups
|
Finit supports cgroups v2 and comes with the following default groups in
|
||||||
in which services and user sessions are placed in:
|
which services and user sessions are placed in:
|
||||||
|
|
||||||
/sys/fs/cgroup
|
/sys/fs/cgroup
|
||||||
|-- init/ # cpu.weight:100
|
|-- 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`
|
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
|
||||||
commands for details.
|
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
|
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.
|
not return until the given command has fully completed.
|
||||||
|
|
||||||
```
|
```
|
||||||
alpine:~# initctl help
|
|
||||||
Usage: initctl [OPTIONS] [COMMAND]
|
Usage: initctl [OPTIONS] [COMMAND]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-b, --batch Batch mode, no screen size probing
|
-b, --batch Batch mode, no screen size probing
|
||||||
-c, --create Create missing paths (and files) as needed
|
-c, --create Create missing paths (and files) as needed
|
||||||
-f, --force Ignore missing files and arguments, never prompt
|
-f, --force Ignore missing files and arguments, never prompt
|
||||||
|
-h, --help This help text
|
||||||
-1, --once Only one lap in commands like 'top'
|
-1, --once Only one lap in commands like 'top'
|
||||||
-p, --plain Use plain table headings, no ctrl chars
|
-p, --plain Use plain table headings, no ctrl chars
|
||||||
-q, --quiet Silent, only return status of command
|
-q, --quiet Silent, only return status of command
|
||||||
-t, --no-heading Skip table headings
|
-t, --no-heading Skip table headings
|
||||||
-v, --verbose Verbose output
|
-v, --verbose Verbose output
|
||||||
-h, --help This help text
|
-V, --version Show program version
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
debug Toggle Finit (daemon) debug
|
debug Toggle Finit (daemon) debug
|
||||||
help This help text
|
help This help text
|
||||||
version Show Finit version
|
version Show program version
|
||||||
|
|
||||||
ls | list List all .conf in /etc/finit.d
|
ls | list List all .conf in /etc/finit.d
|
||||||
create <CONF> Create .conf in /etc/finit.d/available
|
create <CONF> Create .conf in /etc/finit.d/available
|
||||||
@@ -444,15 +461,20 @@ Commands:
|
|||||||
reload Reload *.conf in /etc/finit.d (activate changes)
|
reload Reload *.conf in /etc/finit.d (activate changes)
|
||||||
|
|
||||||
cond set <COND> Set (assert) user-defined condition +usr/COND
|
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 clear <COND> Clear (deassert) user-defined condition -usr/COND
|
||||||
cond status Show condition status, default cond command
|
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
|
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||||
start <NAME>[:ID] Start service by name, with optional ID
|
start <NAME>[:ID] Start service by name, with optional ID
|
||||||
stop <NAME>[:ID] Stop/Pause a running service by name
|
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
|
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 <NAME>[:ID] Show service status, by name
|
||||||
status Show status of services, default command
|
status Show status of services, default command
|
||||||
|
|
||||||
@@ -465,6 +487,8 @@ Commands:
|
|||||||
halt Halt system
|
halt Halt system
|
||||||
poweroff Halt and power off system
|
poweroff Halt and power off system
|
||||||
suspend Suspend system
|
suspend Suspend system
|
||||||
|
|
||||||
|
utmp show Raw dump of UTMP/WTMP db
|
||||||
```
|
```
|
||||||
|
|
||||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||||
@@ -603,7 +627,9 @@ and proposed extensions.
|
|||||||
[Joachim Wiberg]: https://troglobit.com
|
[Joachim Wiberg]: https://troglobit.com
|
||||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
[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]: https://github.com/troglobit/finit/actions/workflows/build.yml/
|
||||||
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
|
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
|
||||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||||
|
|||||||
+9
-1
@@ -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)
|
AC_CONFIG_AUX_DIR(aux)
|
||||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||||
AM_SILENT_RULES([yes])
|
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]),
|
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])
|
[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 ###########################################################################
|
### 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_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
|
||||||
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
|
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
|
# Control build with automake flags
|
||||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||||
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
|
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
|
||||||
|
|||||||
+1
-1
@@ -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.
|
* `--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*
|
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
|
||||||
login if the `root` user is disabled or has no password at all.
|
login if the `root` user is disabled or has no password at all.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
* `debug`: Enable kernel debug. Debug messages are printed to the
|
||||||
console until Finit starts up, unless `loglevel=7` (below) is used.
|
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
|
* `finit.debug[=bool]`: Enable finit debug. This is operated
|
||||||
independently of the kernel `debug` setting. New as of Finit v4.
|
independently of the kernel `debug` setting. New as of Finit v4.
|
||||||
|
|
||||||
|
|||||||
+11
-5
@@ -46,11 +46,16 @@ Triggering
|
|||||||
----------
|
----------
|
||||||
|
|
||||||
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
|
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
|
- `netlink.so`: provides `<net/...>`
|
||||||
or one-shot conditions. They live in the `<usr/...` namespace and are
|
- `pidfile.so`: provides `<pid/...>`
|
||||||
constrained to a flat hierarchy without any sub-directories, unlike the
|
- Cmdline `finit.cond=arg`: provides `<boot/arg`
|
||||||
pidfile plugin, for instance.
|
|
||||||
|
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
|
User-defined conditions are controlled using the `initctl cond set` and
|
||||||
`initctl cond clear` commands:
|
`initctl cond clear` commands:
|
||||||
@@ -118,6 +123,7 @@ Built-in conditions:
|
|||||||
- `sys/pwr/fail`
|
- `sys/pwr/fail`
|
||||||
- `sys/key/ctrlaltdel`
|
- `sys/key/ctrlaltdel`
|
||||||
- `usr/foo`
|
- `usr/foo`
|
||||||
|
- `boot/arg`
|
||||||
|
|
||||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
||||||
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
||||||
|
|||||||
@@ -450,6 +450,8 @@ configurable with the following options:
|
|||||||
a crashing service, default: 2 seconds for the first five retries,
|
a crashing service, default: 2 seconds for the first five retries,
|
||||||
then back-off to 5 seconds. The maximum of this configured value
|
then back-off to 5 seconds. The maximum of this configured value
|
||||||
and the above (2 and 5) will be used
|
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`
|
* `norestart` -- dont restart on failures, same as `restart:0`
|
||||||
* `respawn` -- bypasses the `restart` mechanism completely, allows
|
* `respawn` -- bypasses the `restart` mechanism completely, allows
|
||||||
endless restarts. Useful in many use-cases, but not what `service`
|
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 |
@@ -304,6 +304,10 @@ number of seconds before Finit tries to restart
|
|||||||
a crashing service, default: 2 seconds for the first five retries,
|
a crashing service, default: 2 seconds for the first five retries,
|
||||||
then back-off to 5 seconds. The maximum of this configured value
|
then back-off to 5 seconds. The maximum of this configured value
|
||||||
and the above (2 and 5) will be used
|
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
|
.It Cm norestart
|
||||||
dont restart on failures, same as
|
dont restart on failures, same as
|
||||||
.Cm restart:0
|
.Cm restart:0
|
||||||
|
|||||||
+50
-6
@@ -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:
|
options starting with two dashes (`-'). The options are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl b, -batch
|
.It Fl b, -batch
|
||||||
Batch mode, no screen size probing.
|
Batch mode, no screen size probing. Useful for scripting.
|
||||||
.It Fl c, -create
|
.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
|
.It Fl f, -force
|
||||||
Ignore missing files and arguments, never prompt.
|
Ignore missing files and arguments, never prompt.
|
||||||
.It Fl h, -help
|
.It Fl h, -help
|
||||||
@@ -64,7 +66,16 @@ files in
|
|||||||
Create
|
Create
|
||||||
.Cm .conf
|
.Cm .conf
|
||||||
in
|
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
|
.It Nm Ar delete Ar CONF
|
||||||
Delete
|
Delete
|
||||||
.Cm .conf
|
.Cm .conf
|
||||||
@@ -113,6 +124,20 @@ i.e., activates changes
|
|||||||
.It Nm Ar cond set Ar COND
|
.It Nm Ar cond set Ar COND
|
||||||
Set (assert) user-defined condition,
|
Set (assert) user-defined condition,
|
||||||
.Cm +usr/COND
|
.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
|
.It Nm Ar cond clr | clear Ar COND
|
||||||
Clear (deassert) user-defined condition,
|
Clear (deassert) user-defined condition,
|
||||||
.Cm -usr/COND
|
.Cm -usr/COND
|
||||||
@@ -120,8 +145,8 @@ Clear (deassert) user-defined condition,
|
|||||||
Show condition status, default
|
Show condition status, default
|
||||||
.Cm cond
|
.Cm cond
|
||||||
command.
|
command.
|
||||||
.It Nm Ar cond dump
|
.It Nm Ar cond dump Op Cm TYPE
|
||||||
Dump all conditions and their status
|
Dump all, or a given type of, conditions and their status
|
||||||
.It Nm Ar ident Op Cm NAME
|
.It Nm Ar ident Op Cm NAME
|
||||||
Display indentities of all run/task/services, or only instances
|
Display indentities of all run/task/services, or only instances
|
||||||
matching
|
matching
|
||||||
@@ -139,7 +164,24 @@ Start service by name, with optional ID, e.g.,
|
|||||||
.It Nm Ar stop Cm NAME[:ID]
|
.It Nm Ar stop Cm NAME[:ID]
|
||||||
Stop/Pause a running service by name
|
Stop/Pause a running service by name
|
||||||
.It Nm Ar reload Cm NAME[:ID]
|
.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
|
.It Nm Ar signal Cm NAME:[ID] S
|
||||||
Send signal S to service, by name.
|
Send signal S to service, by name.
|
||||||
.Cm S
|
.Cm S
|
||||||
@@ -160,6 +202,8 @@ List cgroup config overview
|
|||||||
List processes based on cgroups
|
List processes based on cgroups
|
||||||
.It Nm Ar top
|
.It Nm Ar top
|
||||||
Show top-like listing based on cgroups
|
Show top-like listing based on cgroups
|
||||||
|
.It Nm Ar plugins
|
||||||
|
List installed plugins
|
||||||
.It Nm Ar runlevel Op Ar 0-9
|
.It Nm Ar runlevel Op Ar 0-9
|
||||||
Show or set runlevel: 0 halt, 6 reboot
|
Show or set runlevel: 0 halt, 6 reboot
|
||||||
.It Nm Ar reboot
|
.It Nm Ar reboot
|
||||||
|
|||||||
@@ -116,6 +116,45 @@ static void ln(const char *target, const char *linkpath)
|
|||||||
_pe("Failed creating %s -> %s symlink", target, 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
|
* 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"))
|
if (whichp("restorecon"))
|
||||||
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
|
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
|
||||||
|
|
||||||
|
/* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */
|
||||||
|
kernel_links();
|
||||||
|
|
||||||
umask(prev);
|
umask(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-13
@@ -35,16 +35,20 @@
|
|||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
|
|
||||||
#define DAEMON "dbus-daemon"
|
#define DBUS_DAEMON "dbus-daemon"
|
||||||
#define ARGS "--nofork --system --syslog-only"
|
#define DBUS_ARGS "--nofork --system --syslog-only"
|
||||||
#define DESC "D-Bus message bus daemon"
|
#define DBUS_DESC "D-Bus message bus daemon"
|
||||||
|
|
||||||
#ifndef DAEMONUSER
|
#ifndef DBUS_DAEMONUSER
|
||||||
#define DAEMONUSER "messagebus"
|
#define DBUS_DAEMONUSER "messagebus"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DAEMONPIDFILE
|
#ifndef DBUS_DAEMONGROUP
|
||||||
#define DAEMONPIDFILE "/var/run/dbus/pid"
|
#define DBUS_DAEMONGROUP "messagebus"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DBUS_DAEMONPIDFILE
|
||||||
|
#define DBUS_DAEMONPIDFILE "/var/run/dbus/pid"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
@@ -58,26 +62,32 @@ static void setup(void *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = which(DAEMON);
|
cmd = which(DBUS_DAEMON);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
_d("Skipping plugin, %s is not installed.", DAEMON);
|
_d("Skipping plugin, %s is not installed.", DBUS_DAEMON);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
prev =umask(0);
|
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"))
|
if (whichp("dbus-uuidgen"))
|
||||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||||
|
|
||||||
/* Clean up from any previous pre-bootstrap run */
|
/* Clean up from any previous pre-bootstrap run */
|
||||||
remove(DAEMONPIDFILE);
|
remove(DBUS_DAEMONPIDFILE);
|
||||||
|
|
||||||
/* Register service with Finit */
|
/* Register service with Finit */
|
||||||
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
|
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))
|
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||||
_pe("Failed registering %s", DAEMON);
|
_pe("Failed registering %s", DBUS_DAEMON);
|
||||||
free(cmd);
|
free(cmd);
|
||||||
|
|
||||||
umask(prev);
|
umask(prev);
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ static plugin_t plugin = {
|
|||||||
.hook[HOOK_BASEFS_UP] = {
|
.hook[HOOK_BASEFS_UP] = {
|
||||||
.cb = setup
|
.cb = setup
|
||||||
},
|
},
|
||||||
.depends = { "bootmisc" },
|
.depends = { "bootmisc", "modprobe" },
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
+69
-9
@@ -120,27 +120,87 @@ static int alias_exist(char *alias)
|
|||||||
return 0;
|
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];
|
char buf[256];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
if (fnmatch("*/modalias", path, 0))
|
fp = maybe_fopen_alias("modalias", path);
|
||||||
return 0;
|
|
||||||
|
|
||||||
fp = fopen(path, "r");
|
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return 0;
|
return 1;
|
||||||
|
|
||||||
if (fgets(buf, sizeof(buf), fp)) {
|
if (fgets(buf, sizeof(buf), fp)) {
|
||||||
chomp(buf);
|
chomp(buf);
|
||||||
if (!alias_exist(buf))
|
alias_add_uniq(buf);
|
||||||
alias_add(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
return 0;
|
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)
|
static void coldplug(void *arg)
|
||||||
{
|
{
|
||||||
struct module *m, *tmp;
|
struct module *m, *tmp;
|
||||||
|
|||||||
+2
-2
@@ -102,8 +102,8 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
service_forked(svc);
|
service_forked(svc);
|
||||||
|
|
||||||
if (pid != svc->pid) {
|
if (pid != svc->pid) {
|
||||||
_d("Forking service %s changed PID from %d to %d",
|
_d("Forking service %s (cmd %s) changed PID from %d to %d",
|
||||||
svc->cmd, svc->pid, pid);
|
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid);
|
||||||
svc->pid = pid;
|
svc->pid = pid;
|
||||||
|
|
||||||
/* Complement log in service.c for non-forking services */
|
/* Complement log in service.c for non-forking services */
|
||||||
|
|||||||
+2
-1
@@ -64,7 +64,8 @@ if LOGROTATE
|
|||||||
finit_SOURCES += logrotate.c
|
finit_SOURCES += logrotate.c
|
||||||
endif
|
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 = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||||
|
|||||||
@@ -45,8 +45,9 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "sig.h"
|
#include "schedule.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
#include "sig.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
extern svc_t *wdog;
|
extern svc_t *wdog;
|
||||||
@@ -89,8 +90,10 @@ static int restart(svc_t *svc, void *user_data)
|
|||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
svc_mark_dirty(svc);
|
if (!svc_is_running(svc))
|
||||||
svc_start(svc);
|
return start(svc, user_data);
|
||||||
|
|
||||||
|
service_stop(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -225,10 +228,44 @@ static svc_t *do_find_byc(char *buf, size_t len)
|
|||||||
return NULL;
|
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;
|
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) {
|
switch (cmd) {
|
||||||
case INIT_CMD_REBOOT:
|
case INIT_CMD_REBOOT:
|
||||||
_d("reboot");
|
_d("reboot");
|
||||||
@@ -261,7 +298,7 @@ static int do_reboot(int cmd, char *buf, size_t len)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
rc = 255;
|
/* Handled previously */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,6 +356,10 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
if (EAGAIN == errno)
|
if (EAGAIN == errno)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* we get here when client restarts itself */
|
||||||
|
if (ECONNRESET == errno)
|
||||||
|
break;
|
||||||
|
|
||||||
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
_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));
|
result = do_reload(rq.data, sizeof(rq.data));
|
||||||
break;
|
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:
|
case INIT_CMD_GET_RUNLEVEL:
|
||||||
_d("get runlevel");
|
_d("get runlevel");
|
||||||
rq.runlevel = 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_HALT:
|
||||||
case INIT_CMD_POWEROFF:
|
case INIT_CMD_POWEROFF:
|
||||||
case INIT_CMD_SUSPEND:
|
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;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_ACK:
|
case INIT_CMD_ACK:
|
||||||
|
|||||||
@@ -511,6 +511,9 @@ void cgroup_init(uev_ctx_t *ctx)
|
|||||||
|
|
||||||
/* prepare cgroup.events watcher */
|
/* prepare cgroup.events watcher */
|
||||||
fd = iwatch_init(&iw_cgroup);
|
fd = iwatch_init(&iw_cgroup);
|
||||||
|
if (fd == -1)
|
||||||
|
goto abort;
|
||||||
|
|
||||||
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
||||||
_pe("Failed setting up cgroup.events watcher");
|
_pe("Failed setting up cgroup.events watcher");
|
||||||
iwatch_exit(&iw_cgroup);
|
iwatch_exit(&iw_cgroup);
|
||||||
|
|||||||
+9
-8
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <signal.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);
|
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
|
||||||
fp = fopen(fn, "r");
|
fp = fopen(fn, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
err(1, "Cannot open %s", fn);
|
ERR(72, "Cannot open %s", fn);
|
||||||
|
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
uint64_t curr;
|
uint64_t curr;
|
||||||
@@ -280,13 +279,13 @@ static struct cg *append(char *path)
|
|||||||
if (access(fn, F_OK)) {
|
if (access(fn, F_OK)) {
|
||||||
/* older kernels, 4.19, don't have summary cpu.stat in root */
|
/* older kernels, 4.19, don't have summary cpu.stat in root */
|
||||||
if (strcmp(path, FINIT_CGPATH))
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cg = calloc(1, sizeof(struct cg));
|
cg = calloc(1, sizeof(struct cg));
|
||||||
if (!cg)
|
if (!cg)
|
||||||
err(1, "failed allocating struct cg");
|
ERR(71, "failed allocating struct cg");
|
||||||
|
|
||||||
cg->cg_path = strdup(path);
|
cg->cg_path = strdup(path);
|
||||||
if (list)
|
if (list)
|
||||||
@@ -296,7 +295,7 @@ static struct cg *append(char *path)
|
|||||||
item.key = cg->cg_path;
|
item.key = cg->cg_path;
|
||||||
item.data = cg;
|
item.data = cg;
|
||||||
if (!hsearch(item, ENTER))
|
if (!hsearch(item, ENTER))
|
||||||
err(1, "failed adding to hash table");
|
ERR(70, "failed adding to hash table");
|
||||||
|
|
||||||
return cg;
|
return cg;
|
||||||
}
|
}
|
||||||
@@ -434,7 +433,6 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
|||||||
pid_comm(pid, comm, sizeof(comm));
|
pid_comm(pid, comm, sizeof(comm));
|
||||||
if (pid_cmdline(pid, buf, sizeof(buf))) {
|
if (pid_cmdline(pid, buf, sizeof(buf))) {
|
||||||
char proc[ttcols];
|
char proc[ttcols];
|
||||||
int len;
|
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -456,6 +454,8 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
|||||||
if (plain) {
|
if (plain) {
|
||||||
strlcat(row, proc, rplen);
|
strlcat(row, proc, rplen);
|
||||||
} else {
|
} else {
|
||||||
|
int len;
|
||||||
|
|
||||||
strlcat(row, CDIM, rlen);
|
strlcat(row, CDIM, rlen);
|
||||||
strlcat(row, proc, rlen);
|
strlcat(row, proc, rlen);
|
||||||
|
|
||||||
@@ -600,7 +600,6 @@ int show_cgtop(char *arg)
|
|||||||
uev_t timer, input, sigint, sigterm, sigquit;
|
uev_t timer, input, sigint, sigterm, sigquit;
|
||||||
char path[512];
|
char path[512];
|
||||||
uev_ctx_t ctx;
|
uev_ctx_t ctx;
|
||||||
int flags;
|
|
||||||
|
|
||||||
if (!arg)
|
if (!arg)
|
||||||
arg = FINIT_CGPATH;
|
arg = FINIT_CGPATH;
|
||||||
@@ -610,7 +609,7 @@ int show_cgtop(char *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hcreate(ttrows + 25))
|
if (!hcreate(ttrows + 25))
|
||||||
err(1, "failed creating hash table");
|
ERR(70, "failed creating hash table");
|
||||||
|
|
||||||
sysinfo(&si);
|
sysinfo(&si);
|
||||||
total_ram = si.totalram * si.mem_unit;
|
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);
|
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
|
||||||
|
|
||||||
if (!ionce && !plain) {
|
if (!ionce && !plain) {
|
||||||
|
int flags;
|
||||||
|
|
||||||
atexit(cleanup);
|
atexit(cleanup);
|
||||||
ttraw();
|
ttraw();
|
||||||
hidecursor();
|
hidecursor();
|
||||||
|
|||||||
+14
-19
@@ -21,13 +21,13 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "initctl.h"
|
||||||
|
|
||||||
static int sd = -1;
|
static int sd = -1;
|
||||||
|
|
||||||
@@ -40,13 +40,13 @@ int client_connect(void)
|
|||||||
|
|
||||||
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||||
if (-1 == sd) {
|
if (-1 == sd) {
|
||||||
warn("Failed creating UNIX domain socket");
|
WARN("Failed creating UNIX domain socket");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||||
if (errno != ENOENT)
|
if (errno != ENOENT)
|
||||||
warnx("Failed connecting to finit");
|
WARNX("Failed connecting to finit");
|
||||||
close(sd);
|
close(sd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -72,22 +72,21 @@ int client_disconnect(void)
|
|||||||
int client_send(struct init_request *rq, ssize_t len)
|
int client_send(struct init_request *rq, ssize_t len)
|
||||||
{
|
{
|
||||||
struct pollfd pfd = { 0 };
|
struct pollfd pfd = { 0 };
|
||||||
int sd, result = 255;
|
int result = 255;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
sd = client_connect();
|
if (client_connect() == -1)
|
||||||
if (-1 == sd)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
pfd.fd = sd;
|
pfd.fd = sd;
|
||||||
pfd.events = POLLOUT;
|
pfd.events = POLLOUT;
|
||||||
if (poll(&pfd, 1, 2000) <= 0) {
|
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;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write(sd, rq, len) != len) {
|
if (write(sd, rq, len) != len) {
|
||||||
warn("Failed communicating with Finit, errno %d", errno);
|
WARN("Failed communicating with Finit, errno %d", errno);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,14 +96,14 @@ int client_send(struct init_request *rq, ssize_t len)
|
|||||||
if (rc) {
|
if (rc) {
|
||||||
if (errno == EINTR) /* shutdown/reboot */
|
if (errno == EINTR) /* shutdown/reboot */
|
||||||
goto exit;
|
goto exit;
|
||||||
warn("poll(), errno %d", errno);
|
WARN("poll(), errno %d", errno);
|
||||||
} else
|
} else
|
||||||
warnx("Timed out waiting for reply from Finit.");
|
WARNX("Timed out waiting for reply from Finit.");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read(sd, rq, len) != len) {
|
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;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,15 +119,13 @@ exit:
|
|||||||
|
|
||||||
svc_t *client_svc_iterator(int first)
|
svc_t *client_svc_iterator(int first)
|
||||||
{
|
{
|
||||||
int sd = -1;
|
|
||||||
struct init_request rq = {
|
struct init_request rq = {
|
||||||
.magic = INIT_MAGIC,
|
.magic = INIT_MAGIC,
|
||||||
.cmd = INIT_CMD_SVC_ITER,
|
.cmd = INIT_CMD_SVC_ITER,
|
||||||
};
|
};
|
||||||
static svc_t svc;
|
static svc_t svc;
|
||||||
|
|
||||||
sd = client_connect();
|
if (client_connect() == -1)
|
||||||
if (sd == -1)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
@@ -147,7 +144,7 @@ svc_t *client_svc_iterator(int first)
|
|||||||
|
|
||||||
return &svc;
|
return &svc;
|
||||||
error:
|
error:
|
||||||
perror("Failed communicating with finit");
|
WARN("Failed communicating with finit, error %d", errno);
|
||||||
client_disconnect();
|
client_disconnect();
|
||||||
sd = -1;
|
sd = -1;
|
||||||
|
|
||||||
@@ -161,10 +158,8 @@ svc_t *do_cmd(int cmd, const char *arg)
|
|||||||
.cmd = cmd,
|
.cmd = cmd,
|
||||||
};
|
};
|
||||||
static svc_t svc;
|
static svc_t svc;
|
||||||
int sd = -1;
|
|
||||||
|
|
||||||
sd = client_connect();
|
if (client_connect() == -1)
|
||||||
if (sd == -1)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||||
@@ -180,7 +175,7 @@ svc_t *do_cmd(int cmd, const char *arg)
|
|||||||
return &svc;
|
return &svc;
|
||||||
error:
|
error:
|
||||||
client_disconnect();
|
client_disconnect();
|
||||||
perror("Failed communicating with finit");
|
WARN("Failed communicating with finit, error %d", errno);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
+70
-2
@@ -36,6 +36,67 @@
|
|||||||
#include "pid.h"
|
#include "pid.h"
|
||||||
#include "service.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 service condition name is constructed from the 'pid/' prefix and
|
||||||
* the unique NAME:ID tuple that identify each process in Finit. Here
|
* the unique NAME:ID tuple that identify each process in Finit. Here
|
||||||
@@ -145,11 +206,17 @@ static void cond_delpath(const char *path)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
while ((d = readdir(dir))) {
|
while ((d = readdir(dir))) {
|
||||||
|
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
|
||||||
paste(fn, sizeof(fn), path, d->d_name);
|
paste(fn, sizeof(fn), path, d->d_name);
|
||||||
remove(fn);
|
if (remove(fn))
|
||||||
|
_pe("Failed removing condition path, file %s", fn);
|
||||||
}
|
}
|
||||||
closedir(dir);
|
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)
|
int cond_set_path(const char *path, enum cond_state next)
|
||||||
@@ -360,6 +427,7 @@ void cond_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cond_bump_reconf();
|
cond_bump_reconf();
|
||||||
|
cond_boot_strap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
-1
@@ -31,6 +31,7 @@ enum cond_state cond_get (const char *name);
|
|||||||
enum cond_state cond_get_agg (const char *names);
|
enum cond_state cond_get_agg (const char *names);
|
||||||
int cond_affects (const char *name, const char *names);
|
int cond_affects (const char *name, const char *names);
|
||||||
|
|
||||||
|
void cond_boot_parse (char *arg);
|
||||||
int cond_update (const char *name);
|
int cond_update (const char *name);
|
||||||
int cond_set_path (const char *path, enum cond_state new);
|
int cond_set_path (const char *path, enum cond_state new);
|
||||||
void cond_set (const char *name);
|
void cond_set (const char *name);
|
||||||
@@ -38,7 +39,7 @@ void cond_set_oneshot (const char *name);
|
|||||||
void cond_clear (const char *name);
|
void cond_clear (const char *name);
|
||||||
void cond_reload (void);
|
void cond_reload (void);
|
||||||
|
|
||||||
int cond_set_noupdate (const char *name);
|
int cond_set_noupdate(const char *name);
|
||||||
int cond_set_oneshot_noupdate(const char *name);
|
int cond_set_oneshot_noupdate(const char *name);
|
||||||
int cond_clear_noupdate(const char *name);
|
int cond_clear_noupdate(const char *name);
|
||||||
|
|
||||||
|
|||||||
+10
-5
@@ -48,8 +48,6 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define BOOTSTRAP (runlevel == 0)
|
#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_size_max = 200000; /* 200 kB */
|
||||||
int logfile_count_max = 5;
|
int logfile_count_max = 5;
|
||||||
@@ -115,6 +113,11 @@ static void parse_finit_opts(char *opt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string_compare(opt, "cond")) {
|
||||||
|
cond_boot_parse(arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (string_compare(opt, "status_style")) {
|
if (string_compare(opt, "status_style")) {
|
||||||
if (!arg) {
|
if (!arg) {
|
||||||
_e("status_style option requires an argument, skipping.");
|
_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))
|
if (svc_is_daemon(svc))
|
||||||
svc->sighup = 1;
|
svc->sighup = 1;
|
||||||
|
|
||||||
if (!cond)
|
if (!cond) {
|
||||||
|
memset(svc->cond, 0, sizeof(svc->cond));
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* First character must be '!' if SIGHUP is not supported. */
|
/* First character must be '!' if SIGHUP is not supported. */
|
||||||
ptr = cond;
|
ptr = cond;
|
||||||
@@ -454,12 +459,12 @@ void conf_parse_cond(svc_t *svc, char *cond)
|
|||||||
ptr[i] = 0;
|
ptr[i] = 0;
|
||||||
|
|
||||||
if (i >= sizeof(svc->cond)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp(ptr, "svc/", 4)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -329,7 +329,7 @@ static int activate_console(int noclear, int nowait)
|
|||||||
static const char clr[] = "\r\e[K";
|
static const char clr[] = "\r\e[K";
|
||||||
static const char cup[] = "\e[A";
|
static const char cup[] = "\e[A";
|
||||||
struct termios orig;
|
struct termios orig;
|
||||||
char c;
|
char ch;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (nowait || rescue)
|
if (nowait || rescue)
|
||||||
@@ -351,7 +351,7 @@ static int activate_console(int noclear, int nowait)
|
|||||||
|
|
||||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||||
dprint(STDERR_FILENO, msg, strlen(msg));
|
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;
|
continue;
|
||||||
|
|
||||||
/* On any error (likely EINTR), we avoid starting getty */
|
/* On any error (likely EINTR), we avoid starting getty */
|
||||||
|
|||||||
+4
-1
@@ -122,6 +122,9 @@ static int sulogin(int do_reboot)
|
|||||||
for (i = 0; i < NELEMS(cmd); i++) {
|
for (i = 0; i < NELEMS(cmd); i++) {
|
||||||
char *path = which(cmd[i]);
|
char *path = which(cmd[i]);
|
||||||
|
|
||||||
|
if (!path)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (access(path, X_OK)) {
|
if (access(path, X_OK)) {
|
||||||
free(path);
|
free(path);
|
||||||
continue;
|
continue;
|
||||||
@@ -574,7 +577,7 @@ static void bootstrap_worker(void *work)
|
|||||||
.cb = finalize,
|
.cb = finalize,
|
||||||
.delay = 10
|
.delay = 10
|
||||||
};
|
};
|
||||||
static int cnt = 120;
|
static int cnt = 120 * 10; /* We run with 100ms period */
|
||||||
int level = cfglevel;
|
int level = cfglevel;
|
||||||
|
|
||||||
_d("Step all services ...");
|
_d("Step all services ...");
|
||||||
|
|||||||
+2
-2
@@ -83,8 +83,8 @@
|
|||||||
#define INIT_CMD_STOP_SVC 11
|
#define INIT_CMD_STOP_SVC 11
|
||||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||||
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
|
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
|
||||||
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
|
#define INIT_CMD_GET_PLUGINS 14 /* Fill data[] with loaded plugins */
|
||||||
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
#define INIT_CMD_PLUGIN_DEPS 15 /* Fill data[] with plugin deps */
|
||||||
#define INIT_CMD_GET_RUNLEVEL 16
|
#define INIT_CMD_GET_RUNLEVEL 16
|
||||||
#define INIT_CMD_REBOOT 20
|
#define INIT_CMD_REBOOT 20
|
||||||
#define INIT_CMD_HALT 21
|
#define INIT_CMD_HALT 21
|
||||||
|
|||||||
+8
-1
@@ -49,6 +49,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static long logname_len = 32; /* useradd(1) limit at 32 chars */
|
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.
|
* Read one character from stdin.
|
||||||
@@ -185,6 +186,9 @@ static int do_login(char *name)
|
|||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
|
if (passenv)
|
||||||
|
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL);
|
||||||
|
else
|
||||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -252,11 +256,14 @@ int main(int argc, char *argv[])
|
|||||||
char *tty, *speed = "0", *term = NULL;
|
char *tty, *speed = "0", *term = NULL;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "h?")) != EOF) {
|
while ((c = getopt(argc, argv, "h?p")) != EOF) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
return usage(0);
|
return usage(0);
|
||||||
|
case 'p':
|
||||||
|
passenv = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return usage(1);
|
return usage(1);
|
||||||
}
|
}
|
||||||
|
|||||||
+177
-118
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
#include <ftw.h>
|
#include <ftw.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@@ -39,6 +38,7 @@
|
|||||||
# include <lite/lite.h>
|
# include <lite/lite.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "initctl.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "serv.h"
|
#include "serv.h"
|
||||||
@@ -64,6 +64,7 @@ int verbose = 0;
|
|||||||
int plain = 0;
|
int plain = 0;
|
||||||
int quiet = 0;
|
int quiet = 0;
|
||||||
int runlevel = 0;
|
int runlevel = 0;
|
||||||
|
int timeout = 0;
|
||||||
int cgrp = 0;
|
int cgrp = 0;
|
||||||
int utmp = 0;
|
int utmp = 0;
|
||||||
|
|
||||||
@@ -154,12 +155,21 @@ static int toggle_debug(char *arg)
|
|||||||
return client_send(&rq, sizeof(rq));
|
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])
|
if (svc) {
|
||||||
svc = "finit";
|
nm = svc_ident(svc, NULL, 0);
|
||||||
|
pid = svc->pid;
|
||||||
|
if (!pid)
|
||||||
|
return 0; /* not running */
|
||||||
|
} else {
|
||||||
|
nm = "finit";
|
||||||
|
pid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fexist(logfile)) {
|
if (!fexist(logfile)) {
|
||||||
logfile = "/var/log/messages";
|
logfile = "/var/log/messages";
|
||||||
@@ -167,7 +177,20 @@ static int do_log(char *svc)
|
|||||||
return 0; /* bail out, maybe in container */
|
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)
|
static int do_runlevel(char *arg)
|
||||||
@@ -229,20 +252,11 @@ static int do_startstop(int cmd, char *arg)
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!arg || !arg[0])
|
if (!arg || !arg[0])
|
||||||
errx(1, "missing argument to %s", (cmd == INIT_CMD_START_SVC
|
ERRX(2, "missing command argument");
|
||||||
? "start"
|
|
||||||
: (cmd == INIT_CMD_STOP_SVC
|
|
||||||
? "stop"
|
|
||||||
: "restart")));
|
|
||||||
|
|
||||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||||
if (client_send(&rq, sizeof(rq))) {
|
if (client_send(&rq, sizeof(rq)))
|
||||||
fprintf(stderr, "No such task or service(s): %s\n\n", arg);
|
ERRX(69, "no such task or service(s): %s", arg);
|
||||||
fprintf(stderr, "Usage: initctl %s <NAME>[:ID]\n",
|
|
||||||
cmd == INIT_CMD_START_SVC ? "start" :
|
|
||||||
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return do_svc(cmd, arg);
|
return do_svc(cmd, arg);
|
||||||
}
|
}
|
||||||
@@ -260,23 +274,10 @@ static int do_reload (char *arg)
|
|||||||
|
|
||||||
static int do_restart(char *arg)
|
static int do_restart(char *arg)
|
||||||
{
|
{
|
||||||
size_t retries = 3;
|
if (do_startstop(INIT_CMD_RESTART_SVC, arg))
|
||||||
svc_t *svc;
|
ERRX(7, "failed restarting %s", arg);
|
||||||
|
|
||||||
if (do_startstop(INIT_CMD_STOP_SVC, arg))
|
return 0;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,27 +299,19 @@ int do_signal(int argc, char *argv[])
|
|||||||
int signo;
|
int signo;
|
||||||
|
|
||||||
if (argc != 2)
|
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));
|
strlcpy(rq.data, argv[0], sizeof(rq.data));
|
||||||
if (client_send(&rq, sizeof(rq))) {
|
if (client_send(&rq, sizeof(rq)))
|
||||||
fprintf(stderr, "No such task or service(s): %s\n\n", argv[0]);
|
ERRX(69, "no such task or service(s): %s", argv[0]);
|
||||||
goto show_usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate signal name (or number) */
|
|
||||||
signo = str2sig(argv[1]);
|
signo = str2sig(argv[1]);
|
||||||
if (signo == -1) {
|
if (signo == -1) {
|
||||||
/* Not a signal name, is it an actual signal number? */
|
const char *errstr = NULL;
|
||||||
errno = 0;
|
|
||||||
signo = (int)strtol(argv[1], NULL, 10);
|
|
||||||
|
|
||||||
/* Was it a number? Was it a signo that finit recognizes? */
|
signo = (int)strtonum(argv[1], 1, 31, &errstr);
|
||||||
if (errno || !*(sig2str(signo))) {
|
if (errstr)
|
||||||
fprintf(stderr, "Not a valid signal (or signo): %s\n", argv[1]);
|
ERRX(65, "%s signal: %s", errstr, argv[1]);
|
||||||
goto show_usage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reuse runlevel for signal number. */
|
/* Reuse runlevel for signal number. */
|
||||||
@@ -328,12 +321,9 @@ int do_signal(int argc, char *argv[])
|
|||||||
strlcpy(rq.data, argv[0], sizeof(rq.data));
|
strlcpy(rq.data, argv[0], sizeof(rq.data));
|
||||||
|
|
||||||
return client_send(&rq, sizeof(rq));
|
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)
|
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||||
{
|
{
|
||||||
const char *cond, *asserted;
|
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));
|
asserted = condstr(cond_get_path(fpath));
|
||||||
cond = &fpath[strlen(_PATH_COND)];
|
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) {
|
if (strncmp("pid/", cond, 4) == 0) {
|
||||||
svc_t *svc;
|
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)
|
static int do_cond_dump(char *arg)
|
||||||
{
|
{
|
||||||
col_widths();
|
col_widths();
|
||||||
if (heading) {
|
if (heading)
|
||||||
char title[80];
|
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
||||||
|
"STATUS", "CONDITION");
|
||||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
|
||||||
iw, "IDENT", "STATUS", "CONDITION");
|
|
||||||
print_header(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
dump_filter = arg;
|
||||||
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
|
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 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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;
|
size_t off;
|
||||||
|
|
||||||
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
|
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
|
||||||
arg += strlen(COND_USR);
|
arg += strlen(COND_USR);
|
||||||
|
|
||||||
if (!arg || !arg[0])
|
if (!arg || !arg[0])
|
||||||
errx(1, "Invalid condition (empty)");
|
ERRX(2, "Invalid condition (empty)");
|
||||||
if (strchr(arg, '/'))
|
|
||||||
errx(1, "Invalid condition (slashes)");
|
|
||||||
if (strchr(arg, '.'))
|
|
||||||
errx(1, "Invalid condition (periods)");
|
|
||||||
|
|
||||||
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);
|
off = strlen(_PATH_COND);
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case 2:
|
case COND_GET:
|
||||||
off = !fexist(oneshot);
|
off = !fexist(path);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
puts(off ? "off" : "on");
|
puts(off ? "off" : "on");
|
||||||
return off;
|
return off;
|
||||||
case 1:
|
case COND_SET:
|
||||||
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
|
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||||
err(1, "Failed asserting condition <%s>", &oneshot[off]);
|
ERR(73, "Failed asserting condition <%s>", &path[off]);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case COND_CLR:
|
||||||
if (erase(oneshot) && errno != ENOENT)
|
if (erase(path) && errno != ENOENT)
|
||||||
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
|
ERR(73, "Failed deasserting condition <%s>", &path[off]);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errx(1, "Unsupported operation (%d)", op);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_cond_get(char *arg) { return do_cond_act(arg, 2); }
|
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, 1); }
|
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, 0); }
|
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)
|
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;
|
svc_t *svc;
|
||||||
|
|
||||||
col_widths();
|
col_widths();
|
||||||
if (heading) {
|
if (heading)
|
||||||
char title[80];
|
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
||||||
|
"STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
||||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
|
||||||
iw, "IDENT", "STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
|
||||||
print_header(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||||
if (!svc->cond[0])
|
if (!svc->cond[0])
|
||||||
@@ -525,6 +518,7 @@ static int do_cmd(int cmd)
|
|||||||
struct init_request rq = {
|
struct init_request rq = {
|
||||||
.magic = INIT_MAGIC,
|
.magic = INIT_MAGIC,
|
||||||
.cmd = cmd,
|
.cmd = cmd,
|
||||||
|
.sleeptime = timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (client_send(&rq, sizeof(rq))) {
|
if (client_send(&rq, sizeof(rq))) {
|
||||||
@@ -611,6 +605,44 @@ static int show_version(char *arg)
|
|||||||
return 0;
|
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
|
* runlevel_string - Convert a bit encoded runlevel to .conf syntax
|
||||||
* @levels: Bit encoded runlevels
|
* @levels: Bit encoded runlevels
|
||||||
@@ -702,16 +734,17 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len)
|
|||||||
return buf;
|
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;
|
int rc, sig;
|
||||||
|
|
||||||
rc = WEXITSTATUS(status);
|
rc = WEXITSTATUS(svc->status);
|
||||||
sig = WTERMSIG(status);
|
sig = WTERMSIG(svc->status);
|
||||||
|
|
||||||
if (WIFEXITED(status))
|
if (WIFEXITED(svc->status))
|
||||||
snprintf(buf, len, " (code=exited, status=%d%s)", rc, code2str(rc));
|
snprintf(buf, len, " (code=exited, status=%d%s%s)", rc, code2str(rc),
|
||||||
else if (WIFSIGNALED(status))
|
svc->manual ? ", manual=yes" : "");
|
||||||
|
else if (WIFSIGNALED(svc->status))
|
||||||
snprintf(buf, len, " (code=signal, status=%d%s)", sig, sig2str(sig));
|
snprintf(buf, len, " (code=signal, status=%d%s)", sig, sig2str(sig));
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
@@ -727,7 +760,7 @@ static char *status(svc_t *svc, int full)
|
|||||||
s = svc_status(svc);
|
s = svc_status(svc);
|
||||||
switch (svc->state) {
|
switch (svc->state) {
|
||||||
case SVC_HALTED_STATE:
|
case SVC_HALTED_STATE:
|
||||||
exit_status(svc->status, ok, sizeof(ok));
|
exit_status(svc, ok, sizeof(ok));
|
||||||
color = "\e[1m";
|
color = "\e[1m";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -736,7 +769,7 @@ static char *status(svc_t *svc, int full)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_DONE_STATE:
|
case SVC_DONE_STATE:
|
||||||
exit_status(svc->status, ok, sizeof(ok));
|
exit_status(svc, ok, sizeof(ok));
|
||||||
if (WIFEXITED(svc->status)) {
|
if (WIFEXITED(svc->status)) {
|
||||||
if (WEXITSTATUS(svc->status))
|
if (WEXITSTATUS(svc->status))
|
||||||
color = "\e[1;31m";
|
color = "\e[1;31m";
|
||||||
@@ -751,7 +784,7 @@ static char *status(svc_t *svc, int full)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
exit_status(svc->status, ok, sizeof(ok));
|
exit_status(svc, ok, sizeof(ok));
|
||||||
color = "\e[1;33m";
|
color = "\e[1;33m";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -783,6 +816,28 @@ static void show_cgroup_tree(char *group, char *pfx)
|
|||||||
cgroup_tree(path, pfx, 0, 0);
|
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)
|
static int show_status(char *arg)
|
||||||
{
|
{
|
||||||
char ident[MAX_IDENT_LEN];
|
char ident[MAX_IDENT_LEN];
|
||||||
@@ -796,22 +851,16 @@ static int show_status(char *arg)
|
|||||||
long now = jiffies();
|
long now = jiffies();
|
||||||
char uptm[42] = "N/A";
|
char uptm[42] = "N/A";
|
||||||
char *pidfn = NULL;
|
char *pidfn = NULL;
|
||||||
int exact = 0;
|
|
||||||
|
|
||||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0))
|
||||||
svc_ident(svc, ident, sizeof(ident));
|
num += svc_compare(svc, arg);
|
||||||
if (string_compare(ident, arg))
|
|
||||||
num++;
|
|
||||||
if (string_case_compare(ident, arg))
|
|
||||||
exact++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num > 1 && !exact)
|
if (num > 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
svc = client_svc_find(arg);
|
svc = client_svc_find(arg);
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 255;
|
ERRX(69, "no such task or service(s): %s", arg);
|
||||||
|
|
||||||
if (quiet) {
|
if (quiet) {
|
||||||
if (svc_is_runtask(svc))
|
if (svc_is_runtask(svc))
|
||||||
@@ -841,6 +890,8 @@ static int show_status(char *arg)
|
|||||||
printf(" User : %s\n", svc->username);
|
printf(" User : %s\n", svc->username);
|
||||||
printf(" Group : %s\n", svc->group);
|
printf(" Group : %s\n", svc->group);
|
||||||
printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
|
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(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
|
||||||
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||||
if (cgrp && svc->pid > 1) {
|
if (cgrp && svc->pid > 1) {
|
||||||
@@ -864,7 +915,7 @@ static int show_status(char *arg)
|
|||||||
no_cgroup:
|
no_cgroup:
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
return do_log(svc->cmd);
|
return do_log(svc, "| tail -10");
|
||||||
}
|
}
|
||||||
|
|
||||||
col_widths();
|
col_widths();
|
||||||
@@ -878,14 +929,14 @@ static int show_status(char *arg)
|
|||||||
else
|
else
|
||||||
strlcat(title, "COMMAND", sizeof(title));
|
strlcat(title, "COMMAND", sizeof(title));
|
||||||
|
|
||||||
print_header(title);
|
print_header("%s", title);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||||
char *lvls;
|
char *lvls;
|
||||||
|
|
||||||
svc_ident(svc, ident, sizeof(ident));
|
svc_ident(svc, ident, sizeof(ident));
|
||||||
if (num && !string_compare(ident, arg))
|
if (num && !svc_compare(svc, arg))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
printf("%-*d ", pw, svc->pid);
|
printf("%-*d ", pw, svc->pid);
|
||||||
@@ -974,13 +1025,13 @@ static int usage(int rc)
|
|||||||
" -b, --batch Batch mode, no screen size probing\n"
|
" -b, --batch Batch mode, no screen size probing\n"
|
||||||
" -c, --create Create missing paths (and files) as needed\n"
|
" -c, --create Create missing paths (and files) as needed\n"
|
||||||
" -f, --force Ignore missing files and arguments, never prompt\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"
|
" -1, --once Only one lap in commands like 'top'\n"
|
||||||
" -p, --plain Use plain table headings, no ctrl chars\n"
|
" -p, --plain Use plain table headings, no ctrl chars\n"
|
||||||
" -q, --quiet Silent, only return status of command\n"
|
" -q, --quiet Silent, only return status of command\n"
|
||||||
" -t, --no-heading Skip table headings\n"
|
" -t, --no-heading Skip table headings\n"
|
||||||
" -v, --verbose Verbose output\n"
|
" -v, --verbose Verbose output\n"
|
||||||
" -V, --version Show program version\n"
|
" -V, --version Show program version\n"
|
||||||
" -h, --help This help text\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"Commands:\n"
|
"Commands:\n"
|
||||||
" debug Toggle Finit (daemon) debug\n"
|
" debug Toggle Finit (daemon) debug\n"
|
||||||
@@ -1015,18 +1066,19 @@ static int usage(int rc)
|
|||||||
" reload Reload " FINIT_CONF " (activate changes)\n");
|
" reload Reload " FINIT_CONF " (activate changes)\n");
|
||||||
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" cond set <COND> Set (assert) user-defined condition +usr/COND\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 get <COND> Get status of user-defined condition, see $? and -v\n"
|
||||||
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
|
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
|
||||||
" cond status Show condition status, default cond command\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"
|
"\n"
|
||||||
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
|
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
|
||||||
" start <NAME>[:ID] Start service by name, with optional ID\n"
|
" start <NAME>[:ID] Start service by name, with optional ID\n"
|
||||||
" stop <NAME>[:ID] Stop/Pause a running service by name\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"
|
" 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"
|
" 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"
|
" 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"
|
" ps List processes based on cgroups\n"
|
||||||
" top Show top-like listing based on cgroups\n");
|
" top Show top-like listing based on cgroups\n");
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
"\n"
|
||||||
|
" plugins List installed plugins\n"
|
||||||
"\n"
|
"\n"
|
||||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||||
" reboot Reboot system\n"
|
" reboot Reboot system\n"
|
||||||
@@ -1108,7 +1162,7 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argv[0] && strlen(argv[0]) > 0)
|
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 */
|
return command[0].cb(NULL); /* default cmd */
|
||||||
}
|
}
|
||||||
@@ -1160,7 +1214,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
{ "cond", cond, NULL, NULL, NULL },
|
{ "cond", cond, NULL, NULL, NULL },
|
||||||
|
|
||||||
{ "log", NULL, do_log, NULL, NULL },
|
{ "log", NULL, show_log, NULL, NULL },
|
||||||
{ "start", NULL, do_start, NULL, NULL },
|
{ "start", NULL, do_start, NULL, NULL },
|
||||||
{ "stop", NULL, do_stop, NULL, NULL },
|
{ "stop", NULL, do_stop, NULL, NULL },
|
||||||
{ "restart", NULL, do_restart, NULL, NULL },
|
{ "restart", NULL, do_restart, NULL, NULL },
|
||||||
@@ -1170,6 +1224,8 @@ int main(int argc, char *argv[])
|
|||||||
{ "ps", NULL, show_cgps, &cgrp, NULL },
|
{ "ps", NULL, show_cgps, &cgrp, NULL },
|
||||||
{ "top", NULL, show_cgtop, &cgrp, NULL },
|
{ "top", NULL, show_cgtop, &cgrp, NULL },
|
||||||
|
|
||||||
|
{ "plugins", NULL, plugins_list, NULL, NULL },
|
||||||
|
|
||||||
{ "runlevel", NULL, do_runlevel, NULL, NULL },
|
{ "runlevel", NULL, do_runlevel, NULL, NULL },
|
||||||
{ "reboot", NULL, do_reboot, NULL, NULL },
|
{ "reboot", NULL, do_reboot, NULL, NULL },
|
||||||
{ "halt", NULL, do_halt, NULL, NULL },
|
{ "halt", NULL, do_halt, NULL, NULL },
|
||||||
@@ -1245,6 +1301,9 @@ void logit(int prio, const char *fmt, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
|
if (quiet)
|
||||||
|
return;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
if (prio <= LOG_ERR)
|
if (prio <= LOG_ERR)
|
||||||
verrx(1, fmt, ap);
|
verrx(1, fmt, ap);
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#ifndef FINIT_INITCTL_H_
|
#ifndef FINIT_INITCTL_H_
|
||||||
#define FINIT_INITCTL_H_
|
#define FINIT_INITCTL_H_
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -33,6 +35,12 @@ extern int ionce; /* initctl -1 */
|
|||||||
extern int heading; /* initctl -t */
|
extern int heading; /* initctl -t */
|
||||||
extern int verbose; /* initctl -v */
|
extern int verbose; /* initctl -v */
|
||||||
extern int plain; /* initctl -p */
|
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)));
|
extern void print_header(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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 logrotate(char *file, int num, off_t sz)
|
||||||
{
|
{
|
||||||
int cnt;
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (stat(file, &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;
|
size_t len = strlen(file) + 10 + 1;
|
||||||
char ofile[len];
|
char ofile[len];
|
||||||
char nfile[len];
|
char nfile[len];
|
||||||
|
int cnt;
|
||||||
|
|
||||||
/* First age zipped log files */
|
/* First age zipped log files */
|
||||||
for (cnt = num; cnt > 2; cnt--) {
|
for (cnt = num; cnt > 2; cnt--) {
|
||||||
|
|||||||
+62
-6
@@ -260,8 +260,8 @@ int plugin_io_init(plugin_t *p)
|
|||||||
/* Setup any I/O callbacks for plugins that use them */
|
/* Setup any I/O callbacks for plugins that use them */
|
||||||
static int init_plugins(uev_ctx_t *ctx)
|
static int init_plugins(uev_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
int fail = 0;
|
|
||||||
plugin_t *p, *tmp;
|
plugin_t *p, *tmp;
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
PLUGIN_ITERATOR(p, tmp) {
|
PLUGIN_ITERATOR(p, tmp) {
|
||||||
if (plugin_io_init(p))
|
if (plugin_io_init(p))
|
||||||
@@ -302,7 +302,7 @@ static int load_one(char *path, char *name)
|
|||||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||||
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".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);
|
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
_e("Failed loading plugin %s: %s", sofile, dlerror());
|
_e("Failed loading plugin %s: %s", sofile, dlerror());
|
||||||
@@ -356,6 +356,8 @@ static int load_plugins(char *path)
|
|||||||
|
|
||||||
dp = opendir(path);
|
dp = opendir(path);
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
return 0;
|
||||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -381,14 +383,68 @@ static int load_plugins(char *path)
|
|||||||
}
|
}
|
||||||
#endif /* ENABLE_STATIC */
|
#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 plugin_init(uev_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
int fail = 1;
|
load_plugins(PLUGIN_PATH);
|
||||||
|
|
||||||
if (!load_plugins(PLUGIN_PATH))
|
#ifdef EXTERNAL_PLUGIN_PATH
|
||||||
fail = init_plugins(ctx);
|
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)
|
void plugin_exit(void)
|
||||||
|
|||||||
@@ -146,6 +146,9 @@ typedef struct plugin {
|
|||||||
/* Public plugin API */
|
/* Public plugin API */
|
||||||
int plugin_io_init (plugin_t *plugin);
|
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_register (plugin_t *plugin);
|
||||||
int plugin_unregister (plugin_t *plugin);
|
int plugin_unregister (plugin_t *plugin);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
|
||||||
#define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */
|
#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;
|
extern int service_interval;
|
||||||
|
|
||||||
int api_init (uev_ctx_t *ctx);
|
int api_init (uev_ctx_t *ctx);
|
||||||
|
|||||||
+16
-4
@@ -43,6 +43,8 @@ static cmd_t cmd = CMD_UNKNOWN;
|
|||||||
static char *msg = NULL;
|
static char *msg = NULL;
|
||||||
|
|
||||||
/* initctl API */
|
/* initctl API */
|
||||||
|
extern int timeout;
|
||||||
|
|
||||||
extern int do_reboot (char *arg);
|
extern int do_reboot (char *arg);
|
||||||
extern int do_halt (char *arg);
|
extern int do_halt (char *arg);
|
||||||
extern int do_poweroff(char *arg);
|
extern int do_poweroff(char *arg);
|
||||||
@@ -103,12 +105,13 @@ static int usage(int rc)
|
|||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
|
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
" --help This help text\n"
|
" -?, --help This help text\n"
|
||||||
" -f, --force Force unsafe %s now, do not contact the init system.\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"
|
" --halt Halt system, regardless of how the command is called.\n"
|
||||||
" -h Halt or power off after shutdown.\n"
|
" -h Halt or power off after shutdown.\n"
|
||||||
" -P, --poweroff Power-off system, regardless of how the command is called.\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"
|
" -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);
|
"\n", prognm, msg);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@@ -116,20 +119,22 @@ static int usage(int rc)
|
|||||||
|
|
||||||
int reboot_main(int argc, char *argv[])
|
int reboot_main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int c, force = 0;
|
|
||||||
struct option long_options[] = {
|
struct option long_options[] = {
|
||||||
{"help", 0, NULL, 'h'},
|
{"help", 0, NULL, '?'},
|
||||||
{"force", 0, NULL, 'f'},
|
{"force", 0, NULL, 'f'},
|
||||||
{"halt", 0, NULL, 'H'},
|
{"halt", 0, NULL, 'H'},
|
||||||
{"poweroff", 0, NULL, 'p'},
|
{"poweroff", 0, NULL, 'p'},
|
||||||
{"reboot", 0, NULL, 'r'},
|
{"reboot", 0, NULL, 'r'},
|
||||||
|
{"timeout", 1, NULL, 't'},
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
int c, force = 0;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
/* Initial command taken from program name */
|
/* Initial command taken from program name */
|
||||||
transform(prognm);
|
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) {
|
switch(c) {
|
||||||
case '?':
|
case '?':
|
||||||
return usage(0);
|
return usage(0);
|
||||||
@@ -152,6 +157,13 @@ int reboot_main(int argc, char *argv[])
|
|||||||
cmd = CMD_REBOOT;
|
cmd = CMD_REBOOT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
tmp = atoi(optarg);
|
||||||
|
if (tmp < 1)
|
||||||
|
errx(1, "Invalid forced timeout value.");
|
||||||
|
timeout = tmp;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return usage(1);
|
return usage(1);
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-29
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
#include <fcntl.h> /* Definition of AT_* constants */
|
#include <fcntl.h> /* Definition of AT_* constants */
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -233,7 +232,7 @@ int serv_enable(char *arg)
|
|||||||
int ena;
|
int ena;
|
||||||
|
|
||||||
if (!arg || !arg[0]) {
|
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");
|
return serv_list("available");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,18 +242,18 @@ int serv_enable(char *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chdir(FINIT_RCSD))
|
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)
|
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. */
|
ena = !chdir("enabled"); /* System *may* have enabled/ dir. */
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg);
|
snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg);
|
||||||
if (!fexist(path))
|
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))
|
if (fexist(arg))
|
||||||
errx(1, "%s already enabled", arg);
|
ERRX(1, "%s already enabled", arg);
|
||||||
|
|
||||||
return symlink(path, arg) != 0;
|
return symlink(path, arg) != 0;
|
||||||
}
|
}
|
||||||
@@ -265,7 +264,7 @@ int do_disable(char *arg, int check)
|
|||||||
char corr[40];
|
char corr[40];
|
||||||
|
|
||||||
if (!arg || !arg[0]) {
|
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");
|
return serv_list("enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,15 +274,15 @@ int do_disable(char *arg, int check)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chdir(FINIT_RCSD))
|
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. */
|
if (chdir("enabled")) /* System *may* have enabled/ dir. */
|
||||||
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
|
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
|
||||||
|
|
||||||
if (check && stat(arg, &st))
|
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)
|
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;
|
return remove(arg) != 0;
|
||||||
}
|
}
|
||||||
@@ -299,16 +298,16 @@ int serv_touch(char *arg)
|
|||||||
char *fn;
|
char *fn;
|
||||||
|
|
||||||
if (!arg || !arg[0]) {
|
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");
|
return serv_list("enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn = conf(path, sizeof(path), arg, 0);
|
fn = conf(path, sizeof(path), arg, 0);
|
||||||
if (!fexist(fn)) {
|
if (!fexist(fn)) {
|
||||||
if (!strstr(arg, "finit.conf"))
|
if (!strstr(arg, "finit.conf"))
|
||||||
errx(1, "%s not available.", arg);
|
ERRX(72, "%s not available.", arg);
|
||||||
if (is_builtin(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));
|
strlcpy(path, FINIT_CONF, sizeof(path));
|
||||||
fn = path;
|
fn = path;
|
||||||
@@ -316,7 +315,7 @@ int serv_touch(char *arg)
|
|||||||
|
|
||||||
/* libite:touch() follows symlinks */
|
/* libite:touch() follows symlinks */
|
||||||
if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW))
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -329,9 +328,9 @@ int serv_show(char *arg)
|
|||||||
fn = conf(path, sizeof(path), arg, 0);
|
fn = conf(path, sizeof(path), arg, 0);
|
||||||
if (!fexist(fn)) {
|
if (!fexist(fn)) {
|
||||||
if (is_builtin(arg))
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,10 +365,10 @@ static int do_edit(char *arg, int creat)
|
|||||||
fn = conf(path, sizeof(path), arg, creat);
|
fn = conf(path, sizeof(path), arg, creat);
|
||||||
if (!fexist(fn)) {
|
if (!fexist(fn)) {
|
||||||
if (is_builtin(arg))
|
if (is_builtin(arg))
|
||||||
errx(1, "%s is a built-in service.", arg);
|
ERRX(4, "%s is a built-in service.", arg);
|
||||||
|
|
||||||
if (!creat) {
|
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);
|
return serv_list(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +377,7 @@ static int do_edit(char *arg, int creat)
|
|||||||
copyfile(SAMPLE_CONF, fn, 0, 0);
|
copyfile(SAMPLE_CONF, fn, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
} else if (creat)
|
} 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++) {
|
for (size_t i = 0; i < NELEMS(editor); i++) {
|
||||||
if (systemf("%s %s 2>/dev/null", editor[i], path))
|
if (systemf("%s %s 2>/dev/null", editor[i], path))
|
||||||
@@ -407,10 +406,10 @@ int serv_creat(char *arg)
|
|||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
if (!arg || !arg[0])
|
if (!arg || !arg[0])
|
||||||
errx(1, "missing argument to create");
|
ERRX(2, "missing argument to create");
|
||||||
|
|
||||||
if (is_builtin(arg))
|
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? */
|
/* Input from a pipe or a proper TTY? */
|
||||||
if (isatty(STDIN_FILENO))
|
if (isatty(STDIN_FILENO))
|
||||||
@@ -419,16 +418,16 @@ int serv_creat(char *arg)
|
|||||||
/* Open fn for writing from pipe */
|
/* Open fn for writing from pipe */
|
||||||
fn = conf(buf, sizeof(buf), arg, 1);
|
fn = conf(buf, sizeof(buf), arg, 1);
|
||||||
if (!fn)
|
if (!fn)
|
||||||
err(1, "failed creating conf %s", arg);
|
ERR(73, "failed creating conf %s", arg);
|
||||||
|
|
||||||
if (!icreate && fexist(fn)) {
|
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";
|
fn = "/dev/null";
|
||||||
}
|
}
|
||||||
|
|
||||||
fp = fopen(fn, "w");
|
fp = fopen(fn, "w");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
err(1, "failed opening %s for writing", fn);
|
ERR(73, "failed opening %s for writing", fn);
|
||||||
|
|
||||||
while (fgets(buf, sizeof(buf), stdin))
|
while (fgets(buf, sizeof(buf), stdin))
|
||||||
fputs(buf, fp);
|
fputs(buf, fp);
|
||||||
@@ -442,24 +441,24 @@ int serv_delete(char *arg)
|
|||||||
char *fn;
|
char *fn;
|
||||||
|
|
||||||
if (!arg || !arg[0]) {
|
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");
|
return serv_list("available");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn = conf(buf, sizeof(buf), arg, 0);
|
fn = conf(buf, sizeof(buf), arg, 0);
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
if (is_builtin(arg))
|
if (is_builtin(arg))
|
||||||
errx(1, "%s is a built-in service.", arg);
|
ERRX(4, "%s is a built-in service.", arg);
|
||||||
errx(1, FINIT_RCSD " missing on system.");
|
ERRX(72, FINIT_RCSD " missing on system.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fexist(fn))
|
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)) {
|
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
|
||||||
do_disable(arg, 0);
|
do_disable(arg, 0);
|
||||||
if (remove(fn))
|
if (remove(fn))
|
||||||
err(1, "Failed removing %s", fn);
|
ERR(1, "Failed removing %s", fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+94
-66
@@ -198,9 +198,10 @@ static int lredirect(svc_t *svc)
|
|||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
int fds;
|
|
||||||
char *tag = basename(svc->cmd);
|
|
||||||
char *prio = "daemon.info";
|
char *prio = "daemon.info";
|
||||||
|
char buf[MAX_IDENT_LEN];
|
||||||
|
char *tag;
|
||||||
|
int fds;
|
||||||
|
|
||||||
sched_yield();
|
sched_yield();
|
||||||
|
|
||||||
@@ -213,6 +214,9 @@ static int lredirect(svc_t *svc)
|
|||||||
/* Reset signals */
|
/* Reset signals */
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
|
|
||||||
|
/* Default syslog identity name[:id] */
|
||||||
|
tag = svc_ident(svc, buf, sizeof(buf));
|
||||||
|
|
||||||
if (!whichp(_PATH_LOGIT)) {
|
if (!whichp(_PATH_LOGIT)) {
|
||||||
logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name);
|
logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name);
|
||||||
fallback_logger(tag, prio);
|
fallback_logger(tag, prio);
|
||||||
@@ -416,20 +420,19 @@ static pid_t service_fork(svc_t *svc)
|
|||||||
/* Set configured limits */
|
/* Set configured limits */
|
||||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||||
if (setrlimit(i, &svc->rlimit[i]) == -1)
|
if (setrlimit(i, &svc->rlimit[i]) == -1)
|
||||||
logit(LOG_WARNING,
|
logit(LOG_WARNING, "%s: rlimit: failed setting %s",
|
||||||
"%s: rlimit: Failed setting %s",
|
svc_ident(svc, NULL, 0), rlim2str(i));
|
||||||
svc->cmd, rlim2str(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set desired user+group */
|
/* Set desired user+group */
|
||||||
if (gid >= 0) {
|
if (gid >= 0) {
|
||||||
if (setgid(gid))
|
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 (uid >= 0) {
|
||||||
if (setuid(uid))
|
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 */
|
/* Set default path for regular users */
|
||||||
if (uid > 0)
|
if (uid > 0)
|
||||||
@@ -438,7 +441,7 @@ static pid_t service_fork(svc_t *svc)
|
|||||||
setenv("HOME", home, 1);
|
setenv("HOME", home, 1);
|
||||||
if (chdir(home)) {
|
if (chdir(home)) {
|
||||||
if (chdir("/"))
|
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. */
|
/* Don't try and start service if it doesn't exist. */
|
||||||
if (!whichp(svc->cmd)) {
|
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);
|
svc_missing(svc);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unlike systemd we do not allow starting service if env is missing, unless - */
|
/* Unlike systemd we do not allow starting service if env is missing, unless - */
|
||||||
if (!svc_checkenv(svc)) {
|
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);
|
svc_missing(svc);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -535,7 +538,7 @@ static int service_start(svc_t *svc)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if ((rc = wordexp(svc->cmd, &we, 0))) {
|
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:
|
nomem:
|
||||||
wordfree(&we);
|
wordfree(&we);
|
||||||
_exit(1);
|
_exit(1);
|
||||||
@@ -563,19 +566,19 @@ static int service_start(svc_t *svc)
|
|||||||
strlcat(str, arg, sizeof(str));
|
strlcat(str, arg, sizeof(str));
|
||||||
|
|
||||||
if ((rc = wordexp(str, &we, WRDE_APPEND))) {
|
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;
|
goto nomem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (we.we_wordc > MAX_NUM_SVC_ARGS) {
|
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;
|
goto nomem;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < we.we_wordc; i++) {
|
for (i = 0; i < we.we_wordc; i++) {
|
||||||
if (strlen(we.we_wordv[i]) >= sizeof(svc->args[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;
|
rc = WRDE_NOSPACE;
|
||||||
goto nomem;
|
goto nomem;
|
||||||
}
|
}
|
||||||
@@ -684,7 +687,7 @@ static void service_kill(svc_t *svc)
|
|||||||
|
|
||||||
if (svc->pid <= 1) {
|
if (svc->pid <= 1) {
|
||||||
/* Avoid killing ourselves or all processes ... */
|
/* 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,7 +717,7 @@ static void service_cleanup(svc_t *svc)
|
|||||||
fn = pid_file(svc);
|
fn = pid_file(svc);
|
||||||
if (fn && remove(fn) && errno != ENOENT)
|
if (fn && remove(fn) && errno != ENOENT)
|
||||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
||||||
basename(svc->cmd), fn);
|
svc_ident(svc, NULL, 0), fn);
|
||||||
|
|
||||||
/* No longer running, update books. */
|
/* No longer running, update books. */
|
||||||
if (svc_is_tty(svc) && svc->pid > 1)
|
if (svc_is_tty(svc) && svc->pid > 1)
|
||||||
@@ -728,10 +731,14 @@ static void service_cleanup(svc_t *svc)
|
|||||||
* service_stop - Stop service
|
* service_stop - Stop service
|
||||||
* @svc: Service to stop
|
* @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:
|
* Returns:
|
||||||
* 0 if the service was successfully stopped. Non-zero otherwise.
|
* 0 if the service was successfully stopped. Non-zero otherwise.
|
||||||
*/
|
*/
|
||||||
static int service_stop(svc_t *svc)
|
int service_stop(svc_t *svc)
|
||||||
{
|
{
|
||||||
char cmdline[CMD_SIZE] = "";
|
char cmdline[CMD_SIZE] = "";
|
||||||
int do_progress = 1;
|
int do_progress = 1;
|
||||||
@@ -801,7 +808,7 @@ static int service_stop(svc_t *svc)
|
|||||||
|
|
||||||
args[i++] = svc->cmd;
|
args[i++] = svc->cmd;
|
||||||
/* this handles, e.g., bridge-stop br0 stop */
|
/* 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]))
|
if (!strlen(svc->args[j]))
|
||||||
break;
|
break;
|
||||||
args[i++] = svc->args[j];
|
args[i++] = svc->args[j];
|
||||||
@@ -857,7 +864,7 @@ static int service_restart(svc_t *svc)
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (svc->pid <= 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;
|
svc->start_time = svc->pid = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -960,7 +967,7 @@ static void parse_env(svc_t *svc, char *env)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (strlen(env) >= sizeof(svc->env)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -984,7 +991,7 @@ static void parse_cgroup(svc_t *svc, char *cgroup)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(ptr) >= sizeof(svc->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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1009,7 +1016,7 @@ static void parse_killdelay(svc_t *svc, char *delay)
|
|||||||
|
|
||||||
sec = strtonum(delay, 1, 60, &errstr);
|
sec = strtonum(delay, 1, 60, &errstr);
|
||||||
if (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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1137,7 +1144,6 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
|||||||
svc->args_dirty = (diff > 0);
|
svc->args_dirty = (diff > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* service_register - Register service, task or run commands
|
* service_register - Register service, task or run commands
|
||||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
* @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) {
|
while (cmd) {
|
||||||
|
char *arg;
|
||||||
|
|
||||||
if (type == SVC_TYPE_TTY && cmd[0] == '@')
|
if (type == SVC_TYPE_TTY && cmd[0] == '@')
|
||||||
break; /* @console */
|
break; /* @console */
|
||||||
|
|
||||||
@@ -1257,44 +1265,48 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
cond = &cmd[1];
|
cond = &cmd[1];
|
||||||
else if (cmd[0] == ':') /* :ID */
|
else if (cmd[0] == ':') /* :ID */
|
||||||
id = &cmd[1];
|
id = &cmd[1];
|
||||||
else if (!strncasecmp(cmd, "log", 3))
|
else if (MATCH_CMD(cmd, "log", arg))
|
||||||
log = cmd;
|
log = cmd;
|
||||||
else if (!strncasecmp(cmd, "pid", 3))
|
else if (MATCH_CMD(cmd, "pid", arg))
|
||||||
pid = cmd;
|
pid = cmd;
|
||||||
else if (!strncasecmp(cmd, "name:", 5))
|
else if (MATCH_CMD(cmd, "name:", arg))
|
||||||
name = cmd;
|
name = cmd;
|
||||||
else if (!strncasecmp(cmd, "type:forking", 10))
|
else if (MATCH_CMD(cmd, "type:forking", arg))
|
||||||
forking = 1;
|
forking = 1;
|
||||||
else if (!strncasecmp(cmd, "manual:yes", 10))
|
else if (MATCH_CMD(cmd, "manual:yes", arg))
|
||||||
manual = 1;
|
manual = 1;
|
||||||
else if (!strncasecmp(cmd, "restart:", 8))
|
else if (MATCH_CMD(cmd, "restart:", arg)) {
|
||||||
restart_max = atoi(&cmd[8]);
|
if (MATCH_CMD(arg, "always", arg))
|
||||||
else if (!strncasecmp(cmd, "restarttmo:", 11)) /* compat alias */
|
restart_max = -1;
|
||||||
restart_tmo = atoi(&cmd[11]) * 1000;
|
else
|
||||||
else if (!strncasecmp(cmd, "restart_sec:", 12))
|
restart_max = atoi(arg);
|
||||||
restart_tmo = atoi(&cmd[12]) * 1000;
|
}
|
||||||
else if (!strncasecmp(cmd, "norestart", 9))
|
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;
|
restart_max = 0;
|
||||||
else if (!strncasecmp(cmd, "oncrash:", 8)) {
|
else if (MATCH_CMD(cmd, "oncrash:", arg)) {
|
||||||
if (!strncasecmp(&cmd[8], "reboot", 6))
|
if (MATCH_CMD(arg, "reboot", arg))
|
||||||
oncrash_action = SVC_ONCRASH_REBOOT;
|
oncrash_action = SVC_ONCRASH_REBOOT;
|
||||||
}
|
}
|
||||||
else if (!strncasecmp(cmd, "respawn", 7))
|
else if (MATCH_CMD(cmd, "respawn", arg))
|
||||||
respawn = 1;
|
respawn = 1;
|
||||||
else if (!strncasecmp(cmd, "halt:", 5))
|
else if (MATCH_CMD(cmd, "halt:", arg))
|
||||||
halt = &cmd[5];
|
halt = arg;
|
||||||
else if (!strncasecmp(cmd, "kill:", 5))
|
else if (MATCH_CMD(cmd, "kill:", arg))
|
||||||
delay = &cmd[5];
|
delay = arg;
|
||||||
else if (!strncasecmp(cmd, "pre:", 4))
|
else if (MATCH_CMD(cmd, "pre:", arg))
|
||||||
pre_script = &cmd[4];
|
pre_script = arg;
|
||||||
else if (!strncasecmp(cmd, "post:", 5))
|
else if (MATCH_CMD(cmd, "post:", arg))
|
||||||
post_script = &cmd[5];
|
post_script = arg;
|
||||||
else if (!strncasecmp(cmd, "env:", 4))
|
else if (MATCH_CMD(cmd, "env:", arg))
|
||||||
env = &cmd[4];
|
env = arg;
|
||||||
else if (!strncasecmp(cmd, "cgroup:", 7))
|
else if (MATCH_CMD(cmd, "cgroup:", arg))
|
||||||
cgroup = &cmd[7]; /* only settings */
|
cgroup = arg; /* only settings */
|
||||||
else if (!strncasecmp(cmd, "cgroup.", 7))
|
else if (MATCH_CMD(cmd, "cgroup.", arg))
|
||||||
cgroup = &cmd[6]; /* with group */
|
cgroup = arg; /* with group */
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1362,18 +1374,17 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
return errno = ENOMEM;
|
return errno = ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == SVC_TYPE_SERVICE && manual)
|
if (manual)
|
||||||
svc_stop(svc);
|
svc_stop(svc);
|
||||||
|
|
||||||
svc->restart_max = restart_max;
|
|
||||||
svc->restart_tmo = restart_tmo;
|
|
||||||
svc->oncrash_action = oncrash_action;
|
|
||||||
} else {
|
} else {
|
||||||
_d("Found existing svc for %s name %s id %s type %d", cmd, name, id, type);
|
_d("Found existing svc for %s name %s id %s type %d", cmd, name, id, type);
|
||||||
|
|
||||||
/* update type, may have changed from service -> task */
|
/* update type, may have changed from service -> task */
|
||||||
svc->type = type;
|
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 */
|
/* e.g., if missing cmd or env before */
|
||||||
if (!manual)
|
if (!manual)
|
||||||
svc_unblock(svc);
|
svc_unblock(svc);
|
||||||
@@ -1455,8 +1466,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
parse_env(svc, env);
|
parse_env(svc, env);
|
||||||
if (file)
|
if (file)
|
||||||
strlcpy(svc->file, file, sizeof(svc->file));
|
strlcpy(svc->file, file, sizeof(svc->file));
|
||||||
|
svc->manual = manual;
|
||||||
svc->respawn = respawn;
|
svc->respawn = respawn;
|
||||||
svc->forking = forking;
|
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 */
|
/* Decode any (optional) pid:/optional/path/to/file.pid */
|
||||||
if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
|
if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
|
||||||
@@ -1752,7 +1767,7 @@ static void service_retry(svc_t *svc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Peak instability index */
|
/* 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.",
|
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
|
||||||
svc_ident(svc, NULL, 0));
|
svc_ident(svc, NULL, 0));
|
||||||
svc_crashing(svc);
|
svc_crashing(svc);
|
||||||
@@ -1874,18 +1889,15 @@ restart:
|
|||||||
|
|
||||||
switch (svc->state) {
|
switch (svc->state) {
|
||||||
case SVC_HALTED_STATE:
|
case SVC_HALTED_STATE:
|
||||||
if (enabled) {
|
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);
|
svc_set_state(svc, SVC_READY_STATE);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_DONE_STATE:
|
case SVC_DONE_STATE:
|
||||||
if (svc_is_changed(svc))
|
if (svc_is_changed(svc))
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
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;
|
break;
|
||||||
|
|
||||||
case SVC_STOPPING_STATE:
|
case SVC_STOPPING_STATE:
|
||||||
@@ -1909,6 +1921,8 @@ restart:
|
|||||||
|
|
||||||
case SVC_TYPE_TASK:
|
case SVC_TYPE_TASK:
|
||||||
case SVC_TYPE_RUN:
|
case SVC_TYPE_RUN:
|
||||||
|
if (svc->manual)
|
||||||
|
svc_stop(svc);
|
||||||
svc_set_state(svc, SVC_DONE_STATE);
|
svc_set_state(svc, SVC_DONE_STATE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1926,7 +1940,7 @@ restart:
|
|||||||
|
|
||||||
case SVC_SETUP_STATE:
|
case SVC_SETUP_STATE:
|
||||||
if (!svc->pid)
|
if (!svc->pid)
|
||||||
svc_set_state(svc, SVC_READY_STATE);
|
svc_set_state(svc, SVC_STARTING_STATE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_READY_STATE:
|
case SVC_READY_STATE:
|
||||||
@@ -1937,6 +1951,21 @@ restart:
|
|||||||
if (sm_is_in_teardown(&sm))
|
if (sm_is_in_teardown(&sm))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (svc_has_pre(svc)) {
|
||||||
|
svc_set_state(svc, SVC_SETUP_STATE);
|
||||||
|
service_pre_script(svc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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);
|
err = service_start(svc);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (svc_is_missing(svc)) {
|
if (svc_is_missing(svc)) {
|
||||||
@@ -1950,7 +1979,6 @@ restart:
|
|||||||
/* Everything went fine, clean and set state */
|
/* Everything went fine, clean and set state */
|
||||||
svc_mark_clean(svc);
|
svc_mark_clean(svc);
|
||||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_RUNNING_STATE:
|
case SVC_RUNNING_STATE:
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ int service_timeout_cancel (svc_t *svc);
|
|||||||
|
|
||||||
void service_forked (svc_t *svc);
|
void service_forked (svc_t *svc);
|
||||||
|
|
||||||
|
int service_stop (svc_t *svc);
|
||||||
int service_step (svc_t *svc);
|
int service_step (svc_t *svc);
|
||||||
void service_step_all (int types);
|
void service_step_all (int types);
|
||||||
void service_worker (void *unused);
|
void service_worker (void *unused);
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ restart:
|
|||||||
*/
|
*/
|
||||||
svc = svc_stop_completed();
|
svc = svc_stop_completed();
|
||||||
if (svc) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ restart:
|
|||||||
*/
|
*/
|
||||||
svc = svc_stop_completed();
|
svc = svc_stop_completed();
|
||||||
if (svc) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
#include <ctype.h> /* isdigit() */
|
#include <ctype.h> /* isdigit() */
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ typedef enum {
|
|||||||
SVC_SETUP_STATE, /* Running pre: script */
|
SVC_SETUP_STATE, /* Running pre: script */
|
||||||
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
||||||
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
||||||
|
SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */
|
||||||
SVC_RUNNING_STATE, /* Process running */
|
SVC_RUNNING_STATE, /* Process running */
|
||||||
} svc_state_t;
|
} svc_state_t;
|
||||||
|
|
||||||
@@ -121,7 +122,8 @@ typedef struct svc {
|
|||||||
int status; /* From waitpid() when process is collected */
|
int status; /* From waitpid() when process is collected */
|
||||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||||
svc_type_t type; /* Service, run, task, ... */
|
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 dirty; /* 0: unmodified, 1: modified */
|
||||||
const int removed;
|
const int removed;
|
||||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||||
@@ -139,7 +141,7 @@ typedef struct svc {
|
|||||||
/* Counters */
|
/* Counters */
|
||||||
char once; /* run/task, (at least) once per runlevel */
|
char once; /* run/task, (at least) once per runlevel */
|
||||||
unsigned int restart_tot; /* Total restarts ever, summarized, including `initctl restart` */
|
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. */
|
int restart_tmo; /* Time required for the service to start. */
|
||||||
unsigned char oncrash_action; /* Action to perform in crashed state. */
|
unsigned char oncrash_action; /* Action to perform in crashed state. */
|
||||||
char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */
|
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_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_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_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_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); }
|
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ char *tty_atcon(void)
|
|||||||
int tty_parse_args(char *cmd, struct tty *tty)
|
int tty_parse_args(char *cmd, struct tty *tty)
|
||||||
{
|
{
|
||||||
char *dev = NULL;
|
char *dev = NULL;
|
||||||
|
int passenv = 0;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -165,6 +166,8 @@ int tty_parse_args(char *cmd, struct tty *tty)
|
|||||||
tty->nologin = 1;
|
tty->nologin = 1;
|
||||||
else if (!strcmp(cmd, "notty"))
|
else if (!strcmp(cmd, "notty"))
|
||||||
tty->notty = 1; /* for board bringup */
|
tty->notty = 1; /* for board bringup */
|
||||||
|
else if (!strcmp(cmd, "passenv"))
|
||||||
|
passenv = 1;
|
||||||
else if (!strcmp(cmd, "rescue"))
|
else if (!strcmp(cmd, "rescue"))
|
||||||
tty->rescue = 1; /* for rescue shells */
|
tty->rescue = 1; /* for rescue shells */
|
||||||
else if (whichp(cmd)) /* in $PATH? */
|
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");
|
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
|
||||||
|
|
||||||
/* Built-in getty now comes as standalone program */
|
/* 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;
|
tty->cmd = _PATH_GETTY;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
|||||||
EXTRA_DIST += cond-start-task.sh
|
EXTRA_DIST += cond-start-task.sh
|
||||||
EXTRA_DIST += failing-sysv.sh
|
EXTRA_DIST += failing-sysv.sh
|
||||||
EXTRA_DIST += global-envs.sh
|
EXTRA_DIST += global-envs.sh
|
||||||
|
EXTRA_DIST += initctl-status-subset.sh
|
||||||
EXTRA_DIST += pre-post-serv.sh
|
EXTRA_DIST += pre-post-serv.sh
|
||||||
|
EXTRA_DIST += restart-self.sh
|
||||||
EXTRA_DIST += runlevel.sh
|
EXTRA_DIST += runlevel.sh
|
||||||
EXTRA_DIST += start-stop-service.sh
|
EXTRA_DIST += start-stop-service.sh
|
||||||
EXTRA_DIST += start-stop-service-sub-config.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 += cond-start-task.sh
|
||||||
TESTS += failing-sysv.sh
|
TESTS += failing-sysv.sh
|
||||||
TESTS += global-envs.sh
|
TESTS += global-envs.sh
|
||||||
|
TESTS += initctl-status-subset.sh
|
||||||
TESTS += pre-post-serv.sh
|
TESTS += pre-post-serv.sh
|
||||||
|
TESTS += restart-self.sh
|
||||||
TESTS += runlevel.sh
|
TESTS += runlevel.sh
|
||||||
TESTS += start-stop-service.sh
|
TESTS += start-stop-service.sh
|
||||||
TESTS += start-stop-service-sub-config.sh
|
TESTS += start-stop-service-sub-config.sh
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
noinst_PROGRAMS = serv
|
noinst_PROGRAMS = serv
|
||||||
|
|
||||||
serv_SOURCES = serv.c
|
serv_SOURCES = serv.c
|
||||||
|
serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||||
|
|||||||
+39
-3
@@ -21,7 +21,7 @@ static void verify_env(char *arg)
|
|||||||
char *replica;
|
char *replica;
|
||||||
char *env;
|
char *env;
|
||||||
|
|
||||||
key = replica = strdup(arg);
|
key = replica = strdupa(arg);
|
||||||
if (!replica)
|
if (!replica)
|
||||||
err(1, "Failed duplicating arg %s", arg);
|
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);
|
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)
|
static void sig(int signo)
|
||||||
{
|
{
|
||||||
warnx("We got signal %d ...", signo);
|
warnx("We got signal %d ...", signo);
|
||||||
@@ -86,6 +108,7 @@ static int usage(int rc)
|
|||||||
" -n Run in foreground\n"
|
" -n Run in foreground\n"
|
||||||
" -p Create PID file despite running in foreground\n"
|
" -p Create PID file despite running in foreground\n"
|
||||||
" -P FILE Create PID file using FILE\n"
|
" -P FILE Create PID file using FILE\n"
|
||||||
|
" -r SVC Call initctl to restart service SVC (self)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"By default this program daemonizes itself to the background, and,\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"
|
"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_background = 1;
|
||||||
int do_pidfile = 1;
|
int do_pidfile = 1;
|
||||||
|
int do_restart = 0;
|
||||||
char *pidfn = NULL;
|
char *pidfn = NULL;
|
||||||
|
char cmd[80];
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "e:E:hnpP:")) != EOF) {
|
while ((c = getopt(argc, argv, "e:E:hnpP:r:")) != EOF) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
return usage(0);
|
return usage(0);
|
||||||
@@ -123,6 +148,10 @@ int main(int argc, char *argv[])
|
|||||||
case 'P':
|
case 'P':
|
||||||
pidfn = optarg;
|
pidfn = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'r':
|
||||||
|
snprintf(cmd, sizeof(cmd), "initctl restart %s", optarg);
|
||||||
|
do_restart = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return usage(1);
|
return usage(1);
|
||||||
}
|
}
|
||||||
@@ -142,8 +171,15 @@ int main(int argc, char *argv[])
|
|||||||
pidfile(pidfn);
|
pidfile(pidfn);
|
||||||
|
|
||||||
warnx("Entering while(1) loop");
|
warnx("Entering while(1) loop");
|
||||||
while (1)
|
while (1) {
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
if (do_restart) {
|
||||||
|
inc_restarts();
|
||||||
|
if (system(cmd))
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
warnx("Leaving ...");
|
warnx("Leaving ...");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Executable
+57
@@ -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
|
||||||
|
|
||||||
Executable
+47
@@ -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
|
||||||
@@ -31,6 +31,11 @@ assert_num_children()
|
|||||||
assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1"
|
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
|
# shellcheck disable=SC2086
|
||||||
assert_new_pid()
|
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 "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
|
# shellcheck disable=SC2086
|
||||||
assert_pidfile()
|
assert_pidfile()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user