chiark / gitweb /
reload: implement reload/reexec logic
authorLennart Poettering <lennart@poettering.net>
Wed, 21 Apr 2010 01:27:44 +0000 (03:27 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 21 Apr 2010 01:27:44 +0000 (03:27 +0200)
37 files changed:
Makefile.am
automount.c
automount.h
cgroup.c
cgroup.h
dbus-manager.c
dbus.c
device.c
device.h
execute.c
fdset.c [new file with mode: 0644]
fdset.h [new file with mode: 0644]
initctl.c
log.c
logger.c
main.c
manager.c
manager.h
mount.c
mount.h
service.c
service.h
snapshot.c
snapshot.h
socket-util.c
socket-util.h
socket.c
socket.h
systemctl.vala
systemd-interfaces.vala
target.c
target.h
unit-name.c
unit-name.h
unit.c
unit.h
util.c

index 8720e7898fae37f85bd078b2f59666e61c6180aa..567490ea63b6a5136c3bdca2471bf60f1baee9f8 100644 (file)
@@ -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) \
index fa5fd52552afc9af95ee63a1301bfa9f024ce70f..7aa55b4f9f0999ca4fc86a0234031d9b776ff9fb 100644 (file)
@@ -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,
 
index a8c81e17f2b84a7bdd971c348bee39bf69943937..5b623691d4c9a5c09e9d4ba6d708e9a3fdab392a 100644 (file)
@@ -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
index 4ceaf382771a803e0a692c26f8d791bfb28ca3d0..83c5d4028dfed048c99912ae01b7ec82675f2570 100644 (file)
--- 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:
index 6b677d535d8caf69d431122fc56e566ee4ca7263..e290ffee9c14947b59c9c47ccf450d0bdab42bd3 100644 (file)
--- 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);
 
index 734ed7b886abdd601ebad55e0d5bb965e04e6144..a8bf7e52c95f07bd505b6e0f8008f34bd001b0c5 100644 (file)
@@ -56,6 +56,9 @@
         "   <arg nane=\"cleanup\" type=\"b\" direction=\"in\"/>"        \
         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>"          \
         "  </method>"                                                   \
+        "  <method name=\"Reload\"/>"                                   \
+        "  <method name=\"Reexecute\"/>"                                \
+        "  <method name=\"Exit\"/>"                                     \
         "  <signal name=\"UnitNew\">"                                   \
         "   <arg name=\"id\" type=\"s\"/>"                              \
         "   <arg name=\"unit\" type=\"o\"/>"                            \
@@ -504,6 +507,37 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection  *connection
 
                 free(introspection);
 
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
+
+                assert(!m->queued_message);
+
+                /* Instead of sending the reply back right away, we
+                 * just remember that we need to and then send it
+                 * after the reload is finished. That way the caller
+                 * knows when the reload finished. */
+
+                if (!(m->queued_message = dbus_message_new_method_return(message)))
+                        goto oom;
+
+                m->exit_code = MANAGER_RELOAD;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
+
+                if (!(reply = dbus_message_new_method_return(message)))
+                        goto oom;
+
+                m->exit_code = MANAGER_REEXECUTE;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
+
+                if (m->running_as == MANAGER_INIT)
+                        return bus_send_error_reply(m, message, NULL, -ENOTSUP);
+
+                if (!(reply = dbus_message_new_method_return(message)))
+                        goto oom;
+
+                m->exit_code = MANAGER_EXIT;
+
         } else
                 return bus_default_message_handler(m, message, NULL, properties);
 
diff --git a/dbus.c b/dbus.c
index 6b7896b605527f7fc865bffc34d51348f5a54965..0054d1519e3bfdfd936dc0edec533f7b4a52063e 100644 (file)
--- 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) {
index ea33101b7f31a9f8c089ca85570c12e15181e27c..4812a8687886cef49b26e1de5e5bc3dcd64b8937 100644 (file)
--- 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,
index b9ca22d1983c8696bcfc7a5d7e1abbdb9208f927..a5c5f745b81f712dfea4e582e7205269c0a54b8c 100644 (file)
--- 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
index 357fd5c20874aea7a6c670b89f7e737f64aa3fb1..38547677cfe758bc5b5858a6b9cccd34a45e161b 100644 (file)
--- 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 (file)
index 0000000..b6d5286
--- /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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..3483fc8
--- /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 <http://www.gnu.org/licenses/>.
+***/
+
+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
index a4356759c2e03fcb1e67c83a9a6599efcb38bb28..96a4d9900396f0ee15775ff4f9854e47c2dbda9d 100644 (file)
--- 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 b82018f28b509c15091642665aad038879ea1f05..d8cc4048c4fec2232d41afce7b2a75b43cb2913c 100644 (file)
--- 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;
         }
 }
index 7194320c4760f091d4fca4c43c689d26a45c7872..ba325c696c80c71ceeb9feeb36987e844a0a83c1 100644 (file)
--- 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 c473ced2b41c04e0e2bd01e1424423ddd0e6c582..29be801125616f773878129a17af20735ca291f7 100644 (file)
--- 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();
 
index 0187d3b374650cb34550268f514831c77bb35be4..9458aa51baec9d61490d121822c937270695e9e1 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -35,6 +35,8 @@
 #include <libcgroup.h>
 #include <termios.h>
 #include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #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",
index 77e55339cd1baf312ae3568d712c4c2183a6dc6b..8a48c5519cea30278f7f8cba11bf53340036468b 100644 (file)
--- a/manager.h
+++ b/manager.h
 #include <stdbool.h>
 #include <inttypes.h>
 #include <stdio.h>
-
 #include <dbus/dbus.h>
 
+#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 d3e222d4bc83be173eb97582af266b1301fe9ae4..187c6a373a2888de4761bbd31c71b2ed7531a740 100644 (file)
--- 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 66992e10eb9024483f57e6eecbf750f4ebd57e95..3b28e89ed3ecac2a3a94c21f0b0a91f00ba8ea05 100644 (file)
--- 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
index f334027a7986b094f8bc38f6f01337fcaa410b7b..25641768a94606551ee190d2dff9da91f9ccee94 100644 (file)
--- 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,
 
index e603ff74b90b03d73a5520a5df1487e2a7f76dd9..dc597aa0e92dfeb6d2d3dbb3fdbfa90e0234850e 100644 (file)
--- 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;
 
index 1fc0ab8164d82656026ab5286e939749c7a2f0f9..d0aaa51fee48268be8c353f04c1ceb4ae2975a6b 100644 (file)
@@ -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,
 
index 78bcafa448c24075edb3e622f8b4c8419f49baf2..959a5090ecf318d0242191cf954b61788d55a660 100644 (file)
@@ -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
index cd5ab82a2307d756f3aa6addfbd7e7642f85e573..8141ab09ebcd3fa0c08d4a715c1e873f84c2b262 100644 (file)
@@ -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);
+
+}
index e6e1b30b53014b56367794b92a94b23e8f1786ce..af452efddfd6d38f793f728689820efee0883028 100644 (file)
@@ -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
index 22658dcb009c3d273c418f35cfa67dcdf76c7c0c..63346a2bfc7c71630fa6a360d432d7e307e30602 100644 (file)
--- 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,
 
index 5105adfb84762c82393423ef329727dabb9c31c3..eafd64d5e36328839e1f817cd6fc39647ef50d23 100644 (file)
--- 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
index 4de856e756feeb569402167eee2a4c901ad79cc6..c435e558e823acdfe32b20e344c03b1faf90e1b7 100644 (file)
@@ -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;
                 }
index 3b65d3c90b3f2ecd0e352c7e20f444fccfcf6bff..4e845c0bbf39b6d73274e8f2be877924bdc863aa 100644 (file)
@@ -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);
index 61cbebf654bc900b8340b6e24db4ca4f67b067e8..32c2256e2756277d81fdafdf2b3844df3065f3ca 100644 (file)
--- 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,
 
index 4b04635f942a263bd4fd69381992629b3d9d25fb..5397d50d7c1069486cdef29e5c088025fb52b09a 100644 (file)
--- 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
index 5053d30f29a4721a6b5b43671a8400cacff93de8..5d428dd446af79e1049846e3491dacf4309c804c 100644 (file)
@@ -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;
+}
index 587741ba41af6969bc7a8150e5e318cdac84c6b3..b6dd2c912351e0a1ec1b4bcaf239f612aa95c25d 100644 (file)
@@ -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 f8589fe60569fc4c06b2d1afc4a2a9b92c970780..0d459d6760d8e09661bace80dd12cad715df3e78 100644 (file)
--- 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 b3c75ce20a7e5451a2e35653f308eee755d00bc2..76aa0eff5f46676139df7a1e77fd91d9ffa2dee8 100644 (file)
--- 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 dd4dc097a95aaf2f129cec8076e5bdb14db8c3d7..49f5b4be13f46723b48da397487a7c79dc4419e3 100644 (file)
--- 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;
 }