mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a61a8015d7 | ||
|
|
e2f070649d | ||
|
|
f08d375c36 | ||
|
|
1c4bad5b73 | ||
|
|
6c874895dc | ||
|
|
ef1a05425d | ||
|
|
fc3bad2160 | ||
|
|
31ac933d64 | ||
|
|
caa398816d | ||
|
|
0d57a507f3 | ||
|
|
96257cc5c7 | ||
|
|
fbcd7d2384 | ||
|
|
d9dbfa23c7 | ||
|
|
91f077bd08 | ||
|
|
5fb4a6ae8f | ||
|
|
e84333662e | ||
|
|
ddb42fda70 | ||
|
|
621eb21b42 | ||
|
|
6cc44a87c9 | ||
|
|
5f908cae49 | ||
|
|
6de67ed107 | ||
|
|
c129f06195 | ||
|
|
d11dc63bc2 | ||
|
|
d904ec1602 | ||
|
|
2a98b2f055 | ||
|
|
6286ede985 | ||
|
|
4a8ca787d3 | ||
|
|
561c88a868 | ||
|
|
f8dfcdf024 | ||
|
|
f50b5a4ac6 | ||
|
|
59525b80e3 | ||
|
|
0ce028add1 | ||
|
|
75c5c85f07 | ||
|
|
ccb84ced2e | ||
|
|
5ef669fb6c | ||
|
|
96ff9c115c | ||
|
|
83aa6abb1c | ||
|
|
9304024841 | ||
|
|
0de5981948 | ||
|
|
c39106906d | ||
|
|
929a6e7aac | ||
|
|
ceb4d04d23 |
@@ -464,7 +464,7 @@ Commands:
|
||||
cond get <COND> Get status of user-defined condition, see $? and -v
|
||||
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
|
||||
cond status Show condition status, default cond command
|
||||
cond dump Dump all conditions and their status
|
||||
cond dump [TYPE] Dump all, or a type of, conditions and their status
|
||||
|
||||
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||
start <NAME>[:ID] Start service by name, with optional ID
|
||||
|
||||
@@ -151,6 +151,9 @@ AC_ARG_WITH(hook-scripts-path,
|
||||
AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]),
|
||||
[hook_scripts_path=$withval], [hook_scripts_path=yes])
|
||||
|
||||
AC_ARG_WITH(plugin-path,
|
||||
AS_HELP_STRING([--with-plugin-path=DIR], [Search path for external plugins, default /usr/lib/finit/plugins:/usr/local/lib/finit/plugins]),
|
||||
[plugin_path=$withval], [plugin_path=yes])
|
||||
|
||||
### Enable features ###########################################################################
|
||||
|
||||
@@ -251,6 +254,11 @@ AS_IF([test "x$enable_hook_scripts_plugin" = "xyes" -a "x$with_hook_scripts_path
|
||||
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
|
||||
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
|
||||
|
||||
AS_IF([test "x$with_plugin_path" != "xno"], [
|
||||
AS_IF([test "x$plugin_path" = "xyes"], [plugin_path=/usr/lib/finit/plugins:/usr/local/lib/finit/plugins])
|
||||
AC_EXPAND_DIR(plugin_path, "$plugin_path")
|
||||
AC_DEFINE_UNQUOTED(EXTERNAL_PLUGIN_PATH, "$plugin_path", [Built-in default: /usr/lib/finit/plugins:/usr/local/lib/finit/plugins])])
|
||||
|
||||
# Control build with automake flags
|
||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ Below are a few of the main switches to configure:
|
||||
|
||||
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
|
||||
|
||||
* `--with-sulgin`: Enable bundled `sulogin` program. Default is to use the
|
||||
* `--with-sulogin`: Enable bundled `sulogin` program. Default is to use the
|
||||
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
|
||||
login if the `root` user is disabled or has no password at all.
|
||||
|
||||
|
||||
@@ -12,6 +12,17 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
* `debug`: Enable kernel debug. Debug messages are printed to the
|
||||
console until Finit starts up, unless `loglevel=7` (below) is used.
|
||||
|
||||
* `finit.cond=foo[,bar[,baz]]`: set `<boot/foo>` condition, optionally
|
||||
multiple conditions can be set using the same option, separated with a
|
||||
comma. Alternatively, multiple `foo.cond=arg` can be given. Each will
|
||||
result in a `<boot/arg>` condition being set to control the rest of the
|
||||
system bootstrap.
|
||||
|
||||
Very useful for selecting different boot modes, e.g. manufacturing test,
|
||||
firmware upgrade, or rescue mode.
|
||||
|
||||
> Note: `<boot/...>` conditions cannot be cleared with `initctl`!
|
||||
|
||||
* `finit.debug[=bool]`: Enable finit debug. This is operated
|
||||
independently of the kernel `debug` setting. New as of Finit v4.
|
||||
|
||||
|
||||
+11
-5
@@ -46,11 +46,16 @@ Triggering
|
||||
----------
|
||||
|
||||
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
|
||||
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
|
||||
also supports user-defined conditions, sometimes referred to as static
|
||||
or one-shot conditions. They live in the `<usr/...` namespace and are
|
||||
constrained to a flat hierarchy without any sub-directories, unlike the
|
||||
pidfile plugin, for instance.
|
||||
|
||||
- `netlink.so`: provides `<net/...>`
|
||||
- `pidfile.so`: provides `<pid/...>`
|
||||
- Cmdline `finit.cond=arg`: provides `<boot/arg`
|
||||
|
||||
See below for built-in conditions. Finit also supports user-defined
|
||||
conditions, sometimes referred to as static or one-shot conditions.
|
||||
They live in the `<usr/...` namespace and are constrained to a flat
|
||||
hierarchy without any sub-directories, unlike the pidfile plugin, for
|
||||
instance.
|
||||
|
||||
User-defined conditions are controlled using the `initctl cond set` and
|
||||
`initctl cond clear` commands:
|
||||
@@ -118,6 +123,7 @@ Built-in conditions:
|
||||
- `sys/pwr/fail`
|
||||
- `sys/key/ctrlaltdel`
|
||||
- `usr/foo`
|
||||
- `boot/arg`
|
||||
|
||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
||||
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
||||
|
||||
@@ -450,6 +450,8 @@ configurable with the following options:
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
* `restart:always` -- no upper limit on the number of times Finit
|
||||
tries to restart a crashing service. Same as `restart:-1`
|
||||
* `norestart` -- dont restart on failures, same as `restart:0`
|
||||
* `respawn` -- bypasses the `restart` mechanism completely, allows
|
||||
endless restarts. Useful in many use-cases, but not what `service`
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 180 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 103 KiB |
@@ -304,6 +304,10 @@ number of seconds before Finit tries to restart
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
.It Cm restart:always
|
||||
no upper limit on the number of times Finit tries to restart a crashing
|
||||
service. Same as
|
||||
.Cm restart:-1
|
||||
.It Cm norestart
|
||||
dont restart on failures, same as
|
||||
.Cm restart:0
|
||||
|
||||
+4
-2
@@ -145,8 +145,8 @@ Clear (deassert) user-defined condition,
|
||||
Show condition status, default
|
||||
.Cm cond
|
||||
command.
|
||||
.It Nm Ar cond dump
|
||||
Dump all conditions and their status
|
||||
.It Nm Ar cond dump Op Cm TYPE
|
||||
Dump all, or a given type of, conditions and their status
|
||||
.It Nm Ar ident Op Cm NAME
|
||||
Display indentities of all run/task/services, or only instances
|
||||
matching
|
||||
@@ -202,6 +202,8 @@ List cgroup config overview
|
||||
List processes based on cgroups
|
||||
.It Nm Ar top
|
||||
Show top-like listing based on cgroups
|
||||
.It Nm Ar plugins
|
||||
List installed plugins
|
||||
.It Nm Ar runlevel Op Ar 0-9
|
||||
Show or set runlevel: 0 halt, 6 reboot
|
||||
.It Nm Ar reboot
|
||||
|
||||
@@ -116,6 +116,45 @@ static void ln(const char *target, const char *linkpath)
|
||||
_pe("Failed creating %s -> %s symlink", target, linkpath);
|
||||
}
|
||||
|
||||
/* Kernel defines the following compulsory and recommended links
|
||||
* https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links
|
||||
*/
|
||||
static void kernel_links(void)
|
||||
{
|
||||
struct {
|
||||
char *tgt, *lnk;
|
||||
int optional;
|
||||
} k[] = {
|
||||
{ "/proc/self/fd", "/dev/fd", 0 },
|
||||
{ "fd/0", "/dev/stdin", 0 },
|
||||
{ "fd/1", "/dev/stdout", 0 },
|
||||
{ "fd/2", "/dev/stderr", 0 },
|
||||
{ "socksys", "/dev/nfsd", 0 },
|
||||
{ "null", "/dev/X0R", 0 },
|
||||
{ "/proc/kcore", "/dev/core", 1 },
|
||||
{ "ram0", "/dev/ramdisk", 1 },
|
||||
{ "qft0", "/dev/ftape", 1 },
|
||||
{ "video0", "/dev/bttv0", 1 },
|
||||
{ "radio0", "/dev/radio", 1 },
|
||||
};
|
||||
char *fn, buf[80];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(k); i++) {
|
||||
fn = k[i].tgt;
|
||||
|
||||
if (k[i].optional) {
|
||||
if (fn[0] != '/') {
|
||||
snprintf(buf, sizeof(buf), "/dev/%s", k[i].tgt);
|
||||
fn = buf;
|
||||
}
|
||||
if (!fexist(fn))
|
||||
continue;
|
||||
}
|
||||
ln(k[i].tgt, k[i].lnk);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP
|
||||
*/
|
||||
@@ -206,6 +245,9 @@ static void setup(void *arg)
|
||||
if (whichp("restorecon"))
|
||||
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
|
||||
|
||||
/* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */
|
||||
kernel_links();
|
||||
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
|
||||
+23
-13
@@ -35,16 +35,20 @@
|
||||
#include "service.h"
|
||||
#include "conf.h"
|
||||
|
||||
#define DAEMON "dbus-daemon"
|
||||
#define ARGS "--nofork --system --syslog-only"
|
||||
#define DESC "D-Bus message bus daemon"
|
||||
#define DBUS_DAEMON "dbus-daemon"
|
||||
#define DBUS_ARGS "--nofork --system --syslog-only"
|
||||
#define DBUS_DESC "D-Bus message bus daemon"
|
||||
|
||||
#ifndef DAEMONUSER
|
||||
#define DAEMONUSER "messagebus"
|
||||
#ifndef DBUS_DAEMONUSER
|
||||
#define DBUS_DAEMONUSER "messagebus"
|
||||
#endif
|
||||
|
||||
#ifndef DAEMONPIDFILE
|
||||
#define DAEMONPIDFILE "/var/run/dbus/pid"
|
||||
#ifndef DBUS_DAEMONGROUP
|
||||
#define DBUS_DAEMONGROUP "messagebus"
|
||||
#endif
|
||||
|
||||
#ifndef DBUS_DAEMONPIDFILE
|
||||
#define DBUS_DAEMONPIDFILE "/var/run/dbus/pid"
|
||||
#endif
|
||||
|
||||
static void setup(void *arg)
|
||||
@@ -58,26 +62,32 @@ static void setup(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = which(DAEMON);
|
||||
cmd = which(DBUS_DAEMON);
|
||||
if (!cmd) {
|
||||
_d("Skipping plugin, %s is not installed.", DAEMON);
|
||||
_d("Skipping plugin, %s is not installed.", DBUS_DAEMON);
|
||||
return;
|
||||
}
|
||||
|
||||
prev =umask(0);
|
||||
|
||||
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
|
||||
_d("Creating D-Bus Required Directories ...");
|
||||
mksubsys("/var/run/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
||||
mksubsys("/var/run/lock/subsys", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
||||
mksubsys("/var/lib/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
||||
mksubsys("/tmp/dbus", 0755, DBUS_DAEMONUSER, DBUS_DAEMONGROUP);
|
||||
|
||||
/* Generate machine id for dbus */
|
||||
if (whichp("dbus-uuidgen"))
|
||||
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||
|
||||
/* Clean up from any previous pre-bootstrap run */
|
||||
remove(DAEMONPIDFILE);
|
||||
remove(DBUS_DAEMONPIDFILE);
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
|
||||
DAEMONPIDFILE, DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
DBUS_DAEMONPIDFILE, DBUS_DAEMONUSER, DBUS_DAEMONGROUP, cmd, DBUS_ARGS, DBUS_DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
_pe("Failed registering %s", DBUS_DAEMON);
|
||||
free(cmd);
|
||||
|
||||
umask(prev);
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ static plugin_t plugin = {
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc" },
|
||||
.depends = { "bootmisc", "modprobe" },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+70
-10
@@ -106,7 +106,7 @@ static void alias_remove(struct module *m)
|
||||
TAILQ_REMOVE(&modules, m, link);
|
||||
free(m->alias);
|
||||
free(m);
|
||||
}
|
||||
}
|
||||
|
||||
static int alias_exist(char *alias)
|
||||
{
|
||||
@@ -120,27 +120,87 @@ static int alias_exist(char *alias)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_alias(const char *path, const struct stat *st, int flag, struct FTW *unused)
|
||||
static void alias_add_uniq(char *alias)
|
||||
{
|
||||
if (alias_exist(alias))
|
||||
return;
|
||||
|
||||
alias_add(alias);
|
||||
}
|
||||
|
||||
static FILE *maybe_fopen_alias(const char *file, const char *path)
|
||||
{
|
||||
const char *basename;
|
||||
|
||||
basename = rindex(path, '/');
|
||||
if (!basename)
|
||||
return NULL;
|
||||
|
||||
basename++;
|
||||
if (strcmp(file, basename))
|
||||
return NULL;
|
||||
|
||||
return fopen(path, "r");
|
||||
}
|
||||
|
||||
static int scan_modalias(const char *path)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
|
||||
if (fnmatch("*/modalias", path, 0))
|
||||
return 0;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
fp = maybe_fopen_alias("modalias", path);
|
||||
if (!fp)
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp)) {
|
||||
chomp(buf);
|
||||
if (!alias_exist(buf))
|
||||
alias_add(buf);
|
||||
alias_add_uniq(buf);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_uevent(const char *path)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
|
||||
fp = maybe_fopen_alias("uevent", path);
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (strstr(buf, "MODALIAS=") != buf)
|
||||
continue;
|
||||
|
||||
chomp(buf);
|
||||
|
||||
alias_add_uniq(buf + strlen("MODALIAS="));
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_alias(const char *path, const struct stat *st,
|
||||
int flag, struct FTW *unused)
|
||||
{
|
||||
/* Sanity check: is this a regular file we can read? */
|
||||
if (!(S_ISREG(st->st_mode) && (st->st_mode & S_IRUSR)))
|
||||
return 0;
|
||||
|
||||
if (!scan_uevent(path))
|
||||
return 0;
|
||||
|
||||
scan_modalias(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coldplug(void *arg)
|
||||
{
|
||||
struct module *m, *tmp;
|
||||
|
||||
+2
-1
@@ -64,7 +64,8 @@ if LOGROTATE
|
||||
finit_SOURCES += logrotate.c
|
||||
endif
|
||||
|
||||
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
|
||||
pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.h \
|
||||
plugin.h svc.h service.h
|
||||
|
||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
|
||||
@@ -45,8 +45,9 @@
|
||||
#include "log.h"
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "schedule.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "util.h"
|
||||
|
||||
extern svc_t *wdog;
|
||||
@@ -89,8 +90,10 @@ static int restart(svc_t *svc, void *user_data)
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
svc_start(svc);
|
||||
if (!svc_is_running(svc))
|
||||
return start(svc, user_data);
|
||||
|
||||
service_stop(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
@@ -225,10 +228,44 @@ static svc_t *do_find_byc(char *buf, size_t len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int do_reboot(int cmd, char *buf, size_t len)
|
||||
static void bypass_shutdown(void *);
|
||||
struct wq emergency = { .cb = bypass_shutdown };
|
||||
|
||||
static void bypass_shutdown(void *unused)
|
||||
{
|
||||
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
|
||||
do_shutdown(halt);
|
||||
}
|
||||
|
||||
static int do_reboot(int cmd, int timeout, char *buf, size_t len)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
switch (cmd) {
|
||||
case INIT_CMD_REBOOT:
|
||||
halt = SHUT_REBOOT;
|
||||
break;
|
||||
|
||||
case INIT_CMD_HALT:
|
||||
halt = SHUT_HALT;
|
||||
break;
|
||||
|
||||
case INIT_CMD_POWEROFF:
|
||||
halt = SHUT_OFF;
|
||||
break;
|
||||
|
||||
case INIT_CMD_SUSPEND:
|
||||
break;
|
||||
|
||||
default:
|
||||
return 255;
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
emergency.delay = timeout * 1000;
|
||||
schedule_work(&emergency);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case INIT_CMD_REBOOT:
|
||||
_d("reboot");
|
||||
@@ -261,7 +298,7 @@ static int do_reboot(int cmd, char *buf, size_t len)
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = 255;
|
||||
/* Handled previously */
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -319,6 +356,10 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
if (EAGAIN == errno)
|
||||
break;
|
||||
|
||||
/* we get here when client restarts itself */
|
||||
if (ECONNRESET == errno)
|
||||
break;
|
||||
|
||||
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||
}
|
||||
|
||||
@@ -388,6 +429,14 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
result = do_reload(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_PLUGINS:
|
||||
result = plugin_list(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_PLUGIN_DEPS:
|
||||
result = plugin_deps(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_RUNLEVEL:
|
||||
_d("get runlevel");
|
||||
rq.runlevel = runlevel;
|
||||
@@ -398,7 +447,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
case INIT_CMD_HALT:
|
||||
case INIT_CMD_POWEROFF:
|
||||
case INIT_CMD_SUSPEND:
|
||||
result = do_reboot(rq.cmd, rq.data, sizeof(rq.data));
|
||||
result = do_reboot(rq.cmd, rq.sleeptime, rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_ACK:
|
||||
|
||||
@@ -511,6 +511,9 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
|
||||
/* prepare cgroup.events watcher */
|
||||
fd = iwatch_init(&iw_cgroup);
|
||||
if (fd == -1)
|
||||
goto abort;
|
||||
|
||||
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
||||
_pe("Failed setting up cgroup.events watcher");
|
||||
iwatch_exit(&iw_cgroup);
|
||||
|
||||
+70
-2
@@ -36,6 +36,67 @@
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
|
||||
struct cond_boot {
|
||||
TAILQ_ENTRY(cond_boot) link;
|
||||
char *name;
|
||||
};
|
||||
static TAILQ_HEAD(, cond_boot) cond_boot_list = TAILQ_HEAD_INITIALIZER(cond_boot_list);
|
||||
|
||||
|
||||
/*
|
||||
* Parse finit.cond=cond[,cond[,...]] from command line. It creates a
|
||||
* temporary linked list of conds which are freed when finit reaches
|
||||
* cond_init(), which calls cond_boot_strap() to realize the conditions
|
||||
* in the file system as regular static conditions. Policy, however,
|
||||
* dictates that these are read-only and should not be possible to
|
||||
* manage from initctl.
|
||||
*/
|
||||
void cond_boot_parse(char *arg)
|
||||
{
|
||||
struct cond_boot *node;
|
||||
|
||||
if (!arg)
|
||||
return;
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node) {
|
||||
_pe("Out of memory cannot track boot conditions");
|
||||
return;
|
||||
}
|
||||
|
||||
node->name = strdup(arg);
|
||||
TAILQ_INSERT_HEAD(&cond_boot_list, node, link);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert all conditions read from the kernel command line (above) into
|
||||
* proper finit conditions.
|
||||
*/
|
||||
static void cond_boot_strap(void)
|
||||
{
|
||||
struct cond_boot *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &cond_boot_list, link, tmp) {
|
||||
TAILQ_REMOVE(&cond_boot_list, node, link);
|
||||
if (node->name) {
|
||||
char *ptr;
|
||||
|
||||
ptr = strtok(node->name, ",");
|
||||
while (ptr) {
|
||||
char cond[strlen(ptr) + 6];
|
||||
|
||||
snprintf(cond, sizeof(cond), "boot/%s", ptr);
|
||||
cond_set_oneshot_noupdate(cond);
|
||||
|
||||
ptr = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(node->name);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The service condition name is constructed from the 'pid/' prefix and
|
||||
* the unique NAME:ID tuple that identify each process in Finit. Here
|
||||
@@ -145,11 +206,17 @@ static void cond_delpath(const char *path)
|
||||
return;
|
||||
|
||||
while ((d = readdir(dir))) {
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
|
||||
paste(fn, sizeof(fn), path, d->d_name);
|
||||
remove(fn);
|
||||
if (remove(fn))
|
||||
_pe("Failed removing condition path, file %s", fn);
|
||||
}
|
||||
closedir(dir);
|
||||
unlink(path);
|
||||
|
||||
if (unlink(path) && errno != EISDIR)
|
||||
_pe("Failed removing condition path %s", fn);
|
||||
}
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state next)
|
||||
@@ -360,6 +427,7 @@ void cond_init(void)
|
||||
}
|
||||
|
||||
cond_bump_reconf();
|
||||
cond_boot_strap();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-3
@@ -31,6 +31,7 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
void cond_boot_parse (char *arg);
|
||||
int cond_update (const char *name);
|
||||
int cond_set_path (const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
@@ -38,9 +39,9 @@ void cond_set_oneshot (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
|
||||
int cond_set_noupdate (const char *name);
|
||||
int cond_set_oneshot_noupdate(const char *name);
|
||||
int cond_clear_noupdate(const char *name);
|
||||
int cond_set_noupdate(const char *name);
|
||||
int cond_set_oneshot_noupdate(const char *name);
|
||||
int cond_clear_noupdate(const char *name);
|
||||
|
||||
void cond_reassert (const char *pat);
|
||||
void cond_deassert (const char *pat);
|
||||
|
||||
+8
-3
@@ -48,8 +48,6 @@
|
||||
#include "util.h"
|
||||
|
||||
#define BOOTSTRAP (runlevel == 0)
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
|
||||
int logfile_size_max = 200000; /* 200 kB */
|
||||
int logfile_count_max = 5;
|
||||
@@ -115,6 +113,11 @@ static void parse_finit_opts(char *opt)
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "cond")) {
|
||||
cond_boot_parse(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "status_style")) {
|
||||
if (!arg) {
|
||||
_e("status_style option requires an argument, skipping.");
|
||||
@@ -439,8 +442,10 @@ void conf_parse_cond(svc_t *svc, char *cond)
|
||||
if (svc_is_daemon(svc))
|
||||
svc->sighup = 1;
|
||||
|
||||
if (!cond)
|
||||
if (!cond) {
|
||||
memset(svc->cond, 0, sizeof(svc->cond));
|
||||
return;
|
||||
}
|
||||
|
||||
/* First character must be '!' if SIGHUP is not supported. */
|
||||
ptr = cond;
|
||||
|
||||
+2
-2
@@ -329,7 +329,7 @@ static int activate_console(int noclear, int nowait)
|
||||
static const char clr[] = "\r\e[K";
|
||||
static const char cup[] = "\e[A";
|
||||
struct termios orig;
|
||||
char c;
|
||||
char ch;
|
||||
int rc;
|
||||
|
||||
if (nowait || rescue)
|
||||
@@ -351,7 +351,7 @@ static int activate_console(int noclear, int nowait)
|
||||
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, msg, strlen(msg));
|
||||
while ((rc = read(STDIN_FILENO, &c, 1)) > 0 && c != '\r' && c != '\n')
|
||||
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
|
||||
continue;
|
||||
|
||||
/* On any error (likely EINTR), we avoid starting getty */
|
||||
|
||||
+4
-1
@@ -122,6 +122,9 @@ static int sulogin(int do_reboot)
|
||||
for (i = 0; i < NELEMS(cmd); i++) {
|
||||
char *path = which(cmd[i]);
|
||||
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
if (access(path, X_OK)) {
|
||||
free(path);
|
||||
continue;
|
||||
@@ -574,7 +577,7 @@ static void bootstrap_worker(void *work)
|
||||
.cb = finalize,
|
||||
.delay = 10
|
||||
};
|
||||
static int cnt = 120;
|
||||
static int cnt = 120 * 10; /* We run with 100ms period */
|
||||
int level = cfglevel;
|
||||
|
||||
_d("Step all services ...");
|
||||
|
||||
+2
-2
@@ -83,8 +83,8 @@
|
||||
#define INIT_CMD_STOP_SVC 11
|
||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
|
||||
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
|
||||
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
||||
#define INIT_CMD_GET_PLUGINS 14 /* Fill data[] with loaded plugins */
|
||||
#define INIT_CMD_PLUGIN_DEPS 15 /* Fill data[] with plugin deps */
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
#define INIT_CMD_REBOOT 20
|
||||
#define INIT_CMD_HALT 21
|
||||
|
||||
+9
-2
@@ -49,6 +49,7 @@
|
||||
#endif
|
||||
|
||||
static long logname_len = 32; /* useradd(1) limit at 32 chars */
|
||||
static int passenv = 0; /* Set /bin/login -p or not */
|
||||
|
||||
/*
|
||||
* Read one character from stdin.
|
||||
@@ -185,7 +186,10 @@ static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
if (passenv)
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL);
|
||||
else
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
* Failed to exec login, should not happen on normal systems.
|
||||
@@ -252,11 +256,14 @@ int main(int argc, char *argv[])
|
||||
char *tty, *speed = "0", *term = NULL;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "h?")) != EOF) {
|
||||
while ((c = getopt(argc, argv, "h?p")) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
case 'p':
|
||||
passenv = 1;
|
||||
break;
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
+103
-53
@@ -64,6 +64,7 @@ int verbose = 0;
|
||||
int plain = 0;
|
||||
int quiet = 0;
|
||||
int runlevel = 0;
|
||||
int timeout = 0;
|
||||
int cgrp = 0;
|
||||
int utmp = 0;
|
||||
|
||||
@@ -163,6 +164,8 @@ static int do_log(svc_t *svc, char *tail)
|
||||
if (svc) {
|
||||
nm = svc_ident(svc, NULL, 0);
|
||||
pid = svc->pid;
|
||||
if (!pid)
|
||||
return 0; /* not running */
|
||||
} else {
|
||||
nm = "finit";
|
||||
pid = 1;
|
||||
@@ -271,23 +274,10 @@ static int do_reload (char *arg)
|
||||
|
||||
static int do_restart(char *arg)
|
||||
{
|
||||
size_t retries = 3;
|
||||
svc_t *svc;
|
||||
if (do_startstop(INIT_CMD_RESTART_SVC, arg))
|
||||
ERRX(7, "failed restarting %s", arg);
|
||||
|
||||
if (do_startstop(INIT_CMD_STOP_SVC, arg))
|
||||
return 1;
|
||||
|
||||
while (retries-- > 0 && (svc = client_svc_find(arg))) {
|
||||
if (!svc_is_running(svc))
|
||||
break;
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (retries == 0)
|
||||
ERRX(7, "failed stopping %s (restart)", arg);
|
||||
|
||||
return do_startstop(INIT_CMD_RESTART_SVC, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,6 +323,7 @@ int do_signal(int argc, char *argv[])
|
||||
return client_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
char *dump_filter;
|
||||
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||
{
|
||||
const char *cond, *asserted;
|
||||
@@ -347,6 +338,10 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
||||
|
||||
asserted = condstr(cond_get_path(fpath));
|
||||
cond = &fpath[strlen(_PATH_COND)];
|
||||
|
||||
if (dump_filter && dump_filter[0] && strncmp(cond, dump_filter, strlen(dump_filter)))
|
||||
return 0;
|
||||
|
||||
if (strncmp("pid/", cond, 4) == 0) {
|
||||
svc_t *svc;
|
||||
|
||||
@@ -374,14 +369,11 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
||||
static int do_cond_dump(char *arg)
|
||||
{
|
||||
col_widths();
|
||||
if (heading) {
|
||||
char title[80];
|
||||
|
||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
||||
iw, "IDENT", "STATUS", "CONDITION");
|
||||
print_header(title);
|
||||
}
|
||||
if (heading)
|
||||
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
||||
"STATUS", "CONDITION");
|
||||
|
||||
dump_filter = arg;
|
||||
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
|
||||
WARNX("Failed parsing %s", _PATH_COND);
|
||||
return 1;
|
||||
@@ -497,13 +489,9 @@ static int do_cond_show(char *arg)
|
||||
svc_t *svc;
|
||||
|
||||
col_widths();
|
||||
if (heading) {
|
||||
char title[80];
|
||||
|
||||
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
|
||||
iw, "IDENT", "STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
||||
print_header(title);
|
||||
}
|
||||
if (heading)
|
||||
print_header("%-*s %-*s %-6s %s", pw, "PID", iw, "IDENT",
|
||||
"STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
if (!svc->cond[0])
|
||||
@@ -528,8 +516,9 @@ static int do_cond_show(char *arg)
|
||||
static int do_cmd(int cmd)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = cmd,
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = cmd,
|
||||
.sleeptime = timeout,
|
||||
};
|
||||
|
||||
if (client_send(&rq, sizeof(rq))) {
|
||||
@@ -616,6 +605,44 @@ static int show_version(char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plugins_list(char *arg)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_GET_PLUGINS,
|
||||
};
|
||||
char buf[sizeof(rq.data)];
|
||||
char *ptr;
|
||||
int rc;
|
||||
|
||||
rc = client_send(&rq, sizeof(rq));
|
||||
if (rc) {
|
||||
if (rc == 255)
|
||||
return 69;
|
||||
else
|
||||
ERRX(69, "No such command");
|
||||
}
|
||||
memcpy(buf, rq.data, sizeof(buf));
|
||||
|
||||
if (heading)
|
||||
print_header("%-18s %s", "PLUGIN", "DEPENDENCIES");
|
||||
|
||||
ptr = strtok(buf, " ");
|
||||
while (ptr) {
|
||||
rq.cmd = INIT_CMD_PLUGIN_DEPS;
|
||||
strlcpy(rq.data, ptr, sizeof(rq.data));
|
||||
rc = client_send(&rq, sizeof(rq));
|
||||
if (rc)
|
||||
printf("%s\n", ptr);
|
||||
else
|
||||
printf("%-18s %s\n", ptr, rq.data);
|
||||
|
||||
ptr = strtok(NULL, " ");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* runlevel_string - Convert a bit encoded runlevel to .conf syntax
|
||||
* @levels: Bit encoded runlevels
|
||||
@@ -707,16 +734,17 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *exit_status(int status, char *buf, size_t len)
|
||||
static char *exit_status(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
int rc, sig;
|
||||
|
||||
rc = WEXITSTATUS(status);
|
||||
sig = WTERMSIG(status);
|
||||
rc = WEXITSTATUS(svc->status);
|
||||
sig = WTERMSIG(svc->status);
|
||||
|
||||
if (WIFEXITED(status))
|
||||
snprintf(buf, len, " (code=exited, status=%d%s)", rc, code2str(rc));
|
||||
else if (WIFSIGNALED(status))
|
||||
if (WIFEXITED(svc->status))
|
||||
snprintf(buf, len, " (code=exited, status=%d%s%s)", rc, code2str(rc),
|
||||
svc->manual ? ", manual=yes" : "");
|
||||
else if (WIFSIGNALED(svc->status))
|
||||
snprintf(buf, len, " (code=signal, status=%d%s)", sig, sig2str(sig));
|
||||
|
||||
return buf;
|
||||
@@ -732,7 +760,7 @@ static char *status(svc_t *svc, int full)
|
||||
s = svc_status(svc);
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
exit_status(svc->status, ok, sizeof(ok));
|
||||
exit_status(svc, ok, sizeof(ok));
|
||||
color = "\e[1m";
|
||||
break;
|
||||
|
||||
@@ -741,7 +769,7 @@ static char *status(svc_t *svc, int full)
|
||||
break;
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
exit_status(svc->status, ok, sizeof(ok));
|
||||
exit_status(svc, ok, sizeof(ok));
|
||||
if (WIFEXITED(svc->status)) {
|
||||
if (WEXITSTATUS(svc->status))
|
||||
color = "\e[1;31m";
|
||||
@@ -756,7 +784,7 @@ static char *status(svc_t *svc, int full)
|
||||
break;
|
||||
|
||||
default:
|
||||
exit_status(svc->status, ok, sizeof(ok));
|
||||
exit_status(svc, ok, sizeof(ok));
|
||||
color = "\e[1;33m";
|
||||
break;
|
||||
}
|
||||
@@ -788,6 +816,28 @@ static void show_cgroup_tree(char *group, char *pfx)
|
||||
cgroup_tree(path, pfx, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* arg: 'foo' should match foo:1 foo:2, etc. but not foobar
|
||||
* arg: 'foo:1' should only match foo:1
|
||||
* arg: 'foo:' is allowed to fail, unsupported syntax atm
|
||||
* arg: 'foo:*' is allowed to fail, unsupported syntax atm
|
||||
*/
|
||||
static int svc_compare(svc_t *svc, char *arg)
|
||||
{
|
||||
char ident[MAX_IDENT_LEN];
|
||||
char *ptr;
|
||||
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
ptr = strchr(ident, ':');
|
||||
if (ptr && !strchr(arg, ':'))
|
||||
*ptr = 0;
|
||||
|
||||
if (!strcmp(ident, arg))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_status(char *arg)
|
||||
{
|
||||
char ident[MAX_IDENT_LEN];
|
||||
@@ -801,17 +851,11 @@ static int show_status(char *arg)
|
||||
long now = jiffies();
|
||||
char uptm[42] = "N/A";
|
||||
char *pidfn = NULL;
|
||||
int exact = 0;
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
if (string_compare(ident, arg))
|
||||
num++;
|
||||
if (string_case_compare(ident, arg))
|
||||
exact++;
|
||||
}
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0))
|
||||
num += svc_compare(svc, arg);
|
||||
|
||||
if (num > 1 && !exact)
|
||||
if (num > 1)
|
||||
break;
|
||||
|
||||
svc = client_svc_find(arg);
|
||||
@@ -846,6 +890,8 @@ static int show_status(char *arg)
|
||||
printf(" User : %s\n", svc->username);
|
||||
printf(" Group : %s\n", svc->group);
|
||||
printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
|
||||
if (svc->manual)
|
||||
printf(" Starts : %d\n", svc->once);
|
||||
printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
|
||||
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||
if (cgrp && svc->pid > 1) {
|
||||
@@ -883,14 +929,14 @@ static int show_status(char *arg)
|
||||
else
|
||||
strlcat(title, "COMMAND", sizeof(title));
|
||||
|
||||
print_header(title);
|
||||
print_header("%s", title);
|
||||
}
|
||||
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
char *lvls;
|
||||
|
||||
svc_ident(svc, ident, sizeof(ident));
|
||||
if (num && !string_compare(ident, arg))
|
||||
if (num && !svc_compare(svc, arg))
|
||||
continue;
|
||||
|
||||
printf("%-*d ", pw, svc->pid);
|
||||
@@ -1025,7 +1071,7 @@ static int usage(int rc)
|
||||
" cond get <COND> Get status of user-defined condition, see $? and -v\n"
|
||||
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
|
||||
" cond status Show condition status, default cond command\n"
|
||||
" cond dump Dump all conditions and their status\n"
|
||||
" cond dump [TYPE] Dump all, or a type of, conditions and their status\n"
|
||||
"\n"
|
||||
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
|
||||
" start <NAME>[:ID] Start service by name, with optional ID\n"
|
||||
@@ -1045,6 +1091,8 @@ static int usage(int rc)
|
||||
" ps List processes based on cgroups\n"
|
||||
" top Show top-like listing based on cgroups\n");
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
" plugins List installed plugins\n"
|
||||
"\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" reboot Reboot system\n"
|
||||
@@ -1176,6 +1224,8 @@ int main(int argc, char *argv[])
|
||||
{ "ps", NULL, show_cgps, &cgrp, NULL },
|
||||
{ "top", NULL, show_cgtop, &cgrp, NULL },
|
||||
|
||||
{ "plugins", NULL, plugins_list, NULL, NULL },
|
||||
|
||||
{ "runlevel", NULL, do_runlevel, NULL, NULL },
|
||||
{ "reboot", NULL, do_reboot, NULL, NULL },
|
||||
{ "halt", NULL, do_halt, NULL, NULL },
|
||||
|
||||
+1
-1
@@ -49,7 +49,6 @@ static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
*/
|
||||
int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
@@ -60,6 +59,7 @@ int logrotate(char *file, int num, off_t sz)
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
int cnt;
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
|
||||
+62
-6
@@ -260,8 +260,8 @@ int plugin_io_init(plugin_t *p)
|
||||
/* Setup any I/O callbacks for plugins that use them */
|
||||
static int init_plugins(uev_ctx_t *ctx)
|
||||
{
|
||||
int fail = 0;
|
||||
plugin_t *p, *tmp;
|
||||
int fail = 0;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (plugin_io_init(p))
|
||||
@@ -302,7 +302,7 @@ static int load_one(char *path, char *name)
|
||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
|
||||
|
||||
_d("Loading plugin %s ...", basename(sofile));
|
||||
_d("Loading plugin %s ...", sofile);
|
||||
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle) {
|
||||
_e("Failed loading plugin %s: %s", sofile, dlerror());
|
||||
@@ -356,6 +356,8 @@ static int load_plugins(char *path)
|
||||
|
||||
dp = opendir(path);
|
||||
if (!dp) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
@@ -381,14 +383,68 @@ static int load_plugins(char *path)
|
||||
}
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
int plugin_list(char *buf, size_t len)
|
||||
{
|
||||
#ifndef ENABLE_STATIC
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
buf[0] = 0;
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (buf[0])
|
||||
strlcat(buf, " ", len);
|
||||
strlcat(buf, p->name, len);
|
||||
}
|
||||
#else
|
||||
buf[0] = 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_deps(char *buf, size_t len)
|
||||
{
|
||||
#ifndef ENABLE_STATIC
|
||||
plugin_t *p;
|
||||
|
||||
p = plugin_find(buf);
|
||||
buf[0] = 0;
|
||||
if (p) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PLUGIN_DEP_MAX; i++) {
|
||||
if (!p->depends[i])
|
||||
continue;
|
||||
|
||||
if (buf[0])
|
||||
strlcat(buf, " ", len);
|
||||
strlcat(buf, p->depends[i], len);
|
||||
}
|
||||
}
|
||||
#else
|
||||
buf[0] = 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int fail = 1;
|
||||
load_plugins(PLUGIN_PATH);
|
||||
|
||||
if (!load_plugins(PLUGIN_PATH))
|
||||
fail = init_plugins(ctx);
|
||||
#ifdef EXTERNAL_PLUGIN_PATH
|
||||
char *paths = strdup(EXTERNAL_PLUGIN_PATH);
|
||||
char *path;
|
||||
|
||||
return fail;
|
||||
if (paths) {
|
||||
_d("Loading external plugins from %s ...", paths);
|
||||
path = strtok(paths, ":");
|
||||
while (path) {
|
||||
load_plugins(path);
|
||||
path = strtok(NULL, ":");
|
||||
}
|
||||
free(paths);
|
||||
}
|
||||
#endif
|
||||
|
||||
return init_plugins(ctx);
|
||||
}
|
||||
|
||||
void plugin_exit(void)
|
||||
|
||||
@@ -146,6 +146,9 @@ typedef struct plugin {
|
||||
/* Public plugin API */
|
||||
int plugin_io_init (plugin_t *plugin);
|
||||
|
||||
int plugin_list (char *buf, size_t len);
|
||||
int plugin_deps (char *buf, size_t len);
|
||||
|
||||
int plugin_register (plugin_t *plugin);
|
||||
int plugin_unregister (plugin_t *plugin);
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
#include "plugin.h"
|
||||
|
||||
#define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
|
||||
extern int service_interval;
|
||||
|
||||
int api_init (uev_ctx_t *ctx);
|
||||
|
||||
+21
-9
@@ -43,6 +43,8 @@ static cmd_t cmd = CMD_UNKNOWN;
|
||||
static char *msg = NULL;
|
||||
|
||||
/* initctl API */
|
||||
extern int timeout;
|
||||
|
||||
extern int do_reboot (char *arg);
|
||||
extern int do_halt (char *arg);
|
||||
extern int do_poweroff(char *arg);
|
||||
@@ -103,12 +105,13 @@ static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
|
||||
"Options:\n"
|
||||
" --help This help text\n"
|
||||
" -f, --force Force unsafe %s now, do not contact the init system.\n"
|
||||
" --halt Halt system, regardless of how the command is called.\n"
|
||||
" -h Halt or power off after shutdown.\n"
|
||||
" -P, --poweroff Power-off system, regardless of how the command is called.\n"
|
||||
" -r, --reboot Reboot system, regardless of how the command is called.\n"
|
||||
" -?, --help This help text\n"
|
||||
" -f, --force Force unsafe %s now, do not contact the init system.\n"
|
||||
" --halt Halt system, regardless of how the command is called.\n"
|
||||
" -h Halt or power off after shutdown.\n"
|
||||
" -P, --poweroff Power-off system, regardless of how the command is called.\n"
|
||||
" -r, --reboot Reboot system, regardless of how the command is called.\n"
|
||||
" -t, --timeout=SEC Force reboot/shutdown after a given timeout\n"
|
||||
"\n", prognm, msg);
|
||||
|
||||
return rc;
|
||||
@@ -116,20 +119,22 @@ static int usage(int rc)
|
||||
|
||||
int reboot_main(int argc, char *argv[])
|
||||
{
|
||||
int c, force = 0;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"help", 0, NULL, '?'},
|
||||
{"force", 0, NULL, 'f'},
|
||||
{"halt", 0, NULL, 'H'},
|
||||
{"poweroff", 0, NULL, 'p'},
|
||||
{"reboot", 0, NULL, 'r'},
|
||||
{"timeout", 1, NULL, 't'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
int c, force = 0;
|
||||
int tmp;
|
||||
|
||||
/* Initial command taken from program name */
|
||||
transform(prognm);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h?fHPpr", long_options, NULL)) != EOF) {
|
||||
while ((c = getopt_long(argc, argv, "h?fHPprt:", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case '?':
|
||||
return usage(0);
|
||||
@@ -152,6 +157,13 @@ int reboot_main(int argc, char *argv[])
|
||||
cmd = CMD_REBOOT;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
tmp = atoi(optarg);
|
||||
if (tmp < 1)
|
||||
errx(1, "Invalid forced timeout value.");
|
||||
timeout = tmp;
|
||||
break;
|
||||
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
+82
-57
@@ -731,10 +731,14 @@ static void service_cleanup(svc_t *svc)
|
||||
* service_stop - Stop service
|
||||
* @svc: Service to stop
|
||||
*
|
||||
* Called externally by initctl to perform stop/start (restart) of
|
||||
* services. Internally it is used to bring a run/task/service to
|
||||
* HALTED state.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the service was successfully stopped. Non-zero otherwise.
|
||||
*/
|
||||
static int service_stop(svc_t *svc)
|
||||
int service_stop(svc_t *svc)
|
||||
{
|
||||
char cmdline[CMD_SIZE] = "";
|
||||
int do_progress = 1;
|
||||
@@ -804,7 +808,7 @@ static int service_stop(svc_t *svc)
|
||||
|
||||
args[i++] = svc->cmd;
|
||||
/* this handles, e.g., bridge-stop br0 stop */
|
||||
for (j = 0; j < MAX_NUM_SVC_ARGS; j++) {
|
||||
for (j = 0; j < MAX_NUM_SVC_ARGS - 2; j++) {
|
||||
if (!strlen(svc->args[j]))
|
||||
break;
|
||||
args[i++] = svc->args[j];
|
||||
@@ -1140,7 +1144,6 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
svc->args_dirty = (diff > 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* service_register - Register service, task or run commands
|
||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||
@@ -1249,6 +1252,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
|
||||
while (cmd) {
|
||||
char *arg;
|
||||
|
||||
if (type == SVC_TYPE_TTY && cmd[0] == '@')
|
||||
break; /* @console */
|
||||
|
||||
@@ -1260,44 +1265,48 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
cond = &cmd[1];
|
||||
else if (cmd[0] == ':') /* :ID */
|
||||
id = &cmd[1];
|
||||
else if (!strncasecmp(cmd, "log", 3))
|
||||
else if (MATCH_CMD(cmd, "log", arg))
|
||||
log = cmd;
|
||||
else if (!strncasecmp(cmd, "pid", 3))
|
||||
else if (MATCH_CMD(cmd, "pid", arg))
|
||||
pid = cmd;
|
||||
else if (!strncasecmp(cmd, "name:", 5))
|
||||
else if (MATCH_CMD(cmd, "name:", arg))
|
||||
name = cmd;
|
||||
else if (!strncasecmp(cmd, "type:forking", 10))
|
||||
else if (MATCH_CMD(cmd, "type:forking", arg))
|
||||
forking = 1;
|
||||
else if (!strncasecmp(cmd, "manual:yes", 10))
|
||||
else if (MATCH_CMD(cmd, "manual:yes", arg))
|
||||
manual = 1;
|
||||
else if (!strncasecmp(cmd, "restart:", 8))
|
||||
restart_max = atoi(&cmd[8]);
|
||||
else if (!strncasecmp(cmd, "restarttmo:", 11)) /* compat alias */
|
||||
restart_tmo = atoi(&cmd[11]) * 1000;
|
||||
else if (!strncasecmp(cmd, "restart_sec:", 12))
|
||||
restart_tmo = atoi(&cmd[12]) * 1000;
|
||||
else if (!strncasecmp(cmd, "norestart", 9))
|
||||
else if (MATCH_CMD(cmd, "restart:", arg)) {
|
||||
if (MATCH_CMD(arg, "always", arg))
|
||||
restart_max = -1;
|
||||
else
|
||||
restart_max = atoi(arg);
|
||||
}
|
||||
else if (MATCH_CMD(cmd, "restarttmo:", arg)) /* compat alias */
|
||||
restart_tmo = atoi(arg) * 1000;
|
||||
else if (MATCH_CMD(cmd, "restart_sec:", arg))
|
||||
restart_tmo = atoi(arg) * 1000;
|
||||
else if (MATCH_CMD(cmd, "norestart", arg))
|
||||
restart_max = 0;
|
||||
else if (!strncasecmp(cmd, "oncrash:", 8)) {
|
||||
if (!strncasecmp(&cmd[8], "reboot", 6))
|
||||
else if (MATCH_CMD(cmd, "oncrash:", arg)) {
|
||||
if (MATCH_CMD(arg, "reboot", arg))
|
||||
oncrash_action = SVC_ONCRASH_REBOOT;
|
||||
}
|
||||
else if (!strncasecmp(cmd, "respawn", 7))
|
||||
else if (MATCH_CMD(cmd, "respawn", arg))
|
||||
respawn = 1;
|
||||
else if (!strncasecmp(cmd, "halt:", 5))
|
||||
halt = &cmd[5];
|
||||
else if (!strncasecmp(cmd, "kill:", 5))
|
||||
delay = &cmd[5];
|
||||
else if (!strncasecmp(cmd, "pre:", 4))
|
||||
pre_script = &cmd[4];
|
||||
else if (!strncasecmp(cmd, "post:", 5))
|
||||
post_script = &cmd[5];
|
||||
else if (!strncasecmp(cmd, "env:", 4))
|
||||
env = &cmd[4];
|
||||
else if (!strncasecmp(cmd, "cgroup:", 7))
|
||||
cgroup = &cmd[7]; /* only settings */
|
||||
else if (!strncasecmp(cmd, "cgroup.", 7))
|
||||
cgroup = &cmd[6]; /* with group */
|
||||
else if (MATCH_CMD(cmd, "halt:", arg))
|
||||
halt = arg;
|
||||
else if (MATCH_CMD(cmd, "kill:", arg))
|
||||
delay = arg;
|
||||
else if (MATCH_CMD(cmd, "pre:", arg))
|
||||
pre_script = arg;
|
||||
else if (MATCH_CMD(cmd, "post:", arg))
|
||||
post_script = arg;
|
||||
else if (MATCH_CMD(cmd, "env:", arg))
|
||||
env = arg;
|
||||
else if (MATCH_CMD(cmd, "cgroup:", arg))
|
||||
cgroup = arg; /* only settings */
|
||||
else if (MATCH_CMD(cmd, "cgroup.", arg))
|
||||
cgroup = arg; /* with group */
|
||||
else
|
||||
break;
|
||||
|
||||
@@ -1365,18 +1374,17 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
if (type == SVC_TYPE_SERVICE && manual)
|
||||
if (manual)
|
||||
svc_stop(svc);
|
||||
|
||||
svc->restart_max = restart_max;
|
||||
svc->restart_tmo = restart_tmo;
|
||||
svc->oncrash_action = oncrash_action;
|
||||
} else {
|
||||
_d("Found existing svc for %s name %s id %s type %d", cmd, name, id, type);
|
||||
|
||||
/* update type, may have changed from service -> task */
|
||||
svc->type = type;
|
||||
|
||||
/* update path, may have changed on reload */
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
|
||||
/* e.g., if missing cmd or env before */
|
||||
if (!manual)
|
||||
svc_unblock(svc);
|
||||
@@ -1458,8 +1466,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
parse_env(svc, env);
|
||||
if (file)
|
||||
strlcpy(svc->file, file, sizeof(svc->file));
|
||||
svc->manual = manual;
|
||||
svc->respawn = respawn;
|
||||
svc->forking = forking;
|
||||
svc->restart_max = restart_max;
|
||||
svc->restart_tmo = restart_tmo;
|
||||
svc->oncrash_action = oncrash_action;
|
||||
|
||||
/* Decode any (optional) pid:/optional/path/to/file.pid */
|
||||
if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
|
||||
@@ -1755,7 +1767,7 @@ static void service_retry(svc_t *svc)
|
||||
}
|
||||
|
||||
/* Peak instability index */
|
||||
if (*restart_cnt >= svc->restart_max) {
|
||||
if (svc->restart_max != -1 && *restart_cnt >= svc->restart_max) {
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
|
||||
svc_ident(svc, NULL, 0));
|
||||
svc_crashing(svc);
|
||||
@@ -1877,18 +1889,15 @@ restart:
|
||||
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
if (enabled) {
|
||||
if (svc_has_pre(svc)) {
|
||||
svc_set_state(svc, SVC_SETUP_STATE);
|
||||
service_pre_script(svc);
|
||||
} else
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
}
|
||||
if (enabled)
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
break;
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
if (svc_is_changed(svc))
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
if (svc_is_runtask(svc) && svc_is_manual(svc) && enabled)
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
break;
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
@@ -1912,6 +1921,8 @@ restart:
|
||||
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
if (svc->manual)
|
||||
svc_stop(svc);
|
||||
svc_set_state(svc, SVC_DONE_STATE);
|
||||
break;
|
||||
|
||||
@@ -1929,7 +1940,7 @@ restart:
|
||||
|
||||
case SVC_SETUP_STATE:
|
||||
if (!svc->pid)
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
svc_set_state(svc, SVC_STARTING_STATE);
|
||||
break;
|
||||
|
||||
case SVC_READY_STATE:
|
||||
@@ -1940,22 +1951,36 @@ restart:
|
||||
if (sm_is_in_teardown(&sm))
|
||||
break;
|
||||
|
||||
err = service_start(svc);
|
||||
if (err) {
|
||||
if (svc_is_missing(svc)) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
break;
|
||||
}
|
||||
(*restart_cnt)++;
|
||||
if (svc_has_pre(svc)) {
|
||||
svc_set_state(svc, SVC_SETUP_STATE);
|
||||
service_pre_script(svc);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Everything went fine, clean and set state */
|
||||
svc_mark_clean(svc);
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
svc_set_state(svc, SVC_STARTING_STATE);
|
||||
}
|
||||
break;
|
||||
|
||||
case SVC_STARTING_STATE:
|
||||
if (!enabled) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
break;
|
||||
}
|
||||
|
||||
err = service_start(svc);
|
||||
if (err) {
|
||||
if (svc_is_missing(svc)) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
break;
|
||||
}
|
||||
(*restart_cnt)++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Everything went fine, clean and set state */
|
||||
svc_mark_clean(svc);
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
break;
|
||||
|
||||
case SVC_RUNNING_STATE:
|
||||
if (!enabled) {
|
||||
service_stop(svc);
|
||||
|
||||
@@ -40,6 +40,7 @@ int service_timeout_cancel (svc_t *svc);
|
||||
|
||||
void service_forked (svc_t *svc);
|
||||
|
||||
int service_stop (svc_t *svc);
|
||||
int service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
void service_worker (void *unused);
|
||||
|
||||
@@ -63,6 +63,7 @@ typedef enum {
|
||||
SVC_SETUP_STATE, /* Running pre: script */
|
||||
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
||||
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
||||
SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */
|
||||
SVC_RUNNING_STATE, /* Process running */
|
||||
} svc_state_t;
|
||||
|
||||
@@ -121,7 +122,8 @@ typedef struct svc {
|
||||
int status; /* From waitpid() when process is collected */
|
||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
svc_type_t type; /* Service, run, task, ... */
|
||||
int protect; /* Services like dbus-daemon & udev by Finit */
|
||||
char protect; /* Services like dbus-daemon & udev by Finit */
|
||||
char manual; /* run/task that require `initctl start foo` */
|
||||
const int dirty; /* 0: unmodified, 1: modified */
|
||||
const int removed;
|
||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||
@@ -139,7 +141,7 @@ typedef struct svc {
|
||||
/* Counters */
|
||||
char once; /* run/task, (at least) once per runlevel */
|
||||
unsigned int restart_tot; /* Total restarts ever, summarized, including `initctl restart` */
|
||||
unsigned char restart_max; /* Maximum number of restarts allowed */
|
||||
int restart_max; /* Maximum number of restarts allowed */
|
||||
int restart_tmo; /* Time required for the service to start. */
|
||||
unsigned char oncrash_action; /* Action to perform in crashed state. */
|
||||
char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */
|
||||
@@ -229,6 +231,7 @@ static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV
|
||||
static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY == svc->type; }
|
||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);}
|
||||
static inline int svc_is_forking (svc_t *svc) { return svc && svc->forking; }
|
||||
static inline int svc_is_manual (svc_t *svc) { return svc && svc->manual; }
|
||||
|
||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
|
||||
|
||||
@@ -154,6 +154,7 @@ char *tty_atcon(void)
|
||||
int tty_parse_args(char *cmd, struct tty *tty)
|
||||
{
|
||||
char *dev = NULL;
|
||||
int passenv = 0;
|
||||
size_t i;
|
||||
|
||||
do {
|
||||
@@ -165,6 +166,8 @@ int tty_parse_args(char *cmd, struct tty *tty)
|
||||
tty->nologin = 1;
|
||||
else if (!strcmp(cmd, "notty"))
|
||||
tty->notty = 1; /* for board bringup */
|
||||
else if (!strcmp(cmd, "passenv"))
|
||||
passenv = 1;
|
||||
else if (!strcmp(cmd, "rescue"))
|
||||
tty->rescue = 1; /* for rescue shells */
|
||||
else if (whichp(cmd)) /* in $PATH? */
|
||||
@@ -228,8 +231,15 @@ int tty_parse_args(char *cmd, struct tty *tty)
|
||||
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
|
||||
|
||||
/* Built-in getty now comes as standalone program */
|
||||
if (!tty->cmd)
|
||||
if (!tty->cmd) {
|
||||
if (passenv && tty->num < NELEMS(tty->args)) {
|
||||
for (i = tty->num; i > 0; i--)
|
||||
tty->args[i] = tty->args[i - 1];
|
||||
tty->args[0] = "-p";
|
||||
tty->num++;
|
||||
}
|
||||
tty->cmd = _PATH_GETTY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
||||
EXTRA_DIST += cond-start-task.sh
|
||||
EXTRA_DIST += failing-sysv.sh
|
||||
EXTRA_DIST += global-envs.sh
|
||||
EXTRA_DIST += initctl-status-subset.sh
|
||||
EXTRA_DIST += pre-post-serv.sh
|
||||
EXTRA_DIST += restart-self.sh
|
||||
EXTRA_DIST += runlevel.sh
|
||||
EXTRA_DIST += start-stop-service.sh
|
||||
EXTRA_DIST += start-stop-service-sub-config.sh
|
||||
@@ -51,7 +53,9 @@ TESTS += add-remove-dynamic-service-sub-config.sh
|
||||
TESTS += cond-start-task.sh
|
||||
TESTS += failing-sysv.sh
|
||||
TESTS += global-envs.sh
|
||||
TESTS += initctl-status-subset.sh
|
||||
TESTS += pre-post-serv.sh
|
||||
TESTS += restart-self.sh
|
||||
TESTS += runlevel.sh
|
||||
TESTS += start-stop-service.sh
|
||||
TESTS += start-stop-service-sub-config.sh
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
noinst_PROGRAMS = serv
|
||||
|
||||
serv_SOURCES = serv.c
|
||||
serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
|
||||
+39
-3
@@ -21,7 +21,7 @@ static void verify_env(char *arg)
|
||||
char *replica;
|
||||
char *env;
|
||||
|
||||
key = replica = strdup(arg);
|
||||
key = replica = strdupa(arg);
|
||||
if (!replica)
|
||||
err(1, "Failed duplicating arg %s", arg);
|
||||
|
||||
@@ -47,6 +47,28 @@ static void verify_noenv(char *key)
|
||||
errx(1, "Error, key %s is set in environment to '%s'", key, val);
|
||||
}
|
||||
|
||||
static void inc_restarts(void)
|
||||
{
|
||||
char buf[10];
|
||||
int cnt = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/tmp/serv-restart.cnt", "r+");
|
||||
if (!fp) {
|
||||
fp = fopen("/tmp/serv-restart.cnt", "w");
|
||||
if (!fp)
|
||||
err(1, "failed creating restart counter file");
|
||||
} else {
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
cnt = atoi(buf);
|
||||
}
|
||||
cnt++;
|
||||
|
||||
fseek(fp, SEEK_SET, 0);
|
||||
fprintf(fp, "%d", cnt);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static void sig(int signo)
|
||||
{
|
||||
warnx("We got signal %d ...", signo);
|
||||
@@ -86,6 +108,7 @@ static int usage(int rc)
|
||||
" -n Run in foreground\n"
|
||||
" -p Create PID file despite running in foreground\n"
|
||||
" -P FILE Create PID file using FILE\n"
|
||||
" -r SVC Call initctl to restart service SVC (self)\n"
|
||||
"\n"
|
||||
"By default this program daemonizes itself to the background, and,\n"
|
||||
"when it's done setting up its signal handler(s), creates a PID file\n"
|
||||
@@ -100,10 +123,12 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
int do_background = 1;
|
||||
int do_pidfile = 1;
|
||||
int do_restart = 0;
|
||||
char *pidfn = NULL;
|
||||
char cmd[80];
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "e:E:hnpP:")) != EOF) {
|
||||
while ((c = getopt(argc, argv, "e:E:hnpP:r:")) != EOF) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
return usage(0);
|
||||
@@ -123,6 +148,10 @@ int main(int argc, char *argv[])
|
||||
case 'P':
|
||||
pidfn = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
snprintf(cmd, sizeof(cmd), "initctl restart %s", optarg);
|
||||
do_restart = 1;
|
||||
break;
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
@@ -142,8 +171,15 @@ int main(int argc, char *argv[])
|
||||
pidfile(pidfn);
|
||||
|
||||
warnx("Entering while(1) loop");
|
||||
while (1)
|
||||
while (1) {
|
||||
sleep(1);
|
||||
if (do_restart) {
|
||||
inc_restarts();
|
||||
if (system(cmd))
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
warnx("Leaving ...");
|
||||
return 0;
|
||||
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
# Verify that the status command 'initctl status foo' matches three
|
||||
# instances of tasks called foo:1, foo:2, and foo:3. It should not
|
||||
# match the fourth task called foobar. This is a regression test to
|
||||
# ensure issue #275 doesn't happen again.
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
test_setup()
|
||||
{
|
||||
say "Test start $(date)"
|
||||
}
|
||||
|
||||
test_teardown()
|
||||
{
|
||||
say "Test done $(date)"
|
||||
|
||||
texec rm -f "$FINIT_CONF"
|
||||
}
|
||||
|
||||
test_init()
|
||||
{
|
||||
texec sh -c "echo '# Three foo and one bar enter Finit' > $FINIT_CONF"
|
||||
}
|
||||
|
||||
test_add_one()
|
||||
{
|
||||
service=$1
|
||||
|
||||
say "Add service stanza '$service' to $FINIT_CONF ..."
|
||||
texec sh -c "echo '$service' >> $FINIT_CONF"
|
||||
}
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/tenv/lib.sh"
|
||||
|
||||
test_init
|
||||
|
||||
test_add_one "task manual:yes name:foo :1 serv -- Foo #1"
|
||||
test_add_one "task manual:yes name:foo :2 serv -- Foo #2"
|
||||
test_add_one "task manual:yes name:foo :3 serv -- Foo #3"
|
||||
test_add_one "task manual:yes name:foobar serv -- Foobar"
|
||||
|
||||
say 'Reload Finit'
|
||||
#texec sh -c "initctl debug"
|
||||
texec sh -c "initctl reload"
|
||||
texec sh -c "initctl status"
|
||||
|
||||
#texec sh -c "initctl status"
|
||||
#texec sh -c "ps"
|
||||
#texec sh -c "initctl status serv"
|
||||
|
||||
assert_num_services 3 foo
|
||||
assert_desc "Foobar" foobar
|
||||
assert_desc "Foo #1" foo:1
|
||||
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
# Verify that a service can restart itself, issue #280
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
test_setup()
|
||||
{
|
||||
say "Test start $(date)"
|
||||
texec sh -c "mkdir -p /etc/default"
|
||||
}
|
||||
|
||||
test_teardown()
|
||||
{
|
||||
say "Test done $(date)"
|
||||
say "Running test teardown."
|
||||
|
||||
texec rm -f "$FINIT_CONF"
|
||||
}
|
||||
|
||||
check_restarts()
|
||||
{
|
||||
assert "serv restarts" "$(texec cat "$1" | awk '{print $1;}')" -ge "$2"
|
||||
}
|
||||
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/tenv/lib.sh"
|
||||
|
||||
say "Add stanza to $FINIT_CONF"
|
||||
texec sh -c "echo 'service serv -np -r serv -- Restart self' > $FINIT_CONF"
|
||||
|
||||
say 'Reload Finit'
|
||||
texec sh -c "initctl reload"
|
||||
|
||||
sleep 2
|
||||
texec sh -c "initctl status serv"
|
||||
texec sh -c "ps"
|
||||
|
||||
sleep 2
|
||||
texec sh -c "initctl status serv"
|
||||
texec sh -c "ps"
|
||||
|
||||
say 'Pending restarts by itself ...'
|
||||
retry 'check_restarts /tmp/serv-restart.cnt 3' 20 1
|
||||
|
||||
return 0
|
||||
@@ -31,6 +31,11 @@ assert_num_children()
|
||||
assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1"
|
||||
}
|
||||
|
||||
assert_num_services()
|
||||
{
|
||||
assert "$1 services are loaded" "$(texec initctl -t status "$2" | wc -l)" -eq "$1"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
assert_new_pid()
|
||||
{
|
||||
@@ -42,6 +47,11 @@ assert_restarts()
|
||||
assert "Finit has registered restarts" "$(texec initctl status "$2" | awk '/Restarts/{print $3;}')" -ge "$1"
|
||||
}
|
||||
|
||||
assert_desc()
|
||||
{
|
||||
assert "Service description == $1" "$(texec initctl status "$2" | grep 'Description' | sed 's/Description : //')" = "$1"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
assert_pidfile()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user