mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44137806e4 |
@@ -1,3 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [troglobit]
|
||||
+6
-8
@@ -9,16 +9,14 @@ sudo: false
|
||||
#sudo: true
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
# - clang
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
# The secure: declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
env:
|
||||
global:
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig
|
||||
- CFLAGS="-W -Wall -Wextra"
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
|
||||
|
||||
addons:
|
||||
@@ -30,7 +28,7 @@ addons:
|
||||
name: "troglobit/finit"
|
||||
description: "finit | Fast & Extensible init for Linux"
|
||||
notification_email: troglobit@gmail.com
|
||||
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
|
||||
build_command_prepend: "./autogen.sh && PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp"
|
||||
build_command: "make"
|
||||
branch_pattern: dev
|
||||
|
||||
@@ -45,10 +43,10 @@ install:
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --prefix=/tmp --disable-inetd --enable-static
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --disable-inetd --enable-static
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- ./configure --prefix=/tmp --enable-inetd-echo-plugin --enable-inetd-time-plugin --enable-x11-common-plugin --enable-inetd-chargen-plugin --enable-fallback-shell --enable-watchdog --enable-logit
|
||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --enable-inetd-echo-plugin --enable-inetd-time-plugin --enable-x11-common-plugin --enable-inetd-chargen-plugin --enable-fallback-shell --enable-watchdog
|
||||
- cat config.log
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
|
||||
+5
-41
@@ -4,25 +4,14 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[3.2][UNRELEASED] - 2020-04-xx
|
||||
[3.2][UNRELEASED] - 2018-10-xx
|
||||
------------------------------
|
||||
|
||||
Bug fix release, but also cgroups and a new progress!
|
||||
|
||||
https://twitter.com/b0rk/status/1214341831049252870?s=20
|
||||
Bug fix release.
|
||||
|
||||
### Changes
|
||||
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
|
||||
* Support for `sysv` start/stop scripts as well as monitoring forking
|
||||
services, stared using `sysv` or `service` stanza
|
||||
* Support for custom `kill:DELAY`, default 3 sec.
|
||||
* Support for custom `halt:SIGNAL`, default SIGTERM
|
||||
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
|
||||
useful with new `sysv` or `service` which fork to background
|
||||
* Support starting run/task/services without absolute path, trust `$PATH`
|
||||
* Add support for `--disable-docs` and `--disable-contrib` to speed up
|
||||
builds and work around issue with massively parallel builds
|
||||
* Add support for `@console` also for external getty
|
||||
* Add `-b`, batch mode, for non-interactive use to `initctl`
|
||||
* Prefer udev to handle `/dev/` if mdev is also available
|
||||
* Redirect dbus daemon output to syslog
|
||||
@@ -30,7 +19,7 @@ Bug fix release, but also cgroups and a new progress!
|
||||
* Finit no longer automatically reloads its `*.conf` files after running
|
||||
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
|
||||
* `initctl` without an argument or option now defaults to list services
|
||||
* Convert built-in watchdog daemon to standalone mini watchdogd, issue #102
|
||||
* Converted built-in watchdog daemon to standalone mini watchdogd
|
||||
* Improved watchdog hand-over, now based on `svc_t` and not PID
|
||||
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
|
||||
not allowed in the runtime runlevel are unconditionally stopped
|
||||
@@ -45,19 +34,7 @@ Bug fix release, but also cgroups and a new progress!
|
||||
the kernel can invalidate deleted events before enqueing to userspace
|
||||
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
|
||||
from the `hwclock` tool. Note: the kernel can also be set to load
|
||||
and store RTC to/from system clock at boot/halt as well, issue #110
|
||||
* New plugin to support cold plugging devices, auto-loading of modules
|
||||
at boot. Detects required modules by reading `/sys/devices/*`
|
||||
* New plugin for `/etc/modules-load.d/` by Robert Andersson, Atlas Copco
|
||||
* New `name:foo` support for services, by Robert Andersson, Atlas Copco
|
||||
* New `manual:yes` support for services, by Robert Andersson, Atlas Copco
|
||||
* New `log:console` support for services, by Robert Andersson, Atlas Copco
|
||||
* Support for `:ID` as a string, by Jonas Johansson, Westermo
|
||||
* Support for auto-reload, instead of having to do `initctl reload`,
|
||||
when a service configuration has changed. Disabled by default, but
|
||||
can be enabled with `./configure --enable-auto-reload`
|
||||
* Support for logging security related events, e.g., runlevel change,
|
||||
star/stop or failure to start services, by Jonas Holmberg, Westermo
|
||||
and store RTC to/from system clock at boot/halt as well.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -76,29 +53,18 @@ Bug fix release, but also cgroups and a new progress!
|
||||
that do not have a watchdog
|
||||
* Fix #100: Early condition handling may not work if `/var/run` does
|
||||
not yet exist (symlink to `/run`). Added compat layer for access
|
||||
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY,
|
||||
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY
|
||||
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
|
||||
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
|
||||
Fixed by Jonas Johansson, Westermo
|
||||
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
|
||||
Fixed by Jonas Johansson, Westermo
|
||||
* Fix #109: Support for PID files in sub-directories to `/var/run`
|
||||
* Handle rename of PID files, by Robert Andersson, Atlas Copco
|
||||
* Fix #111: Only restart inetd services when necessary. E.g., if the
|
||||
listening interface is changed. Only stop established connections
|
||||
which are no longer allowed, i.e. do not touch already allowed
|
||||
established connections. Fixed by Jonas Johansson, Westermo
|
||||
* Fix #120: Redirect `stdin` to `/dev/null` for services by default
|
||||
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
|
||||
fixed by Jacques de Laval, Westermo
|
||||
* Fix #124: Lingering processes in process group when session leader
|
||||
exits. E.g., lingering `logit` processes when parent dies
|
||||
* Fix: update inetd service args on config change. Found and fixed by
|
||||
Petrus Hellgren, Westermo
|
||||
* Fix service name matching, e.g. for condition handling, may match with
|
||||
wrong service, by Jonas Holmberg, Westermo
|
||||
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard
|
||||
run-parts tool. Found by Magnus Malm, Westermo
|
||||
* Fix `initctl [start | restart]`, should behave the same for services
|
||||
that have crashed. Found by Mattias Walström, Westermo
|
||||
* Wait for bootstrap phase to complete before cleaning out any bootstrap
|
||||
@@ -106,9 +72,7 @@ Bug fix release, but also cgroups and a new progress!
|
||||
* Reassert condition when an unmodified run/task/service goes from
|
||||
WAITING back to RUNNING again after a reconfiguration event.
|
||||
Found and fixed by Jonas Johansson, Westermo
|
||||
* Restore Ctrl-D and Ctrl-U support in built-in getty
|
||||
* Remove service condition when service is deleted
|
||||
* Fix C++ compilation issues, by Robert Andersson, Atlas Copco
|
||||
|
||||
|
||||
[3.1][] - 2018-01-23
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
[![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
|
||||
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -22,18 +21,19 @@
|
||||
|
||||
Introduction
|
||||
------------
|
||||
[![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
|
||||
|
||||
Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
|
||||
was reverse engineered from the [EeePC fastinit][] over ten years ago
|
||||
by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
|
||||
was reverse engineered from the [EeePC fastinit][] ten years ago by
|
||||
[Claudio Matsuoka][] — "gaps filled with frog DNA …"
|
||||
|
||||
Focus is on small and embedded systems, although Finit is fully usable
|
||||
on server and desktop systems as well. For working examples, see the
|
||||
[contrib/](contrib/) section with tutorials for:
|
||||
|
||||
* [Void Linux](contrib/void/),
|
||||
* [Alpine Linux](contrib/alpine/), and
|
||||
* [Debian GNU/Linux](contrib/debian/)
|
||||
* [Alpine Linux](contrib/alpine/),
|
||||
* [Debian GNU/Linux](contrib/debian/), and
|
||||
* [Void Linux](contrib/void/)
|
||||
|
||||

|
||||
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
||||
@@ -280,20 +280,6 @@ from the kernel, leaving only warnings and errors. For other kernel
|
||||
command line parameters, see [docs/build.md](docs/build.md#recovery).
|
||||
|
||||
|
||||
**Automatic Reload**
|
||||
|
||||
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
|
||||
registering any changes to `.conf` files. To activate a change the user
|
||||
must call `initctl reload`, which reloads all modified files, stops any
|
||||
removed services, starts new ones, and restarts any modified ones, with
|
||||
SIGHUP if the process supports it.
|
||||
|
||||
For some use-cases the extra step of calling `initctl reload` creates an
|
||||
unnecessary overhead, which can be removed at build-time using:
|
||||
|
||||
configure --enable-auto-reload
|
||||
|
||||
|
||||
Runparts & /etc/rc.local
|
||||
------------------------
|
||||
|
||||
@@ -443,6 +429,8 @@ Commands:
|
||||
touch <CONF> Mark .conf in /etc/finit.d/ for reload
|
||||
reload Reload *.conf in /etc/finit.d/ (activates changes)
|
||||
|
||||
cond set <COND> Set (assert) condition => +COND
|
||||
cond clear <COND> Clear (deassert) condition => -COND
|
||||
cond show Show condition status
|
||||
cond dump Dump all conditions and their status
|
||||
|
||||
@@ -545,12 +533,12 @@ and proposed extensions.
|
||||
[openrc]: http://www.gentoo.org/proj/en/base/openrc/
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
[original finit]: http://helllabs.org/finit/
|
||||
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||
[EeePC fastinit]: http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
||||
[Joachim Nilsson]: http://troglobit.com
|
||||
[GitHub]: https://github.com/troglobit/finit
|
||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||
[License Badge]: https://img.shields.io/badge/License-MIT-yellow.svg
|
||||
[Travis]: https://travis-ci.org/troglobit/finit
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
|
||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||
|
||||
+6
-37
@@ -1,4 +1,4 @@
|
||||
AC_INIT(Finit, 3.2-rc2, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 3.2-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
@@ -40,9 +40,6 @@ AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
|
||||
# Check for pkg-config first, warn if it's not installed
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
# Check for required libraries
|
||||
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
|
||||
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
|
||||
@@ -57,22 +54,14 @@ AC_ARG_ENABLE(fallback_shell,
|
||||
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
|
||||
enable_fallback_shell=no])
|
||||
|
||||
AC_ARG_ENABLE(auto_reload,
|
||||
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
|
||||
enable_auto_reload=no])
|
||||
|
||||
AC_ARG_ENABLE(rw-rootfs,
|
||||
AS_HELP_STRING([--enable-rw-rootfs], [Remount / R/W at boot, usually not for embedded]),,[
|
||||
enable_rw_rootfs=no])
|
||||
|
||||
AC_ARG_ENABLE(progress,
|
||||
AS_HELP_STRING([--enable-progress], [Modern progress display at bootup]),,[
|
||||
AS_HELP_STRING([--enable-progress], [Pre Finit v3 bootup progress and heading]),,[
|
||||
enable_progress=no])
|
||||
|
||||
AC_ARG_ENABLE(progress-classic,
|
||||
AS_HELP_STRING([--enable-progress-classic], [Pre Finit v3 bootup progress and heading]),,[
|
||||
enable_progress_classic=no])
|
||||
|
||||
AC_ARG_ENABLE(watchdog,
|
||||
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
||||
enable_watchdog=no])
|
||||
@@ -85,10 +74,6 @@ AC_ARG_ENABLE(inetd,
|
||||
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
|
||||
enable_inetd=yes])
|
||||
|
||||
AC_ARG_ENABLE(logit,
|
||||
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
|
||||
enable_logit=yes])
|
||||
|
||||
AC_ARG_ENABLE(logrotate,
|
||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
||||
enable_logrotate=yes])
|
||||
@@ -152,6 +137,7 @@ AC_ARG_WITH(random-seed,
|
||||
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
|
||||
[random_seed=$withval], [random_seed=/var/lib/misc/random-seed])
|
||||
|
||||
|
||||
### Enable features ###########################################################################
|
||||
|
||||
# Create config.h from selected features and fallback defautls
|
||||
@@ -161,24 +147,13 @@ AS_IF([test "x$enable_emergency_shell" = "xyes"], [
|
||||
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
|
||||
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
|
||||
|
||||
AS_IF([test "x$enable_auto_reload" = "xyes"], [
|
||||
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
|
||||
|
||||
AS_IF([test "x$enable_rw_rootfs" = "xyes"], [
|
||||
AC_DEFINE(REMOUNT_ROOTFS, 1, [Remount / as rw at boot])])
|
||||
|
||||
AS_IF([test "x$enable_progress" = "xyes"], [
|
||||
AC_DEFINE(PROGRESS_STYLE, 2, [Modern bootup messages, systemd inspired])])
|
||||
|
||||
AS_IF([test "x$enable_progress_classic" = "xyes"], [
|
||||
enable_progress=no
|
||||
AC_DEFINE(PROGRESS_STYLE, 1, [Pre Finit v3 bootup messages and heading])])
|
||||
|
||||
AS_IF([test "x$enable_progress_classic" = "xyes" -o "x$enable_progress" = "xyes"], [
|
||||
AC_DEFINE(SILENT_MODE, 0, [Display bootup progress, see above for style])
|
||||
AC_DEFINE(SILENT_MODE, 0, [Pre Finit v3 bootup messages and heading])
|
||||
], [
|
||||
AC_DEFINE(PROGRESS_STYLE, 0, [Default progress style])
|
||||
AC_DEFINE(SILENT_MODE, 1, [Completely silent until login prompt (default)])])
|
||||
AC_DEFINE(SILENT_MODE, 1, [Completely silent until login prompt])])
|
||||
|
||||
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
||||
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
||||
@@ -191,9 +166,6 @@ AS_IF([test "x$enable_inetd" != "xno"], [
|
||||
enable_inetd="yes"
|
||||
AC_DEFINE(INETD_ENABLED, 1, [Enable built-in inetd])])
|
||||
|
||||
AS_IF([test "x$enable_logit" != "xno"], [
|
||||
AC_DEFINE(LOGIT_ENABLED, 1, [Enable logit tool, used for log redirection by Finit])])
|
||||
|
||||
AS_IF([test "x$enable_logrotate" != "xno"], [
|
||||
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
||||
|
||||
@@ -252,7 +224,6 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
||||
AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
|
||||
AM_CONDITIONAL(DOCS, [test "x$enable_docs" = "xyes"])
|
||||
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||
|
||||
@@ -298,12 +269,10 @@ Optional features:
|
||||
Built-in inetd........: $enable_inetd
|
||||
Built-in watchdogd....: $enable_watchdog
|
||||
Built-in logrotate....: $enable_logrotate
|
||||
Scripting tool logit..: $enable_logit
|
||||
Emergency shell.......: $enable_emergency_shell
|
||||
Fallback shell........: $enable_fallback_shell
|
||||
Remount / RW at boot..: $enable_rw_rootfs
|
||||
Modern progress.......: $enable_progress
|
||||
Classic progress......: $enable_progress_classic
|
||||
Traditional progress..: $enable_progress
|
||||
Default console dev...: $console
|
||||
Default hostname......: $hostname
|
||||
Default runlevel......: $runlevel
|
||||
|
||||
@@ -66,6 +66,9 @@ how `git commit -s` is used to automatically add a `Signed-off-by`:
|
||||
Signed-off-by: Jane Doe <jane.doe@example.com>
|
||||
|
||||
|
||||
Another good *counter* example [is this][rambling] ...
|
||||
|
||||
|
||||
Code of Conduct
|
||||
---------------
|
||||
|
||||
@@ -76,4 +79,11 @@ Bill & Ted; [be excellent to each other][conduct].
|
||||
[github]: https://github.com/troglobit/finit/
|
||||
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
|
||||
[gitbook]: https://git-scm.com/book/ch5-2.html
|
||||
[rambling]: http://stopwritingramblingcommitmessages.com/
|
||||
[conduct]: https://github.com/troglobit/finit/blob/master/CODE-OF-CONDUCT.md
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
|
||||
@@ -21,9 +21,6 @@ can also be used.
|
||||
Near Future
|
||||
-----------
|
||||
|
||||
* When a process dies, and Finit does not restart it, we should collect
|
||||
a `siginfo_t` from the SIGCHLD signal and supply to the user in case
|
||||
`initctl status <process>` is called to debug the issue.
|
||||
* Merge UDP redirect service uredir as an UDP inetd variant
|
||||
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
|
||||
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
|
||||
|
||||
+17
-28
@@ -47,7 +47,17 @@ service's PID file being created.
|
||||
Triggering
|
||||
----------
|
||||
|
||||
Conditions can only be triggered by built-in plugins.
|
||||
Conditions are triggered by built-in plugins, but can for debugging
|
||||
purposes also be controlled using the `cond set` and `cond clear`
|
||||
sub-commands to the `initctl` control tool:
|
||||
|
||||
* `initctl cond set your/cond/here`
|
||||
|
||||
To set a condition
|
||||
|
||||
* `initctl cond clear your/cond/here`
|
||||
|
||||
To clear a condition
|
||||
|
||||
Conditions retain their current state until the next reconfiguration or
|
||||
runlevel change. At that point all set conditions transition into the
|
||||
@@ -92,25 +102,6 @@ Built-in conditions:
|
||||
difference is that `running` tells if the NIC has link.
|
||||
|
||||
|
||||
Composition
|
||||
-----------
|
||||
|
||||
The `svc/` conditions can be quite tricky to understand. They are
|
||||
generated by the Finit `pidfile.so` plugin and are composed from the
|
||||
daemon's path and the pidfile name (and path).
|
||||
|
||||
| **service** | **pid:** | **pidfile path** | **svc condition** |
|
||||
|--------------------------------------|-----------------|------------------|-------------------|
|
||||
| /sbin/foo | | /run/foo.pid | svc/sbin/foo |
|
||||
| /sbin/bar -p /run/baz.pid | pid:baz | /run/bas.pid | svc/sbin/baz |
|
||||
| lxc-start -n foo -p /run/lxc/foo.pid | pid:lxc/foo.pid | /run/lxc/foo.pid | svc/lxc/foo |
|
||||
| /usr/bin/dbus-daemon | pid:dbus/pid | /run/dbus/pid | svc/usr/bin/dbus |
|
||||
|
||||
Omitting the `pid:` to run/task/service/sysv stanza means Finit will
|
||||
guess the pidfile path based on `/run/` and the basename(1) of the
|
||||
program.
|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
@@ -142,13 +133,12 @@ To fake interface `vlan1` suddenly appearing, and test what happens to
|
||||
|
||||
```shell
|
||||
~ # initctl debug
|
||||
~ # mkdir -p /var/run/finit/cond/net/vlan1
|
||||
~ # cp /var/run/finit/cond /var/run/finit/cond/net/vlan1/exist
|
||||
~ # initctl cond set net/vlan1/exist
|
||||
```
|
||||
|
||||
Then watch the console for the debug messages and then check the output
|
||||
from `initctl cond show` again. The client will likely have failed to
|
||||
start, but at least the condition is now satisfied.
|
||||
from `initctl cond show` again. (The client will likely have failed to
|
||||
start, but at least the condition is now satisfied.)
|
||||
|
||||
There is also the `initctl cond dump` command, which dumps all known
|
||||
conditions and their current status.
|
||||
@@ -157,10 +147,9 @@ conditions and their current status.
|
||||
Internals
|
||||
---------
|
||||
|
||||
As shown previously, conditions are implemented as simple files in the
|
||||
file system, in the `/var/run/finit/cond/` sub-directory. The files
|
||||
are created, updated, and removed by condition plugins. To debug them,
|
||||
see the previous section.
|
||||
Conditions are implemented as simple files in the file system, in the
|
||||
`/var/run/finit/cond/` sub-directory. Use the `initctl cond set/clear`
|
||||
commands to add/remove files in this hierarchy.
|
||||
|
||||
A condition is always in one of three states:
|
||||
|
||||
|
||||
+60
-55
@@ -93,20 +93,59 @@ Syntax
|
||||
case the contents of that file is used.
|
||||
|
||||
* `module <MODULE> [ARGS]`
|
||||
Load a kernel module, with optional arguments. Similar to `insmod`
|
||||
command line tool.
|
||||
|
||||
Note, there is both a `modules-load.so` and a `modprobe.so` plugin
|
||||
that can handle module loading better. The former supports loading
|
||||
from `/etc/modules-load.d/`, and the latter uses kernel modinfo to
|
||||
automatically load (or coldplug) every required module. For hotplug
|
||||
we recommend the BusyBox mdev tool, add to `/etc/mdev.conf`:
|
||||
|
||||
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
|
||||
Load a kernel module, with optional arguments
|
||||
|
||||
* `network <PATH>`
|
||||
Script or program to bring up networking, with optional arguments
|
||||
|
||||
* `cgroup <NAME> cpuset:SPEC cpu:SPEC mem:SPEC`
|
||||
Create a control group for resource limiting services using cgroups.
|
||||
Unlike `rlimit` this setting defines a common group to be selected for
|
||||
any set of run/task/services using the `group` directive (below).
|
||||
|
||||
A cpuset for a cgroup can be the traditional list of CPU cores to
|
||||
assign to the group, or a range that Finit will use to dynamically
|
||||
allocate cores based on the amount available. The range values can be
|
||||
either in absolute form or percent.
|
||||
|
||||
```shell
|
||||
cgroup first cpuset:1
|
||||
```
|
||||
|
||||
To create a cgroup with one exclusive CPU:
|
||||
|
||||
```shell
|
||||
cgroup single cpuset:[1,1]
|
||||
```
|
||||
|
||||
To create cgroup that uses at most two shared CPUs:
|
||||
|
||||
```shell
|
||||
cgroup uptotwo cpuset:[,2]
|
||||
```
|
||||
|
||||
To create cgroup with at least two shared CPUs:
|
||||
|
||||
```shell
|
||||
cgroup dual cpuset:[2,]
|
||||
```
|
||||
|
||||
To create cgroup with at least one CPU and at most half of all
|
||||
available CPUs:
|
||||
|
||||
```shell
|
||||
cgroup greedy cpuset:[1,50%]
|
||||
```
|
||||
|
||||
|
||||
* `group <NAME>`
|
||||
All subsequent `service`, `task`, or `run` directives are executed in the
|
||||
given control group `NAME`, which must be defined prior to this line. If
|
||||
`NAME` does not yet exist the default group `default` is used.
|
||||
|
||||
When separate Finit `.conf` files are used for services the default group
|
||||
is reset for each `.conf` file read.
|
||||
|
||||
* `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
|
||||
limit for a resource, or both if that argument is omitted. `RESOURCE`
|
||||
is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
|
||||
@@ -152,36 +191,11 @@ Syntax
|
||||
task [s] echo "foo" | cat >/tmp/bar
|
||||
```
|
||||
|
||||
* `sysv [LVLS] <COND> /path/to/init-script -- Optional description`__
|
||||
Similar to `task` is the `sysv` stanza, which can be used to call SysV
|
||||
style scripts. The primary intention for this command is to be able to
|
||||
re-use much of existing setup and init scripts in Linux distributions.
|
||||
|
||||
When entering an allowed runlevel, Finit calls `init-script start`,
|
||||
when entering a disallowed runlevel, Finit calls `init-script stop`,
|
||||
and if the Finit .conf, where `sysv` stanza is declared, is modified,
|
||||
Finit calls `init-script restart` on `initctl reload`. Similar to
|
||||
how `service` stanzas work.
|
||||
|
||||
Forking services started with `sysv` scripts can be monitored by Finit
|
||||
by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
|
||||
|
||||
* `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely.
|
||||
|
||||
For daemons that support it, we recommend appending `--foreground`, or
|
||||
`--no-background`, argument to prevent them from forking off a
|
||||
sub-process in the background. This is the most reliable way to
|
||||
monitor a service.
|
||||
|
||||
However, not all daemons support running in the foreground, or they
|
||||
may start logging to the foreground as well, these are called forking
|
||||
services and are supported using the same syntax as forking `sysv`
|
||||
services, using the `pid:!/path/to/pidfile.pid` syntax.
|
||||
|
||||
In the case of `ospfd` (below), we omit the `-d` flag to prevent it
|
||||
from forking (daemonizing):
|
||||
exits prematurely. Please note that you often need to provide a
|
||||
`--foreground` or `--no-background` argument to most daemons to
|
||||
prevent them from forking off a sub-process in the background.
|
||||
|
||||
```shell
|
||||
service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon
|
||||
@@ -203,21 +217,21 @@ Syntax
|
||||
|
||||
pid[:[/path/to/]filename[.pid]]
|
||||
|
||||
For example, by adding `pid:/run/foo.pid` to the service `/sbin/bar`
|
||||
that PID file will, not only be created and removed automatically, but
|
||||
also be used by the Finit condition subsystem. So a service/run/task
|
||||
can depend on `<svc/sbin/foo>`, notice the composition of conditions.
|
||||
For example, by adding `pid:/run/foo.pid` to the service `bar` that
|
||||
PID file will, not only be created and removed automatically, but also
|
||||
be used by the Finit condition subsystem. So another service/run/task
|
||||
can depend on `<svc/foo>`.
|
||||
|
||||
However, if a service `bar` *does* create a PID file, using `foo.pid`,
|
||||
we can inform Finit of this by prepending an `!`:
|
||||
If a service `bar` *does* maintain a PID file, but using `foo.pid`, we
|
||||
can inform Finit of this by simplly prepending an `!` to the argument.
|
||||
|
||||
pid:!/run/foo.pid
|
||||
|
||||
Here Finit will *not* create/remove/touch the PID file, only use it
|
||||
for the condition handling instead of the default PID file name.
|
||||
|
||||
> For a detailed description of conditions, and how to debug them, see
|
||||
> the [Finit Conditions](conditions.md) document.
|
||||
For a detailed description of conditions, and how to debug them, see
|
||||
the [Finit Conditions](conditions.md) document.
|
||||
|
||||
If a service should not be automatically started, it can be configured
|
||||
as manual with the optional `manual` argument. The service can then be
|
||||
@@ -231,15 +245,6 @@ Syntax
|
||||
|
||||
name:<service-name>
|
||||
|
||||
When stopping a service (run/task/sysv/service), either manually or
|
||||
when moving to another runlevel, Finit starts by sending `SIGTERM`, to
|
||||
allow the process to shut down gracefully. If the process has not
|
||||
been collected within 3 seconds, Finit sends `SIGKILL`. To halt the
|
||||
process using a different signal, use the option `halt:SIGNAL`, e.g.,
|
||||
`halt:SIGPWR`. To change the delay between your halt signal and KILL,
|
||||
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
|
||||
sending `SIGKILL`.
|
||||
|
||||
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
||||
Launch a daemon when a client initiates a connection on an Internet
|
||||
port. Available services are listed in the UNIX `/etc/services` file.
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c initctl.c pidfile.c procps.c tty.c urandom.c
|
||||
libplug_la_SOURCES = bootmisc.c rtc.c initctl.c pidfile.c procps.c tty.c urandom.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
@@ -52,7 +52,7 @@ libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
else
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la initctl.la pidfile.la procps.la tty.la urandom.la
|
||||
pkglib_LTLIBRARIES = bootmisc.la rtc.la initctl.la pidfile.la procps.la tty.la urandom.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ static void setup(void *arg)
|
||||
erase("/var/run/dbus/pid");
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
|
||||
snprintf(line, sizeof(line), "[S12345] log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/* Coldplug modules using modalias magic
|
||||
*
|
||||
* Copyright (c) 2020 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cold plug magic taken from Buildroot
|
||||
* https://github.com/buildroot/buildroot/commit/b4fc5a180c81689a982d5c595844331684c14f51
|
||||
*
|
||||
* The plugin basically does this, only in C:
|
||||
*
|
||||
* system("find /sys/devices -name modalias -print0"
|
||||
* " | xargs -0 sort -u -z"
|
||||
* " | xargs -0 modprobe -abq");
|
||||
*
|
||||
* Note: BusyBox must *not* be built with CONFIG_MODPROBE_SMALL
|
||||
*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <ftw.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h> /* gettimeofday() */
|
||||
#include <sys/types.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
struct module {
|
||||
TAILQ_ENTRY(module) link;
|
||||
char *alias;
|
||||
};
|
||||
|
||||
static TAILQ_HEAD(, module) modules = TAILQ_HEAD_INITIALIZER(modules);
|
||||
|
||||
static int modprobe(char *alias)
|
||||
{
|
||||
char *args[] = {
|
||||
"modprobe",
|
||||
"-abq",
|
||||
alias,
|
||||
NULL,
|
||||
};
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
_pe("Failed forking modprobe child");
|
||||
return 1;
|
||||
case 0:
|
||||
execvp(args[0], args);
|
||||
break;
|
||||
default:
|
||||
if (!complete(args[0], pid))
|
||||
_d("Successful modprobe of %s", alias);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alias_add(char *alias)
|
||||
{
|
||||
struct module *m;
|
||||
|
||||
m = malloc(sizeof(*m));
|
||||
if (!m)
|
||||
return;
|
||||
m->alias = strdup(alias);
|
||||
if (!m->alias) {
|
||||
free(m);
|
||||
return;
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&modules, m, link);
|
||||
}
|
||||
|
||||
static void alias_remove(struct module *m)
|
||||
{
|
||||
TAILQ_REMOVE(&modules, m, link);
|
||||
free(m->alias);
|
||||
free(m);
|
||||
}
|
||||
|
||||
static int alias_exist(char *alias)
|
||||
{
|
||||
struct module *m;
|
||||
|
||||
TAILQ_FOREACH(m, &modules, link) {
|
||||
if (!strcmp(m->alias, alias))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_alias(const char *path, const struct stat *st, int flag, struct FTW *unused)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[256];
|
||||
|
||||
if (fnmatch("*/modalias", path, 0))
|
||||
return 0;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
chomp(buf);
|
||||
if (!alias_exist(buf))
|
||||
alias_add(buf);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coldplug(void *arg)
|
||||
{
|
||||
struct module *m, *tmp;
|
||||
int rc = 0;
|
||||
|
||||
print_desc("Cold plugging system", NULL);
|
||||
nftw("/sys/devices", scan_alias, 200, FTW_DEPTH);
|
||||
TAILQ_FOREACH_SAFE(m, &modules, link, tmp) {
|
||||
rc += modprobe(m->alias);
|
||||
alias_remove(m);
|
||||
}
|
||||
|
||||
print_result(rc);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = coldplug },
|
||||
.depends = { "bootmisc", }
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+36
-214
@@ -1,7 +1,6 @@
|
||||
/* Simple pidfile event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||
* Copyright (c) 2016-2020 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -22,234 +21,55 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
#include <lite/queue.h>
|
||||
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "helpers.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
|
||||
struct wd_entry {
|
||||
TAILQ_ENTRY(wd_entry) link;
|
||||
char *path;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct context {
|
||||
int fd;
|
||||
TAILQ_HEAD(, wd_entry) wd_list;
|
||||
int wd;
|
||||
};
|
||||
|
||||
static struct context pidfile_ctx;
|
||||
|
||||
static int watcher_add(struct context *ctx, char *path)
|
||||
{
|
||||
struct wd_entry *wd;
|
||||
uint32_t mask = IN_ONLYDIR | IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO;
|
||||
char *ptr;
|
||||
int fd;
|
||||
|
||||
_d("pidfile: Adding new watcher for path %s", path);
|
||||
ptr = strstr(path, "run/");
|
||||
if (ptr) {
|
||||
char *slash;
|
||||
|
||||
ptr += 4;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
ptr = slash++;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
_d("pidfile: Path too deep, skipping.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fd = inotify_add_watch(ctx->fd, path, mask);
|
||||
if (fd < 0) {
|
||||
_pe("inotify_add_watch()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wd = malloc(sizeof(struct wd_entry));
|
||||
if (!wd) {
|
||||
_pe("Failed allocating new `struct wd_entry`");
|
||||
inotify_rm_watch(ctx->fd, fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wd->path = path;
|
||||
wd->fd = fd;
|
||||
TAILQ_INSERT_HEAD(&ctx->wd_list, wd, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watcher_del(struct context *ctx, struct wd_entry *wde)
|
||||
{
|
||||
_d("pidfile: Removing watcher for removed path %s", wde->path);
|
||||
TAILQ_REMOVE(&ctx->wd_list, wde, link);
|
||||
inotify_rm_watch(ctx->fd, wde->fd);
|
||||
free(wde->path);
|
||||
free(wde);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_conds(char *name, uint32_t mask)
|
||||
{
|
||||
svc_t *svc;
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
_d("name: %s", name);
|
||||
svc = svc_find_by_pidfile(name);
|
||||
if (!svc) {
|
||||
_d("pidfile: No matching svc for %s", name);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("Event %s for svc %s", name, svc->cmd);
|
||||
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
if (svc_is_forking(svc)) {
|
||||
pid_t pid;
|
||||
|
||||
pid = pid_file_read(pid_file(svc));
|
||||
_d("Forking service %s changed PID from %d to %d",
|
||||
svc->cmd, svc->pid, pid);
|
||||
svc->pid = pid;
|
||||
}
|
||||
|
||||
cond_set(cond);
|
||||
} else if (mask & IN_DELETE)
|
||||
cond_clear(cond);
|
||||
}
|
||||
|
||||
/* synthesize events in case of new run dirs */
|
||||
static void scan_for_pidfiles(struct context *ctx, char *dir, int len)
|
||||
{
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
char path[len + 6];
|
||||
int rc;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/*.pid", dir);
|
||||
rc = glob(path, GLOB_NOSORT, NULL, &gl);
|
||||
if (rc && rc != GLOB_NOMATCH)
|
||||
return;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/pid", dir);
|
||||
rc = glob(path, GLOB_NOSORT | GLOB_APPEND, NULL, &gl);
|
||||
if (rc && rc != GLOB_NOMATCH)
|
||||
return;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
_d("scan found %s", gl.gl_pathv[i]);
|
||||
update_conds(gl.gl_pathv[i], IN_CREATE);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
|
||||
static void handle_dir(struct context *ctx, struct wd_entry *wde, char *name, int mask)
|
||||
{
|
||||
char *path;
|
||||
int plen;
|
||||
int exist = 0;
|
||||
|
||||
plen = snprintf(NULL, 0, "%s/%s", wde->path, name) + 1;
|
||||
path = malloc(plen);
|
||||
if (!path) {
|
||||
_pe("Failed allocating path buffer for watcher");
|
||||
return;
|
||||
}
|
||||
snprintf(path, plen, "%s/%s", wde->path, name);
|
||||
_d("pidfile: path is %s", path);
|
||||
|
||||
TAILQ_FOREACH(wde, &ctx->wd_list, link) {
|
||||
if (!strcmp(wde->path, path)) {
|
||||
exist = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & IN_CREATE) {
|
||||
if (!exist) {
|
||||
int rc = watcher_add(ctx, path);
|
||||
|
||||
scan_for_pidfiles(ctx, path, plen);
|
||||
if (!rc)
|
||||
return;
|
||||
}
|
||||
} else if (mask & IN_DELETE) {
|
||||
if (exist)
|
||||
watcher_del(&pidfile_ctx, wde);
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
static void pidfile_callback(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
static char cond[MAX_COND_LEN];
|
||||
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz, off;
|
||||
size_t buflen = 8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1;
|
||||
char *buf;
|
||||
ssize_t sz, len;
|
||||
svc_t *svc;
|
||||
|
||||
buf= malloc(buflen);
|
||||
if (!buf) {
|
||||
_pe("Failed allocating buffer for inotify events");
|
||||
return;
|
||||
}
|
||||
|
||||
_d("pidfile: Entering ... reading %zu bytes into ev_buf[]", buflen - 1);
|
||||
sz = read(fd, buf, buflen - 1);
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
goto done;
|
||||
return;
|
||||
}
|
||||
buf[sz] = 0;
|
||||
_d("pidfile: Read %zd bytes, processing ...", sz);
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
off = 0;
|
||||
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
|
||||
struct wd_entry *wde;
|
||||
|
||||
ev = (struct inotify_event *)&buf[off];
|
||||
|
||||
_d("pidfile: path %s, event: 0x%08x", ev->name, ev->mask);
|
||||
if (!ev->mask)
|
||||
for (ev = (void *)ev_buf;
|
||||
sz > (ssize_t)sizeof(*ev);
|
||||
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||
if (!ev->mask || !strstr(ev->name, ".pid"))
|
||||
continue;
|
||||
|
||||
/* Find base path for this event */
|
||||
TAILQ_FOREACH(wde, &pidfile_ctx.wd_list, link) {
|
||||
if (wde->fd == ev->wd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ev->mask & IN_ISDIR) {
|
||||
handle_dir(&pidfile_ctx, wde, ev->name, ev->mask);
|
||||
svc = svc_find_by_pidfile(ev->name);
|
||||
if (!svc)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & IN_DELETE) {
|
||||
_d("pidfile %s/%s removed ...", wde->path, ev->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||
update_conds(ev->name, ev->mask);
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
cond_set(cond);
|
||||
} else if (ev->mask & IN_DELETE)
|
||||
cond_clear(cond);
|
||||
}
|
||||
done:
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -271,7 +91,7 @@ static void pidfile_reconf(void *arg)
|
||||
if (svc_is_changed(svc) || svc_is_starting(svc))
|
||||
continue;
|
||||
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
if (cond_get(cond) == COND_ON)
|
||||
continue;
|
||||
|
||||
@@ -290,6 +110,8 @@ static void pidfile_reconf(void *arg)
|
||||
static void pidfile_init(void *arg)
|
||||
{
|
||||
char *path;
|
||||
struct context *ctx = arg;
|
||||
uint32_t mask = IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO;
|
||||
|
||||
/*
|
||||
* The bootmisc plugin is responsible for setting up /var/run.
|
||||
@@ -302,9 +124,15 @@ static void pidfile_init(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&pidfile_ctx.wd_list);
|
||||
if (watcher_add(&pidfile_ctx, path))
|
||||
close(pidfile_ctx.fd);
|
||||
ctx->wd = inotify_add_watch(ctx->fd, path, mask);
|
||||
free(path);
|
||||
|
||||
if (ctx->wd < 0) {
|
||||
_pe("inotify_add_watch()");
|
||||
close(ctx->fd);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("pidfile monitor active");
|
||||
}
|
||||
|
||||
@@ -316,7 +144,7 @@ static struct context pidfile_ctx;
|
||||
*/
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
|
||||
.hook[HOOK_BASEFS_UP] = { .arg = &pidfile_ctx, .cb = pidfile_init },
|
||||
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
||||
.io = {
|
||||
.cb = pidfile_callback,
|
||||
@@ -339,13 +167,7 @@ PLUGIN_INIT(plugin_init)
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
struct wd_entry *wde, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(wde, &pidfile_ctx.wd_list, link, tmp) {
|
||||
inotify_rm_watch(pidfile_ctx.fd, wde->fd);
|
||||
free(wde->path);
|
||||
free(wde);
|
||||
}
|
||||
inotify_rm_watch(pidfile_ctx.fd, pidfile_ctx.wd);
|
||||
close(pidfile_ctx.fd);
|
||||
|
||||
plugin_unregister(&plugin);
|
||||
|
||||
+2
-4
@@ -1,4 +1,4 @@
|
||||
EXTRA_DIST = rescue.conf cgreaper.sh
|
||||
EXTRA_DIST = rescue.conf
|
||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
if STATIC
|
||||
@@ -8,17 +8,15 @@ else
|
||||
AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
bin_PROGRAMS = logit
|
||||
sbin_PROGRAMS = finit initctl reboot
|
||||
pkglibexec_SCRIPTS = cgreaper.sh
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS = watchdogd
|
||||
endif
|
||||
|
||||
if LOGIT
|
||||
bin_PROGRAMS = logit
|
||||
logit_SOURCES = logit.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
endif
|
||||
|
||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
cond.c cond-w.c cond.h \
|
||||
|
||||
@@ -108,11 +108,11 @@ static int missing(char *job, char *id)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_query(char *buf, size_t len)
|
||||
static int do_query(struct init_request *rq, size_t len)
|
||||
{
|
||||
memset(query_buf, 0, sizeof(query_buf));
|
||||
if (svc_parse_jobstr(buf, len, NULL, missing)) {
|
||||
memcpy(buf, query_buf, len);
|
||||
if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) {
|
||||
memcpy(rq->data, query_buf, sizeof(rq->data));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -181,6 +181,47 @@ ev_t ev_list[] = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int do_handle_event(char *event)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ev_list[i].event; i++) {
|
||||
ev_t *e = &ev_list[i];
|
||||
size_t len = MAX(strlen(e->event), strlen(event));
|
||||
|
||||
if (!strncasecmp(e->event, event, len)) {
|
||||
e->cb();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (event[0] == '-')
|
||||
cond_clear(&event[1]);
|
||||
else if (event[0] == '+')
|
||||
cond_set(&event[1]);
|
||||
else
|
||||
cond_set(event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_handle_emit(char *buf, size_t len)
|
||||
{
|
||||
int result = 0;
|
||||
char *input, *event, *pos;
|
||||
|
||||
input = sanitize(buf, len);
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
event = strtok_r(input, " ", &pos);
|
||||
while (event) {
|
||||
result += do_handle_event(event);
|
||||
event = strtok_r(NULL, " ", &pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void send_svc(int sd, svc_t *svc)
|
||||
{
|
||||
svc_t empty;
|
||||
@@ -275,30 +316,31 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
case INIT_CMD_START_SVC:
|
||||
_d("start %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_start(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_STOP_SVC:
|
||||
_d("stop %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_stop(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
_d("restart %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_restart(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
case INIT_CMD_QUERY_INETD:
|
||||
_d("query inetd");
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_query_inetd(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case INIT_CMD_EMIT:
|
||||
_d("emit %s", rq.data);
|
||||
result = do_handle_emit(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_RUNLEVEL:
|
||||
_d("get runlevel");
|
||||
rq.runlevel = runlevel;
|
||||
@@ -348,13 +390,11 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
case INIT_CMD_SVC_QUERY:
|
||||
_d("svc query: %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_query(rq.data, sizeof(rq.data));
|
||||
result = do_query(&rq, len);
|
||||
break;
|
||||
|
||||
case INIT_CMD_SVC_FIND:
|
||||
_d("svc find: %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
|
||||
goto leave;
|
||||
|
||||
|
||||
+95
-16
@@ -30,7 +30,15 @@
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static struct {
|
||||
int active;
|
||||
char cgroup[16];
|
||||
char path[32];
|
||||
} cg_avail[200];
|
||||
|
||||
static int cg_init = 0;
|
||||
static int cg_num = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Called by Finit at early boot to mount initial cgroups
|
||||
@@ -63,15 +71,24 @@ void cgroup_init(void)
|
||||
continue;
|
||||
|
||||
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
||||
if (mkdir(rc, 0755) && EEXIST != errno)
|
||||
continue;
|
||||
if (mount("cgroup", rc, "cgroup", opts, cgroup))
|
||||
mkdir(rc, 0755);
|
||||
if (mount("cgroup", rc, "cgroup", opts, cgroup)) {
|
||||
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
cg_avail[cg_num].active = 1;
|
||||
strlcpy(cg_avail[cg_num].cgroup, cgroup, sizeof(cg_avail[cg_num].cgroup));
|
||||
strlcpy(cg_avail[cg_num].path, rc, sizeof(cg_avail[cg_num].path));
|
||||
cg_num++;
|
||||
|
||||
/* Create default group in all available cgroups */
|
||||
strlcat(rc, "/default", sizeof(rc));
|
||||
mkdir(rc, 0755);
|
||||
}
|
||||
|
||||
/* Default cgroups for process monitoring */
|
||||
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
mkdir("/sys/fs/cgroup/finit", 0755);
|
||||
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
|
||||
_pe("Failed mounting Finit cgroup hierarchy");
|
||||
goto fail;
|
||||
@@ -82,12 +99,9 @@ void cgroup_init(void)
|
||||
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
|
||||
|
||||
/* Default groups, PID 1, services, and user/login processes */
|
||||
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir("/sys/fs/cgroup/finit/user", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
mkdir("/sys/fs/cgroup/finit/init", 0755);
|
||||
mkdir("/sys/fs/cgroup/finit/system", 0755);
|
||||
mkdir("/sys/fs/cgroup/finit/user", 0755);
|
||||
|
||||
/* Move ourselves to init */
|
||||
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
|
||||
@@ -98,11 +112,54 @@ fail:
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int move_pid(char *group, char *name, char *id, int pid)
|
||||
int cgroup_add(char *name, void (*cb)(char *, void *), void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cg_num; i++) {
|
||||
char path[80];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", cg_avail[i].path, name);
|
||||
if (mkdir(path, 0755) && EEXIST != errno) {
|
||||
rc++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* XXX: Add to cache of added groups, for cgroup_find() */
|
||||
if (cb)
|
||||
cb(path, arg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: Temporary hackish implementation */
|
||||
int cgroup_find(char *name, char *group, size_t len)
|
||||
{
|
||||
char path[80];
|
||||
|
||||
if (cg_num <= 0)
|
||||
return 1;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", cg_avail[0].path, name);
|
||||
if (access(path, F_OK))
|
||||
return 1;
|
||||
|
||||
strlcpy(group, name, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int cgroup_cpuset_range(char *group, int min, int max) */
|
||||
/* { */
|
||||
/* } */
|
||||
|
||||
static int move_pid(char *group, char *name, int pid)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||
if (mkdir(path, 0755) && errno != EEXIST)
|
||||
return 1;
|
||||
|
||||
@@ -116,11 +173,13 @@ int cgroup_user(char *name)
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
return move_pid("finit/user", name, "0", getpid());
|
||||
return move_pid("finit/user", name, getpid());
|
||||
}
|
||||
|
||||
int cgroup_service(char *nm, char *id, int pid)
|
||||
int cgroup_service(char *cmd, int pid)
|
||||
{
|
||||
char *nm;
|
||||
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
@@ -129,7 +188,27 @@ int cgroup_service(char *nm, char *id, int pid)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return move_pid("finit/system", nm, id, pid);
|
||||
nm = strrchr(cmd, '/');
|
||||
if (nm)
|
||||
nm++;
|
||||
else
|
||||
nm = cmd;
|
||||
|
||||
return move_pid("finit/system", nm, pid);
|
||||
}
|
||||
|
||||
int cgroup_assign(char *name, int pid)
|
||||
{
|
||||
char path[120];
|
||||
int i, rc = 0;
|
||||
|
||||
for (i = 0; i < cg_num; i++) {
|
||||
snprintf(path, sizeof(path), "%s/%s/cgroup.procs",
|
||||
cg_avail[i].path, name);
|
||||
rc += echo(path, 0, "%d", pid);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-1
@@ -26,7 +26,11 @@
|
||||
|
||||
void cgroup_init (void);
|
||||
|
||||
int cgroup_add (char *name, void (*cb)(char *, void *), void *arg);
|
||||
int cgroup_find (char *name, char *group, size_t len);
|
||||
|
||||
int cgroup_user (char *name);
|
||||
int cgroup_service (char *cmd, char *id, int pid);
|
||||
int cgroup_service (char *cmd, int pid);
|
||||
int cgroup_assign (char *name, int pid);
|
||||
|
||||
#endif /* FINIT_CGROUP_H_ */
|
||||
|
||||
+1
-64
@@ -31,68 +31,6 @@
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
/*
|
||||
* The service condition name is constructed from the 'svc/' prefix, the
|
||||
* dirname of the svc->cmd, e.g., '/usr/bin/teamd' => 'usr/bin', and the
|
||||
* service's pid:filename, including an optional subdirectory, without
|
||||
* the .pid extension.
|
||||
*
|
||||
* The following example uses the team (aggregate) service:
|
||||
*
|
||||
* service pid:!/run/teamd/a1.pid /usr/bin/teamd -f /etc/teamd/a1.conf
|
||||
*
|
||||
* => 'svc/' + 'usr/bin' + 'teamd/a1' => svc/usr/bin/teamd/a1
|
||||
*
|
||||
* The next example uses the dbus-daemon:
|
||||
*
|
||||
* service pid:!/run/dbus/pid /usr/bin/dbus-daemon
|
||||
*
|
||||
* => 'svc/' + 'usr/bin' + 'dbus' => svc/usr/bin/dbus
|
||||
*
|
||||
* The last example uses lxc-start to start container foo:
|
||||
*
|
||||
* service pid:!/run/lxc/foo.pid lxc-start -n foo -F -p /run/lxc/foo.pid -- Container foo
|
||||
*
|
||||
* => 'svc/' + '' + 'lxc/foo' => svc/lxc/foo
|
||||
*/
|
||||
char *mkcond(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
char path[256];
|
||||
char *pidfile;
|
||||
char *ptr;
|
||||
|
||||
strlcpy(path, svc->cmd, sizeof(path));
|
||||
ptr = rindex(path, '/');
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
else
|
||||
path[0] = 0;
|
||||
|
||||
pidfile = pid_file(svc);
|
||||
ptr = strstr(pidfile, "run");
|
||||
if (ptr)
|
||||
ptr += 3;
|
||||
else
|
||||
ptr = rindex(pidfile, '/');
|
||||
|
||||
snprintf(buf, len, "svc%s%s%s", path[0] != 0 && path[0] != '/' ? "/" : "", path, ptr);
|
||||
_d("Composed condition from cmd %s (path %s) and pidfile %s => %s", svc->cmd, path, ptr, buf);
|
||||
|
||||
/* Case: /var/run/dbus/pid */
|
||||
ptr = strstr(buf, "/pid");
|
||||
if (ptr && !strcmp(ptr, "/pid"))
|
||||
*ptr = 0;
|
||||
|
||||
/* Case /var/run/teamd/a1.pid */
|
||||
ptr = strstr(buf, ".pid");
|
||||
if (ptr && !strcmp(ptr, ".pid"))
|
||||
*ptr = 0;
|
||||
|
||||
_d("Creating condition => %s", buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int cond_set_gen(const char *file, unsigned int gen)
|
||||
{
|
||||
char *ptr, path[256];
|
||||
@@ -190,7 +128,6 @@ static int svc_has_cond(svc_t *svc)
|
||||
case SVC_TYPE_SERVICE:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_SYSV:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
@@ -234,7 +171,7 @@ void cond_set_oneshot(const char *name)
|
||||
return;
|
||||
|
||||
path = cond_path(name);
|
||||
_d("%s => %s", name, path);
|
||||
_d("s => %s", name, path);
|
||||
|
||||
if (cond_checkpath(path))
|
||||
return;
|
||||
|
||||
+13
-6
@@ -29,6 +29,13 @@
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
|
||||
char *mkcond(char *buf, size_t len, char *nm)
|
||||
{
|
||||
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *condstr(enum cond_state s)
|
||||
{
|
||||
static const char *strs[] = {
|
||||
@@ -42,18 +49,18 @@ const char *condstr(enum cond_state s)
|
||||
|
||||
const char *cond_path(const char *name)
|
||||
{
|
||||
static char path[256];
|
||||
char tmp[MAX_ARG_LEN];
|
||||
static char file[MAX_ARG_LEN];
|
||||
|
||||
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
|
||||
|
||||
return pid_runpath(tmp, path, sizeof(path));
|
||||
return pid_runpath(tmp, file, sizeof(file));
|
||||
}
|
||||
|
||||
unsigned int cond_get_gen(const char *file)
|
||||
{
|
||||
unsigned int gen;
|
||||
char *ptr, path[256];
|
||||
unsigned int gen;
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
@@ -92,9 +99,9 @@ enum cond_state cond_get(const char *name)
|
||||
|
||||
enum cond_state cond_get_agg(const char *names)
|
||||
{
|
||||
static char conds[MAX_COND_LEN];
|
||||
enum cond_state s = COND_ON;
|
||||
char *cond;
|
||||
enum cond_state s = COND_ON;
|
||||
static char conds[MAX_COND_LEN];
|
||||
|
||||
if (!names)
|
||||
return COND_ON;
|
||||
@@ -108,8 +115,8 @@ enum cond_state cond_get_agg(const char *names)
|
||||
|
||||
int cond_affects(const char *name, const char *names)
|
||||
{
|
||||
static char conds[MAX_COND_LEN];
|
||||
char *cond;
|
||||
static char conds[MAX_COND_LEN];
|
||||
|
||||
if (!name || !names)
|
||||
return 0;
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@
|
||||
#define FINIT_COND_H_
|
||||
|
||||
#include <paths.h>
|
||||
#include <svc.h>
|
||||
|
||||
#define COND_DIR "finit/cond"
|
||||
#define COND_PATH _PATH_VARRUN COND_DIR
|
||||
@@ -14,7 +13,7 @@ typedef enum cond_state {
|
||||
COND_ON
|
||||
} cond_state_t;
|
||||
|
||||
char *mkcond (svc_t *svc, char *buf, size_t len);
|
||||
char *mkcond (char *buf, size_t len, char *nm);
|
||||
const char *condstr (enum cond_state s);
|
||||
const char *cond_path (const char *name);
|
||||
unsigned int cond_get_gen (const char *path);
|
||||
|
||||
+62
-18
@@ -33,6 +33,7 @@
|
||||
#include <glob.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
@@ -53,6 +54,7 @@ struct conf_change {
|
||||
char *name;
|
||||
};
|
||||
|
||||
static char cgroup[32];
|
||||
static uev_t w1, w2, w3, w4;
|
||||
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
||||
|
||||
@@ -342,6 +344,45 @@ error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
}
|
||||
|
||||
static void cpuset_cb(char *grp, void *arg)
|
||||
{
|
||||
int cpuset = *(int *)arg;
|
||||
char path[128];
|
||||
|
||||
if (!strstr(grp, "cpuset"))
|
||||
return;
|
||||
|
||||
/* Both .cpus and .mems are mandatory settings */
|
||||
snprintf(path, sizeof(path), "%s/cpuset.cpus", grp);
|
||||
echo(path, 0, "%d", cpuset);
|
||||
snprintf(path, sizeof(path), "%s/cpuset.mems", grp);
|
||||
echo(path, 0, "0");
|
||||
|
||||
/* XXX: HACK for now, fix proper CPU allocation */
|
||||
snprintf(path, sizeof(path), "%s/cpuset.cpu_exclusive", grp);
|
||||
echo(path, 0, "1");
|
||||
}
|
||||
|
||||
static void parse_cgroup(char *line)
|
||||
{
|
||||
char *group, *token;
|
||||
int cpuset = -1;
|
||||
|
||||
group = strtok(line, " ");
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
while ((token = strtok(NULL, " "))) {
|
||||
if (!strncmp(token, "cpuset:", 7)) {
|
||||
token += 7;
|
||||
cpuset = atoi(token); /* XXX: fixme */
|
||||
}
|
||||
}
|
||||
|
||||
if (cpuset >= 0)
|
||||
cgroup_add(group, cpuset_cb, &cpuset);
|
||||
}
|
||||
|
||||
static void parse_static(char *line)
|
||||
{
|
||||
char *x;
|
||||
@@ -430,6 +471,11 @@ static void parse_static(char *line)
|
||||
cfglevel = 2; /* Fallback */
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "cgroup ", x)) {
|
||||
parse_cgroup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
@@ -448,32 +494,26 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
|
||||
/* Monitored daemon, will be respawned on exit */
|
||||
if (MATCH_CMD(line, "service ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, x, rlimit, file);
|
||||
service_register(SVC_TYPE_SERVICE, x, rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* One-shot task, will not be respawned */
|
||||
if (MATCH_CMD(line, "task ", x)) {
|
||||
service_register(SVC_TYPE_TASK, x, rlimit, file);
|
||||
service_register(SVC_TYPE_TASK, x, rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Like task but waits for completion, useful w/ [S] */
|
||||
if (MATCH_CMD(line, "run ", x)) {
|
||||
service_register(SVC_TYPE_RUN, x, rlimit, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Similar to task but is treated like a SysV init script */
|
||||
if (MATCH_CMD(line, "sysv ", x)) {
|
||||
service_register(SVC_TYPE_SYSV, x, rlimit, file);
|
||||
service_register(SVC_TYPE_RUN, x, rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Classic inetd service */
|
||||
if (MATCH_CMD(line, "inetd ", x)) {
|
||||
#ifdef INETD_ENABLED
|
||||
service_register(SVC_TYPE_INETD, x, rlimit, file);
|
||||
service_register(SVC_TYPE_INETD, x, rlimit, cgroup, file);
|
||||
#else
|
||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||
#endif
|
||||
@@ -486,9 +526,15 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
return;
|
||||
}
|
||||
|
||||
/* All subsequent run/task/service/tty/etc. directives to use this cgroup */
|
||||
if (MATCH_CMD(line, "group ", x)) {
|
||||
cgroup_find(x, cgroup, sizeof(cgroup));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Regular or serial TTYs to run getty */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
tty_register(strip_line(x), rlimit, file);
|
||||
tty_register(strip_line(x), rlimit, cgroup, file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -506,8 +552,8 @@ static void tabstospaces(char *line)
|
||||
|
||||
static int parse_conf_dynamic(char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
@@ -518,6 +564,9 @@ static int parse_conf_dynamic(char *file)
|
||||
/* Prepare default limits for each service */
|
||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||
|
||||
/* Prepare default cgroup for each service */
|
||||
strlcpy(cgroup, "default", sizeof(cgroup));
|
||||
|
||||
_d("Parsing %s <<<<<<", file);
|
||||
while (!feof(fp)) {
|
||||
char line[LINE_SIZE] = "";
|
||||
@@ -598,7 +647,7 @@ int conf_reload(void)
|
||||
/* If rescue.conf is missing, fall back to a root shell */
|
||||
rc = parse_conf(RESCUE_CONF);
|
||||
if (rc)
|
||||
tty_register(line, global_rlimit, NULL);
|
||||
tty_register(line, global_rlimit, cgroup, NULL);
|
||||
|
||||
print(rc, "Entering rescue mode");
|
||||
goto done;
|
||||
@@ -705,8 +754,6 @@ static int do_change(char *name, uint32_t mask)
|
||||
{
|
||||
struct conf_change *node;
|
||||
|
||||
_d("Change detected for %s, mask 0x%08x", name, mask);
|
||||
|
||||
node = conf_find(name);
|
||||
if (mask & (IN_DELETE | IN_MOVED_FROM)) {
|
||||
drop_change(node);
|
||||
@@ -785,10 +832,8 @@ static void conf_cb(uev_t *w, void *arg, int events)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AUTO_RELOAD
|
||||
if (conf_any_change())
|
||||
service_reload_dynamic();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
|
||||
@@ -841,7 +886,6 @@ static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
_d("Set up inotify watcher for %s ...", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+17
-25
@@ -231,7 +231,6 @@ int exec_runtask(char *cmd, char *args[])
|
||||
strlcat(buf, args[i], sizeof(buf));
|
||||
}
|
||||
logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf);
|
||||
_d("Calling %s %s", _PATH_BSHELL, buf);
|
||||
|
||||
return execvp(_PATH_BSHELL, argv);
|
||||
}
|
||||
@@ -240,7 +239,6 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct termios term;
|
||||
char name[80];
|
||||
|
||||
/*
|
||||
* Reset to sane defaults in case of messup from prev. session
|
||||
@@ -284,16 +282,13 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
||||
|
||||
/* Finit is responsible for the UTMP INIT_PROCESS record */
|
||||
utmp_set_init(tty, 0);
|
||||
if (!strncmp("/dev/", tty, 5))
|
||||
tty += 5;
|
||||
snprintf(name, sizeof(name), "%s %s", procname, tty);
|
||||
prctl(PR_SET_NAME, name, 0, 0, 0);
|
||||
prctl(PR_SET_NAME, procname, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int activate_console(int noclear, int nowait)
|
||||
{
|
||||
struct termios orig;
|
||||
int ret = 0;
|
||||
struct termios term;
|
||||
|
||||
if (!noclear)
|
||||
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
||||
@@ -302,20 +297,15 @@ static int activate_console(int noclear, int nowait)
|
||||
return 1;
|
||||
|
||||
/* Disable ECHO, XON/OFF while waiting for <CR> */
|
||||
if (!tcgetattr(STDIN_FILENO, &orig)) {
|
||||
struct termios c = orig;
|
||||
|
||||
c.c_iflag &= ~(BRKINT|ICRNL|INPCK|ISTRIP|IXON|IXOFF);
|
||||
c.c_oflag &= ~(OPOST);
|
||||
c.c_cflag |= (CS8);
|
||||
c.c_lflag &= ~(ECHO|ICANON|IEXTEN|ISIG);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &c);
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_iflag &= ~(IXON|IXOFF);
|
||||
term.c_lflag &= ~ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||
}
|
||||
|
||||
while (!fexist(SYNC_SHUTDOWN)) {
|
||||
char c;
|
||||
static const char clr[] = "\r\e[2K";
|
||||
static const char cup[] = "\e[A";
|
||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||
|
||||
if (fexist(SYNC_STOPPED)) {
|
||||
@@ -325,21 +315,23 @@ static int activate_console(int noclear, int nowait)
|
||||
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
(void)write(STDERR_FILENO, msg, strlen(msg));
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
(void)write(STDERR_FILENO, cup, strlen(cup));
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Restore TTY */
|
||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &orig) == -1)
|
||||
ret = 0;
|
||||
/* Restore ECHO, XON/OFF */
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_iflag |= IXON|IXOFF;
|
||||
term.c_lflag |= ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -377,7 +369,7 @@ pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, stru
|
||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||
}
|
||||
|
||||
prepare_tty(tty, speed, "tty", rlimit);
|
||||
prepare_tty(tty, speed, "finit-getty", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
_exit(getty(tty, speed, term, NULL));
|
||||
}
|
||||
@@ -468,7 +460,7 @@ int run_parts(char *dir, char *cmd)
|
||||
char *name = e[i]->d_name;
|
||||
pid_t pid = 0;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
snprintf(path, sizeof(path), "%s/%s ", dir, name);
|
||||
if (stat(path, &st)) {
|
||||
_d("Failed stat(%s): %s", path, strerror(errno));
|
||||
continue;
|
||||
@@ -484,9 +476,9 @@ int run_parts(char *dir, char *cmd)
|
||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||
_d("Checking if %s is a sysvinit startstop script ...", name);
|
||||
if (name[0] == 'S' && isdigit(name[1]))
|
||||
strlcat(path, " start", sizeof(path));
|
||||
strlcat(path, "start", sizeof(path));
|
||||
else if (name[0] == 'K' && isdigit(name[1]))
|
||||
strlcat(path, " stop", sizeof(path));
|
||||
strlcat(path, "stop", sizeof(path));
|
||||
} else {
|
||||
strlcat(path, cmd, sizeof(path));
|
||||
}
|
||||
|
||||
+10
-19
@@ -73,6 +73,9 @@ static int udev = 0; /* Runtime detection of udev */
|
||||
*/
|
||||
static void banner(void)
|
||||
{
|
||||
char *buf = INIT_HEADING;
|
||||
char separator[SCREEN_WIDTH];
|
||||
|
||||
if (plugin_exists(HOOK_BANNER)) {
|
||||
plugin_run_hooks(HOOK_BANNER);
|
||||
return;
|
||||
@@ -81,7 +84,8 @@ static void banner(void)
|
||||
if (log_is_silent())
|
||||
return;
|
||||
|
||||
print_banner(INIT_HEADING);
|
||||
memset(separator, '=', sizeof(separator));
|
||||
fprintf(stderr, "\n\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
||||
}
|
||||
|
||||
static int ismnt(char *file, char *dir)
|
||||
@@ -305,7 +309,7 @@ static void final_worker(void *work)
|
||||
finalize();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
struct wq crank = {
|
||||
.cb = crank_worker
|
||||
@@ -325,19 +329,6 @@ int main(int argc, char *argv[])
|
||||
if (getpid() != 1)
|
||||
return client(argc, argv);
|
||||
|
||||
/*
|
||||
* Hide command line arguments from ps (in particular for
|
||||
* forked children that don't execv()). This is an ugly
|
||||
* hack that only works on Linux.
|
||||
* https://web.archive.org/web/20110227041321/http://netsplit.com/2007/01/10/hiding-arguments-from-ps/
|
||||
*/
|
||||
if (argc > 1) {
|
||||
char *arg_end;
|
||||
|
||||
arg_end = argv[argc-1] + strlen (argv[argc-1]);
|
||||
*arg_end = ' ';
|
||||
}
|
||||
|
||||
/*
|
||||
* Initalize event context.
|
||||
*/
|
||||
@@ -459,19 +450,19 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* Register udevd as a monitored service */
|
||||
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, "default", NULL)) {
|
||||
_pe("Failed registering %s", path);
|
||||
udev = 0;
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
|
||||
"udevadm trigger -c add -t devices "
|
||||
"-- Requesting device events", path);
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||
|
||||
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
|
||||
"udevadm trigger -c add -t subsystems "
|
||||
"-- Requesting subsystem events", path);
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||
}
|
||||
free(path);
|
||||
} else {
|
||||
@@ -492,7 +483,7 @@ int main(int argc, char *argv[])
|
||||
* Start bundled watchdogd as soon as possible, if enabled
|
||||
*/
|
||||
if (which(FINIT_LIBPATH_ "/watchdogd"))
|
||||
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd", global_rlimit, NULL);
|
||||
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd", global_rlimit, "default", NULL);
|
||||
|
||||
/*
|
||||
* Mount filesystems
|
||||
|
||||
+2
-2
@@ -45,6 +45,7 @@
|
||||
#define _PATH_VARRUN "/var/run/"
|
||||
#endif
|
||||
|
||||
|
||||
#define CMD_SIZE 256
|
||||
#define LINE_SIZE 1024
|
||||
#define BUF_SIZE 4096
|
||||
@@ -74,7 +75,7 @@
|
||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
|
||||
#define INIT_CMD_QUERY_INETD 14
|
||||
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
||||
#define INIT_CMD_EMIT 15
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
|
||||
#define INIT_CMD_SVC_ITER 129
|
||||
@@ -83,7 +84,6 @@
|
||||
#define INIT_CMD_NACK 254
|
||||
#define INIT_CMD_ACK 255
|
||||
|
||||
/* Traditionally aligned on 384 bytes */
|
||||
struct init_request {
|
||||
int magic; /* Magic number */
|
||||
int cmd; /* What kind of request */
|
||||
|
||||
+5
-7
@@ -211,9 +211,8 @@ static int do_login(char *name)
|
||||
|
||||
int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
{
|
||||
const char cln[] = "\r\e[2K\n";
|
||||
char name[30];
|
||||
int fd;
|
||||
char name[30];
|
||||
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
@@ -225,15 +224,10 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
warn("Failed TIOCSCTTY");
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(STDIN_FILENO, speed);
|
||||
(void)write(STDOUT_FILENO, cln, strlen(cln));
|
||||
|
||||
/* The getty process is responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
if (!user)
|
||||
@@ -241,6 +235,10 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
else
|
||||
strlcpy(name, user, sizeof(name));
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
|
||||
if (term && term[0])
|
||||
setenv("TERM", term, 1);
|
||||
|
||||
|
||||
+32
-132
@@ -39,30 +39,6 @@
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
static int progress_style = PROGRESS_STYLE;
|
||||
|
||||
/*
|
||||
* Note: the pending status (⋯) must be last item. Also, ⋯ is not
|
||||
* off-by-one, it's a multi-byte character.
|
||||
*/
|
||||
#define STATUS_CLASS { \
|
||||
CHOOSE(" OK ", " OK ", "\e[1;32m"), /* Green */ \
|
||||
CHOOSE("FAIL", "FAIL", "\e[1;31m"), /* Red */ \
|
||||
CHOOSE("WARN", "WARN", "\e[1;31m"), /* Red */ \
|
||||
CHOOSE(" \\/ ", " ⋯ ", "\e[1;33m"), /* Yellow */ \
|
||||
}
|
||||
|
||||
#define CHOOSE(x,y,z) x
|
||||
static const char *status1[] = STATUS_CLASS;
|
||||
#undef CHOOSE
|
||||
|
||||
#define CHOOSE(x,y,z) y
|
||||
static const char *status2[] = STATUS_CLASS;
|
||||
#undef CHOOSE
|
||||
|
||||
#define CHOOSE(x,y,z) z
|
||||
static const char *color[] = STATUS_CLASS;
|
||||
|
||||
|
||||
char *strip_line(char *line)
|
||||
{
|
||||
@@ -81,81 +57,6 @@ char *strip_line(char *line)
|
||||
return line;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return screen length of string, not counting escape chars, and
|
||||
* accounting for unicode characters as only one screen byte wide
|
||||
*/
|
||||
size_t slen(char *string)
|
||||
{
|
||||
unsigned char *buf = (unsigned char *)string;
|
||||
size_t len = 0;
|
||||
|
||||
while (*buf) {
|
||||
/* Skip ANSI escape sequences */
|
||||
if (*buf == '\e') {
|
||||
while (*buf && !isalpha(*buf))
|
||||
buf++;
|
||||
continue;
|
||||
}
|
||||
/* Skip 3-byte unicode */
|
||||
if (*buf == 0xe2) {
|
||||
for (int cnt = 3; *buf && cnt >= 0; cnt--)
|
||||
buf++;
|
||||
continue;
|
||||
}
|
||||
|
||||
len++;
|
||||
buf++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* ch may be an ascii or unicode character
|
||||
*/
|
||||
static char *pad(char *buf, size_t len, char *ch, size_t width)
|
||||
{
|
||||
size_t i = slen(buf);
|
||||
|
||||
strlcat(buf, " ", len);
|
||||
|
||||
width -= 8; /* Skip leading '[ STAT ]' */
|
||||
while (i < width) {
|
||||
strlcat(buf, ch, len);
|
||||
i++;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void print_banner(const char *heading)
|
||||
{
|
||||
char buf[4 * SCREEN_WIDTH];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strlcat(buf, "\r\e[2K", sizeof(buf));
|
||||
if (progress_style == 1) {
|
||||
strlcat(buf, "\e[1m", sizeof(buf));
|
||||
strlcat(buf, heading, sizeof(buf));
|
||||
pad(buf, sizeof(buf), "=", SCREEN_WIDTH - 2);
|
||||
} else {
|
||||
size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH;
|
||||
|
||||
strlcat(buf, "\e[1;31m⏺ \e[1;33m⏺ \e[1;32m⏺ \e[0m\e[1m ", sizeof(buf));
|
||||
strlcat(buf, heading, sizeof(buf));
|
||||
|
||||
/*
|
||||
* Padding with full-width '═' sign from unicode,
|
||||
* we could also use '―' or something else.
|
||||
*/
|
||||
pad(buf, sizeof(buf), "═", wmax);
|
||||
}
|
||||
strlcat(buf, "\e[0m\n", sizeof(buf));
|
||||
|
||||
(void)write(STDERR_FILENO, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static size_t print_timestamp(char *buf, size_t len)
|
||||
{
|
||||
#if defined(CONFIG_PRINTK_TIME)
|
||||
@@ -176,26 +77,6 @@ static size_t print_timestamp(char *buf, size_t len)
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *status(int rc)
|
||||
{
|
||||
static char buf[64];
|
||||
|
||||
if (rc < 0 || rc >= (int)NELEMS(status1))
|
||||
rc = NELEMS(status1) - 1; /* Default to "⋯" (pending) */
|
||||
|
||||
if (progress_style == 1) {
|
||||
int hl = 1;
|
||||
|
||||
if (rc == 1 || rc == 2)
|
||||
hl = 7;
|
||||
|
||||
snprintf(buf, sizeof(buf), "\e[%dm[%s]\e[0m", hl, status1[rc]);
|
||||
} else
|
||||
snprintf(buf, sizeof(buf), "\e[1m[%s%s\e[0m\e[1m]\e[0m ", color[rc], status2[rc]);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void printv(const char *fmt, va_list ap)
|
||||
{
|
||||
char buf[SCREEN_WIDTH];
|
||||
@@ -210,32 +91,51 @@ void printv(const char *fmt, va_list ap)
|
||||
len = print_timestamp(buf, sizeof(buf));
|
||||
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
|
||||
|
||||
if (progress_style == 1)
|
||||
fprintf(stderr, "\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
|
||||
else
|
||||
fprintf(stderr, "\r\e[2K%s%s", status(3), buf);
|
||||
len = strlen(buf);
|
||||
buf[len++] = ' ';
|
||||
for (size_t i = len; i < (sizeof(buf) - 8); i++)
|
||||
buf[i] = '.'; /* pad with dots. */
|
||||
|
||||
fprintf(stderr, "\r%s ", buf);
|
||||
}
|
||||
|
||||
void print(int rc, const char *fmt, ...)
|
||||
void print(int action, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
const char success[] = "\e[1m[ OK ]\e[0m\n";
|
||||
const char failure[] = "\e[7m[FAIL]\e[0m\n";
|
||||
const char warning[] = "\e[7m[WARN]\e[0m\n";
|
||||
const char pending[] = "\e[1m[ \\/ ]\e[0m\n";
|
||||
|
||||
if (log_is_silent())
|
||||
return;
|
||||
|
||||
if (fmt) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
printv(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
return;
|
||||
switch (action) {
|
||||
case -1:
|
||||
break;
|
||||
|
||||
if (progress_style == 1)
|
||||
fprintf(stderr, "%s\n", status(rc));
|
||||
else
|
||||
fprintf(stderr, ".\r%s\n", status(rc));
|
||||
case 0:
|
||||
write(STDERR_FILENO, success, sizeof(success));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
write(STDERR_FILENO, failure, sizeof(failure));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
write(STDERR_FILENO, warning, sizeof(warning));
|
||||
break;
|
||||
|
||||
default:
|
||||
write(STDERR_FILENO, pending, sizeof(pending));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void print_desc(char *action, char *desc)
|
||||
|
||||
@@ -57,7 +57,6 @@ int sh (char *tty);
|
||||
int stty (int fd, speed_t speed);
|
||||
speed_t stty_parse_speed(char *baud);
|
||||
|
||||
void print_banner (const char *heading);
|
||||
void printv (const char *fmt, va_list ap);
|
||||
void print (int action, const char *fmt, ...);
|
||||
void print_desc (char *action, char *desc);
|
||||
|
||||
+25
-18
@@ -140,6 +140,7 @@ static int do_svc(int cmd, char *arg)
|
||||
return client_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
|
||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
|
||||
|
||||
/*
|
||||
@@ -201,6 +202,22 @@ static void show_cond_one(const char *_conds)
|
||||
putchar('>');
|
||||
}
|
||||
|
||||
static int do_cond_magic(char op, char *cond)
|
||||
{
|
||||
char event[368]; /* sizeof(init_request.data) */
|
||||
|
||||
if (!cond || strlen(cond) < 1)
|
||||
return 1;
|
||||
|
||||
event[0] = op;
|
||||
strlcpy(&event[1], cond, sizeof(event) - 1);
|
||||
|
||||
return do_emit(event);
|
||||
}
|
||||
|
||||
static int do_cond_set (char *cond) { return do_cond_magic('+', cond); }
|
||||
static int do_cond_clear(char *cond) { return do_cond_magic('-', cond); }
|
||||
|
||||
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||
{
|
||||
int len;
|
||||
@@ -262,6 +279,8 @@ static int do_cond(char *cmd)
|
||||
char *arg;
|
||||
struct command command[] = {
|
||||
{ "show", do_cond_show },
|
||||
{ "set", do_cond_set },
|
||||
{ "clear", do_cond_clear },
|
||||
{ "dump", do_cond_dump },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -416,7 +435,7 @@ static int show_status(char *arg)
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
||||
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
char jobid[20], args[512] = "", *lvls;
|
||||
@@ -426,7 +445,7 @@ static int show_status(char *arg)
|
||||
// else
|
||||
snprintf(jobid, sizeof(jobid), "%d:%s", svc->job, svc->id);
|
||||
|
||||
printf("%-9s %8s ", jobid, svc_status(svc));
|
||||
printf("%-9s %7s ", jobid, svc_status(svc));
|
||||
if (svc_is_inetd(svc))
|
||||
printf("inetd ");
|
||||
else
|
||||
@@ -512,7 +531,7 @@ static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, stru
|
||||
|
||||
group = strrchr(fpath, '/');
|
||||
if (!group)
|
||||
goto err;
|
||||
return 0;
|
||||
group++;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
@@ -533,9 +552,8 @@ static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, stru
|
||||
|
||||
printf("\n%c :- %d %s", user ? ' ' : '|', pid, buf);
|
||||
}
|
||||
|
||||
err:
|
||||
fclose(fp);
|
||||
|
||||
if (num)
|
||||
printf("\r%c\n", user ? ' ' : '|');
|
||||
|
||||
@@ -577,6 +595,8 @@ static int usage(int rc)
|
||||
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
|
||||
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
|
||||
"\n"
|
||||
" cond set <COND> Set (assert) condition => +COND\n"
|
||||
" cond clear <COND> Clear (deassert) condition => -COND\n"
|
||||
" cond show Show condition status\n"
|
||||
" cond dump Dump all conditions and their status\n"
|
||||
"\n"
|
||||
@@ -688,19 +708,6 @@ int main(int argc, char *argv[])
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
void logit(int prio, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (prio <= LOG_ERR)
|
||||
verrx(1, fmt, ap);
|
||||
else
|
||||
vwarnx(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
@@ -62,8 +62,10 @@ void log_init(int dbg)
|
||||
void log_exit(void)
|
||||
{
|
||||
muffler();
|
||||
if (!silent)
|
||||
if (!silent) {
|
||||
sched_yield();
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unless in debug mode at shutdown, Reinitialize screen,
|
||||
|
||||
@@ -26,11 +26,6 @@
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
/* Local facility, unused in GNU but available in FreeBSD or sysklogd >= 2.0 */
|
||||
#ifndef LOG_CONSOLE
|
||||
#define LOG_CONSOLE (14<<3)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Developer error and debug messages, otherwise --> use logit() <--
|
||||
* ~~~~~~~~~~~
|
||||
|
||||
@@ -45,7 +45,7 @@ int pid_alive(pid_t pid)
|
||||
{
|
||||
char name[24]; /* Enough for max pid_t */
|
||||
|
||||
snprintf(name, sizeof(name), "/proc/%d/status", pid);
|
||||
snprintf(name, sizeof(name), "/proc/%d", pid);
|
||||
|
||||
return fexist(name);
|
||||
}
|
||||
@@ -95,8 +95,8 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
|
||||
char *pid_file(svc_t *svc)
|
||||
{
|
||||
static char path[256];
|
||||
char fn[MAX_ARG_LEN];
|
||||
static char path[MAX_ARG_LEN];
|
||||
|
||||
if (svc->pidfile[0]) {
|
||||
if (svc->pidfile[0] == '!')
|
||||
@@ -109,25 +109,6 @@ char *pid_file(svc_t *svc)
|
||||
return pid_runpath(fn, path, sizeof(path));
|
||||
}
|
||||
|
||||
pid_t pid_file_read(const char *fn)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
FILE *fp;
|
||||
char buf[42];
|
||||
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
chomp(buf);
|
||||
pid = atoi(buf);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
int pid_file_create(svc_t *svc)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
@@ -31,7 +31,6 @@ int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
char *pid_file (svc_t *svc);
|
||||
pid_t pid_file_read (const char *fn);
|
||||
int pid_file_create (svc_t *svc);
|
||||
int pid_file_parse (svc_t *svc, char *arg);
|
||||
|
||||
@@ -47,9 +46,8 @@ int pid_file_parse (svc_t *svc, char *arg);
|
||||
*/
|
||||
static inline char *pid_runpath(const char *file, char *path, size_t len)
|
||||
{
|
||||
static char *prefix = "/var/run";
|
||||
static int unknown = 1;
|
||||
int rc;
|
||||
static char *prefix = "/var/run";
|
||||
|
||||
if (unknown) {
|
||||
if (fisdir("/run"))
|
||||
@@ -62,9 +60,7 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
|
||||
else if (!strncmp(file, "/run/", 5))
|
||||
file += 5;
|
||||
|
||||
rc = snprintf(path, len, "%s/%s", prefix, file);
|
||||
if (rc < 0 || (size_t)rc >= len)
|
||||
_e("File path '%s' truncated, should end with '%s'", path, file);
|
||||
snprintf(path, len, "%s/%s", prefix, file);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ int api_exit (void);
|
||||
|
||||
int client (int argc, char *argv[]);
|
||||
|
||||
void service_monitor (pid_t lost, int status);
|
||||
void service_monitor (pid_t lost);
|
||||
|
||||
const char *plugin_hook_str(hook_point_t no);
|
||||
int plugin_exists (hook_point_t no);
|
||||
|
||||
+4
-5
@@ -128,8 +128,8 @@ int serv_list(char *arg)
|
||||
int serv_enable(char *arg)
|
||||
{
|
||||
char corr[40];
|
||||
char link[256];
|
||||
char path[256];
|
||||
char link[80];
|
||||
char path[80];
|
||||
|
||||
if (!arg || !arg[0])
|
||||
return serv_list(NULL);
|
||||
@@ -140,8 +140,7 @@ int serv_enable(char *arg)
|
||||
}
|
||||
|
||||
pushd(FINIT_RCSD);
|
||||
if (mkdir("enabled", 0755) && EEXIST != errno)
|
||||
err(1, "Failed creating %s/enabled directory", FINIT_RCSD);
|
||||
mkdir("enabled", 0755);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", available, arg);
|
||||
if (!fexist(path))
|
||||
@@ -157,7 +156,7 @@ int serv_enable(char *arg)
|
||||
int serv_disable(char *arg)
|
||||
{
|
||||
char corr[40];
|
||||
char link[256];
|
||||
char link[80];
|
||||
struct stat st;
|
||||
|
||||
if (!arg || !arg[0])
|
||||
|
||||
+137
-330
@@ -116,166 +116,16 @@ static int service_timeout_cancel(svc_t *svc)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Redirect stdin to /dev/null => all reads by process = EOF
|
||||
* https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Logging%20and%20Standard%20Input/Output
|
||||
*/
|
||||
static int stdin_redirect(void)
|
||||
static void redirect_null(void)
|
||||
{
|
||||
int fd;
|
||||
FILE *fp;
|
||||
|
||||
fd = open("/dev/null", O_RDONLY | O_APPEND);
|
||||
if (-1 != fd) {
|
||||
dup2(fd, STDIN_FILENO);
|
||||
return close(fd);
|
||||
fp = fopen("/dev/null", "w");
|
||||
if (fp) {
|
||||
dup2(fileno(fp), STDOUT_FILENO);
|
||||
dup2(fileno(fp), STDERR_FILENO);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Redirect output to a file, e.g., /dev/null, or /dev/console
|
||||
*/
|
||||
static int fredirect(const char *file)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(file, O_WRONLY | O_APPEND);
|
||||
if (-1 != fd) {
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef LOGIT_ENABLED
|
||||
/*
|
||||
* Fallback in case we don't even have logger on the system.
|
||||
* XXX: we should parse 'prio' here to get facility.level
|
||||
*/
|
||||
static void fallback_logger(char *ident, char *prio)
|
||||
{
|
||||
int facility = LOG_DAEMON;
|
||||
int level = LOG_NOTICE;
|
||||
char buf[256];
|
||||
|
||||
openlog(ident, LOG_NOWAIT | LOG_PID, facility);
|
||||
while ((fgets(buf, sizeof(buf), stdin)))
|
||||
syslog(level, "%s", buf);
|
||||
|
||||
closelog();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Redirect output to syslog using the command line logit tool
|
||||
*/
|
||||
static int lredirect(svc_t *svc)
|
||||
{
|
||||
pid_t pid;
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* Open PTY to connect to logger. A pty isn't buffered
|
||||
* like a pipe, and it eats newlines so they aren't logged
|
||||
*/
|
||||
fd = posix_openpt(O_RDWR);
|
||||
if (fd == -1) {
|
||||
svc->log.enabled = 0;
|
||||
return -1;
|
||||
}
|
||||
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
|
||||
close(fd);
|
||||
svc->log.enabled = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
int fds;
|
||||
char *tag = basename(svc->cmd);
|
||||
char *prio = "daemon.info";
|
||||
|
||||
fds = open(ptsname(fd), O_RDONLY);
|
||||
close(fd);
|
||||
if (fds == -1)
|
||||
_exit(0);
|
||||
dup2(fds, STDIN_FILENO);
|
||||
|
||||
/* Reset signals */
|
||||
sig_unblock();
|
||||
|
||||
if (svc->log.file[0] == '/') {
|
||||
#ifdef LOGIT_ENABLED
|
||||
char sz[20], num[3];
|
||||
|
||||
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
|
||||
snprintf(num, sizeof(num), "%d", logfile_count_max);
|
||||
|
||||
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
|
||||
_exit(0);
|
||||
#else
|
||||
logit(LOG_INFO, "logit disabled, logging %s to syslog instead", svc->name);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (svc->log.ident[0])
|
||||
tag = svc->log.ident;
|
||||
if (svc->log.prio[0])
|
||||
prio = svc->log.prio;
|
||||
|
||||
#ifdef LOGIT_ENABLED
|
||||
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
|
||||
#else
|
||||
if (whichp("logger"))
|
||||
execlp("logger", "logger", "-t", tag, "-p", prio, NULL);
|
||||
|
||||
fallback_logger(tag, prio);
|
||||
#endif
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle redirection of process output, if enabled
|
||||
*/
|
||||
static int redirect(svc_t *svc)
|
||||
{
|
||||
/* Redirect inetd socket to stdin for connection */
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd_conn(svc)) {
|
||||
dup2(svc->stdin_fd, STDIN_FILENO);
|
||||
close(svc->stdin_fd);
|
||||
dup2(STDIN_FILENO, STDOUT_FILENO);
|
||||
dup2(STDIN_FILENO, STDERR_FILENO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
stdin_redirect();
|
||||
|
||||
if (svc->log.enabled) {
|
||||
if (svc->log.null)
|
||||
return fredirect("/dev/null");
|
||||
if (svc->log.console)
|
||||
return fredirect(CONSOLE);
|
||||
|
||||
return lredirect(svc);
|
||||
} else if (log_is_debug())
|
||||
return fredirect(CONSOLE);
|
||||
#ifdef REDIRECT_OUTPUT
|
||||
else
|
||||
return fredirect("/dev/null");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_norespawn(void)
|
||||
@@ -316,15 +166,12 @@ static int service_start(svc_t *svc)
|
||||
if (svc_is_inetd(svc))
|
||||
return inetd_start(&svc->inetd);
|
||||
#endif
|
||||
if (svc_is_sysv(svc)) {
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
|
||||
}
|
||||
|
||||
if (!svc->desc[0])
|
||||
do_progress = 0;
|
||||
|
||||
if (do_progress) {
|
||||
if (svc_is_daemon(svc) || svc_is_sysv(svc))
|
||||
if (svc_is_daemon(svc))
|
||||
print_desc("Starting ", svc->desc);
|
||||
else
|
||||
print_desc("", svc->desc);
|
||||
@@ -339,7 +186,11 @@ static int service_start(svc_t *svc)
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
|
||||
pid = fork();
|
||||
cgroup_service(svc->name, svc->id, pid);
|
||||
|
||||
if (pid > 0) {
|
||||
cgroup_service(svc->cmd, pid);
|
||||
cgroup_assign(svc->cgroup, pid);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
int status;
|
||||
@@ -377,31 +228,102 @@ static int service_start(svc_t *svc)
|
||||
}
|
||||
}
|
||||
|
||||
if (!svc_is_sysv(svc)) {
|
||||
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
|
||||
args[i] = svc->args[i];
|
||||
} else {
|
||||
i = 0;
|
||||
args[i++] = svc->cmd;
|
||||
args[i++] = "start";
|
||||
}
|
||||
/* Serve copy of args to process in case it modifies them. */
|
||||
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
|
||||
args[i] = svc->args[i];
|
||||
args[i] = NULL;
|
||||
|
||||
/*
|
||||
* The setsid() call is the most humble of all in this
|
||||
* function. It takes care to detach the process from
|
||||
* its controlling terminal, preventing daemons from
|
||||
* leaking to the console, and allowing us to run such
|
||||
* programs like `lxc-start -F` in the foreground to
|
||||
* properly monitor them.
|
||||
*
|
||||
* If you find yourself here wanting to fix the output
|
||||
* to the console at boot, for debugging or similar,
|
||||
* have a look at redirect() and log.console instead.
|
||||
*/
|
||||
setsid();
|
||||
/* Redirect inetd socket to stdin for connection */
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd_conn(svc)) {
|
||||
dup2(svc->stdin_fd, STDIN_FILENO);
|
||||
close(svc->stdin_fd);
|
||||
dup2(STDIN_FILENO, STDOUT_FILENO);
|
||||
dup2(STDIN_FILENO, STDERR_FILENO);
|
||||
} else
|
||||
#endif
|
||||
|
||||
redirect(svc);
|
||||
if (svc->log.enabled) {
|
||||
int fd;
|
||||
|
||||
if (svc->log.null) {
|
||||
redirect_null();
|
||||
goto logit_done;
|
||||
}
|
||||
if (svc->log.console) {
|
||||
goto logit_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open PTY to connect to logger. A pty isn't buffered
|
||||
* like a pipe, and it eats newlines so they aren't logged
|
||||
*/
|
||||
fd = posix_openpt(O_RDWR);
|
||||
if (fd == -1) {
|
||||
svc->log.enabled = 0;
|
||||
goto logit_done;
|
||||
}
|
||||
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
|
||||
close(fd);
|
||||
svc->log.enabled = 0;
|
||||
goto logit_done;
|
||||
}
|
||||
|
||||
/* SIGCHLD is still blocked for grantpt() and fork() */
|
||||
sigprocmask(SIG_BLOCK, &nmask, NULL);
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
int fds;
|
||||
char *tag = basename(svc->cmd);
|
||||
char *prio = "daemon.info";
|
||||
|
||||
fds = open(ptsname(fd), O_RDONLY);
|
||||
close(fd);
|
||||
if (fds == -1)
|
||||
_exit(0);
|
||||
dup2(fds, STDIN_FILENO);
|
||||
|
||||
/* Reset signals */
|
||||
sig_unblock();
|
||||
|
||||
if (svc->log.file[0] == '/') {
|
||||
char sz[20], num[3];
|
||||
|
||||
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
|
||||
snprintf(num, sizeof(num), "%d", logfile_count_max);
|
||||
|
||||
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (svc->log.ident[0])
|
||||
tag = svc->log.ident;
|
||||
if (svc->log.prio[0])
|
||||
prio = svc->log.prio;
|
||||
|
||||
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
} else if (log_is_debug()) {
|
||||
int fd;
|
||||
|
||||
fd = open(CONSOLE, O_WRONLY | O_APPEND);
|
||||
if (-1 != fd) {
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
#ifdef REDIRECT_OUTPUT
|
||||
else
|
||||
redirect_null();
|
||||
#endif
|
||||
|
||||
logit_done:
|
||||
sig_unblock();
|
||||
|
||||
if (svc->inetd.cmd)
|
||||
@@ -409,7 +331,7 @@ static int service_start(svc_t *svc)
|
||||
else if (svc_is_runtask(svc))
|
||||
status = exec_runtask(svc->cmd, args);
|
||||
else
|
||||
status = execvp(svc->cmd, args);
|
||||
status = execv(svc->cmd, args);
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd_conn(svc)) {
|
||||
@@ -420,33 +342,28 @@ static int service_start(svc_t *svc)
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (svc->log.enabled && !svc->log.null)
|
||||
waitpid(pid, NULL, 0);
|
||||
_exit(status);
|
||||
} else if (log_is_debug()) {
|
||||
char buf[CMD_SIZE] = "";
|
||||
|
||||
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++) {
|
||||
char arg[MAX_ARG_LEN + 1];
|
||||
char arg[MAX_ARG_LEN];
|
||||
|
||||
snprintf(arg, sizeof(arg), "%s ", svc->args[i]);
|
||||
if (strlen(arg) < (sizeof(buf) - strlen(buf)))
|
||||
strlcat(buf, arg, sizeof(buf));
|
||||
strcat(buf, arg);
|
||||
}
|
||||
_d("Starting %s: %s", svc->cmd, buf);
|
||||
}
|
||||
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s:%s, PID: %d",
|
||||
basename(svc->cmd), svc->id, pid);
|
||||
|
||||
svc->pid = pid;
|
||||
svc->start_time = jiffies();
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_RUN:
|
||||
svc->status = complete(svc->cmd, pid);
|
||||
if (WIFEXITED(svc->status) && !WEXITSTATUS(svc->status))
|
||||
result = 0;
|
||||
else
|
||||
result = 1;
|
||||
result = WEXITSTATUS(complete(svc->cmd, pid));
|
||||
svc->start_time = svc->pid = 0;
|
||||
svc->once++;
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
@@ -491,12 +408,10 @@ static void service_kill(svc_t *svc)
|
||||
}
|
||||
|
||||
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s:%s, PID: %d, sending SIGKILL ...",
|
||||
basename(svc->cmd), svc->id, svc->pid);
|
||||
if (runlevel != 1)
|
||||
print_desc("Killing ", svc->desc);
|
||||
|
||||
kill(-svc->pid, SIGKILL);
|
||||
kill(svc->pid, SIGKILL);
|
||||
|
||||
/* Let SIGKILLs stand out, show result as [WARN] */
|
||||
if (runlevel != 1)
|
||||
@@ -538,63 +453,16 @@ static int service_stop(svc_t *svc)
|
||||
#endif
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
if (!svc_is_sysv(svc)) {
|
||||
if (svc->pid <= 1)
|
||||
return 1;
|
||||
|
||||
_d("Sending %s to pid:%d name:%s", sig_name(svc->sighalt),
|
||||
svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s:%s, PID: %d, sending %s ...",
|
||||
basename(svc->cmd), svc->id, svc->pid, sig_name(svc->sighalt));
|
||||
} else {
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s stop' ...", svc->cmd);
|
||||
}
|
||||
if (svc->pid <= 1)
|
||||
return 1;
|
||||
|
||||
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
|
||||
if (runlevel != 1)
|
||||
print_desc("Stopping ", svc->desc);
|
||||
|
||||
if (!svc_is_sysv(svc)) {
|
||||
if (svc->pid <= 1)
|
||||
goto cleanup;
|
||||
|
||||
res = kill(-svc->pid, svc->sighalt);
|
||||
|
||||
/* PID lost or forking process never really started */
|
||||
if (res == -1 && ESRCH == errno) {
|
||||
char *fn;
|
||||
|
||||
/* XXX: Same clenup as done by service_monitor(), refactor */
|
||||
cleanup:
|
||||
fn = pid_file(svc);
|
||||
if (remove(fn) && errno != ENOENT)
|
||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
||||
basename(svc->cmd), fn);
|
||||
|
||||
/* No longer running, update books. */
|
||||
svc->start_time = svc->pid = 0;
|
||||
}
|
||||
} else {
|
||||
char *args[] = { svc->cmd, "stop", NULL };
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case 0:
|
||||
redirect(svc);
|
||||
exec_runtask(svc->cmd, args);
|
||||
_exit(0);
|
||||
break;
|
||||
case -1:
|
||||
_pe("Failed fork() to call sysv script '%s stop'", svc->cmd);
|
||||
res = 1;
|
||||
break;
|
||||
default:
|
||||
res = WEXITSTATUS(complete(svc->cmd, pid));
|
||||
break;
|
||||
}
|
||||
}
|
||||
res = kill(svc->pid, SIGTERM);
|
||||
|
||||
if (runlevel != 1)
|
||||
print_result(res);
|
||||
@@ -638,8 +506,6 @@ static int service_restart(svc_t *svc)
|
||||
print_desc("Restarting ", svc->desc);
|
||||
|
||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Restarting %s:%s, PID: %d, sending SIGHUP ...",
|
||||
basename(svc->cmd), svc->id, svc->pid);
|
||||
rc = kill(svc->pid, SIGHUP);
|
||||
|
||||
/* Declare we're waiting for svc to re-assert/touch its pidfile */
|
||||
@@ -715,32 +581,6 @@ static void parse_log(svc_t *svc, char *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_sighalt(svc_t *svc, char *arg)
|
||||
{
|
||||
int signo;
|
||||
|
||||
signo = sig_num(arg);
|
||||
if (signo == -1)
|
||||
return;
|
||||
|
||||
svc->sighalt = signo;
|
||||
}
|
||||
|
||||
static void parse_killdelay(svc_t *svc, char *delay)
|
||||
{
|
||||
const char *errstr;
|
||||
long long sec;
|
||||
|
||||
sec = strtonum(delay, 1, 60, &errstr);
|
||||
if (errstr) {
|
||||
_e("%s: killdelay %s is %s (1-60)", svc->cmd, delay, errstr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* convert to msec */
|
||||
svc->killdelay = (int)(sec * 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* name:<name>
|
||||
*/
|
||||
@@ -773,17 +613,13 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
|
||||
strlcpy(svc->args[0], cmd, sizeof(svc->args[0]));
|
||||
|
||||
/*
|
||||
* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
|
||||
* array to be zero-terminated.
|
||||
*/
|
||||
for (i = 1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1); i++)
|
||||
/* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
|
||||
* array to be zero-terminated. */
|
||||
for (i=1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS-1); i++)
|
||||
strlcpy(svc->args[i], arg, sizeof(svc->args[0]));
|
||||
|
||||
/*
|
||||
* Clear remaining args in case they were set earlier.
|
||||
* This also zero-terminates the args array.
|
||||
*/
|
||||
/* clear remaining args in case they were set earlier.
|
||||
* This also zero-terminates the args array.*/
|
||||
for (; i < MAX_NUM_SVC_ARGS; i++)
|
||||
svc->args[i][0] = 0;
|
||||
}
|
||||
@@ -794,6 +630,7 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||
* @cfg: Configuration, complete command, with -- for description text
|
||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||
* @cgroup: Control group, may be default
|
||||
* @file: The file name service was loaded from
|
||||
*
|
||||
* This function is used to register commands to be run on different
|
||||
@@ -841,7 +678,7 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||
*/
|
||||
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
int service_register(int type, char *cfg, struct rlimit rlimit[], char *cgroup, char *file)
|
||||
{
|
||||
char id_str[MAX_ID_LEN];
|
||||
#ifdef INETD_ENABLED
|
||||
@@ -854,7 +691,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
char *username = NULL, *log = NULL, *pid = NULL;
|
||||
char *service = NULL, *proto = NULL, *ifaces = NULL;
|
||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
||||
char *name = NULL, *halt = NULL, *delay = NULL;
|
||||
char *name = NULL;
|
||||
svc_t *svc;
|
||||
plugin_t *plugin = NULL;
|
||||
|
||||
@@ -916,10 +753,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
name = cmd;
|
||||
else if (!strncasecmp(cmd, "manual:yes", 10))
|
||||
manual = 1;
|
||||
else if (!strncasecmp(cmd, "halt:", 5))
|
||||
halt = &cmd[5];
|
||||
else if (!strncasecmp(cmd, "kill:", 5))
|
||||
delay = &cmd[5];
|
||||
else if (cmd[0] != '/' && strchr(cmd, '/'))
|
||||
service = cmd; /* inetd service/proto */
|
||||
else
|
||||
@@ -1046,10 +879,7 @@ recreate:
|
||||
conf_parse_cond(svc, cond);
|
||||
|
||||
parse_name(svc, name);
|
||||
if (halt)
|
||||
parse_sighalt(svc, halt);
|
||||
if (delay)
|
||||
parse_killdelay(svc, delay);
|
||||
|
||||
if (log)
|
||||
parse_log(svc, log);
|
||||
if (desc)
|
||||
@@ -1087,6 +917,9 @@ recreate:
|
||||
/* Set configured limits */
|
||||
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
||||
|
||||
/* Register desired control group */
|
||||
strlcpy(svc->cgroup, cgroup, sizeof(svc->cgroup));
|
||||
|
||||
/* New, recently modified or unchanged ... used on reload. */
|
||||
if (file && conf_changed(file))
|
||||
svc_mark_dirty(svc);
|
||||
@@ -1144,7 +977,7 @@ void service_unregister(svc_t *svc)
|
||||
svc_del(svc);
|
||||
}
|
||||
|
||||
void service_monitor(pid_t lost, int status)
|
||||
void service_monitor(pid_t lost)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
@@ -1160,13 +993,7 @@ void service_monitor(pid_t lost, int status)
|
||||
return;
|
||||
}
|
||||
|
||||
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
|
||||
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
|
||||
svc->status = status;
|
||||
|
||||
/* Forking sysv/services declare themselves with pid:!/path/to/pid.file */
|
||||
if (svc_is_starting(svc) && svc_is_forking(svc))
|
||||
return;
|
||||
_d("collected %s(%d)", svc->cmd, lost);
|
||||
|
||||
/* Try removing PID file (in case service does not clean up after itself) */
|
||||
if (svc_is_daemon(svc)) {
|
||||
@@ -1175,16 +1002,8 @@ void service_monitor(pid_t lost, int status)
|
||||
fn = pid_file(svc);
|
||||
if (remove(fn) && errno != ENOENT)
|
||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn);
|
||||
} else if (svc_is_runtask(svc)) {
|
||||
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
||||
svc->started = 1;
|
||||
else
|
||||
svc->started = 0;
|
||||
}
|
||||
|
||||
/* Terminate any children in the same proess group, e.g. logit */
|
||||
kill(-svc->pid, SIGTERM);
|
||||
|
||||
/* No longer running, update books. */
|
||||
svc->start_time = svc->pid = 0;
|
||||
|
||||
@@ -1212,8 +1031,7 @@ static void service_retry(svc_t *svc)
|
||||
}
|
||||
|
||||
if (*restart_cnt >= RESPAWN_MAX) {
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s:%s keeps crashing, not restarting.",
|
||||
basename(svc->cmd), svc->id);
|
||||
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->cmd);
|
||||
svc_crashing(svc);
|
||||
*restart_cnt = 0;
|
||||
service_step(svc);
|
||||
@@ -1223,8 +1041,6 @@ static void service_retry(svc_t *svc)
|
||||
(*restart_cnt)++;
|
||||
|
||||
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s:%s died, restarting (%d/%d)",
|
||||
basename(svc->cmd), svc->id, *restart_cnt, RESPAWN_MAX);
|
||||
svc_unblock(svc);
|
||||
service_step(svc);
|
||||
|
||||
@@ -1241,10 +1057,8 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
||||
|
||||
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
|
||||
if ((*state == SVC_STOPPING_STATE) && !svc_is_inetd(svc)) {
|
||||
_d("%s is stopping, wait %d sec before sending SIGKILL ...",
|
||||
svc->cmd, svc->killdelay / 1000);
|
||||
service_timeout_cancel(svc);
|
||||
service_timeout_after(svc, svc->killdelay, service_kill);
|
||||
service_timeout_after(svc, SVC_TERM_TIMEOUT, service_kill);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1290,7 +1104,6 @@ restart:
|
||||
case SVC_STOPPING_STATE:
|
||||
if (!svc->pid) {
|
||||
/* PID was collected normally, no need to kill it */
|
||||
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
switch (svc->type) {
|
||||
@@ -1302,7 +1115,6 @@ restart:
|
||||
case SVC_TYPE_INETD_CONN:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_SYSV:
|
||||
svc_set_state(svc, SVC_DONE_STATE);
|
||||
break;
|
||||
|
||||
@@ -1361,12 +1173,7 @@ restart:
|
||||
}
|
||||
|
||||
if (svc_is_runtask(svc)) {
|
||||
if (svc_is_sysv(svc)) {
|
||||
if (!svc->started)
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
} else {
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
}
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
svc->once++;
|
||||
break;
|
||||
}
|
||||
@@ -1424,10 +1231,10 @@ restart:
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
/* Reassert condition if we go from waiting and no change */
|
||||
if (!svc_is_changed(svc)) {
|
||||
char name[MAX_COND_LEN];
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
mkcond(svc, name, sizeof(name));
|
||||
cond_set_path(cond_path(name), COND_ON);
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
cond_set_path(cond_path(cond), COND_ON);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
#include "svc.h"
|
||||
|
||||
void service_runlevel (int newlevel);
|
||||
int service_register (int type, char *line, struct rlimit rlimit[], char *file);
|
||||
int service_register (int type, char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||
void service_unregister (svc_t *svc);
|
||||
|
||||
void service_runtask_clean (void);
|
||||
|
||||
@@ -98,55 +98,6 @@ static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
|
||||
static uev_t sigchld_watcher;
|
||||
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
|
||||
|
||||
static struct sigmap {
|
||||
int num;
|
||||
char *name;
|
||||
} signames[] = {
|
||||
/* ISO C99 signals. */
|
||||
{ SIGINT, "SIGINT" },
|
||||
{ SIGILL, "SIGILL" },
|
||||
{ SIGABRT, "SIGABRT" },
|
||||
{ SIGFPE, "SIGFPE" },
|
||||
{ SIGSEGV, "SIGSEGV" },
|
||||
{ SIGTERM, "SIGTERM" },
|
||||
/* Historical signals specified by POSIX. */
|
||||
{ SIGHUP, "SIGHUP" },
|
||||
{ SIGQUIT, "SIGQUIT" },
|
||||
{ SIGTRAP, "SIGTRAP" },
|
||||
{ SIGKILL, "SIGKILL" },
|
||||
{ SIGBUS, "SIGBUS" },
|
||||
{ SIGSYS, "SIGSYS" },
|
||||
{ SIGPIPE, "SIGPIPE" },
|
||||
{ SIGALRM, "SIGALRM" },
|
||||
|
||||
/* New(er) POSIX signals (1003.1-2008, 1003.1-2013). */
|
||||
{ SIGURG, "SIGURG" },
|
||||
{ SIGSTOP, "SIGSTOP" },
|
||||
{ SIGTSTP, "SIGTSTP" },
|
||||
{ SIGCONT, "SIGCONT" },
|
||||
{ SIGCHLD, "SIGCHLD" },
|
||||
{ SIGTTIN, "SIGTTIN" },
|
||||
{ SIGTTOU, "SIGTTOU" },
|
||||
{ SIGPOLL, "SIGPOLL" },
|
||||
{ SIGXCPU, "SIGXCPU" },
|
||||
{ SIGXFSZ, "SIGXFSZ" },
|
||||
{ SIGVTALRM, "SIGVTALRM" },
|
||||
{ SIGPROF, "SIGPROF" },
|
||||
{ SIGUSR1, "SIGUSR1" },
|
||||
{ SIGUSR2, "SIGUSR2" },
|
||||
|
||||
/* Nonstandard signals found in all modern POSIX systems
|
||||
(including both BSD and Linux). */
|
||||
{ SIGWINCH, "SIGWINCH" },
|
||||
{ SIGSTKFLT, "SIGSTKFLT" },
|
||||
{ SIGPWR, "SIGPWR" },
|
||||
|
||||
/* Archaic names for compatibility. */
|
||||
{ SIGIO, "SIGIO" },
|
||||
{ SIGIOT, "SIGIOT" },
|
||||
{ SIGCLD, "SIGCLD" },
|
||||
};
|
||||
|
||||
void mdadm_wait(void);
|
||||
void unmount_tmpfs(void);
|
||||
void unmount_regular(void);
|
||||
@@ -365,7 +316,6 @@ static void sigterm_cb(uev_t *w, void *arg, int events)
|
||||
static void sigchld_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
@@ -374,10 +324,10 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
/* Reap all the children! */
|
||||
do {
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
pid = waitpid(-1, NULL, WNOHANG);
|
||||
if (pid > 0) {
|
||||
_d("Collected child %d", pid);
|
||||
service_monitor(pid, status);
|
||||
service_monitor(pid);
|
||||
}
|
||||
} while (pid > 0);
|
||||
}
|
||||
@@ -418,38 +368,6 @@ int sig_stopped(void)
|
||||
return stopped;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert SIGFOO to a number, if it exists
|
||||
*/
|
||||
int sig_num(const char *name)
|
||||
{
|
||||
size_t i, offset = 0;
|
||||
|
||||
if (strncasecmp(name, "SIG", 3))
|
||||
offset = 3;
|
||||
|
||||
for (i = 0; i < NELEMS(signames); i++) {
|
||||
if (strcasecmp(name, &signames[i].name[offset]))
|
||||
continue;
|
||||
|
||||
return signames[i].num;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *sig_name(int signo)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(signames); i++) {
|
||||
if (signames[i].num == signo)
|
||||
return signames[i].name;
|
||||
}
|
||||
|
||||
return "SIGUNKOWN";
|
||||
}
|
||||
|
||||
/*
|
||||
* Inital signal setup - ignore everything but SIGCHLD until we're capable of responding
|
||||
*/
|
||||
|
||||
@@ -70,13 +70,10 @@ extern shutop_t halt;
|
||||
|
||||
void do_shutdown (shutop_t op);
|
||||
int sig_stopped (void);
|
||||
int sig_num (const char *name);
|
||||
void sig_init (void);
|
||||
void sig_unblock (void);
|
||||
void sig_setup (uev_ctx_t *ctx);
|
||||
|
||||
const char *sig_name(int signo);
|
||||
|
||||
#endif /* FINIT_SIG_H_ */
|
||||
|
||||
/**
|
||||
|
||||
@@ -117,7 +117,7 @@ restart:
|
||||
switch (sm->state) {
|
||||
case SM_BOOTSTRAP_STATE:
|
||||
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
||||
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE);
|
||||
service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE);
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
|
||||
@@ -150,7 +150,6 @@ restart:
|
||||
}
|
||||
|
||||
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %d", INIT_HEADING, runlevel);
|
||||
runlevel_set(prevlevel, runlevel);
|
||||
|
||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||
|
||||
+3
-6
@@ -95,7 +95,7 @@ void stty(int fd, speed_t speed)
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
|
||||
/* Disable modem specific flags */
|
||||
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD|CBAUDEX);
|
||||
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
|
||||
term.c_cflag &= ~CRTSCTS;
|
||||
term.c_cflag |= CLOCAL;
|
||||
|
||||
@@ -104,7 +104,7 @@ void stty(int fd, speed_t speed)
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_iflag = ICRNL|IXON|IXOFF;
|
||||
term.c_oflag = OPOST|ONLCR;
|
||||
term.c_cflag |= CS8|CREAD|HUPCL;
|
||||
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
|
||||
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
|
||||
|
||||
/* Reset special characters to defaults */
|
||||
@@ -116,11 +116,8 @@ void stty(int fd, speed_t speed)
|
||||
term.c_cc[VERASE] = CERASE;
|
||||
tcsetattr(fd, TCSANOW, &term);
|
||||
|
||||
/* Show cursor again, if it was hidden previously */
|
||||
/* Restore cursor if it was hidden previously */
|
||||
write(fd, "\033[?25h", 6);
|
||||
|
||||
/* Enable line wrap, if disabled previously */
|
||||
write(fd, "\033[7h", 4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,7 +60,7 @@ static void svc_gc(void *arg)
|
||||
continue;
|
||||
|
||||
TAILQ_REMOVE(&gc_list, svc, link);
|
||||
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||
cond_clear(mkcond(cond, sizeof(cond), svc->cmd));
|
||||
free(svc);
|
||||
}
|
||||
|
||||
@@ -104,12 +104,6 @@ svc_t *svc_new(char *cmd, char *id, int type)
|
||||
/* Default description, if missing */
|
||||
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
|
||||
|
||||
/* Default HALT signal to send */
|
||||
svc->sighalt = SIGTERM;
|
||||
|
||||
/* Default delay between SIGTERM and SIGKILL */
|
||||
svc->killdelay = SVC_TERM_TIMEOUT;
|
||||
|
||||
TAILQ_INSERT_TAIL(&svc_list, svc, link);
|
||||
|
||||
return svc;
|
||||
@@ -306,7 +300,7 @@ svc_t *svc_find(char *cmd, char *id)
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!strcmp(svc->cmd, cmd) && !strcmp(svc->id, id))
|
||||
if (svc->id && !strcmp(svc->id, id) && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
||||
return svc;
|
||||
}
|
||||
|
||||
@@ -345,7 +339,7 @@ svc_t *svc_find_by_jobid(int job, char *id)
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->job == job && !strcmp(svc->id, id))
|
||||
if (svc->job == job && svc->id && !strcmp(svc->id, id))
|
||||
return svc;
|
||||
}
|
||||
|
||||
@@ -365,7 +359,7 @@ svc_t *svc_find_by_nameid(char *name, char *id)
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!strcmp(svc->id, id) && !strcmp(name, svc->name))
|
||||
if (svc->id && !strcmp(svc->id, id) && !strcmp(name, svc->name))
|
||||
return svc;
|
||||
}
|
||||
|
||||
@@ -513,7 +507,7 @@ int svc_next_id_int(char *cmd)
|
||||
|
||||
snprintf(id, sizeof(id), "%d", n);
|
||||
|
||||
if (!strcmp(svc->cmd, cmd) && strcmp(svc->id, id))
|
||||
if (!strcmp(svc->cmd, cmd) && svc->id && strcmp(svc->id, id))
|
||||
return n;
|
||||
|
||||
n++;
|
||||
@@ -543,58 +537,23 @@ int svc_is_unique(svc_t *svc)
|
||||
return unique;
|
||||
}
|
||||
|
||||
/* break up "job:id job:id job:id ..." into several "job:id" tokens */
|
||||
static char *tokstr(char *str, size_t len)
|
||||
{
|
||||
static char *cur = NULL;
|
||||
static size_t pos = 0;
|
||||
char *token;
|
||||
|
||||
if (!cur && !str)
|
||||
return NULL;
|
||||
|
||||
if (str) {
|
||||
cur = str;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
if (pos >= len)
|
||||
return NULL;
|
||||
|
||||
token = &cur[pos];
|
||||
while (pos < len) {
|
||||
if (isspace(cur[pos])) {
|
||||
cur[pos++] = 0;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
|
||||
*/
|
||||
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *))
|
||||
{
|
||||
char *input, *token;
|
||||
int result = 0;
|
||||
char *input, *token, *pos;
|
||||
|
||||
_d("Got str:'%s'", str);
|
||||
input = tokstr(str, len);
|
||||
while (input) {
|
||||
input = sanitize(str, len);
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
token = strtok_r(input, " ", &pos);
|
||||
while (token) {
|
||||
char *id = NULL;
|
||||
svc_t *svc, *iter = NULL;
|
||||
char *ptr;
|
||||
|
||||
_d("Got token:'%s'", input);
|
||||
token = sanitize(input, len);
|
||||
if (!token) {
|
||||
_d("Sanitation of token:'%s' failed", input);
|
||||
goto next;
|
||||
}
|
||||
ptr = strchr(token, ':');
|
||||
char *ptr = strchr(token, ':');
|
||||
|
||||
if (isdigit(token[0])) {
|
||||
char *ep;
|
||||
@@ -652,8 +611,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
||||
}
|
||||
}
|
||||
|
||||
next:
|
||||
input = tokstr(NULL, len);
|
||||
token = strtok_r(NULL, " ", &pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -43,11 +43,10 @@ typedef enum {
|
||||
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
|
||||
SVC_TYPE_INETD = 8, /* Classic inetd service */
|
||||
SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */
|
||||
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
|
||||
} svc_type_t;
|
||||
|
||||
#define SVC_TYPE_ANY (-1)
|
||||
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV)
|
||||
#define SVC_TYPE_RUNTASK (6)
|
||||
|
||||
typedef enum {
|
||||
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
||||
@@ -73,9 +72,9 @@ typedef enum {
|
||||
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
||||
#define MAX_USER_LEN 16
|
||||
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
||||
#define MAX_NUM_SVC_ARGS 64
|
||||
#define MAX_NUM_SVC_ARGS 32
|
||||
|
||||
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
|
||||
/* Time after SIGTERM that we SIGKILL stopping processes */
|
||||
#define SVC_TERM_TIMEOUT 3000
|
||||
|
||||
/*
|
||||
@@ -94,14 +93,13 @@ typedef struct svc {
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
/* Control group */
|
||||
char cgroup[32];
|
||||
|
||||
/* Service details */
|
||||
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
|
||||
int killdelay; /* Delay in msec before sending SIGKILL */
|
||||
pid_t pid;
|
||||
char pidfile[256];
|
||||
pid_t pid;
|
||||
char pidfile[MAX_ARG_LEN];
|
||||
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||
int started; /* Set for run/task/sysv to track if started */
|
||||
int status; /* From waitpid() when process is collected */
|
||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
svc_type_t type; /* Service, run, task, inetd, ... */
|
||||
int protect; /* Services like dbus-daemon & udev by Finit */
|
||||
@@ -187,9 +185,7 @@ int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *)
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
|
||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
||||
static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; }
|
||||
|
||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "tty.h"
|
||||
@@ -100,6 +101,7 @@ void tty_sweep(void)
|
||||
* tty_register - Register a getty on a device
|
||||
* @line: Configuration, text after initial "tty"
|
||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||
* @cgroup: Control group, may be default
|
||||
* @file: The file name TTY was loaded from
|
||||
*
|
||||
* A Finit tty line can use the internal getty implementation or an
|
||||
@@ -118,7 +120,7 @@ void tty_sweep(void)
|
||||
* Different getty implementations prefer the TTY device argument in
|
||||
* different order, so take care to investigate this first.
|
||||
*/
|
||||
int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
int tty_register(char *line, struct rlimit rlimit[], char *cgroup, char *file)
|
||||
{
|
||||
struct tty *entry;
|
||||
size_t i, num = 0;
|
||||
@@ -153,12 +155,8 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
|
||||
/* Iterate over all args */
|
||||
for (i = 0; i < num; i++) {
|
||||
/*
|
||||
* First, figure out if built-in or external getty
|
||||
* tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in
|
||||
* tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external
|
||||
*/
|
||||
if ((!cmd && !dev) || (cmd && !dev)) {
|
||||
/* First, figure out if built-in or external */
|
||||
if (!dev && !cmd) {
|
||||
if (args[i][0] == '[')
|
||||
runlevels = line;
|
||||
if (!strcmp(args[i], "@console"))
|
||||
@@ -168,14 +166,14 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
if (!strncmp(args[i], "tty", 3))
|
||||
dev = args[i];
|
||||
if (!access(args[i], X_OK))
|
||||
cmd = args[i];
|
||||
cmd = strdup(args[i]);
|
||||
|
||||
/* The first arg must be one of the above */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Built-in getty args */
|
||||
if (!cmd && dev) {
|
||||
if (dev) {
|
||||
if (isdigit(args[i][0])) {
|
||||
baud = args[i];
|
||||
continue;
|
||||
@@ -188,11 +186,23 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
if (i + 1 == num)
|
||||
term = args[i];
|
||||
}
|
||||
|
||||
/* External getty, figure out the device */
|
||||
if (cmd) {
|
||||
if (!strncmp(args[i], "/dev", 4))
|
||||
dev = args[i];
|
||||
|
||||
if (!strncmp(args[i], "tty", 3))
|
||||
dev = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev) {
|
||||
error:
|
||||
_e("Incomplete or non-existing TTY device given, cannot register.");
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
@@ -218,24 +228,18 @@ again:
|
||||
dev = canonicalize(dev);
|
||||
if (!dev)
|
||||
goto error;
|
||||
dev = strdup(dev);
|
||||
|
||||
entry = tty_find(dev);
|
||||
if (!entry) {
|
||||
entry = calloc(1, sizeof(*entry));
|
||||
if (!entry)
|
||||
return errno = ENOMEM;
|
||||
insert = 1;
|
||||
} else {
|
||||
if (entry->cmd) {
|
||||
free(entry->cmd);
|
||||
entry->cmd = NULL;
|
||||
for (i = 0; i < TTY_MAX_ARGS; i++) {
|
||||
if (entry->args[i])
|
||||
free(entry->args[i]);
|
||||
entry->args[i] = NULL;
|
||||
}
|
||||
entry = calloc(1, sizeof(*entry));
|
||||
if (!entry) {
|
||||
free(dev);
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
insert = 0;
|
||||
}
|
||||
|
||||
strlcpy(entry->name, dev, sizeof(entry->name));
|
||||
@@ -255,18 +259,11 @@ again:
|
||||
tok = cmd;
|
||||
else
|
||||
tok++;
|
||||
entry->cmd = strdup(cmd);
|
||||
entry->cmd = cmd;
|
||||
args[1] = strdup(tok);
|
||||
|
||||
for (i = 1; i < num; i++) {
|
||||
char *arg = args[i];
|
||||
|
||||
/* Replace @console with actual device */
|
||||
if (arg && !strcmp(arg, "@console"))
|
||||
arg = dev;
|
||||
if (arg)
|
||||
entry->args[j++] = strdup(arg);
|
||||
}
|
||||
for (i = 1; i < num; i++)
|
||||
entry->args[j++] = strdup(args[i]);
|
||||
entry->args[++j] = NULL;
|
||||
}
|
||||
|
||||
@@ -279,6 +276,9 @@ again:
|
||||
/* Register configured limits */
|
||||
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
|
||||
|
||||
/* Register desired control group */
|
||||
strlcpy(entry->cgroup, cgroup, sizeof(entry->cgroup));
|
||||
|
||||
if (file && conf_changed(file))
|
||||
entry->dirty = 1; /* Modified, restart */
|
||||
else
|
||||
@@ -415,6 +415,8 @@ void tty_start(struct tty *tty)
|
||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
||||
else
|
||||
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
|
||||
|
||||
cgroup_assign(tty->cgroup, tty->pid);
|
||||
}
|
||||
|
||||
void tty_stop(struct tty *tty)
|
||||
|
||||
@@ -51,6 +51,9 @@ struct tty {
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
/* Control group */
|
||||
char cgroup[32];
|
||||
|
||||
/* Set if modified => reloaded, or -1 when marked for removal */
|
||||
int dirty;
|
||||
};
|
||||
@@ -58,7 +61,7 @@ struct tty {
|
||||
void tty_mark (void);
|
||||
void tty_sweep (void);
|
||||
|
||||
int tty_register (char *line, struct rlimit rlimit[], char *file);
|
||||
int tty_register (char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||
int tty_unregister (struct tty *tty);
|
||||
|
||||
struct tty *tty_find (char *dev);
|
||||
|
||||
+18
-24
@@ -24,11 +24,9 @@
|
||||
#include "config.h"
|
||||
#include <ctype.h> /* isprint() */
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <lite/lite.h> /* strlcat() */
|
||||
@@ -106,11 +104,7 @@ int strtobytes(char *arg)
|
||||
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
struct timespec requested_sleep = {
|
||||
.tv_sec = sec,
|
||||
.tv_nsec = 0,
|
||||
};
|
||||
while (nanosleep(&requested_sleep, &requested_sleep))
|
||||
while ((sec = sleep(sec)))
|
||||
;
|
||||
}
|
||||
|
||||
@@ -165,29 +159,29 @@ char *uptime(long secs, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify string argument is NUL terminated
|
||||
* Verify string is a JOB[:ID], JOB and ID
|
||||
* can both be string or number, or combo.
|
||||
*/
|
||||
/* Allowed characters in job/id/name */
|
||||
static int isallowed(int ch)
|
||||
{
|
||||
return isprint(ch);
|
||||
}
|
||||
|
||||
/* Sanitize user input, make sure to NUL terminate. */
|
||||
char *sanitize(char *arg, size_t len)
|
||||
{
|
||||
const char *regex = "[a-z0-9_]+[:]?[a-z0-9_]*";
|
||||
regex_t preg;
|
||||
int rc;
|
||||
size_t i = 0;
|
||||
|
||||
if (strlen(arg) > len)
|
||||
return NULL;
|
||||
while (isallowed(arg[i]) && i < len)
|
||||
i++;
|
||||
|
||||
if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED))
|
||||
return NULL;
|
||||
if (i + 1 < len) {
|
||||
arg[i + 1] = 0;
|
||||
return arg;
|
||||
}
|
||||
|
||||
rc = regexec(&preg, arg, 0, NULL, 0);
|
||||
regfree(&preg);
|
||||
if (rc)
|
||||
return NULL;
|
||||
if (i > 0 && arg[i] == 0)
|
||||
return arg;
|
||||
|
||||
return arg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -52,12 +52,6 @@ void screen_init (void);
|
||||
void screen_exit (void);
|
||||
int screen_width (void);
|
||||
|
||||
static inline char *strterm(char *str, size_t len)
|
||||
{
|
||||
str[len - 1] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif /* FINIT_UTIL_H_ */
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user