summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2010-04-21 03:27:44 +0200
committerLennart Poettering <lennart@poettering.net>2010-04-21 03:27:44 +0200
commita16e112358ea8fea381ee106b89e645aed8b0a8c (patch)
treef38b204ec041c25974397a7711a105cbe7509c07
parent0d9068141e07ba29483ebe8bc4aaed6aacde1562 (diff)
reload: implement reload/reexec logic
-rw-r--r--Makefile.am10
-rw-r--r--automount.c208
-rw-r--r--automount.h5
-rw-r--r--cgroup.c11
-rw-r--r--cgroup.h2
-rw-r--r--dbus-manager.c34
-rw-r--r--dbus.c17
-rw-r--r--device.c81
-rw-r--r--device.h3
-rw-r--r--execute.c21
-rw-r--r--fdset.c162
-rw-r--r--fdset.h40
-rw-r--r--initctl.c4
-rw-r--r--log.c4
-rw-r--r--logger.c14
-rw-r--r--main.c256
-rw-r--r--manager.c248
-rw-r--r--manager.h27
-rw-r--r--mount.c407
-rw-r--r--mount.h9
-rw-r--r--service.c226
-rw-r--r--service.h3
-rw-r--r--snapshot.c151
-rw-r--r--snapshot.h5
-rw-r--r--socket-util.c78
-rw-r--r--socket-util.h4
-rw-r--r--socket.c331
-rw-r--r--socket.h9
-rw-r--r--systemctl.vala12
-rw-r--r--systemd-interfaces.vala4
-rw-r--r--target.c90
-rw-r--r--target.h5
-rw-r--r--unit-name.c40
-rw-r--r--unit-name.h3
-rw-r--r--unit.c126
-rw-r--r--unit.h31
-rw-r--r--util.c10
37 files changed, 2193 insertions, 498 deletions
diff --git a/Makefile.am b/Makefile.am
index 8720e7898f..567490ea63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,7 +32,11 @@ AM_CPPFLAGS = \
-DSYSTEM_SYSVRCND_PATH=\"$(SYSTEM_SYSVRCND_PATH)\" \
-DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
- -DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\"
+ -DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\" \
+ -DSYSTEMD_BINARY_PATH=\"$(sbindir)/systemd\"
+
+# -DSYSTEMD_BINARY_PATH=\"/home/lennart/projects/systemd/systemd\"
+
sbin_PROGRAMS = \
systemd
@@ -155,7 +159,9 @@ COMMON_SOURCES= \
specifier.c \
specifier.h \
unit-name.c \
- unit-name.h
+ unit-name.h \
+ fdset.c \
+ fdset.h
systemd_SOURCES = \
$(COMMON_SOURCES) \
diff --git a/automount.c b/automount.c
index fa5fd52552..7aa55b4f9f 100644
--- a/automount.c
+++ b/automount.c
@@ -43,21 +43,7 @@ static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_MAINTAINANCE] = UNIT_INACTIVE,
};
-static const char* const state_string_table[_AUTOMOUNT_STATE_MAX] = {
- [AUTOMOUNT_DEAD] = "dead",
- [AUTOMOUNT_WAITING] = "waiting",
- [AUTOMOUNT_RUNNING] = "running",
- [AUTOMOUNT_MAINTAINANCE] = "maintainance"
-};
-
-static char *automount_name_from_where(const char *where) {
- assert(where);
-
- if (streq(where, "/"))
- return strdup("-.automount");
-
- return unit_name_build_escape(where+1, NULL, ".automount");
-}
+static int open_dev_autofs(Manager *m);
static void automount_init(Unit *u) {
Automount *a = AUTOMOUNT(u);
@@ -66,6 +52,7 @@ static void automount_init(Unit *u) {
assert(u->meta.load_state == UNIT_STUB);
a->pipe_watch.fd = a->pipe_fd = -1;
+ a->pipe_watch.type = WATCH_INVALID;
}
static void repeat_unmout(const char *path) {
@@ -95,7 +82,12 @@ static void unmount_autofs(Automount *a) {
close_nointr_nofail(a->pipe_fd);
a->pipe_fd = -1;
- repeat_unmout(a->where);
+ /* If we reload/reexecute things we keep the mount point
+ * around */
+ if (a->where &&
+ (UNIT(a)->meta.manager->exit_code != MANAGER_RELOAD &&
+ UNIT(a)->meta.manager->exit_code != MANAGER_REEXECUTE))
+ repeat_unmout(a->where);
}
static void automount_done(Unit *u) {
@@ -106,10 +98,11 @@ static void automount_done(Unit *u) {
unmount_autofs(a);
a->mount = NULL;
- if (a->tokens) {
- set_free(a->tokens);
- a->tokens = NULL;
- }
+ free(a->where);
+ a->where = NULL;
+
+ set_free(a->tokens);
+ a->tokens = NULL;
}
static int automount_verify(Automount *a) {
@@ -120,14 +113,7 @@ static int automount_verify(Automount *a) {
if (UNIT(a)->meta.load_state != UNIT_LOADED)
return 0;
- if (!a->where) {
- log_error("%s lacks Where setting. Refusing.", UNIT(a)->meta.id);
- return -EINVAL;
- }
-
- path_kill_slashes(a->where);
-
- if (!(e = automount_name_from_where(a->where)))
+ if (!(e = unit_name_from_path(a->where, ".automount")))
return -ENOMEM;
b = unit_has_name(UNIT(a), e);
@@ -154,6 +140,12 @@ static int automount_load(Unit *u) {
if (u->meta.load_state == UNIT_LOADED) {
+ if (!a->where)
+ if (!(a->where = unit_name_to_path(u->meta.id)))
+ return -ENOMEM;
+
+ path_kill_slashes(a->where);
+
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
return r;
@@ -176,19 +168,51 @@ static void automount_set_state(Automount *a, AutomountState state) {
unmount_autofs(a);
if (state != old_state)
- log_debug("%s changed %s → %s", UNIT(a)->meta.id, state_string_table[old_state], state_string_table[state]);
+ log_debug("%s changed %s → %s",
+ UNIT(a)->meta.id,
+ automount_state_to_string(old_state),
+ automount_state_to_string(state));
unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state]);
}
+static int automount_coldplug(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+ int r;
+
+ assert(a);
+ assert(a->state == AUTOMOUNT_DEAD);
+
+ if (a->deserialized_state != a->state) {
+
+ if ((r = open_dev_autofs(u->meta.manager)) < 0)
+ return r;
+
+ if (a->deserialized_state == AUTOMOUNT_WAITING ||
+ a->deserialized_state == AUTOMOUNT_RUNNING) {
+
+ assert(a->pipe_fd >= 0);
+
+ if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0)
+ return r;
+ }
+
+ automount_set_state(a, a->deserialized_state);
+ }
+
+ return 0;
+}
+
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
- Automount *s = AUTOMOUNT(u);
+ Automount *a = AUTOMOUNT(u);
- assert(s);
+ assert(a);
fprintf(f,
- "%sAutomount State: %s\n",
- prefix, state_string_table[s->state]);
+ "%sAutomount State: %s\n"
+ "%sWhere: %s\n",
+ prefix, automount_state_to_string(a->state),
+ prefix, a->where);
}
static void automount_enter_dead(Automount *a, bool success) {
@@ -208,7 +232,7 @@ static int open_dev_autofs(Manager *m) {
if (m->dev_autofs_fd >= 0)
return m->dev_autofs_fd;
- if ((m->dev_autofs_fd = open("/dev/autofs", O_RDONLY)) < 0) {
+ if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) {
log_error("Failed to open /dev/autofs: %s", strerror(errno));
return -errno;
}
@@ -254,6 +278,7 @@ static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
goto finish;
}
+ fd_cloexec(param->ioctlfd, true);
r = param->ioctlfd;
finish:
@@ -383,10 +408,6 @@ static void automount_enter_waiting(Automount *a) {
if (a->tokens)
set_clear(a->tokens);
- else if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->meta.manager)) < 0) {
r = dev_autofs_fd;
@@ -396,7 +417,7 @@ static void automount_enter_waiting(Automount *a) {
/* We knowingly ignore the results of this call */
mkdir_p(a->where, 0555);
- if (pipe2(p, O_NONBLOCK) < 0) {
+ if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
r = -errno;
goto fail;
}
@@ -521,7 +542,94 @@ static int automount_stop(Unit *u) {
return 0;
}
+static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Automount *a = AUTOMOUNT(u);
+ void *p;
+ Iterator i;
+
+ assert(a);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", automount_state_to_string(a->state));
+ unit_serialize_item(u, f, "failure", yes_no(a->failure));
+ unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id);
+
+ SET_FOREACH(p, a->tokens, i)
+ unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
+
+ if (a->pipe_fd >= 0) {
+ int copy;
+
+ if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0)
+ return copy;
+
+ unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
+ }
+
+ return 0;
+}
+
+static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Automount *a = AUTOMOUNT(u);
+ int r;
+
+ assert(a);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ AutomountState state;
+
+ if ((state = automount_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ a->deserialized_state = state;
+ } else if (streq(key, "failure")) {
+ int b;
+
+ if ((b = parse_boolean(value)) < 0)
+ log_debug("Failed to parse failure value %s", value);
+ else
+ a->failure = b || a->failure;
+ } else if (streq(key, "dev-id")) {
+ unsigned d;
+
+ if (safe_atou(value, &d) < 0)
+ log_debug("Failed to parse dev-id value %s", value);
+ else
+ a->dev_id = (unsigned) d;
+ } else if (streq(key, "token")) {
+ unsigned token;
+
+ if (safe_atou(value, &token) < 0)
+ log_debug("Failed to parse token value %s", value);
+ else {
+ if (!a->tokens)
+ if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func)))
+ return -ENOMEM;
+
+ if ((r = set_put(a->tokens, UINT_TO_PTR(token))) < 0)
+ return r;
+ }
+ } else if (streq(key, "pipe-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse pipe-fd value %s", value);
+ else {
+ if (a->pipe_fd >= 0)
+ close_nointr_nofail(a->pipe_fd);
+
+ a->pipe_fd = fdset_remove(fds, fd);
+ }
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
static UnitActiveState automount_active_state(Unit *u) {
+ assert(u);
return state_translation_table[AUTOMOUNT(u)->state];
}
@@ -529,7 +637,7 @@ static UnitActiveState automount_active_state(Unit *u) {
static const char *automount_sub_state_to_string(Unit *u) {
assert(u);
- return state_string_table[AUTOMOUNT(u)->state];
+ return automount_state_to_string(AUTOMOUNT(u)->state);
}
static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
@@ -557,6 +665,12 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
case autofs_ptype_missing_direct:
log_debug("Got direct mount request for %s", packet.v5_packet.name);
+ if (!a->tokens)
+ if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
+ log_error("Failed to allocate token set.");
+ goto fail;
+ }
+
if ((r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token))) < 0) {
log_error("Failed to remember token: %s", strerror(-r));
goto fail;
@@ -583,6 +697,15 @@ static void automount_shutdown(Manager *m) {
close_nointr_nofail(m->dev_autofs_fd);
}
+static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
+ [AUTOMOUNT_DEAD] = "dead",
+ [AUTOMOUNT_WAITING] = "waiting",
+ [AUTOMOUNT_RUNNING] = "running",
+ [AUTOMOUNT_MAINTAINANCE] = "maintainance"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
+
const UnitVTable automount_vtable = {
.suffix = ".automount",
@@ -593,11 +716,16 @@ const UnitVTable automount_vtable = {
.load = automount_load,
.done = automount_done,
+ .coldplug = automount_coldplug,
+
.dump = automount_dump,
.start = automount_start,
.stop = automount_stop,
+ .serialize = automount_serialize,
+ .deserialize_item = automount_deserialize_item,
+
.active_state = automount_active_state,
.sub_state_to_string = automount_sub_state_to_string,
diff --git a/automount.h b/automount.h
index a8c81e17f2..5b623691d4 100644
--- a/automount.h
+++ b/automount.h
@@ -38,7 +38,7 @@ typedef enum AutomountState {
struct Automount {
Meta meta;
- AutomountState state;
+ AutomountState state, deserialized_state;
char *where;
@@ -57,4 +57,7 @@ extern const UnitVTable automount_vtable;
int automount_send_ready(Automount *a, int status);
+const char* automount_state_to_string(AutomountState i);
+AutomountState automount_state_from_string(const char *s);
+
#endif
diff --git a/cgroup.c b/cgroup.c
index 4ceaf38277..83c5d4028d 100644
--- a/cgroup.c
+++ b/cgroup.c
@@ -478,7 +478,7 @@ int manager_setup_cgroup(Manager *m) {
return r;
}
-int manager_shutdown_cgroup(Manager *m) {
+int manager_shutdown_cgroup(Manager *m, bool delete) {
struct cgroup *cg;
int r;
@@ -495,11 +495,10 @@ int manager_shutdown_cgroup(Manager *m) {
goto finish;
}
- if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) {
- log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r));
- r = translate_error(r, errno);
- goto finish;
- }
+ /* Often enough we won't be able to delete the cgroup we
+ * ourselves are in, hence ignore all errors here */
+ if (delete)
+ cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE);
r = 0;
finish:
diff --git a/cgroup.h b/cgroup.h
index 6b677d535d..e290ffee9c 100644
--- a/cgroup.h
+++ b/cgroup.h
@@ -75,7 +75,7 @@ char *cgroup_bonding_to_string(CGroupBonding *b);
#include "manager.h"
int manager_setup_cgroup(Manager *m);
-int manager_shutdown_cgroup(Manager *m);
+int manager_shutdown_cgroup(Manager *m, bool delete);
int cgroup_notify_empty(Manager *m, const char *group);
diff --git a/dbus-manager.c b/dbus-manager.c
index 734ed7b886..a8bf7e52c9 100644
--- a/dbus-manager.c
+++ b/dbus-manager.c
@@ -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 6b7896b605..0054d1519e 100644
--- a/dbus.c
+++ b/dbus.c
@@ -381,6 +381,18 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection,
unsigned bus_dispatch(Manager *m) {
assert(m);
+ if (m->queued_message) {
+ /* If we cannot get rid of this message we won't
+ * dispatch any D-Bus messages, so that we won't end
+ * up wanting to queue another message. */
+
+ if (!dbus_connection_send(m->api_bus, m->queued_message, NULL))
+ return 0;
+
+ dbus_message_unref(m->queued_message);
+ m->queued_message = NULL;
+ }
+
if (m->request_api_bus_dispatch) {
if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE)
m->request_api_bus_dispatch = false;
@@ -655,6 +667,11 @@ void bus_done_api(Manager *m) {
if (m->name_data_slot >= 0)
dbus_pending_call_free_data_slot(&m->name_data_slot);
+
+ if (m->queued_message) {
+ dbus_message_unref(m->queued_message);
+ m->queued_message = NULL;
+ }
}
void bus_done_system(Manager *m) {
diff --git a/device.c b/device.c
index ea33101b7f..4812a86878 100644
--- a/device.c
+++ b/device.c
@@ -35,11 +35,6 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
[DEVICE_AVAILABLE] = UNIT_ACTIVE
};
-static const char* const state_string_table[_DEVICE_STATE_MAX] = {
- [DEVICE_DEAD] = "dead",
- [DEVICE_AVAILABLE] = "available"
-};
-
static void device_done(Unit *u) {
Device *d = DEVICE(u);
@@ -49,15 +44,6 @@ static void device_done(Unit *u) {
d->sysfs = NULL;
}
-static void device_init(Unit *u) {
- Device *d = DEVICE(u);
-
- assert(d);
- assert(u->meta.load_state == UNIT_STUB);
-
- d->state = 0;
-}
-
static void device_set_state(Device *d, DeviceState state) {
DeviceState old_state;
assert(d);
@@ -66,7 +52,10 @@ static void device_set_state(Device *d, DeviceState state) {
d->state = state;
if (state != old_state)
- log_debug("%s changed %s → %s", UNIT(d)->meta.id, state_string_table[old_state], state_string_table[state]);
+ log_debug("%s changed %s → %s",
+ UNIT(d)->meta.id,
+ device_state_to_string(old_state),
+ device_state_to_string(state));
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
}
@@ -91,7 +80,7 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f,
"%sDevice State: %s\n"
"%sSysfs Path: %s\n",
- prefix, state_string_table[d->state],
+ prefix, device_state_to_string(d->state),
prefix, strna(d->sysfs));
}
@@ -104,7 +93,7 @@ static UnitActiveState device_active_state(Unit *u) {
static const char *device_sub_state_to_string(Unit *u) {
assert(u);
- return state_string_table[DEVICE(u)->state];
+ return device_state_to_string(DEVICE(u)->state);
}
static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
@@ -115,7 +104,7 @@ static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
assert(dn);
assert(dn[0] == '/');
- if (!(e = unit_name_build_escape(dn+1, NULL, ".device")))
+ if (!(e = unit_name_from_path(dn, ".device")))
return -ENOMEM;
r = unit_add_name(u, e);
@@ -140,7 +129,7 @@ static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
assert(dn[0] == '/');
assert(_u);
- if (!(e = unit_name_build_escape(dn+1, NULL, ".device")))
+ if (!(e = unit_name_from_path(dn, ".device")))
return -ENOMEM;
u = manager_get_unit(m, e);
@@ -308,7 +297,7 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) {
return -ENOMEM;
assert(sysfs[0] == '/');
- if (!(e = unit_name_build_escape(sysfs+1, NULL, ".device")))
+ if (!(e = unit_name_from_path(sysfs, ".device")))
return -ENOMEM;
u = manager_get_unit(m, e);
@@ -328,11 +317,15 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) {
static void device_shutdown(Manager *m) {
assert(m);
- if (m->udev_monitor)
+ if (m->udev_monitor) {
udev_monitor_unref(m->udev_monitor);
+ m->udev_monitor = NULL;
+ }
- if (m->udev)
+ if (m->udev) {
udev_unref(m->udev);
+ m->udev = NULL;
+ }
}
static int device_enumerate(Manager *m) {
@@ -343,28 +336,30 @@ static int device_enumerate(Manager *m) {
assert(m);
- if (!(m->udev = udev_new()))
- return -ENOMEM;
+ if (!m->udev) {
+ if (!(m->udev = udev_new()))
+ return -ENOMEM;
- if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
+ r = -ENOMEM;
+ goto fail;
+ }
- if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
- r = -EIO;
- goto fail;
- }
+ if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
+ r = -EIO;
+ goto fail;
+ }
- m->udev_watch.type = WATCH_UDEV;
- m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
+ m->udev_watch.type = WATCH_UDEV;
+ m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->udev_watch;
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.ptr = &m->udev_watch;
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
- return -errno;
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
+ return -errno;
+ }
if (!(e = udev_enumerate_new(m->udev))) {
r = -ENOMEM;
@@ -425,6 +420,13 @@ fail:
udev_device_unref(dev);
}
+static const char* const device_state_table[_DEVICE_STATE_MAX] = {
+ [DEVICE_DEAD] = "dead",
+ [DEVICE_AVAILABLE] = "available"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
+
const UnitVTable device_vtable = {
.suffix = ".device",
@@ -432,7 +434,6 @@ const UnitVTable device_vtable = {
.no_instances = true,
.no_snapshots = true,
- .init = device_init,
.load = unit_load_fragment_and_dropin_optional,
.done = device_done,
.coldplug = device_coldplug,
diff --git a/device.h b/device.h
index b9ca22d198..a5c5f745b8 100644
--- a/device.h
+++ b/device.h
@@ -47,4 +47,7 @@ extern const UnitVTable device_vtable;
void device_fd_event(Manager *m, int events);
+const char* device_state_to_string(DeviceState i);
+DeviceState device_state_from_string(const char *s);
+
#endif
diff --git a/execute.c b/execute.c
index 357fd5c208..38547677cf 100644
--- a/execute.c
+++ b/execute.c
@@ -1063,30 +1063,9 @@ void exec_context_init(ExecContext *c) {
assert(c);
c->umask = 0002;
- c->oom_adjust = 0;
- c->oom_adjust_set = false;
- c->nice = 0;
- c->nice_set = false;
c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
- c->ioprio_set = false;
c->cpu_sched_policy = SCHED_OTHER;
- c->cpu_sched_priority = 0;
- c->cpu_sched_set = false;
- CPU_ZERO(&c->cpu_affinity);
- c->cpu_affinity_set = false;
- c->timer_slack_ns = 0;
- c->timer_slack_ns_set = false;
-
- c->cpu_sched_reset_on_fork = false;
- c->non_blocking = false;
-
- c->std_input = 0;
- c->std_output = 0;
- c->std_error = 0;
c->syslog_priority = LOG_DAEMON|LOG_INFO;
-
- c->secure_bits = 0;
- c->capability_bounding_set_drop = 0;
}
void exec_context_done(ExecContext *c) {
diff --git a/fdset.c b/fdset.c
new file mode 100644
index 0000000000..b6d5286f82
--- /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
index 0000000000..3483fc8323
--- /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
diff --git a/initctl.c b/initctl.c
index a4356759c2..96a4d99003 100644
--- a/initctl.c
+++ b/initctl.c
@@ -246,7 +246,7 @@ static void fifo_free(Fifo *f) {
if (f->server)
epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
- assert_se(close_nointr(f->fd) == 0);
+ close_nointr_nofail(f->fd);
}
free(f);
@@ -302,7 +302,7 @@ static void server_done(Server *s) {
fifo_free(s->fifos);
if (s->epoll_fd >= 0)
- assert_se(close_nointr(s->epoll_fd) == 0);
+ close_nointr_nofail(s->epoll_fd);
if (s->bus)
dbus_connection_unref(s->bus);
diff --git a/log.c b/log.c
index b82018f28b..d8cc4048c4 100644
--- a/log.c
+++ b/log.c
@@ -43,7 +43,7 @@ static int kmsg_fd = -1;
void log_close_kmsg(void) {
if (kmsg_fd >= 0) {
- close_nointr(kmsg_fd);
+ close_nointr_nofail(kmsg_fd);
kmsg_fd = -1;
}
}
@@ -71,7 +71,7 @@ int log_open_kmsg(void) {
void log_close_syslog(void) {
if (syslog_fd >= 0) {
- close_nointr(syslog_fd);
+ close_nointr_nofail(syslog_fd);
syslog_fd = -1;
}
}
diff --git a/logger.c b/logger.c
index 7194320c47..ba325c696c 100644
--- a/logger.c
+++ b/logger.c
@@ -283,7 +283,7 @@ static void stream_free(Stream *s) {
if (s->server)
epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL);
- assert_se(close_nointr(s->fd) == 0);
+ close_nointr_nofail(s->fd);
}
free(s->process);
@@ -305,12 +305,12 @@ static int stream_new(Server *s, int server_fd) {
if (s->n_streams >= STREAMS_MAX) {
log_warning("Too many connections, refusing connection.");
- assert_se(close_nointr(fd) == 0);
+ close_nointr_nofail(fd);
return 0;
}
if (!(stream = new0(Stream, 1))) {
- assert_se(close_nointr(fd) == 0);
+ close_nointr_nofail(fd);
return -ENOMEM;
}
@@ -399,16 +399,16 @@ static void server_done(Server *s) {
stream_free(s->streams);
for (i = 0; i < s->n_server_fd; i++)
- assert_se(close_nointr(SERVER_FD_START+i) == 0);
+ close_nointr_nofail(SERVER_FD_START+i);
if (s->syslog_fd >= 0)
- assert_se(close_nointr(s->syslog_fd) == 0);
+ close_nointr_nofail(s->syslog_fd);
if (s->epoll_fd >= 0)
- assert_se(close_nointr(s->epoll_fd) == 0);
+ close_nointr_nofail(s->epoll_fd);
if (s->kmsg_fd >= 0)
- assert_se(close_nointr(s->kmsg_fd) == 0);
+ close_nointr_nofail(s->kmsg_fd);
}
static int server_init(Server *s, unsigned n_sockets) {
diff --git a/main.c b/main.c
index c473ced2b4..29be801125 100644
--- a/main.c
+++ b/main.c
@@ -37,6 +37,7 @@
#include "mount-setup.h"
#include "hostname-setup.h"
#include "load-fragment.h"
+#include "fdset.h"
static enum {
ACTION_RUN,
@@ -51,8 +52,8 @@ static ManagerRunningAs running_as = _MANAGER_RUNNING_AS_INVALID;
static bool dump_core = true;
static bool crash_shell = false;
static int crash_chvt = -1;
-
static bool confirm_spawn = false;
+static FILE* serialization = NULL;
_noreturn static void freeze(void) {
for (;;)
@@ -202,10 +203,10 @@ static int console_setup(void) {
finish:
if (tty_fd >= 0)
- close_nointr(tty_fd);
+ close_nointr_nofail(tty_fd);
if (null_fd >= 0)
- close_nointr(null_fd);
+ close_nointr_nofail(null_fd);
return r;
}
@@ -351,19 +352,21 @@ static int parse_argv(int argc, char *argv[]) {
ARG_RUNNING_AS,
ARG_TEST,
ARG_DUMP_CONFIGURATION_ITEMS,
- ARG_CONFIRM_SPAWN
+ ARG_CONFIRM_SPAWN,
+ ARG_DESERIALIZE
};
static const struct option options[] = {
- { "log-level", required_argument, NULL, ARG_LOG_LEVEL },
- { "log-target", required_argument, NULL, ARG_LOG_TARGET },
- { "default", required_argument, NULL, ARG_DEFAULT },
- { "running-as", required_argument, NULL, ARG_RUNNING_AS },
- { "test", no_argument, NULL, ARG_TEST },
- { "help", no_argument, NULL, 'h' },
- { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
- { "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN },
- { NULL, 0, NULL, 0 }
+ { "log-level", required_argument, NULL, ARG_LOG_LEVEL },
+ { "log-target", required_argument, NULL, ARG_LOG_TARGET },
+ { "default", required_argument, NULL, ARG_DEFAULT },
+ { "running-as", required_argument, NULL, ARG_RUNNING_AS },
+ { "test", no_argument, NULL, ARG_TEST },
+ { "help", no_argument, NULL, 'h' },
+ { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
+ { "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN },
+ { "deserialize", required_argument, NULL, ARG_DESERIALIZE },
+ { NULL, 0, NULL, 0 }
};
int c, r;
@@ -425,6 +428,28 @@ static int parse_argv(int argc, char *argv[]) {
confirm_spawn = true;
break;
+ case ARG_DESERIALIZE: {
+ int fd;
+ FILE *f;
+
+ if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) {
+ log_error("Failed to parse deserialize option %s.", optarg);
+ return r;
+ }
+
+ if (!(f = fdopen(fd, "r"))) {
+ log_error("Failed to open serialization fd: %m");
+ return r;
+ }
+
+ if (serialization)
+ fclose(serialization);
+
+ serialization = f;
+
+ break;
+ }
+
case 'h':
action = ACTION_HELP;
break;
@@ -456,11 +481,67 @@ static int help(void) {
return 0;
}
+static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) {
+ FILE *f = NULL;
+ FDSet *fds = NULL;
+ int r;
+
+ assert(m);
+ assert(_f);
+ assert(_fds);
+
+ if ((r = manager_open_serialization(&f)) < 0) {
+ log_error("Failed to create serialization faile: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (!(fds = fdset_new())) {
+ r = -ENOMEM;
+ log_error("Failed to allocate fd set: %s", strerror(-r));
+ goto fail;
+ }
+
+ if ((r = manager_serialize(m, f, fds)) < 0) {
+ log_error("Failed to serialize state: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (fseeko(f, 0, SEEK_SET) < 0) {
+ log_error("Failed to rewind serialization fd: %m");
+ goto fail;
+ }
+
+ if ((r = fd_cloexec(fileno(f), false)) < 0) {
+ log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r));
+ goto fail;
+ }
+
+ if ((r = fdset_cloexec(fds, false)) < 0) {
+ log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r));
+ goto fail;
+ }
+
+ *_f = f;
+ *_fds = fds;
+
+ return 0;
+
+fail:
+ fdset_free(fds);
+
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
int main(int argc, char *argv[]) {
Manager *m = NULL;
Unit *target = NULL;
Job *job = NULL;
int r, retval = 1;
+ FDSet *fds = NULL;
+ bool reexecute = false;
if (getpid() == 1)
running_as = MANAGER_INIT;
@@ -484,9 +565,6 @@ int main(int argc, char *argv[]) {
ignore_signal(SIGKILL);
ignore_signal(SIGPIPE);
- /* Close all open files */
- assert_se(close_all_fds(NULL, 0) == 0);
-
if (running_as != MANAGER_SESSION)
if (parse_proc_cmdline() < 0)
goto finish;
@@ -507,6 +585,17 @@ int main(int argc, char *argv[]) {
assert_se(action == ACTION_RUN || action == ACTION_TEST);
+ /* Remember open file descriptors for later deserialization */
+ if (serialization) {
+ if ((r = fdset_new_fill(&fds)) < 0) {
+ log_error("Failed to allocate fd set: %s", strerror(-r));
+ goto finish;
+ }
+
+ assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
+ } else
+ close_all_fds(NULL, 0);
+
/* Set up PATH unless it is already set */
setenv("PATH",
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
@@ -548,52 +637,82 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if ((r = manager_coldplug(m)) < 0) {
- log_error("Failed to retrieve coldplug information: %s", strerror(-r));
- goto finish;
+ if ((r = manager_startup(m, serialization, fds)) < 0)
+ log_error("Failed to fully startup daemon: %s", strerror(-r));
+
+ if (fds) {
+ /* This will close all file descriptors that were opened, but
+ * not claimed by any unit. */
+
+ fdset_free(fds);
+ fds = NULL;
}
- log_debug("Activating default unit: %s", default_unit);
+ if (serialization) {
+ fclose(serialization);
+ serialization = NULL;
+ } else {
+ log_debug("Activating default unit: %s", default_unit);
+
+ if ((r = manager_load_unit(m, default_unit, NULL, &target)) < 0) {
+ log_error("Failed to load default target: %s", strerror(-r));
- if ((r = manager_load_unit(m, default_unit, NULL, &target)) < 0) {
- log_error("Failed to load default target: %s", strerror(-r));
+ log_info("Trying to load rescue target...");
+ if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &target)) < 0) {
+ log_error("Failed to load rescue target: %s", strerror(-r));
+ goto finish;
+ }
+ }
- log_info("Trying to load rescue target...");
- if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &target)) < 0) {
- log_error("Failed to load rescue target: %s", strerror(-r));
+ if (action == ACTION_TEST) {
+ printf("→ By units:\n");
+ manager_dump_units(m, stdout, "\t");
+ }
+
+ if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) {
+ log_error("Failed to start default target: %s", strerror(-r));
goto finish;
}
- }
- if (action == ACTION_TEST) {
- printf("→ By units:\n");
- manager_dump_units(m, stdout, "\t");
+ if (action == ACTION_TEST) {
+ printf("→ By jobs:\n");
+ manager_dump_jobs(m, stdout, "\t");
+ retval = 0;
+ goto finish;
+ }
}
- if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) {
- log_error("Failed to start default target: %s", strerror(-r));
- goto finish;
- }
+ for (;;) {
+ if ((r = manager_loop(m)) < 0) {
+ log_error("Failed to run mainloop: %s", strerror(-r));
+ goto finish;
+ }
- if (action == ACTION_TEST) {
- printf("→ By jobs:\n");
- manager_dump_jobs(m, stdout, "\t");
+ switch (m->exit_code) {
- if (getpid() == 1)
- pause();
+ case MANAGER_EXIT:
+ retval = 0;
+ log_debug("Exit.");
+ goto finish;
- retval = 0;
- goto finish;
- }
+ case MANAGER_RELOAD:
+ if ((r = manager_reload(m)) < 0)
+ log_error("Failed to reload: %s", strerror(-r));
+ break;
- if ((r = manager_loop(m)) < 0) {
- log_error("Failed to run mainloop: %s", strerror(-r));
- goto finish;
- }
+ case MANAGER_REEXECUTE:
- retval = 0;
+ if (prepare_reexecute(m, &serialization, &fds) < 0)
+ goto finish;
- log_debug("Exit.");
+ reexecute = true;
+ log_debug("Reexecuting.");
+ goto finish;
+
+ default:
+ assert_not_reached("Unknown exit code.");
+ }
+ }
finish:
if (m)
@@ -603,6 +722,49 @@ finish:
dbus_shutdown();
+ if (reexecute) {
+ const char *args[11];
+ unsigned i = 0;
+ char sfd[16];
+
+ assert(serialization);
+ assert(fds);
+
+ args[i++] = SYSTEMD_BINARY_PATH;
+
+ args[i++] = "--log-level";
+ args[i++] = log_level_to_string(log_get_max_level());
+
+ args[i++] = "--log-target";
+ args[i++] = log_target_to_string(log_get_target());
+
+ args[i++] = "--running-as";
+ args[i++] = manager_running_as_to_string(running_as);
+
+ snprintf(sfd, sizeof(sfd), "%i", fileno(serialization));
+ char_array_0(sfd);
+
+ args[i++] = "--deserialize";
+ args[i++] = sfd;
+
+ if (confirm_spawn)
+ args[i++] = "--confirm-spawn";
+
+ args[i++] = NULL;
+
+ assert(i <= ELEMENTSOF(args));
+
+ execv(args[0], (char* const*) args);
+
+ log_error("Failed to reexecute: %m");
+ }
+
+ if (serialization)
+ fclose(serialization);
+
+ if (fds)
+ fdset_free(fds);
+
if (getpid() == 1)
freeze();
diff --git a/manager.c b/manager.c
index 0187d3b374..9458aa51ba 100644
--- 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",
diff --git a/manager.h b/manager.h
index 77e55339cd..8a48c5519c 100644
--- a/manager.h
+++ b/manager.h
@@ -25,13 +25,23 @@
#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 d3e222d4bc..187c6a373a 100644
--- a/mount.c
+++ b/mount.c
@@ -52,32 +52,27 @@ static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
[MOUNT_MAINTAINANCE] = UNIT_INACTIVE,
};
-static const char* const state_string_table[_MOUNT_STATE_MAX] = {
- [MOUNT_DEAD] = "dead",
- [MOUNT_MOUNTING] = "mounting",
- [MOUNT_MOUNTING_DONE] = "mounting-done",
- [MOUNT_MOUNTED] = "mounted",
- [MOUNT_REMOUNTING] = "remounting",
- [MOUNT_UNMOUNTING] = "unmounting",
- [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
- [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
- [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
- [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
- [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
- [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
- [MOUNT_MAINTAINANCE] = "maintainance"
-};
+static void mount_init(Unit *u) {
+ Mount *m = MOUNT(u);
-static char *mount_name_from_where(const char *where) {
- assert(where);
+ assert(u);
+ assert(u->meta.load_state == UNIT_STUB);
+
+ m->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ exec_context_init(&m->exec_context);
- if (streq(where, "/"))
- return strdup("-.mount");
+ /* We need to make sure that /bin/mount is always called in
+ * the same process group as us, so that the autofs kernel
+ * side doesn't send us another mount request while we are
+ * already trying to comply its last one. */
+ m->exec_context.no_setsid = true;
- return unit_name_build_escape(where+1, NULL, ".mount");
+ m->timer_watch.type = WATCH_INVALID;
+
+ m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
-static void service_unwatch_control_pid(Mount *m) {
+static void mount_unwatch_control_pid(Mount *m) {
assert(m);
if (m->control_pid <= 0)
@@ -113,47 +108,11 @@ static void mount_done(Unit *u) {
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
- service_unwatch_control_pid(m);
+ mount_unwatch_control_pid(m);
unit_unwatch_timer(u, &m->timer_watch);
}
-static void mount_init(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(u);
- assert(u->meta.load_state == UNIT_STUB);
-
- m->timeout_usec = DEFAULT_TIMEOUT_USEC;
- exec_context_init(&m->exec_context);
-
- /* We need to make sure that /bin/mount is always called in
- * the same process group as us, so that the autofs kernel
- * side doesn't send us another mount request while we are
- * already trying to comply its last one. */
- m->exec_context.no_setsid = true;
-
- m->timer_watch.type = WATCH_INVALID;
-}
-
-static int mount_notify_automount(Mount *m, int status) {
- Unit *p;
- char *k;
-
- assert(m);
-
- if (!(k = unit_name_change_suffix(UNIT(m)->meta.id, ".automount")))
- return -ENOMEM;
-
- p = manager_get_unit(UNIT(m)->meta.manager, k);
- free(k);
-
- if (!p)
- return 0;
-
- return automount_send_ready(AUTOMOUNT(p), status);
-}
-
static int mount_add_node_links(Mount *m) {
Unit *device;
char *e;
@@ -262,10 +221,9 @@ static bool mount_test_option(const char *haystack, const char *needle) {
static int mount_add_target_links(Mount *m) {
const char *target;
MountParameters *p;
- Unit *u;
+ Unit *tu;
int r;
- bool noauto;
- bool handle;
+ bool noauto, handle, automount;
assert(m);
@@ -278,8 +236,9 @@ static int mount_add_target_links(Mount *m) {
noauto = mount_test_option(p->options, MNTOPT_NOAUTO);
handle = mount_test_option(p->options, "comment=systemd.mount");
+ automount = mount_test_option(p->options, "comment=systemd.automount");
- if (noauto && !handle)
+ if (noauto && !handle && !automount)
return 0;
if (mount_test_option(p->options, "_netdev") ||
@@ -288,14 +247,28 @@ static int mount_add_target_links(Mount *m) {
else
target = SPECIAL_LOCAL_FS_TARGET;
- if ((r = manager_load_unit(UNIT(m)->meta.manager, target, NULL, &u)) < 0)
+ if ((r = manager_load_unit(UNIT(m)->meta.manager, target, NULL, &tu)) < 0)
return r;
- if (handle)
- if ((r = unit_add_dependency(u, UNIT_WANTS, UNIT(m))) < 0)
+ if (automount) {
+ Unit *am;
+
+ if ((r = unit_load_related_unit(UNIT(m), ".automount", &am)) < 0)
return r;
- return unit_add_dependency(UNIT(m), UNIT_BEFORE, u);
+ if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(am))) < 0)
+ return r;
+
+ return unit_add_dependency(UNIT(am), UNIT_BEFORE, tu);
+
+ } else {
+
+ if (handle)
+ if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m))) < 0)
+ return r;
+
+ return unit_add_dependency(UNIT(m), UNIT_BEFORE, tu);
+ }
}
static int mount_verify(Mount *m) {
@@ -306,14 +279,7 @@ static int mount_verify(Mount *m) {
if (UNIT(m)->meta.load_state != UNIT_LOADED)
return 0;
- if (!m->where) {
- log_error("%s lacks Where setting. Refusing.", UNIT(m)->meta.id);
- return -EINVAL;
- }
-
- path_kill_slashes(m->where);
-
- if (!(e = mount_name_from_where(m->where)))
+ if (!(e = unit_name_from_path(m->where, ".mount")))
return -ENOMEM;
b = unit_has_name(UNIT(m), e);
@@ -340,6 +306,12 @@ static int mount_load(Unit *u) {
/* This is a new unit? Then let's add in some extras */
if (u->meta.load_state == UNIT_LOADED) {
+ if (!m->where)
+ if (!(m->where = unit_name_to_path(u->meta.id)))
+ return -ENOMEM;
+
+ path_kill_slashes(m->where);
+
/* Minor validity checking */
if ((m->parameters_fragment.options || m->parameters_fragment.fstype) && !m->parameters_fragment.what)
return -EBADMSG;
@@ -363,6 +335,18 @@ static int mount_load(Unit *u) {
return mount_verify(m);
}
+static int mount_notify_automount(Mount *m, int status) {
+ Unit *p;
+ int r;
+
+ assert(m);
+
+ if ((r = unit_get_related_unit(UNIT(m), ".automount", &p)) < 0)
+ return r == -ENOENT ? 0 : r;
+
+ return automount_send_ready(AUTOMOUNT(p), status);
+}
+
static void mount_set_state(Mount *m, MountState state) {
MountState old_state;
assert(m);
@@ -381,8 +365,9 @@ static void mount_set_state(Mount *m, MountState state) {
state != MOUNT_REMOUNTING_SIGTERM &&
state != MOUNT_REMOUNTING_SIGKILL) {
unit_unwatch_timer(UNIT(m), &m->timer_watch);
- service_unwatch_control_pid(m);
+ mount_unwatch_control_pid(m);
m->control_command = NULL;
+ m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
if (state == MOUNT_MOUNTED ||
@@ -400,57 +385,54 @@ static void mount_set_state(Mount *m, MountState state) {
mount_notify_automount(m, -ENODEV);
if (state != old_state)
- log_debug("%s changed %s → %s", UNIT(m)->meta.id, state_string_table[old_state], state_string_table[state]);
+ log_debug("%s changed %s → %s",
+ UNIT(m)->meta.id,
+ mount_state_to_string(old_state),
+ mount_state_to_string(state));
unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state]);
}
static int mount_coldplug(Unit *u) {
Mount *m = MOUNT(u);
+ MountState new_state = MOUNT_DEAD;
+ int r;
assert(m);
assert(m->state == MOUNT_DEAD);
- if (m->from_proc_self_mountinfo)
- mount_set_state(m, MOUNT_MOUNTED);
+ if (m->deserialized_state != m->state)
+ new_state = m->deserialized_state;
+ else if (m->from_proc_self_mountinfo)
+ new_state = MOUNT_MOUNTED;
- return 0;
-}
+ if (new_state != m->state) {
-static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
- pid_t pid;
- int r;
+ if (new_state == MOUNT_MOUNTING ||
+ new_state == MOUNT_MOUNTING_DONE ||
+ new_state == MOUNT_REMOUNTING ||
+ new_state == MOUNT_UNMOUNTING ||
+ new_state == MOUNT_MOUNTING_SIGTERM ||
+ new_state == MOUNT_MOUNTING_SIGKILL ||
+ new_state == MOUNT_UNMOUNTING_SIGTERM ||
+ new_state == MOUNT_UNMOUNTING_SIGKILL ||
+ new_state == MOUNT_REMOUNTING_SIGTERM ||
+ new_state == MOUNT_REMOUNTING_SIGKILL) {
- assert(m);
- assert(c);
- assert(_pid);
+ if (m->control_pid <= 0)
+ return -EBADMSG;
- if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
- goto fail;
-
- if ((r = exec_spawn(c,
- NULL,
- &m->exec_context,
- NULL, 0,
- true,
- true,
- UNIT(m)->meta.manager->confirm_spawn,
- UNIT(m)->meta.cgroup_bondings,
- &pid)) < 0)
- goto fail;
+ if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0)
+ return r;
- if ((r = unit_watch_pid(UNIT(m), pid)) < 0)
- /* FIXME: we need to do something here */
- goto fail;
+ if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ return r;
+ }
- *_pid = pid;
+ mount_set_state(m, new_state);
+ }
return 0;
-
-fail:
- unit_unwatch_timer(UNIT(m), &m->timer_watch);
-
- return r;
}
static void mount_dump(Unit *u, FILE *f, const char *prefix) {
@@ -477,7 +459,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
"%sFrom /proc/self/mountinfo: %s\n"
"%sFrom fragment: %s\n"
"%sKillMode: %s\n",
- prefix, state_string_table[m->state],
+ prefix, mount_state_to_string(m->state),
prefix, m->where,
prefix, strna(p->what),
prefix, strna(p->fstype),
@@ -495,6 +477,42 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
exec_context_dump(&m->exec_context, f, prefix);
}
+static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
+ pid_t pid;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(_pid);
+
+ if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ goto fail;
+
+ if ((r = exec_spawn(c,
+ NULL,
+ &m->exec_context,
+ NULL, 0,
+ true,
+ true,
+ UNIT(m)->meta.manager->confirm_spawn,
+ UNIT(m)->meta.cgroup_bondings,
+ &pid)) < 0)
+ goto fail;
+
+ if ((r = unit_watch_pid(UNIT(m), pid)) < 0)
+ /* FIXME: we need to do something here */
+ goto fail;
+
+ *_pid = pid;
+
+ return 0;
+
+fail:
+ unit_unwatch_timer(UNIT(m), &m->timer_watch);
+
+ return r;
+}
+
static void mount_enter_dead(Mount *m, bool success) {
assert(m);
@@ -565,7 +583,6 @@ fail:
}
static void mount_enter_unmounting(Mount *m, bool success) {
- ExecCommand *c;
int r;
assert(m);
@@ -573,18 +590,19 @@ static void mount_enter_unmounting(Mount *m, bool success) {
if (!success)
m->failure = true;
- m->control_command = c = m->exec_command + MOUNT_EXEC_UNMOUNT;
+ m->control_command_id = MOUNT_EXEC_UNMOUNT;
+ m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
if ((r = exec_command_set(
- c,
+ m->control_command,
"/bin/umount",
m->where,
NULL)) < 0)
goto fail;
- service_unwatch_control_pid(m);
+ mount_unwatch_control_pid(m);
- if ((r = mount_spawn(m, c, &m->control_pid)) < 0)
+ if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
goto fail;
mount_set_state(m, MOUNT_UNMOUNTING);
@@ -597,16 +615,16 @@ fail:
}
static void mount_enter_mounting(Mount *m) {
- ExecCommand *c;
int r;
assert(m);
- m->control_command = c = m->exec_command + MOUNT_EXEC_MOUNT;
+ m->control_command_id = MOUNT_EXEC_MOUNT;
+ m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
if (m->from_fragment)
r = exec_command_set(
- c,
+ m->control_command,
"/bin/mount",
m->parameters_fragment.what,
m->where,
@@ -615,7 +633,7 @@ static void mount_enter_mounting(Mount *m) {
NULL);
else if (m->from_etc_fstab)
r = exec_command_set(
- c,
+ m->control_command,
"/bin/mount",
m->where,
NULL);
@@ -625,9 +643,9 @@ static void mount_enter_mounting(Mount *m) {
if (r < 0)
goto fail;
- service_unwatch_control_pid(m);
+ mount_unwatch_control_pid(m);
- if ((r = mount_spawn(m, c, &m->control_pid)) < 0)
+ if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
goto fail;
mount_set_state(m, MOUNT_MOUNTING);
@@ -646,7 +664,6 @@ static void mount_enter_mounting_done(Mount *m) {
}
static void mount_enter_remounting(Mount *m, bool success) {
- ExecCommand *c;
int r;
assert(m);
@@ -654,7 +671,8 @@ static void mount_enter_remounting(Mount *m, bool success) {
if (!success)
m->failure = true;
- m->control_command = c = m->exec_command + MOUNT_EXEC_REMOUNT;
+ m->control_command_id = MOUNT_EXEC_REMOUNT;
+ m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
if (m->from_fragment) {
char *buf = NULL;
@@ -671,7 +689,7 @@ static void mount_enter_remounting(Mount *m, bool success) {
o = "remount";
r = exec_command_set(
- c,
+ m->control_command,
"/bin/mount",
m->parameters_fragment.what,
m->where,
@@ -682,7 +700,7 @@ static void mount_enter_remounting(Mount *m, bool success) {
free(buf);
} else if (m->from_etc_fstab)
r = exec_command_set(
- c,
+ m->control_command,
"/bin/mount",
m->where,
"-o", "remount",
@@ -695,9 +713,9 @@ static void mount_enter_remounting(Mount *m, bool success) {
goto fail;
}
- service_unwatch_control_pid(m);
+ mount_unwatch_control_pid(m);
- if ((r = mount_spawn(m, c, &m->control_pid)) < 0)
+ if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
goto fail;
mount_set_state(m, MOUNT_REMOUNTING);
@@ -729,7 +747,6 @@ static int mount_start(Unit *u) {
assert(m->state == MOUNT_DEAD || m->state == MOUNT_MAINTAINANCE);
m->failure = false;
-
mount_enter_mounting(m);
return 0;
}
@@ -775,6 +792,72 @@ static int mount_reload(Unit *u) {
return 0;
}
+static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", mount_state_to_string(m->state));
+ unit_serialize_item(u, f, "failure", yes_no(m->failure));
+
+ if (m->control_pid > 0)
+ unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) m->control_pid);
+
+ if (m->control_command_id >= 0)
+ unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
+
+ return 0;
+}
+
+static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Mount *m = MOUNT(u);
+ int r;
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ MountState state;
+
+ if ((state = mount_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ m->deserialized_state = state;
+ } else if (streq(key, "failure")) {
+ int b;
+
+ if ((b = parse_boolean(value)) < 0)
+ log_debug("Failed to parse failure value %s", value);
+ else
+ m->failure = b || m->failure;
+
+ } else if (streq(key, "control-pid")) {
+ unsigned pid;
+
+ if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
+ log_debug("Failed to parse control-pid value %s", value);
+ else
+ m->control_pid = (pid_t) pid;
+ } else if (streq(key, "control-command")) {
+ MountExecCommand id;
+
+ if ((id = mount_exec_command_from_string(value)) < 0)
+ log_debug("Failed to parse exec-command value %s", value);
+ else {
+ m->control_command_id = id;
+ m->control_command = m->exec_command + id;
+ }
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
static UnitActiveState mount_active_state(Unit *u) {
assert(u);
@@ -784,7 +867,7 @@ static UnitActiveState mount_active_state(Unit *u) {
static const char *mount_sub_state_to_string(Unit *u) {
assert(u);
- return state_string_table[MOUNT(u)->state];
+ return mount_state_to_string(MOUNT(u)->state);
}
static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
@@ -798,11 +881,14 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->failure = m->failure || !success;
assert(m->control_pid == pid);
- assert(m->control_command);
-
- exec_status_fill(&m->control_command->exec_status, pid, code, status);
m->control_pid = 0;
+ if (m->control_command) {
+ exec_status_fill(&m->control_command->exec_status, pid, code, status);
+ m->control_command = NULL;
+ m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
+ }
+
log_debug("%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status);
/* Note that mount(8) returning and the kernel sending us a
@@ -937,9 +1023,7 @@ static int mount_add_one(
if (where[0] != '/')
return 0;
- e = mount_name_from_where(where);
-
- if (!e)
+ if (!(e = unit_name_from_path(where, ".mount")))
return -ENOMEM;
if (!(u = manager_get_unit(m, e))) {
@@ -1172,8 +1256,10 @@ finish:
static void mount_shutdown(Manager *m) {
assert(m);
- if (m->proc_self_mountinfo)
+ if (m->proc_self_mountinfo) {
fclose(m->proc_self_mountinfo);
+ m->proc_self_mountinfo = NULL;
+ }
}
static int mount_enumerate(Manager *m) {
@@ -1181,18 +1267,20 @@ static int mount_enumerate(Manager *m) {
struct epoll_event ev;
assert(m);
- if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "r")))
- return -errno;
+ if (!m->proc_self_mountinfo) {
+ if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
- m->mount_watch.type = WATCH_MOUNT;
- m->mount_watch.fd = fileno(m->proc_self_mountinfo);
+ m->mount_watch.type = WATCH_MOUNT;
+ m->mount_watch.fd = fileno(m->proc_self_mountinfo);
- zero(ev);
- ev.events = EPOLLERR;
- ev.data.ptr = &m->mount_watch;
+ zero(ev);
+ ev.events = EPOLLERR;
+ ev.data.ptr = &m->mount_watch;
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
- return -errno;
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
+ return -errno;
+ }
if ((r = mount_load_etc_fstab(m)) < 0)
goto fail;
@@ -1303,7 +1391,7 @@ int mount_path_is_mounted(Manager *m, const char* path) {
char *e, *slash;
Unit *u;
- if (!(e = mount_name_from_where(t))) {
+ if (!(e = unit_name_from_path(t, ".mount"))) {
r = -ENOMEM;
goto finish;
}
@@ -1335,6 +1423,32 @@ finish:
return r;
}
+static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
+ [MOUNT_DEAD] = "dead",
+ [MOUNT_MOUNTING] = "mounting",
+ [MOUNT_MOUNTING_DONE] = "mounting-done",
+ [MOUNT_MOUNTED] = "mounted",
+ [MOUNT_REMOUNTING] = "remounting",
+ [MOUNT_UNMOUNTING] = "unmounting",
+ [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
+ [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
+ [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
+ [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
+ [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
+ [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
+ [MOUNT_MAINTAINANCE] = "maintainance"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
+
+static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
+ [MOUNT_EXEC_MOUNT] = "ExecMount",
+ [MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
+ [MOUNT_EXEC_REMOUNT] = "ExecRemount",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand);
+
const UnitVTable mount_vtable = {
.suffix = ".mount",
@@ -1353,6 +1467,9 @@ const UnitVTable mount_vtable = {
.stop = mount_stop,
.reload = mount_reload,
+ .serialize = mount_serialize,
+ .deserialize_item = mount_deserialize_item,
+
.active_state = mount_active_state,
.sub_state_to_string = mount_sub_state_to_string,
diff --git a/mount.h b/mount.h
index 66992e10eb..3b28e89ed3 100644
--- a/mount.h
+++ b/mount.h
@@ -84,11 +84,12 @@ struct Mount {
ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX];
ExecContext exec_context;
- MountState state;
+ MountState state, deserialized_state;
KillMode kill_mode;
ExecCommand* control_command;
+ MountExecCommand control_command_id;
pid_t control_pid;
Watch timer_watch;
@@ -100,4 +101,10 @@ void mount_fd_event(Manager *m, int events);
int mount_path_is_mounted(Manager *m, const char* path);
+const char* mount_state_to_string(MountState i);
+MountState mount_state_from_string(const char *s);
+
+const char* mount_exec_command_to_string(MountExecCommand i);
+MountExecCommand mount_exec_command_from_string(const char *s);
+
#endif
diff --git a/service.c b/service.c
index f334027a79..25641768a9 100644
--- a/service.c
+++ b/service.c
@@ -66,6 +66,25 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
};
+static void service_init(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(u);
+ assert(u->meta.load_state == UNIT_STUB);
+
+ s->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ s->restart_usec = DEFAULT_RESTART_USEC;
+ s->timer_watch.type = WATCH_INVALID;
+ s->sysv_start_priority = -1;
+ s->socket_fd = -1;
+
+ exec_context_init(&s->exec_context);
+
+ RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
+
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+}
+
static void service_unwatch_control_pid(Service *s) {
assert(s);
@@ -735,23 +754,6 @@ static int service_add_bus_name(Service *s) {
return r;
}
-static void service_init(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(u);
- assert(u->meta.load_state == UNIT_STUB);
-
- s->timeout_usec = DEFAULT_TIMEOUT_USEC;
- s->restart_usec = DEFAULT_RESTART_USEC;
- s->timer_watch.type = WATCH_INVALID;
- s->sysv_start_priority = -1;
- s->socket_fd = -1;
-
- exec_context_init(&s->exec_context);
-
- RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
-}
-
static int service_verify(Service *s) {
assert(s);
@@ -1047,6 +1049,7 @@ static void service_set_state(Service *s, ServiceState state) {
state != SERVICE_FINAL_SIGKILL) {
service_unwatch_control_pid(s);
s->control_command = NULL;
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
if (state == SERVICE_DEAD ||
@@ -1071,6 +1074,66 @@ static void service_set_state(Service *s, ServiceState state) {
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
}
+static int service_coldplug(Unit *u) {
+ Service *s = SERVICE(u);
+ int r;
+
+ assert(s);
+ assert(s->state == SERVICE_DEAD);
+
+ if (s->deserialized_state != s->state) {
+
+ if (s->deserialized_state == SERVICE_START_PRE ||
+ s->deserialized_state == SERVICE_START ||
+ s->deserialized_state == SERVICE_START_POST ||
+ s->deserialized_state == SERVICE_RELOAD ||
+ s->deserialized_state == SERVICE_STOP ||
+ s->deserialized_state == SERVICE_STOP_SIGTERM ||
+ s->deserialized_state == SERVICE_STOP_SIGKILL ||
+ s->deserialized_state == SERVICE_STOP_POST ||
+ s->deserialized_state == SERVICE_FINAL_SIGTERM ||
+ s->deserialized_state == SERVICE_FINAL_SIGKILL ||
+ s->deserialized_state == SERVICE_AUTO_RESTART)
+ if ((r = unit_watch_timer(UNIT(s),
+ s->deserialized_state == SERVICE_AUTO_RESTART ?
+ s->restart_usec :
+ s->timeout_usec,
+ &s->timer_watch)) < 0)
+ return r;
+
+ if ((s->deserialized_state == SERVICE_START &&
+ (s->type == SERVICE_FORKING ||
+ s->type == SERVICE_DBUS)) ||
+ s->deserialized_state == SERVICE_START_POST ||
+ s->deserialized_state == SERVICE_RUNNING ||
+ s->deserialized_state == SERVICE_RELOAD ||
+ s->deserialized_state == SERVICE_STOP ||
+ s->deserialized_state == SERVICE_STOP_SIGTERM ||
+ s->deserialized_state == SERVICE_STOP_SIGKILL)
+ if (s->main_pid > 0)
+ if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0)
+ return r;
+
+ if (s->deserialized_state == SERVICE_START_PRE ||
+ s->deserialized_state == SERVICE_START ||
+ s->deserialized_state == SERVICE_START_POST ||
+ s->deserialized_state == SERVICE_RELOAD ||
+ s->deserialized_state == SERVICE_STOP ||
+ s->deserialized_state == SERVICE_STOP_SIGTERM ||
+ s->deserialized_state == SERVICE_STOP_SIGKILL ||
+ s->deserialized_state == SERVICE_STOP_POST ||
+ s->deserialized_state == SERVICE_FINAL_SIGTERM ||
+ s->deserialized_state == SERVICE_FINAL_SIGKILL)
+ if (s->control_pid > 0)
+ if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ return r;
+
+ service_set_state(s, s->deserialized_state);
+ }
+
+ return 0;
+}
+
static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
Iterator i;
int r;
@@ -1279,6 +1342,7 @@ static void service_enter_stop_post(Service *s, bool success) {
service_unwatch_control_pid(s);
+ s->control_command_id = SERVICE_EXEC_STOP_POST;
if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) {
if ((r = service_spawn(s,
s->control_command,
@@ -1374,6 +1438,7 @@ static void service_enter_stop(Service *s, bool success) {
service_unwatch_control_pid(s);
+ s->control_command_id = SERVICE_EXEC_STOP;
if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) {
if ((r = service_spawn(s,
s->control_command,
@@ -1417,6 +1482,7 @@ static void service_enter_start_post(Service *s) {
service_unwatch_control_pid(s);
+ s->control_command_id = SERVICE_EXEC_START_POST;
if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) {
if ((r = service_spawn(s,
s->control_command,
@@ -1478,6 +1544,7 @@ static void service_enter_start(Service *s) {
s->control_pid = pid;
+ s->control_command_id = SERVICE_EXEC_START;
s->control_command = s->exec_command[SERVICE_EXEC_START];
service_set_state(s, SERVICE_START);
@@ -1511,6 +1578,7 @@ static void service_enter_start_pre(Service *s) {
service_unwatch_control_pid(s);
+ s->control_command_id = SERVICE_EXEC_START_PRE;
if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) {
if ((r = service_spawn(s,
s->control_command,
@@ -1557,6 +1625,7 @@ static void service_enter_reload(Service *s) {
service_unwatch_control_pid(s);
+ s->control_command_id = SERVICE_EXEC_RELOAD;
if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) {
if ((r = service_spawn(s,
s->control_command,
@@ -1703,6 +1772,112 @@ static bool service_can_reload(Unit *u) {
return !!s->exec_command[SERVICE_EXEC_RELOAD];
}
+static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Service *s = SERVICE(u);
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", service_state_to_string(s->state));
+ unit_serialize_item(u, f, "failure", yes_no(s->failure));
+
+ if (s->control_pid > 0)
+ unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) (s->control_pid));
+
+ if (s->main_pid > 0)
+ unit_serialize_item_format(u, f, "main-pid", "%u", (unsigned) (s->main_pid));
+
+ unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
+
+ /* There's a minor uncleanliness here: if there are multiple
+ * commands attached here, we will start from the first one
+ * again */
+ if (s->control_command_id >= 0)
+ unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(s->control_command_id));
+
+ if (s->socket_fd >= 0) {
+ int copy;
+
+ if ((copy = fdset_put_dup(fds, s->socket_fd)) < 0)
+ return copy;
+
+ unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
+ }
+
+ return 0;
+}
+
+static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Service *s = SERVICE(u);
+ int r;
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ ServiceState state;
+
+ if ((state = service_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+ } else if (streq(key, "failure")) {
+ int b;
+
+ if ((b = parse_boolean(value)) < 0)
+ log_debug("Failed to parse failure value %s", value);
+ else
+ s->failure = b || s->failure;
+ } else if (streq(key, "control-pid")) {
+ unsigned pid;
+
+ if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
+ log_debug("Failed to parse control-pid value %s", value);
+ else
+ s->control_pid = (pid_t) pid;
+ } else if (streq(key, "main-pid")) {
+ unsigned pid;
+
+ if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
+ log_debug("Failed to parse main-pid value %s", value);
+ else
+ s->main_pid = (pid_t) pid;
+ } else if (streq(key, "main-pid-known")) {
+ int b;
+
+ if ((b = parse_boolean(value)) < 0)
+ log_debug("Failed to parse main-pid-known value %s", value);
+ else
+ s->main_pid_known = b;
+ } else if (streq(key, "control-command")) {
+ ServiceExecCommand id;
+
+ if ((id = service_exec_command_from_string(value)) < 0)
+ log_debug("Failed to parse exec-command value %s", value);
+ else {
+ s->control_command_id = id;
+ s->control_command = s->exec_command[id];
+ }
+ } else if (streq(key, "socket-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse socket-fd value %s", value);
+ else {
+
+ if (s->socket_fd >= 0)
+ close_nointr_nofail(s->socket_fd);
+ s->socket_fd = fdset_remove(fds, fd);
+ }
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
static UnitActiveState service_active_state(Unit *u) {
assert(u);
@@ -1777,9 +1952,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
}
} else if (s->control_pid == pid) {
- assert(s->control_command);
- exec_status_fill(&s->control_command->exec_status, pid, code, status);
+ if (s->control_command)
+ exec_status_fill(&s->control_command->exec_status, pid, code, status);
+
s->control_pid = 0;
log_debug("%s: control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status);
@@ -1787,7 +1963,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* If we are shutting things down anyway we
* don't care about failing commands. */
- if (s->control_command->command_next && success) {
+ if (s->control_command && s->control_command->command_next && success) {
/* There is another command to *
* execute, so let's do that. */
@@ -1799,6 +1975,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* No further commands for this step, so let's
* figure out what to do next */
+ s->control_command = NULL;
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+
log_debug("%s got final SIGCHLD for state %s", u->meta.id, service_state_to_string(s->state));
switch (s->state) {
@@ -2218,8 +2397,10 @@ const UnitVTable service_vtable = {
.suffix = ".service",
.init = service_init,
- .load = service_load,
.done = service_done,
+ .load = service_load,
+
+ .coldplug = service_coldplug,
.dump = service_dump,
@@ -2229,6 +2410,9 @@ const UnitVTable service_vtable = {
.can_reload = service_can_reload,
+ .serialize = service_serialize,
+ .deserialize_item = service_deserialize_item,
+
.active_state = service_active_state,
.sub_state_to_string = service_sub_state_to_string,
diff --git a/service.h b/service.h
index e603ff74b9..dc597aa0e9 100644
--- a/service.h
+++ b/service.h
@@ -94,13 +94,14 @@ struct Service {
bool root_directory_start_only;
bool valid_no_process;
- ServiceState state;
+ ServiceState state, deserialized_state;
KillMode kill_mode;
ExecStatus main_exec_status;
ExecCommand *control_command;
+ ServiceExecCommand control_command_id;
pid_t main_pid, control_pid;
bool main_pid_known:1;
diff --git a/snapshot.c b/snapshot.c
index 1fc0ab8164..d0aaa51fee 100644
--- a/snapshot.c
+++ b/snapshot.c
@@ -31,31 +31,30 @@ static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
[SNAPSHOT_ACTIVE] = UNIT_ACTIVE
};
-static const char* const state_string_table[_SNAPSHOT_STATE_MAX] = {
- [SNAPSHOT_DEAD] = "dead",
- [SNAPSHOT_ACTIVE] = "active"
-};
-
-static int snapshot_load(Unit *u) {
- Iterator i;
- Unit *other;
- int r;
+static void snapshot_set_state(Snapshot *s, SnapshotState state) {
+ SnapshotState old_state;
+ assert(s);
- assert(u);
+ old_state = s->state;
+ s->state = state;
- HASHMAP_FOREACH(other, u->meta.manager->units, i) {
+ if (state != old_state)
+ log_debug("%s changed %s → %s",
+ UNIT(s)->meta.id,
+ snapshot_state_to_string(old_state),
+ snapshot_state_to_string(state));
- if (UNIT_VTABLE(other)->no_snapshots)
- continue;
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
+}
- if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0)
- return r;
+static int snapshot_coldplug(Unit *u) {
+ Snapshot *s = SNAPSHOT(u);
- if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0)
- return r;
- }
+ assert(s);
+ assert(s->state == SNAPSHOT_DEAD);
- u->meta.load_state = UNIT_LOADED;
+ if (s->deserialized_state != s->state)
+ snapshot_set_state(s, s->deserialized_state);
return 0;
}
@@ -69,23 +68,10 @@ static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f,
"%sSnapshot State: %s\n"
"%sClean Up: %s\n",
- prefix, state_string_table[s->state],
+ prefix, snapshot_state_to_string(s->state),
prefix, yes_no(s->cleanup));
}
-static void snapshot_set_state(Snapshot *s, SnapshotState state) {
- SnapshotState old_state;
- assert(s);
-
- old_state = s->state;
- s->state = state;
-
- if (state != old_state)
- log_debug("%s changed %s → %s", UNIT(s)->meta.id, state_string_table[old_state], state_string_table[state]);
-
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
-}
-
static int snapshot_start(Unit *u) {
Snapshot *s = SNAPSHOT(u);
@@ -110,6 +96,60 @@ static int snapshot_stop(Unit *u) {
return 0;
}
+static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Snapshot *s = SNAPSHOT(u);
+ Unit *other;
+ Iterator i;
+
+ assert(s);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
+ unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
+ SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i)
+ unit_serialize_item(u, f, "requires", other->meta.id);
+
+ return 0;
+}
+
+static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Snapshot *s = SNAPSHOT(u);
+ int r;
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ SnapshotState state;
+
+ if ((state = snapshot_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+
+ } else if (streq(key, "cleanup")) {
+
+ if ((r = parse_boolean(value)) < 0)
+ log_debug("Failed to parse cleanup value %s", value);
+ else
+ s->cleanup = r;
+
+ } else if (streq(key, "requires")) {
+
+ if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, value, NULL)) < 0)
+ return r;
+
+ if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, value, NULL)) < 0)
+ return r;
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
static UnitActiveState snapshot_active_state(Unit *u) {
assert(u);
@@ -119,13 +159,15 @@ static UnitActiveState snapshot_active_state(Unit *u) {
static const char *snapshot_sub_state_to_string(Unit *u) {
assert(u);
- return state_string_table[SNAPSHOT(u)->state];
+ return snapshot_state_to_string(SNAPSHOT(u)->state);
}
int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) {
- Unit *u;
+ Iterator i;
+ Unit *other, *u = NULL;
char *n = NULL;
int r;
+ const char *k;
assert(m);
assert(_s);
@@ -159,12 +201,36 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) {
free(n);
if (r < 0)
- return r;
+ goto fail;
+
+ HASHMAP_FOREACH_KEY(other, k, m->units, i) {
+
+ if (UNIT_VTABLE(other)->no_snapshots)
+ continue;
+
+ if (k != other->meta.id)
+ continue;
+
+ if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
+ continue;
+
+ if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0)
+ goto fail;
+
+ if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0)
+ goto fail;
+ }
SNAPSHOT(u)->cleanup = cleanup;
*_s = SNAPSHOT(u);
return 0;
+
+fail:
+ if (u)
+ unit_add_to_cleanup_queue(u);
+
+ return r;
}
void snapshot_remove(Snapshot *s) {
@@ -173,6 +239,13 @@ void snapshot_remove(Snapshot *s) {
unit_add_to_cleanup_queue(UNIT(s));
}
+static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
+ [SNAPSHOT_DEAD] = "dead",
+ [SNAPSHOT_ACTIVE] = "active"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
+
const UnitVTable snapshot_vtable = {
.suffix = ".snapshot",
@@ -180,13 +253,17 @@ const UnitVTable snapshot_vtable = {
.no_instances = true,
.no_snapshots = true,
- .load = snapshot_load,
+ .load = unit_load_nop,
+ .coldplug = snapshot_coldplug,
.dump = snapshot_dump,
.start = snapshot_start,
.stop = snapshot_stop,
+ .serialize = snapshot_serialize,
+ .deserialize_item = snapshot_deserialize_item,
+
.active_state = snapshot_active_state,
.sub_state_to_string = snapshot_sub_state_to_string,
diff --git a/snapshot.h b/snapshot.h
index 78bcafa448..959a5090ec 100644
--- a/snapshot.h
+++ b/snapshot.h
@@ -36,7 +36,7 @@ typedef enum SnapshotState {
struct Snapshot {
Meta meta;
- SnapshotState state;
+ SnapshotState state, deserialized_state;
bool cleanup;
};
@@ -46,4 +46,7 @@ extern const UnitVTable snapshot_vtable;
int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **s);
void snapshot_remove(Snapshot *s);
+const char* snapshot_state_to_string(SnapshotState i);
+SnapshotState snapshot_state_from_string(const char *s);
+
#endif
diff --git a/socket-util.c b/socket-util.c
index cd5ab82a23..8141ab09eb 100644
--- a/socket-util.c
+++ b/socket-util.c
@@ -316,7 +316,7 @@ int socket_address_listen(
if ((r = socket_address_verify(a)) < 0)
return r;
- if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK, 0)) < 0)
+ if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
return -errno;
if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
@@ -370,7 +370,7 @@ int socket_address_listen(
fail:
r = -errno;
- close_nointr(fd);
+ close_nointr_nofail(fd);
return r;
}
@@ -381,3 +381,77 @@ bool socket_address_can_accept(const SocketAddress *a) {
a->type == SOCK_STREAM ||
a->type == SOCK_SEQPACKET;
}
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
+ assert(a);
+ assert(b);
+
+ /* Invalid addresses are unequal to all */
+ if (socket_address_verify(a) < 0 ||
+ socket_address_verify(b) < 0)
+ return false;
+
+ if (a->type != b->type)
+ return false;
+
+ if (a->size != b->size)
+ return false;
+
+ if (socket_address_family(a) != socket_address_family(b))
+ return false;
+
+ switch (socket_address_family(a)) {
+
+ case AF_INET:
+ if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
+ return false;
+
+ if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
+ return false;
+
+ break;
+
+ case AF_INET6:
+ if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
+ return false;
+
+ if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
+ return false;
+
+ break;
+
+ case AF_UNIX:
+
+ if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
+ return false;
+
+ if (a->sockaddr.un.sun_path[0]) {
+ if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
+ return false;
+ } else {
+ if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
+ return false;
+ }
+
+ break;
+
+ default:
+ /* Cannot compare, so we assume the addresses are different */
+ return false;
+ }
+
+ return true;
+}
+
+bool socket_address_is(const SocketAddress *a, const char *s) {
+ struct SocketAddress b;
+
+ assert(a);
+ assert(s);
+
+ if (socket_address_parse(&b, s) < 0)
+ return false;
+
+ return socket_address_equal(a, &b);
+
+}
diff --git a/socket-util.h b/socket-util.h
index e6e1b30b53..af452efddf 100644
--- a/socket-util.h
+++ b/socket-util.h
@@ -70,4 +70,8 @@ int socket_address_listen(
mode_t socket_mode,
int *ret);
+bool socket_address_is(const SocketAddress *a, const char *s);
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
+
#endif
diff --git a/socket.c b/socket.c
index 22658dcb00..63346a2bfc 100644
--- a/socket.c
+++ b/socket.c
@@ -52,20 +52,22 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_MAINTAINANCE] = UNIT_INACTIVE,
};
-static const char* const state_string_table[_SOCKET_STATE_MAX] = {
- [SOCKET_DEAD] = "dead",
- [SOCKET_START_PRE] = "start-pre",
- [SOCKET_START_POST] = "start-post",
- [SOCKET_LISTENING] = "listening",
- [SOCKET_RUNNING] = "running",
- [SOCKET_STOP_PRE] = "stop-pre",
- [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
- [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
- [SOCKET_STOP_POST] = "stop-post",
- [SOCKET_FINAL_SIGTERM] = "final-sigterm",
- [SOCKET_FINAL_SIGKILL] = "final-sigkill",
- [SOCKET_MAINTAINANCE] = "maintainance"
-};
+static void socket_init(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(u);
+ assert(u->meta.load_state == UNIT_STUB);
+
+ s->timer_watch.type = WATCH_INVALID;
+ s->backlog = SOMAXCONN;
+ s->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ s->directory_mode = 0755;
+ s->socket_mode = 0666;
+
+ exec_context_init(&s->exec_context);
+
+ s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+}
static void socket_unwatch_control_pid(Socket *s) {
assert(s);
@@ -86,8 +88,11 @@ static void socket_done(Unit *u) {
while ((p = s->ports)) {
LIST_REMOVE(SocketPort, port, s->ports, p);
- if (p->fd >= 0)
- close_nointr(p->fd);
+ if (p->fd >= 0) {
+ unit_unwatch_fd(UNIT(s), &p->fd_watch);
+ close_nointr_nofail(p->fd);
+ }
+
free(p->path);
free(p);
}
@@ -106,21 +111,6 @@ static void socket_done(Unit *u) {
unit_unwatch_timer(u, &s->timer_watch);
}
-static void socket_init(Unit *u) {
- Socket *s = SOCKET(u);
-
- assert(u);
- assert(u->meta.load_state == UNIT_STUB);
-
- s->timer_watch.type = WATCH_INVALID;
- s->backlog = SOMAXCONN;
- s->timeout_usec = DEFAULT_TIMEOUT_USEC;
- s->directory_mode = 0755;
- s->socket_mode = 0666;
-
- exec_context_init(&s->exec_context);
-}
-
static bool have_non_accept_socket(Socket *s) {
SocketPort *p;
@@ -196,13 +186,6 @@ static const char* listen_lookup(int type) {
static void socket_dump(Unit *u, FILE *f, const char *prefix) {
- static const char* const command_table[_SOCKET_EXEC_COMMAND_MAX] = {
- [SOCKET_EXEC_START_PRE] = "StartPre",
- [SOCKET_EXEC_START_POST] = "StartPost",
- [SOCKET_EXEC_STOP_PRE] = "StopPre",
- [SOCKET_EXEC_STOP_POST] = "StopPost"
- };
-
SocketExecCommand c;
Socket *s = SOCKET(u);
SocketPort *p;
@@ -222,7 +205,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sKillMode: %s\n"
"%sSocketMode: %04o\n"
"%sDirectoryMode: %04o\n",
- prefix, state_string_table[s->state],
+ prefix, socket_state_to_string(s->state),
prefix, yes_no(s->bind_ipv6_only),
prefix, s->backlog,
prefix, kill_mode_to_string(s->kill_mode),
@@ -269,7 +252,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
continue;
fprintf(f, "%s→ %s:\n",
- prefix, command_table[c]);
+ prefix, socket_exec_command_to_string(c));
exec_command_dump_list(s->exec_command[c], f, prefix2);
}
@@ -307,7 +290,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
b = ntohl(remote.in.sin_addr.s_addr);
if (asprintf(&r,
- "%u-%u.%u.%u.%u-%u-%u.%u.%u.%u-%u",
+ "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
nr,
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
ntohs(local.in.sin_port),
@@ -322,7 +305,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
if (asprintf(&r,
- "%u-%s-%u-%s-%u",
+ "%u-%s:%u-%s:%u",
nr,
inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
ntohs(local.in6.sin6_port),
@@ -368,7 +351,15 @@ static void socket_close_fds(Socket *s) {
continue;
unit_unwatch_fd(UNIT(s), &p->fd_watch);
- assert_se(close_nointr(p->fd) >= 0);
+ close_nointr_nofail(p->fd);
+
+ /* One little note: we should never delete any sockets
+ * in the file system here! After all some other
+ * process we spawned might still have a reference of
+ * this fd and wants to continue to use it. Therefore
+ * we delete sockets in the file system before we
+ * create a new one, not after we stopped using
+ * one! */
p->fd = -1;
}
@@ -490,8 +481,12 @@ static void socket_set_state(Socket *s, SocketState state) {
unit_unwatch_timer(UNIT(s), &s->timer_watch);
socket_unwatch_control_pid(s);
s->control_command = NULL;
+ s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
}
+ if (state != SOCKET_LISTENING)
+ socket_unwatch_fds(s);
+
if (state != SOCKET_START_POST &&
state != SOCKET_LISTENING &&
state != SOCKET_RUNNING &&
@@ -500,15 +495,62 @@ static void socket_set_state(Socket *s, SocketState state) {
state != SOCKET_STOP_PRE_SIGKILL)
socket_close_fds(s);
- if (state != SOCKET_LISTENING)
- socket_unwatch_fds(s);
-
if (state != old_state)
- log_debug("%s changed %s → %s", s->meta.id, state_string_table[old_state], state_string_table[state]);
+ log_debug("%s changed %s → %s",
+ s->meta.id,
+ socket_state_to_string(old_state),
+ socket_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
}
+static int socket_coldplug(Unit *u) {
+ Socket *s = SOCKET(u);
+ int r;
+
+ assert(s);
+ assert(s->state == SOCKET_DEAD);
+
+ if (s->deserialized_state != s->state) {
+
+ if (s->deserialized_state == SOCKET_START_PRE ||
+ s->deserialized_state == SOCKET_START_POST ||
+ s->deserialized_state == SOCKET_STOP_PRE ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGKILL ||
+ s->deserialized_state == SOCKET_STOP_POST ||
+ s->deserialized_state == SOCKET_FINAL_SIGTERM ||
+ s->deserialized_state == SOCKET_FINAL_SIGKILL) {
+
+ if (s->control_pid <= 0)
+ return -EBADMSG;
+
+ if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ return r;
+
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ return r;
+ }
+
+ if (s->deserialized_state == SOCKET_START_POST ||
+ s->deserialized_state == SOCKET_LISTENING ||
+ s->deserialized_state == SOCKET_RUNNING ||
+ s->deserialized_state == SOCKET_STOP_PRE ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGKILL)
+ if ((r = socket_open_fds(s)) < 0)
+ return r;
+
+ if (s->deserialized_state == SOCKET_LISTENING)
+ if ((r = socket_watch_fds(s)) < 0)
+ return r;
+
+ socket_set_state(s, s->deserialized_state);
+ }
+
+ return 0;
+}
+
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
@@ -574,6 +616,8 @@ static void socket_enter_stop_post(Socket *s, bool success) {
socket_unwatch_control_pid(s);
+ s->control_command_id = SOCKET_EXEC_STOP_POST;
+
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) {
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
goto fail;
@@ -647,6 +691,8 @@ static void socket_enter_stop_pre(Socket *s, bool success) {
socket_unwatch_control_pid(s);
+ s->control_command_id = SOCKET_EXEC_STOP_PRE;
+
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) {
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
goto fail;
@@ -689,6 +735,8 @@ static void socket_enter_start_post(Socket *s) {
socket_unwatch_control_pid(s);
+ s->control_command_id = SOCKET_EXEC_START_POST;
+
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) {
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) {
log_warning("%s failed to run start-post executable: %s", s->meta.id, strerror(-r));
@@ -711,6 +759,8 @@ static void socket_enter_start_pre(Socket *s) {
socket_unwatch_control_pid(s);
+ s->control_command_id = SOCKET_EXEC_START_PRE;
+
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) {
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
goto fail;
@@ -876,6 +926,142 @@ static int socket_stop(Unit *u) {
return 0;
}
+static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Socket *s = SOCKET(u);
+ SocketPort *p;
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", socket_state_to_string(s->state));
+ unit_serialize_item(u, f, "failure", yes_no(s->failure));
+ unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
+
+ if (s->control_pid > 0)
+ unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) s->control_pid);
+
+ if (s->control_command_id >= 0)
+ unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id));
+
+ LIST_FOREACH(port, p, s->ports) {
+ int copy;
+
+ if (p->fd < 0)
+ continue;
+
+ if ((copy = fdset_put_dup(fds, p->fd)) < 0)
+ return copy;
+
+ if (p->type == SOCKET_SOCKET) {
+ char *t;
+
+ if ((r = socket_address_print(&p->address, &t)) < 0)
+ return r;
+
+ unit_serialize_item_format(u, f, "socket", "%i %s", copy, t);
+ free(t);
+ } else {
+ assert(p->type == SOCKET_FIFO);
+ unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
+ }
+ }
+
+ return 0;
+}
+
+static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Socket *s = SOCKET(u);
+ int r;
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ SocketState state;
+
+ if ((state = socket_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+ } else if (streq(key, "failure")) {
+ int b;
+
+ if ((b = parse_boolean(value)) < 0)
+ log_debug("Failed to parse failure value %s", value);
+ else
+ s->failure = b || s->failure;
+
+ } else if (streq(key, "n-accepted")) {
+ unsigned k;
+
+ if ((r = safe_atou(value, &k)) < 0)
+ log_debug("Failed to parse n-accepted value %s", value);
+ else
+ s->n_accepted += k;
+ } else if (streq(key, "control-pid")) {
+ unsigned pid;
+
+ if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
+ log_debug("Failed to parse control-pid value %s", value);
+ else
+ s->control_pid = (pid_t) pid;
+ } else if (streq(key, "control-command")) {
+ SocketExecCommand id;
+
+ if ((id = socket_exec_command_from_string(value)) < 0)
+ log_debug("Failed to parse exec-command value %s", value);
+ else {
+ s->control_command_id = id;
+ s->control_command = s->exec_command[id];
+ }
+ } else if (streq(key, "fifo")) {
+ int fd, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse fifo value %s", value);
+ else {
+
+ LIST_FOREACH(port, p, s->ports)
+ if (streq(p->path, value+skip))
+ break;
+
+ if (p) {
+ if (p->fd >= 0)
+ close_nointr_nofail(p->fd);
+ p->fd = fdset_remove(fds, fd);
+ }
+ }
+
+ } else if (streq(key, "socket")) {
+ int fd, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse socket value %s", value);
+ else {
+
+ LIST_FOREACH(port, p, s->ports)
+ if (socket_address_is(&p->address, value+skip))
+ break;
+
+ if (p) {
+ if (p->fd >= 0)
+ close_nointr_nofail(p->fd);
+ p->fd = fdset_remove(fds, fd);
+ }
+ }
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
static UnitActiveState socket_active_state(Unit *u) {
assert(u);
@@ -885,7 +1071,7 @@ static UnitActiveState socket_active_state(Unit *u) {
static const char *socket_sub_state_to_string(Unit *u) {
assert(u);
- return state_string_table[SOCKET(u)->state];
+ return socket_state_to_string(SOCKET(u)->state);
}
static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
@@ -918,6 +1104,7 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
}
}
+ log_debug("cfd=%i", cfd);
socket_enter_running(s, cfd);
return;
@@ -936,21 +1123,24 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->failure = s->failure || !success;
assert(s->control_pid == pid);
- assert(s->control_command);
-
- exec_status_fill(&s->control_command->exec_status, pid, code, status);
s->control_pid = 0;
+ if (s->control_command)
+ exec_status_fill(&s->control_command->exec_status, pid, code, status);
+
log_debug("%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status);
- if (s->control_command->command_next && success) {
- log_debug("%s running next command for state %s", u->meta.id, state_string_table[s->state]);
+ if (s->control_command && s->control_command->command_next && success) {
+ log_debug("%s running next command for state %s", u->meta.id, socket_state_to_string(s->state));
socket_run_next(s, success);
} else {
+ s->control_command = NULL;
+ s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+
/* No further commands for this step, so let's figure
* out what to do next */
- log_debug("%s got final SIGCHLD for state %s", u->meta.id, state_string_table[s->state]);
+ log_debug("%s got final SIGCHLD for state %s", u->meta.id, socket_state_to_string(s->state));
switch (s->state) {
@@ -1082,18 +1272,49 @@ void socket_notify_service_dead(Socket *s) {
}
}
+static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
+ [SOCKET_DEAD] = "dead",
+ [SOCKET_START_PRE] = "start-pre",
+ [SOCKET_START_POST] = "start-post",
+ [SOCKET_LISTENING] = "listening",
+ [SOCKET_RUNNING] = "running",
+ [SOCKET_STOP_PRE] = "stop-pre",
+ [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
+ [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
+ [SOCKET_STOP_POST] = "stop-post",
+ [SOCKET_FINAL_SIGTERM] = "final-sigterm",
+ [SOCKET_FINAL_SIGKILL] = "final-sigkill",
+ [SOCKET_MAINTAINANCE] = "maintainance"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
+
+static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
+ [SOCKET_EXEC_START_PRE] = "StartPre",
+ [SOCKET_EXEC_START_POST] = "StartPost",
+ [SOCKET_EXEC_STOP_PRE] = "StopPre",
+ [SOCKET_EXEC_STOP_POST] = "StopPost"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
+
const UnitVTable socket_vtable = {
.suffix = ".socket",
.init = socket_init,
- .load = socket_load,
.done = socket_done,
+ .load = socket_load,
+
+ .coldplug = socket_coldplug,
.dump = socket_dump,
.start = socket_start,
.stop = socket_stop,
+ .serialize = socket_serialize,
+ .deserialize_item = socket_deserialize_item,
+
.active_state = socket_active_state,
.sub_state_to_string = socket_sub_state_to_string,
diff --git a/socket.h b/socket.h
index 5105adfb84..eafd64d5e3 100644
--- a/socket.h
+++ b/socket.h
@@ -91,11 +91,12 @@ struct Socket {
Service *service;
- SocketState state;
+ SocketState state, deserialized_state;
KillMode kill_mode;
ExecCommand* control_command;
+ SocketExecCommand control_command_id;
pid_t control_pid;
char *bind_to_device;
@@ -117,4 +118,10 @@ void socket_notify_service_dead(Socket *s);
extern const UnitVTable socket_vtable;
+const char* socket_state_to_string(SocketState i);
+SocketState socket_state_from_string(const char *s);
+
+const char* socket_exec_command_to_string(SocketExecCommand i);
+SocketExecCommand socket_exec_command_from_string(const char *s);
+
#endif
diff --git a/systemctl.vala b/systemctl.vala
index 4de856e756..c435e558e8 100644
--- a/systemctl.vala
+++ b/systemctl.vala
@@ -85,7 +85,9 @@ int main (string[] args) {
" reload [NAME...] Reload on or more units\n" +
" monitor Monitor unit/job changes\n" +
" dump Dump servier status\n" +
- " snapshot [NAME] Create a snapshot\n");
+ " snapshot [NAME] Create a snapshot\n" +
+ " daemon-reload Reload daemon configuration\n" +
+ " daemon-reexecute Reexecute daemon\n");
try {
context.parse(ref args);
@@ -236,7 +238,13 @@ int main (string[] args) {
"org.freedesktop.systemd1.Unit") as Unit;
stdout.printf("%s\n", u.id);
- } else {
+ } else if (args[1] == "daemon-reload")
+ manager.reload();
+ else if (args[1] == "daemon-reexecute" || args[1] == "daemon-reexec")
+ manager.reexecute();
+ else if (args[1] == "daemon-exit")
+ manager.exit();
+ else {
stderr.printf("Unknown command %s.\n", args[1]);
return 1;
}
diff --git a/systemd-interfaces.vala b/systemd-interfaces.vala
index 3b65d3c90b..4e845c0bbf 100644
--- a/systemd-interfaces.vala
+++ b/systemd-interfaces.vala
@@ -57,6 +57,10 @@ public interface Manager : DBus.Object {
public abstract string dump() throws DBus.Error;
+ public abstract void reload() throws DBus.Error;
+ public abstract void reexecute() throws DBus.Error;
+ public abstract void exit() throws DBus.Error;
+
public abstract ObjectPath create_snapshot(string name, bool cleanup = false) throws DBus.Error;
public abstract signal void unit_new(string id, ObjectPath path);
diff --git a/target.c b/target.c
index 61cbebf654..32c2256e27 100644
--- a/target.c
+++ b/target.c
@@ -33,22 +33,6 @@ static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
[TARGET_ACTIVE] = UNIT_ACTIVE
};
-static const char* const state_string_table[_TARGET_STATE_MAX] = {
- [TARGET_DEAD] = "dead",
- [TARGET_ACTIVE] = "active"
-};
-
-static void target_dump(Unit *u, FILE *f, const char *prefix) {
- Target *t = TARGET(u);
-
- assert(t);
- assert(f);
-
- fprintf(f,
- "%sTarget State: %s\n",
- prefix, state_string_table[t->state]);
-}
-
static void target_set_state(Target *t, TargetState state) {
TargetState old_state;
assert(t);
@@ -57,11 +41,37 @@ static void target_set_state(Target *t, TargetState state) {
t->state = state;
if (state != old_state)
- log_debug("%s changed %s → %s", UNIT(t)->meta.id, state_string_table[old_state], state_string_table[state]);
+ log_debug("%s changed %s → %s",
+ UNIT(t)->meta.id,
+ target_state_to_string(old_state),
+ target_state_to_string(state));
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]);
}
+static int target_coldplug(Unit *u) {
+ Target *t = TARGET(u);
+
+ assert(t);
+ assert(t->state == TARGET_DEAD);
+
+ if (t->deserialized_state != t->state)
+ target_set_state(t, t->deserialized_state);
+
+ return 0;
+}
+
+static void target_dump(Unit *u, FILE *f, const char *prefix) {
+ Target *t = TARGET(u);
+
+ assert(t);
+ assert(f);
+
+ fprintf(f,
+ "%sTarget State: %s\n",
+ prefix, target_state_to_string(t->state));
+}
+
static int target_start(Unit *u) {
Target *t = TARGET(u);
@@ -82,6 +92,39 @@ static int target_stop(Unit *u) {
return 0;
}
+static int target_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Target *s = TARGET(u);
+
+ assert(s);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", target_state_to_string(s->state));
+ return 0;
+}
+
+static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Target *s = TARGET(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ TargetState state;
+
+ if ((state = target_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
static UnitActiveState target_active_state(Unit *u) {
assert(u);
@@ -91,7 +134,7 @@ static UnitActiveState target_active_state(Unit *u) {
static const char *target_sub_state_to_string(Unit *u) {
assert(u);
- return state_string_table[TARGET(u)->state];
+ return target_state_to_string(TARGET(u)->state);
}
int target_get_runlevel(Target *t) {
@@ -123,16 +166,27 @@ int target_get_runlevel(Target *t) {
return 0;
}
+static const char* const target_state_table[_TARGET_STATE_MAX] = {
+ [TARGET_DEAD] = "dead",
+ [TARGET_ACTIVE] = "active"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
+
const UnitVTable target_vtable = {
.suffix = ".target",
.load = unit_load_fragment_and_dropin,
+ .coldplug = target_coldplug,
.dump = target_dump,
.start = target_start,
.stop = target_stop,
+ .serialize = target_serialize,
+ .deserialize_item = target_deserialize_item,
+
.active_state = target_active_state,
.sub_state_to_string = target_sub_state_to_string,
diff --git a/target.h b/target.h
index 4b04635f94..5397d50d7c 100644
--- a/target.h
+++ b/target.h
@@ -36,11 +36,14 @@ typedef enum TargetState {
struct Target {
Meta meta;
- TargetState state;
+ TargetState state, deserialized_state;
};
extern const UnitVTable target_vtable;
int target_get_runlevel(Target *t);
+const char* target_state_to_string(TargetState i);
+TargetState target_state_from_string(const char *s);
+
#endif
diff --git a/unit-name.c b/unit-name.c
index 5053d30f29..5d428dd446 100644
--- a/unit-name.c
+++ b/unit-name.c
@@ -371,3 +371,43 @@ char *unit_name_template(const char *f) {
return r;
}
+
+char *unit_name_from_path(const char *path, const char *suffix) {
+ assert(path);
+ assert(suffix);
+
+ if (path[0] == '/')
+ path++;
+
+ if (path[0] == 0)
+ return strappend("-", suffix);
+
+ return unit_name_build_escape(path, NULL, suffix);
+}
+
+char *unit_name_to_path(const char *name) {
+ char *w, *e;
+
+ assert(name);
+
+ if (!(w = unit_name_to_prefix(name)))
+ return NULL;
+
+ e = unit_name_unescape(w);
+ free(w);
+
+ if (!e)
+ return NULL;
+
+ if (e[0] != '/') {
+ w = strappend("/", e);
+ free(e);
+
+ if (!w)
+ return NULL;
+
+ e = w;
+ }
+
+ return e;
+}
diff --git a/unit-name.h b/unit-name.h
index 587741ba41..b6dd2c9123 100644
--- a/unit-name.h
+++ b/unit-name.h
@@ -48,4 +48,7 @@ char *unit_name_replace_instance(const char *f, const char *i);
char *unit_name_template(const char *f);
+char *unit_name_from_path(const char *path, const char *suffix);
+char *unit_name_to_path(const char *name);
+
#endif
diff --git a/unit.c b/unit.c
index f8589fe605..0d459d6760 100644
--- a/unit.c
+++ b/unit.c
@@ -615,6 +615,16 @@ int unit_load_fragment_and_dropin_optional(Unit *u) {
return 0;
}
+/* Common implementation for multiple backends */
+int unit_load_nop(Unit *u) {
+ assert(u);
+
+ if (u->meta.load_state == UNIT_STUB)
+ u->meta.load_state = UNIT_LOADED;
+
+ return 0;
+}
+
int unit_load(Unit *u) {
int r;
@@ -1109,7 +1119,7 @@ void unit_unwatch_timer(Unit *u, Watch *w) {
assert(w->type == WATCH_TIMER && w->data.unit == u);
assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
- assert_se(close_nointr(w->fd) == 0);
+ close_nointr_nofail(w->fd);
w->fd = -1;
w->type = WATCH_INVALID;
@@ -1495,6 +1505,29 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
return r;
}
+int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
+ Unit *found;
+ char *t;
+
+ assert(u);
+ assert(type);
+ assert(_found);
+
+ if (!(t = unit_name_change_suffix(u->meta.id, type)))
+ return -ENOMEM;
+
+ assert(!unit_has_name(u, t));
+
+ found = manager_get_unit(u->meta.manager, t);
+ free(t);
+
+ if (!found)
+ return -ENOENT;
+
+ *_found = found;
+ return 0;
+}
+
static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
Unit *u = userdata;
assert(u);
@@ -1627,6 +1660,97 @@ void unit_unwatch_bus_name(Unit *u, const char *name) {
hashmap_remove_value(u->meta.manager->watch_bus, name, u);
}
+bool unit_can_serialize(Unit *u) {
+ assert(u);
+
+ return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
+}
+
+int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ if (!unit_can_serialize(u))
+ return 0;
+
+ if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
+ return r;
+
+ /* End marker */
+ fputc('\n', f);
+ return 0;
+}
+
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
+ va_list ap;
+
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(format);
+
+ fputs(key, f);
+ fputc('=', f);
+
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+
+ fputc('\n', f);
+}
+
+void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(value);
+
+ fprintf(f, "%s=%s\n", key, value);
+}
+
+int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ if (!unit_can_serialize(u))
+ return 0;
+
+ for (;;) {
+ char line[1024], *l, *v;
+ size_t k;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ return 0;
+ return -errno;
+ }
+
+ l = strstrip(line);
+
+ /* End marker */
+ if (l[0] == 0)
+ return 0;
+
+ k = strcspn(l, "=");
+
+ if (l[k] == '=') {
+ l[k] = 0;
+ v = l+k+1;
+ } else
+ v = l+k;
+
+ if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
+ return r;
+ }
+}
+
+
static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = "service",
[UNIT_TIMER] = "timer",
diff --git a/unit.h b/unit.h
index b3c75ce20a..76aa0eff5f 100644
--- a/unit.h
+++ b/unit.h
@@ -209,24 +209,24 @@ struct UnitVTable {
/* Instances make no sense for this type */
bool no_instances:1;
- /* Execlude this type from snapshots */
+ /* Exclude this type from snapshots */
bool no_snapshots:1;
/* This should reset all type-specific variables. This should
- * not allocate memory, and is either called with 0
- * initialized data, or with data left from done() */
+ * not allocate memory, and is called with zero-initialized
+ * data. It should hence only initialize variables that need
+ * to be set != 0. */
void (*init)(Unit *u);
+ /* This should free all type-specific variables. It should be
+ * idempotent. */
+ void (*done)(Unit *u);
+
/* Actually load data from disk. This may fail, and should set
* load_state to UNIT_LOADED, UNIT_MERGED or leave it at
* UNIT_STUB if no configuration could be found. */
int (*load)(Unit *u);
- /* This should free all type-specific variables. It should be
- * idempotent. There's no need to reset variables that deal
- * with dynamic memory/resources. */
- void (*done)(Unit *u);
-
/* If a a lot of units got created via enumerate(), this is
* where to actually set the state and call unit_notify(). */
int (*coldplug)(Unit *u);
@@ -239,6 +239,13 @@ struct UnitVTable {
bool (*can_reload)(Unit *u);
+ /* Write all data that cannot be restored from other sources
+ * away using unit_serialize_item() */
+ int (*serialize)(Unit *u, FILE *f, FDSet *fds);
+
+ /* Restore one item from the serialization */
+ int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds);
+
/* Boils down the more complex internal state of this unit to
* a simpler one that the engine can understand */
UnitActiveState (*active_state)(Unit *u);
@@ -333,6 +340,7 @@ Unit *unit_follow_merge(Unit *u);
int unit_load_fragment_and_dropin(Unit *u);
int unit_load_fragment_and_dropin_optional(Unit *u);
+int unit_load_nop(Unit *u);
int unit_load(Unit *unit);
const char *unit_description(Unit *u);
@@ -373,11 +381,18 @@ int set_unit_path(const char *p);
char *unit_dbus_path(Unit *u);
int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
+int unit_get_related_unit(Unit *u, const char *type, Unit **_found);
char *unit_name_printf(Unit *u, const char* text);
char *unit_full_printf(Unit *u, const char *text);
char **unit_full_printf_strv(Unit *u, char **l);
+bool unit_can_serialize(Unit *u);
+int unit_serialize(Unit *u, FILE *f, FDSet *fds);
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr(4,5);
+void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
+int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
+
const char *unit_type_to_string(UnitType i);
UnitType unit_type_from_string(const char *s);
diff --git a/util.c b/util.c
index dd4dc097a9..49f5b4be13 100644
--- a/util.c
+++ b/util.c
@@ -1215,7 +1215,7 @@ int close_all_fds(const int except[], unsigned n_except) {
while ((de = readdir(d))) {
int fd = -1;
- if (de->d_name[0] == '.')
+ if (ignore_file(de->d_name))
continue;
if ((r = safe_atoi(de->d_name, &fd)) < 0)
@@ -1324,7 +1324,7 @@ int chvt(int vt) {
if (ioctl(fd, VT_ACTIVATE, vt) < 0)
r = -errno;
- close_nointr(r);
+ close_nointr_nofail(r);
return r;
}
@@ -1612,7 +1612,7 @@ int acquire_terminal(const char *name, bool fail, bool force) {
}
if (notify >= 0)
- close_nointr(notify);
+ close_nointr_nofail(notify);
if ((r = reset_terminal(fd)) < 0)
log_warning("Failed to reset terminal: %s", strerror(-r));
@@ -1621,10 +1621,10 @@ int acquire_terminal(const char *name, bool fail, bool force) {
fail:
if (fd >= 0)
- close_nointr(fd);
+ close_nointr_nofail(fd);
if (notify >= 0)
- close_nointr(notify);
+ close_nointr_nofail(notify);
return r;
}