Commit e537352b authored by Lennart Poettering's avatar Lennart Poettering
Browse files

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.
parent 108736d0
......@@ -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) \
......
......@@ -26,34 +26,44 @@
#include "load-fragment.h"
#include "load-dropin.h"
static int automount_init(Unit *u, UnitLoadState *new_state) {
int r;
Automount *a = AUTOMOUNT(u);
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,
};
assert(a);
static const char* const state_string_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
};
exec_context_init(&a->exec_context);
static void automount_init(Unit *u) {
Automount *a = AUTOMOUNT(u);
/* Load a .automount file */
if ((r = unit_load_fragment(u, new_state)) < 0)
return r;
a->state = 0;
a->mount = NULL;
}
if (*new_state == UNIT_STUB)
*new_state = UNIT_LOADED;
static int automount_load(Unit *u) {
int r;
Automount *a = AUTOMOUNT(u);
/* Load drop-in directory data */
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
assert(u);
assert(u->meta.load_state == UNIT_STUB);
if (*new_state == UNIT_LOADED) {
/* Load a .automount file */
if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
return r;
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0)
return r;
if (u->meta.load_state == UNIT_LOADED) {
if ((r = unit_add_exec_dependencies(u, &a->exec_context)) < 0)
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
return r;
if ((r = unit_add_default_cgroup(u)) < 0)
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0)
return r;
}
......@@ -61,73 +71,35 @@ static int automount_init(Unit *u, UnitLoadState *new_state) {
}
static void automount_done(Unit *u) {
Automount *d = AUTOMOUNT(u);
Automount *a = AUTOMOUNT(u);
assert(a);
assert(d);
free(d->path);
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,
......
......@@ -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;
};
......
......@@ -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,
......
......@@ -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
......@@ -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));
......
......@@ -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;
}