Compare commits

..
Author SHA1 Message Date
Joachim Nilsson 44137806e4 Basic support for cpuset cgroup config and process assignment
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2019-05-14 10:18:45 +02:00
46 changed files with 707 additions and 1511 deletions
-3
View File
@@ -1,3 +0,0 @@
# These are supported funding model platforms
github: [troglobit]
+6 -8
View File
@@ -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
View File
@@ -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
+10 -22
View File
@@ -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/)
![Alpine screenshot](img/alpine-screenshot.png)
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
View File
@@ -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
+10
View File
@@ -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:
-->
-3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
-176
View File
@@ -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
View File
@@ -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
View File
@@ -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 \
+50 -10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
-1
View File
@@ -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
View File
@@ -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
+3 -1
View File
@@ -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,
-5
View File
@@ -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() <--
* ~~~~~~~~~~~
+2 -21
View File
@@ -45,7 +45,7 @@ int pid_alive(pid_t pid)
{
char name[24]; /* Enough for max pid_t */
snprintf(name, sizeof(name), "/proc/%d/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;
+2 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+2 -84
View File
@@ -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
*/
-3
View File
@@ -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_ */
/**
+1 -2
View File
@@ -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
View File
@@ -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);
}
/**
+14 -56
View File
@@ -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;
+8 -12
View File
@@ -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; }
+34 -32
View File
@@ -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)
+4 -1
View File
@@ -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
View File
@@ -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;
}
/*
-6
View File
@@ -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_ */
/**