mirror of
https://github.com/troglobit/finit.git
synced 2026-11-14 19:03:16 +00:00
Compare commits
14
Commits
59a2f69345
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8451c4087a | ||
|
|
e280569941 | ||
|
|
f15e95d1cc | ||
|
|
34a0a4b1ff | ||
|
|
c03059a9a0 | ||
|
|
297d2de1da | ||
|
|
3acf1cb7b1 | ||
|
|
eb9fb83011 | ||
|
|
b6f0d5314b | ||
|
|
90512b07a0 | ||
|
|
d313b1fd74 | ||
|
|
439cab0558 | ||
|
|
98b2effb52 | ||
|
|
3a18ca4a30 |
No files matched your search
@@ -153,6 +153,12 @@ All relevant changes are documented in this file.
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Fix #512: `--` in the `command` of a block-format .conf, it was taken as the
|
||||||
|
start of the description, so `command = "cmd -- arg"` ran `cmd` with
|
||||||
|
description `arg`. Found by Alexandru Paniș
|
||||||
|
- Fix a command whose name starts like a service option, e.g. `logger` or
|
||||||
|
`pidof`. It was taken as the `log` or `pid` option and the next argument
|
||||||
|
ran as the command, which also broke the `logger` example in finit.conf(5)
|
||||||
- Reject a .conf file that declares the same block title twice. The title is
|
- Reject a .conf file that declares the same block title twice. The title is
|
||||||
the service identity and libConfuse merges two sections sharing one, so the
|
the service identity and libConfuse merges two sections sharing one, so the
|
||||||
file loaded as a single service holding a mix of both declarations,
|
file loaded as a single service holding a mix of both declarations,
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ and `/dev/disk/by-label/` symlinks after. keventd is enabled by
|
|||||||
default, so this is a hard requirement unless you build with
|
default, so this is a hard requirement unless you build with
|
||||||
`--without-keventd`.
|
`--without-keventd`.
|
||||||
|
|
||||||
|
Linux capabilities support for services is also built by default. It needs
|
||||||
|
[libcap][] (-lcap); the build falls back to no capabilities support when that
|
||||||
|
library is missing, or with `--disable-libcap`.
|
||||||
|
|
||||||
PAM session support for services is built by default. It needs
|
PAM session support for services is built by default. It needs
|
||||||
[Linux-PAM][] (-lpam); the build falls back to no PAM support when that
|
[Linux-PAM][] (-lpam); the build falls back to no PAM support when that
|
||||||
library is missing. A static build, `--enable-static`, disables PAM
|
library is missing. A static build, `--enable-static`, disables PAM
|
||||||
@@ -37,6 +41,7 @@ version of the above mentioned libraries. Currently required versions:
|
|||||||
- libuEv v2.2.0, or later
|
- libuEv v2.2.0, or later
|
||||||
- libConfuse v3.3, or later
|
- libConfuse v3.3, or later
|
||||||
- libblkid, any version, when keventd is enabled
|
- libblkid, any version, when keventd is enabled
|
||||||
|
- libcap, any version, unless `--disable-libcap` is given
|
||||||
|
|
||||||
|
|
||||||
Configure
|
Configure
|
||||||
@@ -59,6 +64,10 @@ Below are a few of the main switches to configure:
|
|||||||
built-ins (.o files) and all external libraries, except the C library
|
built-ins (.o files) and all external libraries, except the C library
|
||||||
will be linked statically.
|
will be linked statically.
|
||||||
|
|
||||||
|
* `--disable-libcap`: Opt out of Finit's built-in Linux capabilities support,
|
||||||
|
enabled by default. Needs libcap, and falls back to disabled when it is
|
||||||
|
missing. See [Linux Capabilities](config/capabilities.md)
|
||||||
|
|
||||||
* `--disable-pam`: Opt out of Finit's built-in PAM session support,
|
* `--disable-pam`: Opt out of Finit's built-in PAM session support,
|
||||||
enabled by default, which lets a service declare `pam = "name"` and
|
enabled by default, which lets a service declare `pam = "name"` and
|
||||||
run inside a session set up from `/etc/pam.d/name`. Needs libpam,
|
run inside a session set up from `/etc/pam.d/name`. Needs libpam,
|
||||||
@@ -298,4 +307,5 @@ it only for debugging start up issues when Finit crashes.
|
|||||||
[libite]: https://github.com/troglobit/libite
|
[libite]: https://github.com/troglobit/libite
|
||||||
[libConfuse]: https://github.com/libconfuse/libconfuse
|
[libConfuse]: https://github.com/libconfuse/libconfuse
|
||||||
[util-linux]: https://github.com/util-linux/util-linux
|
[util-linux]: https://github.com/util-linux/util-linux
|
||||||
|
[libcap]: https://sites.google.com/site/fullycapable/
|
||||||
[Linux-PAM]: https://github.com/linux-pam/linux-pam
|
[Linux-PAM]: https://github.com/linux-pam/linux-pam
|
||||||
@@ -169,8 +169,9 @@ ps -o user,pid,cmd -p $(pidof nginx)
|
|||||||
|
|
||||||
- Linux kernel 4.3+ (for ambient capabilities support)
|
- Linux kernel 4.3+ (for ambient capabilities support)
|
||||||
- libcap library installed
|
- libcap library installed
|
||||||
- Finit built with `--enable-libcap`, otherwise a `capabilities` list is
|
- Finit built with libcap support (enabled by default unless `--disable-libcap`
|
||||||
ignored, with a warning
|
is given or libcap was missing at build time), otherwise a `capabilities` list
|
||||||
|
is ignored, with a warning
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
|
|||||||
+76
-21
@@ -81,6 +81,13 @@ static int builtin_kmod(struct uevent *ev, int argc, char **argv)
|
|||||||
|
|
||||||
/* ---- hwdb -------------------------------------------------------- */
|
/* ---- hwdb -------------------------------------------------------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Longest lookup key. A key built by the stock name-based evdev rule
|
||||||
|
* (name, phys, ev and the DMI modalias) is 280 to 300 bytes with typical
|
||||||
|
* DMI strings, and 250 to 265 bytes for the plain name rule.
|
||||||
|
*/
|
||||||
|
#define HWDB_KEY_MAX 1024
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Match a single text hwdb file against the lookup key.
|
* Match a single text hwdb file against the lookup key.
|
||||||
* Text format: match lines at column 0, property lines indented.
|
* Text format: match lines at column 0, property lines indented.
|
||||||
@@ -106,7 +113,11 @@ static void hwdb_match_file(const char *path, const char *lookup,
|
|||||||
while (fgets(line, sizeof(line), fp)) {
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
line[strcspn(line, "\n")] = '\0';
|
line[strcspn(line, "\n")] = '\0';
|
||||||
|
|
||||||
if (!line[0] || line[0] == '#') {
|
/* Comments do not end the record being read */
|
||||||
|
if (line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!line[0]) {
|
||||||
matched = 0;
|
matched = 0;
|
||||||
in_props = 0;
|
in_props = 0;
|
||||||
continue;
|
continue;
|
||||||
@@ -145,9 +156,11 @@ static void hwdb_match_file(const char *path, const char *lookup,
|
|||||||
/*
|
/*
|
||||||
* Build the hwdb lookup key for input devices.
|
* Build the hwdb lookup key for input devices.
|
||||||
*
|
*
|
||||||
* udev hwdb files use the format:
|
* The key is the modalias of the input device, which the rule's
|
||||||
* evdev:input:b{BUS}v{VENDOR}p{PRODUCT}e{VERSION}*
|
* --lookup-prefix (evdev:, id-input:modalias:) is put in front of:
|
||||||
* where all fields are 4-digit uppercase hex.
|
* input:b{BUS}v{VENDOR}p{PRODUCT}e{VERSION}-e{CAPABILITIES...}
|
||||||
|
* where the IDs are 4-digit uppercase hex. If the device has no modalias
|
||||||
|
* attribute, the part up to the version is built from the id/ files.
|
||||||
*
|
*
|
||||||
* The id/ files live under the input device directory. For eventN nodes
|
* The id/ files live under the input device directory. For eventN nodes
|
||||||
* (devpath ends in .../inputN/eventN) we strip the last component first.
|
* (devpath ends in .../inputN/eventN) we strip the last component first.
|
||||||
@@ -156,7 +169,7 @@ static void hwdb_input_key(const struct uevent *ev, char *key, size_t len)
|
|||||||
{
|
{
|
||||||
char base[PATH_MAX];
|
char base[PATH_MAX];
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
char bus_s[8], vendor_s[8], product_s[8], version_s[8];
|
char bus_s[8] = "", vendor_s[8] = "", product_s[8] = "", version_s[8] = "";
|
||||||
|
|
||||||
if (!ev->devpath)
|
if (!ev->devpath)
|
||||||
return;
|
return;
|
||||||
@@ -180,11 +193,28 @@ static void hwdb_input_key(const struct uevent *ev, char *key, size_t len)
|
|||||||
snprintf(path, sizeof(path), "/sys%s", ev->devpath);
|
snprintf(path, sizeof(path), "/sys%s", ev->devpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(base, sizeof(base), "%s/id/vendor", path); sysfs_read_file(base, vendor_s, sizeof(vendor_s));
|
/*
|
||||||
snprintf(base, sizeof(base), "%s/id/product", path); sysfs_read_file(base, product_s, sizeof(product_s));
|
* Prefer the modalias of the input device. Unlike the id/ files it
|
||||||
snprintf(base, sizeof(base), "%s/id/version", path); sysfs_read_file(base, version_s, sizeof(version_s));
|
* carries the capability bitmaps (-e0,1,2,4,k110,...) that some
|
||||||
|
* entries match on.
|
||||||
|
*/
|
||||||
|
snprintf(base, sizeof(base), "%s/modalias", path);
|
||||||
|
if (!sysfs_read_file(base, key, len) && !strncmp(key, "input:", 6))
|
||||||
|
return;
|
||||||
|
key[0] = '\0';
|
||||||
|
|
||||||
snprintf(key, len, "evdev:input:b%04Xv%04Xp%04Xe%04X*",
|
/* Otherwise build it from the id/ files; without a full set there is no key. */
|
||||||
|
snprintf(base, sizeof(base), "%s/id/vendor", path);
|
||||||
|
if (sysfs_read_file(base, vendor_s, sizeof(vendor_s)) < 0)
|
||||||
|
return;
|
||||||
|
snprintf(base, sizeof(base), "%s/id/product", path);
|
||||||
|
if (sysfs_read_file(base, product_s, sizeof(product_s)) < 0)
|
||||||
|
return;
|
||||||
|
snprintf(base, sizeof(base), "%s/id/version", path);
|
||||||
|
if (sysfs_read_file(base, version_s, sizeof(version_s)) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
snprintf(key, len, "input:b%04Xv%04Xp%04Xe%04X*",
|
||||||
(unsigned)strtoul(bus_s, NULL, 16),
|
(unsigned)strtoul(bus_s, NULL, 16),
|
||||||
(unsigned)strtoul(vendor_s, NULL, 16),
|
(unsigned)strtoul(vendor_s, NULL, 16),
|
||||||
(unsigned)strtoul(product_s, NULL, 16),
|
(unsigned)strtoul(product_s, NULL, 16),
|
||||||
@@ -265,24 +295,48 @@ static void hwdb_scan(const char *key, struct uevent *ev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Insert prefix in front of key, in place; -1 if the result does not fit. */
|
||||||
|
static int hwdb_prepend(char *key, size_t len, const char *prefix)
|
||||||
|
{
|
||||||
|
size_t plen = strlen(prefix), klen = strlen(key);
|
||||||
|
|
||||||
|
if (plen + klen >= len)
|
||||||
|
return -1;
|
||||||
|
memmove(key + plen, key, klen + 1);
|
||||||
|
memcpy(key, prefix, plen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int builtin_hwdb(struct uevent *ev, int argc, char **argv)
|
static int builtin_hwdb(struct uevent *ev, int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *subsys = NULL;
|
const char *subsys = NULL;
|
||||||
const char *prefix = NULL;
|
const char *prefix = NULL;
|
||||||
char key[256] = "";
|
const char *explicit_key = NULL;
|
||||||
|
char key[HWDB_KEY_MAX] = "";
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Parse --subsystem=X and --lookup-prefix=Y */
|
/*
|
||||||
|
* Parse --subsystem=X and --lookup-prefix=Y. The first argument
|
||||||
|
* that is not an option is the lookup key itself, which is how the
|
||||||
|
* stock udev rules use the builtin:
|
||||||
|
*
|
||||||
|
* IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'"
|
||||||
|
*/
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
if (!strncmp(argv[i], "--subsystem=", 12))
|
if (!strncmp(argv[i], "--subsystem=", 12))
|
||||||
subsys = argv[i] + 12;
|
subsys = argv[i] + 12;
|
||||||
else if (!strncmp(argv[i], "--lookup-prefix=", 16))
|
else if (!strncmp(argv[i], "--lookup-prefix=", 16))
|
||||||
prefix = argv[i] + 16;
|
prefix = argv[i] + 16;
|
||||||
|
else if (argv[i][0] != '-' && !explicit_key)
|
||||||
|
explicit_key = argv[i];
|
||||||
}
|
}
|
||||||
if (!subsys)
|
if (!subsys)
|
||||||
subsys = ev->subsystem;
|
subsys = ev->subsystem;
|
||||||
|
|
||||||
if (subsys && !strcmp(subsys, "input")) {
|
if (explicit_key) {
|
||||||
|
if (snprintf(key, sizeof(key), "%s", explicit_key) >= (int)sizeof(key))
|
||||||
|
goto too_long;
|
||||||
|
} else if (subsys && !strcmp(subsys, "input")) {
|
||||||
hwdb_input_key(ev, key, sizeof(key));
|
hwdb_input_key(ev, key, sizeof(key));
|
||||||
} else if (subsys && (!strcmp(subsys, "usb") ||
|
} else if (subsys && (!strcmp(subsys, "usb") ||
|
||||||
!strcmp(subsys, "usb_device"))) {
|
!strcmp(subsys, "usb_device"))) {
|
||||||
@@ -290,29 +344,30 @@ static int builtin_hwdb(struct uevent *ev, int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Fall back to MODALIAS / ev->modalias for pci, platform, etc. */
|
/* Fall back to MODALIAS / ev->modalias for pci, platform, etc. */
|
||||||
if (!key[0]) {
|
if (!key[0] && !explicit_key) {
|
||||||
const char *m = uevent_getenv(ev, "MODALIAS");
|
const char *m = uevent_getenv(ev, "MODALIAS");
|
||||||
|
|
||||||
if (!m)
|
if (!m)
|
||||||
m = ev->modalias;
|
m = ev->modalias;
|
||||||
if (m)
|
if (m && snprintf(key, sizeof(key), "%s", m) >= (int)sizeof(key))
|
||||||
snprintf(key, sizeof(key), "%s", m);
|
goto too_long;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key[0])
|
if (!key[0])
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* --lookup-prefix scopes the match by prepending to the key. */
|
/* --lookup-prefix scopes the match by prepending to the key. */
|
||||||
if (prefix && *prefix) {
|
if (prefix && *prefix && hwdb_prepend(key, sizeof(key), prefix))
|
||||||
char tmp[sizeof(key)];
|
goto too_long;
|
||||||
|
|
||||||
snprintf(tmp, sizeof(tmp), "%s%s", prefix, key);
|
|
||||||
snprintf(key, sizeof(key), "%s", tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
logit(LOG_DEBUG, "rules: hwdb lookup: %s", key);
|
logit(LOG_DEBUG, "rules: hwdb lookup: %s", key);
|
||||||
hwdb_scan(key, ev);
|
hwdb_scan(key, ev);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
too_long:
|
||||||
|
logit(LOG_WARNING, "rules: hwdb key too long (max %d bytes), lookup skipped",
|
||||||
|
HWDB_KEY_MAX - 1);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- path_id ----------------------------------------------------- */
|
/* ---- path_id ----------------------------------------------------- */
|
||||||
|
|||||||
+34
-3
@@ -501,6 +501,33 @@ void rules_free(struct rule_list *list)
|
|||||||
|
|
||||||
/* ----- variable substitution ------------------------------------------- */
|
/* ----- variable substitution ------------------------------------------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Syspath of the parent device selected by the last successful
|
||||||
|
* KERNELS/SUBSYSTEMS/DRIVERS/ATTRS== match of the rule being evaluated.
|
||||||
|
* As in udev, $attr{} and %s{} fall back to this parent when the event
|
||||||
|
* device has no such attribute itself; the stock rules rely on it:
|
||||||
|
*
|
||||||
|
* ATTRS{bInterfaceNumber}=="?*", SYMLINK+="...-if$attr{bInterfaceNumber}-..."
|
||||||
|
*
|
||||||
|
* Cleared at the start of every rule and again once it has been applied.
|
||||||
|
*/
|
||||||
|
static char matched_parent[PATH_MAX];
|
||||||
|
|
||||||
|
/* Read an attribute for $attr{}/%s{}: the event device first, then the matched parent */
|
||||||
|
static int subst_read_attr(const struct uevent *ev, const char *attr,
|
||||||
|
char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
if (ev->devpath && !sysfs_read_attr(ev->devpath, attr, buf, len))
|
||||||
|
return 0;
|
||||||
|
if (!matched_parent[0])
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", matched_parent, attr);
|
||||||
|
return sysfs_read_file(path, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
static void append_str(char *buf, size_t *pos, size_t bufsz, const char *s)
|
static void append_str(char *buf, size_t *pos, size_t bufsz, const char *s)
|
||||||
{
|
{
|
||||||
size_t l;
|
size_t l;
|
||||||
@@ -604,8 +631,7 @@ static void subst_value(const char *src, char *buf, size_t bufsz,
|
|||||||
al = sizeof(attr) - 1;
|
al = sizeof(attr) - 1;
|
||||||
memcpy(attr, as, al);
|
memcpy(attr, as, al);
|
||||||
attr[al] = 0;
|
attr[al] = 0;
|
||||||
if (!sysfs_read_attr(ev->devpath, attr,
|
if (!subst_read_attr(ev, attr, tmp, sizeof(tmp)))
|
||||||
tmp, sizeof(tmp)))
|
|
||||||
append_str(buf, &pos, bufsz, tmp);
|
append_str(buf, &pos, bufsz, tmp);
|
||||||
p = ae + 1;
|
p = ae + 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -681,7 +707,7 @@ static void subst_value(const char *src, char *buf, size_t bufsz,
|
|||||||
al = sizeof(attr) - 1;
|
al = sizeof(attr) - 1;
|
||||||
memcpy(attr, as, al);
|
memcpy(attr, as, al);
|
||||||
attr[al] = 0;
|
attr[al] = 0;
|
||||||
if (!sysfs_read_attr(ev->devpath, attr, tmp, sizeof(tmp)))
|
if (!subst_read_attr(ev, attr, tmp, sizeof(tmp)))
|
||||||
append_str(buf, &pos, bufsz, tmp);
|
append_str(buf, &pos, bufsz, tmp);
|
||||||
p = ae + 1;
|
p = ae + 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -908,6 +934,8 @@ static int parent_cb(const char *syspath, void *data)
|
|||||||
|
|
||||||
if (pattern_match(k->value, subject, k->pat_type)) {
|
if (pattern_match(k->value, subject, k->pat_type)) {
|
||||||
ctx->found = 1;
|
ctx->found = 1;
|
||||||
|
if (k->op == OP_MATCH_EQ)
|
||||||
|
snprintf(matched_parent, sizeof(matched_parent), "%s", syspath);
|
||||||
return 0; /* stop walking */
|
return 0; /* stop walking */
|
||||||
}
|
}
|
||||||
return 1; /* keep walking */
|
return 1; /* keep walking */
|
||||||
@@ -1101,6 +1129,8 @@ static int rule_matches(const struct rule *r, struct uevent *ev)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
matched_parent[0] = 0;
|
||||||
|
|
||||||
for (i = 0; i < r->nkeys; i++) {
|
for (i = 0; i < r->nkeys; i++) {
|
||||||
const struct rule_key *k = &r->keys[i];
|
const struct rule_key *k = &r->keys[i];
|
||||||
|
|
||||||
@@ -1627,6 +1657,7 @@ int rules_apply(struct rule_list *list, struct uevent *ev)
|
|||||||
uevent_action_str(ev->action), ev->devpath ?: "");
|
uevent_action_str(ev->action), ev->devpath ?: "");
|
||||||
|
|
||||||
exec_rule(r, ev);
|
exec_rule(r, ev);
|
||||||
|
matched_parent[0] = 0;
|
||||||
|
|
||||||
/* GOTO takes effect after this rule's assignments are applied */
|
/* GOTO takes effect after this rule's assignments are applied */
|
||||||
goto_label = NULL;
|
goto_label = NULL;
|
||||||
|
|||||||
+76
-18
@@ -507,6 +507,62 @@ static struct devrule *find_rule(struct uevent *ev)
|
|||||||
return &devrules[NELEMS(devrules) - 1];
|
return &devrules[NELEMS(devrules) - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the relative target of a symlink at /dev/<link> that points at the
|
||||||
|
* device node /dev/<node>, e.g.
|
||||||
|
*
|
||||||
|
* link "input/by-id/usb-X-event-kbd", node "input/event17" -> "../event17"
|
||||||
|
* link "disk/by-id/ata-X", node "sda" -> "../../sda"
|
||||||
|
*
|
||||||
|
* Both arguments are relative to /dev. The directory components the link
|
||||||
|
* and the node have in common are dropped, then one "../" is emitted for
|
||||||
|
* each directory the link still has to climb out of.
|
||||||
|
*/
|
||||||
|
static int symlink_relative_target(const char *link, const char *node,
|
||||||
|
char *buf, size_t len)
|
||||||
|
{
|
||||||
|
const char *l = link, *n = node;
|
||||||
|
size_t off = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (*l == '/')
|
||||||
|
l++;
|
||||||
|
while (*n == '/')
|
||||||
|
n++;
|
||||||
|
|
||||||
|
/* Skip leading directories shared by both paths */
|
||||||
|
for (;;) {
|
||||||
|
const char *ls = strchr(l, '/');
|
||||||
|
const char *ns = strchr(n, '/');
|
||||||
|
|
||||||
|
if (!ls || !ns || ls - l != ns - n || strncmp(l, n, ls - l))
|
||||||
|
break;
|
||||||
|
l = ls + 1;
|
||||||
|
n = ns + 1;
|
||||||
|
while (*l == '/')
|
||||||
|
l++;
|
||||||
|
while (*n == '/')
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[0] = 0;
|
||||||
|
for (; (l = strchr(l, '/')); l++) {
|
||||||
|
while (l[1] == '/')
|
||||||
|
l++;
|
||||||
|
if (!l[1])
|
||||||
|
break;
|
||||||
|
ret = snprintf(buf + off, len - off, "../");
|
||||||
|
if (ret < 0 || (size_t)ret >= len - off)
|
||||||
|
return -1;
|
||||||
|
off += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snprintf(buf + off, len - off, "%s", n);
|
||||||
|
if (ret < 0 || (size_t)ret >= len - off)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create device node in /dev.
|
* Create device node in /dev.
|
||||||
*/
|
*/
|
||||||
@@ -792,7 +848,7 @@ static int disk_path_id(struct uevent *ev, char *id, size_t len)
|
|||||||
static int symlink_add_disk(struct uevent *ev)
|
static int symlink_add_disk(struct uevent *ev)
|
||||||
{
|
{
|
||||||
char target[PATH_MAX], link[PATH_MAX], id[256];
|
char target[PATH_MAX], link[PATH_MAX], id[256];
|
||||||
const char *name;
|
const char *name, *devtype;
|
||||||
|
|
||||||
if (!ev->devname)
|
if (!ev->devname)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -801,13 +857,22 @@ static int symlink_add_disk(struct uevent *ev)
|
|||||||
name = strrchr(ev->devname, '/');
|
name = strrchr(ev->devname, '/');
|
||||||
name = name ? name + 1 : ev->devname;
|
name = name ? name + 1 : ev->devname;
|
||||||
|
|
||||||
/* by-id: serial-based identifier */
|
/*
|
||||||
|
* by-id: serial-based identifier.
|
||||||
|
* Partitions are skipped, as their serial lives on the parent disk and would
|
||||||
|
* clobber its by-id link, partitions get theirs from the udev rules instead.
|
||||||
|
*/
|
||||||
|
devtype = uevent_getenv(ev, "DEVTYPE");
|
||||||
|
if (devtype && !strcmp(devtype, "partition"))
|
||||||
|
goto path;
|
||||||
|
|
||||||
if (!disk_serial_id(ev, id, sizeof(id))) {
|
if (!disk_serial_id(ev, id, sizeof(id))) {
|
||||||
snprintf(target, sizeof(target), "../../%s", name);
|
snprintf(target, sizeof(target), "../../%s", name);
|
||||||
snprintf(link, sizeof(link), "/dev/disk/by-id/%s", id);
|
snprintf(link, sizeof(link), "/dev/disk/by-id/%s", id);
|
||||||
symlink_create(target, link, ev->devpath);
|
symlink_create(target, link, ev->devpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path:
|
||||||
/* by-path: topology-based identifier */
|
/* by-path: topology-based identifier */
|
||||||
if (!disk_path_id(ev, id, sizeof(id))) {
|
if (!disk_path_id(ev, id, sizeof(id))) {
|
||||||
snprintf(target, sizeof(target), "../../%s", name);
|
snprintf(target, sizeof(target), "../../%s", name);
|
||||||
@@ -903,9 +968,6 @@ int symlink_add(struct uevent *ev)
|
|||||||
for (i = 0; i < ev->applied.nsymlinks; i++) {
|
for (i = 0; i < ev->applied.nsymlinks; i++) {
|
||||||
const char *devname, *sl;
|
const char *devname, *sl;
|
||||||
char link[PATH_MAX], target[PATH_MAX];
|
char link[PATH_MAX], target[PATH_MAX];
|
||||||
size_t toff = 0;
|
|
||||||
int depth = 0;
|
|
||||||
const char *p;
|
|
||||||
|
|
||||||
sl = ev->applied.symlinks[i];
|
sl = ev->applied.symlinks[i];
|
||||||
if (!sl || !*sl)
|
if (!sl || !*sl)
|
||||||
@@ -915,19 +977,15 @@ int symlink_add(struct uevent *ev)
|
|||||||
if (!devname)
|
if (!devname)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* basename of devname for the symlink target */
|
/*
|
||||||
const char *base = strrchr(devname, '/');
|
* The node lives at /dev/<devname>, which is not necessarily
|
||||||
base = base ? base + 1 : devname;
|
* directly in /dev (input/event17, dri/card0, ...), so the
|
||||||
|
* target must be computed relative to the link's directory.
|
||||||
/* Count '/' in symlink path to determine relative depth */
|
*/
|
||||||
for (p = sl; *p; p++)
|
if (symlink_relative_target(sl, devname, target, sizeof(target))) {
|
||||||
if (*p == '/')
|
logit(LOG_WARNING, "Symlink %s -> %s: path too long", sl, devname);
|
||||||
depth++;
|
continue;
|
||||||
|
}
|
||||||
target[0] = 0;
|
|
||||||
for (int d = 0; d < depth; d++)
|
|
||||||
toff += snprintf(target + toff, sizeof(target) - toff, "../");
|
|
||||||
snprintf(target + toff, sizeof(target) - toff, "%s", base);
|
|
||||||
|
|
||||||
snprintf(link, sizeof(link), "/dev/%s", sl);
|
snprintf(link, sizeof(link), "/dev/%s", sl);
|
||||||
symlink_create(target, link, ev->devpath ?: "");
|
symlink_create(target, link, ev->devpath ?: "");
|
||||||
|
|||||||
@@ -42,6 +42,9 @@
|
|||||||
#include "iwatch.h"
|
#include "iwatch.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
#ifdef AUTO_RELOAD
|
||||||
|
#include "sm.h"
|
||||||
|
#endif
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
|||||||
+8
-7
@@ -67,6 +67,9 @@
|
|||||||
#include "legacy.h"
|
#include "legacy.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
#ifdef AUTO_RELOAD
|
||||||
|
#include "sm.h"
|
||||||
|
#endif
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -1123,7 +1126,7 @@ static int sec_getbool(cfg_t *sec, const char *key, const char *alias)
|
|||||||
* The line-based format could only spell this as one stanza per
|
* The line-based format could only spell this as one stanza per
|
||||||
* candidate, relying on nowarn to skip the ones that were not
|
* candidate, relying on nowarn to skip the ones that were not
|
||||||
* installed. A candidate not being there is not an error here, so
|
* installed. A candidate not being there is not an error here, so
|
||||||
* only the winner is looked up again by service_register().
|
* only the winner is looked up again by service_register_desc().
|
||||||
*
|
*
|
||||||
* A leading '-' still says a missing binary is expected, cf. systemd
|
* A leading '-' still says a missing binary is expected, cf. systemd
|
||||||
* ExecStart=-.
|
* ExecStart=-.
|
||||||
@@ -1440,7 +1443,7 @@ static void provides_translate(cfg_t *sec, svc_t *svc, char *file)
|
|||||||
/*
|
/*
|
||||||
* These have no legacy token, they are validated by service_set_dir()
|
* These have no legacy token, they are validated by service_set_dir()
|
||||||
* and stored directly on the registered svc. Empty means unset,
|
* and stored directly on the registered svc. Empty means unset,
|
||||||
* service_register() has already cleared the fields.
|
* service_register_desc() has already cleared the fields.
|
||||||
*/
|
*/
|
||||||
static void dirs_translate(cfg_t *sec, svc_t *svc, char *file)
|
static void dirs_translate(cfg_t *sec, svc_t *svc, char *file)
|
||||||
{
|
{
|
||||||
@@ -1725,11 +1728,9 @@ static void svc_translate(cfg_t *sec, int type, struct rlimit rlimit[], char *fi
|
|||||||
/* the command and its arguments, verbatim */
|
/* the command and its arguments, verbatim */
|
||||||
addtok(line, sizeof(line), "%s", cmd);
|
addtok(line, sizeof(line), "%s", cmd);
|
||||||
|
|
||||||
if ((str = sec_getstr(sec, "description", "desc")))
|
str = sec_getstr(sec, "description", "desc");
|
||||||
addtok(line, sizeof(line), "-- %s", str);
|
|
||||||
|
|
||||||
dbg("translated: %s", line);
|
dbg("translated: %s", line);
|
||||||
svc = service_register(type, line, rlimit, file);
|
svc = service_register_desc(type, line, str, rlimit, file);
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1804,7 +1805,7 @@ static void tty_translate(cfg_t *sec, struct rlimit rlimit[], char *file)
|
|||||||
addtok(line, sizeof(line), "%s", str);
|
addtok(line, sizeof(line), "%s", str);
|
||||||
|
|
||||||
dbg("translated: tty %s", line);
|
dbg("translated: tty %s", line);
|
||||||
service_register(SVC_TYPE_TTY, line, rlimit, file);
|
service_register_desc(SVC_TYPE_TTY, line, NULL, rlimit, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+95
-37
@@ -1599,6 +1599,15 @@ int service_restart_now(svc_t *svc)
|
|||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
if (svc_is_runtask(svc) && svc_is_remain(svc) &&
|
||||||
|
svc->state == SVC_DONE_STATE) {
|
||||||
|
service_timeout_cancel(svc);
|
||||||
|
svc_restarting(svc);
|
||||||
|
service_step(svc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!svc_is_running(svc))
|
if (!svc_is_running(svc))
|
||||||
return service_start_now(svc);
|
return service_start_now(svc);
|
||||||
|
|
||||||
@@ -1803,6 +1812,24 @@ static char *parse_args(char **line)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Match an option that takes a value, @opt on its own or followed by
|
||||||
|
* one of the characters in @sep, e.g. "log" and "log:/path". Returns
|
||||||
|
* the value, or NULL, so a command like logger or pidof is not taken
|
||||||
|
* for an option.
|
||||||
|
*/
|
||||||
|
static char *is_opt(char *tok, const char *opt, const char *sep)
|
||||||
|
{
|
||||||
|
size_t len = strlen(opt);
|
||||||
|
|
||||||
|
if (strncasecmp(tok, opt, len))
|
||||||
|
return NULL;
|
||||||
|
if (tok[len] && !strchr(sep, tok[len]))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &tok[len];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* log:/path/to/logfile,priority:facility.level,tag:ident
|
* log:/path/to/logfile,priority:facility.level,tag:ident
|
||||||
*/
|
*/
|
||||||
@@ -2147,9 +2174,10 @@ static void parse_cmdline_args(svc_t *svc, char *cmd, char **args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* service_register - Register service, task or run commands
|
* service_register_desc - Register service, task or run commands
|
||||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||||
* @cfg: Configuration, complete command, with -- for description text
|
* @cfg: Configuration, complete command, without description text
|
||||||
|
* @desc: Description text, or %NULL
|
||||||
* @rlimit: Limits for this service/task/run, may be global limits
|
* @rlimit: Limits for this service/task/run, may be global limits
|
||||||
* @file: The file name service was loaded from
|
* @file: The file name service was loaded from
|
||||||
*
|
*
|
||||||
@@ -2207,9 +2235,10 @@ static void parse_cmdline_args(svc_t *svc, char *cmd, char **args)
|
|||||||
* skipped on purpose -- conditional loading, bootstrap over, nowarn --
|
* skipped on purpose -- conditional loading, bootstrap over, nowarn --
|
||||||
* also returns %NULL, with @errno zero.
|
* also returns %NULL, with @errno zero.
|
||||||
*/
|
*/
|
||||||
svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
svc_t *service_register_desc(int type, char *cfg, const char *desc,
|
||||||
|
struct rlimit rlimit[], char *file)
|
||||||
{
|
{
|
||||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
char *cmd, *runlevels = NULL, *cond = NULL;
|
||||||
char *username = NULL, *log = NULL, *pid = NULL;
|
char *username = NULL, *log = NULL, *pid = NULL;
|
||||||
char *name = NULL, *halt = NULL, *delay = NULL;
|
char *name = NULL, *halt = NULL, *delay = NULL;
|
||||||
char *id = NULL, *env = NULL, *cgroup = NULL;
|
char *id = NULL, *env = NULL, *cgroup = NULL;
|
||||||
@@ -2245,24 +2274,6 @@ svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
desc = strstr(line, "-- ");
|
|
||||||
if (desc) {
|
|
||||||
*desc = 0;
|
|
||||||
desc += 3;
|
|
||||||
|
|
||||||
while (*desc && isblank(*desc))
|
|
||||||
desc++;
|
|
||||||
} else {
|
|
||||||
int pos;
|
|
||||||
|
|
||||||
/* Find "--\n" to denote empty/no description */
|
|
||||||
pos = (int)strlen(line) - 2;
|
|
||||||
if (pos > 0 && !strcmp(&line[pos], "--")) {
|
|
||||||
line[pos] = 0;
|
|
||||||
desc = &line[pos];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args = line;
|
args = line;
|
||||||
cmd = parse_args(&args);
|
cmd = parse_args(&args);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
@@ -2272,6 +2283,10 @@ svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Options ending in ':' can be prefix matched, the rest must match
|
||||||
|
* the whole token or a command like logger is taken for one.
|
||||||
|
*/
|
||||||
while (cmd) {
|
while (cmd) {
|
||||||
char *arg;
|
char *arg;
|
||||||
|
|
||||||
@@ -2286,19 +2301,19 @@ svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
cond = &cmd[1];
|
cond = &cmd[1];
|
||||||
else if (cmd[0] == ':') /* :ID */
|
else if (cmd[0] == ':') /* :ID */
|
||||||
id = &cmd[1];
|
id = &cmd[1];
|
||||||
else if (MATCH_CMD(cmd, "log", arg))
|
else if (is_opt(cmd, "log", ":"))
|
||||||
log = cmd;
|
log = cmd;
|
||||||
else if (MATCH_CMD(cmd, "pid", arg))
|
else if (is_opt(cmd, "pid", ":"))
|
||||||
pid = cmd;
|
pid = cmd;
|
||||||
else if (MATCH_CMD(cmd, "name:", arg))
|
else if (MATCH_CMD(cmd, "name:", arg))
|
||||||
name = cmd;
|
name = cmd;
|
||||||
else if (MATCH_CMD(cmd, "notify:", arg))
|
else if (MATCH_CMD(cmd, "notify:", arg))
|
||||||
notify = arg;
|
notify = arg;
|
||||||
else if (MATCH_CMD(cmd, "type:forking", arg))
|
else if (!strcasecmp(cmd, "type:forking"))
|
||||||
forking = 1;
|
forking = 1;
|
||||||
else if (MATCH_CMD(cmd, "manual:yes", arg))
|
else if (!strcasecmp(cmd, "manual:yes"))
|
||||||
manual = 1;
|
manual = 1;
|
||||||
else if (MATCH_CMD(cmd, "remain:yes", arg))
|
else if (!strcasecmp(cmd, "remain:yes"))
|
||||||
remain = 1;
|
remain = 1;
|
||||||
else if (MATCH_CMD(cmd, "restart:", arg)) {
|
else if (MATCH_CMD(cmd, "restart:", arg)) {
|
||||||
if (MATCH_CMD(arg, "always", arg))
|
if (MATCH_CMD(arg, "always", arg))
|
||||||
@@ -2310,9 +2325,9 @@ svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
restart_tmo = atoi(arg) * 1000;
|
restart_tmo = atoi(arg) * 1000;
|
||||||
else if (MATCH_CMD(cmd, "restart_sec:", arg))
|
else if (MATCH_CMD(cmd, "restart_sec:", arg))
|
||||||
restart_tmo = atoi(arg) * 1000;
|
restart_tmo = atoi(arg) * 1000;
|
||||||
else if (MATCH_CMD(cmd, "norestart", arg))
|
else if (!strcasecmp(cmd, "norestart"))
|
||||||
restart_max = 0;
|
restart_max = 0;
|
||||||
else if (MATCH_CMD(cmd, "nowarn", arg))
|
else if (!strcasecmp(cmd, "nowarn"))
|
||||||
nowarn = 1;
|
nowarn = 1;
|
||||||
else if (MATCH_CMD(cmd, "oncrash:", arg)) {
|
else if (MATCH_CMD(cmd, "oncrash:", arg)) {
|
||||||
if (MATCH_CMD(arg, "reboot", arg))
|
if (MATCH_CMD(arg, "reboot", arg))
|
||||||
@@ -2320,7 +2335,7 @@ svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
if (MATCH_CMD(arg, "script", arg))
|
if (MATCH_CMD(arg, "script", arg))
|
||||||
oncrash_action = SVC_ONCRASH_SCRIPT;
|
oncrash_action = SVC_ONCRASH_SCRIPT;
|
||||||
}
|
}
|
||||||
else if (MATCH_CMD(cmd, "respawn", arg))
|
else if (!strcasecmp(cmd, "respawn"))
|
||||||
respawn = 1;
|
respawn = 1;
|
||||||
else if (MATCH_CMD(cmd, "halt:", arg))
|
else if (MATCH_CMD(cmd, "halt:", arg))
|
||||||
halt = arg;
|
halt = arg;
|
||||||
@@ -2345,7 +2360,7 @@ svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
else if (MATCH_CMD(cmd, "tty:", arg))
|
else if (MATCH_CMD(cmd, "tty:", arg))
|
||||||
ctty = arg;
|
ctty = arg;
|
||||||
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
|
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
|
||||||
else if (MATCH_CMD(cmd, "cgroup", arg))
|
else if ((arg = is_opt(cmd, "cgroup", ".:")))
|
||||||
cgroup = arg;
|
cgroup = arg;
|
||||||
else if (MATCH_CMD(cmd, "conflict:", arg))
|
else if (MATCH_CMD(cmd, "conflict:", arg))
|
||||||
conflict = arg;
|
conflict = arg;
|
||||||
@@ -2730,6 +2745,45 @@ svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_register - Register from a legacy one-liner
|
||||||
|
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||||
|
* @cfg: Configuration, complete command, with -- for description text
|
||||||
|
* @rlimit: Limits for this service/task/run, may be global limits
|
||||||
|
* @file: The file name service was loaded from
|
||||||
|
*
|
||||||
|
* The first "-- " in @cfg starts the description, the rest is handed
|
||||||
|
* to service_register_desc().
|
||||||
|
*/
|
||||||
|
svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||||
|
{
|
||||||
|
char *line, *desc;
|
||||||
|
|
||||||
|
if (!cfg)
|
||||||
|
return service_register_desc(type, cfg, NULL, rlimit, file);
|
||||||
|
|
||||||
|
line = strdupa(cfg);
|
||||||
|
desc = strstr(line, "-- ");
|
||||||
|
if (desc) {
|
||||||
|
*desc = 0;
|
||||||
|
desc += 3;
|
||||||
|
|
||||||
|
while (*desc && isblank(*desc))
|
||||||
|
desc++;
|
||||||
|
} else {
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
/* Find "--\n" to denote empty/no description */
|
||||||
|
pos = (int)strlen(line) - 2;
|
||||||
|
if (pos > 0 && !strcmp(&line[pos], "--")) {
|
||||||
|
line[pos] = 0;
|
||||||
|
desc = &line[pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return service_register_desc(type, line, desc, rlimit, file);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is called at the end of a runlevel change or reload
|
* This function is called at the end of a runlevel change or reload
|
||||||
* command to delete all remnants of removed services.
|
* command to delete all remnants of removed services.
|
||||||
@@ -3378,8 +3432,8 @@ restart:
|
|||||||
svc_set_state(svc, SVC_CLEANUP_STATE);
|
svc_set_state(svc, SVC_CLEANUP_STATE);
|
||||||
service_cleanup_script(svc);
|
service_cleanup_script(svc);
|
||||||
} else {
|
} else {
|
||||||
/* Unblock remain tasks after post script so they can restart */
|
/* Restart remain tasks after runlevel/reload/restart teardown. */
|
||||||
if (svc_is_remain(svc))
|
if (svc_is_remain(svc) && svc_is_restart(svc))
|
||||||
svc_unblock(svc);
|
svc_unblock(svc);
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
}
|
}
|
||||||
@@ -3398,13 +3452,17 @@ restart:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_DONE_STATE:
|
case SVC_DONE_STATE:
|
||||||
/* Remain tasks: always run post script when stopped, even if config changed */
|
/* Remain tasks run post script before stopping or restarting. */
|
||||||
if (svc_is_runtask(svc) && svc_is_remain(svc) && svc_is_stopped(svc)) {
|
if (svc_is_runtask(svc) && svc_is_remain(svc) &&
|
||||||
|
(svc_is_stopped(svc) || svc_is_restart(svc))) {
|
||||||
if (svc_has_post(svc)) {
|
if (svc_has_post(svc)) {
|
||||||
svc_set_state(svc, SVC_TEARDOWN_STATE);
|
svc_set_state(svc, SVC_TEARDOWN_STATE);
|
||||||
service_post_script(svc);
|
service_post_script(svc);
|
||||||
} else
|
} else {
|
||||||
|
if (svc_is_restart(svc))
|
||||||
|
svc_unblock(svc);
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
|
}
|
||||||
} else if (svc_is_changed(svc))
|
} else if (svc_is_changed(svc))
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
else if (svc_is_runtask(svc) && svc_is_manual(svc) && enabled)
|
else if (svc_is_runtask(svc) && svc_is_manual(svc) && enabled)
|
||||||
@@ -3727,7 +3785,7 @@ void service_runtask_clean(void)
|
|||||||
|
|
||||||
/* Config changed or leaving runlevel: stop, run post, restart */
|
/* Config changed or leaving runlevel: stop, run post, restart */
|
||||||
svc->once = 0;
|
svc->once = 0;
|
||||||
svc_stop(svc);
|
svc_restarting(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ extern const struct svcdir svcdirs[NUM_SVCDIRS];
|
|||||||
|
|
||||||
int service_set_dir (svc_t *svc, const struct svcdir *sd, const char *name);
|
int service_set_dir (svc_t *svc, const struct svcdir *sd, const char *name);
|
||||||
svc_t *service_register (int type, char *line, struct rlimit rlimit[], char *file);
|
svc_t *service_register (int type, char *line, struct rlimit rlimit[], char *file);
|
||||||
|
svc_t *service_register_desc (int type, char *line, const char *desc,
|
||||||
|
struct rlimit rlimit[], char *file);
|
||||||
void service_unregister (svc_t *svc);
|
void service_unregister (svc_t *svc);
|
||||||
|
|
||||||
void service_runtask_clean (void);
|
void service_runtask_clean (void);
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ EXTRA_DIST += pre-fail.sh
|
|||||||
EXTRA_DIST += process-depends.sh
|
EXTRA_DIST += process-depends.sh
|
||||||
EXTRA_DIST += rclocal.sh
|
EXTRA_DIST += rclocal.sh
|
||||||
EXTRA_DIST += ready-serv.sh
|
EXTRA_DIST += ready-serv.sh
|
||||||
|
EXTRA_DIST += remain-task-control.sh
|
||||||
EXTRA_DIST += restart-self.sh
|
EXTRA_DIST += restart-self.sh
|
||||||
EXTRA_DIST += runlevel.sh
|
EXTRA_DIST += runlevel.sh
|
||||||
EXTRA_DIST += run-restart-forever.sh
|
EXTRA_DIST += run-restart-forever.sh
|
||||||
@@ -129,6 +130,7 @@ TESTS += pre-fail.sh
|
|||||||
TESTS += process-depends.sh
|
TESTS += process-depends.sh
|
||||||
TESTS += rclocal.sh
|
TESTS += rclocal.sh
|
||||||
TESTS += ready-serv.sh
|
TESTS += ready-serv.sh
|
||||||
|
TESTS += remain-task-control.sh
|
||||||
TESTS += restart-self.sh
|
TESTS += restart-self.sh
|
||||||
TESTS += runlevel.sh
|
TESTS += runlevel.sh
|
||||||
TESTS += sysvparts.sh
|
TESTS += sysvparts.sh
|
||||||
|
|||||||
+24
-1
@@ -76,6 +76,29 @@ retry 'assert_cmd pick "serv -np -i pick"'
|
|||||||
assert_desc "Single command" pick
|
assert_desc "Single command" pick
|
||||||
assert_num_children 1 serv
|
assert_num_children 1 serv
|
||||||
|
|
||||||
|
# The description has its own setting, so a -- in the command is an
|
||||||
|
# argument to it, see issue #512.
|
||||||
|
say 'A -- in the command is passed on to it'
|
||||||
|
run "echo 'service pick {' > $FINIT_CONF"
|
||||||
|
run "echo ' description = \"Dashes\"' >> $FINIT_CONF"
|
||||||
|
run "echo ' command = \"serv -np -i pick -- extra\"' >> $FINIT_CONF"
|
||||||
|
run "echo '}' >> $FINIT_CONF"
|
||||||
|
run "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_cmd pick "serv -np -i pick -- extra"'
|
||||||
|
assert_desc "Dashes" pick
|
||||||
|
assert_num_children 1 serv
|
||||||
|
|
||||||
|
# Options are matched as whole tokens, so a command that merely starts
|
||||||
|
# like one, pidof vs. pid, is still the command.
|
||||||
|
say 'A command named like an option is the command'
|
||||||
|
run "echo 'task lookalike {' > $FINIT_CONF"
|
||||||
|
run "echo ' command = \"pidof serv\"' >> $FINIT_CONF"
|
||||||
|
run "echo '}' >> $FINIT_CONF"
|
||||||
|
run "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_cmd lookalike "pidof serv"'
|
||||||
|
|
||||||
# A tty block takes the same list, its command is the getty to run.
|
# A tty block takes the same list, its command is the getty to run.
|
||||||
# Asserting it is loaded is enough here, a getty in the test namespace
|
# Asserting it is loaded is enough here, a getty in the test namespace
|
||||||
# has no terminal to attach to.
|
# has no terminal to attach to.
|
||||||
@@ -90,7 +113,7 @@ retry 'assert_cmd tty "serv -np -i ttypick"'
|
|||||||
|
|
||||||
# Nothing resolves, so the block is skipped the same way a single
|
# Nothing resolves, so the block is skipped the same way a single
|
||||||
# missing command is. The leading - only decides whether that is
|
# missing command is. The leading - only decides whether that is
|
||||||
# quiet, service_register() bails before svc_new() either way.
|
# quiet, service_register_desc() bails before svc_new() either way.
|
||||||
say 'A block whose candidates are all missing is skipped'
|
say 'A block whose candidates are all missing is skipped'
|
||||||
run "echo 'service ghost {' > $FINIT_CONF"
|
run "echo 'service ghost {' > $FINIT_CONF"
|
||||||
run "echo ' command = { \"-/no/such/one\", \"-/no/such/two\" }' >> $FINIT_CONF"
|
run "echo ' command = { \"-/no/such/one\", \"-/no/such/two\" }' >> $FINIT_CONF"
|
||||||
|
|||||||
Executable
+76
@@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")
|
||||||
|
TEST_CONF=/etc/finit.d/remain-task.conf
|
||||||
|
STATE=/run/remain-task.state
|
||||||
|
|
||||||
|
test_teardown()
|
||||||
|
{
|
||||||
|
run "rm -f $TEST_CONF $STATE"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_runs()
|
||||||
|
{
|
||||||
|
assert "Remain task has run $1 time(s)" \
|
||||||
|
"$(texec initctl -j status remain-task | jq -M .restarts)" -eq "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib/setup.sh"
|
||||||
|
|
||||||
|
say 'Add remain task'
|
||||||
|
run "cat > $TEST_CONF" <<EOF
|
||||||
|
task remain-task {
|
||||||
|
description = "Remain task control"
|
||||||
|
runlevel = "2345"
|
||||||
|
remain-after-exit = true
|
||||||
|
command = "/bin/touch $STATE"
|
||||||
|
exec-stop-post = "/bin/rm -f $STATE"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
run "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_status remain-task done'
|
||||||
|
assert_file_exists "$STATE"
|
||||||
|
assert_runs 1
|
||||||
|
|
||||||
|
say 'Stop remain task'
|
||||||
|
run "initctl stop remain-task"
|
||||||
|
retry 'assert_status remain-task stopped'
|
||||||
|
assert "Post script removed state" \
|
||||||
|
"$(texec sh -c "[ ! -e '$STATE' ] && echo yes")" = yes
|
||||||
|
assert_runs 1
|
||||||
|
|
||||||
|
say 'Start remain task'
|
||||||
|
run "initctl start remain-task"
|
||||||
|
retry 'assert_status remain-task done'
|
||||||
|
assert_file_exists "$STATE"
|
||||||
|
assert_runs 2
|
||||||
|
|
||||||
|
say 'Restart remain task'
|
||||||
|
run "initctl restart remain-task"
|
||||||
|
retry 'assert_runs 3'
|
||||||
|
retry 'assert_status remain-task done'
|
||||||
|
assert_file_exists "$STATE"
|
||||||
|
|
||||||
|
say 'Reload changed remain task'
|
||||||
|
run "sed -i 's/Remain task control/Updated remain task control/' $TEST_CONF"
|
||||||
|
run "initctl reload"
|
||||||
|
retry 'assert_runs 4'
|
||||||
|
retry 'assert_status remain-task done'
|
||||||
|
assert_file_exists "$STATE"
|
||||||
|
|
||||||
|
say 'Leave remain task runlevel'
|
||||||
|
run "initctl runlevel 1"
|
||||||
|
retry 'assert_status remain-task halted'
|
||||||
|
assert "Post script removed state" \
|
||||||
|
"$(texec sh -c "[ ! -e '$STATE' ] && echo yes")" = yes
|
||||||
|
assert_runs 4
|
||||||
|
|
||||||
|
say 'Re-enter remain task runlevel'
|
||||||
|
run "initctl runlevel 2"
|
||||||
|
retry 'assert_runs 5'
|
||||||
|
retry 'assert_status remain-task done'
|
||||||
|
assert_file_exists "$STATE"
|
||||||
Reference in new issue
Block a user