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:
Lennart Poettering 2010-04-10 17:53:17 +02:00
parent 108736d0d3
commit e537352b9b
21 changed files with 1695 additions and 404 deletions

View File

@ -106,7 +106,9 @@ COMMON_SOURCES= \
mount-setup.c \ mount-setup.c \
mount-setup.h \ mount-setup.h \
hostname-setup.c \ hostname-setup.c \
hostname-setup.h hostname-setup.h \
utmp-wtmp.c \
utmp-wtmp.h
systemd_SOURCES = \ systemd_SOURCES = \
$(COMMON_SOURCES) \ $(COMMON_SOURCES) \

View File

@ -26,108 +26,80 @@
#include "load-fragment.h" #include "load-fragment.h"
#include "load-dropin.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; int r;
Automount *a = AUTOMOUNT(u); Automount *a = AUTOMOUNT(u);
assert(a); assert(u);
assert(u->meta.load_state == UNIT_STUB);
exec_context_init(&a->exec_context);
/* Load a .automount file */ /* 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; return r;
if (*new_state == UNIT_STUB) if (u->meta.load_state == UNIT_LOADED) {
*new_state = UNIT_LOADED;
/* Load drop-in directory data */ if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) return r;
return r;
if (*new_state == UNIT_LOADED) {
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0) if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0)
return r; 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; return 0;
} }
static void automount_done(Unit *u) { static void automount_done(Unit *u) {
Automount *d = AUTOMOUNT(u); Automount *a = AUTOMOUNT(u);
assert(d); assert(a);
free(d->path);
a->mount = NULL;
} }
static void automount_dump(Unit *u, FILE *f, const char *prefix) { 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); Automount *s = AUTOMOUNT(u);
assert(s); assert(s);
fprintf(f, fprintf(f,
"%sAutomount State: %s\n" "%sAutomount State: %s\n",
"%sPath: %s\n", prefix, state_string_table[s->state]);
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);
}
} }
static UnitActiveState automount_active_state(Unit *u) { static UnitActiveState automount_active_state(Unit *u) {
static const UnitActiveState table[_AUTOMOUNT_STATE_MAX] = { return state_translation_table[AUTOMOUNT(u)->state];
[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];
} }
const UnitVTable automount_vtable = { const UnitVTable automount_vtable = {
.suffix = ".mount", .suffix = ".mount",
.no_alias = true,
.init = automount_init, .init = automount_init,
.load = automount_load,
.done = automount_done, .done = automount_done,
.dump = automount_dump, .dump = automount_dump,

View File

@ -28,34 +28,17 @@ typedef struct Automount Automount;
typedef enum AutomountState { typedef enum AutomountState {
AUTOMOUNT_DEAD, AUTOMOUNT_DEAD,
AUTOMOUNT_START_PRE,
AUTOMOUNT_START_POST,
AUTOMOUNT_WAITING, AUTOMOUNT_WAITING,
AUTOMOUNT_RUNNING, AUTOMOUNT_RUNNING,
AUTOMOUNT_STOP_PRE,
AUTOMOUNT_STOP_POST,
AUTOMOUNT_MAINTAINANCE, AUTOMOUNT_MAINTAINANCE,
_AUTOMOUNT_STATE_MAX _AUTOMOUNT_STATE_MAX,
_AUTOMOUNT_STATE_INVALID = -1
} AutomountState; } 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 { struct Automount {
Meta meta; Meta meta;
AutomountState state; AutomountState state;
char *path;
ExecCommand* exec_command[_AUTOMOUNT_EXEC_MAX];
ExecContext exec_context;
pid_t contol_pid;
Mount *mount; Mount *mount;
}; };

View File

@ -42,20 +42,29 @@ static void device_done(Unit *u) {
Device *d = DEVICE(u); Device *d = DEVICE(u);
assert(d); assert(d);
free(d->sysfs); 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) { static void device_set_state(Device *d, DeviceState state) {
DeviceState old_state; DeviceState old_state;
assert(d); assert(d);
if (state == d->state)
return;
old_state = d->state; old_state = d->state;
d->state = 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]); 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) if ((r = device_add_escaped_name(u, sysfs, true)) < 0)
goto fail; goto fail;
unit_add_to_load_queue(u);
} else
delete = false;
if (!(DEVICE(u)->sysfs))
if (!(DEVICE(u)->sysfs = strdup(sysfs))) { if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
r = -ENOMEM; r = -ENOMEM;
goto fail; goto fail;
} }
unit_add_to_load_queue(u);
} else
delete = false;
if (dn) if (dn)
if ((r = device_add_escaped_name(u, dn, true)) < 0) if ((r = device_add_escaped_name(u, dn, true)) < 0)
goto fail; goto fail;
@ -460,7 +470,8 @@ fail:
const UnitVTable device_vtable = { const UnitVTable device_vtable = {
.suffix = ".device", .suffix = ".device",
.init = unit_load_fragment_and_dropin_optional, .init = device_init,
.load = unit_load_fragment_and_dropin_optional,
.done = device_done, .done = device_done,
.coldplug = device_coldplug, .coldplug = device_coldplug,

View File

@ -56,6 +56,15 @@ static int config_parse_deps(
assert(lvalue); assert(lvalue);
assert(rvalue); 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) { FOREACH_WORD(w, l, rvalue, state) {
char *t; char *t;
int r; int r;
@ -1116,7 +1125,80 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
return 0; 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] = { static const char* const section_table[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = "Service", [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" }, { "KillMode", config_parse_kill_mode, &u->socket.kill_mode, "Socket" },
EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "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 } { 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; char *filename = NULL, *id = NULL;
Unit *merged; Unit *merged;
if (!u) {
/* Dirty dirty hack. */
dump_items((FILE*) path, items);
return 0;
}
assert(u); assert(u);
assert(new_state); assert(path);
sections[0] = "Meta"; sections[0] = "Meta";
sections[1] = section_table[u->meta.type]; 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; goto finish;
if (merged != u) { if (merged != u) {
*new_state = UNIT_MERGED; u->meta.load_state = UNIT_MERGED;
r = 0; r = 0;
goto finish; goto finish;
} }
@ -1319,7 +1413,7 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
u->meta.fragment_path = filename; u->meta.fragment_path = filename;
filename = NULL; filename = NULL;
*new_state = UNIT_LOADED; u->meta.load_state = UNIT_LOADED;
r = 0; r = 0;
finish: finish:
@ -1335,16 +1429,14 @@ finish:
return r; return r;
} }
int unit_load_fragment(Unit *u, UnitLoadState *new_state) { int unit_load_fragment(Unit *u) {
int r; int r;
assert(u); assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB);
if (u->meta.fragment_path) { 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; return r;
} else { } else {
@ -1353,23 +1445,29 @@ int unit_load_fragment(Unit *u, UnitLoadState *new_state) {
/* Try to find the unit under its id */ /* Try to find the unit under its id */
if ((t = unit_id(u))) 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; return r;
/* Try to find an alias we can load this with */ /* 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) { SET_FOREACH(t, u->meta.names, i) {
if (unit_id(u) == t) if (unit_id(u) == t)
continue; continue;
if ((r = load_from_path(u, t, new_state)) < 0) if ((r = load_from_path(u, t)) < 0)
return r; return r;
if (*new_state != UNIT_STUB) if (u->meta.load_state != UNIT_STUB)
break; break;
} }
} }
return 0; return 0;
} }
void unit_dump_config_items(FILE *f) {
/* OK, this wins a prize for extreme ugliness. */
load_from_path(NULL, (const void*) f);
}

View File

@ -26,6 +26,8 @@
/* Read service data from .desktop file style configuration fragments */ /* 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 #endif

35
main.c
View File

@ -38,7 +38,8 @@
static enum { static enum {
ACTION_RUN, ACTION_RUN,
ACTION_HELP, ACTION_HELP,
ACTION_TEST ACTION_TEST,
ACTION_DUMP_CONFIGURATION_ITEMS
} action = ACTION_RUN; } action = ACTION_RUN;
static char *default_unit = NULL; static char *default_unit = NULL;
@ -138,7 +139,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_LOG_TARGET, ARG_LOG_TARGET,
ARG_DEFAULT, ARG_DEFAULT,
ARG_RUNNING_AS, ARG_RUNNING_AS,
ARG_TEST ARG_TEST,
ARG_DUMP_CONFIGURATION_ITEMS
}; };
static const struct option options[] = { 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 }, { "running-as", required_argument, NULL, ARG_RUNNING_AS },
{ "test", no_argument, NULL, ARG_TEST }, { "test", no_argument, NULL, ARG_TEST },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
@ -202,6 +205,10 @@ static int parse_argv(int argc, char *argv[]) {
action = ACTION_TEST; action = ACTION_TEST;
break; break;
case ARG_DUMP_CONFIGURATION_ITEMS:
action = ACTION_DUMP_CONFIGURATION_ITEMS;
break;
case 'h': case 'h':
action = ACTION_HELP; action = ACTION_HELP;
break; break;
@ -220,12 +227,13 @@ static int parse_argv(int argc, char *argv[]) {
static int help(void) { static int help(void) {
printf("%s [options]\n\n" printf("%s [options]\n\n"
" -h --help Show this help\n" " -h --help Show this help\n"
" --default=UNIT Set default unit\n" " --default=UNIT Set default unit\n"
" --log-level=LEVEL Set log level\n" " --log-level=LEVEL Set log level\n"
" --log-target=TARGET Set log target (console, syslog, kmsg)\n" " --log-target=TARGET Set log target (console, syslog, kmsg)\n"
" --running-as=AS Set running as (init, system, session)\n" " --running-as=AS Set running as (init, system, session)\n"
" --test Determine startup sequence, dump it and exit\n", " --test Determine startup sequence, dump it and exit\n"
" --dump-configuration-items Dump understood unit configuration items\n",
__progname); __progname);
return 0; return 0;
@ -270,12 +278,18 @@ int main(int argc, char *argv[]) {
if (action == ACTION_HELP) { if (action == ACTION_HELP) {
retval = help(); retval = help();
goto finish; 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); assert_se(action == ACTION_RUN || action == ACTION_TEST);
/* Set up PATH unless it is already set */ /* 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 */ /* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0); 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)); log_debug("systemd running in %s mode.", manager_running_as_to_string(running_as));
if (running_as == MANAGER_INIT) if (running_as == MANAGER_INIT)
if (hostname_setup() < 0) hostname_setup();
goto finish;
if ((r = manager_new(running_as, &m)) < 0) { if ((r = manager_new(running_as, &m)) < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r)); log_error("Failed to allocate manager object: %s", strerror(-r));

View File

@ -27,6 +27,7 @@
#include <sys/signalfd.h> #include <sys/signalfd.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include <utmpx.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/reboot.h> #include <sys/reboot.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@ -42,6 +43,7 @@
#include "ratelimit.h" #include "ratelimit.h"
#include "cgroup.h" #include "cgroup.h"
#include "mount-setup.h" #include "mount-setup.h"
#include "utmp-wtmp.h"
static int manager_setup_signals(Manager *m) { static int manager_setup_signals(Manager *m) {
sigset_t mask; sigset_t mask;
@ -291,6 +293,8 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
if (!(m = new0(Manager, 1))) if (!(m = new0(Manager, 1)))
return -ENOMEM; return -ENOMEM;
m->boot_timestamp = now(CLOCK_REALTIME);
m->running_as = running_as; m->running_as = running_as;
m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1; 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 */ 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; 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; return 0;
} }
@ -1704,6 +1712,72 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
return 0; 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] = { static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_INIT] = "init", [MANAGER_INIT] = "init",
[MANAGER_SYSTEM] = "system", [MANAGER_SYSTEM] = "system",

View File

@ -145,6 +145,8 @@ struct Manager {
bool request_api_bus_dispatch:1; bool request_api_bus_dispatch:1;
bool request_system_bus_dispatch:1; bool request_system_bus_dispatch:1;
bool utmp_reboot_written:1;
Hashmap *watch_pids; /* pid => Unit object n:1 */ Hashmap *watch_pids; /* pid => Unit object n:1 */
int epoll_fd; int epoll_fd;
@ -172,6 +174,8 @@ struct Manager {
Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */ Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
char *cgroup_controller; char *cgroup_controller;
char *cgroup_hierarchy; char *cgroup_hierarchy;
usec_t boot_timestamp;
}; };
int manager_new(ManagerRunningAs running_as, Manager **m); 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); const char *manager_running_as_to_string(ManagerRunningAs i);
ManagerRunningAs manager_running_as_from_string(const char *s); 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 #endif

1038
mount.c

File diff suppressed because it is too large Load Diff

57
mount.h
View File

@ -28,31 +28,76 @@ typedef struct Mount Mount;
typedef enum MountState { typedef enum MountState {
MOUNT_DEAD, 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_MOUNTED,
MOUNT_REMOUNTING,
MOUNT_UNMOUNTING, MOUNT_UNMOUNTING,
MOUNT_MOUNTING_SIGTERM,
MOUNT_MOUNTING_SIGKILL,
MOUNT_REMOUNTING_SIGTERM,
MOUNT_REMOUNTING_SIGKILL,
MOUNT_UNMOUNTING_SIGTERM,
MOUNT_UNMOUNTING_SIGKILL,
MOUNT_MAINTAINANCE, MOUNT_MAINTAINANCE,
_MOUNT_STATE_MAX _MOUNT_STATE_MAX,
_MOUNT_STATE_INVALID = -1
} MountState; } 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 { struct Mount {
Meta meta; 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_etc_fstab:1;
bool from_proc_self_mountinfo:1; bool from_proc_self_mountinfo:1;
bool from_fragment:1;
/* Used while looking for mount points that vanished or got /* Used while looking for mount points that vanished or got
* added from/to /proc/self/mountinfo */ * added from/to /proc/self/mountinfo */
bool still_exists:1; bool is_mounted:1;
bool just_created: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; extern const UnitVTable mount_vtable;
void mount_fd_event(Manager *m, int events); void mount_fd_event(Manager *m, int events);
int mount_path_is_mounted(Manager *m, const char* path);
#endif #endif

View File

@ -78,7 +78,7 @@ static void service_done(Unit *u) {
s->sysv_runlevels = NULL; s->sysv_runlevels = NULL;
exec_context_done(&s->exec_context); 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; s->control_command = NULL;
/* This will leak a process, but at least no memory or any of /* 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; 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; FILE *f;
Unit *u; Unit *u;
unsigned line = 0; unsigned line = 0;
@ -294,7 +294,6 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
assert(s); assert(s);
assert(path); assert(path);
assert(new_state);
u = UNIT(s); u = UNIT(s);
@ -607,7 +606,7 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
goto finish; goto finish;
} }
*new_state = UNIT_LOADED; u->meta.load_state = UNIT_LOADED;
r = 0; r = 0;
finish: finish:
@ -618,7 +617,7 @@ finish:
return r; 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; char **p;
assert(s); assert(s);
@ -634,26 +633,25 @@ static int service_load_sysv_name(Service *s, const char *name, UnitLoadState *n
assert(endswith(path, ".service")); assert(endswith(path, ".service"));
path[strlen(path)-8] = 0; path[strlen(path)-8] = 0;
r = service_load_sysv_path(s, path, new_state); r = service_load_sysv_path(s, path);
free(path); free(path);
if (r < 0) if (r < 0)
return r; return r;
if (*new_state != UNIT_STUB) if ((UNIT(s)->meta.load_state != UNIT_STUB))
break; break;
} }
return 0; return 0;
} }
static int service_load_sysv(Service *s, UnitLoadState *new_state) { static int service_load_sysv(Service *s) {
const char *t; const char *t;
Iterator i; Iterator i;
int r; int r;
assert(s); assert(s);
assert(new_state);
/* Load service data from SysV init scripts, preferably with /* Load service data from SysV init scripts, preferably with
* LSB headers ... */ * LSB headers ... */
@ -662,31 +660,29 @@ static int service_load_sysv(Service *s, UnitLoadState *new_state) {
return 0; return 0;
if ((t = unit_id(UNIT(s)))) 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; return r;
if (*new_state == UNIT_STUB) if (UNIT(s)->meta.load_state == UNIT_STUB)
SET_FOREACH(t, UNIT(s)->meta.names, i) { 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; return r;
if (*new_state != UNIT_STUB) if (UNIT(s)->meta.load_state != UNIT_STUB)
break; break;
} }
return 0; return 0;
} }
static int service_init(Unit *u, UnitLoadState *new_state) { static void service_init(Unit *u) {
int r;
Service *s = SERVICE(u); Service *s = SERVICE(u);
assert(s); assert(u);
assert(new_state); assert(u->meta.load_state == UNIT_STUB);
assert(*new_state == UNIT_STUB);
/* First, reset everything to the defaults, in case this is a
* reload */
s->type = 0; s->type = 0;
s->restart = 0; s->restart = 0;
@ -711,18 +707,25 @@ static int service_init(Unit *u, UnitLoadState *new_state) {
s->failure = false; s->failure = false;
RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); 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 */ /* Load a .service file */
if ((r = unit_load_fragment(u, new_state)) < 0) if ((r = unit_load_fragment(u)) < 0)
return r; return r;
/* Load a classic init script as a fallback, if we couldn't find anything */ /* Load a classic init script as a fallback, if we couldn't find anything */
if (*new_state == UNIT_STUB) if (u->meta.load_state == UNIT_STUB)
if ((r = service_load_sysv(s, new_state)) < 0) if ((r = service_load_sysv(s)) < 0)
return r; return r;
/* Still nothing found? Then let's give up */ /* Still nothing found? Then let's give up */
if (*new_state == UNIT_STUB) if (u->meta.load_state == UNIT_STUB)
return -ENOENT; return -ENOENT;
/* We were able to load something, then let's add in the /* 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; return r;
/* This is a new unit? Then let's add in some extras */ /* 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) if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
return r; 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); 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]) if (!s->exec_command[c])
continue; continue;
@ -904,7 +907,7 @@ fail:
} }
static int service_notify_sockets(Service *s) { static int service_notify_sockets_dead(Service *s) {
Iterator i; Iterator i;
Set *set; Set *set;
Socket *sock; Socket *sock;
@ -966,19 +969,14 @@ static void service_set_state(Service *s, ServiceState state) {
state != SERVICE_STOP_SIGKILL && state != SERVICE_STOP_SIGKILL &&
state != SERVICE_STOP_POST && state != SERVICE_STOP_POST &&
state != SERVICE_FINAL_SIGTERM && state != SERVICE_FINAL_SIGTERM &&
state != SERVICE_FINAL_SIGKILL) state != SERVICE_FINAL_SIGKILL) {
if (s->control_pid > 0) { if (s->control_pid > 0) {
unit_unwatch_pid(UNIT(s), s->control_pid); unit_unwatch_pid(UNIT(s), s->control_pid);
s->control_pid = 0; 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; s->control_command = NULL;
}
if (state == SERVICE_DEAD || if (state == SERVICE_DEAD ||
state == SERVICE_STOP || state == SERVICE_STOP ||
@ -989,12 +987,10 @@ static void service_set_state(Service *s, ServiceState state) {
state == SERVICE_FINAL_SIGKILL || state == SERVICE_FINAL_SIGKILL ||
state == SERVICE_MAINTAINANCE || state == SERVICE_MAINTAINANCE ||
state == SERVICE_AUTO_RESTART) state == SERVICE_AUTO_RESTART)
service_notify_sockets(s); service_notify_sockets_dead(s);
if (old_state == state) 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));
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]); 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); assert(s);
/* Cannot do this now */
if (s->state == SERVICE_START_PRE || if (s->state == SERVICE_START_PRE ||
s->state == SERVICE_START || s->state == SERVICE_START ||
s->state == SERVICE_START_POST || s->state == SERVICE_START_POST ||
s->state == SERVICE_RELOAD) s->state == SERVICE_RELOAD)
return -EAGAIN; 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) { if (s->state == SERVICE_AUTO_RESTART) {
service_set_state(s, SERVICE_DEAD); service_set_state(s, SERVICE_DEAD);
return 0; return 0;
@ -1966,7 +1972,7 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); 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_PRE] = "ExecStartPre",
[SERVICE_EXEC_START] = "ExecStart", [SERVICE_EXEC_START] = "ExecStart",
[SERVICE_EXEC_START_POST] = "ExecStartPost", [SERVICE_EXEC_START_POST] = "ExecStartPost",
@ -1981,6 +1987,7 @@ const UnitVTable service_vtable = {
.suffix = ".service", .suffix = ".service",
.init = service_init, .init = service_init,
.load = service_load,
.done = service_done, .done = service_done,
.dump = service_dump, .dump = service_dump,

View File

@ -69,8 +69,8 @@ typedef enum ServiceExecCommand {
SERVICE_EXEC_RELOAD, SERVICE_EXEC_RELOAD,
SERVICE_EXEC_STOP, SERVICE_EXEC_STOP,
SERVICE_EXEC_STOP_POST, SERVICE_EXEC_STOP_POST,
_SERVICE_EXEC_MAX, _SERVICE_EXEC_COMMAND_MAX,
_SERVICE_EXEC_INVALID = -1 _SERVICE_EXEC_COMMAND_INVALID = -1
} ServiceExecCommand; } ServiceExecCommand;
struct Service { struct Service {
@ -85,7 +85,7 @@ struct Service {
usec_t restart_usec; usec_t restart_usec;
usec_t timeout_usec; usec_t timeout_usec;
ExecCommand* exec_command[_SERVICE_EXEC_MAX]; ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX];
ExecContext exec_context; ExecContext exec_context;
bool permissions_start_only; bool permissions_start_only;
@ -102,16 +102,16 @@ struct Service {
pid_t main_pid, control_pid; pid_t main_pid, control_pid;
bool main_pid_known:1; bool main_pid_known:1;
bool sysv_has_lsb:1;
bool failure:1; /* if we shut down, remember why */ bool failure:1; /* if we shut down, remember why */
Watch timer_watch;
bool sysv_has_lsb:1;
char *sysv_path; char *sysv_path;
int sysv_start_priority; int sysv_start_priority;
char *sysv_runlevels; char *sysv_runlevels;
RateLimit ratelimit; RateLimit ratelimit;
Watch timer_watch;
}; };
extern const UnitVTable service_vtable; extern const UnitVTable service_vtable;

102
socket.c
View File

@ -79,7 +79,7 @@ static void socket_done(Unit *u) {
} }
exec_context_done(&s->exec_context); 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; s->control_command = NULL;
if (s->control_pid > 0) { if (s->control_pid > 0) {
@ -90,17 +90,16 @@ static void socket_done(Unit *u) {
s->service = NULL; s->service = NULL;
free(s->bind_to_device); free(s->bind_to_device);
s->bind_to_device = NULL;
unit_unwatch_timer(u, &s->timer_watch); 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); Socket *s = SOCKET(u);
char *t;
int r;
/* First, reset everything to the defaults, in case this is a assert(u);
* reload */ assert(u->meta.load_state == UNIT_STUB);
s->state = 0; s->state = 0;
s->timer_watch.type = WATCH_INVALID; s->timer_watch.type = WATCH_INVALID;
@ -112,27 +111,24 @@ static int socket_init(Unit *u, UnitLoadState *new_state) {
s->kill_mode = 0; s->kill_mode = 0;
s->failure = false; s->failure = false;
s->control_pid = 0; s->control_pid = 0;
s->service = NULL;
exec_context_init(&s->exec_context); exec_context_init(&s->exec_context);
}
if ((r = unit_load_fragment(u, new_state)) < 0) static int socket_load(Unit *u) {
return r; Socket *s = SOCKET(u);
int r;
if (*new_state == UNIT_STUB) assert(u);
return -ENOENT; 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; return r;
/* This is a new unit? Then let's add in some extras */ /* 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"))) if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service)))
return -ENOMEM;
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
free(t);
if (r < 0)
return r; return r;
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0) 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 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_PRE] = "StartPre",
[SOCKET_EXEC_START_POST] = "StartPost", [SOCKET_EXEC_START_POST] = "StartPost",
[SOCKET_EXEC_STOP_PRE] = "StopPre", [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); 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]) if (!s->exec_command[c])
continue; continue;
@ -363,27 +359,16 @@ static void socket_set_state(Socket *s, SocketState state) {
state != SOCKET_STOP_PRE_SIGKILL && state != SOCKET_STOP_PRE_SIGKILL &&
state != SOCKET_STOP_POST && state != SOCKET_STOP_POST &&
state != SOCKET_STOP_POST_SIGTERM && state != SOCKET_STOP_POST_SIGTERM &&
state != SOCKET_STOP_POST_SIGKILL) state != SOCKET_STOP_POST_SIGKILL) {
unit_unwatch_timer(UNIT(s), &s->timer_watch); 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) { if (s->control_pid > 0) {
unit_unwatch_pid(UNIT(s), s->control_pid); unit_unwatch_pid(UNIT(s), s->control_pid);
s->control_pid = 0; 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; s->control_command = NULL;
}
if (state != SOCKET_START_POST && if (state != SOCKET_START_POST &&
state != SOCKET_LISTENING && state != SOCKET_LISTENING &&
@ -396,15 +381,13 @@ static void socket_set_state(Socket *s, SocketState state) {
if (state != SOCKET_LISTENING) if (state != SOCKET_LISTENING)
socket_unwatch_fds(s); socket_unwatch_fds(s);
if (state == old_state) if (state != old_state)
return; log_debug("%s changed %s → %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[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]); 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; pid_t pid;
int r; int r;
@ -412,11 +395,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
assert(c); assert(c);
assert(_pid); assert(_pid);
if (timeout) { if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) goto fail;
goto fail;
} else
unit_unwatch_timer(UNIT(s), &s->timer_watch);
if ((r = exec_spawn(c, if ((r = exec_spawn(c,
&s->exec_context, &s->exec_context,
@ -436,8 +416,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
return 0; return 0;
fail: fail:
if (timeout) unit_unwatch_timer(UNIT(s), &s->timer_watch);
unit_unwatch_timer(UNIT(s), &s->timer_watch);
return r; return r;
} }
@ -459,7 +438,7 @@ static void socket_enter_stop_post(Socket *s, bool success) {
s->failure = true; s->failure = true;
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) 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; goto fail;
socket_set_state(s, SOCKET_STOP_POST); socket_set_state(s, SOCKET_STOP_POST);
@ -502,7 +481,6 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) {
r = -errno; r = -errno;
goto fail; goto fail;
} }
} }
socket_set_state(s, state); socket_set_state(s, state);
@ -529,7 +507,7 @@ static void socket_enter_stop_pre(Socket *s, bool success) {
s->failure = true; s->failure = true;
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) 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; goto fail;
socket_set_state(s, SOCKET_STOP_PRE); 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 ((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)); log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r));
goto fail; goto fail;
} }
@ -591,7 +569,7 @@ static void socket_enter_start_pre(Socket *s) {
assert(s); assert(s);
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) 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; goto fail;
socket_set_state(s, SOCKET_START_PRE); 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; 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; goto fail;
return; return;
@ -671,6 +649,13 @@ static int socket_start(Unit *u) {
if (s->service->meta.load_state != UNIT_LOADED) if (s->service->meta.load_state != UNIT_LOADED)
return -ENOENT; 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); assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE);
s->failure = false; s->failure = false;
@ -689,6 +674,15 @@ static int socket_stop(Unit *u) {
s->state == SOCKET_START_POST) s->state == SOCKET_START_POST)
return -EAGAIN; 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); assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
socket_enter_stop_pre(s, true); 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(s);
assert(elapsed == 1); assert(elapsed == 1);
assert(w == &s->timer_watch); assert(w == &s->timer_watch);
switch (s->state) { switch (s->state) {
case SOCKET_START_PRE: case SOCKET_START_PRE:
case SOCKET_START_POST: 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); socket_enter_stop_pre(s, false);
break; break;
@ -874,6 +867,7 @@ const UnitVTable socket_vtable = {
.suffix = ".socket", .suffix = ".socket",
.init = socket_init, .init = socket_init,
.load = socket_load,
.done = socket_done, .done = socket_done,
.dump = socket_dump, .dump = socket_dump,

View File

@ -41,7 +41,8 @@ typedef enum SocketState {
SOCKET_STOP_POST_SIGTERM, SOCKET_STOP_POST_SIGTERM,
SOCKET_STOP_POST_SIGKILL, SOCKET_STOP_POST_SIGKILL,
SOCKET_MAINTAINANCE, SOCKET_MAINTAINANCE,
_SOCKET_STATE_MAX _SOCKET_STATE_MAX,
_SOCKET_STATE_INVALID = -1
} SocketState; } SocketState;
typedef enum SocketExecCommand { typedef enum SocketExecCommand {
@ -49,12 +50,15 @@ typedef enum SocketExecCommand {
SOCKET_EXEC_START_POST, SOCKET_EXEC_START_POST,
SOCKET_EXEC_STOP_PRE, SOCKET_EXEC_STOP_PRE,
SOCKET_EXEC_STOP_POST, SOCKET_EXEC_STOP_POST,
_SOCKET_EXEC_MAX _SOCKET_EXEC_COMMAND_MAX,
_SOCKET_EXEC_COMMAND_INVALID = -1
} SocketExecCommand; } SocketExecCommand;
typedef enum SocketType { typedef enum SocketType {
SOCKET_SOCKET, SOCKET_SOCKET,
SOCKET_FIFO SOCKET_FIFO,
_SOCKET_FIFO_MAX,
_SOCKET_FIFO_INVALID = -1
} SocketType; } SocketType;
typedef struct SocketPort SocketPort; typedef struct SocketPort SocketPort;
@ -82,7 +86,7 @@ struct Socket {
usec_t timeout_usec; usec_t timeout_usec;
ExecCommand* exec_command[_SOCKET_EXEC_MAX]; ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
ExecContext exec_context; ExecContext exec_context;
Service *service; Service *service;

View File

@ -37,6 +37,15 @@ static const char* const state_string_table[_TARGET_STATE_MAX] = {
[TARGET_ACTIVE] = "active" [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) { static void target_dump(Unit *u, FILE *f, const char *prefix) {
Target *t = TARGET(u); Target *t = TARGET(u);
@ -52,13 +61,11 @@ static void target_set_state(Target *t, TargetState state) {
TargetState old_state; TargetState old_state;
assert(t); assert(t);
if (state == t->state)
return;
old_state = t->state; old_state = t->state;
t->state = 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]); 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 = { const UnitVTable target_vtable = {
.suffix = ".target", .suffix = ".target",
.init = unit_load_fragment_and_dropin, .init = target_init,
.load = unit_load_fragment_and_dropin,
.dump = target_dump, .dump = target_dump,

View File

@ -44,7 +44,7 @@ static UnitActiveState timer_active_state(Unit *u) {
const UnitVTable timer_vtable = { const UnitVTable timer_vtable = {
.suffix = ".timer", .suffix = ".timer",
.init = unit_load_fragment_and_dropin, .load = unit_load_fragment_and_dropin,
.done = timer_done, .done = timer_done,
.active_state = timer_active_state .active_state = timer_active_state

94
unit.c
View File

@ -155,6 +155,11 @@ int unit_add_name(Unit *u, const char *text) {
if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type) if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type)
return -EINVAL; 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))) if (!(s = strdup(text)))
return -ENOMEM; return -ENOMEM;
@ -173,10 +178,14 @@ int unit_add_name(Unit *u, const char *text) {
return r; 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); 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) if (!u->meta.id)
u->meta.id = s; u->meta.id = s;
@ -278,9 +287,6 @@ void unit_free(Unit *u) {
bus_unit_send_removed_signal(u); bus_unit_send_removed_signal(u);
/* Detach from next 'bigger' objects */ /* Detach from next 'bigger' objects */
cgroup_bonding_free_list(u->meta.cgroup_bondings);
SET_FOREACH(t, u->meta.names, i) SET_FOREACH(t, u->meta.names, i)
hashmap_remove_value(u->meta.manager->units, t, u); hashmap_remove_value(u->meta.manager->units, t, u);
@ -296,13 +302,15 @@ void unit_free(Unit *u) {
if (u->meta.in_cleanup_queue) if (u->meta.in_cleanup_queue)
LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta); 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 (u->meta.load_state != UNIT_STUB)
if (UNIT_VTABLE(u)->done) if (UNIT_VTABLE(u)->done)
UNIT_VTABLE(u)->done(u); UNIT_VTABLE(u)->done(u);
/* Free data and next 'smaller' objects */ cgroup_bonding_free_list(u->meta.cgroup_bondings);
if (u->meta.job)
job_free(u->meta.job);
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
bidi_set_free(u, u->meta.dependencies[d]); bidi_set_free(u, u->meta.dependencies[d]);
@ -401,9 +409,6 @@ int unit_merge(Unit *u, Unit *other) {
if (other == u) if (other == u)
return 0; return 0;
/* This merges 'other' into 'unit'. FIXME: This does not
* rollback on failure. */
if (u->meta.type != u->meta.type) if (u->meta.type != u->meta.type)
return -EINVAL; return -EINVAL;
@ -569,18 +574,16 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
} }
/* Common implementation for multiple backends */ /* 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; int r;
assert(u); assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
/* Load a .service file */ /* Load a .service file */
if ((r = unit_load_fragment(u, new_state)) < 0) if ((r = unit_load_fragment(u)) < 0)
return r; return r;
if (*new_state == UNIT_STUB) if (u->meta.load_state == UNIT_STUB)
return -ENOENT; return -ENOENT;
/* Load drop-in directory data */ /* 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 */ /* 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; int r;
assert(u); assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
/* Same as unit_load_fragment_and_dropin(), but whether /* Same as unit_load_fragment_and_dropin(), but whether
* something can be loaded or not doesn't matter. */ * something can be loaded or not doesn't matter. */
/* Load a .service file */ /* Load a .service file */
if ((r = unit_load_fragment(u, new_state)) < 0) if ((r = unit_load_fragment(u)) < 0)
return r; return r;
if (*new_state == UNIT_STUB) if (u->meta.load_state == UNIT_STUB)
*new_state = UNIT_LOADED; u->meta.load_state = UNIT_LOADED;
/* Load drop-in directory data */ /* Load drop-in directory data */
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) 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 unit_load(Unit *u) {
int r; int r;
UnitLoadState res;
assert(u); assert(u);
@ -626,21 +626,21 @@ int unit_load(Unit *u) {
u->meta.in_load_queue = false; u->meta.in_load_queue = false;
} }
if (u->meta.type == _UNIT_TYPE_INVALID)
return -EINVAL;
if (u->meta.load_state != UNIT_STUB) if (u->meta.load_state != UNIT_STUB)
return 0; return 0;
if (UNIT_VTABLE(u)->init) { if (UNIT_VTABLE(u)->load)
res = UNIT_STUB; if ((r = UNIT_VTABLE(u)->load(u)) < 0)
if ((r = UNIT_VTABLE(u)->init(u, &res)) < 0)
goto fail; goto fail;
}
if (res == UNIT_STUB) { if (u->meta.load_state == UNIT_STUB) {
r = -ENOENT; r = -ENOENT;
goto fail; goto fail;
} }
u->meta.load_state = res;
assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into); assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into);
unit_add_to_dbus_queue(unit_follow_merge(u)); unit_add_to_dbus_queue(unit_follow_merge(u));
@ -711,9 +711,6 @@ int unit_stop(Unit *u) {
if (!UNIT_VTABLE(u)->stop) if (!UNIT_VTABLE(u)->stop)
return -EBADR; return -EBADR;
if (state == UNIT_DEACTIVATING)
return 0;
unit_add_to_dbus_queue(u); unit_add_to_dbus_queue(u);
return UNIT_VTABLE(u)->stop(u); return UNIT_VTABLE(u)->stop(u);
} }
@ -940,25 +937,38 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
retroactively_stop_dependencies(u); 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)) { if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
log_info("D-Bus became available, trying to reconnect."); /* The bus just might have become available,
/* The bus just got started, hence try to connect to it. */ * hence try to connect to it, if we aren't
* yet connected. */
bus_init_system(u->meta.manager); bus_init_system(u->meta.manager);
bus_init_api(u->meta.manager); bus_init_api(u->meta.manager);
} }
if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) { if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE))
/* The syslog daemon just got started, hence try to connect to it. */ /* The syslog daemon just might have become
log_info("Syslog became available, trying to reconnect."); * available, hence try to connect to it, if
* we aren't yet connected. */
log_open_syslog(); 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)) 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(); log_close_syslog();
/* We don't care about D-Bus here, since we'll get an /* We don't care about D-Bus here, since we'll get an

29
unit.h
View File

@ -196,8 +196,31 @@ union Unit {
struct UnitVTable { struct UnitVTable {
const char *suffix; 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); 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); int (*coldplug)(Unit *u);
void (*dump)(Unit *u, FILE *f, const char *prefix); 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); Unit *unit_follow_merge(Unit *u);
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state); int unit_load_fragment_and_dropin(Unit *u);
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state); int unit_load_fragment_and_dropin_optional(Unit *u);
int unit_load(Unit *unit); int unit_load(Unit *unit);
const char* unit_id(Unit *u); const char* unit_id(Unit *u);

210
utmp-wtmp.c Normal file
View 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
View 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