mirror of
https://github.com/troglobit/finit.git
synced 2026-10-31 13:00:32 +00:00
Merge pull request #491 from finit-project/devman
Finit 5: udev/eudev replacement Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
94 files changed
+9744
-605
No files matched your search
+2
-9
@@ -13,9 +13,6 @@ pkglibexec_PROGRAMS = getty logit runparts tmpfiles
|
||||
if SULOGIN
|
||||
pkglibexec_PROGRAMS += sulogin
|
||||
endif
|
||||
if KEVENTD
|
||||
pkglibexec_PROGRAMS += keventd
|
||||
endif
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS += watchdogd
|
||||
endif
|
||||
@@ -30,11 +27,6 @@ getty_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
getty_CFLAGS += $(lite_CFLAGS)
|
||||
getty_LDADD = $(lite_LIBS)
|
||||
|
||||
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h
|
||||
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
keventd_CFLAGS += $(lite_CFLAGS)
|
||||
keventd_LDADD = $(lite_LIBS)
|
||||
|
||||
runparts_SOURCES = runparts.c
|
||||
runparts_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||
runparts_CFLAGS += $(lite_CFLAGS)
|
||||
@@ -105,11 +97,12 @@ endif
|
||||
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||
client.c client.h cond.c cond.h reboot.c \
|
||||
serv.c serv.h svc.h util.c util.h log.h
|
||||
initctl_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
if DBUS
|
||||
initctl_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/libink
|
||||
initctl_CPPFLAGS += -I$(top_srcdir)/libink
|
||||
initctl_LDADD += $(top_builddir)/libink/libink.la
|
||||
endif
|
||||
|
||||
|
||||
@@ -61,53 +61,19 @@ static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
|
||||
static int stop(svc_t *svc, void *user_data)
|
||||
{
|
||||
(void)user_data;
|
||||
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
svc_stop(svc);
|
||||
service_step(svc);
|
||||
if (!IS_RESERVED_RUNLEVEL(runlevel))
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
return 0;
|
||||
return service_stop_now(svc);
|
||||
}
|
||||
|
||||
static int start(svc_t *svc, void *user_data)
|
||||
{
|
||||
(void)user_data;
|
||||
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
svc_start(svc);
|
||||
service_step(svc);
|
||||
if (!IS_RESERVED_RUNLEVEL(runlevel))
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
return 0;
|
||||
return service_start_now(svc);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: this does not wait for svc to be stopped first, that is the
|
||||
* responsibility of initctl to do. Otherwise we'd block PID 1,
|
||||
* or introduce some nasty race conditions.
|
||||
*/
|
||||
static int restart(svc_t *svc, void *user_data)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
if (!svc_is_running(svc))
|
||||
return start(svc, user_data);
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
service_stop(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
(void)user_data;
|
||||
return service_restart_now(svc);
|
||||
}
|
||||
|
||||
static int reload(svc_t *svc, void *user_data)
|
||||
@@ -222,16 +188,7 @@ static svc_t *do_find_byc(char *buf, size_t len)
|
||||
return svc_find_by_cond(input);
|
||||
}
|
||||
|
||||
static void bypass_shutdown(void *);
|
||||
struct wq emergency = { .cb = bypass_shutdown };
|
||||
|
||||
static void bypass_shutdown(void *unused)
|
||||
{
|
||||
(void)unused;
|
||||
|
||||
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
|
||||
do_shutdown(halt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle switch_root API command.
|
||||
@@ -302,10 +259,7 @@ static int do_reboot(int cmd, int timeout, char *buf, size_t len)
|
||||
return 255;
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
emergency.delay = timeout * 1000;
|
||||
schedule_work(&emergency);
|
||||
}
|
||||
shutdown_bypass(timeout);
|
||||
|
||||
switch (cmd) {
|
||||
case INIT_CMD_REBOOT:
|
||||
@@ -375,8 +329,8 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static svc_t *iter = NULL;
|
||||
struct init_request rq;
|
||||
int sd, lvl;
|
||||
svc_t *svc;
|
||||
int sd;
|
||||
|
||||
(void)arg;
|
||||
|
||||
@@ -454,14 +408,6 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RUNLVL:
|
||||
/* Allow changing cfglevel in runlevel S */
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||
if (runlevel != INIT_LEVEL) {
|
||||
warnx("Cannot abort runlevel 6/0.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (rq.runlevel) {
|
||||
case 's':
|
||||
case 'S':
|
||||
@@ -470,17 +416,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
case '0'...'9':
|
||||
dbg("Setting new runlevel %c", rq.runlevel);
|
||||
lvl = rq.runlevel - '0';
|
||||
if (lvl == 0)
|
||||
halt = SHUT_OFF;
|
||||
if (lvl == 6)
|
||||
halt = SHUT_REBOOT;
|
||||
|
||||
/* User requested change in next runlevel */
|
||||
if (runlevel == INIT_LEVEL)
|
||||
cfglevel = lvl;
|
||||
else
|
||||
sm_runlevel(lvl);
|
||||
sm_request_runlevel(rq.runlevel - '0');
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -7,12 +7,16 @@
|
||||
|
||||
#define COND_BASE "finit/cond"
|
||||
#define COND_DEV "dev/"
|
||||
#define COND_DRIVER "driver/"
|
||||
#define COND_CLASS "class/"
|
||||
#define COND_PID "pid/"
|
||||
#define COND_SYS "sys/"
|
||||
#define COND_USR "usr/"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||
#define _PATH_CONDDEV _PATH_COND COND_DEV
|
||||
#define _PATH_CONDDRIVER _PATH_COND COND_DRIVER
|
||||
#define _PATH_CONDCLASS _PATH_COND COND_CLASS
|
||||
#define _PATH_CONDPID _PATH_COND COND_PID
|
||||
#define _PATH_CONDSYS _PATH_COND COND_SYS
|
||||
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||
|
||||
+31
-28
@@ -701,9 +701,9 @@ char *conf_escape(const char *str, char *buf, size_t len)
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by plugins and similar that dynamically generate system
|
||||
* services. @body holds the block contents, one indented line per
|
||||
* setting.
|
||||
* Generates /run/finit/system/<file> for Finit's built-in services,
|
||||
* which must probe the running system before registering. @fmt holds
|
||||
* the block contents, one indented line per setting.
|
||||
*/
|
||||
void conf_save_service(int type, const char *name, char *file, const char *fmt, ...)
|
||||
{
|
||||
@@ -2307,28 +2307,23 @@ int conf_reload(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Next, read all *.conf in /lib/finit/system and /etc/finit.d/
|
||||
* The system files were previously created at runtime by plugins
|
||||
* but are now regular files that can be overridden by files in
|
||||
* /etc/finit.d -- similar to how tmfiles.d(5) work. E.g., add
|
||||
* an override .conf, or an ignore by symlinking to /dev/null
|
||||
* Next, read all *.conf in /run/finit/system, /lib/finit/system,
|
||||
* and /etc/finit.d/. The runtime directory holds files Finit
|
||||
* generates for its built-in services (keventd, watchdogd,
|
||||
* runparts), read first so later files can reference them in
|
||||
* if: statements. A same-named file in a later directory
|
||||
* overrides an earlier one, similar to how tmpfiles.d(5) work,
|
||||
* so /etc/finit.d, always read last, is how users override any
|
||||
* built-in or bundled configuration.
|
||||
*
|
||||
* The .conf files (and run/task/service stanzas) are parsed and
|
||||
* started in order. Each directory is sorted alphanumerically
|
||||
* and then the result is appended to the overall order:
|
||||
*
|
||||
* /lib/finit/system/10-hotplug.conf
|
||||
* /lib/finit/system/90-testserv.conf
|
||||
* /run/finit/system/dbus.conf
|
||||
* /run/finit/system/tty.conf
|
||||
* /etc/finit.d/10-abc.conf
|
||||
* /etc/finit.d/20-abc.conf
|
||||
* /etc/finit.d/enabled/1-aaa.conf
|
||||
* /etc/finit.d/enabled/1-abc.conf
|
||||
* /etc/finit.d/enabled/2-aaa.conf
|
||||
* The .conf files (and their run/task/service stanzas) are
|
||||
* parsed and started in order. Each directory is sorted
|
||||
* alphanumerically before being appended to the overall order,
|
||||
* recorded in /run/finit/conf.order at bootstrap. See
|
||||
* doc/config/files.md for an example.
|
||||
*/
|
||||
glob_append(&gl, 0, "%s/*.conf", FINIT_SYSPATH_);
|
||||
glob_append(&gl, 1, "%s/*.conf", FINIT_RUNPATH_);
|
||||
glob_append(&gl, 0, "%s/*.conf", FINIT_RUNPATH_);
|
||||
glob_append(&gl, 1, "%s/*.conf", FINIT_SYSPATH_);
|
||||
glob_append(&gl, 1, "%s/*.conf", finit_rcsd);
|
||||
glob_append(&gl, 1, "%s/enabled/*.conf", finit_rcsd);
|
||||
|
||||
@@ -2683,15 +2678,23 @@ int conf_init(uev_ctx_t *ctx)
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Start kernel event daemon as soon as possible, if enabled
|
||||
* Start kernel event daemon as soon as possible, if enabled.
|
||||
* In passive mode (-p) alongside the hotplug plugin, keventd
|
||||
* only monitors power supply events (sys/pwr/ac condition).
|
||||
*/
|
||||
if (whichp(FINIT_EXECPATH_ "/keventd"))
|
||||
conf_save_service(SVC_TYPE_SERVICE, "keventd", "keventd.conf",
|
||||
"\tdescription = \"Finit kernel event daemon\"\n"
|
||||
"\trunlevel = \"S12345789\"\n"
|
||||
"\tnotify = \"none\"\n"
|
||||
"\tdescription = \"kernel event daemon\"\n"
|
||||
"\trunlevel = \"S0123456789\"\n"
|
||||
"\tnotify = \"pid\"\n"
|
||||
"\tcgroup init {}\n"
|
||||
"\tcommand = \"" FINIT_EXECPATH_ "/keventd\"\n");
|
||||
"\tcommand = \"" FINIT_EXECPATH_ "/keventd"
|
||||
#ifdef HAVE_HOTPLUG_PLUGIN
|
||||
" -p"
|
||||
#else
|
||||
" -c"
|
||||
#endif
|
||||
"\"\n");
|
||||
|
||||
dbg("Allow plugins to register early runlevel 1 run/task/services ...");
|
||||
plugin_run_hooks(HOOK_SVC_PLUGIN);
|
||||
|
||||
+71
-50
@@ -314,46 +314,24 @@ static int manager_get_service(link_call_t *call, void *userdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Service-control helpers used by Start/Stop/Restart/Reload. These
|
||||
* mirror the static helpers in api.c — kept private here so api.c
|
||||
* stays untouched in this increment. */
|
||||
/* svc_parse_jobstr-style adapters over the shared service helpers */
|
||||
|
||||
static int dbus_apply_stop(svc_t *svc, void *user_data)
|
||||
{
|
||||
(void)user_data;
|
||||
if (!svc)
|
||||
return 1;
|
||||
service_timeout_cancel(svc);
|
||||
svc_stop(svc);
|
||||
service_step(svc);
|
||||
if (!IS_RESERVED_RUNLEVEL(runlevel))
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
return 0;
|
||||
return service_stop_now(svc);
|
||||
}
|
||||
|
||||
static int dbus_apply_start(svc_t *svc, void *user_data)
|
||||
{
|
||||
(void)user_data;
|
||||
if (!svc)
|
||||
return 1;
|
||||
service_timeout_cancel(svc);
|
||||
svc_start(svc);
|
||||
service_step(svc);
|
||||
if (!IS_RESERVED_RUNLEVEL(runlevel))
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
return 0;
|
||||
return service_start_now(svc);
|
||||
}
|
||||
|
||||
static int dbus_apply_restart(svc_t *svc, void *user_data)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
if (!svc_is_running(svc))
|
||||
return dbus_apply_start(svc, user_data);
|
||||
service_timeout_cancel(svc);
|
||||
service_stop(svc);
|
||||
service_step(svc);
|
||||
return 0;
|
||||
(void)user_data;
|
||||
return service_restart_now(svc);
|
||||
}
|
||||
|
||||
struct dispatch_ctx {
|
||||
@@ -454,9 +432,7 @@ static int manager_set_runlevel(link_call_t *call, void *userdata)
|
||||
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||
"runlevel must be 0-9 (excluding internal levels)");
|
||||
|
||||
if (lvl == 0) halt = SHUT_OFF;
|
||||
if (lvl == 6) halt = SHUT_REBOOT;
|
||||
sm_runlevel((int)lvl);
|
||||
sm_request_runlevel((int)lvl);
|
||||
|
||||
(void)link_call_reply(call);
|
||||
return 0;
|
||||
@@ -464,11 +440,18 @@ static int manager_set_runlevel(link_call_t *call, void *userdata)
|
||||
|
||||
static int dbus_shutdown(link_call_t *call, shutop_t target, int level)
|
||||
{
|
||||
uint32_t timeout;
|
||||
|
||||
if (link_call_read_u32(call, &timeout) < 0)
|
||||
return link_call_reply_error(call,
|
||||
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||
"expected (u)");
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel))
|
||||
return link_call_reply_error(call,
|
||||
"org.finit.Error.WrongRunlevel",
|
||||
"Already in shutdown");
|
||||
halt = target;
|
||||
shutdown_bypass((int)timeout);
|
||||
sm_runlevel(level);
|
||||
(void)link_call_reply(call);
|
||||
return 0;
|
||||
@@ -490,11 +473,9 @@ static int signal_one(svc_t *svc, void *udata)
|
||||
{
|
||||
int signo = *(int *)udata;
|
||||
|
||||
/* Silently skip stopped services -- a multi-match ident
|
||||
* (e.g. "sshd:*") should not fail the whole call just because
|
||||
* one of the matches happens to be in a halted state. */
|
||||
/* Signalling a stopped service is an error, like the legacy API */
|
||||
if (!svc_is_running(svc))
|
||||
return 0;
|
||||
return 1;
|
||||
return !!kill(svc->pid, signo);
|
||||
}
|
||||
|
||||
@@ -535,6 +516,10 @@ static int manager_signal(link_call_t *call, void *u)
|
||||
static int manager_suspend(link_call_t *call, void *u)
|
||||
{
|
||||
(void)u;
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel))
|
||||
return link_call_reply_error(call,
|
||||
"org.finit.Error.WrongRunlevel",
|
||||
"Unsupported command in runlevel S and 6/0");
|
||||
sync();
|
||||
if (suspend() < 0) {
|
||||
const char *msg = (errno == EINVAL)
|
||||
@@ -623,11 +608,11 @@ static const link_method_t manager_methods[] = {
|
||||
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_reload },
|
||||
{ .name = "SetRunlevel", .in_sig = "u", .out_sig = "",
|
||||
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_set_runlevel },
|
||||
{ .name = "Reboot", .in_sig = "", .out_sig = "",
|
||||
{ .name = "Reboot", .in_sig = "u", .out_sig = "",
|
||||
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_reboot },
|
||||
{ .name = "Poweroff", .in_sig = "", .out_sig = "",
|
||||
{ .name = "Poweroff", .in_sig = "u", .out_sig = "",
|
||||
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_poweroff },
|
||||
{ .name = "Halt", .in_sig = "", .out_sig = "",
|
||||
{ .name = "Halt", .in_sig = "u", .out_sig = "",
|
||||
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_halt },
|
||||
{ .name = "Suspend", .in_sig = "", .out_sig = "",
|
||||
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_suspend },
|
||||
@@ -638,10 +623,18 @@ static const link_method_t manager_methods[] = {
|
||||
{ NULL, NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
static const link_signal_t manager_signals[] = {
|
||||
{ .name = "ServiceStateChanged", .sig = "sss" },
|
||||
{ .name = "RunlevelChanged", .sig = "ss" },
|
||||
{ .name = "ConfigReloaded", .sig = "" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const link_vtable_t manager_vtable = {
|
||||
.interface = "org.finit.Manager1",
|
||||
.methods = manager_methods,
|
||||
.properties = manager_properties,
|
||||
.signals = manager_signals,
|
||||
};
|
||||
|
||||
/* ---------- org.finit.Service1 (one object per service) ----------
|
||||
@@ -665,7 +658,11 @@ static int service_action_method(link_call_t *call, void *userdata,
|
||||
"org.finit.Error.NoSuchService",
|
||||
"Service object no longer valid");
|
||||
|
||||
action(svc, NULL);
|
||||
if (action(svc, NULL))
|
||||
return link_call_reply_error(call,
|
||||
"org.finit.Error.Failed",
|
||||
"failed on service");
|
||||
|
||||
(void)link_call_reply(call);
|
||||
return 0;
|
||||
}
|
||||
@@ -674,18 +671,15 @@ static int service1_start (link_call_t *c, void *u) { return service_action_met
|
||||
static int service1_stop (link_call_t *c, void *u) { return service_action_method(c, u, dbus_apply_stop); }
|
||||
static int service1_restart(link_call_t *c, void *u) { return service_action_method(c, u, dbus_apply_restart); }
|
||||
|
||||
static int service1_reload(link_call_t *call, void *userdata)
|
||||
static int dbus_apply_reload(svc_t *svc, void *user_data)
|
||||
{
|
||||
svc_t *svc = userdata;
|
||||
(void)user_data;
|
||||
return service_reload(svc);
|
||||
}
|
||||
|
||||
if (!svc)
|
||||
return link_call_reply_error(call,
|
||||
"org.finit.Error.NoSuchService",
|
||||
"Service object no longer valid");
|
||||
|
||||
service_reload(svc);
|
||||
(void)link_call_reply(call);
|
||||
return 0;
|
||||
static int service1_reload(link_call_t *c, void *u)
|
||||
{
|
||||
return service_action_method(c, u, dbus_apply_reload);
|
||||
}
|
||||
|
||||
static const link_method_t service_methods[] = {
|
||||
@@ -1049,6 +1043,18 @@ void dbus_notify_runlevel_change(int old_level, int new_level)
|
||||
"RunlevelChanged", "ss", body, (size_t)blen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconfiguration complete: all conditions have been re-asserted by
|
||||
* their in-Finit owners. External providers whose conditions are
|
||||
* generation files, rather than the oneshot symlinks keventd uses,
|
||||
* subscribe to this to re-assert theirs.
|
||||
*/
|
||||
void dbus_notify_reload(void)
|
||||
{
|
||||
dbus_emit_signal("/org/finit/manager", "org.finit.Manager1",
|
||||
"ConfigReloaded", "", NULL, 0);
|
||||
}
|
||||
|
||||
/* ---------- org.finit.Cond1 ---------- */
|
||||
|
||||
#define COND_PATH_OBJECT "/org/finit/cond"
|
||||
@@ -1148,7 +1154,7 @@ static int cond1_set_or_clear(link_call_t *call, int do_set)
|
||||
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||
"Set/Clear is restricted to usr/* conditions");
|
||||
|
||||
if (do_set)
|
||||
if (do_set) {
|
||||
/* cond_set_oneshot, not cond_set: a user-asserted condition
|
||||
* is a symlink to _PATH_RECONF, so it tracks the reconf
|
||||
* generation automatically and stays "on" across reloads
|
||||
@@ -1157,8 +1163,17 @@ static int cond1_set_or_clear(link_call_t *call, int do_set)
|
||||
* semantics for user conditions, and what initctl cond set
|
||||
* has done forever via the filesystem path. */
|
||||
cond_set_oneshot(full);
|
||||
else
|
||||
if (cond_get(full) != COND_ON)
|
||||
return link_call_reply_error(call,
|
||||
"org.finit.Error.Failed",
|
||||
"failed asserting condition");
|
||||
} else {
|
||||
cond_clear(full);
|
||||
if (cond_get(full) != COND_OFF)
|
||||
return link_call_reply_error(call,
|
||||
"org.finit.Error.Failed",
|
||||
"failed clearing condition");
|
||||
}
|
||||
|
||||
(void)link_call_reply(call);
|
||||
return 0;
|
||||
@@ -1256,9 +1271,15 @@ static const link_method_t cond_methods[] = {
|
||||
{ NULL, NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
static const link_signal_t cond_signals[] = {
|
||||
{ .name = "ConditionChanged", .sig = "ss" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const link_vtable_t cond_vtable = {
|
||||
.interface = COND_INTERFACE,
|
||||
.methods = cond_methods,
|
||||
.signals = cond_signals,
|
||||
};
|
||||
|
||||
/* ---------- signal emission: ConditionChanged ---------- */
|
||||
|
||||
+52
-13
@@ -58,6 +58,30 @@ static struct dev_node *find_node(const char *cond)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int devmon_cond(const char *cond)
|
||||
{
|
||||
return !strncmp(cond, COND_DEV, strlen(COND_DEV)) ||
|
||||
!strncmp(cond, COND_CLASS, strlen(COND_CLASS)) ||
|
||||
!strncmp(cond, COND_DRIVER, strlen(COND_DRIVER));
|
||||
}
|
||||
|
||||
/*
|
||||
* keventd asserts class/ and driver/ conditions for devices that never
|
||||
* get a /dev node, e.g. LEDs and DSA switch ports. For those the
|
||||
* condition file is the only record of the device.
|
||||
*/
|
||||
static int dev_exists(const char *cond)
|
||||
{
|
||||
char path[PATH_MAX], tmp[PATH_MAX];
|
||||
|
||||
snprintf(path, sizeof(path), "/%s", cond);
|
||||
if (fexist(path))
|
||||
return 1;
|
||||
|
||||
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", cond);
|
||||
return fexist(pid_runpath(tmp, path, sizeof(path)));
|
||||
}
|
||||
|
||||
static void drop_node(struct dev_node *node)
|
||||
{
|
||||
if (!node)
|
||||
@@ -75,9 +99,8 @@ static void drop_node(struct dev_node *node)
|
||||
void devmon_add_cond(const char *cond)
|
||||
{
|
||||
struct dev_node *node;
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (!cond || strncmp(cond, "dev/", 4)) {
|
||||
if (!cond || !devmon_cond(cond)) {
|
||||
// dbg("no match %s", cond ?: "<NIL>");
|
||||
return;
|
||||
}
|
||||
@@ -105,14 +128,13 @@ void devmon_add_cond(const char *cond)
|
||||
|
||||
TAILQ_INSERT_TAIL(&dev_node_list, node, link);
|
||||
|
||||
snprintf(path, sizeof(path), "/%s", cond);
|
||||
if (fexist(path))
|
||||
if (dev_exists(cond))
|
||||
cond_set(cond);
|
||||
}
|
||||
|
||||
void devmon_del_cond(const char *cond)
|
||||
{
|
||||
if (!cond || strncmp(cond, COND_DEV, strlen(COND_DEV)))
|
||||
if (!cond || !devmon_cond(cond))
|
||||
return;
|
||||
|
||||
drop_node(find_node(cond));
|
||||
@@ -121,11 +143,9 @@ void devmon_del_cond(const char *cond)
|
||||
void devmon_reconf(void)
|
||||
{
|
||||
struct dev_node *node, *tmp;
|
||||
char path[PATH_MAX];
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &dev_node_list, link, tmp) {
|
||||
snprintf(path, sizeof(path), "/%s", node->name);
|
||||
if (fexist(path))
|
||||
if (dev_exists(node->name))
|
||||
cond_set(node->name);
|
||||
else
|
||||
cond_clear(node->name);
|
||||
@@ -161,6 +181,18 @@ static void devmon_update_conds(char *dir, char *name, uint32_t mask)
|
||||
char *cond;
|
||||
|
||||
paste(fn, sizeof(fn), dir, name);
|
||||
|
||||
/*
|
||||
* Events in the condition tree come from keventd, the file is
|
||||
* the condition state so only kick affected services, like the
|
||||
* sys/usr plugins do.
|
||||
*/
|
||||
cond = strstr(fn, COND_BASE);
|
||||
if (cond) {
|
||||
cond_update(cond + strlen(COND_BASE) + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
cond = &fn[1];
|
||||
|
||||
// dbg("path: %s, mask: %08x, cond: %s", fn, mask, cond);
|
||||
@@ -273,23 +305,30 @@ static void devmon_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
void devmon_init(uev_ctx_t *ctx)
|
||||
{
|
||||
const char *cond_dirs[] = { _PATH_CONDDEV, _PATH_CONDCLASS, _PATH_CONDDRIVER };
|
||||
char dir[MAX_ARG_LEN];
|
||||
size_t i;
|
||||
|
||||
fd = iwatch_init(&iw_devmon);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (mkpath(pid_runpath(_PATH_CONDDEV, dir, sizeof(dir)), 0755) && errno != EEXIST) {
|
||||
err(1, "Failed creating %s condition directory, %s", COND_DEV, _PATH_CONDDEV);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uev_io_init(ctx, &devw, devmon_cb, NULL, fd, UEV_READ)) {
|
||||
err(1, "Failed setting up I/O callback for /dev watcher");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < NELEMS(cond_dirs); i++) {
|
||||
pid_runpath(cond_dirs[i], dir, sizeof(dir));
|
||||
if (mkpath(dir, 0755) && errno != EEXIST) {
|
||||
err(1, "Failed creating condition directory %s", dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
devmon_add_path(&iw_devmon, dir);
|
||||
}
|
||||
|
||||
strlcpy(dir, _PATH_DEV, sizeof(dir));
|
||||
if (devmon_add_path(&iw_devmon, dir))
|
||||
iwatch_exit(&iw_devmon);
|
||||
|
||||
+16
-3
@@ -1067,13 +1067,26 @@ static int do_cmd(int cmd)
|
||||
#ifdef HAVE_DBUS
|
||||
static int do_reboot_dbus(const char *method)
|
||||
{
|
||||
int rc = try_dbus_manager(method, "", NULL);
|
||||
link_client_t *c;
|
||||
int rc;
|
||||
|
||||
if (rc == 0) {
|
||||
c = link_client_open(FINIT_BUS_SOCKET);
|
||||
if (!c)
|
||||
return -1; /* fall back to legacy transport */
|
||||
|
||||
/* Timeout in seconds arms the emergency shutdown bypass, 0 = none */
|
||||
rc = link_client_call_v(c, "/org/finit/manager",
|
||||
"org.finit.Manager1", method,
|
||||
"u", (uint32_t)timeout);
|
||||
if (rc == LINK_CALL_ERROR)
|
||||
map_dbus_err(c, method, NULL); /* exits */
|
||||
link_client_close(c);
|
||||
|
||||
if (rc == LINK_CALL_OK) {
|
||||
sleep(5); /* match legacy: wait for finit to shut down */
|
||||
return 0;
|
||||
}
|
||||
return rc; /* 1 = error, -1 = fall back */
|
||||
return -1; /* fall back */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
-319
@@ -1,319 +0,0 @@
|
||||
/* Listens to kernel events like AC power status and manages sys/ conditions
|
||||
*
|
||||
* Copyright (c) 2021-2025 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 <dirent.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "util.h"
|
||||
|
||||
#define _PATH_SYSFS_PWR "/sys/class/power_supply"
|
||||
|
||||
static int num_ac_online;
|
||||
static int num_ac;
|
||||
|
||||
static int running = 1;
|
||||
static int level;
|
||||
static int logon;
|
||||
|
||||
int debug; /* debug in other modules as well */
|
||||
|
||||
void logit(int prio, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (logon)
|
||||
vsyslog(prio, fmt, ap);
|
||||
else if (prio <= level) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
#define panic(fmt, args...) { logit(LOG_CRIT, fmt ":%s", ##args, strerror(errno)); exit(1); }
|
||||
#define warn(fmt, args...) { logit(LOG_WARNING, fmt ":%s", ##args, strerror(errno)); }
|
||||
|
||||
static void sys_cond(char *cond, int set)
|
||||
{
|
||||
char oneshot[256];
|
||||
|
||||
snprintf(oneshot, sizeof(oneshot), "%s/%s", _PATH_CONDSYS, cond);
|
||||
if (set) {
|
||||
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
|
||||
warn("failed asserting sys/%s", cond);
|
||||
} else {
|
||||
if (erase(oneshot) && errno != ENOENT)
|
||||
warn("failed asserting sys/%s", cond);
|
||||
}
|
||||
}
|
||||
|
||||
static int fgetline(char *path, char *buf, size_t len)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
if (!fgets(buf, len, fp)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chomp(buf);
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_online(char *online)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (!online)
|
||||
return 0;
|
||||
|
||||
val = atoi(online);
|
||||
logit(LOG_INFO, "AC %s", val ? "connected" : "disconnected");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int is_ac(char *type)
|
||||
{
|
||||
char *types[] = {
|
||||
"Mains",
|
||||
"USB",
|
||||
"BrickID",
|
||||
"Wireless",
|
||||
NULL
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; types[i]; i++) {
|
||||
if (!strncmp(type, types[i], strlen(types[i])))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
struct dirent **d = NULL;
|
||||
char *cond_dirs[] = {
|
||||
_PATH_CONDSYS,
|
||||
_PATH_CONDSYS "/pwr",
|
||||
};
|
||||
char path[384];
|
||||
int i, n;
|
||||
|
||||
for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
|
||||
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
|
||||
warn("Failed creating %s condition directory, %s", COND_SYS,
|
||||
cond_dirs[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
n = scandir(_PATH_SYSFS_PWR, &d, NULL, alphasort);
|
||||
for (i = 0; i < n; i++) {
|
||||
char *nm = d[i]->d_name;
|
||||
char buf[10];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s/type", _PATH_SYSFS_PWR, nm);
|
||||
if (!fgetline(path, buf, sizeof(buf)) && is_ac(buf)) {
|
||||
num_ac++;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s/online", _PATH_SYSFS_PWR, nm);
|
||||
if (!fgetline(path, buf, sizeof(buf))) {
|
||||
if (check_online(buf))
|
||||
num_ac_online++;
|
||||
}
|
||||
}
|
||||
free(d[i]);
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
free(d);
|
||||
|
||||
/* if any power_supply is online, or none can be found */
|
||||
if (num_ac == 0 || num_ac_online > 0)
|
||||
sys_cond("pwr/ac", 1);
|
||||
}
|
||||
|
||||
static void set_logging(int prio)
|
||||
{
|
||||
setlogmask(LOG_UPTO(prio));
|
||||
level = prio;
|
||||
}
|
||||
|
||||
static void toggle_debug(int signo)
|
||||
{
|
||||
(void)signo;
|
||||
|
||||
debug ^= 1;
|
||||
set_logging(debug ? LOG_DEBUG : LOG_NOTICE);
|
||||
}
|
||||
|
||||
static void shut_down(int signo)
|
||||
{
|
||||
(void)signo;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Started by Finit as soon as possible when base filesystem is up,
|
||||
* modules have been probed, or insmodded from /etc/finit.conf, so by
|
||||
* now we should have /sys/class/power_supply/ available for probing.
|
||||
* If none is found we assert /sys/pwr/ac condition anyway, this is what
|
||||
* systemd does (ConditionACPower) and also makes most sense.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_nl nls = { 0 };
|
||||
struct pollfd pfd;
|
||||
char buf[1024];
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strcmp(argv[1], "-d"))
|
||||
debug = 1;
|
||||
}
|
||||
|
||||
if (!debug) {
|
||||
openlog("keventd", LOG_PID, LOG_DAEMON);
|
||||
set_logging(LOG_NOTICE);
|
||||
logon = 1;
|
||||
}
|
||||
|
||||
signal(SIGUSR1, toggle_debug);
|
||||
signal(SIGTERM, shut_down);
|
||||
init();
|
||||
|
||||
pfd.events = POLLIN;
|
||||
pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||
if (pfd.fd == -1)
|
||||
panic("failed creating netlink socket");
|
||||
|
||||
nls.nl_family = AF_NETLINK;
|
||||
nls.nl_pid = 0;
|
||||
nls.nl_groups = -1;
|
||||
if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl)))
|
||||
panic("bind failed");
|
||||
|
||||
logit(LOG_DEBUG, "Waiting for events ...");
|
||||
while (running) {
|
||||
char *path = NULL;
|
||||
int ac = 0;
|
||||
int i, len;
|
||||
|
||||
if (-1 == poll(&pfd, 1, -1)) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (len == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
continue;
|
||||
case ENOBUFS:
|
||||
warn("lost events");
|
||||
continue;
|
||||
default:
|
||||
panic("unhandled");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
buf[len] = 0;
|
||||
logit(LOG_DEBUG, "%s", buf);
|
||||
|
||||
/* skip libusb events, focus on kernel events/changes */
|
||||
if (strncmp(buf, "change@", 7))
|
||||
continue;
|
||||
|
||||
/* XXX: currently limited to monitoring this subsystem */
|
||||
if (!strstr(buf, "power_supply"))
|
||||
continue;
|
||||
|
||||
i = 0;
|
||||
while (i < len) {
|
||||
char *line = buf + i;
|
||||
|
||||
logit(LOG_DEBUG, "%s", line);
|
||||
if (!strncmp(line, "DEVPATH=", 8)) {
|
||||
path = &line[8];
|
||||
logit(LOG_DEBUG, "Got path %s", path);
|
||||
} else if (!strncmp(line, "POWER_SUPPLY_TYPE=", 18)) {
|
||||
ac = is_ac(&line[18]);
|
||||
} else if (!strncmp(line, "POWER_SUPPLY_ONLINE=", 20) && ac) {
|
||||
if (check_online(&line[20])) {
|
||||
if (!num_ac_online)
|
||||
sys_cond("pwr/ac", 1);
|
||||
num_ac_online++;
|
||||
} else {
|
||||
if (num_ac_online > 0)
|
||||
num_ac_online--;
|
||||
if (!num_ac_online)
|
||||
sys_cond("pwr/ac", 0);
|
||||
}
|
||||
}
|
||||
|
||||
i += strlen(line) + 1;
|
||||
}
|
||||
}
|
||||
close(pfd.fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -54,6 +54,7 @@ void dbus_unregister_service (svc_t *svc);
|
||||
void dbus_notify_service_state (svc_t *svc, int old_state, int new_state);
|
||||
void dbus_notify_condition_change(const char *name, const char *state);
|
||||
void dbus_notify_runlevel_change(int old_level, int new_level);
|
||||
void dbus_notify_reload (void);
|
||||
#endif
|
||||
void conf_flush_events(void);
|
||||
|
||||
|
||||
+13
-1
@@ -193,7 +193,19 @@ int run_parts(char *dir, char *cmd, const char *env[], int progress, int sysv)
|
||||
#ifndef __FINIT__
|
||||
static int usage(int rc)
|
||||
{
|
||||
warnx("usage: runparts [-bdhps?] DIRECTORY");
|
||||
fprintf(stderr,
|
||||
"Usage: runparts [OPTIONS] DIRECTORY\n"
|
||||
"\n"
|
||||
"Run all executable scripts in DIRECTORY in alphabetical order.\n"
|
||||
"Scripts prefixed S<NUM> are called with 'start', K<NUM> with 'stop'.\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -b Batch mode, disable progress output and ANSI colors\n"
|
||||
" -d Enable debug output\n"
|
||||
" -h This help text\n"
|
||||
" -p Show progress and exit status for each script\n"
|
||||
" -s SysV compat, only run S<NUM>name and K<NUM>name scripts\n"
|
||||
"\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -1442,6 +1442,77 @@ int service_stop(svc_t *svc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_stop_now - Stop a service on user request
|
||||
* @svc: Service to stop
|
||||
*
|
||||
* Shared by the legacy API and the D-Bus methods, like
|
||||
* service_reload().
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, non-zero if @svc is NULL.
|
||||
*/
|
||||
int service_stop_now(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
svc_stop(svc);
|
||||
service_step(svc);
|
||||
if (!IS_RESERVED_RUNLEVEL(runlevel))
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_start_now - Start a service on user request
|
||||
* @svc: Service to start
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, non-zero if @svc is NULL.
|
||||
*/
|
||||
int service_start_now(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
svc_start(svc);
|
||||
service_step(svc);
|
||||
if (!IS_RESERVED_RUNLEVEL(runlevel))
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_restart_now - Restart a service on user request
|
||||
* @svc: Service to restart
|
||||
*
|
||||
* Does not wait for @svc to stop first, that is the caller's
|
||||
* (initctl) responsibility. Otherwise we'd block PID 1, or
|
||||
* introduce some nasty race conditions.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, non-zero if @svc is NULL.
|
||||
*/
|
||||
int service_restart_now(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
if (!svc_is_running(svc))
|
||||
return service_start_now(svc);
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
service_stop(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_reload - Request reload of a service, driven by the state machine
|
||||
* @svc: Service to reload
|
||||
|
||||
@@ -60,6 +60,9 @@ void service_forked (svc_t *svc);
|
||||
void service_ready (svc_t *svc, int ready);
|
||||
|
||||
int service_stop (svc_t *svc);
|
||||
int service_start_now (svc_t *svc);
|
||||
int service_stop_now (svc_t *svc);
|
||||
int service_restart_now (svc_t *svc);
|
||||
int service_reload (svc_t *svc);
|
||||
int service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
|
||||
@@ -94,6 +94,53 @@ static void shutdown_wdt_cb(uev_t *w, void *arg, int events)
|
||||
do_shutdown(halt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Emergency shutdown bypass, same job as the shutdown watchdog above
|
||||
* but armed by the user: `initctl -t SEC reboot`, on either transport.
|
||||
*/
|
||||
static void bypass_shutdown(void *unused)
|
||||
{
|
||||
(void)unused;
|
||||
|
||||
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
|
||||
do_shutdown(halt);
|
||||
}
|
||||
|
||||
static struct wq emergency = { .cb = bypass_shutdown };
|
||||
|
||||
void shutdown_bypass(int timeout)
|
||||
{
|
||||
if (timeout <= 0)
|
||||
return;
|
||||
|
||||
emergency.delay = timeout * 1000;
|
||||
schedule_work(&emergency);
|
||||
}
|
||||
|
||||
/*
|
||||
* User-requested runlevel change, shared by the legacy API and the
|
||||
* D-Bus SetRunlevel method. Refused in runlevel 0/6, deferred via
|
||||
* cfglevel during bootstrap.
|
||||
*/
|
||||
void sm_request_runlevel(int lvl)
|
||||
{
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel) && runlevel != INIT_LEVEL) {
|
||||
warnx("Cannot abort runlevel 6/0.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (lvl == 0)
|
||||
halt = SHUT_OFF;
|
||||
if (lvl == 6)
|
||||
halt = SHUT_REBOOT;
|
||||
|
||||
/* Runlevel S: user requested change in next runlevel */
|
||||
if (runlevel == INIT_LEVEL)
|
||||
cfglevel = lvl;
|
||||
else
|
||||
sm_runlevel(lvl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Console input callback - handles Ctrl-C during bootstrap wait
|
||||
*/
|
||||
@@ -586,6 +633,9 @@ restart:
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
dbg("Reconfiguration done");
|
||||
#ifdef HAVE_DBUS
|
||||
dbus_notify_reload();
|
||||
#endif
|
||||
sm.state = SM_RUNNING_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user