Commit 57020a3a authored by Lennart Poettering's avatar Lennart Poettering
Browse files

unit: properly update references to units which are merged

When we merge units that some kind of object points to, those pointers
might become invalidated, and needs to be updated. Introduce a UnitRef
struct which links up all the unit references, to ensure corrected
references.

At the same time, drop configured_sockets in the Service object, and
replace it by proper UNIT_TRIGGERS resp. UNIT_TRIGGERED_BY dependencies,
which allow us to simplify a lot of code.
parent 73aa0c00
......@@ -105,7 +105,7 @@ static void automount_done(Unit *u) {
assert(a);
unmount_autofs(a);
a->mount = NULL;
unit_ref_unset(&a->mount);
free(a->where);
a->where = NULL;
......@@ -205,6 +205,7 @@ static int automount_load(Unit *u) {
return r;
if (u->meta.load_state == UNIT_LOADED) {
Unit *x;
if (!a->where)
if (!(a->where = unit_name_to_path(u->meta.id)))
......@@ -215,10 +216,14 @@ static int automount_load(Unit *u) {
if ((r = automount_add_mount_links(a)) < 0)
return r;
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
r = unit_load_related_unit(u, ".mount", &x);
if (r < 0)
return r;
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount), true)) < 0)
unit_ref_set(&a->mount, x);
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true);
if (r < 0)
return r;
if (a->meta.default_dependencies)
......@@ -569,7 +574,7 @@ static void automount_enter_runnning(Automount *a) {
DBusError error;
assert(a);
assert(a->mount);
assert(UNIT_DEREF(a->mount));
dbus_error_init(&error);
......@@ -591,7 +596,7 @@ static void automount_enter_runnning(Automount *a) {
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
log_info("%s's automount point already active?", a->meta.id);
else if ((r = manager_add_job(a->meta.manager, JOB_START, UNIT(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) {
else if ((r = manager_add_job(a->meta.manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) {
log_warning("%s failed to queue mount startup job: %s", a->meta.id, bus_error(&error, r));
goto fail;
}
......@@ -616,7 +621,7 @@ static int automount_start(Unit *u) {
return -EEXIST;
}
if (a->mount->meta.load_state != UNIT_LOADED)
if (UNIT_DEREF(a->mount)->meta.load_state != UNIT_LOADED)
return -ENOENT;
a->failure = false;
......@@ -738,10 +743,10 @@ static bool automount_check_gc(Unit *u) {
assert(a);
if (!a->mount)
if (!UNIT_DEREF(a->mount))
return false;
return UNIT_VTABLE(UNIT(a->mount))->check_gc(UNIT(a->mount));
return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount));
}
static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
......
......@@ -42,14 +42,13 @@ struct Automount {
char *where;
Mount *mount;
UnitRef mount;
int pipe_fd;
mode_t directory_mode;
Watch pipe_watch;
dev_t dev_id;
Set *tokens;
bool failure:1;
......
......@@ -86,7 +86,7 @@ static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *
assert(property);
assert(u);
t = u->path.unit ? u->path.unit->meta.id : "";
t = UNIT_DEREF(u->path.unit) ? UNIT_DEREF(u->path.unit)->meta.id : "";
return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
}
......
......@@ -59,7 +59,6 @@
" <property name=\"BusName\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"StatusText\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"FsckPassNo\" type=\"i\" access=\"read\"/>\n" \
" <property name=\"Sockets\" type=\"as\" access=\"read\"/>\n" \
BUS_SERVICE_SYSV_INTERFACE_FRAGMENT \
" </interface>\n"
......@@ -120,7 +119,6 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio
{ "org.freedesktop.systemd1.Service", "ControlPID", bus_property_append_pid, "u", &u->service.control_pid },
{ "org.freedesktop.systemd1.Service", "BusName", bus_property_append_string, "s", u->service.bus_name },
{ "org.freedesktop.systemd1.Service", "StatusText", bus_property_append_string, "s", u->service.status_text },
{ "org.freedesktop.systemd1.Service", "Sockets", bus_unit_append_dependencies, "as", u->service.configured_sockets },
#ifdef HAVE_SYSV_COMPAT
{ "org.freedesktop.systemd1.Service", "SysVRunLevels", bus_property_append_string, "s", u->service.sysv_runlevels },
{ "org.freedesktop.systemd1.Service", "SysVStartPriority", bus_property_append_int, "i", &u->service.sysv_start_priority },
......
......@@ -107,7 +107,7 @@ static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void
assert(property);
assert(u);
t = u->timer.unit ? u->timer.unit->meta.id : "";
t = UNIT_DEREF(u->timer.unit) ? UNIT_DEREF(u->timer.unit)->meta.id : "";
return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
}
......
......@@ -141,6 +141,8 @@
{ "org.freedesktop.systemd1.Unit", "Before", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BEFORE] }, \
{ "org.freedesktop.systemd1.Unit", "After", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_AFTER] }, \
{ "org.freedesktop.systemd1.Unit", "OnFailure", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_ON_FAILURE] }, \
{ "org.freedesktop.systemd1.Unit", "Triggers", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_TRIGGERS] }, \
{ "org.freedesktop.systemd1.Unit", "TriggeredBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_TRIGGERED_BY] }, \
{ "org.freedesktop.systemd1.Unit", "Description", bus_unit_append_description, "s", u }, \
{ "org.freedesktop.systemd1.Unit", "LoadState", bus_unit_append_load_state, "s", &u->meta.load_state }, \
{ "org.freedesktop.systemd1.Unit", "ActiveState", bus_unit_append_active_state, "s", u }, \
......
......@@ -84,7 +84,8 @@ int config_parse_unit_deps(
char *t, *k;
int r;
if (!(t = strndup(w, l)))
t = strndup(w, l);
if (!t)
return -ENOMEM;
k = unit_name_printf(u, t);
......@@ -94,12 +95,8 @@ int config_parse_unit_deps(
return -ENOMEM;
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0) {
log_error("Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
free(k);
return 0;
}
if (r < 0)
log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
free(k);
}
......@@ -1265,6 +1262,7 @@ int config_parse_timer_unit(
Timer *t = data;
int r;
DBusError error;
Unit *u;
assert(filename);
assert(lvalue);
......@@ -1278,12 +1276,15 @@ int config_parse_timer_unit(
return 0;
}
if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, NULL, &t->unit)) < 0) {
r = manager_load_unit(t->meta.manager, rvalue, NULL, NULL, &u);
if (r < 0) {
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
dbus_error_free(&error);
return 0;
}
unit_ref_set(&t->unit, u);
return 0;
}
......@@ -1347,6 +1348,7 @@ int config_parse_path_unit(
Path *t = data;
int r;
DBusError error;
Unit *u;
assert(filename);
assert(lvalue);
......@@ -1360,12 +1362,14 @@ int config_parse_path_unit(
return 0;
}
if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, &error, &t->unit)) < 0) {
if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, &error, &u)) < 0) {
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
dbus_error_free(&error);
return 0;
}
unit_ref_set(&t->unit, u);
return 0;
}
......@@ -1416,7 +1420,6 @@ int config_parse_service_sockets(
Service *s = data;
int r;
DBusError error;
char *state, *w;
size_t l;
......@@ -1425,35 +1428,34 @@ int config_parse_service_sockets(
assert(rvalue);
assert(data);
dbus_error_init(&error);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
char *t;
Unit *sock;
char *t, *k;
if (!(t = strndup(w, l)))
t = strndup(w, l);
if (!t)
return -ENOMEM;
if (!endswith(t, ".socket")) {
log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
free(t);
continue;
}
r = manager_load_unit(s->meta.manager, t, NULL, &error, &sock);
k = unit_name_printf(UNIT(s), t);
free(t);
if (r < 0) {
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
dbus_error_free(&error);
if (!k)
return -ENOMEM;
if (!endswith(k, ".socket")) {
log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
free(k);
continue;
}
if ((r = set_ensure_allocated(&s->configured_sockets, trivial_hash_func, trivial_compare_func)) < 0)
return r;
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
if (r < 0)
log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
if ((r = set_put(s->configured_sockets, sock)) < 0)
r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
if (r < 0)
return r;
free(k);
}
return 0;
......
......@@ -108,21 +108,12 @@ static void mount_parameters_done(MountParameters *p) {
static void mount_done(Unit *u) {
Mount *m = MOUNT(u);
Meta *other;
assert(m);
free(m->where);
m->where = NULL;
/* Try to detach us from the automount unit if there is any */
LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_AUTOMOUNT]) {
Automount *a = (Automount*) other;
if (a->mount == m)
a->mount = NULL;
}
mount_parameters_done(&m->parameters_etc_fstab);
mount_parameters_done(&m->parameters_proc_self_mountinfo);
mount_parameters_done(&m->parameters_fragment);
......@@ -647,13 +638,18 @@ static int mount_load(Unit *u) {
static int mount_notify_automount(Mount *m, int status) {
Unit *p;
int r;
Iterator i;
assert(m);
if ((r = unit_get_related_unit(UNIT(m), ".automount", &p)) < 0)
return r == -ENOENT ? 0 : r;
SET_FOREACH(p, m->meta.dependencies[UNIT_TRIGGERED_BY], i)
if (p->meta.type == UNIT_AUTOMOUNT) {
r = automount_send_ready(AUTOMOUNT(p), status);
if (r < 0)
return r;
}
return automount_send_ready(AUTOMOUNT(p), status);
return 0;
}
static void mount_set_state(Mount *m, MountState state) {
......
......@@ -39,7 +39,8 @@ static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
[PATH_FAILED] = UNIT_FAILED
};
int pathspec_watch(PathSpec *s, Unit *u) {
int path_spec_watch(PathSpec *s, Unit *u) {
static const int flags_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
[PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
......@@ -55,7 +56,7 @@ int pathspec_watch(PathSpec *s, Unit *u) {
assert(u);
assert(s);
pathspec_unwatch(s, u);
path_spec_unwatch(s, u);
if (!(k = strdup(s->path)))
return -ENOMEM;
......@@ -96,11 +97,11 @@ int pathspec_watch(PathSpec *s, Unit *u) {
fail:
free(k);
pathspec_unwatch(s, u);
path_spec_unwatch(s, u);
return r;
}
void pathspec_unwatch(PathSpec *s, Unit *u) {
void path_spec_unwatch(PathSpec *s, Unit *u) {
if (s->inotify_fd < 0)
return;
......@@ -111,7 +112,7 @@ void pathspec_unwatch(PathSpec *s, Unit *u) {
s->inotify_fd = -1;
}
int pathspec_fd_event(PathSpec *s, uint32_t events) {
int path_spec_fd_event(PathSpec *s, uint32_t events) {
uint8_t *buf = NULL;
struct inotify_event *e;
ssize_t k;
......@@ -164,7 +165,7 @@ out:
return r;
}
static bool pathspec_check_good(PathSpec *s, bool initial) {
static bool path_spec_check_good(PathSpec *s, bool initial) {
bool good = false;
switch (s->type) {
......@@ -202,11 +203,11 @@ static bool pathspec_check_good(PathSpec *s, bool initial) {
return good;
}
static bool pathspec_startswith(PathSpec *s, const char *what) {
static bool path_spec_startswith(PathSpec *s, const char *what) {
return path_startswith(s->path, what);
}
static void pathspec_mkdir(PathSpec *s, mode_t mode) {
static void path_spec_mkdir(PathSpec *s, mode_t mode) {
int r;
if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
......@@ -216,7 +217,7 @@ static void pathspec_mkdir(PathSpec *s, mode_t mode) {
log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
}
static void pathspec_dump(PathSpec *s, FILE *f, const char *prefix) {
static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
fprintf(f,
"%s%s: %s\n",
prefix,
......@@ -224,8 +225,10 @@ static void pathspec_dump(PathSpec *s, FILE *f, const char *prefix) {
s->path);
}
void pathspec_done(PathSpec *s) {
void path_spec_done(PathSpec *s) {
assert(s);
assert(s->inotify_fd == -1);
free(s->path);
}
......@@ -244,10 +247,12 @@ static void path_done(Unit *u) {
assert(p);
unit_ref_unset(&p->unit);
while ((s = p->specs)) {
pathspec_unwatch(s, u);
path_spec_unwatch(s, u);
LIST_REMOVE(PathSpec, spec, p->specs, s);
pathspec_done(s);
path_spec_done(s);
free(s);
}
}
......@@ -265,7 +270,7 @@ int path_add_one_mount_link(Path *p, Mount *m) {
LIST_FOREACH(spec, s, p->specs) {
if (!pathspec_startswith(s, m->where))
if (!path_spec_startswith(s, m->where))
continue;
if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
......@@ -330,11 +335,18 @@ static int path_load(Unit *u) {
if (u->meta.load_state == UNIT_LOADED) {
if (!p->unit)
if ((r = unit_load_related_unit(u, ".service", &p->unit)))
if (!UNIT_DEREF(p->unit)) {
Unit *x;
r = unit_load_related_unit(u, ".service", &x);
if (r < 0)
return r;
if ((r = unit_add_dependency(u, UNIT_BEFORE, p->unit, true)) < 0)
unit_ref_set(&p->unit, x);
}
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true);
if (r < 0)
return r;
if ((r = path_add_mount_links(p)) < 0)
......@@ -361,12 +373,12 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) {
"%sMakeDirectory: %s\n"
"%sDirectoryMode: %04o\n",
prefix, path_state_to_string(p->state),
prefix, p->unit->meta.id,
prefix, UNIT_DEREF(p->unit)->meta.id,
prefix, yes_no(p->make_directory),
prefix, p->directory_mode);
LIST_FOREACH(spec, s, p->specs)
pathspec_dump(s, f, prefix);
path_spec_dump(s, f, prefix);
}
static void path_unwatch(Path *p) {
......@@ -375,7 +387,7 @@ static void path_unwatch(Path *p) {
assert(p);
LIST_FOREACH(spec, s, p->specs)
pathspec_unwatch(s, UNIT(p));
path_spec_unwatch(s, UNIT(p));
}
static int path_watch(Path *p) {
......@@ -385,7 +397,7 @@ static int path_watch(Path *p) {
assert(p);
LIST_FOREACH(spec, s, p->specs)
if ((r = pathspec_watch(s, UNIT(p))) < 0)
if ((r = path_spec_watch(s, UNIT(p))) < 0)
return r;
return 0;
......@@ -451,7 +463,7 @@ static void path_enter_running(Path *p) {
if (p->meta.job && p->meta.job->type == JOB_STOP)
return;
if ((r = manager_add_job(p->meta.manager, JOB_START, p->unit, JOB_REPLACE, true, &error, NULL)) < 0)
if ((r = manager_add_job(p->meta.manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0)
goto fail;
p->inotify_triggered = false;
......@@ -476,7 +488,7 @@ static bool path_check_good(Path *p, bool initial) {
assert(p);
LIST_FOREACH(spec, s, p->specs) {
good = pathspec_check_good(s, initial);
good = path_spec_check_good(s, initial);
if (good)
break;
......@@ -526,7 +538,7 @@ static void path_mkdir(Path *p) {
return;
LIST_FOREACH(spec, s, p->specs)
pathspec_mkdir(s, p->directory_mode);
path_spec_mkdir(s, p->directory_mode);
}
static int path_start(Unit *u) {
......@@ -535,7 +547,7 @@ static int path_start(Unit *u) {
assert(p);
assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
if (p->unit->meta.load_state != UNIT_LOADED)
if (UNIT_DEREF(p->unit)->meta.load_state != UNIT_LOADED)
return -ENOENT;
path_mkdir(p);
......@@ -616,7 +628,7 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
/* log_debug("inotify wakeup on %s.", u->meta.id); */
LIST_FOREACH(spec, s, p->specs)
if (pathspec_owns_inotify_fd(s, fd))
if (path_spec_owns_inotify_fd(s, fd))
break;
if (!s) {
......@@ -624,7 +636,7 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
goto fail;
}
changed = pathspec_fd_event(s, events);
changed = path_spec_fd_event(s, events);
if (changed < 0)
goto fail;
......@@ -645,36 +657,22 @@ fail:
}
void path_unit_notify(Unit *u, UnitActiveState new_state) {
char *n;
int r;
Iterator i;
Unit *k;
if (u->meta.type == UNIT_PATH)
return;
SET_FOREACH(n, u->meta.names, i) {
char *k;
Unit *t;
SET_FOREACH(k, u->meta.dependencies[UNIT_TRIGGERED_BY], i) {
Path *p;
if (!(k = unit_name_change_suffix(n, ".path"))) {
r = -ENOMEM;
goto fail;
}
t = manager_get_unit(u->meta.manager, k);
free(k);
if (!t)
if (k->meta.type != UNIT_PATH)
continue;
if (t->meta.load_state != UNIT_LOADED)
if (k->meta.load_state != UNIT_LOADED)
continue;
p = PATH(t);
if (p->unit != u)
continue;
p = PATH(k);
if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
log_debug("%s got notified about unit deactivation.", p->meta.id);
......@@ -685,11 +683,6 @@ void path_unit_notify(Unit *u, UnitActiveState new_state) {
path_enter_waiting(p, false, p->inotify_triggered);
}
}
return;
fail:
log_error("Failed find path unit: %s", strerror(-r));
}
static void path_reset_failed(Unit *u) {
......
......@@ -58,14 +58,14 @@ typedef struct PathSpec {
int primary_wd;
bool previous_exists;
} PathSpec;
int pathspec_watch(PathSpec *s, Unit *u);
void pathspec_unwatch(PathSpec *s, Unit *u);
int pathspec_fd_event(PathSpec *s, uint32_t events);
void pathspec_done(PathSpec *s);
static inline bool pathspec_owns_inotify_fd(PathSpec *s, int fd) {
int path_spec_watch(PathSpec *s, Unit *u);
void path_spec_unwatch(PathSpec *s, Unit *u);
int path_spec_fd_event(PathSpec *s, uint32_t events);
void path_spec_done(PathSpec *s);
static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) {
return s->inotify_fd == fd;
}
......@@ -74,7 +74,7 @@ struct Path {
LIST_HEAD(PathSpec, specs);
Unit *unit;
UnitRef unit;
PathState state, deserialized_state;
......
......@@ -187,11 +187,11 @@ static void service_close_socket_fd(Service *s) {
static void service_connection_unref(Service *s) {
assert(s);
if (!s->accept_socket)
if (!UNIT_DEREF(s->accept_socket))
return;
socket_connection_unref(s->accept_socket);
s->accept_socket = NULL;
socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
unit_ref_unset(&s->accept_socket);
}
static void service_done(Unit *u) {
......@@ -232,7 +232,7 @@ static void service_done(Unit *u) {
service_close_socket_fd(s);
service_connection_unref(s);
set_free(s->configured_sockets);
unit_ref_unset(&s->accept_socket);