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

reload: implement reload/reexec logic

parent 0d906814
......@@ -32,7 +32,11 @@ AM_CPPFLAGS = \
-DSYSTEM_SYSVRCND_PATH=\"$(SYSTEM_SYSVRCND_PATH)\" \
-DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
-DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\"
-DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\" \
-DSYSTEMD_BINARY_PATH=\"$(sbindir)/systemd\"
# -DSYSTEMD_BINARY_PATH=\"/home/lennart/projects/systemd/systemd\"
sbin_PROGRAMS = \
systemd
......@@ -155,7 +159,9 @@ COMMON_SOURCES= \
specifier.c \
specifier.h \
unit-name.c \
unit-name.h
unit-name.h \
fdset.c \
fdset.h
systemd_SOURCES = \
$(COMMON_SOURCES) \
......
......@@ -43,21 +43,7 @@ static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[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 char *automount_name_from_where(const char *where) {
assert(where);
if (streq(where, "/"))
return strdup("-.automount");
return unit_name_build_escape(where+1, NULL, ".automount");
}
static int open_dev_autofs(Manager *m);
static void automount_init(Unit *u) {
Automount *a = AUTOMOUNT(u);
......@@ -66,6 +52,7 @@ static void automount_init(Unit *u) {
assert(u->meta.load_state == UNIT_STUB);
a->pipe_watch.fd = a->pipe_fd = -1;
a->pipe_watch.type = WATCH_INVALID;
}
static void repeat_unmout(const char *path) {
......@@ -95,7 +82,12 @@ static void unmount_autofs(Automount *a) {
close_nointr_nofail(a->pipe_fd);
a->pipe_fd = -1;
repeat_unmout(a->where);
/* If we reload/reexecute things we keep the mount point
* around */
if (a->where &&
(UNIT(a)->meta.manager->exit_code != MANAGER_RELOAD &&
UNIT(a)->meta.manager->exit_code != MANAGER_REEXECUTE))
repeat_unmout(a->where);
}
static void automount_done(Unit *u) {
......@@ -106,10 +98,11 @@ static void automount_done(Unit *u) {
unmount_autofs(a);
a->mount = NULL;
if (a->tokens) {
set_free(a->tokens);
a->tokens = NULL;
}
free(a->where);
a->where = NULL;
set_free(a->tokens);
a->tokens = NULL;
}
static int automount_verify(Automount *a) {
......@@ -120,14 +113,7 @@ static int automount_verify(Automount *a) {
if (UNIT(a)->meta.load_state != UNIT_LOADED)
return 0;
if (!a->where) {
log_error("%s lacks Where setting. Refusing.", UNIT(a)->meta.id);
return -EINVAL;
}
path_kill_slashes(a->where);
if (!(e = automount_name_from_where(a->where)))
if (!(e = unit_name_from_path(a->where, ".automount")))
return -ENOMEM;
b = unit_has_name(UNIT(a), e);
......@@ -154,6 +140,12 @@ static int automount_load(Unit *u) {
if (u->meta.load_state == UNIT_LOADED) {
if (!a->where)
if (!(a->where = unit_name_to_path(u->meta.id)))
return -ENOMEM;
path_kill_slashes(a->where);
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
return r;
......@@ -176,19 +168,51 @@ static void automount_set_state(Automount *a, AutomountState state) {
unmount_autofs(a);
if (state != old_state)
log_debug("%s changed %s → %s", UNIT(a)->meta.id, state_string_table[old_state], state_string_table[state]);
log_debug("%s changed %s → %s",
UNIT(a)->meta.id,
automount_state_to_string(old_state),
automount_state_to_string(state));
unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state]);
}
static int automount_coldplug(Unit *u) {
Automount *a = AUTOMOUNT(u);
int r;
assert(a);
assert(a->state == AUTOMOUNT_DEAD);
if (a->deserialized_state != a->state) {
if ((r = open_dev_autofs(u->meta.manager)) < 0)
return r;
if (a->deserialized_state == AUTOMOUNT_WAITING ||
a->deserialized_state == AUTOMOUNT_RUNNING) {
assert(a->pipe_fd >= 0);
if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0)
return r;
}
automount_set_state(a, a->deserialized_state);
}
return 0;
}
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
Automount *s = AUTOMOUNT(u);
Automount *a = AUTOMOUNT(u);
assert(s);
assert(a);
fprintf(f,
"%sAutomount State: %s\n",
prefix, state_string_table[s->state]);
"%sAutomount State: %s\n"
"%sWhere: %s\n",
prefix, automount_state_to_string(a->state),
prefix, a->where);
}
static void automount_enter_dead(Automount *a, bool success) {
......@@ -208,7 +232,7 @@ static int open_dev_autofs(Manager *m) {
if (m->dev_autofs_fd >= 0)
return m->dev_autofs_fd;
if ((m->dev_autofs_fd = open("/dev/autofs", O_RDONLY)) < 0) {
if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) {
log_error("Failed to open /dev/autofs: %s", strerror(errno));
return -errno;
}
......@@ -254,6 +278,7 @@ static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
goto finish;
}
fd_cloexec(param->ioctlfd, true);
r = param->ioctlfd;
finish:
......@@ -383,10 +408,6 @@ static void automount_enter_waiting(Automount *a) {
if (a->tokens)
set_clear(a->tokens);
else if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
r = -ENOMEM;
goto fail;
}
if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->meta.manager)) < 0) {
r = dev_autofs_fd;
......@@ -396,7 +417,7 @@ static void automount_enter_waiting(Automount *a) {
/* We knowingly ignore the results of this call */
mkdir_p(a->where, 0555);
if (pipe2(p, O_NONBLOCK) < 0) {
if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
r = -errno;
goto fail;
}
......@@ -521,7 +542,94 @@ static int automount_stop(Unit *u) {
return 0;
}
static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
Automount *a = AUTOMOUNT(u);
void *p;
Iterator i;
assert(a);
assert(f);
assert(fds);
unit_serialize_item(u, f, "state", automount_state_to_string(a->state));
unit_serialize_item(u, f, "failure", yes_no(a->failure));
unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id);
SET_FOREACH(p, a->tokens, i)
unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
if (a->pipe_fd >= 0) {
int copy;
if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0)
return copy;
unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
}
return 0;
}
static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Automount *a = AUTOMOUNT(u);
int r;
assert(a);
assert(fds);
if (streq(key, "state")) {
AutomountState state;
if ((state = automount_state_from_string(value)) < 0)
log_debug("Failed to parse state value %s", value);
else
a->deserialized_state = state;
} else if (streq(key, "failure")) {
int b;
if ((b = parse_boolean(value)) < 0)
log_debug("Failed to parse failure value %s", value);
else
a->failure = b || a->failure;
} else if (streq(key, "dev-id")) {
unsigned d;
if (safe_atou(value, &d) < 0)
log_debug("Failed to parse dev-id value %s", value);
else
a->dev_id = (unsigned) d;
} else if (streq(key, "token")) {
unsigned token;
if (safe_atou(value, &token) < 0)
log_debug("Failed to parse token value %s", value);
else {
if (!a->tokens)
if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func)))
return -ENOMEM;
if ((r = set_put(a->tokens, UINT_TO_PTR(token))) < 0)
return r;
}
} else if (streq(key, "pipe-fd")) {
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_debug("Failed to parse pipe-fd value %s", value);
else {
if (a->pipe_fd >= 0)
close_nointr_nofail(a->pipe_fd);
a->pipe_fd = fdset_remove(fds, fd);
}
} else
log_debug("Unknown serialization key '%s'", key);
return 0;
}
static UnitActiveState automount_active_state(Unit *u) {
assert(u);
return state_translation_table[AUTOMOUNT(u)->state];
}
......@@ -529,7 +637,7 @@ static UnitActiveState automount_active_state(Unit *u) {
static const char *automount_sub_state_to_string(Unit *u) {
assert(u);
return state_string_table[AUTOMOUNT(u)->state];
return automount_state_to_string(AUTOMOUNT(u)->state);
}
static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
......@@ -557,6 +665,12 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
case autofs_ptype_missing_direct:
log_debug("Got direct mount request for %s", packet.v5_packet.name);
if (!a->tokens)
if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
log_error("Failed to allocate token set.");
goto fail;
}
if ((r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token))) < 0) {
log_error("Failed to remember token: %s", strerror(-r));
goto fail;
......@@ -583,6 +697,15 @@ static void automount_shutdown(Manager *m) {
close_nointr_nofail(m->dev_autofs_fd);
}
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
};
DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
const UnitVTable automount_vtable = {
.suffix = ".automount",
......@@ -593,11 +716,16 @@ const UnitVTable automount_vtable = {
.load = automount_load,
.done = automount_done,
.coldplug = automount_coldplug,
.dump = automount_dump,
.start = automount_start,
.stop = automount_stop,
.serialize = automount_serialize,
.deserialize_item = automount_deserialize_item,
.active_state = automount_active_state,
.sub_state_to_string = automount_sub_state_to_string,
......
......@@ -38,7 +38,7 @@ typedef enum AutomountState {
struct Automount {
Meta meta;
AutomountState state;
AutomountState state, deserialized_state;
char *where;
......@@ -57,4 +57,7 @@ extern const UnitVTable automount_vtable;
int automount_send_ready(Automount *a, int status);
const char* automount_state_to_string(AutomountState i);
AutomountState automount_state_from_string(const char *s);
#endif
......@@ -478,7 +478,7 @@ int manager_setup_cgroup(Manager *m) {
return r;
}
int manager_shutdown_cgroup(Manager *m) {
int manager_shutdown_cgroup(Manager *m, bool delete) {
struct cgroup *cg;
int r;
......@@ -495,11 +495,10 @@ int manager_shutdown_cgroup(Manager *m) {
goto finish;
}
if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) {
log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r));
r = translate_error(r, errno);
goto finish;
}
/* Often enough we won't be able to delete the cgroup we
* ourselves are in, hence ignore all errors here */
if (delete)
cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE);
r = 0;
finish:
......
......@@ -75,7 +75,7 @@ char *cgroup_bonding_to_string(CGroupBonding *b);
#include "manager.h"
int manager_setup_cgroup(Manager *m);
int manager_shutdown_cgroup(Manager *m);
int manager_shutdown_cgroup(Manager *m, bool delete);
int cgroup_notify_empty(Manager *m, const char *group);
......
......@@ -56,6 +56,9 @@
" <arg nane=\"cleanup\" type=\"b\" direction=\"in\"/>" \
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>" \
" </method>" \
" <method name=\"Reload\"/>" \
" <method name=\"Reexecute\"/>" \
" <method name=\"Exit\"/>" \
" <signal name=\"UnitNew\">" \
" <arg name=\"id\" type=\"s\"/>" \
" <arg name=\"unit\" type=\"o\"/>" \
......@@ -504,6 +507,37 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection
free(introspection);
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
assert(!m->queued_message);
/* Instead of sending the reply back right away, we
* just remember that we need to and then send it
* after the reload is finished. That way the caller
* knows when the reload finished. */
if (!(m->queued_message = dbus_message_new_method_return(message)))
goto oom;
m->exit_code = MANAGER_RELOAD;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
m->exit_code = MANAGER_REEXECUTE;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
if (m->running_as == MANAGER_INIT)
return bus_send_error_reply(m, message, NULL, -ENOTSUP);
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
m->exit_code = MANAGER_EXIT;
} else
return bus_default_message_handler(m, message, NULL, properties);
......
......@@ -381,6 +381,18 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection,
unsigned bus_dispatch(Manager *m) {
assert(m);
if (m->queued_message) {
/* If we cannot get rid of this message we won't
* dispatch any D-Bus messages, so that we won't end
* up wanting to queue another message. */
if (!dbus_connection_send(m->api_bus, m->queued_message, NULL))
return 0;
dbus_message_unref(m->queued_message);
m->queued_message = NULL;
}
if (m->request_api_bus_dispatch) {
if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE)
m->request_api_bus_dispatch = false;
......@@ -655,6 +667,11 @@ void bus_done_api(Manager *m) {
if (m->name_data_slot >= 0)
dbus_pending_call_free_data_slot(&m->name_data_slot);
if (m->queued_message) {
dbus_message_unref(m->queued_message);
m->queued_message = NULL;
}
}
void bus_done_system(Manager *m) {
......
......@@ -35,11 +35,6 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
[DEVICE_AVAILABLE] = UNIT_ACTIVE
};
static const char* const state_string_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = "dead",
[DEVICE_AVAILABLE] = "available"
};
static void device_done(Unit *u) {
Device *d = DEVICE(u);
......@@ -49,15 +44,6 @@ static void device_done(Unit *u) {
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);
......@@ -66,7 +52,10 @@ static void device_set_state(Device *d, DeviceState state) {
d->state = state;
if (state != old_state)
log_debug("%s changed %s → %s", UNIT(d)->meta.id, state_string_table[old_state], state_string_table[state]);
log_debug("%s changed %s → %s",
UNIT(d)->meta.id,
device_state_to_string(old_state),
device_state_to_string(state));
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
}
......@@ -91,7 +80,7 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f,
"%sDevice State: %s\n"
"%sSysfs Path: %s\n",
prefix, state_string_table[d->state],
prefix, device_state_to_string(d->state),
prefix, strna(d->sysfs));
}
......@@ -104,7 +93,7 @@ static UnitActiveState device_active_state(Unit *u) {
static const char *device_sub_state_to_string(Unit *u) {
assert(u);
return state_string_table[DEVICE(u)->state];
return device_state_to_string(DEVICE(u)->state);
}
static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
......@@ -115,7 +104,7 @@ static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
assert(dn);
assert(dn[0] == '/');
if (!(e = unit_name_build_escape(dn+1, NULL, ".device")))
if (!(e = unit_name_from_path(dn, ".device")))
return -ENOMEM;
r = unit_add_name(u, e);
......@@ -140,7 +129,7 @@ static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
assert(dn[0] == '/');
assert(_u);
if (!(e = unit_name_build_escape(dn+1, NULL, ".device")))
if (!(e = unit_name_from_path(dn, ".device")))
return -ENOMEM;
u = manager_get_unit(m, e);
......@@ -308,7 +297,7 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) {
return -ENOMEM;
assert(sysfs[0] == '/');
if (!(e = unit_name_build_escape(sysfs+1, NULL, ".device")))
if (!(e = unit_name_from_path(sysfs, ".device")))
return -ENOMEM;
u = manager_get_unit(m, e);
......@@ -328,11 +317,15 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) {
static void device_shutdown(Manager *m) {
assert(m);
if (m->udev_monitor)
if (m->udev_monitor) {
udev_monitor_unref(m->udev_monitor);
m->udev_monitor = NULL;
}
if (m->udev)
if (m->udev) {
udev_unref(m->udev);
m->udev = NULL;
}
}
static int device_enumerate(Manager *m) {
......@@ -343,28 +336,30 @@ static int device_enumerate(Manager *m) {
assert(m);
if (!(m->udev = udev_new()))
return -ENOMEM;
if (!m->udev) {
if (!(m->udev = udev_new()))
return -ENOMEM;
if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
r = -ENOMEM;
goto fail;
}
if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
r = -ENOMEM;
goto fail;
}
if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
r = -EIO;
goto fail;
}
if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
r = -EIO;
goto fail;
}
m->udev_watch.type = WATCH_UDEV;
m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
m->udev_watch.type = WATCH_UDEV;
m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
zero(ev);
ev.events = EPOLLIN;
ev.data.ptr = &m->udev_watch;
zero(ev);
ev.events = EPOLLIN;
ev.data.ptr = &m->udev_watch;