X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Funit.c;h=0ad679ba622f6154f742a9cacee2a794c1872a24;hp=ab313b9b91b6faa4e8bfdd53a82c9c3e331d2e23;hb=58ea275a68cd242ad60161bcb7582614d1d89f13;hpb=b58b8e11c5f769e3c80d5169fdcc4bd04b882b7d diff --git a/src/core/unit.c b/src/core/unit.c index ab313b9b9..0ad679ba6 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -29,8 +29,8 @@ #include #include -#include "systemd/sd-id128.h" -#include "systemd/sd-messages.h" +#include "sd-id128.h" +#include "sd-messages.h" #include "set.h" #include "unit.h" #include "macro.h" @@ -48,17 +48,20 @@ #include "label.h" #include "fileio-label.h" #include "bus-errors.h" +#include "dbus.h" +#include "execute.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, - [UNIT_TIMER] = &timer_vtable, [UNIT_SOCKET] = &socket_vtable, + [UNIT_BUSNAME] = &busname_vtable, [UNIT_TARGET] = &target_vtable, + [UNIT_SNAPSHOT] = &snapshot_vtable, [UNIT_DEVICE] = &device_vtable, [UNIT_MOUNT] = &mount_vtable, [UNIT_AUTOMOUNT] = &automount_vtable, - [UNIT_SNAPSHOT] = &snapshot_vtable, [UNIT_SWAP] = &swap_vtable, + [UNIT_TIMER] = &timer_vtable, [UNIT_PATH] = &path_vtable, [UNIT_SLICE] = &slice_vtable, [UNIT_SCOPE] = &scope_vtable @@ -85,6 +88,7 @@ Unit *unit_new(Manager *m, size_t size) { u->deserialized_job = _JOB_TYPE_INVALID; u->default_dependencies = true; u->unit_file_state = _UNIT_FILE_STATE_INVALID; + u->on_failure_job_mode = JOB_REPLACE; return u; } @@ -115,7 +119,7 @@ int unit_add_name(Unit *u, const char *text) { if (!s) return -ENOMEM; - if (!unit_name_is_valid(s, false)) { + if (!unit_name_is_valid(s, TEMPLATE_INVALID)) { r = -EINVAL; goto fail; } @@ -127,7 +131,8 @@ int unit_add_name(Unit *u, const char *text) { goto fail; } - if ((r = unit_name_to_instance(s, &i)) < 0) + r = unit_name_to_instance(s, &i); + if (r < 0) goto fail; if (i && unit_vtable[t]->no_instances) { @@ -154,13 +159,15 @@ int unit_add_name(Unit *u, const char *text) { goto fail; } - if ((r = set_put(u->names, s)) < 0) { + r = set_put(u->names, s); + if (r < 0) { if (r == -EEXIST) r = 0; goto fail; } - if ((r = hashmap_put(u->manager->units, s, u)) < 0) { + r = hashmap_put(u->manager->units, s, u); + if (r < 0) { set_remove(u->names, s); goto fail; } @@ -171,7 +178,7 @@ int unit_add_name(Unit *u, const char *text) { u->id = s; u->instance = i; - LIST_PREPEND(Unit, units_by_type, u->manager->units_by_type[t], u); + LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u); if (UNIT_VTABLE(u)->init) UNIT_VTABLE(u)->init(u); @@ -201,7 +208,8 @@ int unit_choose_id(Unit *u, const char *name) { if (!u->instance) return -EINVAL; - if (!(t = unit_name_replace_instance(name, u->instance))) + t = unit_name_replace_instance(name, u->instance); + if (!t) return -ENOMEM; name = t; @@ -213,7 +221,8 @@ int unit_choose_id(Unit *u, const char *name) { if (!s) return -ENOENT; - if ((r = unit_name_to_instance(s, &i)) < 0) + r = unit_name_to_instance(s, &i); + if (r < 0) return r; u->id = s; @@ -284,7 +293,7 @@ void unit_add_to_load_queue(Unit *u) { if (u->load_state != UNIT_STUB || u->in_load_queue) return; - LIST_PREPEND(Unit, load_queue, u->manager->load_queue, u); + LIST_PREPEND(load_queue, u->manager->load_queue, u); u->in_load_queue = true; } @@ -294,7 +303,7 @@ void unit_add_to_cleanup_queue(Unit *u) { if (u->in_cleanup_queue) return; - LIST_PREPEND(Unit, cleanup_queue, u->manager->cleanup_queue, u); + LIST_PREPEND(cleanup_queue, u->manager->cleanup_queue, u); u->in_cleanup_queue = true; } @@ -307,7 +316,7 @@ void unit_add_to_gc_queue(Unit *u) { if (unit_check_gc(u)) return; - LIST_PREPEND(Unit, gc_queue, u->manager->gc_queue, u); + LIST_PREPEND(gc_queue, u->manager->gc_queue, u); u->in_gc_queue = true; u->manager->n_in_gc_queue ++; @@ -321,12 +330,12 @@ void unit_add_to_dbus_queue(Unit *u) { return; /* Shortcut things if nobody cares */ - if (!bus_has_subscriber(u->manager)) { + if (set_isempty(u->manager->subscribed)) { u->sent_dbus_new_signal = true; return; } - LIST_PREPEND(Unit, dbus_queue, u->manager->dbus_unit_queue, u); + LIST_PREPEND(dbus_queue, u->manager->dbus_unit_queue, u); u->in_dbus_queue = true; } @@ -374,6 +383,34 @@ static void unit_remove_transient(Unit *u) { } } +static void unit_free_requires_mounts_for(Unit *u) { + char **j; + + STRV_FOREACH(j, u->requires_mounts_for) { + char s[strlen(*j) + 1]; + + PATH_FOREACH_PREFIX_MORE(s, *j) { + char *y; + Set *x; + + x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y); + if (!x) + continue; + + set_remove(x, u); + + if (set_isempty(x)) { + hashmap_remove(u->manager->units_requiring_mounts_for, y); + free(y); + set_free(x); + } + } + } + + strv_free(u->requires_mounts_for); + u->requires_mounts_for = NULL; +} + void unit_free(Unit *u) { UnitDependency d; Iterator i; @@ -390,6 +427,8 @@ void unit_free(Unit *u) { if (UNIT_VTABLE(u)->done) UNIT_VTABLE(u)->done(u); + unit_free_requires_mounts_for(u); + SET_FOREACH(t, u->names, i) hashmap_remove_value(u->manager->units, t, u); @@ -408,30 +447,25 @@ void unit_free(Unit *u) { for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) bidi_set_free(u, u->dependencies[d]); - if (u->requires_mounts_for) { - LIST_REMOVE(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u); - strv_free(u->requires_mounts_for); - } - if (u->type != _UNIT_TYPE_INVALID) - LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u); + LIST_REMOVE(units_by_type, u->manager->units_by_type[u->type], u); if (u->in_load_queue) - LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u); + LIST_REMOVE(load_queue, u->manager->load_queue, u); if (u->in_dbus_queue) - LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u); + LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u); if (u->in_cleanup_queue) - LIST_REMOVE(Unit, cleanup_queue, u->manager->cleanup_queue, u); + LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u); if (u->in_gc_queue) { - LIST_REMOVE(Unit, gc_queue, u->manager->gc_queue, u); + LIST_REMOVE(gc_queue, u->manager->gc_queue, u); u->manager->n_in_gc_queue--; } if (u->in_cgroup_queue) - LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u); + LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); if (u->cgroup_path) { hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); @@ -521,14 +555,13 @@ static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) { SET_FOREACH(back, other->dependencies[d], i) { UnitDependency k; - for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) - if ((r = set_remove_and_put(back->dependencies[k], other, u)) < 0) { - - if (r == -EEXIST) - set_remove(back->dependencies[k], other); - else - assert(r == -ENOENT); - } + for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) { + r = set_remove_and_put(back->dependencies[k], other, u); + if (r == -EEXIST) + set_remove(back->dependencies[k], other); + else + assert(r >= 0 || r == -ENOENT); + } } complete_move(&u->dependencies[d], &other->dependencies[d]); @@ -607,7 +640,8 @@ int unit_merge_by_name(Unit *u, const char *name) { if (!u->instance) return -EINVAL; - if (!(s = unit_name_replace_instance(name, u->instance))) + s = unit_name_replace_instance(name, u->instance); + if (!s) return -ENOMEM; name = s; @@ -637,6 +671,18 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { assert(u); assert(c); + if (c->working_directory) { + r = unit_require_mounts_for(u, c->working_directory); + if (r < 0) + return r; + } + + if (c->root_directory) { + r = unit_require_mounts_for(u, c->root_directory); + if (r < 0) + return r; + } + if (c->std_output != EXEC_OUTPUT_KMSG && c->std_output != EXEC_OUTPUT_SYSLOG && c->std_output != EXEC_OUTPUT_JOURNAL && @@ -685,6 +731,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { timestamp4[FORMAT_TIMESTAMP_MAX], timespan[FORMAT_TIMESPAN_MAX]; Unit *following; + _cleanup_set_free_ Set *following_set = NULL; + int r; assert(u); assert(u->type >= 0); @@ -710,7 +758,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tSlice: %s\n" "%s\tCGroup: %s\n" "%s\tCGroup realized: %s\n" - "%s\tCGroup mask: 0x%x\n", + "%s\tCGroup mask: 0x%x\n" + "%s\tCGroup members mask: 0x%x\n", prefix, u->id, prefix, unit_description(u), prefix, strna(u->instance), @@ -726,7 +775,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(unit_slice_name(u)), prefix, strna(u->cgroup_path), prefix, yes_no(u->cgroup_realized), - prefix, u->cgroup_mask); + prefix, u->cgroup_mask, + prefix, u->cgroup_members_mask); SET_FOREACH(t, u->names, i) fprintf(f, "%s\tName: %s\n", prefix, t); @@ -734,9 +784,18 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { STRV_FOREACH(j, u->documentation) fprintf(f, "%s\tDocumentation: %s\n", prefix, *j); - if ((following = unit_following(u))) + following = unit_following(u); + if (following) fprintf(f, "%s\tFollowing: %s\n", prefix, following->id); + r = unit_following_set(u, &following_set); + if (r >= 0) { + Unit *other; + + SET_FOREACH(other, following_set, i) + fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id); + } + if (u->fragment_path) fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path); @@ -782,14 +841,14 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tRefuseManualStart: %s\n" "%s\tRefuseManualStop: %s\n" "%s\tDefaultDependencies: %s\n" - "%s\tOnFailureIsolate: %s\n" + "%s\tOnFailureJobMode: %s\n" "%s\tIgnoreOnIsolate: %s\n" "%s\tIgnoreOnSnapshot: %s\n", prefix, yes_no(u->stop_when_unneeded), prefix, yes_no(u->refuse_manual_start), prefix, yes_no(u->refuse_manual_stop), prefix, yes_no(u->default_dependencies), - prefix, yes_no(u->on_failure_isolate), + prefix, job_mode_to_string(u->on_failure_job_mode), prefix, yes_no(u->ignore_on_isolate), prefix, yes_no(u->ignore_on_snapshot)); @@ -818,7 +877,7 @@ int unit_load_fragment_and_dropin(Unit *u) { assert(u); - /* Load a .service file */ + /* Load a .{service,socket,...} file */ r = unit_load_fragment(u); if (r < 0) return r; @@ -921,13 +980,51 @@ static int unit_add_default_dependencies(Unit *u) { return 0; } +static int unit_add_mount_links(Unit *u) { + char **i; + int r; + + assert(u); + + STRV_FOREACH(i, u->requires_mounts_for) { + char prefix[strlen(*i) + 1]; + + PATH_FOREACH_PREFIX_MORE(prefix, *i) { + Unit *m; + + r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m); + if (r < 0) + return r; + if (r == 0) + continue; + if (m == u) + continue; + + if (m->load_state != UNIT_LOADED) + continue; + + r = unit_add_dependency(u, UNIT_AFTER, m, true); + if (r < 0) + return r; + + if (m->fragment_path) { + r = unit_add_dependency(u, UNIT_REQUIRES, m, true); + if (r < 0) + return r; + } + } + } + + return 0; +} + int unit_load(Unit *u) { int r; assert(u); if (u->in_load_queue) { - LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u); + LIST_REMOVE(load_queue, u->manager->load_queue, u); u->in_load_queue = false; } @@ -956,15 +1053,17 @@ int unit_load(Unit *u) { goto fail; } + unit_update_member_masks(u); + r = unit_add_mount_links(u); if (r < 0) goto fail; - if (u->on_failure_isolate && + if (u->on_failure_job_mode == JOB_ISOLATE && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { log_error_unit(u->id, - "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id); + "More than one OnFailure= dependencies specified for %s but OnFailureJobMode=isolate set. Refusing.", u->id); r = -EINVAL; goto fail; @@ -990,7 +1089,7 @@ fail: return r; } -bool unit_condition_test(Unit *u) { +static bool unit_condition_test(Unit *u) { assert(u); dual_timestamp_get(&u->condition_timestamp); @@ -1038,6 +1137,8 @@ _pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, Job return NULL; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" static void unit_status_print_starting_stopping(Unit *u, JobType t) { const char *format; @@ -1052,6 +1153,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) { unit_status_printf(u, "", format); } +#pragma GCC diagnostic pop #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" @@ -1214,10 +1316,14 @@ int unit_reload(Unit *u) { if (state == UNIT_RELOADING) return -EALREADY; - if (state != UNIT_ACTIVE) + if (state != UNIT_ACTIVE) { + log_warning_unit(u->id, "Unit %s cannot be reloaded because it is inactive.", + u->id); return -ENOEXEC; + } - if ((following = unit_following(u))) { + following = unit_following(u); + if (following) { log_debug_unit(u->id, "Redirecting reload request from %s to %s.", u->id, following->id); return unit_reload(following); @@ -1369,7 +1475,7 @@ void unit_start_on_failure(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; - r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL); if (r < 0) log_error_unit(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r)); } @@ -1421,6 +1527,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (UNIT_IS_INACTIVE_OR_FAILED(ns)) unit_destroy_cgroup(u); + /* Note that this doesn't apply to RemainAfterExit services exiting + * sucessfully, since there's no change of state in that case. Which is + * why it is handled in service_set_state() */ if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { ExecContext *ec = unit_get_exec_context(u); if (ec && exec_context_may_touch_console(ec)) { @@ -1429,7 +1538,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (m->n_on_console == 0) /* unset no_console_output flag, since the console is free */ - m->no_console_output = 0; + m->no_console_output = false; } else m->n_on_console ++; } @@ -1586,46 +1695,6 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su unit_add_to_gc_queue(u); } -int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) { - struct epoll_event ev = { - .data.ptr = w, - .events = events, - }; - - assert(u); - assert(fd >= 0); - assert(w); - assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u)); - - if (epoll_ctl(u->manager->epoll_fd, - w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, - fd, - &ev) < 0) - return -errno; - - w->fd = fd; - w->type = WATCH_FD; - w->data.unit = u; - - return 0; -} - -void unit_unwatch_fd(Unit *u, Watch *w) { - assert(u); - assert(w); - - if (w->type == WATCH_INVALID) - return; - - assert(w->type == WATCH_FD); - assert(w->data.unit == u); - assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); - - w->fd = -1; - w->type = WATCH_INVALID; - w->data.unit = NULL; -} - int unit_watch_pid(Unit *u, pid_t pid) { assert(u); assert(pid >= 1); @@ -1643,90 +1712,6 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u); } -int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Watch *w) { - struct itimerspec its = {}; - int flags, fd; - bool ours; - - assert(u); - assert(w); - assert(w->type == WATCH_INVALID || (w->type == WATCH_UNIT_TIMER && w->data.unit == u)); - - /* This will try to reuse the old timer if there is one */ - - if (w->type == WATCH_UNIT_TIMER) { - assert(w->data.unit == u); - assert(w->fd >= 0); - - ours = false; - fd = w->fd; - } else if (w->type == WATCH_INVALID) { - - ours = true; - fd = timerfd_create(clock_id, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - return -errno; - } else - assert_not_reached("Invalid watch type"); - - if (usec <= 0) { - /* Set absolute time in the past, but not 0, since we - * don't want to disarm the timer */ - its.it_value.tv_sec = 0; - its.it_value.tv_nsec = 1; - - flags = TFD_TIMER_ABSTIME; - } else { - timespec_store(&its.it_value, usec); - flags = relative ? 0 : TFD_TIMER_ABSTIME; - } - - /* This will also flush the elapse counter */ - if (timerfd_settime(fd, flags, &its, NULL) < 0) - goto fail; - - if (w->type == WATCH_INVALID) { - struct epoll_event ev = { - .data.ptr = w, - .events = EPOLLIN, - }; - - if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) - goto fail; - } - - w->type = WATCH_UNIT_TIMER; - w->fd = fd; - w->data.unit = u; - - return 0; - -fail: - if (ours) - close_nointr_nofail(fd); - - return -errno; -} - -void unit_unwatch_timer(Unit *u, Watch *w) { - assert(u); - assert(w); - - if (w->type == WATCH_INVALID) - return; - - assert(w->type == WATCH_UNIT_TIMER); - assert(w->data.unit == u); - assert(w->fd >= 0); - - assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); - close_nointr_nofail(w->fd); - - w->fd = -1; - w->type = WATCH_INVALID; - w->data.unit = NULL; -} - bool unit_job_is_applicable(Unit *u, JobType j) { assert(u); assert(j >= 0 && j < _JOB_TYPE_MAX); @@ -1780,6 +1765,7 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO, + [UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF, }; int r, q = 0, v = 0, w = 0; @@ -1795,34 +1781,47 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen if (u == other) return 0; - if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) + r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func); + if (r < 0) return r; - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) { + r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func); + if (r < 0) + return r; + } + + if (add_reference) { + r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func); + if (r < 0) return r; - if (add_reference) - if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 || - (r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0) + r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func); + if (r < 0) return r; + } - if ((q = set_put(u->dependencies[d], other)) < 0) + q = set_put(u->dependencies[d], other); + if (q < 0) return q; - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) { + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID && inverse_table[d] != d) { + v = set_put(other->dependencies[inverse_table[d]], u); + if (v < 0) { r = v; goto fail; } + } if (add_reference) { - if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) { + w = set_put(u->dependencies[UNIT_REFERENCES], other); + if (w < 0) { r = w; goto fail; } - if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0) + r = set_put(other->dependencies[UNIT_REFERENCED_BY], u); + if (r < 0) goto fail; } @@ -1864,7 +1863,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, assert(p); if (!name) - name = path_get_file_name(path); + name = basename(path); if (!unit_name_is_template(name)) { *p = NULL; @@ -2093,28 +2092,6 @@ 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) { - _cleanup_free_ char *t = NULL; - Unit *found; - - assert(u); - assert(type); - assert(_found); - - t = unit_name_change_suffix(u->id, type); - if (!t) - return -ENOMEM; - - assert(!unit_has_name(u, t)); - - found = manager_get_unit(u->manager, t); - if (!found) - return -ENOENT; - - *_found = found; - return 0; -} - int unit_watch_bus_name(Unit *u, const char *name) { assert(u); assert(name); @@ -2139,6 +2116,7 @@ bool unit_can_serialize(Unit *u) { } int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { + ExecRuntime *rt; int r; assert(u); @@ -2152,17 +2130,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (r < 0) return r; - - if (serialize_jobs) { - if (u->job) { - fprintf(f, "job\n"); - job_serialize(u->job, f, fds); - } - - if (u->nop_job) { - fprintf(f, "job\n"); - job_serialize(u->nop_job, f, fds); - } + rt = unit_get_exec_runtime(u); + if (rt) { + r = exec_runtime_serialize(rt, u, f, fds); + if (r < 0) + return r; } dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp); @@ -2179,6 +2151,18 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (u->cgroup_path) unit_serialize_item(u, f, "cgroup", u->cgroup_path); + if (serialize_jobs) { + if (u->job) { + fprintf(f, "job\n"); + job_serialize(u->job, f, fds); + } + + if (u->nop_job) { + fprintf(f, "job\n"); + job_serialize(u->nop_job, f, fds); + } + } + /* End marker */ fputc('\n', f); return 0; @@ -2212,6 +2196,8 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) { } int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + size_t offset; + ExecRuntime **rt = NULL; int r; assert(u); @@ -2221,6 +2207,10 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { if (!unit_can_serialize(u)) return 0; + offset = UNIT_VTABLE(u)->exec_runtime_offset; + if (offset > 0) + rt = (ExecRuntime**) ((uint8_t*) u + offset); + for (;;) { char line[LINE_MAX], *l, *v; size_t k; @@ -2333,6 +2323,14 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { continue; } + if (rt) { + r = exec_runtime_deserialize_item(rt, u, l, v, fds); + if (r < 0) + return r; + if (r > 0) + continue; + } + r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds); if (r < 0) return r; @@ -2522,7 +2520,7 @@ bool unit_active_or_pending(Unit *u) { return false; } -int unit_kill(Unit *u, KillWho w, int signo, DBusError *error) { +int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) { assert(u); assert(w >= 0 && w < _KILL_WHO_MAX); assert(signo > 0); @@ -2568,23 +2566,23 @@ int unit_kill_common( int signo, pid_t main_pid, pid_t control_pid, - DBusError *error) { + sd_bus_error *error) { int r = 0; if (who == KILL_MAIN && main_pid <= 0) { if (main_pid < 0) - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); + sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); else - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); + sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); return -ESRCH; } if (who == KILL_CONTROL && control_pid <= 0) { if (control_pid < 0) - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); + sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); else - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); + sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); return -ESRCH; } @@ -2632,7 +2630,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) { if (u->unit_file_state < 0 && u->fragment_path) u->unit_file_state = unit_file_get_state( u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, - NULL, path_get_file_name(u->fragment_path)); + NULL, basename(u->fragment_path)); return u->unit_file_state; } @@ -2645,7 +2643,7 @@ Unit* unit_ref_set(UnitRef *ref, Unit *u) { unit_ref_unset(ref); ref->unit = u; - LIST_PREPEND(UnitRef, refs, u->refs, ref); + LIST_PREPEND(refs, u->refs, ref); return u; } @@ -2655,49 +2653,10 @@ void unit_ref_unset(UnitRef *ref) { if (!ref->unit) return; - LIST_REMOVE(UnitRef, refs, ref->unit->refs, ref); + LIST_REMOVE(refs, ref->unit->refs, ref); ref->unit = NULL; } -int unit_add_one_mount_link(Unit *u, Mount *m) { - char **i; - - assert(u); - assert(m); - - if (u->load_state != UNIT_LOADED || - UNIT(m)->load_state != UNIT_LOADED) - return 0; - - STRV_FOREACH(i, u->requires_mounts_for) { - - if (UNIT(m) == u) - continue; - - if (!path_startswith(*i, m->where)) - continue; - - return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); - } - - return 0; -} - -int unit_add_mount_links(Unit *u) { - Unit *other; - int r; - - assert(u); - - LIST_FOREACH(units_by_type, other, u->manager->units_by_type[UNIT_MOUNT]) { - r = unit_add_one_mount_link(u, MOUNT(other)); - if (r < 0) - return r; - } - - return 0; -} - int unit_exec_context_defaults(Unit *u, ExecContext *c) { unsigned i; int r; @@ -2735,6 +2694,17 @@ ExecContext *unit_get_exec_context(Unit *u) { return (ExecContext*) ((uint8_t*) u + offset); } +KillContext *unit_get_kill_context(Unit *u) { + size_t offset; + assert(u); + + offset = UNIT_VTABLE(u)->kill_context_offset; + if (offset <= 0) + return NULL; + + return (KillContext*) ((uint8_t*) u + offset); +} + CGroupContext *unit_get_cgroup_context(Unit *u) { size_t offset; @@ -2745,6 +2715,16 @@ CGroupContext *unit_get_cgroup_context(Unit *u) { return (CGroupContext*) ((uint8_t*) u + offset); } +ExecRuntime *unit_get_exec_runtime(Unit *u) { + size_t offset; + + offset = UNIT_VTABLE(u)->exec_runtime_offset; + if (offset <= 0) + return NULL; + + return *(ExecRuntime**) ((uint8_t*) u + offset); +} + static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **_p, char **_q) { _cleanup_free_ char *b = NULL; char *p, *q; @@ -2884,6 +2864,9 @@ int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { return 0; r = drop_in_file(u, mode, name, &p, &q); + if (r < 0) + return r; + if (unlink(q) < 0) r = errno == ENOENT ? 0 : -errno; else @@ -2983,7 +2966,7 @@ int unit_kill_context( } } - if (c->kill_mode == KILL_CONTROL_GROUP && u->cgroup_path) { + if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && sigkill)) && u->cgroup_path) { _cleanup_set_free_ Set *pid_set = NULL; /* Exclude the main/control pids from being killed via the cgroup */ @@ -2997,6 +2980,7 @@ int unit_kill_context( log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r)); } else if (r > 0) { wait_for_exit = true; + if (c->send_sighup) { set_free(pid_set); @@ -3012,6 +2996,111 @@ int unit_kill_context( return wait_for_exit; } +int unit_require_mounts_for(Unit *u, const char *path) { + char prefix[strlen(path) + 1], *p; + int r; + + assert(u); + assert(path); + + /* Registers a unit for requiring a certain path and all its + * prefixes. We keep a simple array of these paths in the + * unit, since its usually short. However, we build a prefix + * table for all possible prefixes so that new appearing mount + * units can easily determine which units to make themselves a + * dependency of. */ + + if (!path_is_absolute(path)) + return -EINVAL; + + p = strdup(path); + if (!p) + return -ENOMEM; + + path_kill_slashes(p); + + if (!path_is_safe(p)) { + free(p); + return -EPERM; + } + + if (strv_contains(u->requires_mounts_for, p)) { + free(p); + return 0; + } + + r = strv_push(&u->requires_mounts_for, p); + if (r < 0) { + free(p); + return r; + } + + PATH_FOREACH_PREFIX_MORE(prefix, p) { + Set *x; + + x = hashmap_get(u->manager->units_requiring_mounts_for, prefix); + if (!x) { + char *q; + + if (!u->manager->units_requiring_mounts_for) { + u->manager->units_requiring_mounts_for = hashmap_new(string_hash_func, string_compare_func); + if (!u->manager->units_requiring_mounts_for) + return -ENOMEM; + } + + q = strdup(prefix); + if (!q) + return -ENOMEM; + + x = set_new(NULL, NULL); + if (!x) { + free(q); + return -ENOMEM; + } + + r = hashmap_put(u->manager->units_requiring_mounts_for, q, x); + if (r < 0) { + free(q); + set_free(x); + return r; + } + } + + r = set_put(x, u); + if (r < 0) + return r; + } + + return 0; +} + +int unit_setup_exec_runtime(Unit *u) { + ExecRuntime **rt; + size_t offset; + Iterator i; + Unit *other; + + offset = UNIT_VTABLE(u)->exec_runtime_offset; + assert(offset > 0); + + /* Check if ther already is an ExecRuntime for this unit? */ + rt = (ExecRuntime**) ((uint8_t*) u + offset); + if (*rt) + return 0; + + /* Try to get it from somebody else */ + SET_FOREACH(other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) { + + *rt = unit_get_exec_runtime(other); + if (*rt) { + exec_runtime_ref(*rt); + return 0; + } + } + + return exec_runtime_make(rt, unit_get_exec_context(u), u->id); +} + static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", [UNIT_RELOADING] = "reloading", @@ -3047,6 +3136,7 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", [UNIT_REFERENCES] = "References", [UNIT_REFERENCED_BY] = "ReferencedBy", + [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);