reload: implement reload/reexec logic

This commit is contained in:
Lennart Poettering 2010-04-21 03:27:44 +02:00
parent 0d9068141e
commit a16e112358
37 changed files with 2217 additions and 522 deletions

View File

@ -32,7 +32,11 @@ AM_CPPFLAGS = \
-DSYSTEM_SYSVRCND_PATH=\"$(SYSTEM_SYSVRCND_PATH)\" \
-DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
-DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\"
-DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\" \
-DSYSTEMD_BINARY_PATH=\"$(sbindir)/systemd\"
# -DSYSTEMD_BINARY_PATH=\"/home/lennart/projects/systemd/systemd\"
sbin_PROGRAMS = \
systemd
@ -155,7 +159,9 @@ COMMON_SOURCES= \
specifier.c \
specifier.h \
unit-name.c \
unit-name.h
unit-name.h \
fdset.c \
fdset.h
systemd_SOURCES = \
$(COMMON_SOURCES) \

View File

@ -43,21 +43,7 @@ static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_MAINTAINANCE] = UNIT_INACTIVE,
};
static const char* const state_string_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
};
static char *automount_name_from_where(const char *where) {
assert(where);
if (streq(where, "/"))
return strdup("-.automount");
return unit_name_build_escape(where+1, NULL, ".automount");
}
static int open_dev_autofs(Manager *m);
static void automount_init(Unit *u) {
Automount *a = AUTOMOUNT(u);
@ -66,6 +52,7 @@ static void automount_init(Unit *u) {
assert(u->meta.load_state == UNIT_STUB);
a->pipe_watch.fd = a->pipe_fd = -1;
a->pipe_watch.type = WATCH_INVALID;
}
static void repeat_unmout(const char *path) {
@ -95,7 +82,12 @@ static void unmount_autofs(Automount *a) {
close_nointr_nofail(a->pipe_fd);
a->pipe_fd = -1;
repeat_unmout(a->where);
/* If we reload/reexecute things we keep the mount point
* around */
if (a->where &&
(UNIT(a)->meta.manager->exit_code != MANAGER_RELOAD &&
UNIT(a)->meta.manager->exit_code != MANAGER_REEXECUTE))
repeat_unmout(a->where);
}
static void automount_done(Unit *u) {
@ -106,10 +98,11 @@ static void automount_done(Unit *u) {
unmount_autofs(a);
a->mount = NULL;
if (a->tokens) {
set_free(a->tokens);
a->tokens = NULL;
}
free(a->where);
a->where = NULL;
set_free(a->tokens);
a->tokens = NULL;
}
static int automount_verify(Automount *a) {
@ -120,14 +113,7 @@ static int automount_verify(Automount *a) {
if (UNIT(a)->meta.load_state != UNIT_LOADED)
return 0;
if (!a->where) {
log_error("%s lacks Where setting. Refusing.", UNIT(a)->meta.id);
return -EINVAL;
}
path_kill_slashes(a->where);
if (!(e = automount_name_from_where(a->where)))
if (!(e = unit_name_from_path(a->where, ".automount")))
return -ENOMEM;
b = unit_has_name(UNIT(a), e);
@ -154,6 +140,12 @@ static int automount_load(Unit *u) {
if (u->meta.load_state == UNIT_LOADED) {
if (!a->where)
if (!(a->where = unit_name_to_path(u->meta.id)))
return -ENOMEM;
path_kill_slashes(a->where);
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
return r;
@ -176,19 +168,51 @@ static void automount_set_state(Automount *a, AutomountState state) {
unmount_autofs(a);
if (state != old_state)
log_debug("%s changed %s → %s", UNIT(a)->meta.id, state_string_table[old_state], state_string_table[state]);
log_debug("%s changed %s → %s",
UNIT(a)->meta.id,
automount_state_to_string(old_state),
automount_state_to_string(state));
unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state]);
}
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
Automount *s = AUTOMOUNT(u);
static int automount_coldplug(Unit *u) {
Automount *a = AUTOMOUNT(u);
int r;
assert(s);
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 *a = AUTOMOUNT(u);
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,

View File

@ -38,7 +38,7 @@ typedef enum AutomountState {
struct Automount {
Meta meta;
AutomountState state;
AutomountState state, deserialized_state;
char *where;
@ -57,4 +57,7 @@ extern const UnitVTable automount_vtable;
int automount_send_ready(Automount *a, int status);
const char* automount_state_to_string(AutomountState i);
AutomountState automount_state_from_string(const char *s);
#endif

View File

@ -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:

View File

@ -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);

View File

@ -56,6 +56,9 @@
" <arg nane=\"cleanup\" type=\"b\" direction=\"in\"/>" \
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>" \
" </method>" \
" <method name=\"Reload\"/>" \
" <method name=\"Reexecute\"/>" \
" <method name=\"Exit\"/>" \
" <signal name=\"UnitNew\">" \
" <arg name=\"id\" type=\"s\"/>" \
" <arg name=\"unit\" type=\"o\"/>" \
@ -504,6 +507,37 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection
free(introspection);
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
assert(!m->queued_message);
/* Instead of sending the reply back right away, we
* just remember that we need to and then send it
* after the reload is finished. That way the caller
* knows when the reload finished. */
if (!(m->queued_message = dbus_message_new_method_return(message)))
goto oom;
m->exit_code = MANAGER_RELOAD;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
m->exit_code = MANAGER_REEXECUTE;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
if (m->running_as == MANAGER_INIT)
return bus_send_error_reply(m, message, NULL, -ENOTSUP);
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
m->exit_code = MANAGER_EXIT;
} else
return bus_default_message_handler(m, message, NULL, properties);

17
dbus.c
View File

@ -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) {

View File

@ -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,29 +336,31 @@ 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;
}
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;
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
return -errno;
}
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);
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 (!(e = udev_enumerate_new(m->udev))) {
r = -ENOMEM;
goto fail;
@ -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,

View File

@ -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

View File

@ -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) {

162
fdset.c Normal file
View File

@ -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;
}

40
fdset.h Normal file
View File

@ -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

View File

@ -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);

4
log.c
View File

@ -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;
}
}

View File

@ -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) {

268
main.c
View File

@ -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,53 +637,83 @@ 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));
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;
}
}
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 jobs:\n");
manager_dump_jobs(m, stdout, "\t");
retval = 0;
goto finish;
}
}
if (action == ACTION_TEST) {
printf("→ By units:\n");
manager_dump_units(m, stdout, "\t");
for (;;) {
if ((r = manager_loop(m)) < 0) {
log_error("Failed to run mainloop: %s", strerror(-r));
goto finish;
}
switch (m->exit_code) {
case MANAGER_EXIT:
retval = 0;
log_debug("Exit.");
goto finish;
case MANAGER_RELOAD:
if ((r = manager_reload(m)) < 0)
log_error("Failed to reload: %s", strerror(-r));
break;
case MANAGER_REEXECUTE:
if (prepare_reexecute(m, &serialization, &fds) < 0)
goto finish;
reexecute = true;
log_debug("Reexecuting.");
goto finish;
default:
assert_not_reached("Unknown exit code.");
}
}
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 jobs:\n");
manager_dump_jobs(m, stdout, "\t");
if (getpid() == 1)
pause();
retval = 0;
goto finish;
}
if ((r = manager_loop(m)) < 0) {
log_error("Failed to run mainloop: %s", strerror(-r));
goto finish;
}
retval = 0;
log_debug("Exit.");
finish:
if (m)
manager_free(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();

252
manager.c
View File

@ -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);
}
manager_dispatch_cleanup_queue(m);
void manager_free(Manager *m) {
UnitType c;
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,30 +438,36 @@ 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;
assert(m);
/* 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 ((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);
/* First, 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;
manager_dispatch_load_queue(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",

View File

@ -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);

429
mount.c
View File

@ -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);
if (streq(where, "/"))
return strdup("-.mount");
m->timeout_usec = DEFAULT_TIMEOUT_USEC;
exec_context_init(&m->exec_context);
return unit_name_build_escape(where+1, NULL, ".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;
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,23 +385,98 @@ 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;
if (new_state != m->state) {
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) {
if (m->control_pid <= 0)
return -EBADMSG;
if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0)
return r;
if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
return r;
}
mount_set_state(m, new_state);
}
return 0;
}
static void mount_dump(Unit *u, FILE *f, const char *prefix) {
Mount *m = MOUNT(u);
MountParameters *p;
assert(m);
assert(f);
if (m->from_proc_self_mountinfo)
p = &m->parameters_proc_self_mountinfo;
else if (m->from_fragment)
p = &m->parameters_fragment;
else
p = &m->parameters_etc_fstab;
fprintf(f,
"%sMount State: %s\n"
"%sWhere: %s\n"
"%sWhat: %s\n"
"%sFile System Type: %s\n"
"%sOptions: %s\n"
"%sFrom /etc/fstab: %s\n"
"%sFrom /proc/self/mountinfo: %s\n"
"%sFrom fragment: %s\n"
"%sKillMode: %s\n",
prefix, mount_state_to_string(m->state),
prefix, m->where,
prefix, strna(p->what),
prefix, strna(p->fstype),
prefix, strna(p->options),
prefix, yes_no(m->from_etc_fstab),
prefix, yes_no(m->from_proc_self_mountinfo),
prefix, yes_no(m->from_fragment),
prefix, kill_mode_to_string(m->kill_mode));
if (m->control_pid > 0)
fprintf(f,
"%sControl PID: %llu\n",
prefix, (unsigned long long) m->control_pid);
exec_context_dump(&m->exec_context, f, prefix);
}
static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
@ -453,48 +513,6 @@ fail:
return r;
}
static void mount_dump(Unit *u, FILE *f, const char *prefix) {
Mount *m = MOUNT(u);
MountParameters *p;
assert(m);
assert(f);
if (m->from_proc_self_mountinfo)
p = &m->parameters_proc_self_mountinfo;
else if (m->from_fragment)
p = &m->parameters_fragment;
else
p = &m->parameters_etc_fstab;
fprintf(f,
"%sMount State: %s\n"
"%sWhere: %s\n"
"%sWhat: %s\n"
"%sFile System Type: %s\n"
"%sOptions: %s\n"
"%sFrom /etc/fstab: %s\n"
"%sFrom /proc/self/mountinfo: %s\n"
"%sFrom fragment: %s\n"
"%sKillMode: %s\n",
prefix, state_string_table[m->state],
prefix, m->where,
prefix, strna(p->what),
prefix, strna(p->fstype),
prefix, strna(p->options),
prefix, yes_no(m->from_etc_fstab),
prefix, yes_no(m->from_proc_self_mountinfo),
prefix, yes_no(m->from_fragment),
prefix, kill_mode_to_string(m->kill_mode));
if (m->control_pid > 0)
fprintf(f,
"%sControl PID: %llu\n",
prefix, (unsigned long long) m->control_pid);
exec_context_dump(&m->exec_context, f, prefix);
}
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,

View File

@ -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

226
service.c
View File

@ -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,

View File

@ -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;

View File

@ -31,31 +31,30 @@ static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
[SNAPSHOT_ACTIVE] = UNIT_ACTIVE
};
static const char* const state_string_table[_SNAPSHOT_STATE_MAX] = {
[SNAPSHOT_DEAD] = "dead",
[SNAPSHOT_ACTIVE] = "active"
};
static void snapshot_set_state(Snapshot *s, SnapshotState state) {
SnapshotState old_state;
assert(s);
static int snapshot_load(Unit *u) {
Iterator i;
Unit *other;
int r;
old_state = s->state;
s->state = state;
assert(u);
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));
HASHMAP_FOREACH(other, u->meta.manager->units, i) {
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
}
if (UNIT_VTABLE(other)->no_snapshots)
continue;
static int snapshot_coldplug(Unit *u) {
Snapshot *s = SNAPSHOT(u);
if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0)
return r;
assert(s);
assert(s->state == SNAPSHOT_DEAD);
if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0)
return r;
}
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,

View File

@ -36,7 +36,7 @@ typedef enum SnapshotState {
struct Snapshot {
Meta meta;
SnapshotState state;
SnapshotState state, deserialized_state;
bool cleanup;
};
@ -46,4 +46,7 @@ extern const UnitVTable snapshot_vtable;
int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **s);
void snapshot_remove(Snapshot *s);
const char* snapshot_state_to_string(SnapshotState i);
SnapshotState snapshot_state_from_string(const char *s);
#endif

View File

@ -316,7 +316,7 @@ int socket_address_listen(
if ((r = socket_address_verify(a)) < 0)
return r;
if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK, 0)) < 0)
if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
return -errno;
if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
@ -370,7 +370,7 @@ int socket_address_listen(
fail:
r = -errno;
close_nointr(fd);
close_nointr_nofail(fd);
return r;
}
@ -381,3 +381,77 @@ bool socket_address_can_accept(const SocketAddress *a) {
a->type == SOCK_STREAM ||
a->type == SOCK_SEQPACKET;
}
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
assert(a);
assert(b);
/* Invalid addresses are unequal to all */
if (socket_address_verify(a) < 0 ||
socket_address_verify(b) < 0)
return false;
if (a->type != b->type)
return false;
if (a->size != b->size)
return false;
if (socket_address_family(a) != socket_address_family(b))
return false;
switch (socket_address_family(a)) {
case AF_INET:
if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
return false;
if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
return false;
break;
case AF_INET6:
if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
return false;
if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
return false;
break;
case AF_UNIX:
if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
return false;
if (a->sockaddr.un.sun_path[0]) {
if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
return false;
} else {
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
return false;
}
break;
default:
/* Cannot compare, so we assume the addresses are different */
return false;
}
return true;
}
bool socket_address_is(const SocketAddress *a, const char *s) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse(&b, s) < 0)
return false;
return socket_address_equal(a, &b);
}

View File

@ -70,4 +70,8 @@ int socket_address_listen(
mode_t socket_mode,
int *ret);
bool socket_address_is(const SocketAddress *a, const char *s);
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
#endif

331
socket.c
View File

@ -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,

View File

@ -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

View File

@ -85,7 +85,9 @@ int main (string[] args) {
" reload [NAME...] Reload on or more units\n" +
" monitor Monitor unit/job changes\n" +
" dump Dump servier status\n" +
" snapshot [NAME] Create a snapshot\n");
" snapshot [NAME] Create a snapshot\n" +
" daemon-reload Reload daemon configuration\n" +
" daemon-reexecute Reexecute daemon\n");
try {
context.parse(ref args);
@ -236,7 +238,13 @@ int main (string[] args) {
"org.freedesktop.systemd1.Unit") as Unit;
stdout.printf("%s\n", u.id);
} else {
} else if (args[1] == "daemon-reload")
manager.reload();
else if (args[1] == "daemon-reexecute" || args[1] == "daemon-reexec")
manager.reexecute();
else if (args[1] == "daemon-exit")
manager.exit();
else {
stderr.printf("Unknown command %s.\n", args[1]);
return 1;
}

View File

@ -57,6 +57,10 @@ public interface Manager : DBus.Object {
public abstract string dump() throws DBus.Error;
public abstract void reload() throws DBus.Error;
public abstract void reexecute() throws DBus.Error;
public abstract void exit() throws DBus.Error;
public abstract ObjectPath create_snapshot(string name, bool cleanup = false) throws DBus.Error;
public abstract signal void unit_new(string id, ObjectPath path);

View File

@ -33,10 +33,33 @@ 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_set_state(Target *t, TargetState state) {
TargetState old_state;
assert(t);
old_state = t->state;
t->state = state;
if (state != old_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);
@ -46,20 +69,7 @@ static void target_dump(Unit *u, FILE *f, const char *prefix) {
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);
old_state = t->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]);
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]);
prefix, target_state_to_string(t->state));
}
static int target_start(Unit *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,

View File

@ -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

View File

@ -371,3 +371,43 @@ char *unit_name_template(const char *f) {
return r;
}
char *unit_name_from_path(const char *path, const char *suffix) {
assert(path);
assert(suffix);
if (path[0] == '/')
path++;
if (path[0] == 0)
return strappend("-", suffix);
return unit_name_build_escape(path, NULL, suffix);
}
char *unit_name_to_path(const char *name) {
char *w, *e;
assert(name);
if (!(w = unit_name_to_prefix(name)))
return NULL;
e = unit_name_unescape(w);
free(w);
if (!e)
return NULL;
if (e[0] != '/') {
w = strappend("/", e);
free(e);
if (!w)
return NULL;
e = w;
}
return e;
}

View File

@ -48,4 +48,7 @@ char *unit_name_replace_instance(const char *f, const char *i);
char *unit_name_template(const char *f);
char *unit_name_from_path(const char *path, const char *suffix);
char *unit_name_to_path(const char *name);
#endif

126
unit.c
View File

@ -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",

31
unit.h
View File

@ -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);

10
util.c
View File

@ -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;
}