mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d00a53dfbf | ||
|
|
99a8cf3eb7 | ||
|
|
da95c7d80e | ||
|
|
a9f7f692a9 | ||
|
|
ce55f88a00 | ||
|
|
74a26f559d | ||
|
|
08144df5ea | ||
|
|
230c64927b | ||
|
|
d1e56f300d | ||
|
|
9842786453 | ||
|
|
6d0c679e05 | ||
|
|
5428dea5bf | ||
|
|
4e84ecae89 | ||
|
|
959c9fe5aa | ||
|
|
03d8ef9c8f | ||
|
|
084b4ce8d3 | ||
|
|
31cae6a56d | ||
|
|
1fd45c6f2e | ||
|
|
46995fe446 | ||
|
|
600b2874e6 | ||
|
|
b080b970b7 | ||
|
|
6106574e13 | ||
|
|
2385a1209b | ||
|
|
e8b942cf95 | ||
|
|
ac17d7f5a7 | ||
|
|
1edc4356a6 | ||
|
|
9f06a2db99 | ||
|
|
cfb3a9f2b9 | ||
|
|
8aba63a9e3 | ||
|
|
3d98ad293b | ||
|
|
156e86f395 | ||
|
|
d859ecd21d | ||
|
|
cbec78dbbc | ||
|
|
572291a1d9 | ||
|
|
280608f30e | ||
|
|
5c4146cfe4 | ||
|
|
30e3f5a33c | ||
|
|
2c12379a30 | ||
|
|
5f026c8149 | ||
|
|
90005235e8 | ||
|
|
6b688c4211 | ||
|
|
bdf5f8fc90 | ||
|
|
79f7a8ccfb | ||
|
|
3b19db04e3 | ||
|
|
93402b81d7 | ||
|
|
6dec4c9ca0 | ||
|
|
548acf27cf | ||
|
|
cd0bc57ca8 | ||
|
|
11a3318c02 | ||
|
|
2711b27971 | ||
|
|
05ac9c18c9 | ||
|
|
1a7092f545 | ||
|
|
8000d361b3 | ||
|
|
e3c8febe3b | ||
|
|
32ca117949 | ||
|
|
b966d031c8 | ||
|
|
905c2678a3 | ||
|
|
7d429bdbb1 |
+4
-1
@@ -4,7 +4,7 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[4.0][UNRELEASED] - 2021-02-21
|
||||
[4.0][UNRELEASED] - 2021-03-xx
|
||||
------------------------------
|
||||
|
||||
This release became v4.0, and not v3.2, because of incompatible changes
|
||||
@@ -18,6 +18,9 @@ make sure to read the whole changelog when upgrading.
|
||||
with a fallback to sending signals as per traditional SysV init. The
|
||||
`-f` (force) flag remains, where `reboot(2)` is called directly
|
||||
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
|
||||
* The `inictl cond set|clear COND` have changed completely. Constrained
|
||||
to a flat `<usr/...>` namespace and automatically activated by a new
|
||||
`usr.so` plugin that checks services for usr condition changes
|
||||
* Removed built-in inetd super server. If you need this functionality,
|
||||
use an external inetd, like xinetd, instead. A pull request for a
|
||||
stand-alone inetd, like watchdogd and getty, is most welcome!
|
||||
|
||||
@@ -356,6 +356,7 @@ 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
|
||||
@@ -375,6 +376,8 @@ Commands:
|
||||
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
||||
reload Reload *.conf in /etc/finit.d (activate changes)
|
||||
|
||||
cond set <COND> Set (assert) user-defined condition +usr/COND
|
||||
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
|
||||
cond show Show condition status
|
||||
cond dump Dump all conditions and their status
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
AC_INIT(Finit, 4.0-rc2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 4.0-rc3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
+27
-1
@@ -45,7 +45,31 @@ being created.
|
||||
Triggering
|
||||
----------
|
||||
|
||||
Conditions can only be triggered by built-in plugins.
|
||||
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
|
||||
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
|
||||
also supports user-defined conditions, sometimes referred to as static
|
||||
or one-shot conditions. They live in the `<usr/...` namespace and are
|
||||
constrained to a flat hierarchy without any sub-directories, unlike the
|
||||
pidfile plugin, for instance.
|
||||
|
||||
User-defined conditions are controlled using the `initctl cond set` and
|
||||
`initctl cond clear` commands:
|
||||
|
||||
initctl cond set foo
|
||||
initctl cond clear foo
|
||||
|
||||
The purpose of user-defined conditions is to be able to start or stop
|
||||
services, or run/task jobs, on external site-dependent stimuli.
|
||||
Example:
|
||||
|
||||
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
|
||||
|
||||
For convenience, prefixing with `usr/` is allowed, but any other slashes
|
||||
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
|
||||
the same as above, can also be achieved like this:
|
||||
|
||||
initctl cond set usr/foo
|
||||
initctl cond clear usr/foo
|
||||
|
||||
Conditions retain their current state until the next reconfiguration or
|
||||
runlevel change. At that point all set conditions transition into the
|
||||
@@ -57,6 +81,8 @@ unchanged) state of it.
|
||||
> **Note:** For `pid/` conditions it is expected that services "touch"
|
||||
> or recreate their PID file on `SIGHUP`.
|
||||
|
||||
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||
|
||||
|
||||
Built-in Conditions
|
||||
-------------------
|
||||
|
||||
@@ -483,9 +483,6 @@ Log rotation is controlled using the global `log` setting.
|
||||
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
|
||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
||||
detailed description, see the [Conditions](conditions.md) document.
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
noinst_LTLIBRARIES = libplug.la
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c
|
||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
else
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
|
||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
|
||||
+20
-30
@@ -99,13 +99,14 @@ static void bootclean(void)
|
||||
*/
|
||||
static void setup(void *arg)
|
||||
{
|
||||
int uid, gid;
|
||||
mode_t prev;
|
||||
|
||||
/* Cleanup stale files, if any still linger on. */
|
||||
bootclean();
|
||||
|
||||
prev = umask(0);
|
||||
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
umask(0);
|
||||
makedir("/var/cache", 0755);
|
||||
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
||||
makedir("/var/games", 0755);
|
||||
@@ -133,19 +134,21 @@ static void setup(void *arg)
|
||||
makedir("/var/tmp", 0755);
|
||||
makedir("/var/empty", 0755);
|
||||
|
||||
/*
|
||||
* If /etc/group or "utmp" group is missing,
|
||||
* default to "root"/"wheel" group
|
||||
*/
|
||||
_d("Setting up necessary UTMP files ...");
|
||||
gid = getgroup("utmp");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
/*
|
||||
* UTMP actually needs multiple db files
|
||||
*/
|
||||
if (has_utmp()) {
|
||||
int gid;
|
||||
|
||||
/*
|
||||
* If /etc/group or "utmp" group is missing, default to
|
||||
* "root", or "wheel", group.
|
||||
*/
|
||||
_d("Setting up necessary UTMP files ...");
|
||||
gid = getgroup("utmp");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
|
||||
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
|
||||
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
||||
@@ -165,32 +168,19 @@ static void setup(void *arg)
|
||||
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
|
||||
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
|
||||
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
||||
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/run/frr", 0755); /* Needed by FRR */
|
||||
makedir("/var/log/frr", 0755); /* Needed by FRR */
|
||||
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/run/frr", 0755, "frr", "frr");
|
||||
mksubsys("/var/log/frr", 0755, "frr", "frr");
|
||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||
|
||||
gid = getgroup("tty");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
||||
chown("/dev/xconsole", 0, gid);
|
||||
|
||||
if (!fexist("/etc/mtab"))
|
||||
symlink("../proc/self/mounts", "/etc/mtab");
|
||||
|
||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
||||
uid = getuser("uuidd", NULL);
|
||||
if (uid >= 0) {
|
||||
gid = getgroup("uuidd");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
makedir("/var/run/uuidd", 0755);
|
||||
chown("/var/run/uuidd", uid, gid);
|
||||
}
|
||||
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+12
-10
@@ -32,13 +32,16 @@
|
||||
#include "conf.h"
|
||||
|
||||
#define DAEMON "dbus-daemon"
|
||||
#define ARGS "--nofork --system"
|
||||
#define ARGS "--nofork --system --syslog-only"
|
||||
#define DESC "D-Bus message bus daemon"
|
||||
|
||||
#define DAEMONUSER "messagebus"
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
char *cmd;
|
||||
char line[256];
|
||||
mode_t prev;
|
||||
char *cmd;
|
||||
|
||||
cmd = which(DAEMON);
|
||||
if (!cmd) {
|
||||
@@ -46,24 +49,23 @@ static void setup(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
umask(0);
|
||||
prev =umask(0);
|
||||
|
||||
makedir("/var/run/dbus", 0755);
|
||||
makedir("/var/lock/subsys", 0755);
|
||||
makedir("/var/lock/subsys/messagebus", 0755);
|
||||
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
|
||||
if (whichp("dbus-uuidgen"))
|
||||
run("dbus-uuidgen --ensure");
|
||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||
|
||||
/* Clean up from any previous pre-bootstrap run */
|
||||
erase("/var/run/dbus/pid");
|
||||
remove("/var/run/dbus/pid");
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
|
||||
snprintf(line, sizeof(line), "[S12345] @%s:%s %s %s -- %s",
|
||||
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+14
-15
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
|
||||
}
|
||||
}
|
||||
|
||||
return iwatch_add(iw, path, IN_ONLYDIR);
|
||||
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
|
||||
}
|
||||
|
||||
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
svc_t *svc;
|
||||
|
||||
paste(fn, sizeof(fn), dir, name);
|
||||
_d("path: %s, mask: %08x", fn, mask);
|
||||
|
||||
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
||||
return;
|
||||
|
||||
_d("path: %s, mask: %08x", fn, mask);
|
||||
|
||||
svc = svc_find_by_pidfile(fn);
|
||||
if (!svc) {
|
||||
_d("No matching svc for %s", fn);
|
||||
@@ -82,15 +82,20 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
||||
|
||||
mkcond(svc, cond, sizeof(cond));
|
||||
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||
svc_started(svc);
|
||||
if (!svc_has_pidfile(svc))
|
||||
pid_file_set(svc, fn, 1);
|
||||
|
||||
if (svc_is_forking(svc)) {
|
||||
pid_t pid;
|
||||
|
||||
pid = pid_file_read(pid_file(svc));
|
||||
_d("Forking service %s changed PID from %d to %d",
|
||||
svc->cmd, svc->pid, pid);
|
||||
svc->pid = pid;
|
||||
if (pid != svc->pid) {
|
||||
_d("Forking service %s changed PID from %d to %d",
|
||||
svc->cmd, svc->pid, pid);
|
||||
svc->pid = pid;
|
||||
}
|
||||
}
|
||||
|
||||
cond_set(cond);
|
||||
@@ -174,8 +179,6 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
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)
|
||||
@@ -186,12 +189,7 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & IN_DELETE) {
|
||||
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||
pidfile_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
@@ -242,6 +240,7 @@ static void pidfile_init(void *arg)
|
||||
*/
|
||||
path = realpath(_PATH_VARRUN, NULL);
|
||||
if (!path) {
|
||||
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
|
||||
path = realpath("/run", NULL);
|
||||
if (!path) {
|
||||
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
|
||||
|
||||
+9
-4
@@ -37,10 +37,11 @@ static void setup(void *arg)
|
||||
#ifdef RANDOMSEED
|
||||
if (!fexist(RANDOMSEED)) {
|
||||
int ret = 1;
|
||||
mode_t prev;
|
||||
FILE *fp;
|
||||
|
||||
print_desc("Bootstrapping random seed", NULL);
|
||||
umask(077);
|
||||
prev = umask(077);
|
||||
fp = fopen(RANDOMSEED, "w");
|
||||
if (fp) {
|
||||
int iter = 128;
|
||||
@@ -57,7 +58,7 @@ static void setup(void *arg)
|
||||
ret = fclose(fp);
|
||||
}
|
||||
print_result(ret);
|
||||
umask(0);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
print_desc("Initializing random number generator", NULL);
|
||||
@@ -68,10 +69,14 @@ static void setup(void *arg)
|
||||
static void save(void *arg)
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
umask(077);
|
||||
mode_t prev;
|
||||
|
||||
prev = umask(077);
|
||||
|
||||
print_desc("Saving random seed", NULL);
|
||||
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
||||
umask(0);
|
||||
|
||||
umask(prev);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/* User-defined condtion event monitor for the Finit condition engine
|
||||
*
|
||||
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "plugin.h"
|
||||
#include "iwatch.h"
|
||||
|
||||
static struct iwatch iw_usr;
|
||||
|
||||
|
||||
static void usr_cond(char *name, uint32_t mask)
|
||||
{
|
||||
char cond[MAX_COND_LEN] = COND_USR;
|
||||
|
||||
strlcat(cond, name, sizeof(cond));
|
||||
cond_update(cond);
|
||||
}
|
||||
|
||||
static void usr_callback(void *arg, int fd, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||
if (off + sizeof(*ev) > (size_t)sz)
|
||||
break;
|
||||
|
||||
ev = (struct inotify_event *)&ev_buf[off];
|
||||
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||
break;
|
||||
|
||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
if (ev->mask & IN_ISDIR)
|
||||
continue; /* unsupported */
|
||||
|
||||
if (ev->mask & IN_DELETE)
|
||||
sync(); /* dont ask */
|
||||
|
||||
usr_cond(ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void usr_init(void *arg)
|
||||
{
|
||||
char usrdir[MAX_ARG_LEN];
|
||||
char *path;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
|
||||
return;
|
||||
}
|
||||
|
||||
path = realpath(_PATH_CONDUSR, NULL);
|
||||
if (!path) {
|
||||
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
|
||||
iwatch_exit(&iw_usr);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = iwatch_init(&iw_usr);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
plugin.io.fd = fd;
|
||||
plugin.io.cb = usr_callback;
|
||||
plugin.io.flags = PLUGIN_IO_READ;
|
||||
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
iwatch_exit(&iw_usr);
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -32,13 +32,14 @@
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
const char *username = "nobody";
|
||||
char line[LINE_SIZE];
|
||||
mode_t prev;
|
||||
#ifdef PAM_CONSOLE
|
||||
int fd;
|
||||
#endif
|
||||
char line[LINE_SIZE];
|
||||
const char *username = "nobody";
|
||||
|
||||
umask(0);
|
||||
prev = umask(0);
|
||||
|
||||
makedir("/var/run/console", 01777);
|
||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
||||
@@ -58,7 +59,7 @@ static void setup(void *arg)
|
||||
if (whichp("restorecon"))
|
||||
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+4
-4
@@ -59,14 +59,14 @@ else
|
||||
finit_LDADD += -ldl
|
||||
endif
|
||||
|
||||
initctl_SOURCES = initctl.c client.c client.h \
|
||||
reboot.c serv.c serv.h svc.h \
|
||||
cond.c cond.h util.c util.h
|
||||
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||
client.c client.h cond.c cond.h reboot.c \
|
||||
serv.c serv.h svc.h util.c util.h
|
||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
initctl_CFLAGS += $(lite_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS)
|
||||
|
||||
INIT_LNKS = init
|
||||
INIT_LNKS = init telinit
|
||||
REBOOT_LNKS = reboot shutdown halt poweroff suspend
|
||||
|
||||
if WATCHDOGD
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
rmdir "$DIR"
|
||||
|
||||
exit 0
|
||||
|
||||
+83
-22
@@ -26,21 +26,65 @@
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysinfo.h> /* get_nprocs_conf() */
|
||||
|
||||
#include "finit.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static int cg_init = 0;
|
||||
static char controllers[256] = "none";
|
||||
|
||||
static void group_init(char *path, char *nproc, int share)
|
||||
{
|
||||
char file[strlen(path) + 64];
|
||||
|
||||
paste(file, sizeof(file), path, "cgroup.clone_children");
|
||||
echo(file, 0, "1");
|
||||
|
||||
paste(file, sizeof(file), path, "cpuset.cpus");
|
||||
echo(file, 0, nproc);
|
||||
|
||||
paste(file, sizeof(file), path, "cpuset.mems");
|
||||
echo(file, 0, "0");
|
||||
|
||||
paste(file, sizeof(file), path, "cpu.shares");
|
||||
echo(file, 0, "%d", share);
|
||||
|
||||
paste(file, sizeof(file), path, "memory.use_hierarchy");
|
||||
echo(file, 0, "1");
|
||||
}
|
||||
|
||||
static void append_ctrl(char *ctrl)
|
||||
{
|
||||
char *wanted[] = {
|
||||
"cpu",
|
||||
"cpuacct",
|
||||
"cpuset",
|
||||
"memory"
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(wanted); i++) {
|
||||
if (strcmp(wanted[i], ctrl))
|
||||
continue;
|
||||
|
||||
if (strcmp(controllers, "none"))
|
||||
strlcat(controllers, ",", sizeof(controllers));
|
||||
else
|
||||
strlcpy(controllers, "", sizeof(controllers));
|
||||
|
||||
strlcat(controllers, ctrl, sizeof(controllers));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by Finit at early boot to mount initial cgroups
|
||||
*/
|
||||
void cgroup_init(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[80];
|
||||
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||
char buf[80];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/cgroups", "r");
|
||||
if (!fp) {
|
||||
@@ -55,6 +99,8 @@ void cgroup_init(void)
|
||||
|
||||
/* Skip first line, header */
|
||||
(void)fgets(buf, sizeof(buf), fp);
|
||||
|
||||
/* Create and mount traditional cgroups v1 hier */
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *cgroup;
|
||||
char rc[80];
|
||||
@@ -64,16 +110,28 @@ void cgroup_init(void)
|
||||
continue;
|
||||
|
||||
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
||||
#if 0
|
||||
if (mkdir(rc, 0755) && EEXIST != errno)
|
||||
continue;
|
||||
|
||||
if (mount("cgroup", rc, "cgroup", opts, cgroup))
|
||||
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
||||
#else
|
||||
symlink("finit", rc);
|
||||
append_ctrl(cgroup);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Default cgroups for process monitoring */
|
||||
/* Finit default cgroups for process monitoring */
|
||||
if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mount("none", FINIT_CGPATH, "cgroup", opts, "none,name=finit")) {
|
||||
|
||||
#if 0
|
||||
strlcpy(controllers, "none,name=finit", sizeof(controllers));
|
||||
#else
|
||||
strlcat(controllers, ",name=finit", sizeof(controllers));
|
||||
#endif
|
||||
if (mount("none", FINIT_CGPATH, "cgroup", opts, controllers)) {
|
||||
_pe("Failed mounting Finit cgroup hierarchy");
|
||||
goto fail;
|
||||
}
|
||||
@@ -90,24 +148,33 @@ void cgroup_init(void)
|
||||
if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Set up basic limits for our groups. Finit optimized defaults
|
||||
* for embedded and server systems include limiting the groups
|
||||
* init and user to a single CPU with 10% share, the system
|
||||
* group gets the rest. Override this from finit.conf
|
||||
*/
|
||||
#if 0
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "0-%d", get_nprocs_conf() - 1);
|
||||
|
||||
group_init(FINIT_CGPATH "/init", "0", 50);
|
||||
group_init(FINIT_CGPATH "/user", "0", 75);
|
||||
group_init(FINIT_CGPATH "/system", buf, 900);
|
||||
#endif
|
||||
|
||||
/* Move ourselves to init */
|
||||
echo(FINIT_CGPATH "/init/cgroup.procs", 0, "1");
|
||||
|
||||
/* We have signal, main screen turn on! */
|
||||
cg_init = 1;
|
||||
fail:
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int move_pid(char *group, char *name, char *id, int pid)
|
||||
static int move_pid(char *group, char *name, int pid)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||
if (id && strlen(id)) {
|
||||
strlcat(path, ":", sizeof(path));
|
||||
strlcat(path, id, sizeof(path));
|
||||
}
|
||||
if (mkdir(path, 0755) && errno != EEXIST)
|
||||
return 1;
|
||||
|
||||
@@ -116,25 +183,19 @@ static int move_pid(char *group, char *name, char *id, int pid)
|
||||
return echo(path, 0, "%d", pid);
|
||||
}
|
||||
|
||||
int cgroup_user(char *name)
|
||||
int cgroup_user(char *name, int pid)
|
||||
{
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
return move_pid("finit/user", name, "0", getpid());
|
||||
return move_pid("finit/user", name, pid);
|
||||
}
|
||||
|
||||
int cgroup_service(char *nm, char *id, int pid)
|
||||
int cgroup_service(char *name, int pid)
|
||||
{
|
||||
if (!cg_init)
|
||||
return 0;
|
||||
|
||||
if (pid <= 0) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return move_pid("finit/system", nm, id, pid);
|
||||
return move_pid("finit/system", name, pid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@
|
||||
|
||||
void cgroup_init (void);
|
||||
|
||||
int cgroup_user (char *name);
|
||||
int cgroup_service (char *cmd, char *id, int pid);
|
||||
int cgroup_user (char *name, int pid);
|
||||
int cgroup_service (char *name, int pid);
|
||||
|
||||
#endif /* FINIT_CGROUP_H_ */
|
||||
|
||||
+483
@@ -0,0 +1,483 @@
|
||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <search.h>
|
||||
#include <inttypes.h>
|
||||
#include <lite/lite.h>
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
|
||||
#include "initctl.h"
|
||||
|
||||
#define CDIM plain ? "" : "\e[2m"
|
||||
#define CRST plain ? "" : "\e[0m"
|
||||
|
||||
#define NONE " "
|
||||
#define PIPE plain ? "| " : "│ "
|
||||
#define FORK plain ? "|-" : "├─"
|
||||
#define END plain ? "`-" : "└─"
|
||||
|
||||
uint64_t total_ram; /* From sysinfo() */
|
||||
|
||||
struct cg {
|
||||
struct cg *cg_next;
|
||||
|
||||
char *cg_path; /* path in /sys/fs/cgroup */
|
||||
uint64_t cg_prev; /* cpuacct.usage */
|
||||
uint64_t cg_rss; /* memory.stat */
|
||||
uint64_t cg_vmlib; /* memory.stat */
|
||||
uint64_t cg_vmsize; /* memory.usage_in_bytes */
|
||||
float cg_mem; /* cg_rss / total_ram * 100 */
|
||||
float cg_load; /* curr - prev / 10000000.0 */
|
||||
};
|
||||
|
||||
struct cg dummy; /* empty result "NULL" */
|
||||
struct cg *list;
|
||||
|
||||
|
||||
static char *pid_cmdline(int pid, char *buf, size_t len)
|
||||
{
|
||||
size_t i, sz;
|
||||
char *ptr;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/cmdline", pid);
|
||||
if (!fp) {
|
||||
buf[0] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
|
||||
fclose(fp);
|
||||
if (!sz)
|
||||
return NULL; /* kernel thread */
|
||||
|
||||
buf[sz] = 0;
|
||||
|
||||
ptr = strchr(buf, 0);
|
||||
if (ptr && ptr != buf) {
|
||||
ptr++;
|
||||
sz -= ptr - buf;
|
||||
memmove(buf, ptr, sz + 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
if (buf[i] == 0)
|
||||
buf[i] = ' ';
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *pid_comm(int pid, char *buf, size_t len)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/comm", pid);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
fgets(buf, len, fp);
|
||||
fclose(fp);
|
||||
|
||||
return chomp(buf);
|
||||
}
|
||||
|
||||
static uint64_t cgroup_uint64(char *path, char *file)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
char buf[42];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "%s/%s", path, file);
|
||||
if (!fp)
|
||||
return val;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
val = strtoull(buf, NULL, 10);
|
||||
fclose(fp);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cgroup_shares(char *path)
|
||||
{
|
||||
return (int)cgroup_uint64(path, "cpu.shares");
|
||||
}
|
||||
|
||||
static uint64_t cgroup_memuse(struct cg *cg)
|
||||
{
|
||||
char buf[42];
|
||||
FILE *fp;
|
||||
|
||||
|
||||
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (!strncmp(buf, "total_rss", 9)) {
|
||||
cg->cg_rss = strtoull(&buf[10], NULL, 10);
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(buf, "total_mapped_file", 17)) {
|
||||
cg->cg_vmlib = strtoull(&buf[18], NULL, 10);
|
||||
break; /* done */
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
cg->cg_mem = (float)(cg->cg_rss * 100 / total_ram);
|
||||
|
||||
/* "fuzz value for efficient access", sort of like total_cache + total_rss */
|
||||
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.usage_in_bytes");
|
||||
}
|
||||
|
||||
static char *memsz(uint64_t sz, char *buf, size_t len)
|
||||
{
|
||||
int gb, mb, kb, b;
|
||||
|
||||
gb = sz / (1024 * 1024 * 1024);
|
||||
sz = sz % (1024 * 1024 * 1024);
|
||||
mb = sz / (1024 * 1024);
|
||||
sz = sz % (1024 * 1024);
|
||||
kb = sz / (1024);
|
||||
b = sz % (1024);
|
||||
|
||||
if (gb)
|
||||
snprintf(buf, len, "%d.%dG", gb, mb / 102);
|
||||
else if (mb)
|
||||
snprintf(buf, len, "%d.%dM", mb, kb / 102);
|
||||
else
|
||||
snprintf(buf, len, "%d.%dk", kb, b / 102);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static float cgroup_cpuload(struct cg *cg)
|
||||
{
|
||||
char fn[256];
|
||||
char buf[64];
|
||||
FILE *fp;
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", cg->cg_path);
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
err(1, "Cannot open %s", fn);
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
uint64_t curr;
|
||||
|
||||
curr = strtoull(buf, NULL, 10);
|
||||
if (cg->cg_prev != 0) {
|
||||
uint64_t diff = curr - cg->cg_prev;
|
||||
|
||||
/* this expects 1 sec poll interval */
|
||||
cg->cg_load = (float)(diff / 1000000);
|
||||
cg->cg_load /= 10.0;
|
||||
}
|
||||
cg->cg_prev = curr;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return cg->cg_load;
|
||||
}
|
||||
|
||||
static struct cg *append(char *path)
|
||||
{
|
||||
struct cg *cg;
|
||||
char fn[256];
|
||||
ENTRY item;
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", path);
|
||||
if (access(fn, F_OK)) {
|
||||
warn("not a cgroup path with cpuacct controller, %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cg = calloc(1, sizeof(struct cg));
|
||||
if (!cg)
|
||||
err(1, "failed allocating struct cg");
|
||||
|
||||
cg->cg_path = strdup(path);
|
||||
if (list)
|
||||
cg->cg_next = list;
|
||||
list = cg;
|
||||
|
||||
item.key = cg->cg_path;
|
||||
item.data = cg;
|
||||
if (!hsearch(item, ENTER))
|
||||
err(1, "failed adding to hash table");
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
static struct cg *find(char *path)
|
||||
{
|
||||
ENTRY *ep, item = { path, NULL };
|
||||
|
||||
ep = hsearch(item, FIND);
|
||||
if (ep)
|
||||
return ep->data;
|
||||
|
||||
return append(path);
|
||||
}
|
||||
|
||||
static struct cg *cg_update(char *path)
|
||||
{
|
||||
struct cg *cg;
|
||||
|
||||
cg = find(path);
|
||||
if (!cg)
|
||||
return &dummy;
|
||||
|
||||
cgroup_cpuload(cg);
|
||||
cgroup_memuse(cg);
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
static int cgroup_filter(const struct dirent *entry)
|
||||
{
|
||||
/* Skip current dir ".", and prev dir "..", from list of files */
|
||||
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
|
||||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
|
||||
return 0;
|
||||
|
||||
if (entry->d_name[0] == '.')
|
||||
return 0;
|
||||
|
||||
if (entry->d_type != DT_DIR)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cgroup_tree(char *path, char *pfx, int top)
|
||||
{
|
||||
struct dirent **namelist = NULL;
|
||||
char s[32], r[32], l[32];
|
||||
char row[screen_cols + 9]; /* + control codes */
|
||||
size_t rlen = sizeof(row) - 1;
|
||||
size_t rplen = rlen - 9;
|
||||
struct stat st;
|
||||
struct cg *cg;
|
||||
char buf[512];
|
||||
int rc = 0;
|
||||
FILE *fp;
|
||||
int i, n;
|
||||
int num;
|
||||
|
||||
if (top >= screen_rows)
|
||||
return 0;
|
||||
|
||||
if (-1 == lstat(path, &st))
|
||||
return 1;
|
||||
|
||||
if ((st.st_mode & S_IFMT) != S_IFDIR) {
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp = fopenf("r", "%s/cgroup.procs", path);
|
||||
if (!fp)
|
||||
return -1;
|
||||
num = 0;
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
num++;
|
||||
|
||||
if (!pfx) {
|
||||
pfx = "";
|
||||
if (top) {
|
||||
cg = cg_update(path);
|
||||
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f [%s]",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
memsz(cg->cg_rss, r, sizeof(r)),
|
||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||
cg->cg_mem, cg->cg_load, path);
|
||||
} else
|
||||
strlcpy(row, path, rplen);
|
||||
|
||||
puts(row);
|
||||
}
|
||||
|
||||
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
||||
if (n > 0) {
|
||||
for (i = 0; i < n; i++) {
|
||||
char *nm = namelist[i]->d_name;
|
||||
char prefix[80], tmp[42];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
||||
if (top) {
|
||||
cg = cg_update(buf);
|
||||
snprintf(row, rplen,
|
||||
" %6.6s %6.6s %6.6s %5.1f %5.1f ",
|
||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||
memsz(cg->cg_rss, r, sizeof(r)),
|
||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||
cg->cg_mem, cg->cg_load);
|
||||
} else
|
||||
row[0] = 0;
|
||||
|
||||
strlcat(row, pfx, rplen);
|
||||
if (i + 1 == n) {
|
||||
strlcat(row, END, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s ", pfx);
|
||||
} else {
|
||||
strlcat(row, FORK, rplen);
|
||||
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
|
||||
}
|
||||
strlcat(row, " ", rplen);
|
||||
|
||||
strlcat(row, nm, rplen);
|
||||
strlcat(row, "/ ", rplen);
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "[cpu.shares: %d]", cgroup_shares(buf));
|
||||
strlcat(row, tmp, rplen);
|
||||
puts(row);
|
||||
|
||||
rc += cgroup_tree(buf, prefix, top ? top + i : 0);
|
||||
|
||||
free(namelist[i]);
|
||||
}
|
||||
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
if (num > 0) {
|
||||
rewind(fp);
|
||||
|
||||
i = 0;
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char comm[80] = { 0 };
|
||||
pid_t pid;
|
||||
|
||||
pid = atoi(chomp(buf));
|
||||
if (pid <= 0)
|
||||
continue;
|
||||
|
||||
/* skip kernel threads for now (no cmdline) */
|
||||
pid_comm(pid, comm, sizeof(comm));
|
||||
if (pid_cmdline(pid, buf, sizeof(buf))) {
|
||||
char proc[screen_cols];
|
||||
int len;
|
||||
|
||||
if (top)
|
||||
snprintf(row, rplen, "%37s", " ");
|
||||
else
|
||||
row[0] = 0;
|
||||
|
||||
strlcat(row, pfx, rplen);
|
||||
strlcat(row, ++i == num ? END : FORK, rlen);
|
||||
|
||||
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf);
|
||||
|
||||
if (plain) {
|
||||
strlcat(row, proc, rplen);
|
||||
} else {
|
||||
strlcat(row, CDIM, rlen);
|
||||
strlcat(row, proc, rlen);
|
||||
|
||||
len = strlen(row) + strlen(CRST);
|
||||
if (len > (int)rlen)
|
||||
row[rlen - strlen(CRST)] = 0;
|
||||
|
||||
strlcat(row, CRST, sizeof(row));
|
||||
}
|
||||
|
||||
puts(row);
|
||||
}
|
||||
|
||||
if (top) {
|
||||
top += i;
|
||||
if (top >= screen_rows)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int show_cgroup(char *arg)
|
||||
{
|
||||
char path[512];
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
else if (arg[0] != '/') {
|
||||
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||
arg = path;
|
||||
}
|
||||
|
||||
return cgroup_tree(arg, NULL, 0);
|
||||
}
|
||||
|
||||
int show_cgtop(char *arg)
|
||||
{
|
||||
struct sysinfo si = { 0 };
|
||||
char path[512];
|
||||
int laps = 0;
|
||||
|
||||
if (!arg)
|
||||
arg = FINIT_CGPATH;
|
||||
else if (arg[0] != '/') {
|
||||
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||
arg = path;
|
||||
}
|
||||
|
||||
if (!hcreate(screen_rows + 25))
|
||||
err(1, "failed creating hash table");
|
||||
|
||||
sysinfo(&si);
|
||||
total_ram = si.totalram * si.mem_unit;
|
||||
|
||||
while (1) {
|
||||
fputs("\e[2J\e[1;1H", stdout);
|
||||
if (heading)
|
||||
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
|
||||
cgroup_tree(arg, NULL, 1);
|
||||
|
||||
if (ionce && laps++)
|
||||
break;
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Cgroup utility functions for initctl
|
||||
*
|
||||
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_CGUTIL_H_
|
||||
#define FINIT_CGUTIL_H_
|
||||
|
||||
int show_cgroup (char *arg);
|
||||
int show_cgtop (char *arg);
|
||||
|
||||
#endif /* FINIT_CGUTIL_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+4
-8
@@ -21,6 +21,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
@@ -38,17 +39,12 @@ int client_connect(void)
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
goto error;
|
||||
err(1, "Failed creating UNIX domain socket");
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||
close(sd);
|
||||
goto error;
|
||||
}
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
err(1, "Failed connecting to finit");
|
||||
|
||||
return sd;
|
||||
error:
|
||||
perror("Failed connecting to finit");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int client_disconnect(void)
|
||||
|
||||
+13
-11
@@ -99,13 +99,13 @@ static void cond_bump_reconf(void)
|
||||
unsigned int rgen;
|
||||
|
||||
/*
|
||||
* If %COND_RECONF does not exist, cond_get_gen() returns 0
|
||||
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0
|
||||
* meaning that rgen++ is always what we want.
|
||||
*/
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
rgen++;
|
||||
|
||||
cond_set_gen(COND_RECONF, rgen);
|
||||
cond_set_gen(_PATH_RECONF, rgen);
|
||||
}
|
||||
|
||||
static int cond_checkpath(const char *path)
|
||||
@@ -119,7 +119,7 @@ static int cond_checkpath(const char *path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (makepath(dir) && errno != EEXIST) {
|
||||
if (mkpath(dir, 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 1;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
|
||||
_d("%s", path);
|
||||
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen) {
|
||||
_e("Unable to read configuration generation (%s)", path);
|
||||
return -1;
|
||||
@@ -162,7 +162,8 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
return new != old;
|
||||
}
|
||||
|
||||
static void cond_update(const char *name)
|
||||
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
|
||||
void cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
@@ -201,7 +202,8 @@ void cond_set_oneshot(const char *name)
|
||||
if (cond_checkpath(path))
|
||||
return;
|
||||
|
||||
symlink(COND_RECONF, path);
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||
_pe("Failed creating onshot cond %s", name);
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
@@ -234,13 +236,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
if (tflg != FTW_F)
|
||||
return 0;
|
||||
|
||||
nm = strstr((char *)fpath, COND_DIR);
|
||||
nm = strstr((char *)fpath, COND_BASE);
|
||||
if (!nm) {
|
||||
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
nm += sizeof(COND_DIR);
|
||||
nm += strlen(COND_BASE);
|
||||
_d("Reasserting %s => %s", fpath, nm);
|
||||
cond_set(nm);
|
||||
|
||||
@@ -261,8 +263,8 @@ void cond_init(void)
|
||||
{
|
||||
char path[MAX_ARG_LEN];
|
||||
|
||||
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
||||
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", _PATH_COND);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -45,7 +45,7 @@ const char *cond_path(const char *name)
|
||||
static char path[256];
|
||||
char tmp[MAX_ARG_LEN];
|
||||
|
||||
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
|
||||
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name);
|
||||
|
||||
return pid_runpath(tmp, path, sizeof(path));
|
||||
}
|
||||
@@ -74,14 +74,14 @@ enum cond_state cond_get_path(const char *path)
|
||||
{
|
||||
int cgen, rgen;
|
||||
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
if (!rgen)
|
||||
return COND_OFF;
|
||||
|
||||
cgen = cond_get_gen(path);
|
||||
if (!cgen)
|
||||
return COND_OFF;
|
||||
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen)
|
||||
return COND_OFF;
|
||||
|
||||
return (cgen == rgen) ? COND_ON : COND_FLUX;
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -4,9 +4,12 @@
|
||||
#include <paths.h>
|
||||
#include <svc.h>
|
||||
|
||||
#define COND_DIR "finit/cond"
|
||||
#define COND_PATH _PATH_VARRUN COND_DIR
|
||||
#define COND_RECONF COND_PATH "/reconf"
|
||||
#define COND_BASE "finit/cond"
|
||||
#define COND_USR "usr/"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||
#define _PATH_RECONF _PATH_COND "reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
COND_OFF = 0,
|
||||
@@ -23,6 +26,7 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
void cond_update (const char *name);
|
||||
int cond_set_path (const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
void cond_set_oneshot (const char *name);
|
||||
|
||||
@@ -900,6 +900,9 @@ int conf_init(uev_ctx_t *ctx)
|
||||
rlim2str(i), strerror(errno));
|
||||
}
|
||||
|
||||
/* Initialize global rlimits, e.g. for built-in services */
|
||||
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||
|
||||
/* prepare /etc watcher */
|
||||
fd = iwatch_init(&iw_conf);
|
||||
if (fd < 0)
|
||||
|
||||
+5
-5
@@ -199,7 +199,6 @@ static void fs_mount(void)
|
||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||
|
||||
run("swapon -ea");
|
||||
umask(0022);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -222,6 +221,9 @@ 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;
|
||||
|
||||
@@ -448,7 +450,7 @@ int main(int argc, char *argv[])
|
||||
};
|
||||
struct wq final = {
|
||||
.cb = final_worker,
|
||||
.delay = 1000
|
||||
.delay = 100
|
||||
};
|
||||
uev_ctx_t loop;
|
||||
|
||||
@@ -479,14 +481,13 @@ int main(int argc, char *argv[])
|
||||
ctx = &loop;
|
||||
|
||||
/*
|
||||
* Set PATH, SHELL, PWD, and umask early to something sane
|
||||
* Set PATH, SHELL, and PWD early to something sane
|
||||
*/
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
setenv("SHELL", _PATH_BSHELL, 1);
|
||||
|
||||
if (chdir("/"))
|
||||
_pe("Failed cd /");
|
||||
umask(0);
|
||||
|
||||
/* Allow progress, if enabled */
|
||||
enable_progress(1);
|
||||
@@ -548,7 +549,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
_d("Starting initctl API responder ...");
|
||||
api_init(&loop);
|
||||
umask(022);
|
||||
|
||||
_d("Starting the big state machine ...");
|
||||
schedule_work(&crank);
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
cgroup_user(name);
|
||||
cgroup_user(name, getpid());
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
|
||||
+61
-1
@@ -103,6 +103,9 @@ static void add_console(char *cons)
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
if (!strncmp(cons, "/dev/", 5))
|
||||
cons += 5;
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/%s", cons);
|
||||
|
||||
/* check for and skip duplicates */
|
||||
@@ -131,7 +134,7 @@ static void find_active_consoles(void)
|
||||
if (!fp) {
|
||||
fallback:
|
||||
_d("Using fallback /dev/console");
|
||||
add_console("/dev/console");
|
||||
add_console("console");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -410,6 +413,63 @@ int getgroup(char *group)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* get current user */
|
||||
int getcuser(char *buf, size_t len)
|
||||
{
|
||||
char *ptr = "root";
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
|
||||
if (pw)
|
||||
ptr = pw->pw_name;
|
||||
#endif
|
||||
if (strlcpy(buf, ptr, len) > len)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get current group */
|
||||
int getcgroup(char *buf, size_t len)
|
||||
{
|
||||
char *ptr = "root";
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
struct group *gr = getgrgid(getgid());
|
||||
|
||||
if (gr)
|
||||
ptr = gr->gr_name;
|
||||
#endif
|
||||
if (strlcpy(buf, ptr, len) > len)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
{
|
||||
mode_t omask;
|
||||
int uid, gid;
|
||||
int rc = 0;
|
||||
|
||||
omask = umask(0);
|
||||
|
||||
uid = getuser(user, NULL);
|
||||
if (uid >= 0) {
|
||||
gid = getgroup(group);
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
rc = makedir(dir, mode);
|
||||
chown(dir, uid, gid);
|
||||
}
|
||||
|
||||
umask(omask);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void set_hostname(char **hostname)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
@@ -81,6 +81,12 @@ int print_result (int fail);
|
||||
|
||||
int getuser (char *username, char **home);
|
||||
int getgroup (char *group);
|
||||
|
||||
int getcuser (char *buf, size_t len);
|
||||
int getcgroup (char *buf, size_t len);
|
||||
|
||||
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
|
||||
|
||||
void set_hostname (char **hostname);
|
||||
void networking (int updown);
|
||||
|
||||
|
||||
+281
-254
@@ -26,7 +26,6 @@
|
||||
#include <err.h>
|
||||
#include <ftw.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <getopt.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
@@ -40,22 +39,18 @@
|
||||
#include "cond.h"
|
||||
#include "serv.h"
|
||||
#include "service.h"
|
||||
#include "cgutil.h"
|
||||
#include "util.h"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||
|
||||
#define NONE " "
|
||||
#define PIPE plain ? "|" : "│"
|
||||
#define FORK plain ? "|-" : "├─"
|
||||
#define END plain ? "`-" : "└─"
|
||||
|
||||
struct command {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
struct cmd {
|
||||
char *cmd;
|
||||
struct cmd *ctx;
|
||||
int (*cb)(char *arg);
|
||||
};
|
||||
|
||||
int icreate = 0;
|
||||
int iforce = 0;
|
||||
int ionce = 0;
|
||||
int heading = 1;
|
||||
int verbose = 0;
|
||||
int plain = 0;
|
||||
@@ -95,6 +90,30 @@ static void col_widths(void)
|
||||
pw = 3;
|
||||
}
|
||||
|
||||
void print_header(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (plain) {
|
||||
int len;
|
||||
|
||||
vfprintf(stdout, fmt, ap);
|
||||
fputs("\n", stdout);
|
||||
for (len = 0; len < screen_cols; len++)
|
||||
fputc('=', stdout);
|
||||
fputs("\n", stdout);
|
||||
} else {
|
||||
char buf[screen_cols];
|
||||
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
printheader(stdout, buf, 0);
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static int runlevel_get(int *prevlevel)
|
||||
{
|
||||
int result;
|
||||
@@ -126,7 +145,6 @@ static int toggle_debug(char *arg)
|
||||
|
||||
static int do_log(char *svc)
|
||||
{
|
||||
char cmd[128];
|
||||
char *logfile = "/var/log/syslog";
|
||||
|
||||
if (!svc || !svc[0])
|
||||
@@ -135,9 +153,7 @@ static int do_log(char *svc)
|
||||
if (!fexist(logfile))
|
||||
logfile = "/var/log/messages";
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "cat %s | grep %s | tail -10", logfile, svc);
|
||||
|
||||
return system(cmd);
|
||||
return systemf("cat %s | grep %s | tail -10", logfile, svc);
|
||||
}
|
||||
|
||||
static int do_runlevel(char *arg)
|
||||
@@ -231,7 +247,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
||||
if (tflag != FTW_F)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(fpath, _PATH_COND "reconf"))
|
||||
if (!strcmp(fpath, _PATH_RECONF))
|
||||
return 0;
|
||||
|
||||
asserted = condstr(cond_get_path(fpath));
|
||||
@@ -247,9 +263,15 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
||||
nm = svc_ident(svc, NULL, 0);
|
||||
pid = svc->pid;
|
||||
}
|
||||
} else if (strncmp("usr/", cond, 4) == 0) {
|
||||
nm = "static";
|
||||
pid = 0;
|
||||
} else if (strncmp("hook/", cond, 4) == 0) {
|
||||
nm = "static";
|
||||
pid = 1;
|
||||
}
|
||||
|
||||
printf("%-*d %-*s %-6s %s\n", pw, pid, iw, nm, asserted, cond);
|
||||
printf("%-*d %-*s %-6s <%s>\n", pw, pid, iw, nm, asserted, cond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -262,7 +284,7 @@ static int do_cond_dump(char *arg)
|
||||
|
||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
||||
iw, "IDENT", "STATUS", "CONDITION");
|
||||
printheader(NULL, title, 0);
|
||||
print_header(title);
|
||||
}
|
||||
|
||||
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
|
||||
@@ -273,41 +295,93 @@ static int do_cond_dump(char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_cond_one(const char *_conds)
|
||||
static int do_cond_act(char *arg, int creat)
|
||||
{
|
||||
static char conds[MAX_COND_LEN];
|
||||
char *cond;
|
||||
char oneshot[256];
|
||||
size_t off;
|
||||
|
||||
strlcpy(conds, _conds, sizeof(conds));
|
||||
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
|
||||
arg += strlen(COND_USR);
|
||||
|
||||
putchar('<');
|
||||
if (!arg || !arg[0])
|
||||
errx(1, "Invalid condition (empty)");
|
||||
if (strchr(arg, '/'))
|
||||
errx(1, "Invalid condition (slashes)");
|
||||
if (strchr(arg, '.'))
|
||||
errx(1, "Invalid condition (periods)");
|
||||
|
||||
snprintf(oneshot, sizeof(oneshot), _PATH_CONDUSR "%s", arg);
|
||||
off = strlen(_PATH_COND);
|
||||
|
||||
if (creat) {
|
||||
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
|
||||
err(1, "Failed asserting condition <%s>", &oneshot[off]);
|
||||
} else {
|
||||
if (erase(oneshot))
|
||||
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_cond_set(char *arg) { return do_cond_act(arg, 1); }
|
||||
static int do_cond_clr(char *arg) { return do_cond_act(arg, 0); }
|
||||
|
||||
static char *svc_cond(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
char *cond, *conds;
|
||||
|
||||
memset(buf, 0, len);
|
||||
|
||||
if (!svc->cond[0])
|
||||
return buf;
|
||||
|
||||
conds = strdup(svc->cond);
|
||||
if (!conds)
|
||||
return buf;
|
||||
|
||||
strlcat(buf, "<", len);
|
||||
|
||||
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
||||
if (cond != conds)
|
||||
putchar(',');
|
||||
strlcat(buf, ",", len);
|
||||
|
||||
switch (cond_get(cond)) {
|
||||
case COND_ON:
|
||||
printf("+%s", cond);
|
||||
strlcat(buf, "+", len);
|
||||
strlcat(buf, cond, len);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
printf("\e[1m~%s\e[0m", cond);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[1m", len);
|
||||
strlcat(buf, "~", len);
|
||||
strlcat(buf, cond, len);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[0m", len);
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
printf("\e[1m-%s\e[0m", cond);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[1m", len);
|
||||
strlcat(buf, "-", len);
|
||||
strlcat(buf, cond, len);
|
||||
if (!plain)
|
||||
strlcat(buf, "\e[0m", len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
putchar('>');
|
||||
strlcat(buf, ">", len);
|
||||
free(conds);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int do_cond_show(char *arg)
|
||||
{
|
||||
char ident[MAX_IDENT_LEN];
|
||||
enum cond_state cond;
|
||||
char buf[512];
|
||||
svc_t *svc;
|
||||
|
||||
col_widths();
|
||||
@@ -316,7 +390,7 @@ static int do_cond_show(char *arg)
|
||||
|
||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
||||
iw, "IDENT", "STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
||||
printheader(NULL, title, 0);
|
||||
print_header(title);
|
||||
}
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
@@ -325,47 +399,20 @@ static int do_cond_show(char *arg)
|
||||
|
||||
cond = cond_get_agg(svc->cond);
|
||||
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
printf("%-*d %-*s ", pw, svc->pid, iw, ident);
|
||||
svc_ident(svc, buf, sizeof(buf));
|
||||
printf("%-*d %-*s ", pw, svc->pid, iw, buf);
|
||||
|
||||
if (cond == COND_ON)
|
||||
printf("%-6.6s ", condstr(cond));
|
||||
else
|
||||
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
|
||||
|
||||
show_cond_one(svc->cond);
|
||||
puts("");
|
||||
puts(svc_cond(svc, buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_cond(char *cmd)
|
||||
{
|
||||
int c;
|
||||
char *arg;
|
||||
struct command command[] = {
|
||||
{ "show", do_cond_show },
|
||||
{ "dump", do_cond_dump },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
if (!cmd)
|
||||
goto fallback;
|
||||
|
||||
arg = strpbrk(cmd, " \0");
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (string_match(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
|
||||
fallback:
|
||||
return do_cond_show(NULL);
|
||||
}
|
||||
|
||||
static int do_cmd(int cmd)
|
||||
{
|
||||
struct init_request rq = {
|
||||
@@ -393,15 +440,18 @@ int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
|
||||
|
||||
int utmp_show(char *file)
|
||||
{
|
||||
time_t sec;
|
||||
struct utmp *ut;
|
||||
static int once = 0;
|
||||
struct tm *sectm;
|
||||
struct utmp *ut;
|
||||
time_t sec;
|
||||
|
||||
int pid;
|
||||
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
|
||||
|
||||
if (heading)
|
||||
printheader(NULL, file, 0);
|
||||
if (heading) {
|
||||
print_header("%s%s ", once ? "\n" : "", file);
|
||||
once++;
|
||||
}
|
||||
utmpname(file);
|
||||
|
||||
setutent();
|
||||
@@ -468,7 +518,7 @@ char *runlevel_string(int runlevel, int levels)
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ISSET(levels, i)) {
|
||||
if (runlevel == i)
|
||||
if (!plain && runlevel == i)
|
||||
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
|
||||
|
||||
if (i == 0)
|
||||
@@ -476,7 +526,7 @@ char *runlevel_string(int runlevel, int levels)
|
||||
else
|
||||
lvl[pos++] = '0' + i;
|
||||
|
||||
if (runlevel == i)
|
||||
if (!plain && runlevel == i)
|
||||
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
|
||||
} else {
|
||||
lvl[pos++] = '-';
|
||||
@@ -489,45 +539,96 @@ char *runlevel_string(int runlevel, int levels)
|
||||
return lvl;
|
||||
}
|
||||
|
||||
static char *svc_command(svc_t *svc, char *cmd, size_t len)
|
||||
static int missing(svc_t *svc)
|
||||
{
|
||||
strlcpy(cmd, svc->cmd, len);
|
||||
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(cmd, " ", len);
|
||||
strlcat(cmd, svc->args[i], len);
|
||||
strlcat(buf, " ", len);
|
||||
strlcat(buf, svc->args[i], len);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
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 cmd[512];
|
||||
char buf[512];
|
||||
svc_t *svc;
|
||||
|
||||
runlevel = runlevel_get(NULL);
|
||||
|
||||
if (arg && arg[0]) {
|
||||
long now = jiffies();
|
||||
char buf[42] = "N/A";
|
||||
char uptm[42] = "N/A";
|
||||
|
||||
svc = client_svc_find(arg);
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
printf("Status : %s\n", status(svc));
|
||||
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
||||
printf("Description : %s\n", svc->desc);
|
||||
printf("Origin : %s\n", svc->file[0] ? svc->file : "built-in");
|
||||
printf("Environment : %s\n", svc->env);
|
||||
printf("Command : %s\n", svc_command(svc, cmd, sizeof(cmd)));
|
||||
printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf)));
|
||||
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
|
||||
printf("Command : %s\n", svc_command(svc, buf, sizeof(buf)));
|
||||
printf("PID file : %s\n", svc->pidfile);
|
||||
printf("PID : %d\n", svc->pid);
|
||||
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
|
||||
printf("User : %s\n", svc->username);
|
||||
printf("Group : %s\n", svc->group);
|
||||
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
|
||||
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||
printf("Status : %s\n", svc_status(svc));
|
||||
printf("\n");
|
||||
|
||||
return do_log(svc->cmd);
|
||||
@@ -544,7 +645,7 @@ static int show_status(char *arg)
|
||||
else
|
||||
strlcat(title, "COMMAND", sizeof(title));
|
||||
|
||||
printheader(NULL, title, 0);
|
||||
print_header(title);
|
||||
}
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
@@ -553,7 +654,7 @@ static int show_status(char *arg)
|
||||
printf("%-*d ", pw, svc->pid);
|
||||
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
printf("%-*s %-8s ", iw, ident, svc_status(svc));
|
||||
printf("%-*s %s ", iw, ident, status(svc));
|
||||
|
||||
lvls = runlevel_string(runlevel, svc->runlevels);
|
||||
if (strchr(lvls, '\e'))
|
||||
@@ -564,123 +665,12 @@ static int show_status(char *arg)
|
||||
if (!verbose)
|
||||
puts(svc->desc);
|
||||
else
|
||||
puts(svc_command(svc, cmd, sizeof(cmd)));
|
||||
puts(svc_command(svc, buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 dump_cgroup(char *path, char *pfx)
|
||||
{
|
||||
struct dirent **namelist = NULL;
|
||||
struct stat st;
|
||||
char buf[512];
|
||||
int rc = 0;
|
||||
FILE *fp;
|
||||
int i, n;
|
||||
int num;
|
||||
|
||||
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 = "";
|
||||
puts(path);
|
||||
}
|
||||
|
||||
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
||||
if (n > 0) {
|
||||
for (i = 0; i < n; i++) {
|
||||
char *nm = namelist[i]->d_name;
|
||||
char dir[80];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
||||
|
||||
if (i + 1 == n) {
|
||||
printf("%s%s ", pfx, END);
|
||||
snprintf(dir, sizeof(dir), "%s ", pfx);
|
||||
} else {
|
||||
printf("%s%s ", pfx, FORK);
|
||||
snprintf(dir, sizeof(dir), "%s%s ", pfx, PIPE);
|
||||
}
|
||||
puts(nm);
|
||||
|
||||
rc += dump_cgroup(buf, dir);
|
||||
|
||||
free(namelist[i]);
|
||||
}
|
||||
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
if (num > 0) {
|
||||
rewind(fp);
|
||||
|
||||
i = 0;
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
FILE *cfp;
|
||||
pid_t pid;
|
||||
|
||||
pid = atoi(chomp(buf));
|
||||
cfp = fopenf("r", "/proc/%d/cmdline", pid);
|
||||
if (!cfp)
|
||||
continue;
|
||||
|
||||
if (fgets(buf, sizeof(buf), cfp))
|
||||
printf("%s%s %d %s\n", pfx, ++i == num ? END : FORK, pid, buf);
|
||||
fclose(cfp);
|
||||
}
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static 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 dump_cgroup(arg, NULL);
|
||||
}
|
||||
|
||||
static int transform(char *nm)
|
||||
{
|
||||
char *names[] = {
|
||||
@@ -725,6 +715,8 @@ static int usage(int rc)
|
||||
" -b, --batch Batch mode, no screen size probing\n"
|
||||
" -c, --create Create missing paths (and files) as needed\n"
|
||||
" -f, --force Ignore missing files and arguments, never prompt\n"
|
||||
" -1, --once Only one lap in commands like 'top'\n"
|
||||
" -p, --plain Use plain table headings, no ctrl chars\n"
|
||||
" -t, --no-heading Skip table headings\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
" -h, --help This help text\n"
|
||||
@@ -764,7 +756,9 @@ static int usage(int rc)
|
||||
fprintf(stderr,
|
||||
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
|
||||
"\n"
|
||||
" cond show Show condition status\n"
|
||||
" cond set <COND> Set (assert) user-defined condition +usr/COND\n"
|
||||
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
|
||||
" cond status Show condition status, default cond command\n"
|
||||
" cond dump Dump all conditions and their status\n"
|
||||
"\n"
|
||||
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
|
||||
@@ -775,6 +769,7 @@ static int usage(int rc)
|
||||
" status Show status of services, default command\n"
|
||||
"\n"
|
||||
" ps List processes based on cgroups\n"
|
||||
" top Show top-like listing based on cgroups\n"
|
||||
"\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" reboot Reboot system\n"
|
||||
@@ -790,6 +785,11 @@ 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;
|
||||
}
|
||||
|
||||
@@ -807,62 +807,106 @@ 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 },
|
||||
{ "devel", do_devel },
|
||||
{ "help", do_help },
|
||||
{ "version", show_version },
|
||||
|
||||
{ "list", serv_list },
|
||||
{ "ls", serv_list },
|
||||
{ "enable", serv_enable },
|
||||
{ "disable", serv_disable },
|
||||
{ "touch", serv_touch },
|
||||
{ "show", serv_show },
|
||||
{ "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, for now */
|
||||
|
||||
{ "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' },
|
||||
{ "no-heading", 0, NULL, 't' },
|
||||
{ "verbose", 0, NULL, 'v' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
struct cmd cond[] = {
|
||||
{ "status", NULL, do_cond_show }, /* default cmd */
|
||||
{ "dump", NULL, do_cond_dump },
|
||||
{ "set", NULL, do_cond_set },
|
||||
{ "clr", NULL, do_cond_clr },
|
||||
{ "clear", NULL, do_cond_clr },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
struct cmd command[] = {
|
||||
{ "status", NULL, show_status }, /* default cmd */
|
||||
|
||||
{ "debug", NULL, toggle_debug },
|
||||
{ "devel", NULL, do_devel },
|
||||
{ "help", NULL, do_help },
|
||||
{ "version", NULL, show_version },
|
||||
|
||||
{ "list", NULL, serv_list },
|
||||
{ "ls", NULL, serv_list },
|
||||
{ "enable", NULL, serv_enable },
|
||||
{ "disable", NULL, serv_disable },
|
||||
{ "touch", NULL, serv_touch },
|
||||
{ "show", NULL, serv_show },
|
||||
{ "edit", NULL, serv_edit },
|
||||
{ "create", NULL, serv_creat },
|
||||
{ "delete", NULL, serv_delete },
|
||||
{ "reload", NULL, do_reload },
|
||||
|
||||
{ "cond", cond, NULL },
|
||||
|
||||
{ "log", NULL, do_log },
|
||||
{ "start", NULL, do_start },
|
||||
{ "stop", NULL, do_stop },
|
||||
{ "restart", NULL, do_restart },
|
||||
|
||||
{ "ps", NULL, show_cgroup },
|
||||
{ "top", NULL, show_cgtop },
|
||||
|
||||
{ "runlevel", NULL, do_runlevel },
|
||||
{ "reboot", NULL, do_reboot },
|
||||
{ "halt", NULL, do_halt },
|
||||
{ "poweroff", NULL, do_poweroff },
|
||||
{ "suspend", NULL, do_suspend },
|
||||
|
||||
{ "utmp", NULL, do_utmp },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
int interactive = 1, c;
|
||||
char *cmd;
|
||||
|
||||
if (transform(progname(argv[0])))
|
||||
return reboot_main(argc, argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "bcfh?tv", long_options, NULL)) != EOF) {
|
||||
while ((c = getopt_long(argc, argv, "1bcfh?ptv", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case '1':
|
||||
ionce = 1;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
interactive = 0;
|
||||
break;
|
||||
@@ -879,6 +923,10 @@ int main(int argc, char *argv[])
|
||||
case '?':
|
||||
return usage(0);
|
||||
|
||||
case 'p':
|
||||
plain = 1;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
heading = 0;
|
||||
break;
|
||||
@@ -892,10 +940,8 @@ int main(int argc, char *argv[])
|
||||
if (interactive)
|
||||
screen_init();
|
||||
|
||||
if (optind >= argc)
|
||||
return show_status(NULL);
|
||||
|
||||
if (!has_utmp()) {
|
||||
/* system w/o utmp support, disable 'utmp' command */
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!string_compare(command[c].cmd, "utmp"))
|
||||
continue;
|
||||
@@ -905,26 +951,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
cmd = argv[optind++];
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
int rc = 0;
|
||||
|
||||
if (!string_match(command[c].cmd, cmd))
|
||||
continue;
|
||||
if (!command[c].cb)
|
||||
continue;
|
||||
|
||||
/* no arg, just a command, e.g. initctl ls */
|
||||
if (optind >= argc)
|
||||
return command[c].cb(NULL);
|
||||
|
||||
/* >=1 arg, e.g., initctl del foo bar baz */
|
||||
while (optind < argc)
|
||||
rc |= command[c].cb(argv[optind++]);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
return cmd_parse(argc - optind, &argv[optind], command);
|
||||
}
|
||||
|
||||
void logit(int prio, const char *fmt, ...)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_INITCTL_H_
|
||||
#define FINIT_INITCTL_H_
|
||||
|
||||
#include "finit.h"
|
||||
#include "util.h"
|
||||
|
||||
extern int icreate; /* initctl -c */
|
||||
extern int iforce; /* initctl -f */
|
||||
extern int ionce; /* initctl -1 */
|
||||
extern int heading; /* initctl -t */
|
||||
extern int verbose; /* initctl -v */
|
||||
extern int plain; /* initctl -p */
|
||||
|
||||
extern void print_header(const char *fmt, ...);
|
||||
|
||||
#endif /* FINIT_INITCTL_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+1
-1
@@ -80,7 +80,7 @@ int logrotate(char *file, int num, off_t sz)
|
||||
}
|
||||
|
||||
if (cnt == 2 && fexist(nfile)) {
|
||||
if (systemf("gzip %s", nfile))
|
||||
if (systemf("gzip %s 2>/dev/null", nfile))
|
||||
continue; /* no gzip, probably */
|
||||
|
||||
(void)remove(nfile);
|
||||
|
||||
@@ -95,18 +95,15 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
|
||||
char *pid_file(svc_t *svc)
|
||||
{
|
||||
static char path[256];
|
||||
char fn[MAX_ARG_LEN];
|
||||
|
||||
if (svc->pidfile[0]) {
|
||||
if (svc->pidfile[0] == '!')
|
||||
return &svc->pidfile[1];
|
||||
return svc->pidfile;
|
||||
}
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
||||
errno = ENOENT;
|
||||
|
||||
return pid_runpath(fn, path, sizeof(path));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid_t pid_file_read(const char *fn)
|
||||
@@ -143,12 +140,13 @@ int pid_file_create(svc_t *svc)
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
static int pid_realpath(svc_t *svc, char *file)
|
||||
int pid_file_set(svc_t *svc, char *file, int not)
|
||||
{
|
||||
int not = 0;
|
||||
|
||||
if (!file)
|
||||
if (!file) {
|
||||
file = pid_file(svc);
|
||||
if (!file)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file[0] == '!') {
|
||||
not = 1;
|
||||
@@ -202,7 +200,7 @@ int pid_file_parse(svc_t *svc, char *arg)
|
||||
|
||||
arg += 4;
|
||||
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
|
||||
return pid_realpath(svc, arg);
|
||||
return pid_file_set(svc, arg, 0);
|
||||
|
||||
if (arg[0] == '!') {
|
||||
arg++;
|
||||
@@ -213,12 +211,12 @@ int pid_file_parse(svc_t *svc, char *arg)
|
||||
if (len > 4 && strcmp(&path[len - 4], ".pid"))
|
||||
strlcat(path, ".pid", sizeof(path));
|
||||
|
||||
return pid_realpath(svc, path);
|
||||
return pid_file_set(svc, path, 0);
|
||||
}
|
||||
|
||||
/* 'pid' arg, no argument following */
|
||||
if (!strcmp(arg, "pid"))
|
||||
return pid_realpath(svc, NULL);
|
||||
return pid_file_set(svc, NULL, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
char *pid_file (svc_t *svc);
|
||||
int pid_file_set (svc_t *svc, char *file, int not);
|
||||
pid_t pid_file_read (const char *fn);
|
||||
int pid_file_create (svc_t *svc);
|
||||
int pid_file_parse (svc_t *svc, char *arg);
|
||||
|
||||
+22
-7
@@ -30,10 +30,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
extern int icreate; /* initctl -c */
|
||||
extern int iforce; /* initctl -f */
|
||||
#include "initctl.h"
|
||||
|
||||
|
||||
static int calc_width(char *arr[], size_t len)
|
||||
@@ -53,7 +50,7 @@ static int calc_width(char *arr[], size_t len)
|
||||
|
||||
static void do_list(const char *path)
|
||||
{
|
||||
char buf[screen_cols];
|
||||
static int once = 0;
|
||||
int width, num, prev;
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
@@ -67,8 +64,26 @@ static void do_list(const char *path)
|
||||
if (gl.gl_pathc <= 0)
|
||||
goto done;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s ", path);
|
||||
printheader(NULL, buf, 0);
|
||||
if (plain) {
|
||||
if (heading)
|
||||
print_header("%s%s ", once ? "\n" : "", path);
|
||||
once++;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
if (!heading) {
|
||||
char buf[512];
|
||||
|
||||
paste(buf, sizeof(buf), path, gl.gl_pathv[i]);
|
||||
puts(buf);
|
||||
} else
|
||||
puts(gl.gl_pathv[i]);
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (heading)
|
||||
print_header("%s ", path);
|
||||
|
||||
width = calc_width(gl.gl_pathv, gl.gl_pathc);
|
||||
if (width <= 0)
|
||||
|
||||
+54
-44
@@ -49,8 +49,6 @@
|
||||
#include "utmp-api.h"
|
||||
#include "schedule.h"
|
||||
|
||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||
|
||||
static struct wq work = {
|
||||
.cb = service_worker,
|
||||
};
|
||||
@@ -262,37 +260,6 @@ static int redirect(svc_t *svc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* non-zero env, no checking if file exists or not
|
||||
*/
|
||||
static char *svc_getenv(svc_t *svc)
|
||||
{
|
||||
int v = 0;
|
||||
|
||||
if (!svc->env[0])
|
||||
return NULL;
|
||||
|
||||
if (svc->env[0] == '-')
|
||||
v = 1;
|
||||
|
||||
return &svc->env[v];
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if svc has env, if env file exists returns true
|
||||
* if it doesn't exist, but has '-', also true. if svc
|
||||
* doesn't have env, also true.
|
||||
*/
|
||||
static int check_env(svc_t *svc)
|
||||
{
|
||||
char *env = svc_getenv(svc);
|
||||
|
||||
if (!env)
|
||||
return 1;
|
||||
|
||||
return fexist(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Source environment file, if it exists
|
||||
* Note: must be called from privsepped child
|
||||
@@ -309,7 +276,7 @@ static void source_env(svc_t *svc)
|
||||
if (!fn)
|
||||
return;
|
||||
|
||||
/* Warning in service_start() after check_env() */
|
||||
/* Warning in service_start() after svc_checkenv() */
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
@@ -384,6 +351,30 @@ static int is_norespawn(void)
|
||||
fexist("/tmp/norespawn");
|
||||
}
|
||||
|
||||
/* used for process group name, derived from originating filename,
|
||||
* so to group multiple services, place them in the same .conf
|
||||
*/
|
||||
static char *group_name(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (!svc->file[0])
|
||||
return svc_ident(svc, buf, len);
|
||||
|
||||
ptr = strrchr(svc->file, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = svc->file;
|
||||
|
||||
strlcpy(buf, ptr, len);
|
||||
ptr = strstr(buf, ".conf");
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_start - Start service
|
||||
* @svc: Service to start
|
||||
@@ -395,6 +386,7 @@ static int service_start(svc_t *svc)
|
||||
{
|
||||
int result = 0, do_progress = 1;
|
||||
sigset_t nmask, omask;
|
||||
char grnam[80];
|
||||
pid_t pid;
|
||||
size_t i;
|
||||
|
||||
@@ -407,14 +399,15 @@ static int service_start(svc_t *svc)
|
||||
|
||||
/* Don't try and start service if it doesn't exist. */
|
||||
if (!whichp(svc->cmd)) {
|
||||
print(1, "%s: does not exist", svc->cmd);
|
||||
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
|
||||
svc_missing(svc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Unlike systemd we do not allow starting service if env is missing, unless - */
|
||||
if (!check_env(svc)) {
|
||||
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
|
||||
if (!svc_checkenv(svc)) {
|
||||
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
|
||||
svc_missing(svc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -440,8 +433,6 @@ static int service_start(svc_t *svc)
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
|
||||
pid = fork();
|
||||
cgroup_service(svc->name, svc->id, pid);
|
||||
|
||||
if (pid == 0) {
|
||||
int status;
|
||||
char *home = NULL;
|
||||
@@ -579,6 +570,8 @@ static int service_start(svc_t *svc)
|
||||
_d("Starting %s %s", svc->cmd, buf);
|
||||
}
|
||||
|
||||
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid);
|
||||
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
|
||||
|
||||
svc->pid = pid;
|
||||
@@ -648,11 +641,12 @@ static void service_cleanup(svc_t *svc)
|
||||
char *fn;
|
||||
|
||||
fn = pid_file(svc);
|
||||
if (remove(fn) && errno != ENOENT)
|
||||
if (fn && remove(fn) && errno != ENOENT)
|
||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
||||
basename(svc->cmd), fn);
|
||||
|
||||
/* No longer running, update books. */
|
||||
svc->oldpid = svc->pid;
|
||||
svc->start_time = svc->pid = 0;
|
||||
}
|
||||
|
||||
@@ -1127,6 +1121,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
|
||||
if (type == SVC_TYPE_SERVICE && manual)
|
||||
svc_stop(svc);
|
||||
} else {
|
||||
/* e.g., if missing cmd or env before */
|
||||
svc_unblock(svc);
|
||||
}
|
||||
|
||||
/* Always clear svc PID file, for now. See TODO */
|
||||
@@ -1143,6 +1140,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
strlcpy(svc->group, ptr, sizeof(svc->group));
|
||||
}
|
||||
strlcpy(svc->username, username, sizeof(svc->username));
|
||||
} else {
|
||||
getcuser(svc->username, sizeof(svc->username));
|
||||
getcgroup(svc->group, sizeof(svc->group));
|
||||
}
|
||||
|
||||
parse_cmdline_args(svc, cmd);
|
||||
@@ -1229,6 +1229,7 @@ void service_monitor(pid_t lost, int status)
|
||||
if (svc_is_daemon(svc)) {
|
||||
service_cleanup(svc);
|
||||
} else if (svc_is_runtask(svc)) {
|
||||
/* run/task should run at least once per runlevel */
|
||||
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
||||
svc->started = 1;
|
||||
else
|
||||
@@ -1296,7 +1297,7 @@ static void service_retry(svc_t *svc)
|
||||
return;
|
||||
}
|
||||
|
||||
if (*restart_cnt >= RESPAWN_MAX) {
|
||||
if (*restart_cnt >= SVC_RESPAWN_MAX) {
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
|
||||
svc_ident(svc, NULL, 0));
|
||||
svc_crashing(svc);
|
||||
@@ -1309,12 +1310,12 @@ static void service_retry(svc_t *svc)
|
||||
|
||||
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
|
||||
svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
|
||||
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX);
|
||||
svc_unblock(svc);
|
||||
service_step(svc);
|
||||
|
||||
/* Wait 2s for the first 5 respawns, then back off to 5s */
|
||||
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
|
||||
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000;
|
||||
service_timeout_after(svc, timeout, service_retry);
|
||||
}
|
||||
|
||||
@@ -1368,10 +1369,15 @@ restart:
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
if (!svc->pid) {
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
/* PID was collected normally, no need to kill it */
|
||||
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
|
||||
_d("%s: stopped normally, no need to send SIGKILL :)", svc->cmd);
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
_d("%s: clearing pid condition ...", svc->name);
|
||||
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
@@ -1400,6 +1406,10 @@ restart:
|
||||
|
||||
err = service_start(svc);
|
||||
if (err) {
|
||||
if (svc_is_missing(svc)) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
break;
|
||||
}
|
||||
(*restart_cnt)++;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -192,10 +192,10 @@ void do_kill(int signo)
|
||||
if (fgets(file, sizeof(file), fp)) {
|
||||
if (strstr(file, "gdbserver"))
|
||||
_d("Skipping %s ...", file);
|
||||
else if (file[0] != '@')
|
||||
kill(pid, signo);
|
||||
else
|
||||
else if (file[0] == '@')
|
||||
_d("Skipping %s ...", &file[1]);
|
||||
else
|
||||
kill(pid, signo);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
@@ -214,11 +214,11 @@ void do_shutdown(shutop_t op)
|
||||
utmp_set_halt();
|
||||
|
||||
/*
|
||||
* Tell all remaining non-monitored processes to exit, give them
|
||||
* some time to exit gracefully, 2 sec is customary.
|
||||
* Tell remaining non-monitored processes to exit, give them
|
||||
* time to exit gracefully, 2 sec was customary, we go for 1.
|
||||
*/
|
||||
do_kill(SIGTERM);
|
||||
do_sleep(2);
|
||||
do_sleep(1);
|
||||
do_kill(SIGKILL);
|
||||
|
||||
/* Exit plugins and API gracefully */
|
||||
@@ -274,15 +274,15 @@ void do_shutdown(shutop_t op)
|
||||
do_sleep(1);
|
||||
}
|
||||
|
||||
_e("Rebooting ...");
|
||||
_d("Rebooting ...");
|
||||
reboot(RB_AUTOBOOT);
|
||||
} else if (op == SHUT_OFF) {
|
||||
_e("Powering down ...");
|
||||
_d("Powering down ...");
|
||||
reboot(RB_POWER_OFF);
|
||||
}
|
||||
|
||||
/* Also fallback if any of the other two fails */
|
||||
_e("Halting ...");
|
||||
_d("Halting ...");
|
||||
reboot(RB_HALT_SYSTEM);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,9 +156,11 @@ restart:
|
||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||
nologin();
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (conf_any_change())
|
||||
conf_reload();
|
||||
if (runlevel != 0 && runlevel != 6) {
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (conf_any_change())
|
||||
conf_reload();
|
||||
}
|
||||
|
||||
/* Reset once flag of runtasks */
|
||||
service_runtask_clean();
|
||||
|
||||
@@ -415,8 +415,20 @@ svc_t *svc_find_by_pidfile(char *fn)
|
||||
pid_t pid;
|
||||
|
||||
pid = pid_file_read(fn);
|
||||
if (pid == -1)
|
||||
if (pid <= 0) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
int v = 0;
|
||||
|
||||
if (svc->pidfile[0] == '!')
|
||||
v = 1;
|
||||
if (strcmp(&svc->pidfile[v], fn))
|
||||
continue;
|
||||
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->pid != pid)
|
||||
@@ -532,6 +544,9 @@ int svc_enabled(svc_t *svc)
|
||||
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||
return 0;
|
||||
|
||||
if (svc_is_missing(svc))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,9 @@ typedef enum {
|
||||
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
|
||||
#define SVC_TERM_TIMEOUT 3000
|
||||
|
||||
/* Prevent endless respawn of faulty services. */
|
||||
#define SVC_RESPAWN_MAX 10
|
||||
|
||||
/*
|
||||
* Default enable for all services, can be stopped by means
|
||||
* of issuing an initctl call. E.g.
|
||||
@@ -95,7 +98,7 @@ typedef struct svc {
|
||||
/* Service details */
|
||||
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
|
||||
int killdelay; /* Delay in msec before sending SIGKILL */
|
||||
pid_t pid;
|
||||
pid_t oldpid, pid;
|
||||
char pidfile[256];
|
||||
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||
int started; /* Set for run/task/sysv to track if started */
|
||||
@@ -194,12 +197,14 @@ static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1;
|
||||
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
|
||||
|
||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
|
||||
|
||||
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
||||
#define svc_start(svc) svc_unblock(svc)
|
||||
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
|
||||
@@ -347,6 +352,37 @@ static inline char *svc_jobid(svc_t *svc, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* non-zero env, no checking if file exists or not
|
||||
*/
|
||||
static inline char *svc_getenv(svc_t *svc)
|
||||
{
|
||||
int v = 0;
|
||||
|
||||
if (!svc->env[0])
|
||||
return NULL;
|
||||
|
||||
if (svc->env[0] == '-')
|
||||
v = 1;
|
||||
|
||||
return &svc->env[v];
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if svc has env, if env file exists returns true
|
||||
* if it doesn't exist, but has '-', also true. if svc
|
||||
* doesn't have env, also true.
|
||||
*/
|
||||
static inline int svc_checkenv(svc_t *svc)
|
||||
{
|
||||
char *env = svc_getenv(svc);
|
||||
|
||||
if (!env)
|
||||
return 1;
|
||||
|
||||
return fexist(env);
|
||||
}
|
||||
|
||||
#endif /* FINIT_SVC_H_ */
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user