From: Lennart Poettering Date: Wed, 21 Apr 2010 01:27:44 +0000 (+0200) Subject: reload: implement reload/reexec logic X-Git-Tag: 0.git+20100605+dfd8ee-1~98 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a16e112358ea8fea381ee106b89e645aed8b0a8c;p=systemd reload: implement reload/reexec logic --- diff --git a/Makefile.am b/Makefile.am index 8720e789..567490ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) \ diff --git a/automount.c b/automount.c index fa5fd525..7aa55b4f 100644 --- a/automount.c +++ b/automount.c @@ -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, diff --git a/automount.h b/automount.h index a8c81e17..5b623691 100644 --- a/automount.h +++ b/automount.h @@ -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 diff --git a/cgroup.c b/cgroup.c index 4ceaf382..83c5d402 100644 --- a/cgroup.c +++ b/cgroup.c @@ -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: diff --git a/cgroup.h b/cgroup.h index 6b677d53..e290ffee 100644 --- a/cgroup.h +++ b/cgroup.h @@ -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); diff --git a/dbus-manager.c b/dbus-manager.c index 734ed7b8..a8bf7e52 100644 --- a/dbus-manager.c +++ b/dbus-manager.c @@ -56,6 +56,9 @@ " " \ " " \ " " \ + " " \ + " " \ + " " \ " " \ " " \ " " \ @@ -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); diff --git a/dbus.c b/dbus.c index 6b7896b6..0054d151 100644 --- a/dbus.c +++ b/dbus.c @@ -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) { diff --git a/device.c b/device.c index ea33101b..4812a868 100644 --- a/device.c +++ b/device.c @@ -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; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0) - return -errno; + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0) + return -errno; + } if (!(e = udev_enumerate_new(m->udev))) { r = -ENOMEM; @@ -425,6 +420,13 @@ fail: udev_device_unref(dev); } +static const char* const device_state_table[_DEVICE_STATE_MAX] = { + [DEVICE_DEAD] = "dead", + [DEVICE_AVAILABLE] = "available" +}; + +DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState); + const UnitVTable device_vtable = { .suffix = ".device", @@ -432,7 +434,6 @@ const UnitVTable device_vtable = { .no_instances = true, .no_snapshots = true, - .init = device_init, .load = unit_load_fragment_and_dropin_optional, .done = device_done, .coldplug = device_coldplug, diff --git a/device.h b/device.h index b9ca22d1..a5c5f745 100644 --- a/device.h +++ b/device.h @@ -47,4 +47,7 @@ extern const UnitVTable device_vtable; void device_fd_event(Manager *m, int events); +const char* device_state_to_string(DeviceState i); +DeviceState device_state_from_string(const char *s); + #endif diff --git a/execute.c b/execute.c index 357fd5c2..38547677 100644 --- a/execute.c +++ b/execute.c @@ -1063,30 +1063,9 @@ void exec_context_init(ExecContext *c) { assert(c); c->umask = 0002; - c->oom_adjust = 0; - c->oom_adjust_set = false; - c->nice = 0; - c->nice_set = false; c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); - c->ioprio_set = false; c->cpu_sched_policy = SCHED_OTHER; - c->cpu_sched_priority = 0; - c->cpu_sched_set = false; - CPU_ZERO(&c->cpu_affinity); - c->cpu_affinity_set = false; - c->timer_slack_ns = 0; - c->timer_slack_ns_set = false; - - c->cpu_sched_reset_on_fork = false; - c->non_blocking = false; - - c->std_input = 0; - c->std_output = 0; - c->std_error = 0; c->syslog_priority = LOG_DAEMON|LOG_INFO; - - c->secure_bits = 0; - c->capability_bounding_set_drop = 0; } void exec_context_done(ExecContext *c) { diff --git a/fdset.c b/fdset.c new file mode 100644 index 00000000..b6d5286f --- /dev/null +++ b/fdset.c @@ -0,0 +1,162 @@ +/*-*- 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 . +***/ + +#include +#include +#include +#include + +#include "set.h" +#include "util.h" +#include "macro.h" +#include "fdset.h" + +#define MAKE_SET(s) ((Set*) s) +#define MAKE_FDSET(s) ((FDSet*) s) + +/* Make sure we can distuingish fd 0 and NULL */ +#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) +#define PTR_TO_FD(p) (PTR_TO_INT(p)-1) + +FDSet *fdset_new(void) { + return MAKE_FDSET(set_new(trivial_hash_func, trivial_compare_func)); +} + +void fdset_free(FDSet *s) { + void *p; + + while ((p = set_steal_first(MAKE_SET(s)))) { + /* Valgrind's fd might have ended up in this set here, + * due to fdset_new_fill(). We'll ignore all failures + * here, so that the EBADFD that valgrind will return + * us on close() doesn't influence us */ + + log_warning("Closing left-over fd %i", PTR_TO_FD(p)); + close_nointr(PTR_TO_FD(p)); + } + + set_free(MAKE_SET(s)); +} + +int fdset_put(FDSet *s, int fd) { + assert(s); + assert(fd >= 0); + + return set_put(MAKE_SET(s), FD_TO_PTR(fd)); +} + +int fdset_put_dup(FDSet *s, int fd) { + int copy, r; + + assert(s); + assert(fd >= 0); + + if ((copy = fcntl(fd, F_DUPFD_CLOEXEC, 3)) < 0) + return -errno; + + if ((r = fdset_put(s, copy)) < 0) { + close_nointr_nofail(copy); + return r; + } + + return copy; +} + +bool fdset_contains(FDSet *s, int fd) { + assert(s); + assert(fd >= 0); + + return !!set_get(MAKE_SET(s), FD_TO_PTR(fd)); +} + +int fdset_remove(FDSet *s, int fd) { + assert(s); + assert(fd >= 0); + + return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT; +} + +int fdset_new_fill(FDSet **_s) { + DIR *d; + struct dirent *de; + int r = 0; + FDSet *s; + + assert(_s); + + /* Creates an fdsets and fills in all currently open file + * descriptors. */ + + if (!(d = opendir("/proc/self/fd"))) + return -errno; + + if (!(s = fdset_new())) { + r = -ENOMEM; + goto finish; + } + + while ((de = readdir(d))) { + int fd = -1; + + if (ignore_file(de->d_name)) + continue; + + if ((r = safe_atoi(de->d_name, &fd)) < 0) + goto finish; + + if (fd < 3) + continue; + + if (fd == dirfd(d)) + continue; + + if ((r = fdset_put(s, fd)) < 0) + goto finish; + } + + r = 0; + *_s = s; + s = NULL; + +finish: + closedir(d); + + /* We won't close the fds here! */ + if (s) + set_free(MAKE_SET(s)); + + return r; + +} + +int fdset_cloexec(FDSet *fds, bool b) { + Iterator i; + void *p; + int r; + + assert(fds); + + SET_FOREACH(p, MAKE_SET(fds), i) + if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0) + return r; + + return 0; +} diff --git a/fdset.h b/fdset.h new file mode 100644 index 00000000..3483fc83 --- /dev/null +++ b/fdset.h @@ -0,0 +1,40 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foofdsethfoo +#define foofdsethfoo + +/*** + 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 . +***/ + +typedef struct FDSet FDSet; + +FDSet* fdset_new(void); +void fdset_free(FDSet *s); + +int fdset_put(FDSet *s, int fd); +int fdset_put_dup(FDSet *s, int fd); + +bool fdset_contains(FDSet *s, int fd); +int fdset_remove(FDSet *s, int fd); + +int fdset_new_fill(FDSet **_s); + +int fdset_cloexec(FDSet *fds, bool b); + +#endif diff --git a/initctl.c b/initctl.c index a4356759..96a4d990 100644 --- a/initctl.c +++ b/initctl.c @@ -246,7 +246,7 @@ static void fifo_free(Fifo *f) { if (f->server) epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL); - assert_se(close_nointr(f->fd) == 0); + close_nointr_nofail(f->fd); } free(f); @@ -302,7 +302,7 @@ static void server_done(Server *s) { fifo_free(s->fifos); if (s->epoll_fd >= 0) - assert_se(close_nointr(s->epoll_fd) == 0); + close_nointr_nofail(s->epoll_fd); if (s->bus) dbus_connection_unref(s->bus); diff --git a/log.c b/log.c index b82018f2..d8cc4048 100644 --- a/log.c +++ b/log.c @@ -43,7 +43,7 @@ static int kmsg_fd = -1; void log_close_kmsg(void) { if (kmsg_fd >= 0) { - close_nointr(kmsg_fd); + close_nointr_nofail(kmsg_fd); kmsg_fd = -1; } } @@ -71,7 +71,7 @@ int log_open_kmsg(void) { void log_close_syslog(void) { if (syslog_fd >= 0) { - close_nointr(syslog_fd); + close_nointr_nofail(syslog_fd); syslog_fd = -1; } } diff --git a/logger.c b/logger.c index 7194320c..ba325c69 100644 --- a/logger.c +++ b/logger.c @@ -283,7 +283,7 @@ static void stream_free(Stream *s) { if (s->server) epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL); - assert_se(close_nointr(s->fd) == 0); + close_nointr_nofail(s->fd); } free(s->process); @@ -305,12 +305,12 @@ static int stream_new(Server *s, int server_fd) { if (s->n_streams >= STREAMS_MAX) { log_warning("Too many connections, refusing connection."); - assert_se(close_nointr(fd) == 0); + close_nointr_nofail(fd); return 0; } if (!(stream = new0(Stream, 1))) { - assert_se(close_nointr(fd) == 0); + close_nointr_nofail(fd); return -ENOMEM; } @@ -399,16 +399,16 @@ static void server_done(Server *s) { stream_free(s->streams); for (i = 0; i < s->n_server_fd; i++) - assert_se(close_nointr(SERVER_FD_START+i) == 0); + close_nointr_nofail(SERVER_FD_START+i); if (s->syslog_fd >= 0) - assert_se(close_nointr(s->syslog_fd) == 0); + close_nointr_nofail(s->syslog_fd); if (s->epoll_fd >= 0) - assert_se(close_nointr(s->epoll_fd) == 0); + close_nointr_nofail(s->epoll_fd); if (s->kmsg_fd >= 0) - assert_se(close_nointr(s->kmsg_fd) == 0); + close_nointr_nofail(s->kmsg_fd); } static int server_init(Server *s, unsigned n_sockets) { diff --git a/main.c b/main.c index c473ced2..29be8011 100644 --- a/main.c +++ b/main.c @@ -37,6 +37,7 @@ #include "mount-setup.h" #include "hostname-setup.h" #include "load-fragment.h" +#include "fdset.h" static enum { ACTION_RUN, @@ -51,8 +52,8 @@ static ManagerRunningAs running_as = _MANAGER_RUNNING_AS_INVALID; static bool dump_core = true; static bool crash_shell = false; static int crash_chvt = -1; - static bool confirm_spawn = false; +static FILE* serialization = NULL; _noreturn static void freeze(void) { for (;;) @@ -202,10 +203,10 @@ static int console_setup(void) { finish: if (tty_fd >= 0) - close_nointr(tty_fd); + close_nointr_nofail(tty_fd); if (null_fd >= 0) - close_nointr(null_fd); + close_nointr_nofail(null_fd); return r; } @@ -351,19 +352,21 @@ static int parse_argv(int argc, char *argv[]) { ARG_RUNNING_AS, ARG_TEST, ARG_DUMP_CONFIGURATION_ITEMS, - ARG_CONFIRM_SPAWN + ARG_CONFIRM_SPAWN, + ARG_DESERIALIZE }; static const struct option options[] = { - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, - { "default", required_argument, NULL, ARG_DEFAULT }, - { "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 }, - { "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN }, - { NULL, 0, NULL, 0 } + { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, + { "log-target", required_argument, NULL, ARG_LOG_TARGET }, + { "default", required_argument, NULL, ARG_DEFAULT }, + { "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 }, + { "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN }, + { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, + { NULL, 0, NULL, 0 } }; int c, r; @@ -425,6 +428,28 @@ static int parse_argv(int argc, char *argv[]) { confirm_spawn = true; break; + case ARG_DESERIALIZE: { + int fd; + FILE *f; + + if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) { + log_error("Failed to parse deserialize option %s.", optarg); + return r; + } + + if (!(f = fdopen(fd, "r"))) { + log_error("Failed to open serialization fd: %m"); + return r; + } + + if (serialization) + fclose(serialization); + + serialization = f; + + break; + } + case 'h': action = ACTION_HELP; break; @@ -456,11 +481,67 @@ static int help(void) { return 0; } +static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) { + FILE *f = NULL; + FDSet *fds = NULL; + int r; + + assert(m); + assert(_f); + assert(_fds); + + if ((r = manager_open_serialization(&f)) < 0) { + log_error("Failed to create serialization faile: %s", strerror(-r)); + goto fail; + } + + if (!(fds = fdset_new())) { + r = -ENOMEM; + log_error("Failed to allocate fd set: %s", strerror(-r)); + goto fail; + } + + if ((r = manager_serialize(m, f, fds)) < 0) { + log_error("Failed to serialize state: %s", strerror(-r)); + goto fail; + } + + if (fseeko(f, 0, SEEK_SET) < 0) { + log_error("Failed to rewind serialization fd: %m"); + goto fail; + } + + if ((r = fd_cloexec(fileno(f), false)) < 0) { + log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r)); + goto fail; + } + + if ((r = fdset_cloexec(fds, false)) < 0) { + log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r)); + goto fail; + } + + *_f = f; + *_fds = fds; + + return 0; + +fail: + fdset_free(fds); + + if (f) + fclose(f); + + return r; +} + int main(int argc, char *argv[]) { Manager *m = NULL; Unit *target = NULL; Job *job = NULL; int r, retval = 1; + FDSet *fds = NULL; + bool reexecute = false; if (getpid() == 1) running_as = MANAGER_INIT; @@ -484,9 +565,6 @@ int main(int argc, char *argv[]) { ignore_signal(SIGKILL); ignore_signal(SIGPIPE); - /* Close all open files */ - assert_se(close_all_fds(NULL, 0) == 0); - if (running_as != MANAGER_SESSION) if (parse_proc_cmdline() < 0) goto finish; @@ -507,6 +585,17 @@ int main(int argc, char *argv[]) { assert_se(action == ACTION_RUN || action == ACTION_TEST); + /* Remember open file descriptors for later deserialization */ + if (serialization) { + if ((r = fdset_new_fill(&fds)) < 0) { + log_error("Failed to allocate fd set: %s", strerror(-r)); + goto finish; + } + + assert_se(fdset_remove(fds, fileno(serialization)) >= 0); + } else + close_all_fds(NULL, 0); + /* Set up PATH unless it is already set */ setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", @@ -548,52 +637,82 @@ int main(int argc, char *argv[]) { goto finish; } - if ((r = manager_coldplug(m)) < 0) { - log_error("Failed to retrieve coldplug information: %s", strerror(-r)); - goto finish; + if ((r = manager_startup(m, serialization, fds)) < 0) + log_error("Failed to fully startup daemon: %s", strerror(-r)); + + if (fds) { + /* This will close all file descriptors that were opened, but + * not claimed by any unit. */ + + fdset_free(fds); + fds = NULL; } - log_debug("Activating default unit: %s", default_unit); + if (serialization) { + fclose(serialization); + serialization = NULL; + } else { + log_debug("Activating default unit: %s", default_unit); + + if ((r = manager_load_unit(m, default_unit, NULL, &target)) < 0) { + log_error("Failed to load default target: %s", strerror(-r)); - if ((r = manager_load_unit(m, default_unit, NULL, &target)) < 0) { - log_error("Failed to load default target: %s", strerror(-r)); + log_info("Trying to load rescue target..."); + if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &target)) < 0) { + log_error("Failed to load rescue target: %s", strerror(-r)); + goto finish; + } + } - log_info("Trying to load rescue target..."); - if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &target)) < 0) { - log_error("Failed to load rescue target: %s", strerror(-r)); + if (action == ACTION_TEST) { + printf("→ By units:\n"); + manager_dump_units(m, stdout, "\t"); + } + + if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) { + log_error("Failed to start default target: %s", strerror(-r)); goto finish; } - } - if (action == ACTION_TEST) { - printf("→ By units:\n"); - manager_dump_units(m, stdout, "\t"); + if (action == ACTION_TEST) { + printf("→ By jobs:\n"); + manager_dump_jobs(m, stdout, "\t"); + retval = 0; + goto finish; + } } - if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) { - log_error("Failed to start default target: %s", strerror(-r)); - goto finish; - } + for (;;) { + if ((r = manager_loop(m)) < 0) { + log_error("Failed to run mainloop: %s", strerror(-r)); + goto finish; + } - if (action == ACTION_TEST) { - printf("→ By jobs:\n"); - manager_dump_jobs(m, stdout, "\t"); + switch (m->exit_code) { - if (getpid() == 1) - pause(); + case MANAGER_EXIT: + retval = 0; + log_debug("Exit."); + goto finish; - retval = 0; - goto finish; - } + case MANAGER_RELOAD: + if ((r = manager_reload(m)) < 0) + log_error("Failed to reload: %s", strerror(-r)); + break; - if ((r = manager_loop(m)) < 0) { - log_error("Failed to run mainloop: %s", strerror(-r)); - goto finish; - } + case MANAGER_REEXECUTE: - retval = 0; + if (prepare_reexecute(m, &serialization, &fds) < 0) + goto finish; - log_debug("Exit."); + reexecute = true; + log_debug("Reexecuting."); + goto finish; + + default: + assert_not_reached("Unknown exit code."); + } + } finish: if (m) @@ -603,6 +722,49 @@ finish: dbus_shutdown(); + if (reexecute) { + const char *args[11]; + unsigned i = 0; + char sfd[16]; + + assert(serialization); + assert(fds); + + args[i++] = SYSTEMD_BINARY_PATH; + + args[i++] = "--log-level"; + args[i++] = log_level_to_string(log_get_max_level()); + + args[i++] = "--log-target"; + args[i++] = log_target_to_string(log_get_target()); + + args[i++] = "--running-as"; + args[i++] = manager_running_as_to_string(running_as); + + snprintf(sfd, sizeof(sfd), "%i", fileno(serialization)); + char_array_0(sfd); + + args[i++] = "--deserialize"; + args[i++] = sfd; + + if (confirm_spawn) + args[i++] = "--confirm-spawn"; + + args[i++] = NULL; + + assert(i <= ELEMENTSOF(args)); + + execv(args[0], (char* const*) args); + + log_error("Failed to reexecute: %m"); + } + + if (serialization) + fclose(serialization); + + if (fds) + fdset_free(fds); + if (getpid() == 1) freeze(); diff --git a/manager.c b/manager.c index 0187d3b3..9458aa51 100644 --- a/manager.c +++ b/manager.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "manager.h" #include "hashmap.h" @@ -323,6 +325,7 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) { m->running_as = running_as; m->confirm_spawn = confirm_spawn; m->name_data_slot = -1; + m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ @@ -386,10 +389,9 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) { return n; } -void manager_free(Manager *m) { - UnitType c; - Unit *u; +static void manager_clear_jobs_and_units(Manager *m) { Job *j; + Unit *u; assert(m); @@ -398,14 +400,22 @@ void manager_free(Manager *m) { while ((u = hashmap_first(m->units))) unit_free(u); +} + +void manager_free(Manager *m) { + UnitType c; - manager_dispatch_cleanup_queue(m); + assert(m); + + manager_clear_jobs_and_units(m); for (c = 0; c < _UNIT_TYPE_MAX; c++) if (unit_vtable[c]->shutdown) unit_vtable[c]->shutdown(m); - manager_shutdown_cgroup(m); + /* If we reexecute ourselves, we keep the root cgroup + * around */ + manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); bus_done_api(m); bus_done_system(m); @@ -417,9 +427,9 @@ void manager_free(Manager *m) { hashmap_free(m->watch_bus); if (m->epoll_fd >= 0) - close_nointr(m->epoll_fd); + close_nointr_nofail(m->epoll_fd); if (m->signal_watch.fd >= 0) - close_nointr(m->signal_watch.fd); + close_nointr_nofail(m->signal_watch.fd); strv_free(m->unit_path); strv_free(m->sysvinit_path); @@ -428,29 +438,35 @@ void manager_free(Manager *m) { free(m->cgroup_controller); free(m->cgroup_hierarchy); - assert(hashmap_isempty(m->cgroup_bondings)); hashmap_free(m->cgroup_bondings); free(m); } -int manager_coldplug(Manager *m) { - int r; +int manager_enumerate(Manager *m) { + int r = 0, q; UnitType c; - Iterator i; - Unit *u; - char *k; assert(m); - /* First, let's ask every type to load all units from - * disk/kernel that it might know */ + /* Let's ask every type to load all units from disk/kernel + * that it might know */ for (c = 0; c < _UNIT_TYPE_MAX; c++) if (unit_vtable[c]->enumerate) - if ((r = unit_vtable[c]->enumerate(m)) < 0) - return r; + if ((q = unit_vtable[c]->enumerate(m)) < 0) + r = q; manager_dispatch_load_queue(m); + return r; +} + +int manager_coldplug(Manager *m) { + int r = 0, q; + Iterator i; + Unit *u; + char *k; + + assert(m); /* Then, let's set up their initial state. */ HASHMAP_FOREACH_KEY(u, k, m->units, i) { @@ -460,15 +476,35 @@ int manager_coldplug(Manager *m) { continue; if (UNIT_VTABLE(u)->coldplug) - if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0) - return r; + if ((q = UNIT_VTABLE(u)->coldplug(u)) < 0) + r = q; } + return r; +} + +int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { + int r, q; + + assert(m); + + /* First, enumerate what we can from all config files */ + r = manager_enumerate(m); + + /* Second, deserialize if there is something to deserialize */ + if (serialization) + if ((q = manager_deserialize(m, serialization, fds)) < 0) + r = q; + + /* Third, fire things up! */ + if ((q = manager_coldplug(m)) < 0) + r = q; + /* 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 r; } static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) { @@ -1553,7 +1589,7 @@ static void manager_start_target(Manager *m, const char *name) { log_error("Failed to enqueue %s job: %s", name, strerror(-r)); } -static int manager_process_signal_fd(Manager *m, bool *quit) { +static int manager_process_signal_fd(Manager *m) { ssize_t n; struct signalfd_siginfo sfsi; bool sigchld = false; @@ -1586,7 +1622,7 @@ static int manager_process_signal_fd(Manager *m, bool *quit) { break; } - *quit = true; + m->exit_code = MANAGER_EXIT; return 0; case SIGWINCH: @@ -1628,6 +1664,10 @@ static int manager_process_signal_fd(Manager *m, bool *quit) { break; } + case SIGHUP: + m->exit_code = MANAGER_RELOAD; + break; + default: log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo)); } @@ -1639,7 +1679,7 @@ static int manager_process_signal_fd(Manager *m, bool *quit) { return 0; } -static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { +static int process_event(Manager *m, struct epoll_event *ev) { int r; Watch *w; @@ -1656,7 +1696,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { if (ev->events != EPOLLIN) return -EINVAL; - if ((r = manager_process_signal_fd(m, quit)) < 0) + if ((r = manager_process_signal_fd(m)) < 0) return r; break; @@ -1711,13 +1751,13 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { int manager_loop(Manager *m) { int r; - bool quit = false; RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000); assert(m); + m->exit_code = MANAGER_RUNNING; - do { + while (m->exit_code == MANAGER_RUNNING) { struct epoll_event event; int n; @@ -1752,11 +1792,11 @@ int manager_loop(Manager *m) { assert(n == 1); - if ((r = process_event(m, &event, &quit)) < 0) + if ((r = process_event(m, &event)) < 0) return r; - } while (!quit); + } - return 0; + return m->exit_code; } int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) { @@ -1907,6 +1947,156 @@ void manager_dispatch_bus_query_pid_done( UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid); } +int manager_open_serialization(FILE **_f) { + char *path; + mode_t saved_umask; + int fd; + FILE *f; + + assert(_f); + + if (asprintf(&path, "/dev/shm/systemd-%u.dump-XXXXXX", (unsigned) getpid()) < 0) + return -ENOMEM; + + saved_umask = umask(0077); + fd = mkostemp(path, O_RDWR|O_CLOEXEC); + umask(saved_umask); + + if (fd < 0) { + free(path); + return -errno; + } + + unlink(path); + + log_debug("Serializing state to %s", path); + free(path); + + if (!(f = fdopen(fd, "w+")) < 0) + return -errno; + + *_f = f; + + return 0; +} + +int manager_serialize(Manager *m, FILE *f, FDSet *fds) { + Iterator i; + Unit *u; + const char *t; + int r; + + assert(m); + assert(f); + assert(fds); + + HASHMAP_FOREACH_KEY(u, t, m->units, i) { + if (u->meta.id != t) + continue; + + if (!unit_can_serialize(u)) + continue; + + /* Start marker */ + fputs(u->meta.id, f); + fputc('\n', f); + + if ((r = unit_serialize(u, f, fds)) < 0) + return r; + } + + if (ferror(f)) + return -EIO; + + return 0; +} + +int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + int r = 0; + + assert(m); + assert(f); + + log_debug("Deserializing state..."); + + for (;;) { + Unit *u; + char name[UNIT_NAME_MAX+2]; + + /* Start marker */ + if (!fgets(name, sizeof(name), f)) { + if (feof(f)) + break; + + return -errno; + } + + char_array_0(name); + + if ((r = manager_load_unit(m, strstrip(name), NULL, &u)) < 0) + return r; + + if ((r = unit_deserialize(u, f, fds)) < 0) + return r; + } + + if (ferror(f)) + return -EIO; + + return 0; +} + +int manager_reload(Manager *m) { + int r, q; + FILE *f; + FDSet *fds; + + assert(m); + + if ((r = manager_open_serialization(&f)) < 0) + return r; + + if (!(fds = fdset_new())) { + r = -ENOMEM; + goto finish; + } + + if ((r = manager_serialize(m, f, fds)) < 0) + goto finish; + + if (fseeko(f, 0, SEEK_SET) < 0) { + r = -errno; + goto finish; + } + + /* From here on there is no way back. */ + manager_clear_jobs_and_units(m); + + /* First, enumerate what we can from all config files */ + if ((q = manager_enumerate(m)) < 0) + r = q; + + /* Second, deserialize our stored data */ + if ((q = manager_deserialize(m, f, fds)) < 0) + r = q; + + fclose(f); + f = NULL; + + /* Third, fire things up! */ + if ((q = manager_coldplug(m)) < 0) + r = q; + +finish: + if (f) + fclose(f); + + if (fds) + fdset_free(fds); + + return r; +} + static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { [MANAGER_INIT] = "init", [MANAGER_SYSTEM] = "system", diff --git a/manager.h b/manager.h index 77e55339..8a48c551 100644 --- a/manager.h +++ b/manager.h @@ -25,13 +25,23 @@ #include #include #include - #include +#include "fdset.h" + typedef struct Manager Manager; typedef enum WatchType WatchType; typedef struct Watch Watch; +typedef enum ManagerExitCode { + MANAGER_RUNNING, + MANAGER_EXIT, + MANAGER_RELOAD, + MANAGER_REEXECUTE, + _MANAGER_EXIT_CODE_MAX, + _MANAGER_EXIT_CODE_INVALID = -1 +} ManagerExitCode; + typedef enum ManagerRunningAs { MANAGER_INIT, /* root and pid=1 */ MANAGER_SYSTEM, /* root and pid!=1 */ @@ -155,6 +165,8 @@ struct Manager { bool confirm_spawn:1; + ManagerExitCode exit_code; + Hashmap *watch_pids; /* pid => Unit object n:1 */ int epoll_fd; @@ -179,6 +191,10 @@ struct Manager { /* Data specific to the D-Bus subsystem */ DBusConnection *api_bus, *system_bus; Set *subscribed; + DBusMessage *queued_message; /* This is used during reloading: + * before the reload we queue the + * reply message here, and + * afterwards we send it */ Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */ int32_t name_data_slot; @@ -198,7 +214,9 @@ struct Manager { int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **m); void manager_free(Manager *m); +int manager_enumerate(Manager *m); int manager_coldplug(Manager *m); +int manager_startup(Manager *m, FILE *serialization, FDSet *fds); Job *manager_get_job(Manager *m, uint32_t id); Unit *manager_get_unit(Manager *m, const char *name); @@ -230,6 +248,13 @@ void manager_write_utmp_runlevel(Manager *m, Unit *t); void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner); void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid); +int manager_open_serialization(FILE **_f); + +int manager_serialize(Manager *m, FILE *f, FDSet *fds); +int manager_deserialize(Manager *m, FILE *f, FDSet *fds); + +int manager_reload(Manager *m); + const char *manager_running_as_to_string(ManagerRunningAs i); ManagerRunningAs manager_running_as_from_string(const char *s); diff --git a/mount.c b/mount.c index d3e222d4..187c6a37 100644 --- a/mount.c +++ b/mount.c @@ -52,32 +52,27 @@ static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { [MOUNT_MAINTAINANCE] = UNIT_INACTIVE, }; -static const char* const state_string_table[_MOUNT_STATE_MAX] = { - [MOUNT_DEAD] = "dead", - [MOUNT_MOUNTING] = "mounting", - [MOUNT_MOUNTING_DONE] = "mounting-done", - [MOUNT_MOUNTED] = "mounted", - [MOUNT_REMOUNTING] = "remounting", - [MOUNT_UNMOUNTING] = "unmounting", - [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm", - [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill", - [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm", - [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill", - [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm", - [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill", - [MOUNT_MAINTAINANCE] = "maintainance" -}; +static void mount_init(Unit *u) { + Mount *m = MOUNT(u); -static char *mount_name_from_where(const char *where) { - assert(where); + assert(u); + assert(u->meta.load_state == UNIT_STUB); + + m->timeout_usec = DEFAULT_TIMEOUT_USEC; + exec_context_init(&m->exec_context); - if (streq(where, "/")) - return strdup("-.mount"); + /* We need to make sure that /bin/mount is always called in + * the same process group as us, so that the autofs kernel + * side doesn't send us another mount request while we are + * already trying to comply its last one. */ + m->exec_context.no_setsid = true; - return unit_name_build_escape(where+1, NULL, ".mount"); + m->timer_watch.type = WATCH_INVALID; + + m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } -static void service_unwatch_control_pid(Mount *m) { +static void mount_unwatch_control_pid(Mount *m) { assert(m); if (m->control_pid <= 0) @@ -113,47 +108,11 @@ static void mount_done(Unit *u) { exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); m->control_command = NULL; - service_unwatch_control_pid(m); + mount_unwatch_control_pid(m); unit_unwatch_timer(u, &m->timer_watch); } -static void mount_init(Unit *u) { - Mount *m = MOUNT(u); - - assert(u); - assert(u->meta.load_state == UNIT_STUB); - - m->timeout_usec = DEFAULT_TIMEOUT_USEC; - exec_context_init(&m->exec_context); - - /* We need to make sure that /bin/mount is always called in - * the same process group as us, so that the autofs kernel - * side doesn't send us another mount request while we are - * already trying to comply its last one. */ - m->exec_context.no_setsid = true; - - m->timer_watch.type = WATCH_INVALID; -} - -static int mount_notify_automount(Mount *m, int status) { - Unit *p; - char *k; - - assert(m); - - if (!(k = unit_name_change_suffix(UNIT(m)->meta.id, ".automount"))) - return -ENOMEM; - - p = manager_get_unit(UNIT(m)->meta.manager, k); - free(k); - - if (!p) - return 0; - - return automount_send_ready(AUTOMOUNT(p), status); -} - static int mount_add_node_links(Mount *m) { Unit *device; char *e; @@ -262,10 +221,9 @@ static bool mount_test_option(const char *haystack, const char *needle) { static int mount_add_target_links(Mount *m) { const char *target; MountParameters *p; - Unit *u; + Unit *tu; int r; - bool noauto; - bool handle; + bool noauto, handle, automount; assert(m); @@ -278,8 +236,9 @@ static int mount_add_target_links(Mount *m) { noauto = mount_test_option(p->options, MNTOPT_NOAUTO); handle = mount_test_option(p->options, "comment=systemd.mount"); + automount = mount_test_option(p->options, "comment=systemd.automount"); - if (noauto && !handle) + if (noauto && !handle && !automount) return 0; if (mount_test_option(p->options, "_netdev") || @@ -288,14 +247,28 @@ static int mount_add_target_links(Mount *m) { else target = SPECIAL_LOCAL_FS_TARGET; - if ((r = manager_load_unit(UNIT(m)->meta.manager, target, NULL, &u)) < 0) + if ((r = manager_load_unit(UNIT(m)->meta.manager, target, NULL, &tu)) < 0) return r; - if (handle) - if ((r = unit_add_dependency(u, UNIT_WANTS, UNIT(m))) < 0) + if (automount) { + Unit *am; + + if ((r = unit_load_related_unit(UNIT(m), ".automount", &am)) < 0) return r; - return unit_add_dependency(UNIT(m), UNIT_BEFORE, u); + if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(am))) < 0) + return r; + + return unit_add_dependency(UNIT(am), UNIT_BEFORE, tu); + + } else { + + if (handle) + if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m))) < 0) + return r; + + return unit_add_dependency(UNIT(m), UNIT_BEFORE, tu); + } } static int mount_verify(Mount *m) { @@ -306,14 +279,7 @@ static int mount_verify(Mount *m) { if (UNIT(m)->meta.load_state != UNIT_LOADED) return 0; - if (!m->where) { - log_error("%s lacks Where setting. Refusing.", UNIT(m)->meta.id); - return -EINVAL; - } - - path_kill_slashes(m->where); - - if (!(e = mount_name_from_where(m->where))) + if (!(e = unit_name_from_path(m->where, ".mount"))) return -ENOMEM; b = unit_has_name(UNIT(m), e); @@ -340,6 +306,12 @@ static int mount_load(Unit *u) { /* This is a new unit? Then let's add in some extras */ if (u->meta.load_state == UNIT_LOADED) { + if (!m->where) + if (!(m->where = unit_name_to_path(u->meta.id))) + return -ENOMEM; + + path_kill_slashes(m->where); + /* Minor validity checking */ if ((m->parameters_fragment.options || m->parameters_fragment.fstype) && !m->parameters_fragment.what) return -EBADMSG; @@ -363,6 +335,18 @@ static int mount_load(Unit *u) { return mount_verify(m); } +static int mount_notify_automount(Mount *m, int status) { + Unit *p; + int r; + + assert(m); + + if ((r = unit_get_related_unit(UNIT(m), ".automount", &p)) < 0) + return r == -ENOENT ? 0 : r; + + return automount_send_ready(AUTOMOUNT(p), status); +} + static void mount_set_state(Mount *m, MountState state) { MountState old_state; assert(m); @@ -381,8 +365,9 @@ static void mount_set_state(Mount *m, MountState state) { state != MOUNT_REMOUNTING_SIGTERM && state != MOUNT_REMOUNTING_SIGKILL) { unit_unwatch_timer(UNIT(m), &m->timer_watch); - service_unwatch_control_pid(m); + mount_unwatch_control_pid(m); m->control_command = NULL; + m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } if (state == MOUNT_MOUNTED || @@ -400,57 +385,54 @@ static void mount_set_state(Mount *m, MountState state) { mount_notify_automount(m, -ENODEV); if (state != old_state) - log_debug("%s changed %s → %s", UNIT(m)->meta.id, state_string_table[old_state], state_string_table[state]); + log_debug("%s changed %s → %s", + UNIT(m)->meta.id, + mount_state_to_string(old_state), + mount_state_to_string(state)); unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state]); } static int mount_coldplug(Unit *u) { Mount *m = MOUNT(u); + MountState new_state = MOUNT_DEAD; + int r; assert(m); assert(m->state == MOUNT_DEAD); - if (m->from_proc_self_mountinfo) - mount_set_state(m, MOUNT_MOUNTED); + if (m->deserialized_state != m->state) + new_state = m->deserialized_state; + else if (m->from_proc_self_mountinfo) + new_state = MOUNT_MOUNTED; - return 0; -} + if (new_state != m->state) { -static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { - pid_t pid; - int r; + if (new_state == MOUNT_MOUNTING || + new_state == MOUNT_MOUNTING_DONE || + new_state == MOUNT_REMOUNTING || + new_state == MOUNT_UNMOUNTING || + new_state == MOUNT_MOUNTING_SIGTERM || + new_state == MOUNT_MOUNTING_SIGKILL || + new_state == MOUNT_UNMOUNTING_SIGTERM || + new_state == MOUNT_UNMOUNTING_SIGKILL || + new_state == MOUNT_REMOUNTING_SIGTERM || + new_state == MOUNT_REMOUNTING_SIGKILL) { - assert(m); - assert(c); - assert(_pid); + if (m->control_pid <= 0) + return -EBADMSG; - if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) - goto fail; - - if ((r = exec_spawn(c, - NULL, - &m->exec_context, - NULL, 0, - true, - true, - UNIT(m)->meta.manager->confirm_spawn, - UNIT(m)->meta.cgroup_bondings, - &pid)) < 0) - goto fail; + if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0) + return r; - if ((r = unit_watch_pid(UNIT(m), pid)) < 0) - /* FIXME: we need to do something here */ - goto fail; + if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) + return r; + } - *_pid = pid; + mount_set_state(m, new_state); + } return 0; - -fail: - unit_unwatch_timer(UNIT(m), &m->timer_watch); - - return r; } static void mount_dump(Unit *u, FILE *f, const char *prefix) { @@ -477,7 +459,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { "%sFrom /proc/self/mountinfo: %s\n" "%sFrom fragment: %s\n" "%sKillMode: %s\n", - prefix, state_string_table[m->state], + prefix, mount_state_to_string(m->state), prefix, m->where, prefix, strna(p->what), prefix, strna(p->fstype), @@ -495,6 +477,42 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { exec_context_dump(&m->exec_context, f, prefix); } +static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { + pid_t pid; + int r; + + assert(m); + assert(c); + assert(_pid); + + if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) + goto fail; + + if ((r = exec_spawn(c, + NULL, + &m->exec_context, + NULL, 0, + true, + true, + UNIT(m)->meta.manager->confirm_spawn, + UNIT(m)->meta.cgroup_bondings, + &pid)) < 0) + goto fail; + + if ((r = unit_watch_pid(UNIT(m), pid)) < 0) + /* FIXME: we need to do something here */ + goto fail; + + *_pid = pid; + + return 0; + +fail: + unit_unwatch_timer(UNIT(m), &m->timer_watch); + + return r; +} + static void mount_enter_dead(Mount *m, bool success) { assert(m); @@ -565,7 +583,6 @@ fail: } static void mount_enter_unmounting(Mount *m, bool success) { - ExecCommand *c; int r; assert(m); @@ -573,18 +590,19 @@ static void mount_enter_unmounting(Mount *m, bool success) { if (!success) m->failure = true; - m->control_command = c = m->exec_command + MOUNT_EXEC_UNMOUNT; + m->control_command_id = MOUNT_EXEC_UNMOUNT; + m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT; if ((r = exec_command_set( - c, + m->control_command, "/bin/umount", m->where, NULL)) < 0) goto fail; - service_unwatch_control_pid(m); + mount_unwatch_control_pid(m); - if ((r = mount_spawn(m, c, &m->control_pid)) < 0) + if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0) goto fail; mount_set_state(m, MOUNT_UNMOUNTING); @@ -597,16 +615,16 @@ fail: } static void mount_enter_mounting(Mount *m) { - ExecCommand *c; int r; assert(m); - m->control_command = c = m->exec_command + MOUNT_EXEC_MOUNT; + m->control_command_id = MOUNT_EXEC_MOUNT; + m->control_command = m->exec_command + MOUNT_EXEC_MOUNT; if (m->from_fragment) r = exec_command_set( - c, + m->control_command, "/bin/mount", m->parameters_fragment.what, m->where, @@ -615,7 +633,7 @@ static void mount_enter_mounting(Mount *m) { NULL); else if (m->from_etc_fstab) r = exec_command_set( - c, + m->control_command, "/bin/mount", m->where, NULL); @@ -625,9 +643,9 @@ static void mount_enter_mounting(Mount *m) { if (r < 0) goto fail; - service_unwatch_control_pid(m); + mount_unwatch_control_pid(m); - if ((r = mount_spawn(m, c, &m->control_pid)) < 0) + if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0) goto fail; mount_set_state(m, MOUNT_MOUNTING); @@ -646,7 +664,6 @@ static void mount_enter_mounting_done(Mount *m) { } static void mount_enter_remounting(Mount *m, bool success) { - ExecCommand *c; int r; assert(m); @@ -654,7 +671,8 @@ static void mount_enter_remounting(Mount *m, bool success) { if (!success) m->failure = true; - m->control_command = c = m->exec_command + MOUNT_EXEC_REMOUNT; + m->control_command_id = MOUNT_EXEC_REMOUNT; + m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; if (m->from_fragment) { char *buf = NULL; @@ -671,7 +689,7 @@ static void mount_enter_remounting(Mount *m, bool success) { o = "remount"; r = exec_command_set( - c, + m->control_command, "/bin/mount", m->parameters_fragment.what, m->where, @@ -682,7 +700,7 @@ static void mount_enter_remounting(Mount *m, bool success) { free(buf); } else if (m->from_etc_fstab) r = exec_command_set( - c, + m->control_command, "/bin/mount", m->where, "-o", "remount", @@ -695,9 +713,9 @@ static void mount_enter_remounting(Mount *m, bool success) { goto fail; } - service_unwatch_control_pid(m); + mount_unwatch_control_pid(m); - if ((r = mount_spawn(m, c, &m->control_pid)) < 0) + if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0) goto fail; mount_set_state(m, MOUNT_REMOUNTING); @@ -729,7 +747,6 @@ static int mount_start(Unit *u) { assert(m->state == MOUNT_DEAD || m->state == MOUNT_MAINTAINANCE); m->failure = false; - mount_enter_mounting(m); return 0; } @@ -775,6 +792,72 @@ static int mount_reload(Unit *u) { return 0; } +static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { + Mount *m = MOUNT(u); + + assert(m); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", mount_state_to_string(m->state)); + unit_serialize_item(u, f, "failure", yes_no(m->failure)); + + if (m->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) m->control_pid); + + if (m->control_command_id >= 0) + unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id)); + + return 0; +} + +static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Mount *m = MOUNT(u); + int r; + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + MountState state; + + if ((state = mount_state_from_string(value)) < 0) + log_debug("Failed to parse state value %s", value); + else + m->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 + m->failure = b || m->failure; + + } else if (streq(key, "control-pid")) { + unsigned pid; + + if ((r = safe_atou(value, &pid)) < 0 || pid <= 0) + log_debug("Failed to parse control-pid value %s", value); + else + m->control_pid = (pid_t) pid; + } else if (streq(key, "control-command")) { + MountExecCommand id; + + if ((id = mount_exec_command_from_string(value)) < 0) + log_debug("Failed to parse exec-command value %s", value); + else { + m->control_command_id = id; + m->control_command = m->exec_command + id; + } + + } else + log_debug("Unknown serialization key '%s'", key); + + return 0; +} + static UnitActiveState mount_active_state(Unit *u) { assert(u); @@ -784,7 +867,7 @@ static UnitActiveState mount_active_state(Unit *u) { static const char *mount_sub_state_to_string(Unit *u) { assert(u); - return state_string_table[MOUNT(u)->state]; + return mount_state_to_string(MOUNT(u)->state); } static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { @@ -798,11 +881,14 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { m->failure = m->failure || !success; assert(m->control_pid == pid); - assert(m->control_command); - - exec_status_fill(&m->control_command->exec_status, pid, code, status); m->control_pid = 0; + if (m->control_command) { + exec_status_fill(&m->control_command->exec_status, pid, code, status); + m->control_command = NULL; + m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; + } + log_debug("%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); /* Note that mount(8) returning and the kernel sending us a @@ -937,9 +1023,7 @@ static int mount_add_one( if (where[0] != '/') return 0; - e = mount_name_from_where(where); - - if (!e) + if (!(e = unit_name_from_path(where, ".mount"))) return -ENOMEM; if (!(u = manager_get_unit(m, e))) { @@ -1172,8 +1256,10 @@ finish: static void mount_shutdown(Manager *m) { assert(m); - if (m->proc_self_mountinfo) + if (m->proc_self_mountinfo) { fclose(m->proc_self_mountinfo); + m->proc_self_mountinfo = NULL; + } } static int mount_enumerate(Manager *m) { @@ -1181,18 +1267,20 @@ static int mount_enumerate(Manager *m) { struct epoll_event ev; assert(m); - if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "r"))) - return -errno; + if (!m->proc_self_mountinfo) { + if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"))) + return -errno; - m->mount_watch.type = WATCH_MOUNT; - m->mount_watch.fd = fileno(m->proc_self_mountinfo); + m->mount_watch.type = WATCH_MOUNT; + m->mount_watch.fd = fileno(m->proc_self_mountinfo); - zero(ev); - ev.events = EPOLLERR; - ev.data.ptr = &m->mount_watch; + zero(ev); + ev.events = EPOLLERR; + ev.data.ptr = &m->mount_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0) - return -errno; + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0) + return -errno; + } if ((r = mount_load_etc_fstab(m)) < 0) goto fail; @@ -1303,7 +1391,7 @@ int mount_path_is_mounted(Manager *m, const char* path) { char *e, *slash; Unit *u; - if (!(e = mount_name_from_where(t))) { + if (!(e = unit_name_from_path(t, ".mount"))) { r = -ENOMEM; goto finish; } @@ -1335,6 +1423,32 @@ finish: return r; } +static const char* const mount_state_table[_MOUNT_STATE_MAX] = { + [MOUNT_DEAD] = "dead", + [MOUNT_MOUNTING] = "mounting", + [MOUNT_MOUNTING_DONE] = "mounting-done", + [MOUNT_MOUNTED] = "mounted", + [MOUNT_REMOUNTING] = "remounting", + [MOUNT_UNMOUNTING] = "unmounting", + [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm", + [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill", + [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm", + [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill", + [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm", + [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill", + [MOUNT_MAINTAINANCE] = "maintainance" +}; + +DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState); + +static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { + [MOUNT_EXEC_MOUNT] = "ExecMount", + [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", + [MOUNT_EXEC_REMOUNT] = "ExecRemount", +}; + +DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand); + const UnitVTable mount_vtable = { .suffix = ".mount", @@ -1353,6 +1467,9 @@ const UnitVTable mount_vtable = { .stop = mount_stop, .reload = mount_reload, + .serialize = mount_serialize, + .deserialize_item = mount_deserialize_item, + .active_state = mount_active_state, .sub_state_to_string = mount_sub_state_to_string, diff --git a/mount.h b/mount.h index 66992e10..3b28e89e 100644 --- a/mount.h +++ b/mount.h @@ -84,11 +84,12 @@ struct Mount { ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX]; ExecContext exec_context; - MountState state; + MountState state, deserialized_state; KillMode kill_mode; ExecCommand* control_command; + MountExecCommand control_command_id; pid_t control_pid; Watch timer_watch; @@ -100,4 +101,10 @@ void mount_fd_event(Manager *m, int events); int mount_path_is_mounted(Manager *m, const char* path); +const char* mount_state_to_string(MountState i); +MountState mount_state_from_string(const char *s); + +const char* mount_exec_command_to_string(MountExecCommand i); +MountExecCommand mount_exec_command_from_string(const char *s); + #endif diff --git a/service.c b/service.c index f334027a..25641768 100644 --- a/service.c +++ b/service.c @@ -66,6 +66,25 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING, }; +static void service_init(Unit *u) { + Service *s = SERVICE(u); + + assert(u); + assert(u->meta.load_state == UNIT_STUB); + + s->timeout_usec = DEFAULT_TIMEOUT_USEC; + s->restart_usec = DEFAULT_RESTART_USEC; + s->timer_watch.type = WATCH_INVALID; + s->sysv_start_priority = -1; + s->socket_fd = -1; + + exec_context_init(&s->exec_context); + + RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); + + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; +} + static void service_unwatch_control_pid(Service *s) { assert(s); @@ -735,23 +754,6 @@ static int service_add_bus_name(Service *s) { return r; } -static void service_init(Unit *u) { - Service *s = SERVICE(u); - - assert(u); - assert(u->meta.load_state == UNIT_STUB); - - s->timeout_usec = DEFAULT_TIMEOUT_USEC; - s->restart_usec = DEFAULT_RESTART_USEC; - s->timer_watch.type = WATCH_INVALID; - s->sysv_start_priority = -1; - s->socket_fd = -1; - - exec_context_init(&s->exec_context); - - RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); -} - static int service_verify(Service *s) { assert(s); @@ -1047,6 +1049,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_FINAL_SIGKILL) { service_unwatch_control_pid(s); s->control_command = NULL; + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; } if (state == SERVICE_DEAD || @@ -1071,6 +1074,66 @@ static void service_set_state(Service *s, ServiceState state) { unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); } +static int service_coldplug(Unit *u) { + Service *s = SERVICE(u); + int r; + + assert(s); + assert(s->state == SERVICE_DEAD); + + if (s->deserialized_state != s->state) { + + if (s->deserialized_state == SERVICE_START_PRE || + s->deserialized_state == SERVICE_START || + s->deserialized_state == SERVICE_START_POST || + s->deserialized_state == SERVICE_RELOAD || + s->deserialized_state == SERVICE_STOP || + s->deserialized_state == SERVICE_STOP_SIGTERM || + s->deserialized_state == SERVICE_STOP_SIGKILL || + s->deserialized_state == SERVICE_STOP_POST || + s->deserialized_state == SERVICE_FINAL_SIGTERM || + s->deserialized_state == SERVICE_FINAL_SIGKILL || + s->deserialized_state == SERVICE_AUTO_RESTART) + if ((r = unit_watch_timer(UNIT(s), + s->deserialized_state == SERVICE_AUTO_RESTART ? + s->restart_usec : + s->timeout_usec, + &s->timer_watch)) < 0) + return r; + + if ((s->deserialized_state == SERVICE_START && + (s->type == SERVICE_FORKING || + s->type == SERVICE_DBUS)) || + s->deserialized_state == SERVICE_START_POST || + s->deserialized_state == SERVICE_RUNNING || + s->deserialized_state == SERVICE_RELOAD || + s->deserialized_state == SERVICE_STOP || + s->deserialized_state == SERVICE_STOP_SIGTERM || + s->deserialized_state == SERVICE_STOP_SIGKILL) + if (s->main_pid > 0) + if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0) + return r; + + if (s->deserialized_state == SERVICE_START_PRE || + s->deserialized_state == SERVICE_START || + s->deserialized_state == SERVICE_START_POST || + s->deserialized_state == SERVICE_RELOAD || + s->deserialized_state == SERVICE_STOP || + s->deserialized_state == SERVICE_STOP_SIGTERM || + s->deserialized_state == SERVICE_STOP_SIGKILL || + s->deserialized_state == SERVICE_STOP_POST || + s->deserialized_state == SERVICE_FINAL_SIGTERM || + s->deserialized_state == SERVICE_FINAL_SIGKILL) + if (s->control_pid > 0) + if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) + return r; + + service_set_state(s, s->deserialized_state); + } + + return 0; +} + static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { Iterator i; int r; @@ -1279,6 +1342,7 @@ static void service_enter_stop_post(Service *s, bool success) { service_unwatch_control_pid(s); + s->control_command_id = SERVICE_EXEC_STOP_POST; if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) { if ((r = service_spawn(s, s->control_command, @@ -1374,6 +1438,7 @@ static void service_enter_stop(Service *s, bool success) { service_unwatch_control_pid(s); + s->control_command_id = SERVICE_EXEC_STOP; if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) { if ((r = service_spawn(s, s->control_command, @@ -1417,6 +1482,7 @@ static void service_enter_start_post(Service *s) { service_unwatch_control_pid(s); + s->control_command_id = SERVICE_EXEC_START_POST; if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { if ((r = service_spawn(s, s->control_command, @@ -1478,6 +1544,7 @@ static void service_enter_start(Service *s) { s->control_pid = pid; + s->control_command_id = SERVICE_EXEC_START; s->control_command = s->exec_command[SERVICE_EXEC_START]; service_set_state(s, SERVICE_START); @@ -1511,6 +1578,7 @@ static void service_enter_start_pre(Service *s) { service_unwatch_control_pid(s); + s->control_command_id = SERVICE_EXEC_START_PRE; if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) { if ((r = service_spawn(s, s->control_command, @@ -1557,6 +1625,7 @@ static void service_enter_reload(Service *s) { service_unwatch_control_pid(s); + s->control_command_id = SERVICE_EXEC_RELOAD; if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) { if ((r = service_spawn(s, s->control_command, @@ -1703,6 +1772,112 @@ static bool service_can_reload(Unit *u) { return !!s->exec_command[SERVICE_EXEC_RELOAD]; } +static int service_serialize(Unit *u, FILE *f, FDSet *fds) { + Service *s = SERVICE(u); + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", service_state_to_string(s->state)); + unit_serialize_item(u, f, "failure", yes_no(s->failure)); + + if (s->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) (s->control_pid)); + + if (s->main_pid > 0) + unit_serialize_item_format(u, f, "main-pid", "%u", (unsigned) (s->main_pid)); + + unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known)); + + /* There's a minor uncleanliness here: if there are multiple + * commands attached here, we will start from the first one + * again */ + if (s->control_command_id >= 0) + unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(s->control_command_id)); + + if (s->socket_fd >= 0) { + int copy; + + if ((copy = fdset_put_dup(fds, s->socket_fd)) < 0) + return copy; + + unit_serialize_item_format(u, f, "socket-fd", "%i", copy); + } + + return 0; +} + +static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Service *s = SERVICE(u); + int r; + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + ServiceState state; + + if ((state = service_state_from_string(value)) < 0) + log_debug("Failed to parse state value %s", value); + else + s->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 + s->failure = b || s->failure; + } else if (streq(key, "control-pid")) { + unsigned pid; + + if ((r = safe_atou(value, &pid)) < 0 || pid <= 0) + log_debug("Failed to parse control-pid value %s", value); + else + s->control_pid = (pid_t) pid; + } else if (streq(key, "main-pid")) { + unsigned pid; + + if ((r = safe_atou(value, &pid)) < 0 || pid <= 0) + log_debug("Failed to parse main-pid value %s", value); + else + s->main_pid = (pid_t) pid; + } else if (streq(key, "main-pid-known")) { + int b; + + if ((b = parse_boolean(value)) < 0) + log_debug("Failed to parse main-pid-known value %s", value); + else + s->main_pid_known = b; + } else if (streq(key, "control-command")) { + ServiceExecCommand id; + + if ((id = service_exec_command_from_string(value)) < 0) + log_debug("Failed to parse exec-command value %s", value); + else { + s->control_command_id = id; + s->control_command = s->exec_command[id]; + } + } else if (streq(key, "socket-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse socket-fd value %s", value); + else { + + if (s->socket_fd >= 0) + close_nointr_nofail(s->socket_fd); + s->socket_fd = fdset_remove(fds, fd); + } + } else + log_debug("Unknown serialization key '%s'", key); + + return 0; +} + static UnitActiveState service_active_state(Unit *u) { assert(u); @@ -1777,9 +1952,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { } } else if (s->control_pid == pid) { - assert(s->control_command); - exec_status_fill(&s->control_command->exec_status, pid, code, status); + if (s->control_command) + exec_status_fill(&s->control_command->exec_status, pid, code, status); + s->control_pid = 0; log_debug("%s: control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); @@ -1787,7 +1963,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* If we are shutting things down anyway we * don't care about failing commands. */ - if (s->control_command->command_next && success) { + if (s->control_command && s->control_command->command_next && success) { /* There is another command to * * execute, so let's do that. */ @@ -1799,6 +1975,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* No further commands for this step, so let's * figure out what to do next */ + s->control_command = NULL; + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; + log_debug("%s got final SIGCHLD for state %s", u->meta.id, service_state_to_string(s->state)); switch (s->state) { @@ -2218,8 +2397,10 @@ const UnitVTable service_vtable = { .suffix = ".service", .init = service_init, - .load = service_load, .done = service_done, + .load = service_load, + + .coldplug = service_coldplug, .dump = service_dump, @@ -2229,6 +2410,9 @@ const UnitVTable service_vtable = { .can_reload = service_can_reload, + .serialize = service_serialize, + .deserialize_item = service_deserialize_item, + .active_state = service_active_state, .sub_state_to_string = service_sub_state_to_string, diff --git a/service.h b/service.h index e603ff74..dc597aa0 100644 --- a/service.h +++ b/service.h @@ -94,13 +94,14 @@ struct Service { bool root_directory_start_only; bool valid_no_process; - ServiceState state; + ServiceState state, deserialized_state; KillMode kill_mode; ExecStatus main_exec_status; ExecCommand *control_command; + ServiceExecCommand control_command_id; pid_t main_pid, control_pid; bool main_pid_known:1; diff --git a/snapshot.c b/snapshot.c index 1fc0ab81..d0aaa51f 100644 --- a/snapshot.c +++ b/snapshot.c @@ -31,31 +31,30 @@ static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = { [SNAPSHOT_ACTIVE] = UNIT_ACTIVE }; -static const char* const state_string_table[_SNAPSHOT_STATE_MAX] = { - [SNAPSHOT_DEAD] = "dead", - [SNAPSHOT_ACTIVE] = "active" -}; - -static int snapshot_load(Unit *u) { - Iterator i; - Unit *other; - int r; +static void snapshot_set_state(Snapshot *s, SnapshotState state) { + SnapshotState old_state; + assert(s); - assert(u); + old_state = s->state; + s->state = state; - HASHMAP_FOREACH(other, u->meta.manager->units, i) { + if (state != old_state) + log_debug("%s changed %s → %s", + UNIT(s)->meta.id, + snapshot_state_to_string(old_state), + snapshot_state_to_string(state)); - if (UNIT_VTABLE(other)->no_snapshots) - continue; + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); +} - if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0) - return r; +static int snapshot_coldplug(Unit *u) { + Snapshot *s = SNAPSHOT(u); - if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0) - return r; - } + assert(s); + assert(s->state == SNAPSHOT_DEAD); - u->meta.load_state = UNIT_LOADED; + if (s->deserialized_state != s->state) + snapshot_set_state(s, s->deserialized_state); return 0; } @@ -69,23 +68,10 @@ static void snapshot_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sSnapshot State: %s\n" "%sClean Up: %s\n", - prefix, state_string_table[s->state], + prefix, snapshot_state_to_string(s->state), prefix, yes_no(s->cleanup)); } -static void snapshot_set_state(Snapshot *s, SnapshotState state) { - SnapshotState old_state; - assert(s); - - old_state = s->state; - s->state = state; - - if (state != old_state) - log_debug("%s changed %s → %s", UNIT(s)->meta.id, state_string_table[old_state], state_string_table[state]); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); -} - static int snapshot_start(Unit *u) { Snapshot *s = SNAPSHOT(u); @@ -110,6 +96,60 @@ static int snapshot_stop(Unit *u) { return 0; } +static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) { + Snapshot *s = SNAPSHOT(u); + Unit *other; + Iterator i; + + assert(s); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state)); + unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup)); + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i) + unit_serialize_item(u, f, "requires", other->meta.id); + + return 0; +} + +static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Snapshot *s = SNAPSHOT(u); + int r; + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + SnapshotState state; + + if ((state = snapshot_state_from_string(value)) < 0) + log_debug("Failed to parse state value %s", value); + else + s->deserialized_state = state; + + } else if (streq(key, "cleanup")) { + + if ((r = parse_boolean(value)) < 0) + log_debug("Failed to parse cleanup value %s", value); + else + s->cleanup = r; + + } else if (streq(key, "requires")) { + + if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, value, NULL)) < 0) + return r; + + if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, value, NULL)) < 0) + return r; + } else + log_debug("Unknown serialization key '%s'", key); + + return 0; +} + static UnitActiveState snapshot_active_state(Unit *u) { assert(u); @@ -119,13 +159,15 @@ static UnitActiveState snapshot_active_state(Unit *u) { static const char *snapshot_sub_state_to_string(Unit *u) { assert(u); - return state_string_table[SNAPSHOT(u)->state]; + return snapshot_state_to_string(SNAPSHOT(u)->state); } int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) { - Unit *u; + Iterator i; + Unit *other, *u = NULL; char *n = NULL; int r; + const char *k; assert(m); assert(_s); @@ -159,12 +201,36 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) { free(n); if (r < 0) - return r; + goto fail; + + HASHMAP_FOREACH_KEY(other, k, m->units, i) { + + if (UNIT_VTABLE(other)->no_snapshots) + continue; + + if (k != other->meta.id) + continue; + + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) + continue; + + if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0) + goto fail; + + if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0) + goto fail; + } SNAPSHOT(u)->cleanup = cleanup; *_s = SNAPSHOT(u); return 0; + +fail: + if (u) + unit_add_to_cleanup_queue(u); + + return r; } void snapshot_remove(Snapshot *s) { @@ -173,6 +239,13 @@ void snapshot_remove(Snapshot *s) { unit_add_to_cleanup_queue(UNIT(s)); } +static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = { + [SNAPSHOT_DEAD] = "dead", + [SNAPSHOT_ACTIVE] = "active" +}; + +DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState); + const UnitVTable snapshot_vtable = { .suffix = ".snapshot", @@ -180,13 +253,17 @@ const UnitVTable snapshot_vtable = { .no_instances = true, .no_snapshots = true, - .load = snapshot_load, + .load = unit_load_nop, + .coldplug = snapshot_coldplug, .dump = snapshot_dump, .start = snapshot_start, .stop = snapshot_stop, + .serialize = snapshot_serialize, + .deserialize_item = snapshot_deserialize_item, + .active_state = snapshot_active_state, .sub_state_to_string = snapshot_sub_state_to_string, diff --git a/snapshot.h b/snapshot.h index 78bcafa4..959a5090 100644 --- a/snapshot.h +++ b/snapshot.h @@ -36,7 +36,7 @@ typedef enum SnapshotState { struct Snapshot { Meta meta; - SnapshotState state; + SnapshotState state, deserialized_state; bool cleanup; }; @@ -46,4 +46,7 @@ extern const UnitVTable snapshot_vtable; int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **s); void snapshot_remove(Snapshot *s); +const char* snapshot_state_to_string(SnapshotState i); +SnapshotState snapshot_state_from_string(const char *s); + #endif diff --git a/socket-util.c b/socket-util.c index cd5ab82a..8141ab09 100644 --- a/socket-util.c +++ b/socket-util.c @@ -316,7 +316,7 @@ int socket_address_listen( if ((r = socket_address_verify(a)) < 0) return r; - if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK, 0)) < 0) + if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) return -errno; if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { @@ -370,7 +370,7 @@ int socket_address_listen( fail: r = -errno; - close_nointr(fd); + close_nointr_nofail(fd); return r; } @@ -381,3 +381,77 @@ bool socket_address_can_accept(const SocketAddress *a) { a->type == SOCK_STREAM || a->type == SOCK_SEQPACKET; } + +bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { + assert(a); + assert(b); + + /* Invalid addresses are unequal to all */ + if (socket_address_verify(a) < 0 || + socket_address_verify(b) < 0) + return false; + + if (a->type != b->type) + return false; + + if (a->size != b->size) + return false; + + if (socket_address_family(a) != socket_address_family(b)) + return false; + + switch (socket_address_family(a)) { + + case AF_INET: + if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr) + return false; + + if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port) + return false; + + break; + + case AF_INET6: + if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0) + return false; + + if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port) + return false; + + break; + + case AF_UNIX: + + if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0)) + return false; + + if (a->sockaddr.un.sun_path[0]) { + if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0) + return false; + } else { + if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0) + return false; + } + + break; + + default: + /* Cannot compare, so we assume the addresses are different */ + return false; + } + + return true; +} + +bool socket_address_is(const SocketAddress *a, const char *s) { + struct SocketAddress b; + + assert(a); + assert(s); + + if (socket_address_parse(&b, s) < 0) + return false; + + return socket_address_equal(a, &b); + +} diff --git a/socket-util.h b/socket-util.h index e6e1b30b..af452efd 100644 --- a/socket-util.h +++ b/socket-util.h @@ -70,4 +70,8 @@ int socket_address_listen( mode_t socket_mode, int *ret); +bool socket_address_is(const SocketAddress *a, const char *s); + +bool socket_address_equal(const SocketAddress *a, const SocketAddress *b); + #endif diff --git a/socket.c b/socket.c index 22658dcb..63346a2b 100644 --- a/socket.c +++ b/socket.c @@ -52,20 +52,22 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_MAINTAINANCE] = UNIT_INACTIVE, }; -static const char* const state_string_table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = "dead", - [SOCKET_START_PRE] = "start-pre", - [SOCKET_START_POST] = "start-post", - [SOCKET_LISTENING] = "listening", - [SOCKET_RUNNING] = "running", - [SOCKET_STOP_PRE] = "stop-pre", - [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", - [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", - [SOCKET_STOP_POST] = "stop-post", - [SOCKET_FINAL_SIGTERM] = "final-sigterm", - [SOCKET_FINAL_SIGKILL] = "final-sigkill", - [SOCKET_MAINTAINANCE] = "maintainance" -}; +static void socket_init(Unit *u) { + Socket *s = SOCKET(u); + + assert(u); + assert(u->meta.load_state == UNIT_STUB); + + s->timer_watch.type = WATCH_INVALID; + s->backlog = SOMAXCONN; + s->timeout_usec = DEFAULT_TIMEOUT_USEC; + s->directory_mode = 0755; + s->socket_mode = 0666; + + exec_context_init(&s->exec_context); + + s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; +} static void socket_unwatch_control_pid(Socket *s) { assert(s); @@ -86,8 +88,11 @@ static void socket_done(Unit *u) { while ((p = s->ports)) { LIST_REMOVE(SocketPort, port, s->ports, p); - if (p->fd >= 0) - close_nointr(p->fd); + if (p->fd >= 0) { + unit_unwatch_fd(UNIT(s), &p->fd_watch); + close_nointr_nofail(p->fd); + } + free(p->path); free(p); } @@ -106,21 +111,6 @@ static void socket_done(Unit *u) { unit_unwatch_timer(u, &s->timer_watch); } -static void socket_init(Unit *u) { - Socket *s = SOCKET(u); - - assert(u); - assert(u->meta.load_state == UNIT_STUB); - - s->timer_watch.type = WATCH_INVALID; - s->backlog = SOMAXCONN; - s->timeout_usec = DEFAULT_TIMEOUT_USEC; - s->directory_mode = 0755; - s->socket_mode = 0666; - - exec_context_init(&s->exec_context); -} - static bool have_non_accept_socket(Socket *s) { SocketPort *p; @@ -196,13 +186,6 @@ static const char* listen_lookup(int type) { static void socket_dump(Unit *u, FILE *f, const char *prefix) { - static const char* const command_table[_SOCKET_EXEC_COMMAND_MAX] = { - [SOCKET_EXEC_START_PRE] = "StartPre", - [SOCKET_EXEC_START_POST] = "StartPost", - [SOCKET_EXEC_STOP_PRE] = "StopPre", - [SOCKET_EXEC_STOP_POST] = "StopPost" - }; - SocketExecCommand c; Socket *s = SOCKET(u); SocketPort *p; @@ -222,7 +205,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sKillMode: %s\n" "%sSocketMode: %04o\n" "%sDirectoryMode: %04o\n", - prefix, state_string_table[s->state], + prefix, socket_state_to_string(s->state), prefix, yes_no(s->bind_ipv6_only), prefix, s->backlog, prefix, kill_mode_to_string(s->kill_mode), @@ -269,7 +252,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { continue; fprintf(f, "%s→ %s:\n", - prefix, command_table[c]); + prefix, socket_exec_command_to_string(c)); exec_command_dump_list(s->exec_command[c], f, prefix2); } @@ -307,7 +290,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) { b = ntohl(remote.in.sin_addr.s_addr); if (asprintf(&r, - "%u-%u.%u.%u.%u-%u-%u.%u.%u.%u-%u", + "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", nr, a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, ntohs(local.in.sin_port), @@ -322,7 +305,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) { char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN]; if (asprintf(&r, - "%u-%s-%u-%s-%u", + "%u-%s:%u-%s:%u", nr, inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)), ntohs(local.in6.sin6_port), @@ -368,7 +351,15 @@ static void socket_close_fds(Socket *s) { continue; unit_unwatch_fd(UNIT(s), &p->fd_watch); - assert_se(close_nointr(p->fd) >= 0); + close_nointr_nofail(p->fd); + + /* One little note: we should never delete any sockets + * in the file system here! After all some other + * process we spawned might still have a reference of + * this fd and wants to continue to use it. Therefore + * we delete sockets in the file system before we + * create a new one, not after we stopped using + * one! */ p->fd = -1; } @@ -490,8 +481,12 @@ static void socket_set_state(Socket *s, SocketState state) { unit_unwatch_timer(UNIT(s), &s->timer_watch); socket_unwatch_control_pid(s); s->control_command = NULL; + s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; } + if (state != SOCKET_LISTENING) + socket_unwatch_fds(s); + if (state != SOCKET_START_POST && state != SOCKET_LISTENING && state != SOCKET_RUNNING && @@ -500,15 +495,62 @@ static void socket_set_state(Socket *s, SocketState state) { state != SOCKET_STOP_PRE_SIGKILL) socket_close_fds(s); - if (state != SOCKET_LISTENING) - socket_unwatch_fds(s); - if (state != old_state) - log_debug("%s changed %s → %s", s->meta.id, state_string_table[old_state], state_string_table[state]); + log_debug("%s changed %s → %s", + s->meta.id, + socket_state_to_string(old_state), + socket_state_to_string(state)); unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); } +static int socket_coldplug(Unit *u) { + Socket *s = SOCKET(u); + int r; + + assert(s); + assert(s->state == SOCKET_DEAD); + + if (s->deserialized_state != s->state) { + + if (s->deserialized_state == SOCKET_START_PRE || + s->deserialized_state == SOCKET_START_POST || + s->deserialized_state == SOCKET_STOP_PRE || + s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || + s->deserialized_state == SOCKET_STOP_PRE_SIGKILL || + s->deserialized_state == SOCKET_STOP_POST || + s->deserialized_state == SOCKET_FINAL_SIGTERM || + s->deserialized_state == SOCKET_FINAL_SIGKILL) { + + if (s->control_pid <= 0) + return -EBADMSG; + + if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) + return r; + + if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + return r; + } + + if (s->deserialized_state == SOCKET_START_POST || + s->deserialized_state == SOCKET_LISTENING || + s->deserialized_state == SOCKET_RUNNING || + s->deserialized_state == SOCKET_STOP_PRE || + s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || + s->deserialized_state == SOCKET_STOP_PRE_SIGKILL) + if ((r = socket_open_fds(s)) < 0) + return r; + + if (s->deserialized_state == SOCKET_LISTENING) + if ((r = socket_watch_fds(s)) < 0) + return r; + + socket_set_state(s, s->deserialized_state); + } + + return 0; +} + static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; @@ -574,6 +616,8 @@ static void socket_enter_stop_post(Socket *s, bool success) { socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_STOP_POST; + if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) { if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) goto fail; @@ -647,6 +691,8 @@ static void socket_enter_stop_pre(Socket *s, bool success) { socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_STOP_PRE; + if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) { if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) goto fail; @@ -689,6 +735,8 @@ static void socket_enter_start_post(Socket *s) { socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_POST; + if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) { log_warning("%s failed to run start-post executable: %s", s->meta.id, strerror(-r)); @@ -711,6 +759,8 @@ static void socket_enter_start_pre(Socket *s) { socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_PRE; + if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) { if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) goto fail; @@ -876,6 +926,142 @@ static int socket_stop(Unit *u) { return 0; } +static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { + Socket *s = SOCKET(u); + SocketPort *p; + int r; + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", socket_state_to_string(s->state)); + unit_serialize_item(u, f, "failure", yes_no(s->failure)); + unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted); + + if (s->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) s->control_pid); + + if (s->control_command_id >= 0) + unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id)); + + LIST_FOREACH(port, p, s->ports) { + int copy; + + if (p->fd < 0) + continue; + + if ((copy = fdset_put_dup(fds, p->fd)) < 0) + return copy; + + if (p->type == SOCKET_SOCKET) { + char *t; + + if ((r = socket_address_print(&p->address, &t)) < 0) + return r; + + unit_serialize_item_format(u, f, "socket", "%i %s", copy, t); + free(t); + } else { + assert(p->type == SOCKET_FIFO); + unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path); + } + } + + return 0; +} + +static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Socket *s = SOCKET(u); + int r; + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + SocketState state; + + if ((state = socket_state_from_string(value)) < 0) + log_debug("Failed to parse state value %s", value); + else + s->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 + s->failure = b || s->failure; + + } else if (streq(key, "n-accepted")) { + unsigned k; + + if ((r = safe_atou(value, &k)) < 0) + log_debug("Failed to parse n-accepted value %s", value); + else + s->n_accepted += k; + } else if (streq(key, "control-pid")) { + unsigned pid; + + if ((r = safe_atou(value, &pid)) < 0 || pid <= 0) + log_debug("Failed to parse control-pid value %s", value); + else + s->control_pid = (pid_t) pid; + } else if (streq(key, "control-command")) { + SocketExecCommand id; + + if ((id = socket_exec_command_from_string(value)) < 0) + log_debug("Failed to parse exec-command value %s", value); + else { + s->control_command_id = id; + s->control_command = s->exec_command[id]; + } + } else if (streq(key, "fifo")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse fifo value %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (streq(p->path, value+skip)) + break; + + if (p) { + if (p->fd >= 0) + close_nointr_nofail(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else if (streq(key, "socket")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse socket value %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (socket_address_is(&p->address, value+skip)) + break; + + if (p) { + if (p->fd >= 0) + close_nointr_nofail(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else + log_debug("Unknown serialization key '%s'", key); + + return 0; +} + static UnitActiveState socket_active_state(Unit *u) { assert(u); @@ -885,7 +1071,7 @@ static UnitActiveState socket_active_state(Unit *u) { static const char *socket_sub_state_to_string(Unit *u) { assert(u); - return state_string_table[SOCKET(u)->state]; + return socket_state_to_string(SOCKET(u)->state); } static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { @@ -918,6 +1104,7 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { } } + log_debug("cfd=%i", cfd); socket_enter_running(s, cfd); return; @@ -936,21 +1123,24 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->failure = s->failure || !success; assert(s->control_pid == pid); - assert(s->control_command); - - exec_status_fill(&s->control_command->exec_status, pid, code, status); s->control_pid = 0; + if (s->control_command) + exec_status_fill(&s->control_command->exec_status, pid, code, status); + log_debug("%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); - if (s->control_command->command_next && success) { - log_debug("%s running next command for state %s", u->meta.id, state_string_table[s->state]); + if (s->control_command && s->control_command->command_next && success) { + log_debug("%s running next command for state %s", u->meta.id, socket_state_to_string(s->state)); socket_run_next(s, success); } else { + s->control_command = NULL; + s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; + /* No further commands for this step, so let's figure * out what to do next */ - log_debug("%s got final SIGCHLD for state %s", u->meta.id, state_string_table[s->state]); + log_debug("%s got final SIGCHLD for state %s", u->meta.id, socket_state_to_string(s->state)); switch (s->state) { @@ -1082,18 +1272,49 @@ void socket_notify_service_dead(Socket *s) { } } +static const char* const socket_state_table[_SOCKET_STATE_MAX] = { + [SOCKET_DEAD] = "dead", + [SOCKET_START_PRE] = "start-pre", + [SOCKET_START_POST] = "start-post", + [SOCKET_LISTENING] = "listening", + [SOCKET_RUNNING] = "running", + [SOCKET_STOP_PRE] = "stop-pre", + [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", + [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", + [SOCKET_STOP_POST] = "stop-post", + [SOCKET_FINAL_SIGTERM] = "final-sigterm", + [SOCKET_FINAL_SIGKILL] = "final-sigkill", + [SOCKET_MAINTAINANCE] = "maintainance" +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState); + +static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { + [SOCKET_EXEC_START_PRE] = "StartPre", + [SOCKET_EXEC_START_POST] = "StartPost", + [SOCKET_EXEC_STOP_PRE] = "StopPre", + [SOCKET_EXEC_STOP_POST] = "StopPost" +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand); + const UnitVTable socket_vtable = { .suffix = ".socket", .init = socket_init, - .load = socket_load, .done = socket_done, + .load = socket_load, + + .coldplug = socket_coldplug, .dump = socket_dump, .start = socket_start, .stop = socket_stop, + .serialize = socket_serialize, + .deserialize_item = socket_deserialize_item, + .active_state = socket_active_state, .sub_state_to_string = socket_sub_state_to_string, diff --git a/socket.h b/socket.h index 5105adfb..eafd64d5 100644 --- a/socket.h +++ b/socket.h @@ -91,11 +91,12 @@ struct Socket { Service *service; - SocketState state; + SocketState state, deserialized_state; KillMode kill_mode; ExecCommand* control_command; + SocketExecCommand control_command_id; pid_t control_pid; char *bind_to_device; @@ -117,4 +118,10 @@ void socket_notify_service_dead(Socket *s); extern const UnitVTable socket_vtable; +const char* socket_state_to_string(SocketState i); +SocketState socket_state_from_string(const char *s); + +const char* socket_exec_command_to_string(SocketExecCommand i); +SocketExecCommand socket_exec_command_from_string(const char *s); + #endif diff --git a/systemctl.vala b/systemctl.vala index 4de856e7..c435e558 100644 --- a/systemctl.vala +++ b/systemctl.vala @@ -85,7 +85,9 @@ int main (string[] args) { " reload [NAME...] Reload on or more units\n" + " monitor Monitor unit/job changes\n" + " dump Dump servier status\n" + - " snapshot [NAME] Create a snapshot\n"); + " snapshot [NAME] Create a snapshot\n" + + " daemon-reload Reload daemon configuration\n" + + " daemon-reexecute Reexecute daemon\n"); try { context.parse(ref args); @@ -236,7 +238,13 @@ int main (string[] args) { "org.freedesktop.systemd1.Unit") as Unit; stdout.printf("%s\n", u.id); - } else { + } else if (args[1] == "daemon-reload") + manager.reload(); + else if (args[1] == "daemon-reexecute" || args[1] == "daemon-reexec") + manager.reexecute(); + else if (args[1] == "daemon-exit") + manager.exit(); + else { stderr.printf("Unknown command %s.\n", args[1]); return 1; } diff --git a/systemd-interfaces.vala b/systemd-interfaces.vala index 3b65d3c9..4e845c0b 100644 --- a/systemd-interfaces.vala +++ b/systemd-interfaces.vala @@ -57,6 +57,10 @@ public interface Manager : DBus.Object { public abstract string dump() throws DBus.Error; + public abstract void reload() throws DBus.Error; + public abstract void reexecute() throws DBus.Error; + public abstract void exit() throws DBus.Error; + public abstract ObjectPath create_snapshot(string name, bool cleanup = false) throws DBus.Error; public abstract signal void unit_new(string id, ObjectPath path); diff --git a/target.c b/target.c index 61cbebf6..32c2256e 100644 --- a/target.c +++ b/target.c @@ -33,22 +33,6 @@ static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = { [TARGET_ACTIVE] = UNIT_ACTIVE }; -static const char* const state_string_table[_TARGET_STATE_MAX] = { - [TARGET_DEAD] = "dead", - [TARGET_ACTIVE] = "active" -}; - -static void target_dump(Unit *u, FILE *f, const char *prefix) { - Target *t = TARGET(u); - - assert(t); - assert(f); - - fprintf(f, - "%sTarget State: %s\n", - prefix, state_string_table[t->state]); -} - static void target_set_state(Target *t, TargetState state) { TargetState old_state; assert(t); @@ -57,11 +41,37 @@ static void target_set_state(Target *t, TargetState state) { t->state = state; if (state != old_state) - log_debug("%s changed %s → %s", UNIT(t)->meta.id, state_string_table[old_state], state_string_table[state]); + log_debug("%s changed %s → %s", + UNIT(t)->meta.id, + target_state_to_string(old_state), + target_state_to_string(state)); unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]); } +static int target_coldplug(Unit *u) { + Target *t = TARGET(u); + + assert(t); + assert(t->state == TARGET_DEAD); + + if (t->deserialized_state != t->state) + target_set_state(t, t->deserialized_state); + + return 0; +} + +static void target_dump(Unit *u, FILE *f, const char *prefix) { + Target *t = TARGET(u); + + assert(t); + assert(f); + + fprintf(f, + "%sTarget State: %s\n", + prefix, target_state_to_string(t->state)); +} + static int target_start(Unit *u) { Target *t = TARGET(u); @@ -82,6 +92,39 @@ static int target_stop(Unit *u) { return 0; } +static int target_serialize(Unit *u, FILE *f, FDSet *fds) { + Target *s = TARGET(u); + + assert(s); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", target_state_to_string(s->state)); + return 0; +} + +static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Target *s = TARGET(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + TargetState state; + + if ((state = target_state_from_string(value)) < 0) + log_debug("Failed to parse state value %s", value); + else + s->deserialized_state = state; + + } else + log_debug("Unknown serialization key '%s'", key); + + return 0; +} + static UnitActiveState target_active_state(Unit *u) { assert(u); @@ -91,7 +134,7 @@ static UnitActiveState target_active_state(Unit *u) { static const char *target_sub_state_to_string(Unit *u) { assert(u); - return state_string_table[TARGET(u)->state]; + return target_state_to_string(TARGET(u)->state); } int target_get_runlevel(Target *t) { @@ -123,16 +166,27 @@ int target_get_runlevel(Target *t) { return 0; } +static const char* const target_state_table[_TARGET_STATE_MAX] = { + [TARGET_DEAD] = "dead", + [TARGET_ACTIVE] = "active" +}; + +DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState); + const UnitVTable target_vtable = { .suffix = ".target", .load = unit_load_fragment_and_dropin, + .coldplug = target_coldplug, .dump = target_dump, .start = target_start, .stop = target_stop, + .serialize = target_serialize, + .deserialize_item = target_deserialize_item, + .active_state = target_active_state, .sub_state_to_string = target_sub_state_to_string, diff --git a/target.h b/target.h index 4b04635f..5397d50d 100644 --- a/target.h +++ b/target.h @@ -36,11 +36,14 @@ typedef enum TargetState { struct Target { Meta meta; - TargetState state; + TargetState state, deserialized_state; }; extern const UnitVTable target_vtable; int target_get_runlevel(Target *t); +const char* target_state_to_string(TargetState i); +TargetState target_state_from_string(const char *s); + #endif diff --git a/unit-name.c b/unit-name.c index 5053d30f..5d428dd4 100644 --- a/unit-name.c +++ b/unit-name.c @@ -371,3 +371,43 @@ char *unit_name_template(const char *f) { return r; } + +char *unit_name_from_path(const char *path, const char *suffix) { + assert(path); + assert(suffix); + + if (path[0] == '/') + path++; + + if (path[0] == 0) + return strappend("-", suffix); + + return unit_name_build_escape(path, NULL, suffix); +} + +char *unit_name_to_path(const char *name) { + char *w, *e; + + assert(name); + + if (!(w = unit_name_to_prefix(name))) + return NULL; + + e = unit_name_unescape(w); + free(w); + + if (!e) + return NULL; + + if (e[0] != '/') { + w = strappend("/", e); + free(e); + + if (!w) + return NULL; + + e = w; + } + + return e; +} diff --git a/unit-name.h b/unit-name.h index 587741ba..b6dd2c91 100644 --- a/unit-name.h +++ b/unit-name.h @@ -48,4 +48,7 @@ char *unit_name_replace_instance(const char *f, const char *i); char *unit_name_template(const char *f); +char *unit_name_from_path(const char *path, const char *suffix); +char *unit_name_to_path(const char *name); + #endif diff --git a/unit.c b/unit.c index f8589fe6..0d459d67 100644 --- a/unit.c +++ b/unit.c @@ -615,6 +615,16 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { return 0; } +/* Common implementation for multiple backends */ +int unit_load_nop(Unit *u) { + assert(u); + + if (u->meta.load_state == UNIT_STUB) + u->meta.load_state = UNIT_LOADED; + + return 0; +} + int unit_load(Unit *u) { int r; @@ -1109,7 +1119,7 @@ void unit_unwatch_timer(Unit *u, Watch *w) { assert(w->type == WATCH_TIMER && w->data.unit == u); assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); - assert_se(close_nointr(w->fd) == 0); + close_nointr_nofail(w->fd); w->fd = -1; w->type = WATCH_INVALID; @@ -1495,6 +1505,29 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { return r; } +int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { + Unit *found; + char *t; + + assert(u); + assert(type); + assert(_found); + + if (!(t = unit_name_change_suffix(u->meta.id, type))) + return -ENOMEM; + + assert(!unit_has_name(u, t)); + + found = manager_get_unit(u->meta.manager, t); + free(t); + + if (!found) + return -ENOENT; + + *_found = found; + return 0; +} + static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) { Unit *u = userdata; assert(u); @@ -1627,6 +1660,97 @@ void unit_unwatch_bus_name(Unit *u, const char *name) { hashmap_remove_value(u->meta.manager->watch_bus, name, u); } +bool unit_can_serialize(Unit *u) { + assert(u); + + return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item; +} + +int unit_serialize(Unit *u, FILE *f, FDSet *fds) { + int r; + + assert(u); + assert(f); + assert(fds); + + if (!unit_can_serialize(u)) + return 0; + + if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0) + return r; + + /* End marker */ + fputc('\n', f); + return 0; +} + +void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) { + va_list ap; + + assert(u); + assert(f); + assert(key); + assert(format); + + fputs(key, f); + fputc('=', f); + + va_start(ap, format); + vfprintf(f, format, ap); + va_end(ap); + + fputc('\n', f); +} + +void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) { + assert(u); + assert(f); + assert(key); + assert(value); + + fprintf(f, "%s=%s\n", key, value); +} + +int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + int r; + + assert(u); + assert(f); + assert(fds); + + if (!unit_can_serialize(u)) + return 0; + + for (;;) { + char line[1024], *l, *v; + size_t k; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + return 0; + return -errno; + } + + l = strstrip(line); + + /* End marker */ + if (l[0] == 0) + return 0; + + k = strcspn(l, "="); + + if (l[k] == '=') { + l[k] = 0; + v = l+k+1; + } else + v = l+k; + + if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0) + return r; + } +} + + static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "service", [UNIT_TIMER] = "timer", diff --git a/unit.h b/unit.h index b3c75ce2..76aa0eff 100644 --- a/unit.h +++ b/unit.h @@ -209,24 +209,24 @@ struct UnitVTable { /* Instances make no sense for this type */ bool no_instances:1; - /* Execlude this type from snapshots */ + /* Exclude this type from snapshots */ bool no_snapshots: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() */ + * not allocate memory, and is called with zero-initialized + * data. It should hence only initialize variables that need + * to be set != 0. */ void (*init)(Unit *u); + /* This should free all type-specific variables. It should be + * idempotent. */ + void (*done)(Unit *u); + /* Actually load data from disk. This may fail, and should set * load_state to UNIT_LOADED, UNIT_MERGED or leave it at * UNIT_STUB if no configuration could be found. */ int (*load)(Unit *u); - /* This should free all type-specific variables. It should be - * idempotent. There's no need to reset variables that deal - * with dynamic memory/resources. */ - void (*done)(Unit *u); - /* If a a lot of units got created via enumerate(), this is * where to actually set the state and call unit_notify(). */ int (*coldplug)(Unit *u); @@ -239,6 +239,13 @@ struct UnitVTable { bool (*can_reload)(Unit *u); + /* Write all data that cannot be restored from other sources + * away using unit_serialize_item() */ + int (*serialize)(Unit *u, FILE *f, FDSet *fds); + + /* Restore one item from the serialization */ + int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds); + /* Boils down the more complex internal state of this unit to * a simpler one that the engine can understand */ UnitActiveState (*active_state)(Unit *u); @@ -333,6 +340,7 @@ Unit *unit_follow_merge(Unit *u); int unit_load_fragment_and_dropin(Unit *u); int unit_load_fragment_and_dropin_optional(Unit *u); +int unit_load_nop(Unit *u); int unit_load(Unit *unit); const char *unit_description(Unit *u); @@ -373,11 +381,18 @@ int set_unit_path(const char *p); char *unit_dbus_path(Unit *u); int unit_load_related_unit(Unit *u, const char *type, Unit **_found); +int unit_get_related_unit(Unit *u, const char *type, Unit **_found); char *unit_name_printf(Unit *u, const char* text); char *unit_full_printf(Unit *u, const char *text); char **unit_full_printf_strv(Unit *u, char **l); +bool unit_can_serialize(Unit *u); +int unit_serialize(Unit *u, FILE *f, FDSet *fds); +void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr(4,5); +void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value); +int unit_deserialize(Unit *u, FILE *f, FDSet *fds); + const char *unit_type_to_string(UnitType i); UnitType unit_type_from_string(const char *s); diff --git a/util.c b/util.c index dd4dc097..49f5b4be 100644 --- a/util.c +++ b/util.c @@ -1215,7 +1215,7 @@ int close_all_fds(const int except[], unsigned n_except) { while ((de = readdir(d))) { int fd = -1; - if (de->d_name[0] == '.') + if (ignore_file(de->d_name)) continue; if ((r = safe_atoi(de->d_name, &fd)) < 0) @@ -1324,7 +1324,7 @@ int chvt(int vt) { if (ioctl(fd, VT_ACTIVATE, vt) < 0) r = -errno; - close_nointr(r); + close_nointr_nofail(r); return r; } @@ -1612,7 +1612,7 @@ int acquire_terminal(const char *name, bool fail, bool force) { } if (notify >= 0) - close_nointr(notify); + close_nointr_nofail(notify); if ((r = reset_terminal(fd)) < 0) log_warning("Failed to reset terminal: %s", strerror(-r)); @@ -1621,10 +1621,10 @@ int acquire_terminal(const char *name, bool fail, bool force) { fail: if (fd >= 0) - close_nointr(fd); + close_nointr_nofail(fd); if (notify >= 0) - close_nointr(notify); + close_nointr_nofail(notify); return r; }