Compare commits

..
Author SHA1 Message Date
Jacques de Laval 50c99762f1 Add run target for running Finit as a stand alone unprivileged process
It could be very useful, during development, to be able to run Finit on a
host system without having to provision a full VM. We have all the building
blocks for easily containerizing Finit - in the test framework - so adding
a "run" target that does the orchestration makes sense.

Signed-off-by: Jacques de Laval <jacques@de-laval.se>
2021-03-05 01:39:42 +01:00
Jacques de Laval 067043b298 test: Use STDOUT of parent process instead of a pipe as tty for finit
Signed-off-by: Jacques de Laval <jacques@de-laval.se>
2021-03-05 01:24:31 +01:00
58 changed files with 814 additions and 2022 deletions
+5 -2
View File
@@ -5,6 +5,9 @@ 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
@@ -27,7 +30,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 --disable-doc --disable-contrib --enable-watchdog --enable-fallback-shell"
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
build_command: "make"
branch_pattern: dev
@@ -45,7 +48,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
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog --enable-logit
- cat config.log
- make clean
- make V=1 -j5
+1 -4
View File
@@ -4,7 +4,7 @@ Change Log
All relevant changes are documented in this file.
[4.0][UNRELEASED] - 2021-03-xx
[4.0][UNRELEASED] - 2021-02-21
------------------------------
This release became v4.0, and not v3.2, because of incompatible changes
@@ -18,9 +18,6 @@ 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
View File
@@ -29,3 +29,19 @@ 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
+33 -58
View File
@@ -26,9 +26,6 @@ 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 …"
![Alpine screenshot](img/finit4-screenshot.png)
*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:
@@ -37,6 +34,9 @@ on server and desktop systems as well. For working examples, see the
* [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/)
![Alpine screenshot](img/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
Example
-------
@@ -348,16 +348,11 @@ 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.
```
alpine:~# initctl help
```shell
~ $ initctl -h
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
@@ -365,36 +360,29 @@ Commands:
debug Toggle Finit (daemon) debug
help This help text
version Show Finit version
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
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)
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 <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
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
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system
halt Halt system
poweroff Halt and power off system
suspend Suspend system
utmp show Raw dump of UTMP/WTMP db
```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
@@ -407,31 +395,18 @@ 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
@@ -498,6 +473,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.svg?branch=master
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
+6 -5
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.0-rc3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_INIT(Finit, 4.0-rc1, 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 sys/ioctl.h])
AC_CHECK_HEADERS([fstaby.h termios.h])
AC_CHECK_FUNCS([strstr getopt getfsenty])
# Check for uint[8,16,32]_t
@@ -153,8 +153,6 @@ 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"], [
@@ -226,6 +224,9 @@ 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
@@ -234,7 +235,7 @@ cat <<EOF
------------------ Summary ------------------
$PACKAGE_NAME version $PACKAGE_VERSION
Prefix................: $prefix
Exec prefix...........: $eprefix
Exec prefix...........: `eval echo $exec_prefix`
Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir`
Finit config file.....: $conf_path
+1 -27
View File
@@ -45,31 +45,7 @@ being created.
Triggering
----------
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static
or one-shot conditions. They live in the `<usr/...` namespace and are
constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
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 can only be triggered by built-in plugins.
Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the
@@ -81,8 +57,6 @@ 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
-------------------
+3
View File
@@ -483,6 +483,9 @@ 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
View File
@@ -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 NAME`
- The user has manually stopped the service using `initctl stop JOB`
- 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.

Before

Width:  |  Height:  |  Size: 7.6 KiB

+2 -2
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC
noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.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 usr.la
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la
+30 -20
View File
@@ -99,14 +99,13 @@ static void bootclean(void)
*/
static void setup(void *arg)
{
mode_t prev;
int uid, gid;
/* 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);
@@ -134,21 +133,19 @@ 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 */
@@ -168,19 +165,32 @@ 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 */
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/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 */
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 */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
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);
}
umask(prev);
umask(022);
}
static plugin_t plugin = {
+10 -12
View File
@@ -32,16 +32,13 @@
#include "conf.h"
#define DAEMON "dbus-daemon"
#define ARGS "--nofork --system --syslog-only"
#define ARGS "--nofork --system"
#define DESC "D-Bus message bus daemon"
#define DAEMONUSER "messagebus"
static void setup(void *arg)
{
char line[256];
mode_t prev;
char *cmd;
char line[256];
cmd = which(DAEMON);
if (!cmd) {
@@ -49,23 +46,24 @@ static void setup(void *arg)
return;
}
prev =umask(0);
umask(0);
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
makedir("/var/run/dbus", 0755);
makedir("/var/lock/subsys", 0755);
makedir("/var/lock/subsys/messagebus", 0755);
if (whichp("dbus-uuidgen"))
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
run("dbus-uuidgen --ensure");
/* Clean up from any previous pre-bootstrap run */
remove("/var/run/dbus/pid");
erase("/var/run/dbus/pid");
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345] @%s:%s %s %s -- %s",
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON);
free(cmd);
umask(prev);
umask(022);
}
static plugin_t plugin = {
+40 -75
View File
@@ -17,7 +17,6 @@
*/
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <net/if.h> /* IFNAMSIZ */
#include <sys/socket.h>
@@ -49,7 +48,7 @@ static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
return 0;
}
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
static void nl_route(struct nlmsghdr *nlmsg)
{
struct rtmsg *r;
struct rtattr *a;
@@ -64,14 +63,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
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);
@@ -112,27 +105,7 @@ static void net_cond_set(char *ifname, char *cond, int set)
cond_clear(msg);
}
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)
static void nl_link(struct nlmsghdr *nlmsg)
{
int la;
char ifname[IFNAMSIZ + 1];
@@ -147,61 +120,53 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
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;
}
for (; RTA_OK(a, la); a = RTA_NEXT(a, la)) {
if (a->rta_type != IFLA_IFNAME)
continue;
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;
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
if (validate_ifname(ifname)) {
_d("Invalid interface name '%s', skipping ...", 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;
}
}
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;
}
a = RTA_NEXT(a, la);
}
}
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);
@@ -214,9 +179,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, len);
nl_route(nh);
else
nl_link(nh, len);
nl_link(nh);
}
}
+17 -23
View File
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
}
}
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
return iwatch_add(iw, path, IN_ONLYDIR);
}
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,20 +82,15 @@ 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_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
if (mask & (IN_CREATE | 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));
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
}
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
}
cond_set(cond);
@@ -156,8 +151,7 @@ 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;
size_t off;
ssize_t sz, off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
@@ -166,19 +160,15 @@ static void pidfile_callback(void *arg, int fd, int events)
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
for (off = 0; off < 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)
@@ -189,7 +179,12 @@ static void pidfile_callback(void *arg, int fd, int events)
continue;
}
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
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))
pidfile_update_conds(iwp->path, ev->name, ev->mask);
}
}
@@ -240,7 +235,6 @@ 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);
+21 -36
View File
@@ -53,48 +53,33 @@ 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)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
char buf[EVENT_SIZE];
int len = 0;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
while ((len = read(fd, buf, sizeof(buf)))) {
struct inotify_event *notified = (struct inotify_event *)buf;
char name[notified->len + 6];
struct tty *entry;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
if (-1 == len) {
if (errno == EINVAL)
setup();
if (errno == EINTR)
continue;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
break; /* Likely EAGAIN */
}
if (!ev->mask)
continue;
_d("tty %s, event: 0x%08x", ev->name, ev->mask);
do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
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);
}
}
}
+4 -9
View File
@@ -37,11 +37,10 @@ static void setup(void *arg)
#ifdef RANDOMSEED
if (!fexist(RANDOMSEED)) {
int ret = 1;
mode_t prev;
FILE *fp;
print_desc("Bootstrapping random seed", NULL);
prev = umask(077);
umask(077);
fp = fopen(RANDOMSEED, "w");
if (fp) {
int iter = 128;
@@ -58,7 +57,7 @@ static void setup(void *arg)
ret = fclose(fp);
}
print_result(ret);
umask(prev);
umask(0);
}
print_desc("Initializing random number generator", NULL);
@@ -69,14 +68,10 @@ static void setup(void *arg)
static void save(void *arg)
{
#ifdef RANDOMSEED
mode_t prev;
prev = umask(077);
umask(077);
print_desc("Saving random seed", NULL);
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
umask(prev);
umask(0);
#endif
}
-133
View File
@@ -1,133 +0,0 @@
/* 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:
*/
+4 -5
View File
@@ -32,14 +32,13 @@
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";
prev = umask(0);
umask(0);
makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username);
@@ -59,7 +58,7 @@ static void setup(void *arg)
if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
umask(prev);
umask(022);
}
static plugin_t plugin = {
+7 -12
View File
@@ -1,5 +1,6 @@
EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
AM_CPPFLAGS = -U_FORTIFY_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
@@ -19,10 +20,8 @@ endif
if LOGIT
pkglibexec_PROGRAMS = logit
logit_SOURCES = logit.c logrotate.c
logit_SOURCES = logit.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 \
@@ -44,10 +43,6 @@ 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
@@ -59,14 +54,14 @@ else
finit_LDADD += -ldl
endif
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_SOURCES = initctl.c client.c client.h \
reboot.c serv.c serv.h svc.h \
cond.c cond.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 telinit
INIT_LNKS = init
REBOOT_LNKS = reboot shutdown halt poweroff suspend
if WATCHDOGD
+2 -2
View File
@@ -237,7 +237,7 @@ ev_t ev_list[] = {
static void send_svc(int sd, svc_t *svc)
{
svc_t empty = { 0 };
svc_t empty;
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
View File
@@ -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
+27 -93
View File
@@ -26,65 +26,20 @@
#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 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));
}
}
static int cg_init = 0;
/*
* Called by Finit at early boot to mount initial cgroups
*/
void cgroup_init(void)
{
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
char buf[80];
FILE *fp;
char buf[80];
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
fp = fopen("/proc/cgroups", "r");
if (!fp) {
@@ -98,9 +53,7 @@ void cgroup_init(void)
}
/* Skip first line, header */
(void)fgets(buf, sizeof(buf), fp);
/* Create and mount traditional cgroups v1 hier */
fgets(buf, sizeof(buf), fp);
while (fgets(buf, sizeof(buf), fp)) {
char *cgroup;
char rc[80];
@@ -110,71 +63,46 @@ 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
}
/* Finit default cgroups for process monitoring */
if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno)
/* Default cgroups for process monitoring */
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
goto fail;
#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)) {
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
_pe("Failed mounting Finit cgroup hierarchy");
goto fail;
}
/* Set up reaper and enable callbacks */
echo(FINIT_CGPATH "/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
echo(FINIT_CGPATH "/notify_on_release", 0, "1");
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
/* Default groups, PID 1, services, and user/login processes */
if (mkdir(FINIT_CGPATH "/init", 0755) && EEXIST != errno)
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
goto fail;
if (mkdir(FINIT_CGPATH "/system", 0755) && EEXIST != errno)
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
goto fail;
if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno)
if (mkdir("/sys/fs/cgroup/finit/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(FINIT_CGPATH "/init/cgroup.procs", 0, "1");
echo("/sys/fs/cgroup/finit/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, int pid)
static int move_pid(char *group, char *name, char *id, int pid)
{
char path[256];
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
if (mkdir(path, 0755) && errno != EEXIST)
return 1;
@@ -183,19 +111,25 @@ static int move_pid(char *group, char *name, int pid)
return echo(path, 0, "%d", pid);
}
int cgroup_user(char *name, int pid)
int cgroup_user(char *name)
{
return move_pid("finit/user", name, pid);
if (!cg_init)
return 0;
return move_pid("finit/user", name, "0", getpid());
}
int cgroup_service(char *name, int pid)
int cgroup_service(char *nm, char *id, int pid)
{
if (!cg_init)
return 0;
if (pid <= 0) {
errno = EINVAL;
return 1;
}
return move_pid("finit/system", name, pid);
return move_pid("finit/system", nm, id, pid);
}
/**
+2 -2
View File
@@ -26,7 +26,7 @@
void cgroup_init (void);
int cgroup_user (char *name, int pid);
int cgroup_service (char *name, int pid);
int cgroup_user (char *name);
int cgroup_service (char *cmd, char *id, int pid);
#endif /* FINIT_CGROUP_H_ */
-483
View File
@@ -1,483 +0,0 @@
/* 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:
*/
-37
View File
@@ -1,37 +0,0 @@
/* 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:
*/
+8 -4
View File
@@ -21,7 +21,6 @@
* THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -39,12 +38,17 @@ int client_connect(void)
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
err(1, "Failed creating UNIX domain socket");
goto error;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
err(1, "Failed connecting to finit");
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
close(sd);
goto error;
}
return sd;
error:
perror("Failed connecting to finit");
return -1;
}
int client_disconnect(void)
+31 -13
View File
@@ -99,13 +99,13 @@ static void cond_bump_reconf(void)
unsigned int rgen;
/*
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0
* If %COND_RECONF does not exist, cond_get_gen() returns 0
* meaning that rgen++ is always what we want.
*/
rgen = cond_get_gen(_PATH_RECONF);
rgen = cond_get_gen(COND_RECONF);
rgen++;
cond_set_gen(_PATH_RECONF, rgen);
cond_set_gen(COND_RECONF, rgen);
}
static int cond_checkpath(const char *path)
@@ -119,7 +119,7 @@ static int cond_checkpath(const char *path)
return 1;
}
if (mkpath(dir, 0755) && errno != EEXIST) {
if (makepath(dir) && 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(_PATH_RECONF);
rgen = cond_get_gen(COND_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
@@ -162,8 +162,27 @@ int cond_set_path(const char *path, enum cond_state new)
return new != old;
}
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
void cond_update(const char *name)
/* 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)
{
svc_t *svc, *iter = NULL;
@@ -202,8 +221,7 @@ void cond_set_oneshot(const char *name)
if (cond_checkpath(path))
return;
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
_pe("Failed creating onshot cond %s", name);
symlink(COND_RECONF, path);
cond_update(name);
}
@@ -236,13 +254,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_BASE);
nm = strstr((char *)fpath, COND_DIR);
if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath);
return 1;
}
nm += strlen(COND_BASE);
nm += sizeof(COND_DIR);
_d("Reasserting %s => %s", fpath, nm);
cond_set(nm);
@@ -263,8 +281,8 @@ void cond_init(void)
{
char path[MAX_ARG_LEN];
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", _PATH_COND);
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", COND_PATH);
return;
}
+5 -5
View File
@@ -45,7 +45,7 @@ const char *cond_path(const char *name)
static char path[256];
char tmp[MAX_ARG_LEN];
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name);
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
return pid_runpath(tmp, path, sizeof(path));
}
@@ -74,12 +74,12 @@ enum cond_state cond_get_path(const char *path)
{
int cgen, rgen;
cgen = cond_get_gen(path);
if (!cgen)
rgen = cond_get_gen(COND_RECONF);
if (!rgen)
return COND_OFF;
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen)
cgen = cond_get_gen(path);
if (!cgen)
return COND_OFF;
return (cgen == rgen) ? COND_ON : COND_FLUX;
+3 -7
View File
@@ -4,12 +4,9 @@
#include <paths.h>
#include <svc.h>
#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"
#define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
#define COND_RECONF COND_PATH "/reconf"
typedef enum cond_state {
COND_OFF = 0,
@@ -26,7 +23,6 @@ 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);
+20 -38
View File
@@ -47,7 +47,6 @@
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 {
@@ -79,14 +78,14 @@ static void hide_args(int argc, char *argv[])
}
}
int get_bool(char *arg, int default_value)
int get_bool(char *ptr, int default_value)
{
if (!arg)
if (!ptr)
goto fallback;
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
if (string_compare(ptr, "true") || string_compare(ptr, "on") || string_compare(ptr, "1"))
return 1;
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
if (string_compare(ptr, "false") || string_compare(ptr, "off") || string_compare(ptr, "0"))
return 0;
fallback:
return default_value;
@@ -98,24 +97,19 @@ fallback:
*/
static void parse_finit_opts(char *opt)
{
char *arg;
char *ptr;
arg = strchr(opt, '=');
if (arg)
*arg++ = 0;
ptr = strchr(opt, '=');
if (ptr)
*ptr++ = 0;
if (string_compare(opt, "debug")) {
debug = get_bool(arg, 1);
debug = get_bool(ptr, 1);
return;
}
if (string_compare(opt, "status_style")) {
if (!arg) {
_e("status_style option requires an argument, skipping.");
return;
}
if (string_compare(arg, "old") || string_compare(arg, "classic"))
if (string_compare(ptr, "old") || string_compare(ptr, "classic"))
show_progress(PROGRESS_CLASSIC);
else
show_progress(PROGRESS_MODERN);
@@ -123,7 +117,7 @@ static void parse_finit_opts(char *opt)
}
if (string_compare(opt, "show_status")) {
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
show_progress(get_bool(ptr, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
return;
}
}
@@ -626,9 +620,12 @@ int conf_reload(void)
tty_mark();
/*
* Reset global rlimit to bootstrap values from conf_init().
* 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.
*/
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
if (rescue) {
int rc;
@@ -697,8 +694,6 @@ int conf_reload(void)
globfree(&gl);
/* Mark any reverse deps as chenaged. */
service_update_rdeps();
done:
/* Drop record of all .conf changes */
drop_changes();
@@ -805,8 +800,7 @@ 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;
size_t off;
ssize_t sz, off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
@@ -815,16 +809,10 @@ static void conf_cb(uev_t *w, void *arg, int events)
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
for (off = 0; off < 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;
@@ -894,14 +882,8 @@ 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++) {
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));
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
/* prepare /etc watcher */
fd = iwatch_init(&iw_conf);
+15 -23
View File
@@ -179,8 +179,7 @@ static void fs_remount_root(int fsckerr)
* XXX: Untested, in the initramfs age we should
* probably use switch_root instead.
*/
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL))
_pe("Failed %s / MS_MOVE");
rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
}
#endif /* SYSROOT */
@@ -199,6 +198,7 @@ static void fs_mount(void)
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
umask(0022);
}
/*
@@ -206,9 +206,6 @@ 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)
{
@@ -221,16 +218,8 @@ static void fs_init(void)
};
size_t i;
/* 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);
}
for (i = 0; i < NELEMS(fs); i++)
mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
}
/*
@@ -450,7 +439,7 @@ int main(int argc, char *argv[])
};
struct wq final = {
.cb = final_worker,
.delay = 100
.delay = 1000
};
uev_ctx_t loop;
@@ -481,13 +470,17 @@ int main(int argc, char *argv[])
ctx = &loop;
/*
* Set PATH, SHELL, and PWD early to something sane
* Set PATH, SHELL, PWD, and umask early to something sane
*/
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
if (chdir("/"))
_pe("Failed cd /");
chdir("/");
umask(0);
/* Set up canvas */
if (!rescue && !debug)
screen_init();
/* Allow progress, if enabled */
enable_progress(1);
@@ -497,10 +490,8 @@ 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);
/*
@@ -549,6 +540,7 @@ 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);
+1 -10
View File
@@ -36,17 +36,8 @@
#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/"
#endif
#ifndef FINIT_CGPATH
#define FINIT_CGPATH "/sys/fs/cgroup/finit"
#define _PATH_VARRUN "/var/run/"
#endif
#define LOGIT_PATH FINIT_LIBPATH_ "/logit"
+1 -1
View File
@@ -186,7 +186,7 @@ static int do_login(char *name)
{
struct stat st;
cgroup_user(name, getpid());
cgroup_user(name);
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/*
+8 -76
View File
@@ -103,9 +103,6 @@ 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 */
@@ -124,7 +121,7 @@ static void add_console(char *cons)
num_cons++;
}
static void find_active_consoles(void)
void console_init(void)
{
char *cons, *ptr, *tok;
char buf[512];
@@ -134,7 +131,7 @@ static void find_active_consoles(void)
if (!fp) {
fallback:
_d("Using fallback /dev/console");
add_console("console");
add_console("/dev/console");
return;
}
@@ -151,12 +148,6 @@ static void find_active_consoles(void)
}
}
void console_init(void)
{
find_active_consoles();
screen_init();
}
ssize_t cprintf(const char *fmt, ...)
{
const size_t len = strlen(fmt) * 2;
@@ -413,63 +404,6 @@ 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;
@@ -492,14 +426,14 @@ void set_hostname(char **hostname)
return;
}
if (fgets(*hostname, st.st_size, fp))
chomp(*hostname);
fgets(*hostname, st.st_size, fp);
chomp(*hostname);
fclose(fp);
}
done:
if (*hostname)
(void)sethostname(*hostname, strlen(*hostname));
sethostname(*hostname, strlen(*hostname));
}
/*
@@ -623,12 +557,10 @@ int ismnt(char *file, char *dir, char *mode)
while ((mnt = getmntent(fp))) {
if (!strcmp(mnt->mnt_dir, dir)) {
if (mode) {
if (hasopt(mnt->mnt_opts, mode))
found = 1;
} else
if (mode && hasopt(mnt->mnt_opts, mode))
found = 1;
else
found = 1;
break;
}
}
-6
View File
@@ -81,12 +81,6 @@ 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);
+239 -319
View File
@@ -39,21 +39,20 @@
#include "cond.h"
#include "serv.h"
#include "service.h"
#include "cgutil.h"
#include "util.h"
struct cmd {
char *cmd;
struct cmd *ctx;
int (*cb)(char *arg);
#define _PATH_COND _PATH_VARRUN "finit/cond/"
struct command {
char *cmd;
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;
@@ -73,7 +72,11 @@ static void col_widths(void)
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
int w, p;
svc_ident(svc, ident, sizeof(ident));
if (!numeric)
svc_ident(svc, ident, sizeof(ident));
else
svc_jobid(svc, ident, sizeof(ident));
w = (int)strlen(ident);
if (w > iw)
iw = w;
@@ -90,30 +93,6 @@ 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;
@@ -145,6 +124,7 @@ static int toggle_debug(char *arg)
static int do_log(char *svc)
{
char cmd[128];
char *logfile = "/var/log/syslog";
if (!svc || !svc[0])
@@ -153,7 +133,9 @@ static int do_log(char *svc)
if (!fexist(logfile))
logfile = "/var/log/messages";
return systemf("cat %s | grep %s | tail -10", logfile, svc);
snprintf(cmd, sizeof(cmd), "cat %s | grep %s | tail -10", logfile, svc);
return system(cmd);
}
static int do_runlevel(char *arg)
@@ -224,8 +206,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 task or service(s): %s\n\n", arg);
fprintf(stderr, "Usage: initctl %s <NAME>[:ID]\n",
fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data);
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]\n",
cmd == INIT_CMD_START_SVC ? "start" :
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
return 1;
@@ -247,7 +229,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_RECONF))
if (!strcmp(fpath, _PATH_COND "reconf"))
return 0;
asserted = condstr(cond_get_path(fpath));
@@ -263,15 +245,9 @@ 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;
}
@@ -284,7 +260,7 @@ static int do_cond_dump(char *arg)
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
iw, "IDENT", "STATUS", "CONDITION");
print_header(title);
printheader(NULL, title, 0);
}
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
@@ -295,93 +271,41 @@ static int do_cond_dump(char *arg)
return 0;
}
static int do_cond_act(char *arg, int creat)
static void show_cond_one(const char *_conds)
{
char oneshot[256];
size_t off;
static char conds[MAX_COND_LEN];
char *cond;
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
arg += strlen(COND_USR);
strlcpy(conds, _conds, sizeof(conds));
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);
putchar('<');
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
if (cond != conds)
strlcat(buf, ",", len);
putchar(',');
switch (cond_get(cond)) {
case COND_ON:
strlcat(buf, "+", len);
strlcat(buf, cond, len);
printf("+%s", cond);
break;
case COND_FLUX:
if (!plain)
strlcat(buf, "\e[1m", len);
strlcat(buf, "~", len);
strlcat(buf, cond, len);
if (!plain)
strlcat(buf, "\e[0m", len);
printf("\e[1m~%s\e[0m", cond);
break;
case COND_OFF:
if (!plain)
strlcat(buf, "\e[1m", len);
strlcat(buf, "-", len);
strlcat(buf, cond, len);
if (!plain)
strlcat(buf, "\e[0m", len);
printf("\e[1m-%s\e[0m", cond);
break;
}
}
strlcat(buf, ">", len);
free(conds);
return buf;
putchar('>');
}
static int do_cond_show(char *arg)
{
char ident[MAX_IDENT_LEN];
enum cond_state cond;
char buf[512];
svc_t *svc;
col_widths();
@@ -390,7 +314,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)");
print_header(title);
printheader(NULL, title, 0);
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
@@ -399,20 +323,51 @@ static int do_cond_show(char *arg)
cond = cond_get_agg(svc->cond);
svc_ident(svc, buf, sizeof(buf));
printf("%-*d %-*s ", pw, svc->pid, iw, buf);
if (!numeric)
svc_ident(svc, ident, sizeof(ident));
else
svc_jobid(svc, ident, sizeof(ident));
printf("%-*d %-*s ", pw, svc->pid, iw, ident);
if (cond == COND_ON)
printf("%-6.6s ", condstr(cond));
else
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
puts(svc_cond(svc, buf, sizeof(buf)));
show_cond_one(svc->cond);
puts("");
}
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 = {
@@ -440,18 +395,15 @@ int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
int utmp_show(char *file)
{
static int once = 0;
struct tm *sectm;
struct utmp *ut;
time_t sec;
struct utmp *ut;
struct tm *sectm;
int pid;
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
if (heading) {
print_header("%s%s ", once ? "\n" : "", file);
once++;
}
if (heading)
printheader(NULL, file, 0);
utmpname(file);
setutent();
@@ -518,7 +470,7 @@ char *runlevel_string(int runlevel, int levels)
for (i = 0; i < 10; i++) {
if (ISSET(levels, i)) {
if (!plain && runlevel == i)
if (runlevel == i)
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
if (i == 0)
@@ -526,7 +478,7 @@ char *runlevel_string(int runlevel, int levels)
else
lvl[pos++] = '0' + i;
if (!plain && runlevel == i)
if (runlevel == i)
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
} else {
lvl[pos++] = '-';
@@ -539,96 +491,30 @@ 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 uptm[42] = "N/A";
char buf[42] = "N/A";
svc = client_svc_find(arg);
if (!svc)
return 1;
printf("Status : %s\n", status(svc));
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
printf("Job : %d\n", svc->job);
printf("Service : %s\n", svc->cmd);
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("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
printf("Environment : %s\n", svc->env);
printf("PID : %d\n", svc->pid);
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("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
printf("Status : %s\n", svc_status(svc));
printf("\n");
return do_log(svc->cmd);
@@ -645,16 +531,21 @@ static int show_status(char *arg)
else
strlcat(title, "COMMAND", sizeof(title));
print_header(title);
printheader(NULL, title, 0);
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char *lvls;
int i;
printf("%-*d ", pw, svc->pid);
svc_ident(svc, ident, sizeof(ident));
printf("%-*s %s ", iw, ident, status(svc));
if (!numeric)
svc_ident(svc, ident, sizeof(ident));
else
svc_jobid(svc, ident, sizeof(ident));
printf("%-*s %-8s ", iw, ident, svc_status(svc));
lvls = runlevel_string(runlevel, svc->runlevels);
if (strchr(lvls, '\e'))
@@ -662,15 +553,91 @@ static int show_status(char *arg)
else
printf("%-12.12s ", lvls);
if (!verbose)
puts(svc->desc);
else
puts(svc_command(svc, buf, sizeof(buf)));
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);
}
}
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[] = {
@@ -715,8 +682,7 @@ 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"
" -1, --once Only one lap in commands like 'top'\n"
" -p, --plain Use plain table headings, no ctrl chars\n"
" -n, --numeric Show JOB:ID instead of NAME:ID\n"
" -t, --no-heading Skip table headings\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n"
@@ -732,14 +698,9 @@ 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);
else
fprintf(stderr,
" show Show %s\n", FINIT_CONF);
avail, avail, avail, avail);
if (has_ena)
fprintf(stderr,
" enable <CONF> Enable .conf in %s\n", avail);
@@ -754,22 +715,19 @@ static int usage(int rc)
" reload Reload " FINIT_CONF " (activate changes)\n");
fprintf(stderr,
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
"\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 show Show condition status\n"
" cond dump Dump all conditions and their status\n"
"\n"
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
" start <NAME>[:ID] Start service by name, with optional ID\n"
" stop <NAME>[:ID] Stop/Pause a running service by name\n"
" 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"
" 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"
"\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"
@@ -785,128 +743,69 @@ 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' },
{ "once", 0, NULL, '1' },
{ "plain", 0, NULL, 'p' },
{ "numeric", 0, NULL, 'n' },
{ "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, "1bcfh?ptv", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "bcfh?ntv", long_options, NULL)) != EOF) {
switch(c) {
case '1':
ionce = 1;
break;
case 'b':
interactive = 0;
break;
@@ -923,8 +822,8 @@ int main(int argc, char *argv[])
case '?':
return usage(0);
case 'p':
plain = 1;
case 'n':
numeric = 1;
break;
case 't':
@@ -940,8 +839,10 @@ 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;
@@ -951,7 +852,26 @@ int main(int argc, char *argv[])
}
}
return cmd_parse(argc - optind, &argv[optind], command);
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);
}
void logit(int prio, const char *fmt, ...)
-46
View File
@@ -1,46 +0,0 @@
/* 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:
*/
+7 -4
View File
@@ -22,6 +22,7 @@
*/
#include "config.h"
#include <sched.h> /* sched_yield() */
#include <stdio.h>
#include <stdarg.h>
#include <lite/lite.h>
@@ -49,13 +50,15 @@ void log_exit(void)
* Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime
*/
if (!debug)
if (debug)
screen_exit();
else
screen_init();
enable_progress(1);
}
static void log_open(void)
void log_open(void)
{
int opts = LOG_PID;
@@ -76,6 +79,7 @@ void log_debug(void)
if (debug) {
loglevel = LOG_DEBUG;
screen_exit();
} else {
loglevel = LOG_NOTICE;
screen_init();
@@ -99,12 +103,11 @@ void logit(int prio, const char *fmt, ...)
if (up || fexist("/dev/log")) {
if (!up)
log_open();
vsyslog(prio, fmt, ap);
goto done;
}
if (prio > loglevel)
if (!debug && prio > loglevel)
goto done;
fp = fopen("/dev/kmsg", "w");
+65 -2
View File
@@ -30,12 +30,75 @@
#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
View File
@@ -1,102 +0,0 @@
/*
* 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;
}
+12 -10
View File
@@ -95,15 +95,18 @@ 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;
}
errno = ENOENT;
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
return NULL;
return pid_runpath(fn, path, sizeof(path));
}
pid_t pid_file_read(const char *fn)
@@ -140,13 +143,12 @@ int pid_file_create(svc_t *svc)
return fclose(fp);
}
int pid_file_set(svc_t *svc, char *file, int not)
static int pid_realpath(svc_t *svc, char *file)
{
if (!file) {
int not = 0;
if (!file)
file = pid_file(svc);
if (!file)
return -1;
}
if (file[0] == '!') {
not = 1;
@@ -200,7 +202,7 @@ int pid_file_parse(svc_t *svc, char *arg)
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
return pid_file_set(svc, arg, 0);
return pid_realpath(svc, arg);
if (arg[0] == '!') {
arg++;
@@ -211,12 +213,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_file_set(svc, path, 0);
return pid_realpath(svc, path);
}
/* 'pid' arg, no argument following */
if (!strcmp(arg, "pid"))
return pid_file_set(svc, NULL, 0);
return pid_realpath(svc, NULL);
return 1;
}
-1
View File
@@ -32,7 +32,6 @@ int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc);
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);
+10 -47
View File
@@ -30,7 +30,10 @@
#include <stdlib.h>
#include <lite/lite.h>
#include "initctl.h"
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
static int calc_width(char *arr[], size_t len)
@@ -50,40 +53,20 @@ static int calc_width(char *arr[], size_t len)
static void do_list(const char *path)
{
static int once = 0;
char buf[screen_cols];
int width, num, prev;
glob_t gl;
size_t i;
if (chdir(path))
return;
chdir(path);
if (glob("*.conf", 0, NULL, &gl))
return;
if (gl.gl_pathc <= 0)
goto done;
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);
snprintf(buf, sizeof(buf), "%s ", path);
printheader(NULL, buf, 0);
width = calc_width(gl.gl_pathv, gl.gl_pathc);
if (width <= 0)
@@ -155,11 +138,6 @@ 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;
@@ -230,7 +208,7 @@ int do_disable(char *arg, int check)
if (chdir(FINIT_RCSD))
err(1, "failed cd %s", FINIT_RCSD);
(void)chdir("enabled"); /* System *may* have enabled/ dir. */
chdir("enabled"); /* System *may* have enabled/ dir. */
if (check && stat(arg, &st))
errx(1, "%s not (an) enabled (service).", arg);
@@ -272,20 +250,6 @@ 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
@@ -394,8 +358,7 @@ int serv_delete(char *arg)
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
do_disable(arg, 0);
if (remove(fn))
err(1, "Failed removing %s", fn);
remove(fn);
}
return 0;
-1
View File
@@ -28,7 +28,6 @@ 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);
+47 -92
View File
@@ -49,6 +49,8 @@
#include "utmp-api.h"
#include "schedule.h"
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static struct wq work = {
.cb = service_worker,
};
@@ -260,6 +262,37 @@ 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
@@ -276,7 +309,7 @@ static void source_env(svc_t *svc)
if (!fn)
return;
/* Warning in service_start() after svc_checkenv() */
/* Warning in service_start() after check_env() */
fp = fopen(fn, "r");
if (!fp)
return;
@@ -351,30 +384,6 @@ 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
@@ -386,7 +395,6 @@ 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;
@@ -399,15 +407,14 @@ static int service_start(svc_t *svc)
/* Don't try and start service if it doesn't exist. */
if (!whichp(svc->cmd)) {
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
print(1, "%s: does not exist", svc->cmd);
svc_missing(svc);
return 1;
}
/* Unlike systemd we do not allow starting service if env is missing, unless - */
if (!svc_checkenv(svc)) {
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
svc_missing(svc);
if (!check_env(svc)) {
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
return 1;
}
@@ -433,6 +440,8 @@ 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;
@@ -455,18 +464,17 @@ static int service_start(svc_t *svc)
/* Set desired user+group */
if (gid >= 0)
(void)setgid(gid);
setgid(gid);
if (uid >= 0) {
(void)setuid(uid);
setuid(uid);
/* Set default path for regular users */
if (uid > 0)
setenv("PATH", _PATH_DEFPATH, 1);
if (home) {
setenv("HOME", home, 1);
if (chdir(home))
(void)chdir("/");
chdir(home);
}
}
@@ -570,8 +578,6 @@ 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;
@@ -641,12 +647,11 @@ static void service_cleanup(svc_t *svc)
char *fn;
fn = pid_file(svc);
if (fn && remove(fn) && errno != ENOENT)
if (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;
}
@@ -1121,9 +1126,6 @@ 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 */
@@ -1140,9 +1142,6 @@ 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);
@@ -1163,8 +1162,6 @@ 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));
@@ -1229,7 +1226,6 @@ 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
@@ -1251,38 +1247,6 @@ 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;
@@ -1297,7 +1261,7 @@ static void service_retry(svc_t *svc)
return;
}
if (*restart_cnt >= SVC_RESPAWN_MAX) {
if (*restart_cnt >= RESPAWN_MAX) {
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
svc_ident(svc, NULL, 0));
svc_crashing(svc);
@@ -1310,12 +1274,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->oldpid, *restart_cnt, SVC_RESPAWN_MAX);
svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
svc_unblock(svc);
service_step(svc);
/* Wait 2s for the first 5 respawns, then back off to 5s */
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000;
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
service_timeout_after(svc, timeout, service_retry);
}
@@ -1369,15 +1333,10 @@ 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);
@@ -1406,10 +1365,6 @@ restart:
err = service_start(svc);
if (err) {
if (svc_is_missing(svc)) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
(*restart_cnt)++;
break;
}
-1
View File
@@ -33,7 +33,6 @@ 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);
+9 -9
View File
@@ -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] == '@')
_d("Skipping %s ...", &file[1]);
else
else if (file[0] != '@')
kill(pid, signo);
else
_d("Skipping %s ...", &file[1]);
}
fclose(fp);
}
@@ -214,11 +214,11 @@ void do_shutdown(shutop_t op)
utmp_set_halt();
/*
* Tell remaining non-monitored processes to exit, give them
* time to exit gracefully, 2 sec was customary, we go for 1.
* Tell all remaining non-monitored processes to exit, give them
* some time to exit gracefully, 2 sec is customary.
*/
do_kill(SIGTERM);
do_sleep(1);
do_sleep(2);
do_kill(SIGKILL);
/* Exit plugins and API gracefully */
@@ -274,15 +274,15 @@ void do_shutdown(shutop_t op)
do_sleep(1);
}
_d("Rebooting ...");
_e("Rebooting ...");
reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) {
_d("Powering down ...");
_e("Powering down ...");
reboot(RB_POWER_OFF);
}
/* Also fallback if any of the other two fails */
_d("Halting ...");
_e("Halting ...");
reboot(RB_HALT_SYSTEM);
}
+3 -5
View File
@@ -156,11 +156,9 @@ restart:
/* Disable login in single-user mode as well as shutdown/reboot */
nologin();
if (runlevel != 0 && runlevel != 6) {
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
conf_reload();
}
/* 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();
+1 -16
View File
@@ -415,20 +415,8 @@ svc_t *svc_find_by_pidfile(char *fn)
pid_t pid;
pid = pid_file_read(fn);
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;
}
if (pid == -1)
return NULL;
}
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid != pid)
@@ -544,9 +532,6 @@ 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;
}
+7 -66
View File
@@ -77,9 +77,6 @@ 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.
@@ -89,8 +86,9 @@ typedef enum {
typedef struct svc {
TAILQ_ENTRY(svc) link;
/* Origin of service */
char file[MAX_ARG_LEN];
/* Instance specifics */
int job; /* JOB: */
char id[MAX_ID_LEN]; /* :ID */
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
@@ -98,7 +96,7 @@ typedef struct svc {
/* Service details */
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
int killdelay; /* Delay in msec before sending SIGKILL */
pid_t oldpid, pid;
pid_t 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 */
@@ -112,11 +110,7 @@ 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 */
@@ -197,14 +191,12 @@ 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; }
@@ -213,26 +205,6 @@ 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)
@@ -352,37 +324,6 @@ 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_ */
/**
+1
View File
@@ -30,6 +30,7 @@
#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;
+15 -62
View File
@@ -28,15 +28,9 @@
#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"
@@ -208,66 +202,25 @@ char *sanitize(char *arg, size_t len)
}
/*
* Called by initctl, and by finit at boot and shutdown, to
* (re)initialize the screen size for print() et al.
* Called at boot, and shutdown, to (re)initialize the
* screen size for print() et al.
*/
int screen_init(void)
void screen_init(void)
{
#ifdef HAVE_TERMIOS_H
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
struct winsize ws = { 0 };
struct termios tc, saved;
if (!isatty(STDOUT_FILENO))
return;
/*
* 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);
initscr(&screen_rows, &screen_cols);
}
/* 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;
/*
* Called when debug mode is enabled to revert back
* to old-school safe defaults.
*/
void screen_exit(void)
{
screen_rows = 24;
screen_cols = 80;
}
/**
+3 -1
View File
@@ -55,7 +55,9 @@ char *uptime (long secs, char *buf, size_t len);
char *sanitize (char *arg, size_t len);
int screen_init (void);
void screen_init (void);
void screen_exit (void);
int screen_width (void);
static inline char *strterm(char *str, size_t len)
{
+61 -5
View File
@@ -35,9 +35,6 @@
#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;
@@ -49,6 +46,65 @@ 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
*
@@ -138,13 +194,13 @@ static int set_getty(int type, char *tty, char *id, char *user)
return 0;
if (!tty)
line = NULL;
line = "";
else if (!strncmp(tty, "/dev/", 5))
line = tty + 5;
else
line = tty;
if (!id && line) {
if (!id) {
id = line;
if (!strncmp(id, "pts/", 4))
id += 4;
+1 -1
View File
@@ -44,6 +44,6 @@ fi
mkdir -p "$FINIT_RCSD"
tty=/dev/$(cat /sys/class/tty/console/active)
mkfifo "$tty"
ln -fs /shared/pts "$tty"
exec "$@"
-4
View File
@@ -138,7 +138,3 @@ 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
+2
View File
@@ -38,6 +38,7 @@ 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 \
@@ -57,6 +58,7 @@ dirs = $(DEST)/bin \
$(DEST)/etc \
$(DEST)/proc \
$(DEST)/sbin \
$(DEST)/shared \
$(DEST)/sys \
$(DEST)/test_assets \
$(DEST)/tmp
+4 -1
View File
@@ -45,4 +45,7 @@ exec "$unshare" \
--mount \
--mount-proc \
--uts --ipc --net \
"$chroot" "$TENV_ROOT" /bin/chrootsetup.sh "$@"
sh -c "
ln -fs /proc/1/fd/1 $TENV_ROOT/shared/pts;
exec $chroot $TENV_ROOT /bin/chrootsetup.sh $*
"