mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
103
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d00a53dfbf | ||
|
|
99a8cf3eb7 | ||
|
|
da95c7d80e | ||
|
|
a9f7f692a9 | ||
|
|
ce55f88a00 | ||
|
|
74a26f559d | ||
|
|
08144df5ea | ||
|
|
230c64927b | ||
|
|
d1e56f300d | ||
|
|
9842786453 | ||
|
|
6d0c679e05 | ||
|
|
5428dea5bf | ||
|
|
4e84ecae89 | ||
|
|
959c9fe5aa | ||
|
|
03d8ef9c8f | ||
|
|
084b4ce8d3 | ||
|
|
31cae6a56d | ||
|
|
1fd45c6f2e | ||
|
|
46995fe446 | ||
|
|
600b2874e6 | ||
|
|
b080b970b7 | ||
|
|
6106574e13 | ||
|
|
2385a1209b | ||
|
|
e8b942cf95 | ||
|
|
ac17d7f5a7 | ||
|
|
1edc4356a6 | ||
|
|
9f06a2db99 | ||
|
|
cfb3a9f2b9 | ||
|
|
8aba63a9e3 | ||
|
|
3d98ad293b | ||
|
|
156e86f395 | ||
|
|
d859ecd21d | ||
|
|
cbec78dbbc | ||
|
|
572291a1d9 | ||
|
|
280608f30e | ||
|
|
5c4146cfe4 | ||
|
|
30e3f5a33c | ||
|
|
2c12379a30 | ||
|
|
5f026c8149 | ||
|
|
90005235e8 | ||
|
|
6b688c4211 | ||
|
|
bdf5f8fc90 | ||
|
|
79f7a8ccfb | ||
|
|
3b19db04e3 | ||
|
|
93402b81d7 | ||
|
|
6dec4c9ca0 | ||
|
|
548acf27cf | ||
|
|
cd0bc57ca8 | ||
|
|
11a3318c02 | ||
|
|
2711b27971 | ||
|
|
05ac9c18c9 | ||
|
|
1a7092f545 | ||
|
|
8000d361b3 | ||
|
|
e3c8febe3b | ||
|
|
32ca117949 | ||
|
|
b966d031c8 | ||
|
|
905c2678a3 | ||
|
|
7d429bdbb1 | ||
|
|
ccfea59cb3 | ||
|
|
7b315364ca | ||
|
|
e9e28ece1c | ||
|
|
c1f9b4e1f6 | ||
|
|
6ebb23e0f0 | ||
|
|
026c229005 | ||
|
|
4d05bf9359 | ||
|
|
289247dab9 | ||
|
|
e44dfde54e | ||
|
|
35b200d3a9 | ||
|
|
5badf4d376 | ||
|
|
24f274c126 | ||
|
|
1abd43384b | ||
|
|
70a1acc210 | ||
|
|
6af0446490 | ||
|
|
ecaf111a4b | ||
|
|
32ec25b070 | ||
|
|
305ad302f2 | ||
|
|
d6390244ad | ||
|
|
790a316607 | ||
|
|
77aa941e84 | ||
|
|
e0a65f67c9 | ||
|
|
3ed291789c | ||
|
|
774a24cdd8 | ||
|
|
6011f91f22 | ||
|
|
07a364e570 | ||
|
|
5b9d99011b | ||
|
|
f4f773b4c7 | ||
|
|
9d949c4186 | ||
|
|
1434561a46 | ||
|
|
cf3d3f6f8e | ||
|
|
621da582c2 | ||
|
|
9167d9255d | ||
|
|
c71bafd495 | ||
|
|
b3b7a33252 | ||
|
|
d1efc60c84 | ||
|
|
9a24dfd98d | ||
|
|
0e3fe5e3bf | ||
|
|
224f42c344 | ||
|
|
e60eb94810 | ||
|
|
464bc831a5 | ||
|
|
e84911fe52 | ||
|
|
4bcfd606b4 | ||
|
|
fe27ed17eb | ||
|
|
b3ea23ed26 |
+2
-5
@@ -5,9 +5,6 @@ language: c
|
||||
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
|
||||
sudo: false
|
||||
|
||||
# Use non-docker builds atm. to install libuEv and libite from source
|
||||
#sudo: true
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
compiler:
|
||||
- gcc
|
||||
@@ -30,7 +27,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 && ./configure --prefix=/tmp --disable-doc --disable-contrib --enable-watchdog --enable-fallback-shell"
|
||||
build_command: "make"
|
||||
branch_pattern: dev
|
||||
|
||||
@@ -48,7 +45,7 @@ script:
|
||||
- ./configure --prefix=/tmp --enable-static
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog --enable-logit
|
||||
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog
|
||||
- cat config.log
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
|
||||
+4
-1
@@ -4,7 +4,7 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[4.0][UNRELEASED] - 2021-02-21
|
||||
[4.0][UNRELEASED] - 2021-03-xx
|
||||
------------------------------
|
||||
|
||||
This release became v4.0, and not v3.2, because of incompatible changes
|
||||
@@ -18,6 +18,9 @@ make sure to read the whole changelog when upgrading.
|
||||
with a fallback to sending signals as per traditional SysV init. The
|
||||
`-f` (force) flag remains, where `reboot(2)` is called directly
|
||||
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
|
||||
* The `inictl cond set|clear COND` have changed completely. Constrained
|
||||
to a flat `<usr/...>` namespace and automatically activated by a new
|
||||
`usr.so` plugin that checks services for usr condition changes
|
||||
* Removed built-in inetd super server. If you need this functionality,
|
||||
use an external inetd, like xinetd, instead. A pull request for a
|
||||
stand-alone inetd, like watchdogd and getty, is most welcome!
|
||||
|
||||
-16
@@ -29,19 +29,3 @@ release: distcheck
|
||||
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
|
||||
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
|
||||
done
|
||||
|
||||
run:
|
||||
srcdir=@srcdir@/test \
|
||||
top_builddir=@top_builddir@ \
|
||||
TENV_ROOT=$(tenv_root) \
|
||||
@srcdir@/test/setup-root.sh
|
||||
|
||||
mkdir -p $(dir $(tenv_root)/@FINIT_CONF@)
|
||||
echo 'tty [12345] @console linux noclear nologin' > $(tenv_root)/@FINIT_CONF@
|
||||
|
||||
-TENV_ROOT=$(tenv_root) \
|
||||
TESTENV_PATH=@sbindir@:/bin \
|
||||
FINIT_RCSD=@FINIT_RCSD@ \
|
||||
test/tenv/start.sh finit
|
||||
|
||||
run : tenv_root ?= @abs_top_builddir@/run-rootfs
|
||||
|
||||
@@ -26,6 +26,9 @@ 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 …"
|
||||
|
||||

|
||||
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
|
||||
|
||||
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:
|
||||
@@ -34,9 +37,6 @@ on server and desktop systems as well. For working examples, see the
|
||||
* [Alpine Linux](contrib/alpine/), and
|
||||
* [Debian GNU/Linux](contrib/debian/)
|
||||
|
||||

|
||||
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
@@ -348,11 +348,16 @@ Finit also implements a modern API to query status, and start/stop
|
||||
services, called `initctl`. Unlike `telinit` the `initctl` tool does
|
||||
not return until the given command has fully completed.
|
||||
|
||||
```shell
|
||||
~ $ initctl -h
|
||||
```
|
||||
alpine:~# initctl help
|
||||
Usage: initctl [OPTIONS] [COMMAND]
|
||||
|
||||
Options:
|
||||
-b, --batch Batch mode, no screen size probing
|
||||
-c, --create Create missing paths (and files) as needed
|
||||
-f, --force Ignore missing files and arguments, never prompt
|
||||
-p, --plain Use plain table headings, no ctrl chars
|
||||
-t, --no-heading Skip table headings
|
||||
-v, --verbose Verbose output
|
||||
-h, --help This help text
|
||||
|
||||
@@ -360,29 +365,36 @@ Commands:
|
||||
debug Toggle Finit (daemon) debug
|
||||
help This help text
|
||||
version Show Finit version
|
||||
|
||||
list List all .conf in /etc/finit.d/
|
||||
enable <CONF> Enable .conf in /etc/finit.d/available/
|
||||
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
|
||||
touch <CONF> Mark .conf in /etc/finit.d/ for reload
|
||||
reload Reload *.conf in /etc/finit.d/ (activates changes)
|
||||
|
||||
|
||||
ls | list List all .conf in /etc/finit.d
|
||||
create <CONF> Create .conf in /etc/finit.d/available
|
||||
delete <CONF> Delete .conf in /etc/finit.d/available
|
||||
show <CONF> Show .conf in /etc/finit.d/available
|
||||
edit <CONF> Edit .conf in /etc/finit.d/available
|
||||
touch <CONF> Change .conf in /etc/finit.d/available
|
||||
enable <CONF> Enable .conf in /etc/finit.d/available
|
||||
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
||||
reload Reload *.conf in /etc/finit.d (activate changes)
|
||||
|
||||
cond set <COND> Set (assert) user-defined condition +usr/COND
|
||||
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
|
||||
cond show Show condition status
|
||||
cond dump Dump all conditions and their status
|
||||
|
||||
|
||||
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
|
||||
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
|
||||
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
|
||||
status <JOB|NAME>[:ID] Show service status, by job# or name
|
||||
status | show Show status of services, default command
|
||||
|
||||
start <NAME>[:ID] Start service by name, with optional ID
|
||||
stop <NAME>[:ID] Stop/Pause a running service by name
|
||||
restart <NAME>[:ID] Restart (stop/start) service by name
|
||||
status <NAME>[:ID] Show service status, by name
|
||||
status Show status of services, default command
|
||||
|
||||
ps List processes based on cgroups
|
||||
|
||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||
reboot Reboot system
|
||||
halt Halt system
|
||||
poweroff Halt and power off system
|
||||
|
||||
utmp show Raw dump of UTMP/WTMP db
|
||||
suspend Suspend system
|
||||
```
|
||||
|
||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||
@@ -395,18 +407,31 @@ the current runlevel these services will not be respawned automatically
|
||||
by Finit if they exit (crash). Hence, if the runlevel is 2, the below
|
||||
Dropbear SSH service will not be restarted if it is killed or exits.
|
||||
|
||||
```shell
|
||||
~ $ initctl status -v
|
||||
1 running 476 [S12345] /sbin/watchdog -T 16 -t 2 -F /dev/watchdog
|
||||
2 running 477 [S12345] /sbin/syslogd -n -b 3 -D
|
||||
3 running 478 [S12345] /sbin/klogd -n
|
||||
4:1 inetd 0 [2345] internal time allow *:37,!eth0
|
||||
4:2 inetd 0 [2345] internal time allow eth0:3737
|
||||
5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
|
||||
5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
|
||||
6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
|
||||
6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
|
||||
```
|
||||
alpine:~# initctl
|
||||
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] ACPI daemon
|
||||
1509 crond running [--2345----] Cron daemon
|
||||
1489 dropbear running [--2345----] Dropbear SSH daemon
|
||||
1511 klogd running [S12345----] Kernel log daemon
|
||||
1512 ntpd running [--2345----] NTP daemon
|
||||
1473 syslogd running [S12345----] Syslog daemon
|
||||
|
||||
alpine:~# initctl -v
|
||||
PID IDENT STATUS RUNLEVELS COMMAND
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] acpid -f
|
||||
1509 crond running [--2345----] crond -f -S $CRON_OPTS
|
||||
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
|
||||
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
|
||||
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
|
||||
1473 syslogd running [S12345----] syslogd -n
|
||||
```
|
||||
|
||||
The environment variables to each of the services above are read from,
|
||||
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
|
||||
have other directories, e.g., Debian use `/etc/default/`.
|
||||
|
||||
|
||||
Requirements
|
||||
@@ -473,6 +498,6 @@ and proposed extensions.
|
||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||
[Travis]: https://travis-ci.org/troglobit/finit
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
|
||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
|
||||
|
||||
+5
-6
@@ -1,4 +1,4 @@
|
||||
AC_INIT(Finit, 4.0-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 4.0-rc3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
@@ -32,7 +32,7 @@ AC_PROG_MKDIR_P
|
||||
|
||||
# Configuration.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([fstaby.h termios.h])
|
||||
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h])
|
||||
AC_CHECK_FUNCS([strstr getopt getfsenty])
|
||||
|
||||
# Check for uint[8,16,32]_t
|
||||
@@ -153,6 +153,8 @@ AS_IF([test "x$enable_logit" != "xno"], [
|
||||
AS_IF([test "x$enable_logrotate" != "xno"], [
|
||||
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
||||
|
||||
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
|
||||
|
||||
### With features ##############################################################################
|
||||
AS_IF([test "x$with_config" != "xno"], [
|
||||
AS_IF([test "x$conf" = "xyes"], [
|
||||
@@ -224,9 +226,6 @@ AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
|
||||
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf")
|
||||
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
|
||||
|
||||
AC_EXPAND_DIR(path_stdpath, "$sbindir:$bindir:/usr/sbin:/usr/bin:/sbin:/bin")
|
||||
AC_DEFINE_UNQUOTED(_PATH_STDPATH, "$path_stdpath", [Default PATH])
|
||||
|
||||
# Generate all files
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -235,7 +234,7 @@ cat <<EOF
|
||||
------------------ Summary ------------------
|
||||
$PACKAGE_NAME version $PACKAGE_VERSION
|
||||
Prefix................: $prefix
|
||||
Exec prefix...........: `eval echo $exec_prefix`
|
||||
Exec prefix...........: $eprefix
|
||||
Sysconfdir............: `eval echo $sysconfdir`
|
||||
Localstatedir.........: `eval echo $localstatedir`
|
||||
Finit config file.....: $conf_path
|
||||
|
||||
+27
-1
@@ -45,7 +45,31 @@ being created.
|
||||
Triggering
|
||||
----------
|
||||
|
||||
Conditions can only be triggered by built-in plugins.
|
||||
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
|
||||
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
|
||||
also supports user-defined conditions, sometimes referred to as static
|
||||
or one-shot conditions. They live in the `<usr/...` namespace and are
|
||||
constrained to a flat hierarchy without any sub-directories, unlike the
|
||||
pidfile plugin, for instance.
|
||||
|
||||
User-defined conditions are controlled using the `initctl cond set` and
|
||||
`initctl cond clear` commands:
|
||||
|
||||
initctl cond set foo
|
||||
initctl cond clear foo
|
||||
|
||||
The purpose of user-defined conditions is to be able to start or stop
|
||||
services, or run/task jobs, on external site-dependent stimuli.
|
||||
Example:
|
||||
|
||||
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
|
||||
|
||||
For convenience, prefixing with `usr/` is allowed, but any other slashes
|
||||
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
|
||||
the same as above, can also be achieved like this:
|
||||
|
||||
initctl cond set usr/foo
|
||||
initctl cond clear usr/foo
|
||||
|
||||
Conditions retain their current state until the next reconfiguration or
|
||||
runlevel change. At that point all set conditions transition into the
|
||||
@@ -57,6 +81,8 @@ unchanged) state of it.
|
||||
> **Note:** For `pid/` conditions it is expected that services "touch"
|
||||
> or recreate their PID file on `SIGHUP`.
|
||||
|
||||
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||
|
||||
|
||||
Built-in Conditions
|
||||
-------------------
|
||||
|
||||
@@ -483,9 +483,6 @@ Log rotation is controlled using the global `log` setting.
|
||||
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
|
||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
||||
detailed description, see the [Conditions](conditions.md) document.
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ The current state depends on the two following conditions:
|
||||
|
||||
A service may be stopped, or blocked, for several reasons:
|
||||
|
||||
- The user has manually stopped the service using `initctl stop JOB`
|
||||
- The user has manually stopped the service using `initctl stop NAME`
|
||||
- The program exits immediately. I.e. keeps crashing (make sure to use
|
||||
the 'run this service in the foreground' command line option)
|
||||
- The binary is missing in the filesystem
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
+2
-2
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
else
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
|
||||
+20
-30
@@ -99,13 +99,14 @@ static void bootclean(void)
|
||||
*/
|
||||
static void setup(void *arg)
|
||||
{
|
||||
int uid, gid;
|
||||
mode_t prev;
|
||||
|
||||
/* Cleanup stale files, if any still linger on. */
|
||||
bootclean();
|
||||
|
||||
prev = umask(0);
|
||||
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
umask(0);
|
||||
makedir("/var/cache", 0755);
|
||||
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
||||
makedir("/var/games", 0755);
|
||||
@@ -133,19 +134,21 @@ static void setup(void *arg)
|
||||
makedir("/var/tmp", 0755);
|
||||
makedir("/var/empty", 0755);
|
||||
|
||||
/*
|
||||
* If /etc/group or "utmp" group is missing,
|
||||
* default to "root"/"wheel" group
|
||||
*/
|
||||
_d("Setting up necessary UTMP files ...");
|
||||
gid = getgroup("utmp");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
/*
|
||||
* UTMP actually needs multiple db files
|
||||
*/
|
||||
if (has_utmp()) {
|
||||
int gid;
|
||||
|
||||
/*
|
||||
* If /etc/group or "utmp" group is missing, default to
|
||||
* "root", or "wheel", group.
|
||||
*/
|
||||
_d("Setting up necessary UTMP files ...");
|
||||
gid = getgroup("utmp");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
|
||||
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
|
||||
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
||||
@@ -165,32 +168,19 @@ static void setup(void *arg)
|
||||
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
|
||||
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
|
||||
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
||||
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/run/frr", 0755); /* Needed by FRR */
|
||||
makedir("/var/log/frr", 0755); /* Needed by FRR */
|
||||
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/run/frr", 0755, "frr", "frr");
|
||||
mksubsys("/var/log/frr", 0755, "frr", "frr");
|
||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||
|
||||
gid = getgroup("tty");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
||||
chown("/dev/xconsole", 0, gid);
|
||||
|
||||
if (!fexist("/etc/mtab"))
|
||||
symlink("../proc/self/mounts", "/etc/mtab");
|
||||
|
||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
||||
uid = getuser("uuidd", NULL);
|
||||
if (uid >= 0) {
|
||||
gid = getgroup("uuidd");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
makedir("/var/run/uuidd", 0755);
|
||||
chown("/var/run/uuidd", uid, gid);
|
||||
}
|
||||
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+12
-10
@@ -32,13 +32,16 @@
|
||||
#include "conf.h"
|
||||
|
||||
#define DAEMON "dbus-daemon"
|
||||
#define ARGS "--nofork --system"
|
||||
#define ARGS "--nofork --system --syslog-only"
|
||||
#define DESC "D-Bus message bus daemon"
|
||||
|
||||
#define DAEMONUSER "messagebus"
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
char *cmd;
|
||||
char line[256];
|
||||
mode_t prev;
|
||||
char *cmd;
|
||||
|
||||
cmd = which(DAEMON);
|
||||
if (!cmd) {
|
||||
@@ -46,24 +49,23 @@ static void setup(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
umask(0);
|
||||
prev =umask(0);
|
||||
|
||||
makedir("/var/run/dbus", 0755);
|
||||
makedir("/var/lock/subsys", 0755);
|
||||
makedir("/var/lock/subsys/messagebus", 0755);
|
||||
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
|
||||
if (whichp("dbus-uuidgen"))
|
||||
run("dbus-uuidgen --ensure");
|
||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||
|
||||
/* Clean up from any previous pre-bootstrap run */
|
||||
erase("/var/run/dbus/pid");
|
||||
remove("/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] @%s:%s %s %s -- %s",
|
||||
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+75
-40
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h> /* IFNAMSIZ */
|
||||
#include <sys/socket.h>
|
||||
@@ -48,7 +49,7 @@ static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_route(struct nlmsghdr *nlmsg)
|
||||
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
struct rtmsg *r;
|
||||
struct rtattr *a;
|
||||
@@ -63,8 +64,14 @@ static void nl_route(struct nlmsghdr *nlmsg)
|
||||
r = NLMSG_DATA(nlmsg);
|
||||
a = RTM_RTA(r);
|
||||
la = RTM_PAYLOAD(nlmsg);
|
||||
if (la >= len) {
|
||||
_e("Packet too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
while (RTA_OK(a, la)) {
|
||||
void *data = RTA_DATA(a);
|
||||
|
||||
switch (a->rta_type) {
|
||||
case RTA_GATEWAY:
|
||||
gw = *((int *)data);
|
||||
@@ -105,7 +112,27 @@ static void net_cond_set(char *ifname, char *cond, int set)
|
||||
cond_clear(msg);
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg)
|
||||
static int validate_ifname(const char *ifname)
|
||||
{
|
||||
if (!ifname || !ifname[0])
|
||||
return 1;
|
||||
|
||||
if (strlen(ifname) >= IFNAMSIZ)
|
||||
return 1;
|
||||
|
||||
if (strstr(ifname, ".."))
|
||||
return 1;
|
||||
|
||||
while (*ifname) {
|
||||
if (*ifname == '/' || isspace(*ifname))
|
||||
return 1;
|
||||
ifname++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
int la;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
@@ -120,53 +147,61 @@ static void nl_link(struct nlmsghdr *nlmsg)
|
||||
i = NLMSG_DATA(nlmsg);
|
||||
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
|
||||
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
|
||||
if (la >= len) {
|
||||
_e("Packet too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
while (RTA_OK(a, la)) {
|
||||
if (a->rta_type == IFLA_IFNAME) {
|
||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||
switch (nlmsg->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
/*
|
||||
* New interface has appeared, or interface flags has changed.
|
||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||
*/
|
||||
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||
net_cond_set(ifname, "exist", 1);
|
||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||
break;
|
||||
for (; RTA_OK(a, la); a = RTA_NEXT(a, la)) {
|
||||
if (a->rta_type != IFLA_IFNAME)
|
||||
continue;
|
||||
|
||||
case RTM_DELLINK:
|
||||
/* NOTE: Interface has disappeared, not link down ... */
|
||||
_d("%s: Delete link", ifname);
|
||||
net_cond_set(ifname, "exist", 0);
|
||||
net_cond_set(ifname, "up", 0);
|
||||
net_cond_set(ifname, "running", 0);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
_d("%s: New Address", ifname);
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
_d("%s: Deconfig Address", ifname);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||
if (validate_ifname(ifname)) {
|
||||
_d("Invalid interface name '%s', skipping ...", ifname);
|
||||
continue;
|
||||
}
|
||||
|
||||
a = RTA_NEXT(a, la);
|
||||
switch (nlmsg->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
/*
|
||||
* New interface has appeared, or interface flags has changed.
|
||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||
*/
|
||||
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||
net_cond_set(ifname, "exist", 1);
|
||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
/* NOTE: Interface has disappeared, not link down ... */
|
||||
_d("%s: Delete link", ifname);
|
||||
net_cond_set(ifname, "exist", 0);
|
||||
net_cond_set(ifname, "up", 0);
|
||||
net_cond_set(ifname, "running", 0);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
_d("%s: New Address", ifname);
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
_d("%s: Deconfig Address", ifname);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nl_callback(void *arg, int sd, int events)
|
||||
{
|
||||
ssize_t len;
|
||||
static char buf[4096];
|
||||
struct nlmsghdr *nh;
|
||||
ssize_t len;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
len = recv(sd, buf, sizeof(buf), 0);
|
||||
@@ -179,9 +214,9 @@ static void nl_callback(void *arg, int sd, int events)
|
||||
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
|
||||
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
|
||||
nl_route(nh);
|
||||
nl_route(nh, len);
|
||||
else
|
||||
nl_link(nh);
|
||||
nl_link(nh, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-17
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
|
||||
}
|
||||
}
|
||||
|
||||
return iwatch_add(iw, path, IN_ONLYDIR);
|
||||
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
|
||||
}
|
||||
|
||||
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
svc_t *svc;
|
||||
|
||||
paste(fn, sizeof(fn), dir, name);
|
||||
_d("path: %s, mask: %08x", fn, mask);
|
||||
|
||||
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
||||
return;
|
||||
|
||||
_d("path: %s, mask: %08x", fn, mask);
|
||||
|
||||
svc = svc_find_by_pidfile(fn);
|
||||
if (!svc) {
|
||||
_d("No matching svc for %s", fn);
|
||||
@@ -82,15 +82,20 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
||||
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
if (!svc_has_pidfile(svc))
|
||||
pid_file_set(svc, fn, 1);
|
||||
|
||||
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;
|
||||
if (pid != svc->pid) {
|
||||
_d("Forking service %s changed PID from %d to %d",
|
||||
svc->cmd, svc->pid, pid);
|
||||
svc->pid = pid;
|
||||
}
|
||||
}
|
||||
|
||||
cond_set(cond);
|
||||
@@ -151,7 +156,8 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz, off;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
@@ -160,15 +166,19 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
|
||||
/* Find base path for this event */
|
||||
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
|
||||
if (!iwp)
|
||||
@@ -179,12 +189,7 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & IN_DELETE) {
|
||||
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||
pidfile_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
@@ -235,6 +240,7 @@ static void pidfile_init(void *arg)
|
||||
*/
|
||||
path = realpath(_PATH_VARRUN, NULL);
|
||||
if (!path) {
|
||||
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
|
||||
path = realpath("/run", NULL);
|
||||
if (!path) {
|
||||
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
|
||||
|
||||
+36
-21
@@ -53,33 +53,48 @@ static void setup(void)
|
||||
_pe("Failed starting TTY watcher");
|
||||
}
|
||||
|
||||
static void do_tty(char *tty, size_t len, int creat)
|
||||
{
|
||||
char name[len + 6];
|
||||
struct tty *entry;
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/%s", tty);
|
||||
entry = tty_find(name);
|
||||
if (entry && tty_enabled(entry)) {
|
||||
if (creat)
|
||||
tty_start(entry);
|
||||
else if (entry->pid)
|
||||
tty_stop(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void watcher(void *arg, int fd, int events)
|
||||
{
|
||||
char buf[EVENT_SIZE];
|
||||
int len = 0;
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
while ((len = read(fd, buf, sizeof(buf)))) {
|
||||
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||
char name[notified->len + 6];
|
||||
struct tty *entry;
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
if (-1 == len) {
|
||||
if (errno == EINVAL)
|
||||
setup();
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
break; /* Likely EAGAIN */
|
||||
}
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
||||
entry = tty_find(name);
|
||||
if (entry && tty_enabled(entry)) {
|
||||
if (notified->mask & IN_CREATE)
|
||||
tty_start(entry);
|
||||
else if (entry->pid)
|
||||
tty_stop(entry);
|
||||
}
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
_d("tty %s, event: 0x%08x", ev->name, ev->mask);
|
||||
do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-4
@@ -37,10 +37,11 @@ static void setup(void *arg)
|
||||
#ifdef RANDOMSEED
|
||||
if (!fexist(RANDOMSEED)) {
|
||||
int ret = 1;
|
||||
mode_t prev;
|
||||
FILE *fp;
|
||||
|
||||
print_desc("Bootstrapping random seed", NULL);
|
||||
umask(077);
|
||||
prev = umask(077);
|
||||
fp = fopen(RANDOMSEED, "w");
|
||||
if (fp) {
|
||||
int iter = 128;
|
||||
@@ -57,7 +58,7 @@ static void setup(void *arg)
|
||||
ret = fclose(fp);
|
||||
}
|
||||
print_result(ret);
|
||||
umask(0);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
print_desc("Initializing random number generator", NULL);
|
||||
@@ -68,10 +69,14 @@ static void setup(void *arg)
|
||||
static void save(void *arg)
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
umask(077);
|
||||
mode_t prev;
|
||||
|
||||
prev = umask(077);
|
||||
|
||||
print_desc("Saving random seed", NULL);
|
||||
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
||||
umask(0);
|
||||
|
||||
umask(prev);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/* User-defined condtion event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "iwatch.h"
|
||||
|
||||
static struct iwatch iw_usr;
|
||||
|
||||
|
||||
static void usr_cond(char *name, uint32_t mask)
|
||||
{
|
||||
char cond[MAX_COND_LEN] = COND_USR;
|
||||
|
||||
strlcat(cond, name, sizeof(cond));
|
||||
cond_update(cond);
|
||||
}
|
||||
|
||||
static void usr_callback(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
if (ev->mask & IN_ISDIR)
|
||||
continue; /* unsupported */
|
||||
|
||||
if (ev->mask & IN_DELETE)
|
||||
sync(); /* dont ask */
|
||||
|
||||
usr_cond(ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void usr_init(void *arg)
|
||||
{
|
||||
char usrdir[MAX_ARG_LEN];
|
||||
char *path;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
|
||||
return;
|
||||
}
|
||||
|
||||
path = realpath(_PATH_CONDUSR, NULL);
|
||||
if (!path) {
|
||||
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
|
||||
iwatch_exit(&iw_usr);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = iwatch_init(&iw_usr);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
plugin.io.fd = fd;
|
||||
plugin.io.cb = usr_callback;
|
||||
plugin.io.flags = PLUGIN_IO_READ;
|
||||
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
iwatch_exit(&iw_usr);
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -32,13 +32,14 @@
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
const char *username = "nobody";
|
||||
char line[LINE_SIZE];
|
||||
mode_t prev;
|
||||
#ifdef PAM_CONSOLE
|
||||
int fd;
|
||||
#endif
|
||||
char line[LINE_SIZE];
|
||||
const char *username = "nobody";
|
||||
|
||||
umask(0);
|
||||
prev = umask(0);
|
||||
|
||||
makedir("/var/run/console", 01777);
|
||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
||||
@@ -58,7 +59,7 @@ static void setup(void *arg)
|
||||
if (whichp("restorecon"))
|
||||
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+12
-7
@@ -1,6 +1,5 @@
|
||||
EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
|
||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
if STATIC
|
||||
AM_CPPFLAGS += -DENABLE_STATIC
|
||||
AM_LDFLAGS = -static-libtool-libs
|
||||
@@ -20,8 +19,10 @@ endif
|
||||
|
||||
if LOGIT
|
||||
pkglibexec_PROGRAMS = logit
|
||||
logit_SOURCES = logit.c
|
||||
logit_SOURCES = logit.c logrotate.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
logit_CFLAGS += $(lite_CFLAGS)
|
||||
logit_LDADD = $(lite_LIBS)
|
||||
endif
|
||||
|
||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
@@ -43,6 +44,10 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
tty.c tty.h \
|
||||
util.c util.h \
|
||||
utmp-api.c utmp-api.h
|
||||
if LOGROTATE
|
||||
finit_SOURCES += logrotate.c
|
||||
endif
|
||||
|
||||
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
|
||||
|
||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
@@ -54,14 +59,14 @@ else
|
||||
finit_LDADD += -ldl
|
||||
endif
|
||||
|
||||
initctl_SOURCES = initctl.c client.c client.h \
|
||||
reboot.c serv.c serv.h svc.h \
|
||||
cond.c cond.h util.c util.h
|
||||
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||
client.c client.h cond.c cond.h reboot.c \
|
||||
serv.c serv.h svc.h util.c util.h
|
||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
initctl_CFLAGS += $(lite_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS)
|
||||
|
||||
INIT_LNKS = init
|
||||
INIT_LNKS = init telinit
|
||||
REBOOT_LNKS = reboot shutdown halt poweroff suspend
|
||||
|
||||
if WATCHDOGD
|
||||
|
||||
@@ -237,7 +237,7 @@ ev_t ev_list[] = {
|
||||
|
||||
static void send_svc(int sd, svc_t *svc)
|
||||
{
|
||||
svc_t empty;
|
||||
svc_t empty = { 0 };
|
||||
size_t len;
|
||||
|
||||
if (!svc) {
|
||||
@@ -381,7 +381,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
break;
|
||||
|
||||
case INIT_CMD_SVC_ITER:
|
||||
_d("svc iter, first: %d", rq.runlevel);
|
||||
// _d("svc iter, first: %d", rq.runlevel);
|
||||
/*
|
||||
* XXX: This severly limits the number of
|
||||
* simultaneous client connections, but will
|
||||
|
||||
+3
-3
@@ -2,10 +2,10 @@
|
||||
# Called by kernel when last child in cgroup exists
|
||||
|
||||
BASE=/sys/fs/cgroup/finit
|
||||
PROC=`basename $1`
|
||||
PROC=$(basename "$1")
|
||||
DIR="$BASE$1"
|
||||
|
||||
/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
|
||||
rmdir $DIR
|
||||
#/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
|
||||
rmdir "$DIR"
|
||||
|
||||
exit 0
|
||||
|
||||
+93
-27
@@ -26,20 +26,65 @@
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysinfo.h> /* get_nprocs_conf() */
|
||||
|
||||
#include "finit.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static int cg_init = 0;
|
||||
static char controllers[256] = "none";
|
||||
|
||||
static void group_init(char *path, char *nproc, int share)
|
||||
{
|
||||
char file[strlen(path) + 64];
|
||||
|
||||
paste(file, sizeof(file), path, "cgroup.clone_children");
|
||||
echo(file, 0, "1");
|
||||
|
||||
paste(file, sizeof(file), path, "cpuset.cpus");
|
||||
echo(file, 0, nproc);
|
||||
|
||||
paste(file, sizeof(file), path, "cpuset.mems");
|
||||
echo(file, 0, "0");
|
||||
|
||||
paste(file, sizeof(file), path, "cpu.shares");
|
||||
echo(file, 0, "%d", share);
|
||||
|
||||
paste(file, sizeof(file), path, "memory.use_hierarchy");
|
||||
echo(file, 0, "1");
|
||||
}
|
||||
|
||||
static void append_ctrl(char *ctrl)
|
||||
{
|
||||
char *wanted[] = {
|
||||
"cpu",
|
||||
"cpuacct",
|
||||
"cpuset",
|
||||
"memory"
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(wanted); i++) {
|
||||
if (strcmp(wanted[i], ctrl))
|
||||
continue;
|
||||
|
||||
if (strcmp(controllers, "none"))
|
||||
strlcat(controllers, ",", sizeof(controllers));
|
||||
else
|
||||
strlcpy(controllers, "", sizeof(controllers));
|
||||
|
||||
strlcat(controllers, ctrl, sizeof(controllers));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by Finit at early boot to mount initial cgroups
|
||||
*/
|
||||
void cgroup_init(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[80];
|
||||
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||
char buf[80];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/cgroups", "r");
|
||||
if (!fp) {
|
||||
@@ -53,7 +98,9 @@ void cgroup_init(void)
|
||||
}
|
||||
|
||||
/* Skip first line, header */
|
||||
fgets(buf, sizeof(buf), fp);
|
||||
(void)fgets(buf, sizeof(buf), fp);
|
||||
|
||||
/* Create and mount traditional cgroups v1 hier */
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *cgroup;
|
||||
char rc[80];
|
||||
@@ -63,46 +110,71 @@ void cgroup_init(void)
|
||||
continue;
|
||||
|
||||
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
||||
#if 0
|
||||
if (mkdir(rc, 0755) && EEXIST != errno)
|
||||
continue;
|
||||
|
||||
if (mount("cgroup", rc, "cgroup", opts, cgroup))
|
||||
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
||||
#else
|
||||
symlink("finit", rc);
|
||||
append_ctrl(cgroup);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Default cgroups for process monitoring */
|
||||
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
|
||||
/* Finit default cgroups for process monitoring */
|
||||
if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
|
||||
|
||||
#if 0
|
||||
strlcpy(controllers, "none,name=finit", sizeof(controllers));
|
||||
#else
|
||||
strlcat(controllers, ",name=finit", sizeof(controllers));
|
||||
#endif
|
||||
if (mount("none", FINIT_CGPATH, "cgroup", opts, controllers)) {
|
||||
_pe("Failed mounting Finit cgroup hierarchy");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set up reaper and enable callbacks */
|
||||
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
||||
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
|
||||
echo(FINIT_CGPATH "/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
||||
echo(FINIT_CGPATH "/notify_on_release", 0, "1");
|
||||
|
||||
/* Default groups, PID 1, services, and user/login processes */
|
||||
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
|
||||
if (mkdir(FINIT_CGPATH "/init", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
|
||||
if (mkdir(FINIT_CGPATH "/system", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir("/sys/fs/cgroup/finit/user", 0755) && EEXIST != errno)
|
||||
if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Set up basic limits for our groups. Finit optimized defaults
|
||||
* for embedded and server systems include limiting the groups
|
||||
* init and user to a single CPU with 10% share, the system
|
||||
* group gets the rest. Override this from finit.conf
|
||||
*/
|
||||
#if 0
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "0-%d", get_nprocs_conf() - 1);
|
||||
|
||||
group_init(FINIT_CGPATH "/init", "0", 50);
|
||||
group_init(FINIT_CGPATH "/user", "0", 75);
|
||||
group_init(FINIT_CGPATH "/system", buf, 900);
|
||||
#endif
|
||||
|
||||
/* Move ourselves to init */
|
||||
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
|
||||
echo(FINIT_CGPATH "/init/cgroup.procs", 0, "1");
|
||||
|
||||
/* We have signal, main screen turn on! */
|
||||
cg_init = 1;
|
||||
fail:
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int move_pid(char *group, char *name, char *id, int pid)
|
||||
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;
|
||||
|
||||
@@ -111,25 +183,19 @@ static int move_pid(char *group, char *name, char *id, int pid)
|
||||
return echo(path, 0, "%d", pid);
|
||||
}
|
||||
|
||||
int cgroup_user(char *name)
|
||||
int cgroup_user(char *name, int pid)
|
||||
{
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
return move_pid("finit/user", name, "0", getpid());
|
||||
return move_pid("finit/user", name, pid);
|
||||
}
|
||||
|
||||
int cgroup_service(char *nm, char *id, int pid)
|
||||
int cgroup_service(char *name, int pid)
|
||||
{
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
if (pid <= 0) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return move_pid("finit/system", nm, id, pid);
|
||||
return move_pid("finit/system", name, pid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@
|
||||
|
||||
void cgroup_init (void);
|
||||
|
||||
int cgroup_user (char *name);
|
||||
int cgroup_service (char *cmd, char *id, int pid);
|
||||
int cgroup_user (char *name, int pid);
|
||||
int cgroup_service (char *name, int pid);
|
||||
|
||||
#endif /* FINIT_CGROUP_H_ */
|
||||
|
||||
+483
@@ -0,0 +1,483 @@
|
||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <search.h>
|
||||
#include <inttypes.h>
|
||||
#include <lite/lite.h>
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
|
||||
#include "initctl.h"
|
||||
|
||||
#define CDIM plain ? "" : "\e[2m"
|
||||
#define CRST plain ? "" : "\e[0m"
|
||||
|
||||
#define NONE " "
|
||||
#define PIPE plain ? "| " : "│ "
|
||||
#define FORK plain ? "|-" : "├─"
|
||||
#define END plain ? "`-" : "└─"
|
||||
|
||||
uint64_t total_ram; /* From sysinfo() */
|
||||
|
||||
struct cg {
|
||||
struct cg *cg_next;
|
||||
|
||||
char *cg_path; /* path in /sys/fs/cgroup */
|
||||
uint64_t cg_prev; /* cpuacct.usage */
|
||||
uint64_t cg_rss; /* memory.stat */
|
||||
uint64_t cg_vmlib; /* memory.stat */
|
||||
uint64_t cg_vmsize; /* memory.usage_in_bytes */
|
||||
float cg_mem; /* cg_rss / total_ram * 100 */
|
||||
float cg_load; /* curr - prev / 10000000.0 */
|
||||
};
|
||||
|
||||
struct cg dummy; /* empty result "NULL" */
|
||||
struct cg *list;
|
||||
|
||||
|
||||
static char *pid_cmdline(int pid, char *buf, size_t len)
|
||||
{
|
||||
size_t i, sz;
|
||||
char *ptr;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/cmdline", pid);
|
||||
if (!fp) {
|
||||
buf[0] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
|
||||
fclose(fp);
|
||||
if (!sz)
|
||||
return NULL; /* kernel thread */
|
||||
|
||||
buf[sz] = 0;
|
||||
|
||||
ptr = strchr(buf, 0);
|
||||
if (ptr && ptr != buf) {
|
||||
ptr++;
|
||||
sz -= ptr - buf;
|
||||
memmove(buf, ptr, sz + 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
if (buf[i] == 0)
|
||||
buf[i] = ' ';
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *pid_comm(int pid, char *buf, size_t len)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/comm", pid);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
fgets(buf, len, fp);
|
||||
fclose(fp);
|
||||
|
||||
return chomp(buf);
|
||||
}
|
||||
|
||||
static uint64_t cgroup_uint64(char *path, char *file)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
char buf[42];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "%s/%s", path, file);
|
||||
if (!fp)
|
||||
return val;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
val = strtoull(buf, NULL, 10);
|
||||
fclose(fp);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cgroup_shares(char *path)
|
||||
{
|
||||
return (int)cgroup_uint64(path, "cpu.shares");
|
||||
}
|
||||
|
||||
static uint64_t cgroup_memuse(struct cg *cg)
|
||||
{
|
||||
char buf[42];
|
||||
FILE *fp;
|
||||
|
||||
|
||||
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (!strncmp(buf, "total_rss", 9)) {
|
||||
cg->cg_rss = strtoull(&buf[10], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "total_mapped_file", 17)) {
|
||||
cg->cg_vmlib = strtoull(&buf[18], NULL, 10);
|
||||
break; /* done */
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
cg->cg_mem = (float)(cg->cg_rss * 100 / total_ram);
|
||||
|
||||
/* "fuzz value for efficient access", sort of like total_cache + total_rss */
|
||||
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.usage_in_bytes");
|
||||
}
|
||||
|
||||
static char *memsz(uint64_t sz, char *buf, size_t len)
|
||||
{
|
||||
int gb, mb, kb, b;
|
||||
|
||||
gb = sz / (1024 * 1024 * 1024);
|
||||
sz = sz % (1024 * 1024 * 1024);
|
||||
mb = sz / (1024 * 1024);
|
||||
sz = sz % (1024 * 1024);
|
||||
kb = sz / (1024);
|
||||
b = sz % (1024);
|
||||
|
||||
if (gb)
|
||||
snprintf(buf, len, "%d.%dG", gb, mb / 102);
|
||||
else if (mb)
|
||||
snprintf(buf, len, "%d.%dM", mb, kb / 102);
|
||||
else
|
||||
snprintf(buf, len, "%d.%dk", kb, b / 102);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static float cgroup_cpuload(struct cg *cg)
|
||||
{
|
||||
char fn[256];
|
||||
char buf[64];
|
||||
FILE *fp;
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", cg->cg_path);
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
err(1, "Cannot open %s", fn);
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
uint64_t curr;
|
||||
|
||||
curr = strtoull(buf, NULL, 10);
|
||||
if (cg->cg_prev != 0) {
|
||||
uint64_t diff = curr - cg->cg_prev;
|
||||
|
||||
/* this expects 1 sec poll interval */
|
||||
cg->cg_load = (float)(diff / 1000000);
|
||||
cg->cg_load /= 10.0;
|
||||
}
|
||||
cg->cg_prev = curr;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return cg->cg_load;
|
||||
}
|
||||
|
||||
static struct cg *append(char *path)
|
||||
{
|
||||
struct cg *cg;
|
||||
char fn[256];
|
||||
ENTRY item;
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", path);
|
||||
if (access(fn, F_OK)) {
|
||||
warn("not a cgroup path with cpuacct controller, %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cg = calloc(1, sizeof(struct cg));
|
||||
if (!cg)
|
||||
err(1, "failed allocating struct cg");
|
||||
|
||||
cg->cg_path = strdup(path);
|
||||
if (list)
|
||||
cg->cg_next = list;
|
||||
list = cg;
|
||||
|
||||
item.key = cg->cg_path;
|
||||
item.data = cg;
|
||||
if (!hsearch(item, ENTER))
|
||||
err(1, "failed adding to hash table");
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
static struct cg *find(char *path)
|
||||
{
|
||||
ENTRY *ep, item = { path, NULL };
|
||||
|
||||
ep = hsearch(item, FIND);
|
||||
if (ep)
|
||||
return ep->data;
|
||||
|
||||
return append(path);
|
||||
}
|
||||
|
||||
static struct cg *cg_update(char *path)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
cg = find(path);
|
||||
if (!cg)
|
||||
return &dummy;
|
||||
|
||||
cgroup_cpuload(cg);
|
||||
cgroup_memuse(cg);
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
static int cgroup_filter(const struct dirent *entry)
|
||||
{
|
||||
/* Skip current dir ".", and prev dir "..", from list of files */
|
||||
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
|
||||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
|
||||
return 0;
|
||||
|
||||
if (entry->d_name[0] == '.')
|
||||
return 0;
|
||||
|
||||
if (entry->d_type != DT_DIR)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cgroup_tree(char *path, char *pfx, int top)
|
||||
{
|
||||
struct dirent **namelist = NULL;
|
||||
char s[32], r[32], l[32];
|
||||
char row[screen_cols + 9]; /* + control codes */
|
||||
size_t rlen = sizeof(row) - 1;
|
||||
size_t rplen = rlen - 9;
|
||||
struct stat st;
|
||||
struct cg *cg;
|
||||
char buf[512];
|
||||
int rc = 0;
|
||||
FILE *fp;
|
||||
int i, n;
|
||||
int num;
|
||||
|
||||
if (top >= screen_rows)
|
||||
return 0;
|
||||
|
||||
if (-1 == lstat(path, &st))
|
||||
return 1;
|
||||
|
||||
if ((st.st_mode & S_IFMT) != S_IFDIR) {
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp = fopenf("r", "%s/cgroup.procs", path);
|
||||
if (!fp)
|
||||
return -1;
|
||||
num = 0;
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
num++;
|
||||
|
||||
if (!pfx) {
|
||||
pfx = "";
|
||||
if (top) {
|
||||
cg = cg_update(path);
|
||||
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f [%s]",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
memsz(cg->cg_rss, r, sizeof(r)),
|
||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||
cg->cg_mem, cg->cg_load, path);
|
||||
} else
|
||||
strlcpy(row, path, rplen);
|
||||
|
||||
puts(row);
|
||||
}
|
||||
|
||||
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
||||
if (n > 0) {
|
||||
for (i = 0; i < n; i++) {
|
||||
char *nm = namelist[i]->d_name;
|
||||
char prefix[80], tmp[42];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
||||
if (top) {
|
||||
cg = cg_update(buf);
|
||||
snprintf(row, rplen,
|
||||
" %6.6s %6.6s %6.6s %5.1f %5.1f ",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
memsz(cg->cg_rss, r, sizeof(r)),
|
||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||
cg->cg_mem, cg->cg_load);
|
||||
} else
|
||||
row[0] = 0;
|
||||
|
||||
strlcat(row, pfx, rplen);
|
||||
if (i + 1 == n) {
|
||||
strlcat(row, END, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s ", pfx);
|
||||
} else {
|
||||
strlcat(row, FORK, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
|
||||
}
|
||||
strlcat(row, " ", rplen);
|
||||
|
||||
strlcat(row, nm, rplen);
|
||||
strlcat(row, "/ ", rplen);
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "[cpu.shares: %d]", cgroup_shares(buf));
|
||||
strlcat(row, tmp, rplen);
|
||||
puts(row);
|
||||
|
||||
rc += cgroup_tree(buf, prefix, top ? top + i : 0);
|
||||
|
||||
free(namelist[i]);
|
||||
}
|
||||
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
if (num > 0) {
|
||||
rewind(fp);
|
||||
|
||||
i = 0;
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char comm[80] = { 0 };
|
||||
pid_t pid;
|
||||
|
||||
pid = atoi(chomp(buf));
|
||||
if (pid <= 0)
|
||||
continue;
|
||||
|
||||
/* skip kernel threads for now (no cmdline) */
|
||||
pid_comm(pid, comm, sizeof(comm));
|
||||
if (pid_cmdline(pid, buf, sizeof(buf))) {
|
||||
char proc[screen_cols];
|
||||
int len;
|
||||
|
||||
if (top)
|
||||
snprintf(row, rplen, "%37s", " ");
|
||||
else
|
||||
row[0] = 0;
|
||||
|
||||
strlcat(row, pfx, rplen);
|
||||
strlcat(row, ++i == num ? END : FORK, rlen);
|
||||
|
||||
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf);
|
||||
|
||||
if (plain) {
|
||||
strlcat(row, proc, rplen);
|
||||
} else {
|
||||
strlcat(row, CDIM, rlen);
|
||||
strlcat(row, proc, rlen);
|
||||
|
||||
len = strlen(row) + strlen(CRST);
|
||||
if (len > (int)rlen)
|
||||
row[rlen - strlen(CRST)] = 0;
|
||||
|
||||
strlcat(row, CRST, sizeof(row));
|
||||
}
|
||||
|
||||
puts(row);
|
||||
}
|
||||
|
||||
if (top) {
|
||||
top += i;
|
||||
if (top >= screen_rows)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int show_cgroup(char *arg)
|
||||
{
|
||||
char path[512];
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
else if (arg[0] != '/') {
|
||||
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||
arg = path;
|
||||
}
|
||||
|
||||
return cgroup_tree(arg, NULL, 0);
|
||||
}
|
||||
|
||||
int show_cgtop(char *arg)
|
||||
{
|
||||
struct sysinfo si = { 0 };
|
||||
char path[512];
|
||||
int laps = 0;
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
else if (arg[0] != '/') {
|
||||
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||
arg = path;
|
||||
}
|
||||
|
||||
if (!hcreate(screen_rows + 25))
|
||||
err(1, "failed creating hash table");
|
||||
|
||||
sysinfo(&si);
|
||||
total_ram = si.totalram * si.mem_unit;
|
||||
|
||||
while (1) {
|
||||
fputs("\e[2J\e[1;1H", stdout);
|
||||
if (heading)
|
||||
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
|
||||
cgroup_tree(arg, NULL, 1);
|
||||
|
||||
if (ionce && laps++)
|
||||
break;
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Cgroup utility functions for initctl
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_CGUTIL_H_
|
||||
#define FINIT_CGUTIL_H_
|
||||
|
||||
int show_cgroup (char *arg);
|
||||
int show_cgtop (char *arg);
|
||||
|
||||
#endif /* FINIT_CGUTIL_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+4
-8
@@ -21,6 +21,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
@@ -38,17 +39,12 @@ int client_connect(void)
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
goto error;
|
||||
err(1, "Failed creating UNIX domain socket");
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||
close(sd);
|
||||
goto error;
|
||||
}
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
err(1, "Failed connecting to finit");
|
||||
|
||||
return sd;
|
||||
error:
|
||||
perror("Failed connecting to finit");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int client_disconnect(void)
|
||||
|
||||
+13
-31
@@ -99,13 +99,13 @@ static void cond_bump_reconf(void)
|
||||
unsigned int rgen;
|
||||
|
||||
/*
|
||||
* If %COND_RECONF does not exist, cond_get_gen() returns 0
|
||||
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0
|
||||
* meaning that rgen++ is always what we want.
|
||||
*/
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
rgen++;
|
||||
|
||||
cond_set_gen(COND_RECONF, rgen);
|
||||
cond_set_gen(_PATH_RECONF, rgen);
|
||||
}
|
||||
|
||||
static int cond_checkpath(const char *path)
|
||||
@@ -119,7 +119,7 @@ static int cond_checkpath(const char *path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (makepath(dir) && errno != EEXIST) {
|
||||
if (mkpath(dir, 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 1;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
|
||||
_d("%s", path);
|
||||
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen) {
|
||||
_e("Unable to read configuration generation (%s)", path);
|
||||
return -1;
|
||||
@@ -162,27 +162,8 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
return new != old;
|
||||
}
|
||||
|
||||
/* Has condition in configuration and cond is allowed? */
|
||||
static int svc_has_cond(svc_t *svc)
|
||||
{
|
||||
if (!svc->cond[0])
|
||||
return 0;
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_SYSV:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cond_update(const char *name)
|
||||
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
|
||||
void cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
@@ -221,7 +202,8 @@ void cond_set_oneshot(const char *name)
|
||||
if (cond_checkpath(path))
|
||||
return;
|
||||
|
||||
symlink(COND_RECONF, path);
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||
_pe("Failed creating onshot cond %s", name);
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
@@ -254,13 +236,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
if (tflg != FTW_F)
|
||||
return 0;
|
||||
|
||||
nm = strstr((char *)fpath, COND_DIR);
|
||||
nm = strstr((char *)fpath, COND_BASE);
|
||||
if (!nm) {
|
||||
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
nm += sizeof(COND_DIR);
|
||||
nm += strlen(COND_BASE);
|
||||
_d("Reasserting %s => %s", fpath, nm);
|
||||
cond_set(nm);
|
||||
|
||||
@@ -281,8 +263,8 @@ void cond_init(void)
|
||||
{
|
||||
char path[MAX_ARG_LEN];
|
||||
|
||||
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
||||
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", _PATH_COND);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -45,7 +45,7 @@ const char *cond_path(const char *name)
|
||||
static char path[256];
|
||||
char tmp[MAX_ARG_LEN];
|
||||
|
||||
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
|
||||
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name);
|
||||
|
||||
return pid_runpath(tmp, path, sizeof(path));
|
||||
}
|
||||
@@ -74,14 +74,14 @@ enum cond_state cond_get_path(const char *path)
|
||||
{
|
||||
int cgen, rgen;
|
||||
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
if (!rgen)
|
||||
return COND_OFF;
|
||||
|
||||
cgen = cond_get_gen(path);
|
||||
if (!cgen)
|
||||
return COND_OFF;
|
||||
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen)
|
||||
return COND_OFF;
|
||||
|
||||
return (cgen == rgen) ? COND_ON : COND_FLUX;
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -4,9 +4,12 @@
|
||||
#include <paths.h>
|
||||
#include <svc.h>
|
||||
|
||||
#define COND_DIR "finit/cond"
|
||||
#define COND_PATH _PATH_VARRUN COND_DIR
|
||||
#define COND_RECONF COND_PATH "/reconf"
|
||||
#define COND_BASE "finit/cond"
|
||||
#define COND_USR "usr/"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||
#define _PATH_RECONF _PATH_COND "reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
COND_OFF = 0,
|
||||
@@ -23,6 +26,7 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
void cond_update (const char *name);
|
||||
int cond_set_path (const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
void cond_set_oneshot (const char *name);
|
||||
|
||||
+38
-20
@@ -47,6 +47,7 @@
|
||||
int logfile_size_max = 200000; /* 200 kB */
|
||||
int logfile_count_max = 5;
|
||||
|
||||
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
||||
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
struct conf_change {
|
||||
@@ -78,14 +79,14 @@ static void hide_args(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
int get_bool(char *ptr, int default_value)
|
||||
int get_bool(char *arg, int default_value)
|
||||
{
|
||||
if (!ptr)
|
||||
if (!arg)
|
||||
goto fallback;
|
||||
|
||||
if (string_compare(ptr, "true") || string_compare(ptr, "on") || string_compare(ptr, "1"))
|
||||
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
|
||||
return 1;
|
||||
if (string_compare(ptr, "false") || string_compare(ptr, "off") || string_compare(ptr, "0"))
|
||||
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
|
||||
return 0;
|
||||
fallback:
|
||||
return default_value;
|
||||
@@ -97,19 +98,24 @@ fallback:
|
||||
*/
|
||||
static void parse_finit_opts(char *opt)
|
||||
{
|
||||
char *ptr;
|
||||
char *arg;
|
||||
|
||||
ptr = strchr(opt, '=');
|
||||
if (ptr)
|
||||
*ptr++ = 0;
|
||||
arg = strchr(opt, '=');
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
if (string_compare(opt, "debug")) {
|
||||
debug = get_bool(ptr, 1);
|
||||
debug = get_bool(arg, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "status_style")) {
|
||||
if (string_compare(ptr, "old") || string_compare(ptr, "classic"))
|
||||
if (!arg) {
|
||||
_e("status_style option requires an argument, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(arg, "old") || string_compare(arg, "classic"))
|
||||
show_progress(PROGRESS_CLASSIC);
|
||||
else
|
||||
show_progress(PROGRESS_MODERN);
|
||||
@@ -117,7 +123,7 @@ static void parse_finit_opts(char *opt)
|
||||
}
|
||||
|
||||
if (string_compare(opt, "show_status")) {
|
||||
show_progress(get_bool(ptr, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
|
||||
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -620,12 +626,9 @@ int conf_reload(void)
|
||||
tty_mark();
|
||||
|
||||
/*
|
||||
* Get current global limits, which may be overridden from both
|
||||
* finit.conf, for Finit and its services like getty+watchdogd,
|
||||
* and *.conf in finit.d/, for each service(s) listed there.
|
||||
* Reset global rlimit to bootstrap values from conf_init().
|
||||
*/
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++)
|
||||
getrlimit(i, &global_rlimit[i]);
|
||||
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||
|
||||
if (rescue) {
|
||||
int rc;
|
||||
@@ -694,6 +697,8 @@ int conf_reload(void)
|
||||
|
||||
globfree(&gl);
|
||||
|
||||
/* Mark any reverse deps as chenaged. */
|
||||
service_update_rdeps();
|
||||
done:
|
||||
/* Drop record of all .conf changes */
|
||||
drop_changes();
|
||||
@@ -800,7 +805,8 @@ static void conf_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz, off;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
@@ -809,10 +815,16 @@ static void conf_cb(uev_t *w, void *arg, int events)
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
struct iwatch_path *iwp;
|
||||
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
@@ -882,8 +894,14 @@ int conf_init(uev_ctx_t *ctx)
|
||||
* finit.conf, for Finit and its services like getty+watchdogd,
|
||||
* and *.conf in finit.d/, for each service(s) listed there.
|
||||
*/
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++)
|
||||
getrlimit(i, &global_rlimit[i]);
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
if (getrlimit(i, &initial_rlimit[i]))
|
||||
logit(LOG_WARNING, "rlimit: Failed reading setting %s: %s",
|
||||
rlim2str(i), strerror(errno));
|
||||
}
|
||||
|
||||
/* Initialize global rlimits, e.g. for built-in services */
|
||||
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||
|
||||
/* prepare /etc watcher */
|
||||
fd = iwatch_init(&iw_conf);
|
||||
|
||||
+23
-15
@@ -179,7 +179,8 @@ static void fs_remount_root(int fsckerr)
|
||||
* XXX: Untested, in the initramfs age we should
|
||||
* probably use switch_root instead.
|
||||
*/
|
||||
rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL))
|
||||
_pe("Failed %s / MS_MOVE");
|
||||
}
|
||||
#endif /* SYSROOT */
|
||||
|
||||
@@ -198,7 +199,6 @@ static void fs_mount(void)
|
||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||
|
||||
run("swapon -ea");
|
||||
umask(0022);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -206,6 +206,9 @@ static void fs_mount(void)
|
||||
* for early multi-console, and /sys for the cgroups support. Any
|
||||
* occurrence of these file systems in /etc/fstab will replace these
|
||||
* mounts later in fs_mount()
|
||||
*
|
||||
* Ignore any mount errors with EBUSY, kernel likely alread mounted
|
||||
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
|
||||
*/
|
||||
static void fs_init(void)
|
||||
{
|
||||
@@ -218,8 +221,16 @@ static void fs_init(void)
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(fs); i++)
|
||||
mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
/* mask writable bit for g and o */
|
||||
umask(022);
|
||||
|
||||
for (i = 0; i < NELEMS(fs); i++) {
|
||||
int rc;
|
||||
|
||||
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
if (rc && errno != EBUSY)
|
||||
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -439,7 +450,7 @@ int main(int argc, char *argv[])
|
||||
};
|
||||
struct wq final = {
|
||||
.cb = final_worker,
|
||||
.delay = 1000
|
||||
.delay = 100
|
||||
};
|
||||
uev_ctx_t loop;
|
||||
|
||||
@@ -470,17 +481,13 @@ int main(int argc, char *argv[])
|
||||
ctx = &loop;
|
||||
|
||||
/*
|
||||
* Set PATH, SHELL, PWD, and umask early to something sane
|
||||
* Set PATH, SHELL, and PWD early to something sane
|
||||
*/
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
setenv("SHELL", _PATH_BSHELL, 1);
|
||||
|
||||
chdir("/");
|
||||
umask(0);
|
||||
|
||||
/* Set up canvas */
|
||||
if (!rescue && !debug)
|
||||
screen_init();
|
||||
if (chdir("/"))
|
||||
_pe("Failed cd /");
|
||||
|
||||
/* Allow progress, if enabled */
|
||||
enable_progress(1);
|
||||
@@ -490,8 +497,10 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
emergency_shell();
|
||||
|
||||
/* Load plugins early, the first hook is in banner(), so we
|
||||
* need plugins loaded before calling it. */
|
||||
/*
|
||||
* Load plugins early, the first hook is in banner(), so we
|
||||
* need plugins loaded before calling it.
|
||||
*/
|
||||
plugin_init(&loop);
|
||||
|
||||
/*
|
||||
@@ -540,7 +549,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
_d("Starting initctl API responder ...");
|
||||
api_init(&loop);
|
||||
umask(022);
|
||||
|
||||
_d("Starting the big state machine ...");
|
||||
schedule_work(&crank);
|
||||
|
||||
+10
-1
@@ -36,8 +36,17 @@
|
||||
#include <lite/lite.h>
|
||||
#include <uev/uev.h>
|
||||
|
||||
/* just in case */
|
||||
#ifndef _PATH_STDPATH
|
||||
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
|
||||
#endif
|
||||
|
||||
#ifndef _PATH_VARRUN
|
||||
#define _PATH_VARRUN "/var/run/"
|
||||
#define _PATH_VARRUN "/var/run/"
|
||||
#endif
|
||||
|
||||
#ifndef FINIT_CGPATH
|
||||
#define FINIT_CGPATH "/sys/fs/cgroup/finit"
|
||||
#endif
|
||||
|
||||
#define LOGIT_PATH FINIT_LIBPATH_ "/logit"
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
cgroup_user(name);
|
||||
cgroup_user(name, getpid());
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
|
||||
+76
-8
@@ -103,6 +103,9 @@ static void add_console(char *cons)
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
if (!strncmp(cons, "/dev/", 5))
|
||||
cons += 5;
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/%s", cons);
|
||||
|
||||
/* check for and skip duplicates */
|
||||
@@ -121,7 +124,7 @@ static void add_console(char *cons)
|
||||
num_cons++;
|
||||
}
|
||||
|
||||
void console_init(void)
|
||||
static void find_active_consoles(void)
|
||||
{
|
||||
char *cons, *ptr, *tok;
|
||||
char buf[512];
|
||||
@@ -131,7 +134,7 @@ void console_init(void)
|
||||
if (!fp) {
|
||||
fallback:
|
||||
_d("Using fallback /dev/console");
|
||||
add_console("/dev/console");
|
||||
add_console("console");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,6 +151,12 @@ void console_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
void console_init(void)
|
||||
{
|
||||
find_active_consoles();
|
||||
screen_init();
|
||||
}
|
||||
|
||||
ssize_t cprintf(const char *fmt, ...)
|
||||
{
|
||||
const size_t len = strlen(fmt) * 2;
|
||||
@@ -404,6 +413,63 @@ int getgroup(char *group)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* get current user */
|
||||
int getcuser(char *buf, size_t len)
|
||||
{
|
||||
char *ptr = "root";
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
|
||||
if (pw)
|
||||
ptr = pw->pw_name;
|
||||
#endif
|
||||
if (strlcpy(buf, ptr, len) > len)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get current group */
|
||||
int getcgroup(char *buf, size_t len)
|
||||
{
|
||||
char *ptr = "root";
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
struct group *gr = getgrgid(getgid());
|
||||
|
||||
if (gr)
|
||||
ptr = gr->gr_name;
|
||||
#endif
|
||||
if (strlcpy(buf, ptr, len) > len)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
{
|
||||
mode_t omask;
|
||||
int uid, gid;
|
||||
int rc = 0;
|
||||
|
||||
omask = umask(0);
|
||||
|
||||
uid = getuser(user, NULL);
|
||||
if (uid >= 0) {
|
||||
gid = getgroup(group);
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
rc = makedir(dir, mode);
|
||||
chown(dir, uid, gid);
|
||||
}
|
||||
|
||||
umask(omask);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void set_hostname(char **hostname)
|
||||
{
|
||||
FILE *fp;
|
||||
@@ -426,14 +492,14 @@ void set_hostname(char **hostname)
|
||||
return;
|
||||
}
|
||||
|
||||
fgets(*hostname, st.st_size, fp);
|
||||
chomp(*hostname);
|
||||
if (fgets(*hostname, st.st_size, fp))
|
||||
chomp(*hostname);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
done:
|
||||
if (*hostname)
|
||||
sethostname(*hostname, strlen(*hostname));
|
||||
(void)sethostname(*hostname, strlen(*hostname));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -557,10 +623,12 @@ int ismnt(char *file, char *dir, char *mode)
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(mnt->mnt_dir, dir)) {
|
||||
if (mode && hasopt(mnt->mnt_opts, mode))
|
||||
found = 1;
|
||||
else
|
||||
if (mode) {
|
||||
if (hasopt(mnt->mnt_opts, mode))
|
||||
found = 1;
|
||||
} else
|
||||
found = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,12 @@ int print_result (int fail);
|
||||
|
||||
int getuser (char *username, char **home);
|
||||
int getgroup (char *group);
|
||||
|
||||
int getcuser (char *buf, size_t len);
|
||||
int getcgroup (char *buf, size_t len);
|
||||
|
||||
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
|
||||
|
||||
void set_hostname (char **hostname);
|
||||
void networking (int updown);
|
||||
|
||||
|
||||
+319
-239
@@ -39,20 +39,21 @@
|
||||
#include "cond.h"
|
||||
#include "serv.h"
|
||||
#include "service.h"
|
||||
#include "cgutil.h"
|
||||
#include "util.h"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||
|
||||
struct command {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
struct cmd {
|
||||
char *cmd;
|
||||
struct cmd *ctx;
|
||||
int (*cb)(char *arg);
|
||||
};
|
||||
|
||||
int icreate = 0;
|
||||
int iforce = 0;
|
||||
int ionce = 0;
|
||||
int heading = 1;
|
||||
int numeric = 0;
|
||||
int verbose = 0;
|
||||
int plain = 0;
|
||||
int runlevel = 0;
|
||||
int iw, pw;
|
||||
|
||||
@@ -72,11 +73,7 @@ static void col_widths(void)
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
int w, p;
|
||||
|
||||
if (!numeric)
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
else
|
||||
svc_jobid(svc, ident, sizeof(ident));
|
||||
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
w = (int)strlen(ident);
|
||||
if (w > iw)
|
||||
iw = w;
|
||||
@@ -93,6 +90,30 @@ static void col_widths(void)
|
||||
pw = 3;
|
||||
}
|
||||
|
||||
void print_header(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (plain) {
|
||||
int len;
|
||||
|
||||
vfprintf(stdout, fmt, ap);
|
||||
fputs("\n", stdout);
|
||||
for (len = 0; len < screen_cols; len++)
|
||||
fputc('=', stdout);
|
||||
fputs("\n", stdout);
|
||||
} else {
|
||||
char buf[screen_cols];
|
||||
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
printheader(stdout, buf, 0);
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static int runlevel_get(int *prevlevel)
|
||||
{
|
||||
int result;
|
||||
@@ -124,7 +145,6 @@ static int toggle_debug(char *arg)
|
||||
|
||||
static int do_log(char *svc)
|
||||
{
|
||||
char cmd[128];
|
||||
char *logfile = "/var/log/syslog";
|
||||
|
||||
if (!svc || !svc[0])
|
||||
@@ -133,9 +153,7 @@ static int do_log(char *svc)
|
||||
if (!fexist(logfile))
|
||||
logfile = "/var/log/messages";
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "cat %s | grep %s | tail -10", logfile, svc);
|
||||
|
||||
return system(cmd);
|
||||
return systemf("cat %s | grep %s | tail -10", logfile, svc);
|
||||
}
|
||||
|
||||
static int do_runlevel(char *arg)
|
||||
@@ -206,8 +224,8 @@ static int do_startstop(int cmd, char *arg)
|
||||
|
||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||
if (client_send(&rq, sizeof(rq))) {
|
||||
fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data);
|
||||
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]\n",
|
||||
fprintf(stderr, "No such task or service(s): %s\n\n", arg);
|
||||
fprintf(stderr, "Usage: initctl %s <NAME>[:ID]\n",
|
||||
cmd == INIT_CMD_START_SVC ? "start" :
|
||||
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
|
||||
return 1;
|
||||
@@ -229,7 +247,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
||||
if (tflag != FTW_F)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(fpath, _PATH_COND "reconf"))
|
||||
if (!strcmp(fpath, _PATH_RECONF))
|
||||
return 0;
|
||||
|
||||
asserted = condstr(cond_get_path(fpath));
|
||||
@@ -245,9 +263,15 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
||||
nm = svc_ident(svc, NULL, 0);
|
||||
pid = svc->pid;
|
||||
}
|
||||
} else if (strncmp("usr/", cond, 4) == 0) {
|
||||
nm = "static";
|
||||
pid = 0;
|
||||
} else if (strncmp("hook/", cond, 4) == 0) {
|
||||
nm = "static";
|
||||
pid = 1;
|
||||
}
|
||||
|
||||
printf("%-*d %-*s %-6s %s\n", pw, pid, iw, nm, asserted, cond);
|
||||
printf("%-*d %-*s %-6s <%s>\n", pw, pid, iw, nm, asserted, cond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -260,7 +284,7 @@ static int do_cond_dump(char *arg)
|
||||
|
||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
||||
iw, "IDENT", "STATUS", "CONDITION");
|
||||
printheader(NULL, title, 0);
|
||||
print_header(title);
|
||||
}
|
||||
|
||||
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
|
||||
@@ -271,41 +295,93 @@ static int do_cond_dump(char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_cond_one(const char *_conds)
|
||||
static int do_cond_act(char *arg, int creat)
|
||||
{
|
||||
static char conds[MAX_COND_LEN];
|
||||
char *cond;
|
||||
char oneshot[256];
|
||||
size_t off;
|
||||
|
||||
strlcpy(conds, _conds, sizeof(conds));
|
||||
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
|
||||
arg += strlen(COND_USR);
|
||||
|
||||
putchar('<');
|
||||
if (!arg || !arg[0])
|
||||
errx(1, "Invalid condition (empty)");
|
||||
if (strchr(arg, '/'))
|
||||
errx(1, "Invalid condition (slashes)");
|
||||
if (strchr(arg, '.'))
|
||||
errx(1, "Invalid condition (periods)");
|
||||
|
||||
snprintf(oneshot, sizeof(oneshot), _PATH_CONDUSR "%s", arg);
|
||||
off = strlen(_PATH_COND);
|
||||
|
||||
if (creat) {
|
||||
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
|
||||
err(1, "Failed asserting condition <%s>", &oneshot[off]);
|
||||
} else {
|
||||
if (erase(oneshot))
|
||||
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_cond_set(char *arg) { return do_cond_act(arg, 1); }
|
||||
static int do_cond_clr(char *arg) { return do_cond_act(arg, 0); }
|
||||
|
||||
static char *svc_cond(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
char *cond, *conds;
|
||||
|
||||
memset(buf, 0, len);
|
||||
|
||||
if (!svc->cond[0])
|
||||
return buf;
|
||||
|
||||
conds = strdup(svc->cond);
|
||||
if (!conds)
|
||||
return buf;
|
||||
|
||||
strlcat(buf, "<", len);
|
||||
|
||||
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
||||
if (cond != conds)
|
||||
putchar(',');
|
||||
strlcat(buf, ",", len);
|
||||
|
||||
switch (cond_get(cond)) {
|
||||
case COND_ON:
|
||||
printf("+%s", cond);
|
||||
strlcat(buf, "+", len);
|
||||
strlcat(buf, cond, len);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
printf("\e[1m~%s\e[0m", cond);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[1m", len);
|
||||
strlcat(buf, "~", len);
|
||||
strlcat(buf, cond, len);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[0m", len);
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
printf("\e[1m-%s\e[0m", cond);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[1m", len);
|
||||
strlcat(buf, "-", len);
|
||||
strlcat(buf, cond, len);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[0m", len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
putchar('>');
|
||||
strlcat(buf, ">", len);
|
||||
free(conds);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int do_cond_show(char *arg)
|
||||
{
|
||||
char ident[MAX_IDENT_LEN];
|
||||
enum cond_state cond;
|
||||
char buf[512];
|
||||
svc_t *svc;
|
||||
|
||||
col_widths();
|
||||
@@ -314,7 +390,7 @@ static int do_cond_show(char *arg)
|
||||
|
||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
||||
iw, "IDENT", "STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
||||
printheader(NULL, title, 0);
|
||||
print_header(title);
|
||||
}
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
@@ -323,51 +399,20 @@ static int do_cond_show(char *arg)
|
||||
|
||||
cond = cond_get_agg(svc->cond);
|
||||
|
||||
if (!numeric)
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
else
|
||||
svc_jobid(svc, ident, sizeof(ident));
|
||||
|
||||
printf("%-*d %-*s ", pw, svc->pid, iw, ident);
|
||||
svc_ident(svc, buf, sizeof(buf));
|
||||
printf("%-*d %-*s ", pw, svc->pid, iw, buf);
|
||||
|
||||
if (cond == COND_ON)
|
||||
printf("%-6.6s ", condstr(cond));
|
||||
else
|
||||
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
|
||||
|
||||
show_cond_one(svc->cond);
|
||||
puts("");
|
||||
puts(svc_cond(svc, buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_cond(char *cmd)
|
||||
{
|
||||
int c;
|
||||
char *arg;
|
||||
struct command command[] = {
|
||||
{ "show", do_cond_show },
|
||||
{ "dump", do_cond_dump },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
if (!cmd)
|
||||
goto fallback;
|
||||
|
||||
arg = strpbrk(cmd, " \0");
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (string_match(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
|
||||
fallback:
|
||||
return do_cond_show(NULL);
|
||||
}
|
||||
|
||||
static int do_cmd(int cmd)
|
||||
{
|
||||
struct init_request rq = {
|
||||
@@ -395,15 +440,18 @@ int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
|
||||
|
||||
int utmp_show(char *file)
|
||||
{
|
||||
time_t sec;
|
||||
struct utmp *ut;
|
||||
static int once = 0;
|
||||
struct tm *sectm;
|
||||
struct utmp *ut;
|
||||
time_t sec;
|
||||
|
||||
int pid;
|
||||
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
|
||||
|
||||
if (heading)
|
||||
printheader(NULL, file, 0);
|
||||
if (heading) {
|
||||
print_header("%s%s ", once ? "\n" : "", file);
|
||||
once++;
|
||||
}
|
||||
utmpname(file);
|
||||
|
||||
setutent();
|
||||
@@ -470,7 +518,7 @@ char *runlevel_string(int runlevel, int levels)
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ISSET(levels, i)) {
|
||||
if (runlevel == i)
|
||||
if (!plain && runlevel == i)
|
||||
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
|
||||
|
||||
if (i == 0)
|
||||
@@ -478,7 +526,7 @@ char *runlevel_string(int runlevel, int levels)
|
||||
else
|
||||
lvl[pos++] = '0' + i;
|
||||
|
||||
if (runlevel == i)
|
||||
if (!plain && runlevel == i)
|
||||
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
|
||||
} else {
|
||||
lvl[pos++] = '-';
|
||||
@@ -491,30 +539,96 @@ char *runlevel_string(int runlevel, int levels)
|
||||
return lvl;
|
||||
}
|
||||
|
||||
static int missing(svc_t *svc)
|
||||
{
|
||||
if (svc->state == SVC_HALTED_STATE && svc_is_missing(svc))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *svc_command(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
int bold = missing(svc);
|
||||
|
||||
if (plain || whichp(svc->cmd))
|
||||
bold = 0;
|
||||
|
||||
strlcpy(buf, bold ? "\e[1m" : "", len);
|
||||
strlcat(buf, svc->cmd, len);
|
||||
|
||||
for (int i = 1; i < MAX_NUM_SVC_ARGS; i++) {
|
||||
if (!svc->args[i][0])
|
||||
break;
|
||||
|
||||
strlcat(buf, " ", len);
|
||||
strlcat(buf, svc->args[i], len);
|
||||
}
|
||||
|
||||
strlcat(buf, bold ? "\e[0m" : "", len);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *svc_environ(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
int bold = missing(svc);
|
||||
|
||||
if (plain || svc_checkenv(svc) || svc->env[0] == '-')
|
||||
bold = 0;
|
||||
|
||||
strlcpy(buf, bold ? "\e[1m" : "", len);
|
||||
strlcat(buf, svc->env, len);
|
||||
strlcat(buf, bold ? "\e[0m" : "", len);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *status(svc_t *svc)
|
||||
{
|
||||
static char buf[25];
|
||||
int bold = !plain;
|
||||
char *s;
|
||||
|
||||
s = svc_status(svc);
|
||||
if (svc->state != SVC_HALTED_STATE)
|
||||
bold = 0;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%-8.8s%s",
|
||||
bold ? "\e[1m" : "", s, bold ? "\e[0m" : "");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int show_status(char *arg)
|
||||
{
|
||||
char ident[MAX_IDENT_LEN];
|
||||
char buf[512];
|
||||
svc_t *svc;
|
||||
|
||||
runlevel = runlevel_get(NULL);
|
||||
|
||||
if (arg && arg[0]) {
|
||||
long now = jiffies();
|
||||
char buf[42] = "N/A";
|
||||
char uptm[42] = "N/A";
|
||||
|
||||
svc = client_svc_find(arg);
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
printf("Job : %d\n", svc->job);
|
||||
printf("Service : %s\n", svc->cmd);
|
||||
printf("Description : %s\n", svc->desc);
|
||||
printf("Status : %s\n", status(svc));
|
||||
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
||||
printf("Environment : %s\n", svc->env);
|
||||
printf("Description : %s\n", svc->desc);
|
||||
printf("Origin : %s\n", svc->file[0] ? svc->file : "built-in");
|
||||
printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf)));
|
||||
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
|
||||
printf("Command : %s\n", svc_command(svc, buf, sizeof(buf)));
|
||||
printf("PID file : %s\n", svc->pidfile);
|
||||
printf("PID : %d\n", svc->pid);
|
||||
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
|
||||
printf("User : %s\n", svc->username);
|
||||
printf("Group : %s\n", svc->group);
|
||||
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
|
||||
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||
printf("Status : %s\n", svc_status(svc));
|
||||
printf("\n");
|
||||
|
||||
return do_log(svc->cmd);
|
||||
@@ -531,21 +645,16 @@ static int show_status(char *arg)
|
||||
else
|
||||
strlcat(title, "COMMAND", sizeof(title));
|
||||
|
||||
printheader(NULL, title, 0);
|
||||
print_header(title);
|
||||
}
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
char *lvls;
|
||||
int i;
|
||||
|
||||
printf("%-*d ", pw, svc->pid);
|
||||
|
||||
if (!numeric)
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
else
|
||||
svc_jobid(svc, ident, sizeof(ident));
|
||||
|
||||
printf("%-*s %-8s ", iw, ident, svc_status(svc));
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
printf("%-*s %s ", iw, ident, status(svc));
|
||||
|
||||
lvls = runlevel_string(runlevel, svc->runlevels);
|
||||
if (strchr(lvls, '\e'))
|
||||
@@ -553,91 +662,15 @@ static int show_status(char *arg)
|
||||
else
|
||||
printf("%-12.12s ", lvls);
|
||||
|
||||
if (!verbose) {
|
||||
printf("%s\n", svc->desc);
|
||||
} else {
|
||||
char args[CMD_SIZE] = "";
|
||||
|
||||
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
|
||||
if (strlen(svc->args[i]) == 0)
|
||||
continue;
|
||||
if (args[0])
|
||||
strlcat(args, " ", sizeof(args));
|
||||
strlcat(args, svc->args[i], sizeof(args));
|
||||
}
|
||||
|
||||
printf("%s %s\n", svc->cmd, args);
|
||||
}
|
||||
if (!verbose)
|
||||
puts(svc->desc);
|
||||
else
|
||||
puts(svc_command(svc, buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||
{
|
||||
FILE *fp;
|
||||
char *group;
|
||||
char path[256];
|
||||
char buf[256];
|
||||
int user = 0;
|
||||
int num = 0;
|
||||
|
||||
if (tflag == FTW_F)
|
||||
return 0;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/cgroup.procs", fpath);
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
if (strstr(fpath, "finit/user"))
|
||||
user = 1;
|
||||
|
||||
group = strrchr(fpath, '/');
|
||||
if (!group)
|
||||
goto err;
|
||||
group++;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
FILE *cfp;
|
||||
int pid;
|
||||
|
||||
if (num == 0)
|
||||
printf("%c :- %s/", user ? ' ' : '|', group);
|
||||
num++;
|
||||
|
||||
pid = atoi(chomp(buf));
|
||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||
cfp = fopen(path, "r");
|
||||
if (!cfp)
|
||||
continue;
|
||||
fgets(buf, sizeof(buf), cfp);
|
||||
fclose(cfp);
|
||||
|
||||
printf("\n%c :- %d %s", user ? ' ' : '|', pid, buf);
|
||||
}
|
||||
|
||||
err:
|
||||
fclose(fp);
|
||||
if (num)
|
||||
printf("\r%c\n", user ? ' ' : '|');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_cgroup(char *arg)
|
||||
{
|
||||
puts("finit/");
|
||||
puts("|- init/");
|
||||
nftw("/sys/fs/cgroup/finit/init", dump_cgroup, 20, 0);
|
||||
puts("|- system/");
|
||||
nftw("/sys/fs/cgroup/finit/system", dump_cgroup, 20, 0);
|
||||
puts("`- user/");
|
||||
nftw("/sys/fs/cgroup/finit/user", dump_cgroup, 20, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transform(char *nm)
|
||||
{
|
||||
char *names[] = {
|
||||
@@ -682,7 +715,8 @@ static int usage(int rc)
|
||||
" -b, --batch Batch mode, no screen size probing\n"
|
||||
" -c, --create Create missing paths (and files) as needed\n"
|
||||
" -f, --force Ignore missing files and arguments, never prompt\n"
|
||||
" -n, --numeric Show JOB:ID instead of NAME:ID\n"
|
||||
" -1, --once Only one lap in commands like 'top'\n"
|
||||
" -p, --plain Use plain table headings, no ctrl chars\n"
|
||||
" -t, --no-heading Skip table headings\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
" -h, --help This help text\n"
|
||||
@@ -698,9 +732,14 @@ static int usage(int rc)
|
||||
" ls | list List all .conf in " FINIT_RCSD "\n"
|
||||
" create <CONF> Create .conf in %s\n"
|
||||
" delete <CONF> Delete .conf in %s\n"
|
||||
" show <CONF> Show .conf in %s\n"
|
||||
" edit <CONF> Edit .conf in %s\n"
|
||||
" touch <CONF> Change .conf in %s\n",
|
||||
avail, avail, avail, avail);
|
||||
avail, avail, avail, avail, avail);
|
||||
else
|
||||
fprintf(stderr,
|
||||
" show Show %s\n", FINIT_CONF);
|
||||
|
||||
if (has_ena)
|
||||
fprintf(stderr,
|
||||
" enable <CONF> Enable .conf in %s\n", avail);
|
||||
@@ -715,19 +754,22 @@ static int usage(int rc)
|
||||
" reload Reload " FINIT_CONF " (activate changes)\n");
|
||||
|
||||
fprintf(stderr,
|
||||
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
|
||||
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
|
||||
"\n"
|
||||
" cond show Show condition status\n"
|
||||
" cond set <COND> Set (assert) user-defined condition +usr/COND\n"
|
||||
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
|
||||
" cond status Show condition status, default cond command\n"
|
||||
" cond dump Dump all conditions and their status\n"
|
||||
"\n"
|
||||
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
|
||||
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
|
||||
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
|
||||
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
|
||||
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
|
||||
" status | show Show status of services, default command\n"
|
||||
" start <NAME>[:ID] Start service by name, with optional ID\n"
|
||||
" stop <NAME>[:ID] Stop/Pause a running service by name\n"
|
||||
" restart <NAME>[:ID] Restart (stop/start) service by name\n"
|
||||
" status <NAME>[:ID] Show service status, by name\n"
|
||||
" status Show status of services, default command\n"
|
||||
"\n"
|
||||
" ps List processes based on cgroups\n"
|
||||
" top Show top-like listing based on cgroups\n"
|
||||
"\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" reboot Reboot system\n"
|
||||
@@ -743,69 +785,128 @@ static int usage(int rc)
|
||||
else
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
||||
#ifdef PACKAGE_URL
|
||||
printf("Project homepage: %s\n", PACKAGE_URL);
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_devel(char *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
printf("Screen %dx%d\n", screen_cols, screen_rows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_help(char *arg)
|
||||
{
|
||||
return usage(0);
|
||||
}
|
||||
|
||||
static int cmd_parse(int argc, char *argv[], struct cmd *command)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; argc > 0 && command[i].cmd; i++) {
|
||||
if (!string_match(command[i].cmd, argv[0]))
|
||||
continue;
|
||||
|
||||
if (command[i].ctx)
|
||||
return cmd_parse(argc - 1, &argv[1], command[i].ctx);
|
||||
|
||||
if (command[i].cb) {
|
||||
int rc = 0;
|
||||
|
||||
if (argc == 1)
|
||||
return command[i].cb(NULL);
|
||||
|
||||
for (j = 1; j < argc; j++)
|
||||
rc |= command[i].cb(argv[j]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (argv[0] && strlen(argv[0]) > 0)
|
||||
errx(1, "No such command. See 'initctl help' for an overview of available commands.");
|
||||
|
||||
return command[0].cb(NULL); /* default cmd */
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct command command[] = {
|
||||
{ "debug", toggle_debug },
|
||||
{ "help", do_help },
|
||||
{ "version", show_version },
|
||||
|
||||
{ "list", serv_list },
|
||||
{ "ls", serv_list },
|
||||
{ "enable", serv_enable },
|
||||
{ "disable", serv_disable },
|
||||
{ "touch", serv_touch },
|
||||
{ "edit", serv_edit },
|
||||
{ "create", serv_creat },
|
||||
{ "delete", serv_delete },
|
||||
{ "reload", do_reload },
|
||||
|
||||
{ "cond", do_cond },
|
||||
|
||||
{ "log", do_log },
|
||||
{ "start", do_start },
|
||||
{ "stop", do_stop },
|
||||
{ "restart", do_restart },
|
||||
{ "status", show_status },
|
||||
{ "show", show_status }, /* Convenience alias */
|
||||
|
||||
{ "ps", show_cgroup },
|
||||
|
||||
{ "runlevel", do_runlevel },
|
||||
{ "reboot", do_reboot },
|
||||
{ "halt", do_halt },
|
||||
{ "poweroff", do_poweroff },
|
||||
{ "suspend", do_suspend },
|
||||
|
||||
{ "utmp", do_utmp },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
struct option long_options[] = {
|
||||
{ "batch", 0, NULL, 'b' },
|
||||
{ "create", 0, NULL, 'c' },
|
||||
{ "force", 0, NULL, 'f' },
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ "numeric", 0, NULL, 'n' },
|
||||
{ "once", 0, NULL, '1' },
|
||||
{ "plain", 0, NULL, 'p' },
|
||||
{ "no-heading", 0, NULL, 't' },
|
||||
{ "verbose", 0, NULL, 'v' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
struct cmd cond[] = {
|
||||
{ "status", NULL, do_cond_show }, /* default cmd */
|
||||
{ "dump", NULL, do_cond_dump },
|
||||
{ "set", NULL, do_cond_set },
|
||||
{ "clr", NULL, do_cond_clr },
|
||||
{ "clear", NULL, do_cond_clr },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
struct cmd command[] = {
|
||||
{ "status", NULL, show_status }, /* default cmd */
|
||||
|
||||
{ "debug", NULL, toggle_debug },
|
||||
{ "devel", NULL, do_devel },
|
||||
{ "help", NULL, do_help },
|
||||
{ "version", NULL, show_version },
|
||||
|
||||
{ "list", NULL, serv_list },
|
||||
{ "ls", NULL, serv_list },
|
||||
{ "enable", NULL, serv_enable },
|
||||
{ "disable", NULL, serv_disable },
|
||||
{ "touch", NULL, serv_touch },
|
||||
{ "show", NULL, serv_show },
|
||||
{ "edit", NULL, serv_edit },
|
||||
{ "create", NULL, serv_creat },
|
||||
{ "delete", NULL, serv_delete },
|
||||
{ "reload", NULL, do_reload },
|
||||
|
||||
{ "cond", cond, NULL },
|
||||
|
||||
{ "log", NULL, do_log },
|
||||
{ "start", NULL, do_start },
|
||||
{ "stop", NULL, do_stop },
|
||||
{ "restart", NULL, do_restart },
|
||||
|
||||
{ "ps", NULL, show_cgroup },
|
||||
{ "top", NULL, show_cgtop },
|
||||
|
||||
{ "runlevel", NULL, do_runlevel },
|
||||
{ "reboot", NULL, do_reboot },
|
||||
{ "halt", NULL, do_halt },
|
||||
{ "poweroff", NULL, do_poweroff },
|
||||
{ "suspend", NULL, do_suspend },
|
||||
|
||||
{ "utmp", NULL, do_utmp },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
int interactive = 1, c;
|
||||
char *cmd;
|
||||
|
||||
if (transform(progname(argv[0])))
|
||||
return reboot_main(argc, argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "bcfh?ntv", long_options, NULL)) != EOF) {
|
||||
while ((c = getopt_long(argc, argv, "1bcfh?ptv", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case '1':
|
||||
ionce = 1;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
interactive = 0;
|
||||
break;
|
||||
@@ -822,8 +923,8 @@ int main(int argc, char *argv[])
|
||||
case '?':
|
||||
return usage(0);
|
||||
|
||||
case 'n':
|
||||
numeric = 1;
|
||||
case 'p':
|
||||
plain = 1;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
@@ -839,10 +940,8 @@ int main(int argc, char *argv[])
|
||||
if (interactive)
|
||||
screen_init();
|
||||
|
||||
if (optind >= argc)
|
||||
return show_status(NULL);
|
||||
|
||||
if (!has_utmp()) {
|
||||
/* system w/o utmp support, disable 'utmp' command */
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!string_compare(command[c].cmd, "utmp"))
|
||||
continue;
|
||||
@@ -852,26 +951,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
cmd = argv[optind++];
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
int rc = 0;
|
||||
|
||||
if (!string_match(command[c].cmd, cmd))
|
||||
continue;
|
||||
if (!command[c].cb)
|
||||
continue;
|
||||
|
||||
/* no arg, just a command, e.g. initctl ls */
|
||||
if (optind >= argc)
|
||||
return command[c].cb(NULL);
|
||||
|
||||
/* >=1 arg, e.g., initctl del foo bar baz */
|
||||
while (optind < argc)
|
||||
rc |= command[c].cb(argv[optind++]);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
return cmd_parse(argc - optind, &argv[optind], command);
|
||||
}
|
||||
|
||||
void logit(int prio, const char *fmt, ...)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_INITCTL_H_
|
||||
#define FINIT_INITCTL_H_
|
||||
|
||||
#include "finit.h"
|
||||
#include "util.h"
|
||||
|
||||
extern int icreate; /* initctl -c */
|
||||
extern int iforce; /* initctl -f */
|
||||
extern int ionce; /* initctl -1 */
|
||||
extern int heading; /* initctl -t */
|
||||
extern int verbose; /* initctl -v */
|
||||
extern int plain; /* initctl -p */
|
||||
|
||||
extern void print_header(const char *fmt, ...);
|
||||
|
||||
#endif /* FINIT_INITCTL_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -22,7 +22,6 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <lite/lite.h>
|
||||
@@ -50,15 +49,13 @@ void log_exit(void)
|
||||
* Unless in debug mode at shutdown, Reinitialize screen,
|
||||
* terminal may have been resized at runtime
|
||||
*/
|
||||
if (debug)
|
||||
screen_exit();
|
||||
else
|
||||
if (!debug)
|
||||
screen_init();
|
||||
|
||||
enable_progress(1);
|
||||
}
|
||||
|
||||
void log_open(void)
|
||||
static void log_open(void)
|
||||
{
|
||||
int opts = LOG_PID;
|
||||
|
||||
@@ -79,7 +76,6 @@ void log_debug(void)
|
||||
|
||||
if (debug) {
|
||||
loglevel = LOG_DEBUG;
|
||||
screen_exit();
|
||||
} else {
|
||||
loglevel = LOG_NOTICE;
|
||||
screen_init();
|
||||
@@ -103,11 +99,12 @@ void logit(int prio, const char *fmt, ...)
|
||||
if (up || fexist("/dev/log")) {
|
||||
if (!up)
|
||||
log_open();
|
||||
|
||||
vsyslog(prio, fmt, ap);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!debug && prio > loglevel)
|
||||
if (prio > loglevel)
|
||||
goto done;
|
||||
|
||||
fp = fopen("/dev/kmsg", "w");
|
||||
|
||||
+2
-65
@@ -30,75 +30,12 @@
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
|
||||
extern int logrotate(char *file, int num, off_t sz);
|
||||
|
||||
|
||||
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||
* At most @num old versions are kept and by default it starts gzipping
|
||||
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||
* files are kept uncompressed.
|
||||
*/
|
||||
static int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
return 1;
|
||||
|
||||
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||
if (num > 0) {
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
|
||||
if (cnt == 2 && !access(nfile, F_OK)) {
|
||||
size_t len = 5 + strlen(nfile) + 1;
|
||||
char cmd[len];
|
||||
|
||||
snprintf(cmd, len, "gzip %s", nfile);
|
||||
system(cmd);
|
||||
|
||||
remove(nfile);
|
||||
}
|
||||
}
|
||||
|
||||
if (rename(file, nfile))
|
||||
(void)truncate(file, 0);
|
||||
else
|
||||
create(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
if (truncate(file, 0))
|
||||
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checksz(FILE *fp, off_t sz)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
if (mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid)) {
|
||||
syslog(LOG_ERR, "Failed recreating %s: %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||
* At most @num old versions are kept and by default it starts gzipping
|
||||
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||
* files are kept uncompressed.
|
||||
*/
|
||||
int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
return 1;
|
||||
|
||||
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||
if (num > 0) {
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
if (rename(ofile, nfile) && errno != ENOENT)
|
||||
syslog(LOG_ERR, "Failed logrotate %s: %s",
|
||||
ofile, strerror(errno));
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
if (rename(ofile, nfile) && errno != ENOENT) {
|
||||
syslog(LOG_ERR, "Failed logrotate %s: %s",
|
||||
ofile, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cnt == 2 && fexist(nfile)) {
|
||||
if (systemf("gzip %s 2>/dev/null", nfile))
|
||||
continue; /* no gzip, probably */
|
||||
|
||||
(void)remove(nfile);
|
||||
}
|
||||
}
|
||||
|
||||
if (rename(file, nfile))
|
||||
goto fallback;
|
||||
recreate(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
fallback:
|
||||
if (truncate(file, 0))
|
||||
syslog(LOG_ERR, "Failed truncating %s during logrotate: %s",
|
||||
file, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -95,18 +95,15 @@ 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];
|
||||
|
||||
if (svc->pidfile[0]) {
|
||||
if (svc->pidfile[0] == '!')
|
||||
return &svc->pidfile[1];
|
||||
return svc->pidfile;
|
||||
}
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
||||
errno = ENOENT;
|
||||
|
||||
return pid_runpath(fn, path, sizeof(path));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid_t pid_file_read(const char *fn)
|
||||
@@ -143,12 +140,13 @@ int pid_file_create(svc_t *svc)
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
static int pid_realpath(svc_t *svc, char *file)
|
||||
int pid_file_set(svc_t *svc, char *file, int not)
|
||||
{
|
||||
int not = 0;
|
||||
|
||||
if (!file)
|
||||
if (!file) {
|
||||
file = pid_file(svc);
|
||||
if (!file)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file[0] == '!') {
|
||||
not = 1;
|
||||
@@ -202,7 +200,7 @@ int pid_file_parse(svc_t *svc, char *arg)
|
||||
|
||||
arg += 4;
|
||||
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
|
||||
return pid_realpath(svc, arg);
|
||||
return pid_file_set(svc, arg, 0);
|
||||
|
||||
if (arg[0] == '!') {
|
||||
arg++;
|
||||
@@ -213,12 +211,12 @@ int pid_file_parse(svc_t *svc, char *arg)
|
||||
if (len > 4 && strcmp(&path[len - 4], ".pid"))
|
||||
strlcat(path, ".pid", sizeof(path));
|
||||
|
||||
return pid_realpath(svc, path);
|
||||
return pid_file_set(svc, path, 0);
|
||||
}
|
||||
|
||||
/* 'pid' arg, no argument following */
|
||||
if (!strcmp(arg, "pid"))
|
||||
return pid_realpath(svc, NULL);
|
||||
return pid_file_set(svc, NULL, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
char *pid_file (svc_t *svc);
|
||||
int pid_file_set (svc_t *svc, char *file, int not);
|
||||
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
-10
@@ -30,10 +30,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
extern int icreate; /* initctl -c */
|
||||
extern int iforce; /* initctl -f */
|
||||
#include "initctl.h"
|
||||
|
||||
|
||||
static int calc_width(char *arr[], size_t len)
|
||||
@@ -53,20 +50,40 @@ static int calc_width(char *arr[], size_t len)
|
||||
|
||||
static void do_list(const char *path)
|
||||
{
|
||||
char buf[screen_cols];
|
||||
static int once = 0;
|
||||
int width, num, prev;
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
|
||||
chdir(path);
|
||||
if (chdir(path))
|
||||
return;
|
||||
|
||||
if (glob("*.conf", 0, NULL, &gl))
|
||||
return;
|
||||
|
||||
if (gl.gl_pathc <= 0)
|
||||
goto done;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s ", path);
|
||||
printheader(NULL, buf, 0);
|
||||
if (plain) {
|
||||
if (heading)
|
||||
print_header("%s%s ", once ? "\n" : "", path);
|
||||
once++;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
if (!heading) {
|
||||
char buf[512];
|
||||
|
||||
paste(buf, sizeof(buf), path, gl.gl_pathv[i]);
|
||||
puts(buf);
|
||||
} else
|
||||
puts(gl.gl_pathv[i]);
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (heading)
|
||||
print_header("%s ", path);
|
||||
|
||||
width = calc_width(gl.gl_pathv, gl.gl_pathc);
|
||||
if (width <= 0)
|
||||
@@ -138,6 +155,11 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
||||
{
|
||||
char corr[40];
|
||||
|
||||
if (!name || !name[0]) {
|
||||
strlcpy(path, FINIT_CONF, len);
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!strstr(name, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", name);
|
||||
name = corr;
|
||||
@@ -208,7 +230,7 @@ int do_disable(char *arg, int check)
|
||||
|
||||
if (chdir(FINIT_RCSD))
|
||||
err(1, "failed cd %s", FINIT_RCSD);
|
||||
chdir("enabled"); /* System *may* have enabled/ dir. */
|
||||
(void)chdir("enabled"); /* System *may* have enabled/ dir. */
|
||||
|
||||
if (check && stat(arg, &st))
|
||||
errx(1, "%s not (an) enabled (service).", arg);
|
||||
@@ -250,6 +272,20 @@ int serv_touch(char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serv_show(char *arg)
|
||||
{
|
||||
char path[256];
|
||||
char *fn;
|
||||
|
||||
fn = conf(path, sizeof(path), arg, 0);
|
||||
if (!fexist(fn)) {
|
||||
warnx("Cannot find %s", arg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return systemf("cat %s", fn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to open an editor for the given file, if creat is given we
|
||||
* create a new file based on /lib/finit/sample.conf
|
||||
@@ -358,7 +394,8 @@ int serv_delete(char *arg)
|
||||
|
||||
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
|
||||
do_disable(arg, 0);
|
||||
remove(fn);
|
||||
if (remove(fn))
|
||||
err(1, "Failed removing %s", fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -28,6 +28,7 @@ int serv_list (char *arg);
|
||||
int serv_enable (char *arg);
|
||||
int serv_disable (char *arg);
|
||||
int serv_touch (char *arg);
|
||||
int serv_show (char *arg);
|
||||
int serv_edit (char *arg);
|
||||
int serv_creat (char *arg);
|
||||
int serv_delete (char *arg);
|
||||
|
||||
+92
-47
@@ -49,8 +49,6 @@
|
||||
#include "utmp-api.h"
|
||||
#include "schedule.h"
|
||||
|
||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||
|
||||
static struct wq work = {
|
||||
.cb = service_worker,
|
||||
};
|
||||
@@ -262,37 +260,6 @@ static int redirect(svc_t *svc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* non-zero env, no checking if file exists or not
|
||||
*/
|
||||
static char *svc_getenv(svc_t *svc)
|
||||
{
|
||||
int v = 0;
|
||||
|
||||
if (!svc->env[0])
|
||||
return NULL;
|
||||
|
||||
if (svc->env[0] == '-')
|
||||
v = 1;
|
||||
|
||||
return &svc->env[v];
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if svc has env, if env file exists returns true
|
||||
* if it doesn't exist, but has '-', also true. if svc
|
||||
* doesn't have env, also true.
|
||||
*/
|
||||
static int check_env(svc_t *svc)
|
||||
{
|
||||
char *env = svc_getenv(svc);
|
||||
|
||||
if (!env)
|
||||
return 1;
|
||||
|
||||
return fexist(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Source environment file, if it exists
|
||||
* Note: must be called from privsepped child
|
||||
@@ -309,7 +276,7 @@ static void source_env(svc_t *svc)
|
||||
if (!fn)
|
||||
return;
|
||||
|
||||
/* Warning in service_start() after check_env() */
|
||||
/* Warning in service_start() after svc_checkenv() */
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
@@ -384,6 +351,30 @@ static int is_norespawn(void)
|
||||
fexist("/tmp/norespawn");
|
||||
}
|
||||
|
||||
/* used for process group name, derived from originating filename,
|
||||
* so to group multiple services, place them in the same .conf
|
||||
*/
|
||||
static char *group_name(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (!svc->file[0])
|
||||
return svc_ident(svc, buf, len);
|
||||
|
||||
ptr = strrchr(svc->file, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = svc->file;
|
||||
|
||||
strlcpy(buf, ptr, len);
|
||||
ptr = strstr(buf, ".conf");
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_start - Start service
|
||||
* @svc: Service to start
|
||||
@@ -395,6 +386,7 @@ static int service_start(svc_t *svc)
|
||||
{
|
||||
int result = 0, do_progress = 1;
|
||||
sigset_t nmask, omask;
|
||||
char grnam[80];
|
||||
pid_t pid;
|
||||
size_t i;
|
||||
|
||||
@@ -407,14 +399,15 @@ static int service_start(svc_t *svc)
|
||||
|
||||
/* Don't try and start service if it doesn't exist. */
|
||||
if (!whichp(svc->cmd)) {
|
||||
print(1, "%s: does not exist", svc->cmd);
|
||||
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
|
||||
svc_missing(svc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Unlike systemd we do not allow starting service if env is missing, unless - */
|
||||
if (!check_env(svc)) {
|
||||
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
|
||||
if (!svc_checkenv(svc)) {
|
||||
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
|
||||
svc_missing(svc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -440,8 +433,6 @@ static int service_start(svc_t *svc)
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
|
||||
pid = fork();
|
||||
cgroup_service(svc->name, svc->id, pid);
|
||||
|
||||
if (pid == 0) {
|
||||
int status;
|
||||
char *home = NULL;
|
||||
@@ -464,17 +455,18 @@ static int service_start(svc_t *svc)
|
||||
|
||||
/* Set desired user+group */
|
||||
if (gid >= 0)
|
||||
setgid(gid);
|
||||
(void)setgid(gid);
|
||||
|
||||
if (uid >= 0) {
|
||||
setuid(uid);
|
||||
(void)setuid(uid);
|
||||
|
||||
/* Set default path for regular users */
|
||||
if (uid > 0)
|
||||
setenv("PATH", _PATH_DEFPATH, 1);
|
||||
if (home) {
|
||||
setenv("HOME", home, 1);
|
||||
chdir(home);
|
||||
if (chdir(home))
|
||||
(void)chdir("/");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,6 +570,8 @@ static int service_start(svc_t *svc)
|
||||
_d("Starting %s %s", svc->cmd, buf);
|
||||
}
|
||||
|
||||
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid);
|
||||
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
|
||||
|
||||
svc->pid = pid;
|
||||
@@ -647,11 +641,12 @@ static void service_cleanup(svc_t *svc)
|
||||
char *fn;
|
||||
|
||||
fn = pid_file(svc);
|
||||
if (remove(fn) && errno != ENOENT)
|
||||
if (fn && remove(fn) && errno != ENOENT)
|
||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
||||
basename(svc->cmd), fn);
|
||||
|
||||
/* No longer running, update books. */
|
||||
svc->oldpid = svc->pid;
|
||||
svc->start_time = svc->pid = 0;
|
||||
}
|
||||
|
||||
@@ -1126,6 +1121,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
|
||||
if (type == SVC_TYPE_SERVICE && manual)
|
||||
svc_stop(svc);
|
||||
} else {
|
||||
/* e.g., if missing cmd or env before */
|
||||
svc_unblock(svc);
|
||||
}
|
||||
|
||||
/* Always clear svc PID file, for now. See TODO */
|
||||
@@ -1142,6 +1140,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
strlcpy(svc->group, ptr, sizeof(svc->group));
|
||||
}
|
||||
strlcpy(svc->username, username, sizeof(svc->username));
|
||||
} else {
|
||||
getcuser(svc->username, sizeof(svc->username));
|
||||
getcgroup(svc->group, sizeof(svc->group));
|
||||
}
|
||||
|
||||
parse_cmdline_args(svc, cmd);
|
||||
@@ -1162,6 +1163,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
if (env)
|
||||
parse_env(svc, env);
|
||||
if (file)
|
||||
strlcpy(svc->file, file, sizeof(svc->file));
|
||||
|
||||
/* Set configured limits */
|
||||
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
||||
@@ -1226,6 +1229,7 @@ void service_monitor(pid_t lost, int status)
|
||||
if (svc_is_daemon(svc)) {
|
||||
service_cleanup(svc);
|
||||
} else if (svc_is_runtask(svc)) {
|
||||
/* run/task should run at least once per runlevel */
|
||||
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
||||
svc->started = 1;
|
||||
else
|
||||
@@ -1247,6 +1251,38 @@ void service_monitor(pid_t lost, int status)
|
||||
sm_step(&sm);
|
||||
}
|
||||
|
||||
static void svc_mark_affected(char *cond)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc_has_cond(svc))
|
||||
continue;
|
||||
|
||||
if (cond_affects(cond, svc->cond))
|
||||
svc_mark_dirty(svc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on conf_reload() to update service reverse dependencies.
|
||||
* E.g., if ospfd depends on zebra and the zebra Finit conf has
|
||||
* changed, we need to mark the ospfd Finit conf as changed too.
|
||||
*/
|
||||
void service_update_rdeps(void)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
if (!svc_is_changed(svc))
|
||||
continue;
|
||||
|
||||
svc_mark_affected(mkcond(svc, cond, sizeof(cond)));
|
||||
}
|
||||
}
|
||||
|
||||
static void service_retry(svc_t *svc)
|
||||
{
|
||||
int timeout;
|
||||
@@ -1261,7 +1297,7 @@ static void service_retry(svc_t *svc)
|
||||
return;
|
||||
}
|
||||
|
||||
if (*restart_cnt >= RESPAWN_MAX) {
|
||||
if (*restart_cnt >= SVC_RESPAWN_MAX) {
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
|
||||
svc_ident(svc, NULL, 0));
|
||||
svc_crashing(svc);
|
||||
@@ -1274,12 +1310,12 @@ static void service_retry(svc_t *svc)
|
||||
|
||||
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
|
||||
svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
|
||||
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX);
|
||||
svc_unblock(svc);
|
||||
service_step(svc);
|
||||
|
||||
/* Wait 2s for the first 5 respawns, then back off to 5s */
|
||||
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
|
||||
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000;
|
||||
service_timeout_after(svc, timeout, service_retry);
|
||||
}
|
||||
|
||||
@@ -1333,10 +1369,15 @@ restart:
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
if (!svc->pid) {
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
/* PID was collected normally, no need to kill it */
|
||||
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
|
||||
_d("%s: stopped normally, no need to send SIGKILL :)", svc->cmd);
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
_d("%s: clearing pid condition ...", svc->name);
|
||||
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
@@ -1365,6 +1406,10 @@ restart:
|
||||
|
||||
err = service_start(svc);
|
||||
if (err) {
|
||||
if (svc_is_missing(svc)) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
break;
|
||||
}
|
||||
(*restart_cnt)++;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ void service_unregister (svc_t *svc);
|
||||
|
||||
void service_runtask_clean (void);
|
||||
void service_reload_dynamic (void);
|
||||
void service_update_rdeps (void);
|
||||
|
||||
int service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
|
||||
@@ -192,10 +192,10 @@ void do_kill(int signo)
|
||||
if (fgets(file, sizeof(file), fp)) {
|
||||
if (strstr(file, "gdbserver"))
|
||||
_d("Skipping %s ...", file);
|
||||
else if (file[0] != '@')
|
||||
kill(pid, signo);
|
||||
else
|
||||
else if (file[0] == '@')
|
||||
_d("Skipping %s ...", &file[1]);
|
||||
else
|
||||
kill(pid, signo);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
@@ -214,11 +214,11 @@ void do_shutdown(shutop_t op)
|
||||
utmp_set_halt();
|
||||
|
||||
/*
|
||||
* Tell all remaining non-monitored processes to exit, give them
|
||||
* some time to exit gracefully, 2 sec is customary.
|
||||
* Tell remaining non-monitored processes to exit, give them
|
||||
* time to exit gracefully, 2 sec was customary, we go for 1.
|
||||
*/
|
||||
do_kill(SIGTERM);
|
||||
do_sleep(2);
|
||||
do_sleep(1);
|
||||
do_kill(SIGKILL);
|
||||
|
||||
/* Exit plugins and API gracefully */
|
||||
@@ -274,15 +274,15 @@ void do_shutdown(shutop_t op)
|
||||
do_sleep(1);
|
||||
}
|
||||
|
||||
_e("Rebooting ...");
|
||||
_d("Rebooting ...");
|
||||
reboot(RB_AUTOBOOT);
|
||||
} else if (op == SHUT_OFF) {
|
||||
_e("Powering down ...");
|
||||
_d("Powering down ...");
|
||||
reboot(RB_POWER_OFF);
|
||||
}
|
||||
|
||||
/* Also fallback if any of the other two fails */
|
||||
_e("Halting ...");
|
||||
_d("Halting ...");
|
||||
reboot(RB_HALT_SYSTEM);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,9 +156,11 @@ restart:
|
||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||
nologin();
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (conf_any_change())
|
||||
conf_reload();
|
||||
if (runlevel != 0 && runlevel != 6) {
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (conf_any_change())
|
||||
conf_reload();
|
||||
}
|
||||
|
||||
/* Reset once flag of runtasks */
|
||||
service_runtask_clean();
|
||||
|
||||
@@ -415,8 +415,20 @@ svc_t *svc_find_by_pidfile(char *fn)
|
||||
pid_t pid;
|
||||
|
||||
pid = pid_file_read(fn);
|
||||
if (pid == -1)
|
||||
if (pid <= 0) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
int v = 0;
|
||||
|
||||
if (svc->pidfile[0] == '!')
|
||||
v = 1;
|
||||
if (strcmp(&svc->pidfile[v], fn))
|
||||
continue;
|
||||
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->pid != pid)
|
||||
@@ -532,6 +544,9 @@ int svc_enabled(svc_t *svc)
|
||||
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||
return 0;
|
||||
|
||||
if (svc_is_missing(svc))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,9 @@ typedef enum {
|
||||
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
|
||||
#define SVC_TERM_TIMEOUT 3000
|
||||
|
||||
/* Prevent endless respawn of faulty services. */
|
||||
#define SVC_RESPAWN_MAX 10
|
||||
|
||||
/*
|
||||
* Default enable for all services, can be stopped by means
|
||||
* of issuing an initctl call. E.g.
|
||||
@@ -86,9 +89,8 @@ typedef enum {
|
||||
typedef struct svc {
|
||||
TAILQ_ENTRY(svc) link;
|
||||
|
||||
/* Instance specifics */
|
||||
int job; /* JOB: */
|
||||
char id[MAX_ID_LEN]; /* :ID */
|
||||
/* Origin of service */
|
||||
char file[MAX_ARG_LEN];
|
||||
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
@@ -96,7 +98,7 @@ typedef struct svc {
|
||||
/* Service details */
|
||||
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
|
||||
int killdelay; /* Delay in msec before sending SIGKILL */
|
||||
pid_t pid;
|
||||
pid_t oldpid, pid;
|
||||
char pidfile[256];
|
||||
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||
int started; /* Set for run/task/sysv to track if started */
|
||||
@@ -110,7 +112,11 @@ typedef struct svc {
|
||||
int sighup; /* This service supports SIGHUP :) */
|
||||
svc_block_t block; /* Reason that this service is currently stopped */
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
/* Instance specifics */
|
||||
int job; /* For intenal use only, canonical ref is NAME:ID */
|
||||
char name[MAX_ARG_LEN];
|
||||
char id[MAX_ID_LEN]; /* :ID */
|
||||
|
||||
/* Counters */
|
||||
char once; /* run/task, (at least) once per runlevel */
|
||||
@@ -191,12 +197,14 @@ static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1;
|
||||
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
|
||||
|
||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
|
||||
|
||||
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
||||
#define svc_start(svc) svc_unblock(svc)
|
||||
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
|
||||
@@ -205,6 +213,26 @@ static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOC
|
||||
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
|
||||
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
|
||||
|
||||
/* Has condition in configuration and cond is allowed? */
|
||||
static inline int svc_has_cond(svc_t *svc)
|
||||
{
|
||||
if (!svc->cond[0])
|
||||
return 0;
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_SYSV:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline char *svc_status(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
@@ -324,6 +352,37 @@ static inline char *svc_jobid(svc_t *svc, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* non-zero env, no checking if file exists or not
|
||||
*/
|
||||
static inline char *svc_getenv(svc_t *svc)
|
||||
{
|
||||
int v = 0;
|
||||
|
||||
if (!svc->env[0])
|
||||
return NULL;
|
||||
|
||||
if (svc->env[0] == '-')
|
||||
v = 1;
|
||||
|
||||
return &svc->env[v];
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if svc has env, if env file exists returns true
|
||||
* if it doesn't exist, but has '-', also true. if svc
|
||||
* doesn't have env, also true.
|
||||
*/
|
||||
static inline int svc_checkenv(svc_t *svc)
|
||||
{
|
||||
char *env = svc_getenv(svc);
|
||||
|
||||
if (!env)
|
||||
return 1;
|
||||
|
||||
return fexist(env);
|
||||
}
|
||||
|
||||
#endif /* FINIT_SVC_H_ */
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#define TTY_MAX_ARGS 16
|
||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||
|
||||
struct tty {
|
||||
LIST_ENTRY(tty) link;
|
||||
|
||||
+62
-15
@@ -28,9 +28,15 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
# include <termios.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <lite/lite.h> /* strlcat() */
|
||||
#include "util.h"
|
||||
@@ -202,25 +208,66 @@ char *sanitize(char *arg, size_t len)
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at boot, and shutdown, to (re)initialize the
|
||||
* screen size for print() et al.
|
||||
* Called by initctl, and by finit at boot and shutdown, to
|
||||
* (re)initialize the screen size for print() et al.
|
||||
*/
|
||||
void screen_init(void)
|
||||
int screen_init(void)
|
||||
{
|
||||
if (!isatty(STDOUT_FILENO))
|
||||
return;
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
|
||||
struct winsize ws = { 0 };
|
||||
struct termios tc, saved;
|
||||
|
||||
initscr(&screen_rows, &screen_cols);
|
||||
}
|
||||
/*
|
||||
* Basic TTY init, CLOCaL is important or TIOCWINSZ will block
|
||||
* until DCD is asserted, and we won't ever get it.
|
||||
*/
|
||||
tcgetattr(STDERR_FILENO, &tc);
|
||||
saved = tc;
|
||||
tc.c_cflag |= (CLOCAL | CREAD);
|
||||
tc.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &tc);
|
||||
|
||||
/*
|
||||
* Called when debug mode is enabled to revert back
|
||||
* to old-school safe defaults.
|
||||
*/
|
||||
void screen_exit(void)
|
||||
{
|
||||
screen_rows = 24;
|
||||
screen_cols = 80;
|
||||
/* 1. Try TIOCWINSZ to query window size from kernel */
|
||||
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) {
|
||||
screen_rows = ws.ws_row;
|
||||
screen_cols = ws.ws_col;
|
||||
|
||||
/* Likely doesn't work in PID 1 after kernel starts us ... */
|
||||
if (!ws.ws_row && !ws.ws_col)
|
||||
goto fallback;
|
||||
} else if (!isatty(STDOUT_FILENO)) {
|
||||
/* 2. We may be running under watch(1) */
|
||||
screen_cols = atonum(getenv("COLUMNS"));
|
||||
screen_rows = atonum(getenv("LINES"));
|
||||
} else {
|
||||
fallback:
|
||||
/* 3. ANSI goto + query cursor position trick as fallback */
|
||||
fprintf(stderr, "\e7\e[r\e[999;999H\e[6n");
|
||||
|
||||
if (poll(&fd, 1, 300) > 0) {
|
||||
int row, col;
|
||||
|
||||
if (scanf("\e[%d;%dR", &row, &col) == 2) {
|
||||
screen_cols = col;
|
||||
screen_rows = row;
|
||||
}
|
||||
}
|
||||
|
||||
/* Jump back to where we started (\e7) */
|
||||
fprintf(stderr, "\e8");
|
||||
}
|
||||
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &saved);
|
||||
|
||||
/* Sanity check */
|
||||
if (screen_cols <= 0)
|
||||
screen_cols = 80;
|
||||
if (screen_rows <= 0)
|
||||
screen_rows = 24;
|
||||
#endif
|
||||
|
||||
return screen_cols;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-3
@@ -55,9 +55,7 @@ char *uptime (long secs, char *buf, size_t len);
|
||||
|
||||
char *sanitize (char *arg, size_t len);
|
||||
|
||||
void screen_init (void);
|
||||
void screen_exit (void);
|
||||
int screen_width (void);
|
||||
int screen_init (void);
|
||||
|
||||
static inline char *strterm(char *str, size_t len)
|
||||
{
|
||||
|
||||
+5
-61
@@ -35,6 +35,9 @@
|
||||
#define MAX_NO 5
|
||||
#define MAX_SZ 100 * 1024
|
||||
|
||||
extern int logrotate(char *file, int num, off_t sz);
|
||||
|
||||
|
||||
static void utmp_strncpy(char *dst, const char *src, size_t dlen)
|
||||
{
|
||||
size_t i;
|
||||
@@ -46,65 +49,6 @@ static void utmp_strncpy(char *dst, const char *src, size_t dlen)
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
#ifdef LOGROTATE_ENABLED
|
||||
/*
|
||||
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||
* At most @num old versions are kept and by default it starts gzipping
|
||||
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||
* files are kept uncompressed.
|
||||
*/
|
||||
static int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
return 1;
|
||||
|
||||
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||
if (num > 0) {
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
|
||||
if (cnt == 2 && fexist(nfile)) {
|
||||
size_t len = 5 + strlen(nfile) + 1;
|
||||
char cmd[len];
|
||||
|
||||
snprintf(cmd, len, "gzip %s", nfile);
|
||||
run(cmd);
|
||||
|
||||
remove(nfile);
|
||||
}
|
||||
}
|
||||
|
||||
(void)rename(file, nfile);
|
||||
create(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
truncate(file, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* LOGROTATE_ENABLED */
|
||||
|
||||
/*
|
||||
* Rotate /var/log/wtmp (+ btmp?) and /run/utmp
|
||||
*
|
||||
@@ -194,13 +138,13 @@ static int set_getty(int type, char *tty, char *id, char *user)
|
||||
return 0;
|
||||
|
||||
if (!tty)
|
||||
line = "";
|
||||
line = NULL;
|
||||
else if (!strncmp(tty, "/dev/", 5))
|
||||
line = tty + 5;
|
||||
else
|
||||
line = tty;
|
||||
|
||||
if (!id) {
|
||||
if (!id && line) {
|
||||
id = line;
|
||||
if (!strncmp(id, "pts/", 4))
|
||||
id += 4;
|
||||
|
||||
@@ -44,6 +44,6 @@ fi
|
||||
mkdir -p "$FINIT_RCSD"
|
||||
|
||||
tty=/dev/$(cat /sys/class/tty/console/active)
|
||||
ln -fs /shared/pts "$tty"
|
||||
mkfifo "$tty"
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -138,3 +138,7 @@ echo "$finit_ppid" > "$TENV_ROOT"/running_test.pid
|
||||
log "$color_reset" 'Setup of test environment done' ''
|
||||
|
||||
finit_pid=$(retry "pgrep -P $finit_ppid")
|
||||
|
||||
tty=/dev/$(texec cat /sys/class/tty/console/active)
|
||||
texec cat "$tty" &
|
||||
sleep 1
|
||||
|
||||
@@ -38,7 +38,6 @@ binaries = $(DEST)/bin/cat \
|
||||
$(DEST)/bin/env \
|
||||
$(DEST)/bin/find \
|
||||
$(DEST)/bin/kill \
|
||||
$(DEST)/bin/ln \
|
||||
$(DEST)/bin/ls \
|
||||
$(DEST)/bin/mkdir \
|
||||
$(DEST)/bin/mkfifo \
|
||||
@@ -58,7 +57,6 @@ dirs = $(DEST)/bin \
|
||||
$(DEST)/etc \
|
||||
$(DEST)/proc \
|
||||
$(DEST)/sbin \
|
||||
$(DEST)/shared \
|
||||
$(DEST)/sys \
|
||||
$(DEST)/test_assets \
|
||||
$(DEST)/tmp
|
||||
|
||||
+1
-4
@@ -45,7 +45,4 @@ exec "$unshare" \
|
||||
--mount \
|
||||
--mount-proc \
|
||||
--uts --ipc --net \
|
||||
sh -c "
|
||||
ln -fs /proc/1/fd/1 $TENV_ROOT/shared/pts;
|
||||
exec $chroot $TENV_ROOT /bin/chrootsetup.sh $*
|
||||
"
|
||||
"$chroot" "$TENV_ROOT" /bin/chrootsetup.sh "$@"
|
||||
|
||||
Reference in New Issue
Block a user