mirror of
https://github.com/troglobit/finit.git
synced 2026-11-01 13:10:32 +00:00
Merge branch 'wip-tests' into wip-tests-build-refactor
Signed-off-by: Jacques de Laval <jacques@de-laval.se>
This commit is contained in:
commit
1efdbeda16
28 files changed
+703
-221
No files matched your search
@@ -167,6 +167,8 @@ static void setup(void *arg)
|
|||||||
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
||||||
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
|
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
|
||||||
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
|
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
|
||||||
|
makedir("/var/run/frr", 0755); /* Needed by FRR */
|
||||||
|
makedir("/var/log/frr", 0755); /* Needed by FRR */
|
||||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||||
|
|
||||||
gid = getgroup("tty");
|
gid = getgroup("tty");
|
||||||
|
|||||||
+49
-116
@@ -22,11 +22,10 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <fnmatch.h>
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <lite/queue.h>
|
|
||||||
#include <sys/inotify.h>
|
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
@@ -34,28 +33,15 @@
|
|||||||
#include "pid.h"
|
#include "pid.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
#include "iwatch.h"
|
||||||
|
|
||||||
struct wd_entry {
|
static struct iwatch iw_pidfile;
|
||||||
TAILQ_ENTRY(wd_entry) link;
|
|
||||||
char *path;
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct context {
|
|
||||||
int fd;
|
|
||||||
TAILQ_HEAD(, wd_entry) wd_list;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct context pidfile_ctx;
|
static int pidfile_add_path(struct iwatch *iw, char *path)
|
||||||
|
|
||||||
static int watcher_add(struct context *ctx, char *path)
|
|
||||||
{
|
{
|
||||||
struct wd_entry *wd;
|
|
||||||
uint32_t mask = IN_ONLYDIR | IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO;
|
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int fd;
|
|
||||||
|
|
||||||
_d("pidfile: Adding new watcher for path %s", path);
|
|
||||||
ptr = strstr(path, "run/");
|
ptr = strstr(path, "run/");
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
char *slash;
|
char *slash;
|
||||||
@@ -66,44 +52,16 @@ static int watcher_add(struct context *ctx, char *path)
|
|||||||
ptr = slash++;
|
ptr = slash++;
|
||||||
slash = strchr(ptr, '/');
|
slash = strchr(ptr, '/');
|
||||||
if (slash) {
|
if (slash) {
|
||||||
_d("pidfile: Path too deep, skipping.");
|
_d("Path too deep, skipping.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = inotify_add_watch(ctx->fd, path, mask);
|
return iwatch_add(iw, path, IN_ONLYDIR);
|
||||||
if (fd < 0) {
|
|
||||||
_pe("inotify_add_watch()");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
wd = malloc(sizeof(struct wd_entry));
|
|
||||||
if (!wd) {
|
|
||||||
_pe("Failed allocating new `struct wd_entry`");
|
|
||||||
inotify_rm_watch(ctx->fd, fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
wd->path = path;
|
|
||||||
wd->fd = fd;
|
|
||||||
TAILQ_INSERT_HEAD(&ctx->wd_list, wd, link);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int watcher_del(struct context *ctx, struct wd_entry *wde)
|
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||||
{
|
|
||||||
_d("pidfile: Removing watcher for removed path %s", wde->path);
|
|
||||||
TAILQ_REMOVE(&ctx->wd_list, wde, link);
|
|
||||||
inotify_rm_watch(ctx->fd, wde->fd);
|
|
||||||
free(wde->path);
|
|
||||||
free(wde);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_conds(char *dir, char *name, uint32_t mask)
|
|
||||||
{
|
{
|
||||||
char cond[MAX_COND_LEN];
|
char cond[MAX_COND_LEN];
|
||||||
char fn[PATH_MAX];
|
char fn[PATH_MAX];
|
||||||
@@ -112,13 +70,16 @@ static void update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
_d("Got dir: %s, name: %s, mask: %08x", dir, name, mask);
|
_d("Got dir: %s, name: %s, mask: %08x", dir, name, mask);
|
||||||
snprintf(fn, sizeof(fn), "%s/%s", dir, name);
|
snprintf(fn, sizeof(fn), "%s/%s", dir, name);
|
||||||
|
|
||||||
|
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
svc = svc_find_by_pidfile(fn);
|
svc = svc_find_by_pidfile(fn);
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_d("pidfile: No matching svc for %s", fn);
|
_d("No matching svc for %s", fn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("pidfile: Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
||||||
|
|
||||||
mkcond(svc, cond, sizeof(cond));
|
mkcond(svc, cond, sizeof(cond));
|
||||||
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||||
@@ -138,7 +99,7 @@ static void update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* synthesize events in case of new run dirs */
|
/* synthesize events in case of new run dirs */
|
||||||
static void scan_for_pidfiles(struct context *ctx, char *dir, int len)
|
static void pidfile_scandir(struct iwatch *iw, char *dir, int len)
|
||||||
{
|
{
|
||||||
glob_t gl;
|
glob_t gl;
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -157,47 +118,33 @@ static void scan_for_pidfiles(struct context *ctx, char *dir, int len)
|
|||||||
|
|
||||||
for (i = 0; i < gl.gl_pathc; i++) {
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
_d("scan found %s", gl.gl_pathv[i]);
|
_d("scan found %s", gl.gl_pathv[i]);
|
||||||
update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||||
}
|
}
|
||||||
globfree(&gl);
|
globfree(&gl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_dir(struct context *ctx, struct wd_entry *wde, char *name, int mask)
|
/*
|
||||||
|
* create/remove sub-directory in monitored directory
|
||||||
|
*/
|
||||||
|
static void pidfile_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||||
{
|
{
|
||||||
char *path;
|
char path[strlen(dir) + strlen(name) + 2];
|
||||||
int plen;
|
struct iwatch_path *iwp;
|
||||||
int exist = 0;
|
|
||||||
|
|
||||||
plen = snprintf(NULL, 0, "%s/%s", wde->path, name) + 1;
|
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||||
path = malloc(plen);
|
_d("path: %s", path);
|
||||||
if (!path) {
|
|
||||||
_pe("Failed allocating path buffer for watcher");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
snprintf(path, plen, "%s/%s", wde->path, name);
|
|
||||||
_d("pidfile: path is %s", path);
|
|
||||||
|
|
||||||
TAILQ_FOREACH(wde, &ctx->wd_list, link) {
|
iwp = iwatch_find_by_path(iw, path);
|
||||||
if (!strcmp(wde->path, path)) {
|
|
||||||
exist = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask & IN_CREATE) {
|
if (mask & IN_CREATE) {
|
||||||
if (!exist) {
|
if (!iwp) {
|
||||||
int rc = watcher_add(ctx, path);
|
pidfile_add_path(iw, path);
|
||||||
|
pidfile_scandir(iw, path, sizeof(path));
|
||||||
scan_for_pidfiles(ctx, path, plen);
|
|
||||||
if (!rc)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else if (mask & IN_DELETE) {
|
} else if (mask & IN_DELETE) {
|
||||||
if (exist)
|
if (iwp)
|
||||||
watcher_del(&pidfile_ctx, wde);
|
iwatch_del(&iw_pidfile, iwp);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pidfile_callback(void *arg, int fd, int events)
|
static void pidfile_callback(void *arg, int fd, int events)
|
||||||
@@ -213,43 +160,42 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("pidfile: Entering ... reading %zu bytes into ev_buf[]", buflen - 1);
|
_d("Entering ... reading %zu bytes into ev_buf[]", buflen - 1);
|
||||||
sz = read(fd, buf, buflen - 1);
|
sz = read(fd, buf, buflen - 1);
|
||||||
if (sz <= 0) {
|
if (sz <= 0) {
|
||||||
_pe("invalid inotify event");
|
_pe("invalid inotify event");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
buf[sz] = 0;
|
buf[sz] = 0;
|
||||||
_d("pidfile: Read %zd bytes, processing ...", sz);
|
_d("Read %zd bytes, processing ...", sz);
|
||||||
|
|
||||||
off = 0;
|
off = 0;
|
||||||
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
|
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
|
||||||
struct wd_entry *wde;
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
ev = (struct inotify_event *)&buf[off];
|
ev = (struct inotify_event *)&buf[off];
|
||||||
|
|
||||||
_d("pidfile: path %s, event: 0x%08x", ev->name, ev->mask);
|
_d("path %s, event: 0x%08x", ev->name, ev->mask);
|
||||||
if (!ev->mask)
|
if (!ev->mask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Find base path for this event */
|
/* Find base path for this event */
|
||||||
TAILQ_FOREACH(wde, &pidfile_ctx.wd_list, link) {
|
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
|
||||||
if (wde->fd == ev->wd)
|
if (!iwp)
|
||||||
break;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (ev->mask & IN_ISDIR) {
|
if (ev->mask & IN_ISDIR) {
|
||||||
handle_dir(&pidfile_ctx, wde, ev->name, ev->mask);
|
pidfile_handle_dir(&iw_pidfile, iwp->path, ev->name, ev->mask);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev->mask & IN_DELETE) {
|
if (ev->mask & IN_DELETE) {
|
||||||
_d("pidfile %s/%s removed ...", wde->path, ev->name);
|
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||||
update_conds(wde->path, ev->name, ev->mask);
|
pidfile_update_conds(iwp->path, ev->name, ev->mask);
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
free(buf);
|
free(buf);
|
||||||
@@ -305,14 +251,10 @@ static void pidfile_init(void *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TAILQ_INIT(&pidfile_ctx.wd_list);
|
if (pidfile_add_path(&iw_pidfile, path))
|
||||||
if (watcher_add(&pidfile_ctx, path))
|
iwatch_exit(&iw_pidfile);
|
||||||
close(pidfile_ctx.fd);
|
|
||||||
_d("pidfile monitor active");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct context pidfile_ctx;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When performing an `initctl reload` with one (unchanged) service
|
* When performing an `initctl reload` with one (unchanged) service
|
||||||
* depending on, e.g. `net/iface/lo`, its condition will not be set
|
* depending on, e.g. `net/iface/lo`, its condition will not be set
|
||||||
@@ -331,36 +273,27 @@ static plugin_t plugin = {
|
|||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
|
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
|
||||||
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
||||||
.io = {
|
|
||||||
.cb = pidfile_callback,
|
|
||||||
.flags = PLUGIN_IO_READ,
|
|
||||||
},
|
|
||||||
.depends = { "netlink" },
|
.depends = { "netlink" },
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
{
|
{
|
||||||
pidfile_ctx.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
int fd;
|
||||||
if (pidfile_ctx.fd < 0) {
|
|
||||||
_pe("inotify_init()");
|
fd = iwatch_init(&iw_pidfile);
|
||||||
|
if (fd < 0)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
plugin.io.fd = fd;
|
||||||
|
plugin.io.cb = pidfile_callback;
|
||||||
|
plugin.io.flags = PLUGIN_IO_READ;
|
||||||
|
|
||||||
plugin.io.fd = pidfile_ctx.fd;
|
|
||||||
plugin_register(&plugin);
|
plugin_register(&plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
PLUGIN_EXIT(plugin_exit)
|
PLUGIN_EXIT(plugin_exit)
|
||||||
{
|
{
|
||||||
struct wd_entry *wde, *tmp;
|
iwatch_exit(&iw_pidfile);
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(wde, &pidfile_ctx.wd_list, link, tmp) {
|
|
||||||
inotify_rm_watch(pidfile_ctx.fd, wde->fd);
|
|
||||||
free(wde->path);
|
|
||||||
free(wde);
|
|
||||||
}
|
|
||||||
close(pidfile_ctx.fd);
|
|
||||||
|
|
||||||
plugin_unregister(&plugin);
|
plugin_unregister(&plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
|
|||||||
exec.c finit.c finit.h \
|
exec.c finit.c finit.h \
|
||||||
getty.c stty.c \
|
getty.c stty.c \
|
||||||
helpers.c helpers.h \
|
helpers.c helpers.h \
|
||||||
|
iwatch.c iwatch.h \
|
||||||
log.c log.h \
|
log.c log.h \
|
||||||
mdadm.c mount.c \
|
mdadm.c mount.c \
|
||||||
pid.c pid.h \
|
pid.c pid.h \
|
||||||
|
|||||||
@@ -494,6 +494,7 @@ static int show_status(char *arg)
|
|||||||
printf("Service : %s\n", svc->cmd);
|
printf("Service : %s\n", svc->cmd);
|
||||||
printf("Description : %s\n", svc->desc);
|
printf("Description : %s\n", svc->desc);
|
||||||
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
|
||||||
|
printf("Environment : %s\n", svc->env);
|
||||||
printf("PID : %d\n", svc->pid);
|
printf("PID : %d\n", svc->pid);
|
||||||
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
|
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
|
||||||
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||||
|
|||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
/* inotify watcher for files or directories
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
|
* Copyright (c) 2016-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 <errno.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "iwatch.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iwatch is initialized and used mainly by the pidfile plugin, which is
|
||||||
|
* one of the cornerstones in the condition subsystem. Other parts of
|
||||||
|
* Finit may use iwatch too, like env: watchers, but are disabled if the
|
||||||
|
* pidfile plugin hasn't called iwatch_init() -- this is by design.
|
||||||
|
*/
|
||||||
|
static int initialized = 0;
|
||||||
|
|
||||||
|
|
||||||
|
int iwatch_init(struct iwatch *iw)
|
||||||
|
{
|
||||||
|
if (!iw) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_INIT(&iw->iwp_list);
|
||||||
|
|
||||||
|
iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
|
if (iw->fd < 0) {
|
||||||
|
_pe("inotify_init()");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
initialized = 1;
|
||||||
|
|
||||||
|
return iw->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iwatch_exit(struct iwatch *iw)
|
||||||
|
{
|
||||||
|
struct iwatch_path *iwp, *tmp;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(iwp, &iw->iwp_list, link, tmp) {
|
||||||
|
TAILQ_REMOVE(&iw->iwp_list, iwp, link);
|
||||||
|
inotify_rm_watch(iw->fd, iwp->wd);
|
||||||
|
free(iwp->path);
|
||||||
|
free(iwp);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(iw->fd);
|
||||||
|
initialized = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||||
|
{
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
char *path;
|
||||||
|
int wd;
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
_d("Adding new watcher for path %s", file);
|
||||||
|
|
||||||
|
path = strdup(file);
|
||||||
|
if (!path) {
|
||||||
|
_pe("Out of memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
|
||||||
|
if (wd < 0) {
|
||||||
|
_pe("inotify_add_watch(%s)", path);
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iwp = malloc(sizeof(struct iwatch_path));
|
||||||
|
if (!iwp) {
|
||||||
|
_pe("Failed allocating new `struct iwatch_path`");
|
||||||
|
inotify_rm_watch(iw->fd, wd);
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iwp->path = path;
|
||||||
|
iwp->wd = wd;
|
||||||
|
TAILQ_INSERT_HEAD(&iw->iwp_list, iwp, link);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp)
|
||||||
|
{
|
||||||
|
if (!initialized)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
_d("Removing watcher for removed path %s", iwp->path);
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&iw->iwp_list, iwp, link);
|
||||||
|
inotify_rm_watch(iw->fd, iwp->wd);
|
||||||
|
free(iwp->path);
|
||||||
|
free(iwp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iwatch_path *iwatch_find_by_wd(struct iwatch *iw, int wd)
|
||||||
|
{
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(iwp, &iw->iwp_list, link) {
|
||||||
|
if (iwp->wd == wd)
|
||||||
|
return iwp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iwatch_path *iwatch_find_by_path(struct iwatch *iw, const char *path)
|
||||||
|
{
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(iwp, &iw->iwp_list, link) {
|
||||||
|
if (!strcmp(iwp->path, path))
|
||||||
|
return iwp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/* inotify watcher for files or directories
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
|
* Copyright (c) 2016-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_IWATCH_H_
|
||||||
|
#define FINIT_IWATCH_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lite/queue.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Monitors changes to both directories and files by default */
|
||||||
|
#define IWATCH_MASK (IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE)
|
||||||
|
|
||||||
|
struct iwatch_path {
|
||||||
|
TAILQ_ENTRY(iwatch_path) link;
|
||||||
|
char *path;
|
||||||
|
int wd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iwatch {
|
||||||
|
int fd;
|
||||||
|
TAILQ_HEAD(, iwatch_path) iwp_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int iwatch_init (struct iwatch *iw);
|
||||||
|
void iwatch_exit (struct iwatch *iw);
|
||||||
|
|
||||||
|
int iwatch_add (struct iwatch *iw, char *file, uint32_t mask);
|
||||||
|
int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp);
|
||||||
|
|
||||||
|
struct iwatch_path *iwatch_find_by_wd (struct iwatch *iw, int wd);
|
||||||
|
struct iwatch_path *iwatch_find_by_path (struct iwatch *iw, const char *path);
|
||||||
|
|
||||||
|
#endif /* FINIT_IWATCH_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+185
-13
@@ -32,6 +32,7 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
#include <wordexp.h>
|
||||||
|
|
||||||
#include "cgroup.h"
|
#include "cgroup.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
@@ -261,6 +262,121 @@ static int redirect(svc_t *svc)
|
|||||||
return 0;
|
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
|
||||||
|
*/
|
||||||
|
static void source_env(svc_t *svc)
|
||||||
|
{
|
||||||
|
char buf[LINE_SIZE];
|
||||||
|
char *line;
|
||||||
|
size_t len;
|
||||||
|
FILE *fp;
|
||||||
|
char *fn;
|
||||||
|
|
||||||
|
fn = svc_getenv(svc);
|
||||||
|
if (!fn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Warning in service_start() after check_env() */
|
||||||
|
fp = fopen(fn, "r");
|
||||||
|
if (!fp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
line = buf;
|
||||||
|
len = sizeof(buf);
|
||||||
|
while (fgets(line, len, fp)) {
|
||||||
|
char *key = chomp(line);
|
||||||
|
char *value, *end;
|
||||||
|
|
||||||
|
/* skip any leading whitespace */
|
||||||
|
while (isspace(*key))
|
||||||
|
key++;
|
||||||
|
|
||||||
|
/* skip comments */
|
||||||
|
if (*key == '#' || *key == ';')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* find end of line */
|
||||||
|
end = key;
|
||||||
|
while (*end)
|
||||||
|
end++;
|
||||||
|
|
||||||
|
/* strip trailing whitespace */
|
||||||
|
if (end > key) {
|
||||||
|
end--;
|
||||||
|
while (isspace(*end))
|
||||||
|
*end-- = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = strchr(key, '=');
|
||||||
|
if (!value)
|
||||||
|
continue;
|
||||||
|
*value++ = 0;
|
||||||
|
|
||||||
|
/* strip leading whitespace from value */
|
||||||
|
while (isspace(*value))
|
||||||
|
value++;
|
||||||
|
|
||||||
|
/* unquote value, if quoted */
|
||||||
|
if (value[0] == '"' || value[0] == '\'') {
|
||||||
|
char q = value[0];
|
||||||
|
|
||||||
|
if (*end == q) {
|
||||||
|
value = &value[1];
|
||||||
|
*end = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find end of key */
|
||||||
|
end = key;
|
||||||
|
while (*end)
|
||||||
|
end++;
|
||||||
|
|
||||||
|
/* strip trailing whitespace */
|
||||||
|
if (end > key) {
|
||||||
|
end--;
|
||||||
|
while (isspace(*end))
|
||||||
|
*end-- = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setenv(key, value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
static int is_norespawn(void)
|
static int is_norespawn(void)
|
||||||
{
|
{
|
||||||
return sig_stopped() ||
|
return sig_stopped() ||
|
||||||
@@ -277,9 +393,10 @@ static int is_norespawn(void)
|
|||||||
*/
|
*/
|
||||||
static int service_start(svc_t *svc)
|
static int service_start(svc_t *svc)
|
||||||
{
|
{
|
||||||
int i, result = 0, do_progress = 1;
|
int result = 0, do_progress = 1;
|
||||||
pid_t pid;
|
|
||||||
sigset_t nmask, omask;
|
sigset_t nmask, omask;
|
||||||
|
pid_t pid;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -290,11 +407,17 @@ static int service_start(svc_t *svc)
|
|||||||
|
|
||||||
/* Don't try and start service if it doesn't exist. */
|
/* Don't try and start service if it doesn't exist. */
|
||||||
if (!whichp(svc->cmd)) {
|
if (!whichp(svc->cmd)) {
|
||||||
print(1, "Service %s does not exist", svc->cmd);
|
print(1, "%s: does not exist", svc->cmd);
|
||||||
svc_missing(svc);
|
svc_missing(svc);
|
||||||
return 1;
|
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);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (svc_is_sysv(svc))
|
if (svc_is_sysv(svc))
|
||||||
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
|
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
|
||||||
|
|
||||||
@@ -355,12 +478,45 @@ static int service_start(svc_t *svc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Source any environment from env:/path/to/file */
|
||||||
|
source_env(svc);
|
||||||
|
|
||||||
if (!svc_is_sysv(svc)) {
|
if (!svc_is_sysv(svc)) {
|
||||||
|
wordexp_t we = { 0 };
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if ((rc = wordexp(svc->cmd, &we, 0))) {
|
||||||
|
nomem:
|
||||||
|
_e("%s: failed wordexp(): %d", svc->cmd, rc);
|
||||||
|
wordfree(&we);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
||||||
if (strlen(svc->args[i]) == 0)
|
if (strlen(svc->args[i]) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if ((rc = wordexp(svc->args[i], &we, WRDE_APPEND)))
|
||||||
|
goto nomem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (we.we_wordc > MAX_NUM_SVC_ARGS) {
|
||||||
|
logit(LOG_ERR, "%s: too man args after expansion.", svc->cmd);
|
||||||
|
goto nomem;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < we.we_wordc; i++) {
|
||||||
|
if (strlen(we.we_wordv[i]) >= sizeof(svc->args[i])) {
|
||||||
|
logit(LOG_ERR, "%s: expanded arg. '%s' too long", we.we_wordv[i]);
|
||||||
|
rc = WRDE_NOSPACE;
|
||||||
|
goto nomem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overwrite the child's svc with expanded args */
|
||||||
|
strlcpy(svc->args[i], we.we_wordv[i], sizeof(svc->args[i]));
|
||||||
args[i] = svc->args[i];
|
args[i] = svc->args[i];
|
||||||
}
|
}
|
||||||
|
wordfree(&we);
|
||||||
} else {
|
} else {
|
||||||
i = 0;
|
i = 0;
|
||||||
args[i++] = svc->cmd;
|
args[i++] = svc->cmd;
|
||||||
@@ -372,12 +528,11 @@ static int service_start(svc_t *svc)
|
|||||||
sig_unblock();
|
sig_unblock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The setsid() call is the most humble of all in this
|
* The setsid() call takes care to detach the process
|
||||||
* function. It takes care to detach the process from
|
* from its controlling terminal, preventing daemons
|
||||||
* its controlling terminal, preventing daemons from
|
* from leaking to the console, and allowing us to run
|
||||||
* leaking to the console, and allowing us to run such
|
* such programs like `lxc-start -F` in the foreground
|
||||||
* programs like `lxc-start -F` in the foreground to
|
* to properly monitor them.
|
||||||
* properly monitor them.
|
|
||||||
*
|
*
|
||||||
* If you find yourself here wanting to fix the output
|
* If you find yourself here wanting to fix the output
|
||||||
* to the console at boot, for debugging or similar,
|
* to the console at boot, for debugging or similar,
|
||||||
@@ -386,9 +541,9 @@ static int service_start(svc_t *svc)
|
|||||||
setsid();
|
setsid();
|
||||||
|
|
||||||
if (svc_is_runtask(svc))
|
if (svc_is_runtask(svc))
|
||||||
status = exec_runtask(svc->cmd, args);
|
status = exec_runtask(args[0], &args[1]);
|
||||||
else
|
else
|
||||||
status = execvp(svc->cmd, args);
|
status = execvp(args[0], &args[1]);
|
||||||
|
|
||||||
_exit(status);
|
_exit(status);
|
||||||
} else if (debug) {
|
} else if (debug) {
|
||||||
@@ -396,7 +551,7 @@ static int service_start(svc_t *svc)
|
|||||||
|
|
||||||
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
||||||
if (strlen(svc->args[i]) == 0)
|
if (strlen(svc->args[i]) == 0)
|
||||||
continue;
|
break;
|
||||||
if (buf[0])
|
if (buf[0])
|
||||||
strlcat(buf, " ", sizeof(buf));
|
strlcat(buf, " ", sizeof(buf));
|
||||||
strlcat(buf, svc->args[i], sizeof(buf));
|
strlcat(buf, svc->args[i], sizeof(buf));
|
||||||
@@ -661,6 +816,19 @@ static void parse_log(svc_t *svc, char *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_env(svc_t *svc, char *env)
|
||||||
|
{
|
||||||
|
if (!env)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (strlen(env) >= sizeof(svc->env)) {
|
||||||
|
_e("%s: env file is too long (>%d chars)", svc->cmd, sizeof(svc->env));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(svc->env, env, sizeof(svc->env));
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_sighalt(svc_t *svc, char *arg)
|
static void parse_sighalt(svc_t *svc, char *arg)
|
||||||
{
|
{
|
||||||
int signo;
|
int signo;
|
||||||
@@ -798,7 +966,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
char *cmd, *desc, *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;
|
char *id = NULL, *env = NULL;
|
||||||
int levels = 0;
|
int levels = 0;
|
||||||
int manual = 0;
|
int manual = 0;
|
||||||
char *line;
|
char *line;
|
||||||
@@ -860,6 +1028,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
halt = &cmd[5];
|
halt = &cmd[5];
|
||||||
else if (!strncasecmp(cmd, "kill:", 5))
|
else if (!strncasecmp(cmd, "kill:", 5))
|
||||||
delay = &cmd[5];
|
delay = &cmd[5];
|
||||||
|
else if (!strncasecmp(cmd, "env:", 4))
|
||||||
|
env = &cmd[4];
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -925,6 +1095,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
parse_log(svc, log);
|
parse_log(svc, log);
|
||||||
if (desc)
|
if (desc)
|
||||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||||
|
if (env)
|
||||||
|
parse_env(svc, env);
|
||||||
|
|
||||||
/* Set configured limits */
|
/* Set configured limits */
|
||||||
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ svc_t *svc_find_by_nameid(char *name, char *id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_find_by_plidfile - Find an service object by its PID file
|
* svc_find_by_plidfile - Find a service by its PID file
|
||||||
* @fn: PID file, can be absolute path or relative to /run
|
* @fn: PID file, can be absolute path or relative to /run
|
||||||
*
|
*
|
||||||
* This function is primarily used by the pidfile plugin to track the
|
* This function is primarily used by the pidfile plugin to track the
|
||||||
@@ -412,32 +412,13 @@ svc_t *svc_find_by_nameid(char *name, char *id)
|
|||||||
svc_t *svc_find_by_pidfile(char *fn)
|
svc_t *svc_find_by_pidfile(char *fn)
|
||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
char adjpath[strlen(fn) + 10]; /* + sizeof("/var/run/") */
|
pid_t pid;
|
||||||
char fnpath[PATH_MAX];
|
|
||||||
|
|
||||||
pid_runpath(fn, adjpath, sizeof(adjpath));
|
pid = pid_file_read(fn);
|
||||||
if (!realpath(adjpath, fnpath)) {
|
if (pid == -1)
|
||||||
if (errno != ENOENT)
|
|
||||||
_pe("adjpath: %s, errno %d", adjpath, errno);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
char path[PATH_MAX];
|
|
||||||
char *pidfn;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
pidfn = pid_file(svc);
|
|
||||||
if (!pidfn || !realpath(pidfn, path)) {
|
|
||||||
if (errno != ENOENT)
|
|
||||||
_pe("pidfn: %s, errno %d", pidfn ?: "<NULL>", errno);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string_compare(fnpath, path))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
pid = pid_file_read(path);
|
|
||||||
if (svc->pid != pid)
|
if (svc->pid != pid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ typedef struct svc {
|
|||||||
char cmd[MAX_ARG_LEN];
|
char cmd[MAX_ARG_LEN];
|
||||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
||||||
char desc[MAX_STR_LEN];
|
char desc[MAX_STR_LEN];
|
||||||
|
char env[MAX_ARG_LEN];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to forcefully kill services that won't shutdown on
|
* Used to forcefully kill services that won't shutdown on
|
||||||
|
|||||||
+15
-10
@@ -1,19 +1,24 @@
|
|||||||
EXTRA_DIST = lib.sh testenv_exec.sh testenv_start.sh
|
EXTRA_DIST = lib.sh testenv_exec.sh testenv_start.sh
|
||||||
EXTRA_DIST += test_root/Makefile test_root/busybox-x86_64.md5
|
EXTRA_DIST += test-root/Makefile test-root/busybox-x86_64.md5
|
||||||
EXTRA_DIST += test_root/bin/chrootsetup.sh
|
EXTRA_DIST += test-root/bin/chrootsetup.sh
|
||||||
|
EXTRA_DIST += setup-root.sh teardown-root.sh
|
||||||
|
EXTRA_DIST += common/service.conf common/service.sh
|
||||||
EXTRA_DIST += checkself.sh
|
EXTRA_DIST += checkself.sh
|
||||||
EXTRA_DIST += setup_root.sh
|
EXTRA_DIST += add-remove-dynamic-service/test.sh
|
||||||
EXTRA_DIST += teardown_root.sh
|
EXTRA_DIST += add-remove-dynamic-service-sub-config/test.sh
|
||||||
EXTRA_DIST += test_start_stop_service/test.sh
|
EXTRA_DIST += start-stop-service/test.sh
|
||||||
EXTRA_DIST += test_start_stop_service/service.sh
|
EXTRA_DIST += start-stop-service-sub-config/test.sh
|
||||||
|
|
||||||
AM_TESTS_ENVIRONMENT = TESTS_ROOT='$(builddir)/test_root/';
|
AM_TESTS_ENVIRONMENT = TESTS_ROOT='$(abs_builddir)/test-root/';
|
||||||
AM_TESTS_ENVIRONMENT += export TESTS_ROOT;
|
AM_TESTS_ENVIRONMENT += export TESTS_ROOT;
|
||||||
AM_TESTS_ENVIRONMENT += export LD_LIBRARY_PATH;
|
AM_TESTS_ENVIRONMENT += export LD_LIBRARY_PATH;
|
||||||
AM_TESTS_ENVIRONMENT += PREFIX=$(prefix);
|
AM_TESTS_ENVIRONMENT += PREFIX=$(prefix);
|
||||||
AM_TESTS_ENVIRONMENT += export PREFIX;
|
AM_TESTS_ENVIRONMENT += export PREFIX;
|
||||||
|
|
||||||
TESTS = checkself.sh
|
TESTS = checkself.sh
|
||||||
TESTS += setup_root.sh
|
TESTS += setup-root.sh
|
||||||
TESTS += test_start_stop_service/test.sh
|
TESTS += add-remove-dynamic-service/test.sh
|
||||||
TESTS += teardown_root.sh
|
TESTS += add-remove-dynamic-service-sub-config/test.sh
|
||||||
|
TESTS += start-stop-service/test.sh
|
||||||
|
TESTS += start-stop-service-sub-config/test.sh
|
||||||
|
TESTS += teardown-root.sh
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")/..
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib.sh"
|
||||||
|
|
||||||
|
test_teardown() {
|
||||||
|
say "Test done $(date)"
|
||||||
|
say "Running test teardown."
|
||||||
|
|
||||||
|
texec rm -f "$FINIT_RCSD/service.conf"
|
||||||
|
texec rm -f /test_assets/service.sh
|
||||||
|
}
|
||||||
|
|
||||||
|
say "Test start $(date)"
|
||||||
|
|
||||||
|
cp "$TEST_DIR"/common/service.sh "$TESTS_ROOT"/test_assets/
|
||||||
|
|
||||||
|
say "Add a dynamic service in $FINIT_RCSD/service.conf"
|
||||||
|
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_RCSD/service.conf"
|
||||||
|
|
||||||
|
say 'Reload Finit'
|
||||||
|
texec sh -c "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_num_children 1 service.sh'
|
||||||
|
|
||||||
|
say 'Remove the dynamic service from /etc/finit.d/service.conf'
|
||||||
|
texec sh -c "echo > $FINIT_RCSD/service.conf"
|
||||||
|
|
||||||
|
say 'Reload Finit'
|
||||||
|
texec sh -c "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_num_children 0 service.sh'
|
||||||
Executable
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")/..
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib.sh"
|
||||||
|
|
||||||
|
test_teardown() {
|
||||||
|
say "Test done $(date)"
|
||||||
|
say "Running test teardown."
|
||||||
|
|
||||||
|
texec rm -f "$FINIT_CONF"
|
||||||
|
texec rm -f /test_assets/service.sh
|
||||||
|
}
|
||||||
|
|
||||||
|
say "Test start $(date)"
|
||||||
|
|
||||||
|
cp "$TEST_DIR"/common/service.sh "$TESTS_ROOT"/test_assets/
|
||||||
|
|
||||||
|
say "Add a dynamic service in $FINIT_CONF"
|
||||||
|
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF"
|
||||||
|
|
||||||
|
say 'Reload Finit'
|
||||||
|
texec sh -c "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_num_children 1 service.sh'
|
||||||
|
|
||||||
|
say 'Remove the dynamic service from /etc/finit.conf'
|
||||||
|
texec sh -c "echo > $FINIT_CONF"
|
||||||
|
|
||||||
|
say 'Reload Finit'
|
||||||
|
texec sh -c "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_num_children 0 service.sh'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
service [2345] kill:20 log /test_assets/service.sh
|
||||||
File renamed without changes.
+25
-5
@@ -1,8 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
assert_num_children() {
|
||||||
|
assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1"
|
||||||
|
}
|
||||||
|
|
||||||
texec() {
|
texec() {
|
||||||
# shellcheck disable=SC2154
|
# shellcheck disable=SC2154
|
||||||
TESTS_ROOT=. "$TEST_DIR/testenv_exec.sh" "$finit_pid" "$@"
|
"$TEST_DIR/testenv_exec.sh" "$finit_pid" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
pause() {
|
||||||
@@ -21,7 +25,7 @@ fg_red='\e[1;31m'
|
|||||||
fg_green='\e[1;32m'
|
fg_green='\e[1;32m'
|
||||||
fg_yellow='\e[1;33m'
|
fg_yellow='\e[1;33m'
|
||||||
log() {
|
log() {
|
||||||
printf "< TEST > %b%b%b %s\n" "$1" "$2" "$color_reset" "$3"
|
printf "< $TEST_NAME > %b%b%b %s\n" "$1" "$2" "$color_reset" "$3"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert() {
|
assert() {
|
||||||
@@ -69,6 +73,11 @@ say() {
|
|||||||
|
|
||||||
teardown() {
|
teardown() {
|
||||||
test_status="$?"
|
test_status="$?"
|
||||||
|
|
||||||
|
if type test_teardown > /dev/null 2>&1 ; then
|
||||||
|
test_teardown
|
||||||
|
fi
|
||||||
|
|
||||||
log "$color_reset" '--' ''
|
log "$color_reset" '--' ''
|
||||||
if [ "$test_status" -eq 0 ]; then
|
if [ "$test_status" -eq 0 ]; then
|
||||||
log "$fg_green" 'TEST PASS' ''
|
log "$fg_green" 'TEST PASS' ''
|
||||||
@@ -80,6 +89,7 @@ teardown() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
wait
|
wait
|
||||||
|
rm -f "$TESTS_ROOT"/running_test.pid
|
||||||
}
|
}
|
||||||
|
|
||||||
trap teardown EXIT
|
trap teardown EXIT
|
||||||
@@ -87,12 +97,22 @@ trap teardown EXIT
|
|||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
. "$TESTS_ROOT/../test.env"
|
. "$TESTS_ROOT/../test.env"
|
||||||
|
|
||||||
log "$color_reset" 'Test start' ''
|
TEST_NAME="$(dirname "$0")"
|
||||||
log "$color_reset" '--' ''
|
TEST_NAME=${TEST_NAME#*/}
|
||||||
|
export TEST_NAME
|
||||||
|
|
||||||
# Setup
|
# Setup test environment
|
||||||
|
|
||||||
|
# Setup test environment
|
||||||
"$TEST_DIR/testenv_start.sh" finit &
|
"$TEST_DIR/testenv_start.sh" finit &
|
||||||
finit_ppid=$!
|
finit_ppid=$!
|
||||||
|
echo "$finit_ppid" > "$TESTS_ROOT"/running_test.pid
|
||||||
|
|
||||||
|
>&2 echo "Hint: Execute 'test/testenv_enter.sh' to enter the test namespace"
|
||||||
|
>&2 echo "finit conf '$FINIT_CONF'"
|
||||||
|
>&2 echo "finit_conf dir '$FINIT_RCSD'"
|
||||||
|
log "$color_reset" 'Setup of test environment done' ''
|
||||||
|
|
||||||
finit_pid=$(retry "pgrep -P $finit_ppid")
|
finit_pid=$(retry "pgrep -P $finit_ppid")
|
||||||
|
|
||||||
tty=/dev/$(texec cat /sys/class/tty/console/active)
|
tty=/dev/$(texec cat /sys/class/tty/console/active)
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ set -eu
|
|||||||
if [ ! -f "$srcdir"/../config.status ]; then
|
if [ ! -f "$srcdir"/../config.status ]; then
|
||||||
"$srcdir"/../configure --prefix=/
|
"$srcdir"/../configure --prefix=/
|
||||||
make
|
make
|
||||||
make DESTDIR="$(pwd)/test_root/" install
|
make DESTDIR="$(pwd)/test-root/" install
|
||||||
|
|
||||||
mkdir -p test_root/bin
|
mkdir -p test-root/bin
|
||||||
cp "${srcdir}/test_root/bin/chrootsetup.sh" test_root/bin
|
cp "${srcdir}/test-root/bin/chrootsetup.sh" test-root/bin
|
||||||
cp "${srcdir}/test_root/busybox-x86_64.md5" test_root/
|
cp "${srcdir}/test-root/busybox-x86_64.md5" test-root/
|
||||||
cp "${srcdir}/test_root/Makefile" test_root/
|
cp "${srcdir}/test-root/Makefile" test-root/
|
||||||
make -C test_root/
|
make -C test-root/
|
||||||
config_h='./config.h'
|
config_h='./config.h'
|
||||||
echo "export PATH=/bin:/sbin" > test.env
|
echo "export PATH=/bin:/sbin" > test.env
|
||||||
else
|
else
|
||||||
make -C ../ DESTDIR="$(pwd)/test_root/" install
|
make -C ../ DESTDIR="$(pwd)/test-root/" install
|
||||||
FINITBIN=../../src/finit make -C test_root/
|
FINITBIN=../../src/finit make -C test-root/
|
||||||
config_h='../config.h'
|
config_h='../config.h'
|
||||||
echo "export PATH=/bin:/sbin:$PREFIX/sbin:$PREFIX/bin" > test.env
|
echo "export PATH=/bin:/sbin:$PREFIX/sbin:$PREFIX/bin" > test.env
|
||||||
fi
|
fi
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")/..
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib.sh"
|
||||||
|
|
||||||
|
test_teardown() {
|
||||||
|
say "Test done $(date)"
|
||||||
|
say "Running test teardown."
|
||||||
|
|
||||||
|
texec rm -f "$FINIT_RCSD/service.conf"
|
||||||
|
texec rm -f /test_assets/service.sh
|
||||||
|
}
|
||||||
|
|
||||||
|
say "Test start $(date)"
|
||||||
|
|
||||||
|
cp "$TEST_DIR"/common/service.sh "$TESTS_ROOT"/test_assets/
|
||||||
|
|
||||||
|
say "Add service stanza in $FINIT_RCSD/service.conf"
|
||||||
|
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_RCSD/service.conf"
|
||||||
|
|
||||||
|
say 'Reload Finit'
|
||||||
|
texec sh -c "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_num_children 1 service.sh'
|
||||||
|
|
||||||
|
say 'Stop the service'
|
||||||
|
texec sh -c "initctl stop service.sh"
|
||||||
|
|
||||||
|
retry 'assert_num_children 0 service.sh'
|
||||||
|
|
||||||
|
say 'Start the service again'
|
||||||
|
texec sh -c "initctl start service.sh"
|
||||||
|
|
||||||
|
retry 'assert_num_children 1 service.sh'
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")/..
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib.sh"
|
||||||
|
|
||||||
|
test_teardown() {
|
||||||
|
say "Test done $(date)"
|
||||||
|
say "Running test teardown."
|
||||||
|
|
||||||
|
texec rm -f "$FINIT_CONF"
|
||||||
|
texec rm -f /test_assets/service.sh
|
||||||
|
}
|
||||||
|
|
||||||
|
say "Test start $(date)"
|
||||||
|
|
||||||
|
cp "$TEST_DIR"/common/service.sh "$TESTS_ROOT"/test_assets/
|
||||||
|
|
||||||
|
say "Add service stanza in $FINIT_CONF"
|
||||||
|
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF"
|
||||||
|
|
||||||
|
say 'Reload Finit'
|
||||||
|
texec sh -c "initctl reload"
|
||||||
|
|
||||||
|
retry 'assert_num_children 1 service.sh'
|
||||||
|
|
||||||
|
say 'Stop the service'
|
||||||
|
texec sh -c "initctl stop service.sh"
|
||||||
|
|
||||||
|
retry 'assert_num_children 0 service.sh'
|
||||||
|
|
||||||
|
say 'Start the service again'
|
||||||
|
texec sh -c "initctl start service.sh"
|
||||||
|
|
||||||
|
retry 'assert_num_children 1 service.sh'
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
if [ -d test-root/var/lock ]; then
|
||||||
|
chmod +r test-root/var/lock
|
||||||
|
fi
|
||||||
|
make -C test-root/ clean
|
||||||
|
rm -f test.env
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
if [ -d test_root/var/lock ]; then
|
|
||||||
chmod +r test_root/var/lock
|
|
||||||
fi
|
|
||||||
make -C test_root/ clean
|
|
||||||
rm -f test.env
|
|
||||||
File renamed without changes.
File renamed without changes.
@@ -17,7 +17,7 @@ mount -n -t tmpfs none /dev
|
|||||||
mount -t proc none /proc
|
mount -t proc none /proc
|
||||||
mount -t sysfs none /sys
|
mount -t sysfs none /sys
|
||||||
# mount -t tmpfs none /tmp
|
# mount -t tmpfs none /tmp
|
||||||
mount -t tmpfs none /etc
|
# mount -t tmpfs none /etc
|
||||||
|
|
||||||
# mkdir -p /dev/pts
|
# mkdir -p /dev/pts
|
||||||
# mount -t devpts none /dev/pts
|
# mount -t devpts none /dev/pts
|
||||||
File renamed without changes.
@@ -1,32 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
TEST_DIR=$(dirname "$0")/..
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
. "$TEST_DIR/lib.sh"
|
|
||||||
|
|
||||||
assert_num_children() {
|
|
||||||
assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test
|
|
||||||
say 'Set up a service'
|
|
||||||
cp "$TEST_DIR"/test_start_stop_service/service.sh "$TESTS_ROOT"/test_assets/
|
|
||||||
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF"
|
|
||||||
|
|
||||||
say 'Reload Finit'
|
|
||||||
texec kill -SIGHUP 1
|
|
||||||
|
|
||||||
retry 'assert_num_children 1 service.sh'
|
|
||||||
|
|
||||||
say 'Remove the service'
|
|
||||||
texec sh -c "echo > $FINIT_CONF"
|
|
||||||
|
|
||||||
say 'Reload Finit'
|
|
||||||
texec kill -SIGHUP 1
|
|
||||||
|
|
||||||
retry 'assert_num_children 0 service.sh'
|
|
||||||
|
|
||||||
# Teardown
|
|
||||||
rm -f "$TESTS_ROOT"/bin/service.sh
|
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cmd=""
|
||||||
|
if [ "$#" -lt 1 ]; then
|
||||||
|
cmd="/bin/sh"
|
||||||
|
else
|
||||||
|
cmd=$1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
PID_FILE=$(dirname "$0")/test-root/running_test.pid
|
||||||
|
|
||||||
|
if [ -f "$PID_FILE" ] ; then
|
||||||
|
target=$(cat "$PID_FILE")
|
||||||
|
target=$(pgrep -P "$target")
|
||||||
|
else
|
||||||
|
echo "No test running!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$(dirname "$0")"/testenv_exec.sh "$target" "$cmd" "$@"
|
||||||
@@ -11,7 +11,7 @@ target="$1"
|
|||||||
shift
|
shift
|
||||||
|
|
||||||
TEST_DIR=$(dirname "$0")
|
TEST_DIR=$(dirname "$0")
|
||||||
TESTS_ROOT="${TESTS_ROOT:-$(pwd)/${TEST_DIR}/test_root}"
|
TESTS_ROOT="${TESTS_ROOT:-$(pwd)/${TEST_DIR}/test-root}"
|
||||||
|
|
||||||
nsenter=$(command -v nsenter)
|
nsenter=$(command -v nsenter)
|
||||||
chroot=$(command -v chroot)
|
chroot=$(command -v chroot)
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
TEST_DIR=$(dirname "$0")
|
TEST_DIR=$(dirname "$0")
|
||||||
TESTS_ROOT="${TESTS_ROOT:-$(pwd)/${TEST_DIR}/test_root}"
|
TESTS_ROOT="${TESTS_ROOT:-$(pwd)/${TEST_DIR}/test-root}"
|
||||||
|
|
||||||
>&2 echo "Hint: Execute '${TEST_DIR}/testenv_exec.sh \$(pgrep -P $$) /bin/sh; reset' to enter the test namespace"
|
|
||||||
|
|
||||||
unshare=$(command -v unshare)
|
unshare=$(command -v unshare)
|
||||||
chroot=$(command -v chroot)
|
chroot=$(command -v chroot)
|
||||||
|
|||||||
Reference in new issue
Block a user