mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
mount: implement mounting properly
This also includes code that writes utmp/wtmp records when applicable, making use the mount infrastructure to detct when those files are accessible. Finally, this also introduces a --dump-configuration-items switch.
This commit is contained in:
parent
108736d0d3
commit
e537352b9b
@ -106,7 +106,9 @@ COMMON_SOURCES= \
|
||||
mount-setup.c \
|
||||
mount-setup.h \
|
||||
hostname-setup.c \
|
||||
hostname-setup.h
|
||||
hostname-setup.h \
|
||||
utmp-wtmp.c \
|
||||
utmp-wtmp.h
|
||||
|
||||
systemd_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
|
104
automount.c
104
automount.c
@ -26,108 +26,80 @@
|
||||
#include "load-fragment.h"
|
||||
#include "load-dropin.h"
|
||||
|
||||
static int automount_init(Unit *u, UnitLoadState *new_state) {
|
||||
static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
|
||||
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
|
||||
[AUTOMOUNT_WAITING] = UNIT_ACTIVE,
|
||||
[AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
|
||||
[AUTOMOUNT_MAINTAINANCE] = UNIT_INACTIVE,
|
||||
};
|
||||
|
||||
static const char* const state_string_table[_AUTOMOUNT_STATE_MAX] = {
|
||||
[AUTOMOUNT_DEAD] = "dead",
|
||||
[AUTOMOUNT_WAITING] = "waiting",
|
||||
[AUTOMOUNT_RUNNING] = "running",
|
||||
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
static void automount_init(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
a->state = 0;
|
||||
a->mount = NULL;
|
||||
}
|
||||
|
||||
static int automount_load(Unit *u) {
|
||||
int r;
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(a);
|
||||
|
||||
exec_context_init(&a->exec_context);
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
/* Load a .automount file */
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
*new_state = UNIT_LOADED;
|
||||
if (u->meta.load_state == UNIT_LOADED) {
|
||||
|
||||
/* Load drop-in directory data */
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_LOADED) {
|
||||
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_exec_dependencies(u, &a->exec_context)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_default_cgroup(u)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void automount_done(Unit *u) {
|
||||
Automount *d = AUTOMOUNT(u);
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(d);
|
||||
free(d->path);
|
||||
assert(a);
|
||||
|
||||
a->mount = NULL;
|
||||
}
|
||||
|
||||
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const state_table[_AUTOMOUNT_STATE_MAX] = {
|
||||
[AUTOMOUNT_DEAD] = "dead",
|
||||
[AUTOMOUNT_START_PRE] = "start-pre",
|
||||
[AUTOMOUNT_START_POST] = "start-post",
|
||||
[AUTOMOUNT_WAITING] = "waiting",
|
||||
[AUTOMOUNT_RUNNING] = "running",
|
||||
[AUTOMOUNT_STOP_PRE] = "stop-pre",
|
||||
[AUTOMOUNT_STOP_POST] = "stop-post",
|
||||
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
static const char* const command_table[_AUTOMOUNT_EXEC_MAX] = {
|
||||
[AUTOMOUNT_EXEC_START_PRE] = "StartPre",
|
||||
[AUTOMOUNT_EXEC_START_POST] = "StartPost",
|
||||
[AUTOMOUNT_EXEC_STOP_PRE] = "StopPre",
|
||||
[AUTOMOUNT_EXEC_STOP_POST] = "StopPost"
|
||||
};
|
||||
|
||||
AutomountExecCommand c;
|
||||
Automount *s = AUTOMOUNT(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
fprintf(f,
|
||||
"%sAutomount State: %s\n"
|
||||
"%sPath: %s\n",
|
||||
prefix, state_table[s->state],
|
||||
prefix, s->path);
|
||||
|
||||
exec_context_dump(&s->exec_context, f, prefix);
|
||||
|
||||
for (c = 0; c < _AUTOMOUNT_EXEC_MAX; c++) {
|
||||
ExecCommand *i;
|
||||
|
||||
LIST_FOREACH(command, i, s->exec_command[c])
|
||||
fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
|
||||
}
|
||||
"%sAutomount State: %s\n",
|
||||
prefix, state_string_table[s->state]);
|
||||
}
|
||||
|
||||
static UnitActiveState automount_active_state(Unit *u) {
|
||||
|
||||
static const UnitActiveState table[_AUTOMOUNT_STATE_MAX] = {
|
||||
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
|
||||
[AUTOMOUNT_START_PRE] = UNIT_ACTIVATING,
|
||||
[AUTOMOUNT_START_POST] = UNIT_ACTIVATING,
|
||||
[AUTOMOUNT_WAITING] = UNIT_ACTIVE,
|
||||
[AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
|
||||
[AUTOMOUNT_STOP_PRE] = UNIT_DEACTIVATING,
|
||||
[AUTOMOUNT_STOP_POST] = UNIT_DEACTIVATING,
|
||||
[AUTOMOUNT_MAINTAINANCE] = UNIT_INACTIVE,
|
||||
};
|
||||
|
||||
return table[AUTOMOUNT(u)->state];
|
||||
return state_translation_table[AUTOMOUNT(u)->state];
|
||||
}
|
||||
|
||||
const UnitVTable automount_vtable = {
|
||||
.suffix = ".mount",
|
||||
|
||||
.no_alias = true,
|
||||
|
||||
.init = automount_init,
|
||||
.load = automount_load,
|
||||
.done = automount_done,
|
||||
|
||||
.dump = automount_dump,
|
||||
|
21
automount.h
21
automount.h
@ -28,34 +28,17 @@ typedef struct Automount Automount;
|
||||
|
||||
typedef enum AutomountState {
|
||||
AUTOMOUNT_DEAD,
|
||||
AUTOMOUNT_START_PRE,
|
||||
AUTOMOUNT_START_POST,
|
||||
AUTOMOUNT_WAITING,
|
||||
AUTOMOUNT_RUNNING,
|
||||
AUTOMOUNT_STOP_PRE,
|
||||
AUTOMOUNT_STOP_POST,
|
||||
AUTOMOUNT_MAINTAINANCE,
|
||||
_AUTOMOUNT_STATE_MAX
|
||||
_AUTOMOUNT_STATE_MAX,
|
||||
_AUTOMOUNT_STATE_INVALID = -1
|
||||
} AutomountState;
|
||||
|
||||
typedef enum AutomountExecCommand {
|
||||
AUTOMOUNT_EXEC_START_PRE,
|
||||
AUTOMOUNT_EXEC_START_POST,
|
||||
AUTOMOUNT_EXEC_STOP_PRE,
|
||||
AUTOMOUNT_EXEC_STOP_POST,
|
||||
_AUTOMOUNT_EXEC_MAX
|
||||
} AutomountExecCommand;
|
||||
|
||||
struct Automount {
|
||||
Meta meta;
|
||||
|
||||
AutomountState state;
|
||||
char *path;
|
||||
|
||||
ExecCommand* exec_command[_AUTOMOUNT_EXEC_MAX];
|
||||
ExecContext exec_context;
|
||||
|
||||
pid_t contol_pid;
|
||||
|
||||
Mount *mount;
|
||||
};
|
||||
|
29
device.c
29
device.c
@ -42,20 +42,29 @@ static void device_done(Unit *u) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
assert(d);
|
||||
|
||||
free(d->sysfs);
|
||||
d->sysfs = NULL;
|
||||
}
|
||||
|
||||
static void device_init(Unit *u) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
assert(d);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
d->state = 0;
|
||||
}
|
||||
|
||||
static void device_set_state(Device *d, DeviceState state) {
|
||||
DeviceState old_state;
|
||||
assert(d);
|
||||
|
||||
if (state == d->state)
|
||||
return;
|
||||
|
||||
old_state = d->state;
|
||||
d->state = state;
|
||||
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]);
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]);
|
||||
|
||||
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
@ -231,15 +240,16 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u
|
||||
if ((r = device_add_escaped_name(u, sysfs, true)) < 0)
|
||||
goto fail;
|
||||
|
||||
unit_add_to_load_queue(u);
|
||||
} else
|
||||
delete = false;
|
||||
|
||||
if (!(DEVICE(u)->sysfs))
|
||||
if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
unit_add_to_load_queue(u);
|
||||
} else
|
||||
delete = false;
|
||||
|
||||
if (dn)
|
||||
if ((r = device_add_escaped_name(u, dn, true)) < 0)
|
||||
goto fail;
|
||||
@ -460,7 +470,8 @@ fail:
|
||||
const UnitVTable device_vtable = {
|
||||
.suffix = ".device",
|
||||
|
||||
.init = unit_load_fragment_and_dropin_optional,
|
||||
.init = device_init,
|
||||
.load = unit_load_fragment_and_dropin_optional,
|
||||
.done = device_done,
|
||||
.coldplug = device_coldplug,
|
||||
|
||||
|
124
load-fragment.c
124
load-fragment.c
@ -56,6 +56,15 @@ static int config_parse_deps(
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
if (UNIT_VTABLE(u)->refuse_requires &&
|
||||
(d == UNIT_REQUIRES ||
|
||||
d == UNIT_SOFT_REQUIRES ||
|
||||
d == UNIT_REQUISITE ||
|
||||
d == UNIT_SOFT_REQUISITE)) {
|
||||
log_error("[%s:%u] Dependency of type %s not acceptable for this unit type.", filename, line, lvalue);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
FOREACH_WORD(w, l, rvalue, state) {
|
||||
char *t;
|
||||
int r;
|
||||
@ -1116,7 +1125,80 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
|
||||
static void dump_items(FILE *f, const ConfigItem *items) {
|
||||
const ConfigItem *i;
|
||||
const char *prev_section = NULL;
|
||||
bool not_first = false;
|
||||
|
||||
struct {
|
||||
ConfigParserCallback callback;
|
||||
const char *rvalue;
|
||||
} table[] = {
|
||||
{ config_parse_int, "INTEGER" },
|
||||
{ config_parse_unsigned, "UNSIGNED" },
|
||||
{ config_parse_size, "SIZE" },
|
||||
{ config_parse_bool, "BOOLEAN" },
|
||||
{ config_parse_string, "STRING" },
|
||||
{ config_parse_path, "PATH" },
|
||||
{ config_parse_strv, "STRING [...]" },
|
||||
{ config_parse_nice, "NICE" },
|
||||
{ config_parse_oom_adjust, "OOMADJUST" },
|
||||
{ config_parse_io_class, "IOCLASS" },
|
||||
{ config_parse_io_priority, "IOPRIORITY" },
|
||||
{ config_parse_cpu_sched_policy, "CPUSCHEDPOLICY" },
|
||||
{ config_parse_cpu_sched_prio, "CPUSCHEDPRIO" },
|
||||
{ config_parse_cpu_affinity, "CPUAFFINITY" },
|
||||
{ config_parse_mode, "MODE" },
|
||||
{ config_parse_output, "OUTPUT" },
|
||||
{ config_parse_input, "INPUT" },
|
||||
{ config_parse_facility, "FACILITY" },
|
||||
{ config_parse_level, "LEVEL" },
|
||||
{ config_parse_capabilities, "CAPABILITIES" },
|
||||
{ config_parse_secure_bits, "SECUREBITS" },
|
||||
{ config_parse_bounding_set, "BOUNDINGSET" },
|
||||
{ config_parse_timer_slack_ns, "TIMERSLACK" },
|
||||
{ config_parse_limit, "LIMIT" },
|
||||
{ config_parse_cgroup, "CGROUP [...]" },
|
||||
{ config_parse_deps, "UNIT [...]" },
|
||||
{ config_parse_names, "UNIT [...]" },
|
||||
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
|
||||
{ config_parse_service_type, "SERVICETYPE" },
|
||||
{ config_parse_service_restart, "SERVICERESTART" },
|
||||
{ config_parse_sysv_priority, "SYSVPRIORITY" },
|
||||
{ config_parse_kill_mode, "KILLMODE" },
|
||||
{ config_parse_listen, "SOCKET [...]" },
|
||||
{ config_parse_socket_bind, "SOCKETBIND" },
|
||||
{ config_parse_bindtodevice, "NETWORKINTERFACE" }
|
||||
};
|
||||
|
||||
assert(f);
|
||||
assert(items);
|
||||
|
||||
for (i = items; i->lvalue; i++) {
|
||||
unsigned j;
|
||||
const char *rvalue = "OTHER";
|
||||
|
||||
if (!streq_ptr(i->section, prev_section)) {
|
||||
if (!not_first)
|
||||
not_first = true;
|
||||
else
|
||||
fputc('\n', f);
|
||||
|
||||
fprintf(f, "[%s]\n", i->section);
|
||||
prev_section = i->section;
|
||||
}
|
||||
|
||||
for (j = 0; j < ELEMENTSOF(table); j++)
|
||||
if (i->parse == table[j].callback) {
|
||||
rvalue = table[j].rvalue;
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(f, "%s=%s\n", i->lvalue, rvalue);
|
||||
}
|
||||
}
|
||||
|
||||
static int load_from_path(Unit *u, const char *path) {
|
||||
|
||||
static const char* const section_table[_UNIT_TYPE_MAX] = {
|
||||
[UNIT_SERVICE] = "Service",
|
||||
@ -1223,7 +1305,13 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
|
||||
{ "KillMode", config_parse_kill_mode, &u->socket.kill_mode, "Socket" },
|
||||
EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
|
||||
|
||||
EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"),
|
||||
{ "What", config_parse_string, &u->mount.parameters_fragment.what, "Mount" },
|
||||
{ "Where", config_parse_path, &u->mount.where, "Mount" },
|
||||
{ "Options", config_parse_string, &u->mount.parameters_fragment.options, "Mount" },
|
||||
{ "Type", config_parse_string, &u->mount.parameters_fragment.fstype, "Mount" },
|
||||
{ "TimeoutSec", config_parse_usec, &u->mount.timeout_usec, "Mount" },
|
||||
{ "KillMode", config_parse_kill_mode, &u->mount.kill_mode, "Mount" },
|
||||
EXEC_CONTEXT_CONFIG_ITEMS(u->mount.exec_context, "Mount"),
|
||||
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
@ -1238,8 +1326,14 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
|
||||
char *filename = NULL, *id = NULL;
|
||||
Unit *merged;
|
||||
|
||||
if (!u) {
|
||||
/* Dirty dirty hack. */
|
||||
dump_items((FILE*) path, items);
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(u);
|
||||
assert(new_state);
|
||||
assert(path);
|
||||
|
||||
sections[0] = "Meta";
|
||||
sections[1] = section_table[u->meta.type];
|
||||
@ -1306,7 +1400,7 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
|
||||
goto finish;
|
||||
|
||||
if (merged != u) {
|
||||
*new_state = UNIT_MERGED;
|
||||
u->meta.load_state = UNIT_MERGED;
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
@ -1319,7 +1413,7 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
|
||||
u->meta.fragment_path = filename;
|
||||
filename = NULL;
|
||||
|
||||
*new_state = UNIT_LOADED;
|
||||
u->meta.load_state = UNIT_LOADED;
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
@ -1335,16 +1429,14 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
int unit_load_fragment(Unit *u, UnitLoadState *new_state) {
|
||||
int unit_load_fragment(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB);
|
||||
|
||||
if (u->meta.fragment_path) {
|
||||
|
||||
if ((r = load_from_path(u, u->meta.fragment_path, new_state)) < 0)
|
||||
if ((r = load_from_path(u, u->meta.fragment_path)) < 0)
|
||||
return r;
|
||||
|
||||
} else {
|
||||
@ -1353,23 +1445,29 @@ int unit_load_fragment(Unit *u, UnitLoadState *new_state) {
|
||||
|
||||
/* Try to find the unit under its id */
|
||||
if ((t = unit_id(u)))
|
||||
if ((r = load_from_path(u, t, new_state)) < 0)
|
||||
if ((r = load_from_path(u, t)) < 0)
|
||||
return r;
|
||||
|
||||
/* Try to find an alias we can load this with */
|
||||
if (*new_state == UNIT_STUB)
|
||||
if (u->meta.load_state == UNIT_STUB)
|
||||
SET_FOREACH(t, u->meta.names, i) {
|
||||
|
||||
if (unit_id(u) == t)
|
||||
continue;
|
||||
|
||||
if ((r = load_from_path(u, t, new_state)) < 0)
|
||||
if ((r = load_from_path(u, t)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state != UNIT_STUB)
|
||||
if (u->meta.load_state != UNIT_STUB)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unit_dump_config_items(FILE *f) {
|
||||
/* OK, this wins a prize for extreme ugliness. */
|
||||
|
||||
load_from_path(NULL, (const void*) f);
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
|
||||
/* Read service data from .desktop file style configuration fragments */
|
||||
|
||||
int unit_load_fragment(Unit *u, UnitLoadState *new_state);
|
||||
int unit_load_fragment(Unit *u);
|
||||
|
||||
void unit_dump_config_items(FILE *f);
|
||||
|
||||
#endif
|
||||
|
35
main.c
35
main.c
@ -38,7 +38,8 @@
|
||||
static enum {
|
||||
ACTION_RUN,
|
||||
ACTION_HELP,
|
||||
ACTION_TEST
|
||||
ACTION_TEST,
|
||||
ACTION_DUMP_CONFIGURATION_ITEMS
|
||||
} action = ACTION_RUN;
|
||||
|
||||
static char *default_unit = NULL;
|
||||
@ -138,7 +139,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_LOG_TARGET,
|
||||
ARG_DEFAULT,
|
||||
ARG_RUNNING_AS,
|
||||
ARG_TEST
|
||||
ARG_TEST,
|
||||
ARG_DUMP_CONFIGURATION_ITEMS
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -148,6 +150,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "running-as", required_argument, NULL, ARG_RUNNING_AS },
|
||||
{ "test", no_argument, NULL, ARG_TEST },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
@ -202,6 +205,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
action = ACTION_TEST;
|
||||
break;
|
||||
|
||||
case ARG_DUMP_CONFIGURATION_ITEMS:
|
||||
action = ACTION_DUMP_CONFIGURATION_ITEMS;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
action = ACTION_HELP;
|
||||
break;
|
||||
@ -220,12 +227,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
static int help(void) {
|
||||
|
||||
printf("%s [options]\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --default=UNIT Set default unit\n"
|
||||
" --log-level=LEVEL Set log level\n"
|
||||
" --log-target=TARGET Set log target (console, syslog, kmsg)\n"
|
||||
" --running-as=AS Set running as (init, system, session)\n"
|
||||
" --test Determine startup sequence, dump it and exit\n",
|
||||
" -h --help Show this help\n"
|
||||
" --default=UNIT Set default unit\n"
|
||||
" --log-level=LEVEL Set log level\n"
|
||||
" --log-target=TARGET Set log target (console, syslog, kmsg)\n"
|
||||
" --running-as=AS Set running as (init, system, session)\n"
|
||||
" --test Determine startup sequence, dump it and exit\n"
|
||||
" --dump-configuration-items Dump understood unit configuration items\n",
|
||||
__progname);
|
||||
|
||||
return 0;
|
||||
@ -270,12 +278,18 @@ int main(int argc, char *argv[]) {
|
||||
if (action == ACTION_HELP) {
|
||||
retval = help();
|
||||
goto finish;
|
||||
} else if (action == ACTION_DUMP_CONFIGURATION_ITEMS) {
|
||||
unit_dump_config_items(stdout);
|
||||
retval = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
assert_se(action == ACTION_RUN || action == ACTION_TEST);
|
||||
|
||||
/* Set up PATH unless it is already set */
|
||||
setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", false);
|
||||
setenv("PATH",
|
||||
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
running_as == MANAGER_INIT);
|
||||
|
||||
/* Move out of the way, so that we won't block unmounts */
|
||||
assert_se(chdir("/") == 0);
|
||||
@ -296,8 +310,7 @@ int main(int argc, char *argv[]) {
|
||||
log_debug("systemd running in %s mode.", manager_running_as_to_string(running_as));
|
||||
|
||||
if (running_as == MANAGER_INIT)
|
||||
if (hostname_setup() < 0)
|
||||
goto finish;
|
||||
hostname_setup();
|
||||
|
||||
if ((r = manager_new(running_as, &m)) < 0) {
|
||||
log_error("Failed to allocate manager object: %s", strerror(-r));
|
||||
|
74
manager.c
74
manager.c
@ -27,6 +27,7 @@
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <utmpx.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -42,6 +43,7 @@
|
||||
#include "ratelimit.h"
|
||||
#include "cgroup.h"
|
||||
#include "mount-setup.h"
|
||||
#include "utmp-wtmp.h"
|
||||
|
||||
static int manager_setup_signals(Manager *m) {
|
||||
sigset_t mask;
|
||||
@ -291,6 +293,8 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
|
||||
if (!(m = new0(Manager, 1)))
|
||||
return -ENOMEM;
|
||||
|
||||
m->boot_timestamp = now(CLOCK_REALTIME);
|
||||
|
||||
m->running_as = running_as;
|
||||
m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
|
||||
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
|
||||
@ -428,6 +432,10 @@ int manager_coldplug(Manager *m) {
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Now that the initial devices are available, let's see if we
|
||||
* can write the utmp file */
|
||||
manager_write_utmp_reboot(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1704,6 +1712,72 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool manager_utmp_good(Manager *m) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if ((r = mount_path_is_mounted(m, _PATH_UTMPX)) <= 0) {
|
||||
|
||||
if (r < 0)
|
||||
log_warning("Failed to determine whether " _PATH_UTMPX " is mounted: %s", strerror(-r));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void manager_write_utmp_reboot(Manager *m) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (m->utmp_reboot_written)
|
||||
return;
|
||||
|
||||
if (m->running_as != MANAGER_INIT)
|
||||
return;
|
||||
|
||||
if (!manager_utmp_good(m))
|
||||
return;
|
||||
|
||||
if ((r = utmp_put_reboot(m->boot_timestamp)) < 0) {
|
||||
|
||||
if (r != -ENOENT && r != -EROFS)
|
||||
log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m->utmp_reboot_written = true;
|
||||
}
|
||||
|
||||
void manager_write_utmp_runlevel(Manager *m, Unit *u) {
|
||||
int runlevel, r;
|
||||
|
||||
assert(m);
|
||||
assert(u);
|
||||
|
||||
if (u->meta.type != UNIT_TARGET)
|
||||
return;
|
||||
|
||||
if (m->running_as != MANAGER_INIT)
|
||||
return;
|
||||
|
||||
if (!manager_utmp_good(m))
|
||||
return;
|
||||
|
||||
if ((runlevel = target_get_runlevel(TARGET(u))) <= 0)
|
||||
return;
|
||||
|
||||
if ((r = utmp_put_runlevel(0, runlevel, 0)) < 0) {
|
||||
|
||||
if (r != -ENOENT && r != -EROFS)
|
||||
log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
|
||||
[MANAGER_INIT] = "init",
|
||||
[MANAGER_SYSTEM] = "system",
|
||||
|
@ -145,6 +145,8 @@ struct Manager {
|
||||
bool request_api_bus_dispatch:1;
|
||||
bool request_system_bus_dispatch:1;
|
||||
|
||||
bool utmp_reboot_written:1;
|
||||
|
||||
Hashmap *watch_pids; /* pid => Unit object n:1 */
|
||||
|
||||
int epoll_fd;
|
||||
@ -172,6 +174,8 @@ struct Manager {
|
||||
Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
|
||||
char *cgroup_controller;
|
||||
char *cgroup_hierarchy;
|
||||
|
||||
usec_t boot_timestamp;
|
||||
};
|
||||
|
||||
int manager_new(ManagerRunningAs running_as, Manager **m);
|
||||
@ -204,4 +208,8 @@ int manager_loop(Manager *m);
|
||||
const char *manager_running_as_to_string(ManagerRunningAs i);
|
||||
ManagerRunningAs manager_running_as_from_string(const char *s);
|
||||
|
||||
void manager_write_utmp_reboot(Manager *m);
|
||||
|
||||
void manager_write_utmp_runlevel(Manager *m, Unit *t);
|
||||
|
||||
#endif
|
||||
|
57
mount.h
57
mount.h
@ -28,31 +28,76 @@ typedef struct Mount Mount;
|
||||
|
||||
typedef enum MountState {
|
||||
MOUNT_DEAD,
|
||||
MOUNT_MOUNTING,
|
||||
MOUNT_MOUNTING, /* /bin/mount is running, but the mount is not done yet. */
|
||||
MOUNT_MOUNTING_DONE, /* /bin/mount is running, and the mount is done. */
|
||||
MOUNT_MOUNTED,
|
||||
MOUNT_REMOUNTING,
|
||||
MOUNT_UNMOUNTING,
|
||||
MOUNT_MOUNTING_SIGTERM,
|
||||
MOUNT_MOUNTING_SIGKILL,
|
||||
MOUNT_REMOUNTING_SIGTERM,
|
||||
MOUNT_REMOUNTING_SIGKILL,
|
||||
MOUNT_UNMOUNTING_SIGTERM,
|
||||
MOUNT_UNMOUNTING_SIGKILL,
|
||||
MOUNT_MAINTAINANCE,
|
||||
_MOUNT_STATE_MAX
|
||||
_MOUNT_STATE_MAX,
|
||||
_MOUNT_STATE_INVALID = -1
|
||||
} MountState;
|
||||
|
||||
typedef enum MountExecCommand {
|
||||
MOUNT_EXEC_MOUNT,
|
||||
MOUNT_EXEC_UNMOUNT,
|
||||
MOUNT_EXEC_REMOUNT,
|
||||
_MOUNT_EXEC_COMMAND_MAX,
|
||||
_MOUNT_EXEC_COMMAND_INVALID = -1
|
||||
} MountExecCommand;
|
||||
|
||||
typedef struct MountParameters {
|
||||
char *what;
|
||||
char *options;
|
||||
char *fstype;
|
||||
} MountParameters;
|
||||
|
||||
struct Mount {
|
||||
Meta meta;
|
||||
|
||||
MountState state;
|
||||
char *where;
|
||||
|
||||
char *what, *where;
|
||||
MountParameters parameters_etc_fstab;
|
||||
MountParameters parameters_proc_self_mountinfo;
|
||||
MountParameters parameters_fragment;
|
||||
|
||||
bool from_etc_fstab:1;
|
||||
bool from_proc_self_mountinfo:1;
|
||||
bool from_fragment:1;
|
||||
|
||||
/* Used while looking for mount points that vanished or got
|
||||
* added from/to /proc/self/mountinfo */
|
||||
bool still_exists:1;
|
||||
bool just_created:1;
|
||||
bool is_mounted:1;
|
||||
bool just_mounted:1;
|
||||
bool just_changed:1;
|
||||
|
||||
bool failure:1;
|
||||
|
||||
usec_t timeout_usec;
|
||||
|
||||
ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX];
|
||||
ExecContext exec_context;
|
||||
|
||||
MountState state;
|
||||
|
||||
KillMode kill_mode;
|
||||
|
||||
ExecCommand* control_command;
|
||||
pid_t control_pid;
|
||||
|
||||
Watch timer_watch;
|
||||
};
|
||||
|
||||
extern const UnitVTable mount_vtable;
|
||||
|
||||
void mount_fd_event(Manager *m, int events);
|
||||
|
||||
int mount_path_is_mounted(Manager *m, const char* path);
|
||||
|
||||
#endif
|
||||
|
89
service.c
89
service.c
@ -78,7 +78,7 @@ static void service_done(Unit *u) {
|
||||
s->sysv_runlevels = NULL;
|
||||
|
||||
exec_context_done(&s->exec_context);
|
||||
exec_command_free_array(s->exec_command, _SERVICE_EXEC_MAX);
|
||||
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
|
||||
s->control_command = NULL;
|
||||
|
||||
/* This will leak a process, but at least no memory or any of
|
||||
@ -280,7 +280,7 @@ static int priority_from_rcd(Service *s, const char *init_script) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *new_state) {
|
||||
static int service_load_sysv_path(Service *s, const char *path) {
|
||||
FILE *f;
|
||||
Unit *u;
|
||||
unsigned line = 0;
|
||||
@ -294,7 +294,6 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
|
||||
|
||||
assert(s);
|
||||
assert(path);
|
||||
assert(new_state);
|
||||
|
||||
u = UNIT(s);
|
||||
|
||||
@ -607,7 +606,7 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
|
||||
goto finish;
|
||||
}
|
||||
|
||||
*new_state = UNIT_LOADED;
|
||||
u->meta.load_state = UNIT_LOADED;
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
@ -618,7 +617,7 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int service_load_sysv_name(Service *s, const char *name, UnitLoadState *new_state) {
|
||||
static int service_load_sysv_name(Service *s, const char *name) {
|
||||
char **p;
|
||||
|
||||
assert(s);
|
||||
@ -634,26 +633,25 @@ static int service_load_sysv_name(Service *s, const char *name, UnitLoadState *n
|
||||
assert(endswith(path, ".service"));
|
||||
path[strlen(path)-8] = 0;
|
||||
|
||||
r = service_load_sysv_path(s, path, new_state);
|
||||
r = service_load_sysv_path(s, path);
|
||||
free(path);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state != UNIT_STUB)
|
||||
if ((UNIT(s)->meta.load_state != UNIT_STUB))
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_load_sysv(Service *s, UnitLoadState *new_state) {
|
||||
static int service_load_sysv(Service *s) {
|
||||
const char *t;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(new_state);
|
||||
|
||||
/* Load service data from SysV init scripts, preferably with
|
||||
* LSB headers ... */
|
||||
@ -662,31 +660,29 @@ static int service_load_sysv(Service *s, UnitLoadState *new_state) {
|
||||
return 0;
|
||||
|
||||
if ((t = unit_id(UNIT(s))))
|
||||
if ((r = service_load_sysv_name(s, t, new_state)) < 0)
|
||||
if ((r = service_load_sysv_name(s, t)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
if (UNIT(s)->meta.load_state == UNIT_STUB)
|
||||
SET_FOREACH(t, UNIT(s)->meta.names, i) {
|
||||
if ((r == service_load_sysv_name(s, t, new_state)) < 0)
|
||||
if (t == unit_id(UNIT(s)))
|
||||
continue;
|
||||
|
||||
if ((r == service_load_sysv_name(s, t)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state != UNIT_STUB)
|
||||
if (UNIT(s)->meta.load_state != UNIT_STUB)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_init(Unit *u, UnitLoadState *new_state) {
|
||||
int r;
|
||||
static void service_init(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB);
|
||||
|
||||
/* First, reset everything to the defaults, in case this is a
|
||||
* reload */
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
s->type = 0;
|
||||
s->restart = 0;
|
||||
@ -711,18 +707,25 @@ static int service_init(Unit *u, UnitLoadState *new_state) {
|
||||
s->failure = false;
|
||||
|
||||
RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
|
||||
}
|
||||
|
||||
static int service_load(Unit *u) {
|
||||
int r;
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Load a .service file */
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
if ((r = unit_load_fragment(u)) < 0)
|
||||
return r;
|
||||
|
||||
/* Load a classic init script as a fallback, if we couldn't find anything */
|
||||
if (*new_state == UNIT_STUB)
|
||||
if ((r = service_load_sysv(s, new_state)) < 0)
|
||||
if (u->meta.load_state == UNIT_STUB)
|
||||
if ((r = service_load_sysv(s)) < 0)
|
||||
return r;
|
||||
|
||||
/* Still nothing found? Then let's give up */
|
||||
if (*new_state == UNIT_STUB)
|
||||
if (u->meta.load_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
|
||||
/* We were able to load something, then let's add in the
|
||||
@ -731,7 +734,7 @@ static int service_init(Unit *u, UnitLoadState *new_state) {
|
||||
return r;
|
||||
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
if (*new_state == UNIT_LOADED) {
|
||||
if (u->meta.load_state == UNIT_LOADED) {
|
||||
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
|
||||
return r;
|
||||
|
||||
@ -788,7 +791,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
exec_context_dump(&s->exec_context, f, prefix);
|
||||
|
||||
for (c = 0; c < _SERVICE_EXEC_MAX; c++) {
|
||||
for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
|
||||
|
||||
if (!s->exec_command[c])
|
||||
continue;
|
||||
@ -904,7 +907,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
static int service_notify_sockets(Service *s) {
|
||||
static int service_notify_sockets_dead(Service *s) {
|
||||
Iterator i;
|
||||
Set *set;
|
||||
Socket *sock;
|
||||
@ -966,19 +969,14 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
state != SERVICE_STOP_SIGKILL &&
|
||||
state != SERVICE_STOP_POST &&
|
||||
state != SERVICE_FINAL_SIGTERM &&
|
||||
state != SERVICE_FINAL_SIGKILL)
|
||||
state != SERVICE_FINAL_SIGKILL) {
|
||||
if (s->control_pid > 0) {
|
||||
unit_unwatch_pid(UNIT(s), s->control_pid);
|
||||
s->control_pid = 0;
|
||||
}
|
||||
|
||||
if (state != SERVICE_START_PRE &&
|
||||
state != SERVICE_START &&
|
||||
state != SERVICE_START_POST &&
|
||||
state != SERVICE_RELOAD &&
|
||||
state != SERVICE_STOP &&
|
||||
state != SERVICE_STOP_POST)
|
||||
s->control_command = NULL;
|
||||
}
|
||||
|
||||
if (state == SERVICE_DEAD ||
|
||||
state == SERVICE_STOP ||
|
||||
@ -989,12 +987,10 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
state == SERVICE_FINAL_SIGKILL ||
|
||||
state == SERVICE_MAINTAINANCE ||
|
||||
state == SERVICE_AUTO_RESTART)
|
||||
service_notify_sockets(s);
|
||||
service_notify_sockets_dead(s);
|
||||
|
||||
if (old_state == state)
|
||||
return;
|
||||
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(s)), service_state_to_string(old_state), service_state_to_string(state));
|
||||
if (old_state != state)
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(s)), service_state_to_string(old_state), service_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
@ -1489,12 +1485,22 @@ static int service_stop(Unit *u) {
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Cannot do this now */
|
||||
if (s->state == SERVICE_START_PRE ||
|
||||
s->state == SERVICE_START ||
|
||||
s->state == SERVICE_START_POST ||
|
||||
s->state == SERVICE_RELOAD)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Already on it */
|
||||
if (s->state == SERVICE_STOP ||
|
||||
s->state == SERVICE_STOP_SIGTERM ||
|
||||
s->state == SERVICE_STOP_SIGKILL ||
|
||||
s->state == SERVICE_STOP_POST ||
|
||||
s->state == SERVICE_FINAL_SIGTERM ||
|
||||
s->state == SERVICE_FINAL_SIGKILL)
|
||||
return 0;
|
||||
|
||||
if (s->state == SERVICE_AUTO_RESTART) {
|
||||
service_set_state(s, SERVICE_DEAD);
|
||||
return 0;
|
||||
@ -1966,7 +1972,7 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
|
||||
|
||||
static const char* const service_exec_command_table[_SERVICE_EXEC_MAX] = {
|
||||
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
|
||||
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
|
||||
[SERVICE_EXEC_START] = "ExecStart",
|
||||
[SERVICE_EXEC_START_POST] = "ExecStartPost",
|
||||
@ -1981,6 +1987,7 @@ const UnitVTable service_vtable = {
|
||||
.suffix = ".service",
|
||||
|
||||
.init = service_init,
|
||||
.load = service_load,
|
||||
.done = service_done,
|
||||
|
||||
.dump = service_dump,
|
||||
|
12
service.h
12
service.h
@ -69,8 +69,8 @@ typedef enum ServiceExecCommand {
|
||||
SERVICE_EXEC_RELOAD,
|
||||
SERVICE_EXEC_STOP,
|
||||
SERVICE_EXEC_STOP_POST,
|
||||
_SERVICE_EXEC_MAX,
|
||||
_SERVICE_EXEC_INVALID = -1
|
||||
_SERVICE_EXEC_COMMAND_MAX,
|
||||
_SERVICE_EXEC_COMMAND_INVALID = -1
|
||||
} ServiceExecCommand;
|
||||
|
||||
struct Service {
|
||||
@ -85,7 +85,7 @@ struct Service {
|
||||
usec_t restart_usec;
|
||||
usec_t timeout_usec;
|
||||
|
||||
ExecCommand* exec_command[_SERVICE_EXEC_MAX];
|
||||
ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX];
|
||||
ExecContext exec_context;
|
||||
|
||||
bool permissions_start_only;
|
||||
@ -102,16 +102,16 @@ struct Service {
|
||||
pid_t main_pid, control_pid;
|
||||
bool main_pid_known:1;
|
||||
|
||||
bool sysv_has_lsb:1;
|
||||
|
||||
bool failure:1; /* if we shut down, remember why */
|
||||
Watch timer_watch;
|
||||
|
||||
bool sysv_has_lsb:1;
|
||||
char *sysv_path;
|
||||
int sysv_start_priority;
|
||||
char *sysv_runlevels;
|
||||
|
||||
RateLimit ratelimit;
|
||||
|
||||
Watch timer_watch;
|
||||
};
|
||||
|
||||
extern const UnitVTable service_vtable;
|
||||
|
102
socket.c
102
socket.c
@ -79,7 +79,7 @@ static void socket_done(Unit *u) {
|
||||
}
|
||||
|
||||
exec_context_done(&s->exec_context);
|
||||
exec_command_free_array(s->exec_command, _SOCKET_EXEC_MAX);
|
||||
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
|
||||
s->control_command = NULL;
|
||||
|
||||
if (s->control_pid > 0) {
|
||||
@ -90,17 +90,16 @@ static void socket_done(Unit *u) {
|
||||
s->service = NULL;
|
||||
|
||||
free(s->bind_to_device);
|
||||
s->bind_to_device = NULL;
|
||||
|
||||
unit_unwatch_timer(u, &s->timer_watch);
|
||||
}
|
||||
|
||||
static int socket_init(Unit *u, UnitLoadState *new_state) {
|
||||
static void socket_init(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
char *t;
|
||||
int r;
|
||||
|
||||
/* First, reset everything to the defaults, in case this is a
|
||||
* reload */
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
s->state = 0;
|
||||
s->timer_watch.type = WATCH_INVALID;
|
||||
@ -112,27 +111,24 @@ static int socket_init(Unit *u, UnitLoadState *new_state) {
|
||||
s->kill_mode = 0;
|
||||
s->failure = false;
|
||||
s->control_pid = 0;
|
||||
s->service = NULL;
|
||||
exec_context_init(&s->exec_context);
|
||||
}
|
||||
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
return r;
|
||||
static int socket_load(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
int r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
if ((r = unit_load_fragment_and_dropin(u)) < 0)
|
||||
return r;
|
||||
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
if (*new_state == UNIT_LOADED) {
|
||||
if (u->meta.load_state == UNIT_LOADED) {
|
||||
|
||||
if (!(t = unit_name_change_suffix(unit_id(u), ".service")))
|
||||
return -ENOMEM;
|
||||
|
||||
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
|
||||
free(t);
|
||||
|
||||
if (r < 0)
|
||||
if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service)))
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
|
||||
@ -163,7 +159,7 @@ static const char* listen_lookup(int type) {
|
||||
|
||||
static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const command_table[_SOCKET_EXEC_MAX] = {
|
||||
static const char* const command_table[_SOCKET_EXEC_COMMAND_MAX] = {
|
||||
[SOCKET_EXEC_START_PRE] = "StartPre",
|
||||
[SOCKET_EXEC_START_POST] = "StartPost",
|
||||
[SOCKET_EXEC_STOP_PRE] = "StopPre",
|
||||
@ -226,7 +222,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
exec_context_dump(&s->exec_context, f, prefix);
|
||||
|
||||
for (c = 0; c < _SOCKET_EXEC_MAX; c++) {
|
||||
for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) {
|
||||
if (!s->exec_command[c])
|
||||
continue;
|
||||
|
||||
@ -363,27 +359,16 @@ static void socket_set_state(Socket *s, SocketState state) {
|
||||
state != SOCKET_STOP_PRE_SIGKILL &&
|
||||
state != SOCKET_STOP_POST &&
|
||||
state != SOCKET_STOP_POST_SIGTERM &&
|
||||
state != SOCKET_STOP_POST_SIGKILL)
|
||||
state != SOCKET_STOP_POST_SIGKILL) {
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_watch);
|
||||
|
||||
if (state != SOCKET_START_PRE &&
|
||||
state != SOCKET_START_POST &&
|
||||
state != SOCKET_STOP_PRE &&
|
||||
state != SOCKET_STOP_PRE_SIGTERM &&
|
||||
state != SOCKET_STOP_PRE_SIGKILL &&
|
||||
state != SOCKET_STOP_POST &&
|
||||
state != SOCKET_STOP_POST_SIGTERM &&
|
||||
state != SOCKET_STOP_POST_SIGKILL)
|
||||
if (s->control_pid > 0) {
|
||||
unit_unwatch_pid(UNIT(s), s->control_pid);
|
||||
s->control_pid = 0;
|
||||
}
|
||||
|
||||
if (state != SOCKET_START_PRE &&
|
||||
state != SOCKET_START_POST &&
|
||||
state != SOCKET_STOP_PRE &&
|
||||
state != SOCKET_STOP_POST)
|
||||
s->control_command = NULL;
|
||||
}
|
||||
|
||||
if (state != SOCKET_START_POST &&
|
||||
state != SOCKET_LISTENING &&
|
||||
@ -396,15 +381,13 @@ static void socket_set_state(Socket *s, SocketState state) {
|
||||
if (state != SOCKET_LISTENING)
|
||||
socket_unwatch_fds(s);
|
||||
|
||||
if (state == old_state)
|
||||
return;
|
||||
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]);
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]);
|
||||
|
||||
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
|
||||
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
@ -412,11 +395,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
|
||||
assert(c);
|
||||
assert(_pid);
|
||||
|
||||
if (timeout) {
|
||||
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
|
||||
goto fail;
|
||||
} else
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_watch);
|
||||
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = exec_spawn(c,
|
||||
&s->exec_context,
|
||||
@ -436,8 +416,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (timeout)
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_watch);
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_watch);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -459,7 +438,7 @@ static void socket_enter_stop_post(Socket *s, bool success) {
|
||||
s->failure = true;
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST]))
|
||||
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
socket_set_state(s, SOCKET_STOP_POST);
|
||||
@ -502,7 +481,6 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
socket_set_state(s, state);
|
||||
@ -529,7 +507,7 @@ static void socket_enter_stop_pre(Socket *s, bool success) {
|
||||
s->failure = true;
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE]))
|
||||
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
socket_set_state(s, SOCKET_STOP_PRE);
|
||||
@ -570,7 +548,7 @@ static void socket_enter_start_post(Socket *s) {
|
||||
}
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST]))
|
||||
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0) {
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) {
|
||||
log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
@ -591,7 +569,7 @@ static void socket_enter_start_pre(Socket *s) {
|
||||
assert(s);
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]))
|
||||
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
socket_set_state(s, SOCKET_START_PRE);
|
||||
@ -634,7 +612,7 @@ static void socket_run_next(Socket *s, bool success) {
|
||||
|
||||
s->control_command = s->control_command->command_next;
|
||||
|
||||
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
return;
|
||||
@ -671,6 +649,13 @@ static int socket_start(Unit *u) {
|
||||
if (s->service->meta.load_state != UNIT_LOADED)
|
||||
return -ENOENT;
|
||||
|
||||
/* If the service is alredy actvie we cannot start the
|
||||
* socket */
|
||||
if (s->service->state != SERVICE_DEAD &&
|
||||
s->service->state != SERVICE_MAINTAINANCE &&
|
||||
s->service->state != SERVICE_AUTO_RESTART)
|
||||
return -EBUSY;
|
||||
|
||||
assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE);
|
||||
|
||||
s->failure = false;
|
||||
@ -689,6 +674,15 @@ static int socket_stop(Unit *u) {
|
||||
s->state == SOCKET_START_POST)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Already on it */
|
||||
if (s->state == SOCKET_STOP_PRE ||
|
||||
s->state == SOCKET_STOP_PRE_SIGTERM ||
|
||||
s->state == SOCKET_STOP_PRE_SIGKILL ||
|
||||
s->state == SOCKET_STOP_POST ||
|
||||
s->state == SOCKET_STOP_POST_SIGTERM ||
|
||||
s->state == SOCKET_STOP_POST_SIGTERM)
|
||||
return 0;
|
||||
|
||||
assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
|
||||
|
||||
socket_enter_stop_pre(s, true);
|
||||
@ -781,14 +775,13 @@ static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
|
||||
|
||||
assert(s);
|
||||
assert(elapsed == 1);
|
||||
|
||||
assert(w == &s->timer_watch);
|
||||
|
||||
switch (s->state) {
|
||||
|
||||
case SOCKET_START_PRE:
|
||||
case SOCKET_START_POST:
|
||||
log_warning("%s operation timed out. Stopping.", unit_id(u));
|
||||
log_warning("%s starting timed out. Stopping.", unit_id(u));
|
||||
socket_enter_stop_pre(s, false);
|
||||
break;
|
||||
|
||||
@ -874,6 +867,7 @@ const UnitVTable socket_vtable = {
|
||||
.suffix = ".socket",
|
||||
|
||||
.init = socket_init,
|
||||
.load = socket_load,
|
||||
.done = socket_done,
|
||||
|
||||
.dump = socket_dump,
|
||||
|
12
socket.h
12
socket.h
@ -41,7 +41,8 @@ typedef enum SocketState {
|
||||
SOCKET_STOP_POST_SIGTERM,
|
||||
SOCKET_STOP_POST_SIGKILL,
|
||||
SOCKET_MAINTAINANCE,
|
||||
_SOCKET_STATE_MAX
|
||||
_SOCKET_STATE_MAX,
|
||||
_SOCKET_STATE_INVALID = -1
|
||||
} SocketState;
|
||||
|
||||
typedef enum SocketExecCommand {
|
||||
@ -49,12 +50,15 @@ typedef enum SocketExecCommand {
|
||||
SOCKET_EXEC_START_POST,
|
||||
SOCKET_EXEC_STOP_PRE,
|
||||
SOCKET_EXEC_STOP_POST,
|
||||
_SOCKET_EXEC_MAX
|
||||
_SOCKET_EXEC_COMMAND_MAX,
|
||||
_SOCKET_EXEC_COMMAND_INVALID = -1
|
||||
} SocketExecCommand;
|
||||
|
||||
typedef enum SocketType {
|
||||
SOCKET_SOCKET,
|
||||
SOCKET_FIFO
|
||||
SOCKET_FIFO,
|
||||
_SOCKET_FIFO_MAX,
|
||||
_SOCKET_FIFO_INVALID = -1
|
||||
} SocketType;
|
||||
|
||||
typedef struct SocketPort SocketPort;
|
||||
@ -82,7 +86,7 @@ struct Socket {
|
||||
|
||||
usec_t timeout_usec;
|
||||
|
||||
ExecCommand* exec_command[_SOCKET_EXEC_MAX];
|
||||
ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
|
||||
ExecContext exec_context;
|
||||
|
||||
Service *service;
|
||||
|
18
target.c
18
target.c
@ -37,6 +37,15 @@ static const char* const state_string_table[_TARGET_STATE_MAX] = {
|
||||
[TARGET_ACTIVE] = "active"
|
||||
};
|
||||
|
||||
static void target_init(Unit *u) {
|
||||
Target *t = TARGET(u);
|
||||
|
||||
assert(t);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
t->state = 0;
|
||||
}
|
||||
|
||||
static void target_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Target *t = TARGET(u);
|
||||
|
||||
@ -52,13 +61,11 @@ static void target_set_state(Target *t, TargetState state) {
|
||||
TargetState old_state;
|
||||
assert(t);
|
||||
|
||||
if (state == t->state)
|
||||
return;
|
||||
|
||||
old_state = t->state;
|
||||
t->state = state;
|
||||
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(t)), state_string_table[old_state], state_string_table[state]);
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", unit_id(UNIT(t)), state_string_table[old_state], state_string_table[state]);
|
||||
|
||||
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
@ -121,7 +128,8 @@ int target_get_runlevel(Target *t) {
|
||||
const UnitVTable target_vtable = {
|
||||
.suffix = ".target",
|
||||
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
.init = target_init,
|
||||
.load = unit_load_fragment_and_dropin,
|
||||
|
||||
.dump = target_dump,
|
||||
|
||||
|
2
timer.c
2
timer.c
@ -44,7 +44,7 @@ static UnitActiveState timer_active_state(Unit *u) {
|
||||
const UnitVTable timer_vtable = {
|
||||
.suffix = ".timer",
|
||||
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
.load = unit_load_fragment_and_dropin,
|
||||
.done = timer_done,
|
||||
|
||||
.active_state = timer_active_state
|
||||
|
94
unit.c
94
unit.c
@ -155,6 +155,11 @@ int unit_add_name(Unit *u, const char *text) {
|
||||
if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type)
|
||||
return -EINVAL;
|
||||
|
||||
if (u->meta.type != _UNIT_TYPE_INVALID &&
|
||||
UNIT_VTABLE(u)->no_alias &&
|
||||
!set_isempty(u->meta.names))
|
||||
return -EEXIST;
|
||||
|
||||
if (!(s = strdup(text)))
|
||||
return -ENOMEM;
|
||||
|
||||
@ -173,10 +178,14 @@ int unit_add_name(Unit *u, const char *text) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (u->meta.type == _UNIT_TYPE_INVALID)
|
||||
if (u->meta.type == _UNIT_TYPE_INVALID) {
|
||||
LIST_PREPEND(Meta, units_per_type, u->meta.manager->units_per_type[t], &u->meta);
|
||||
|
||||
u->meta.type = t;
|
||||
u->meta.type = t;
|
||||
|
||||
if (UNIT_VTABLE(u)->init)
|
||||
UNIT_VTABLE(u)->init(u);
|
||||
}
|
||||
|
||||
if (!u->meta.id)
|
||||
u->meta.id = s;
|
||||
@ -278,9 +287,6 @@ void unit_free(Unit *u) {
|
||||
bus_unit_send_removed_signal(u);
|
||||
|
||||
/* Detach from next 'bigger' objects */
|
||||
|
||||
cgroup_bonding_free_list(u->meta.cgroup_bondings);
|
||||
|
||||
SET_FOREACH(t, u->meta.names, i)
|
||||
hashmap_remove_value(u->meta.manager->units, t, u);
|
||||
|
||||
@ -296,13 +302,15 @@ void unit_free(Unit *u) {
|
||||
if (u->meta.in_cleanup_queue)
|
||||
LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta);
|
||||
|
||||
/* Free data and next 'smaller' objects */
|
||||
if (u->meta.job)
|
||||
job_free(u->meta.job);
|
||||
|
||||
if (u->meta.load_state != UNIT_STUB)
|
||||
if (UNIT_VTABLE(u)->done)
|
||||
UNIT_VTABLE(u)->done(u);
|
||||
|
||||
/* Free data and next 'smaller' objects */
|
||||
if (u->meta.job)
|
||||
job_free(u->meta.job);
|
||||
cgroup_bonding_free_list(u->meta.cgroup_bondings);
|
||||
|
||||
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
|
||||
bidi_set_free(u, u->meta.dependencies[d]);
|
||||
@ -401,9 +409,6 @@ int unit_merge(Unit *u, Unit *other) {
|
||||
if (other == u)
|
||||
return 0;
|
||||
|
||||
/* This merges 'other' into 'unit'. FIXME: This does not
|
||||
* rollback on failure. */
|
||||
|
||||
if (u->meta.type != u->meta.type)
|
||||
return -EINVAL;
|
||||
|
||||
@ -569,18 +574,16 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
}
|
||||
|
||||
/* Common implementation for multiple backends */
|
||||
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) {
|
||||
int unit_load_fragment_and_dropin(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
|
||||
|
||||
/* Load a .service file */
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
if ((r = unit_load_fragment(u)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
if (u->meta.load_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
|
||||
/* Load drop-in directory data */
|
||||
@ -591,22 +594,20 @@ int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) {
|
||||
}
|
||||
|
||||
/* Common implementation for multiple backends */
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) {
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
|
||||
|
||||
/* Same as unit_load_fragment_and_dropin(), but whether
|
||||
* something can be loaded or not doesn't matter. */
|
||||
|
||||
/* Load a .service file */
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
if ((r = unit_load_fragment(u)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
*new_state = UNIT_LOADED;
|
||||
if (u->meta.load_state == UNIT_STUB)
|
||||
u->meta.load_state = UNIT_LOADED;
|
||||
|
||||
/* Load drop-in directory data */
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
@ -617,7 +618,6 @@ int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) {
|
||||
|
||||
int unit_load(Unit *u) {
|
||||
int r;
|
||||
UnitLoadState res;
|
||||
|
||||
assert(u);
|
||||
|
||||
@ -626,21 +626,21 @@ int unit_load(Unit *u) {
|
||||
u->meta.in_load_queue = false;
|
||||
}
|
||||
|
||||
if (u->meta.type == _UNIT_TYPE_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
if (u->meta.load_state != UNIT_STUB)
|
||||
return 0;
|
||||
|
||||
if (UNIT_VTABLE(u)->init) {
|
||||
res = UNIT_STUB;
|
||||
if ((r = UNIT_VTABLE(u)->init(u, &res)) < 0)
|
||||
if (UNIT_VTABLE(u)->load)
|
||||
if ((r = UNIT_VTABLE(u)->load(u)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (res == UNIT_STUB) {
|
||||
if (u->meta.load_state == UNIT_STUB) {
|
||||
r = -ENOENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u->meta.load_state = res;
|
||||
assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into);
|
||||
|
||||
unit_add_to_dbus_queue(unit_follow_merge(u));
|
||||
@ -711,9 +711,6 @@ int unit_stop(Unit *u) {
|
||||
if (!UNIT_VTABLE(u)->stop)
|
||||
return -EBADR;
|
||||
|
||||
if (state == UNIT_DEACTIVATING)
|
||||
return 0;
|
||||
|
||||
unit_add_to_dbus_queue(u);
|
||||
return UNIT_VTABLE(u)->stop(u);
|
||||
}
|
||||
@ -940,25 +937,38 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
|
||||
retroactively_stop_dependencies(u);
|
||||
}
|
||||
|
||||
if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
|
||||
|
||||
/* Some names are special */
|
||||
if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
|
||||
if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
|
||||
log_info("D-Bus became available, trying to reconnect.");
|
||||
/* The bus just got started, hence try to connect to it. */
|
||||
/* The bus just might have become available,
|
||||
* hence try to connect to it, if we aren't
|
||||
* yet connected. */
|
||||
bus_init_system(u->meta.manager);
|
||||
bus_init_api(u->meta.manager);
|
||||
}
|
||||
|
||||
if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) {
|
||||
/* The syslog daemon just got started, hence try to connect to it. */
|
||||
log_info("Syslog became available, trying to reconnect.");
|
||||
if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE))
|
||||
/* The syslog daemon just might have become
|
||||
* available, hence try to connect to it, if
|
||||
* we aren't yet connected. */
|
||||
log_open_syslog();
|
||||
}
|
||||
|
||||
} else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
|
||||
if (u->meta.type == UNIT_MOUNT)
|
||||
/* Another directory became available, let's
|
||||
* check if that is enough to write our utmp
|
||||
* entry. */
|
||||
manager_write_utmp_reboot(u->meta.manager);
|
||||
|
||||
if (u->meta.type == UNIT_TARGET)
|
||||
/* A target got activated, maybe this is a runlevel? */
|
||||
manager_write_utmp_runlevel(u->meta.manager, u);
|
||||
|
||||
} else if (!UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
|
||||
|
||||
if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE))
|
||||
/* The syslog daemon just got terminated, hence try to disconnect from it. */
|
||||
/* The syslog daemon might just have
|
||||
* terminated, hence try to disconnect from
|
||||
* it. */
|
||||
log_close_syslog();
|
||||
|
||||
/* We don't care about D-Bus here, since we'll get an
|
||||
|
29
unit.h
29
unit.h
@ -196,8 +196,31 @@ union Unit {
|
||||
struct UnitVTable {
|
||||
const char *suffix;
|
||||
|
||||
int (*init)(Unit *u, UnitLoadState *new_state);
|
||||
/* Can units of this type have multiple names? */
|
||||
bool no_alias:1;
|
||||
|
||||
/* If true units of this types can never have "Requires"
|
||||
* dependencies, because state changes can only be observed,
|
||||
* not triggered */
|
||||
bool refuse_requires:1;
|
||||
|
||||
/* This should reset all type-specific variables. This should
|
||||
* not allocate memory, and is either called with 0
|
||||
* initialized data, or with data left from done() */
|
||||
void (*init)(Unit *u);
|
||||
|
||||
/* Actually load data from disk. This may fail, and should set
|
||||
* load_state to UNIT_LOADED, UNIT_MERGED or leave it at
|
||||
* UNIT_STUB if no configuration could be found. */
|
||||
int (*load)(Unit *u);
|
||||
|
||||
/* This should free all type-specific variables. It should be
|
||||
* idempotent. There's no need to reset variables that deal
|
||||
* with dynamic memory/resources. */
|
||||
void (*done)(Unit *u);
|
||||
|
||||
/* If a a lot of units got created via enumerate(), this is
|
||||
* where to actually set the state and call unit_notify(). */
|
||||
int (*coldplug)(Unit *u);
|
||||
|
||||
void (*dump)(Unit *u, FILE *f, const char *prefix);
|
||||
@ -285,8 +308,8 @@ int unit_merge_by_name(Unit *u, const char *other);
|
||||
|
||||
Unit *unit_follow_merge(Unit *u);
|
||||
|
||||
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state);
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state);
|
||||
int unit_load_fragment_and_dropin(Unit *u);
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u);
|
||||
int unit_load(Unit *unit);
|
||||
|
||||
const char* unit_id(Unit *u);
|
||||
|
210
utmp-wtmp.c
Normal file
210
utmp-wtmp.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <utmpx.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "utmp-wtmp.h"
|
||||
|
||||
int utmp_get_runlevel(int *runlevel, int *previous) {
|
||||
struct utmpx lookup, *found;
|
||||
int r;
|
||||
const char *e;
|
||||
|
||||
assert(runlevel);
|
||||
|
||||
/* If these values are set in the environment this takes
|
||||
* precedence. Presumably, sysvinit does this to work around a
|
||||
* race condition that would otherwise exist where we'd always
|
||||
* go to disk and hence might read runlevel data that might be
|
||||
* very new and does not apply to the current script being
|
||||
* executed. */
|
||||
|
||||
if ((e = getenv("RUNLEVEL")) && e[0] > 0) {
|
||||
*runlevel = e[0];
|
||||
|
||||
if (previous) {
|
||||
/* $PREVLEVEL seems to be an Upstart thing */
|
||||
|
||||
if ((e = getenv("PREVLEVEL")) && e[0] > 0)
|
||||
*previous = e[0];
|
||||
else
|
||||
*previous = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (utmpxname(_PATH_UTMPX) < 0)
|
||||
return -errno;
|
||||
|
||||
setutxent();
|
||||
|
||||
zero(lookup);
|
||||
lookup.ut_type = RUN_LVL;
|
||||
|
||||
if (!(found = getutxid(&lookup)))
|
||||
r = -errno;
|
||||
else {
|
||||
int a, b;
|
||||
|
||||
a = found->ut_pid & 0xFF;
|
||||
b = (found->ut_pid >> 8) & 0xFF;
|
||||
|
||||
if (a < 0 || b < 0)
|
||||
r = -EIO;
|
||||
else {
|
||||
*runlevel = a;
|
||||
|
||||
if (previous)
|
||||
*previous = b;
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
endutxent();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void init_entry(struct utmpx *store, usec_t timestamp) {
|
||||
struct utsname uts;
|
||||
|
||||
assert(store);
|
||||
|
||||
zero(*store);
|
||||
zero(uts);
|
||||
|
||||
if (timestamp <= 0)
|
||||
timestamp = now(CLOCK_REALTIME);
|
||||
|
||||
store->ut_tv.tv_sec = timestamp / USEC_PER_SEC;
|
||||
store->ut_tv.tv_usec = timestamp % USEC_PER_SEC;
|
||||
|
||||
if (uname(&uts) >= 0)
|
||||
strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
|
||||
|
||||
strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
|
||||
strncpy(store->ut_id, "~~", sizeof(store->ut_id));
|
||||
}
|
||||
|
||||
static int write_entry_utmp(const struct utmpx *store) {
|
||||
int r;
|
||||
|
||||
assert(store);
|
||||
|
||||
/* utmp is similar to wtmp, but there is only one entry for
|
||||
* each entry type resp. user; i.e. basically a key/value
|
||||
* table. */
|
||||
|
||||
if (utmpxname(_PATH_UTMPX) < 0)
|
||||
return -errno;
|
||||
|
||||
setutxent();
|
||||
|
||||
if (!pututxline(store))
|
||||
r = -errno;
|
||||
else
|
||||
r = 0;
|
||||
|
||||
endutxent();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int write_entry_wtmp(const struct utmpx *store) {
|
||||
assert(store);
|
||||
|
||||
/* wtmp is a simple append-only file where each entry is
|
||||
simply appended to * the end; i.e. basically a log. */
|
||||
|
||||
errno = 0;
|
||||
updwtmpx(_PATH_WTMPX, store);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
static int write_entry_both(const struct utmpx *store) {
|
||||
int r, s;
|
||||
|
||||
r = write_entry_utmp(store);
|
||||
s = write_entry_wtmp(store);
|
||||
|
||||
if (r >= 0)
|
||||
r = s;
|
||||
|
||||
/* If utmp/wtmp have been disabled, that's a good thing, hence
|
||||
* ignore the errors */
|
||||
if (r == -ENOENT)
|
||||
r = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int utmp_put_shutdown(usec_t timestamp) {
|
||||
struct utmpx store;
|
||||
|
||||
init_entry(&store, timestamp);
|
||||
|
||||
store.ut_type = RUN_LVL;
|
||||
strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
|
||||
|
||||
return write_entry_both(&store);
|
||||
}
|
||||
|
||||
int utmp_put_reboot(usec_t timestamp) {
|
||||
struct utmpx store;
|
||||
|
||||
init_entry(&store, timestamp);
|
||||
|
||||
store.ut_type = BOOT_TIME;
|
||||
strncpy(store.ut_user, "reboot", sizeof(store.ut_type));
|
||||
|
||||
return write_entry_both(&store);
|
||||
}
|
||||
|
||||
int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous) {
|
||||
struct utmpx store;
|
||||
int r;
|
||||
|
||||
assert(runlevel > 0);
|
||||
|
||||
if (previous <= 0) {
|
||||
/* Find the old runlevel automatically */
|
||||
|
||||
if ((r = utmp_get_runlevel(&previous, NULL)) < 0)
|
||||
return r;
|
||||
|
||||
if (previous == runlevel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
init_entry(&store, timestamp);
|
||||
|
||||
store.ut_type = RUN_LVL;
|
||||
store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
|
||||
strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
|
||||
|
||||
return write_entry_both(&store);
|
||||
}
|
33
utmp-wtmp.h
Normal file
33
utmp-wtmp.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooutmpwtmphfoo
|
||||
#define fooutmpwtmphfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int utmp_get_runlevel(int *runlevel, int *previous);
|
||||
|
||||
int utmp_put_shutdown(usec_t timestamp);
|
||||
int utmp_put_reboot(usec_t timestamp);
|
||||
int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user