X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Funit.c;h=7029b6443e649179424f5080fcb5218f8be6aefc;hp=37711afeafc6760fc26de27a26fa7ba5baad8e36;hb=b1e2b33c5258f1cefcee55116ac5d049478c804d;hpb=f69614f811b133ececad4394e88f9549a017bd4e diff --git a/src/core/unit.c b/src/core/unit.c index 37711afea..7029b6443 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -29,6 +29,8 @@ #include #include +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "set.h" #include "unit.h" #include "macro.h" @@ -38,12 +40,15 @@ #include "load-dropin.h" #include "log.h" #include "unit-name.h" -#include "specifier.h" #include "dbus-unit.h" #include "special.h" #include "cgroup-util.h" #include "missing.h" #include "cgroup-attr.h" +#include "mkdir.h" +#include "label.h" +#include "fileio-label.h" +#include "bus-errors.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -256,6 +261,9 @@ bool unit_check_gc(Unit *u) { if (unit_active_state(u) != UNIT_INACTIVE) return true; + if (u->refs) + return true; + if (UNIT_VTABLE(u)->check_gc) if (UNIT_VTABLE(u)->check_gc(u)) return true; @@ -400,6 +408,7 @@ void unit_free(Unit *u) { strv_free(u->documentation); free(u->fragment_path); free(u->source_path); + strv_free(u->dropin_paths); free(u->instance); set_free_free(u->names); @@ -609,9 +618,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { /* If syslog or kernel logging is requested, make sure our own * logging daemon is run first. */ - if (u->manager->running_as == MANAGER_SYSTEM) - if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0) + if (u->manager->running_as == SYSTEMD_SYSTEM) { + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true); + if (r < 0) return r; + } return 0; } @@ -686,8 +697,11 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { if (u->source_path) fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path); + STRV_FOREACH(j, u->dropin_paths) + fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j); + if (u->job_timeout > 0) - fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout)); + fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0)); condition_dump_list(u->conditions, f, prefix); @@ -740,15 +754,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, b->controller, b->path); LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - char *v = NULL; + _cleanup_free_ char *v = NULL; - if (a->map_callback) - a->map_callback(a->controller, a->name, a->value, &v); + if (a->semantics && a->semantics->map_write) + a->semantics->map_write(a->semantics, a->value, &v); fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n", prefix, a->controller, a->name, v ? v : a->value); - - free(v); } if (UNIT_VTABLE(u)->dump) @@ -902,8 +914,8 @@ int unit_load(Unit *u) { if (u->on_failure_isolate && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { - log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", - u->id); + log_error_unit(u->id, + "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id); r = -EINVAL; goto fail; @@ -922,7 +934,8 @@ fail: unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); - log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r)); + log_debug_unit(u->id, "Failed to load configuration for %s: %s", + u->id, strerror(-r)); return r; } @@ -936,20 +949,95 @@ bool unit_condition_test(Unit *u) { return u->condition_result; } -static void unit_status_print_starting_stopping(Unit *u, bool stopping) { +static const char* unit_get_status_message_format(Unit *u, JobType t) { const UnitStatusMessageFormats *format_table; - const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + if (t != JOB_START && t != JOB_STOP) + return NULL; format_table = &UNIT_VTABLE(u)->status_message_formats; if (!format_table) + return NULL; + + return format_table->starting_stopping[t == JOB_STOP]; +} + +static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) { + const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + format = unit_get_status_message_format(u, t); + if (format) + return format; + + /* Return generic strings */ + if (t == JOB_START) + return "Starting %s."; + else if (t == JOB_STOP) + return "Stopping %s."; + else if (t == JOB_RELOAD) + return "Reloading %s."; + + return NULL; +} + +static void unit_status_print_starting_stopping(Unit *u, JobType t) { + const char *format; + + assert(u); + + /* We only print status messages for selected units on + * selected operations. */ + + format = unit_get_status_message_format(u, t); + if (!format) + return; + + unit_status_printf(u, "", format); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { + const char *format; + char buf[LINE_MAX]; + sd_id128_t mid; + + assert(u); + + if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD) + return; + + if (log_on_console()) return; - format = format_table->starting_stopping[stopping]; + /* We log status messages for all units and all operations. */ + + format = unit_get_status_message_format_try_harder(u, t); if (!format) return; - unit_status_printf(u, "", format, unit_description(u)); + snprintf(buf, sizeof(buf), format, unit_description(u)); + char_array_0(buf); + + mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : + t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : + SD_MESSAGE_UNIT_RELOADING; + + log_struct_unit(LOG_INFO, + u->id, + MESSAGE_ID(mid), + "MESSAGE=%s", buf, + NULL); } +#pragma GCC diagnostic pop /* Errors: * -EBADR: This unit type does not support starting. @@ -980,17 +1068,19 @@ int unit_start(Unit *u) { * but we don't want to recheck the condition in that case. */ if (state != UNIT_ACTIVATING && !unit_condition_test(u)) { - log_debug("Starting of %s requested but condition failed. Ignoring.", u->id); + log_debug_unit(u->id, "Starting of %s requested but condition failed. Ignoring.", u->id); return -EALREADY; } /* Forward to the main object, if we aren't it. */ if ((following = unit_following(u))) { - log_debug("Redirecting start request from %s to %s.", u->id, following->id); + log_debug_unit(u->id, "Redirecting start request from %s to %s.", + u->id, following->id); return unit_start(following); } - unit_status_print_starting_stopping(u, false); + unit_status_log_starting_stopping_reloading(u, JOB_START); + unit_status_print_starting_stopping(u, JOB_START); /* If it is stopped, but we cannot start it, then fail */ if (!UNIT_VTABLE(u)->start) @@ -1036,11 +1126,13 @@ int unit_stop(Unit *u) { return -EALREADY; if ((following = unit_following(u))) { - log_debug("Redirecting stop request from %s to %s.", u->id, following->id); + log_debug_unit(u->id, "Redirecting stop request from %s to %s.", + u->id, following->id); return unit_stop(following); } - unit_status_print_starting_stopping(u, true); + unit_status_log_starting_stopping_reloading(u, JOB_STOP); + unit_status_print_starting_stopping(u, JOB_STOP); if (!UNIT_VTABLE(u)->stop) return -EBADR; @@ -1075,10 +1167,13 @@ int unit_reload(Unit *u) { return -ENOEXEC; if ((following = unit_following(u))) { - log_debug("Redirecting reload request from %s to %s.", u->id, following->id); + log_debug_unit(u->id, "Redirecting reload request from %s to %s.", + u->id, following->id); return unit_reload(following); } + unit_status_log_starting_stopping_reloading(u, JOB_RELOAD); + unit_add_to_dbus_queue(u); return UNIT_VTABLE(u)->reload(u); } @@ -1111,22 +1206,22 @@ static void unit_check_unneeded(Unit *u) { return; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; - log_info("Service %s is not needed anymore. Stopping.", u->id); + log_info_unit(u->id, "Service %s is not needed anymore. Stopping.", u->id); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); @@ -1144,7 +1239,7 @@ static void retroactively_start_dependencies(Unit *u) { !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i) + SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); @@ -1154,11 +1249,6 @@ static void retroactively_start_dependencies(Unit *u) { !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); - SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) @@ -1209,12 +1299,12 @@ static void check_unneeded_dependencies(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i) + SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); } -void unit_trigger_on_failure(Unit *u) { +void unit_start_on_failure(Unit *u) { Unit *other; Iterator i; @@ -1223,17 +1313,30 @@ void unit_trigger_on_failure(Unit *u) { if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0) return; - log_info("Triggering OnFailure= dependencies of %s.", u->id); + log_info_unit(u->id, "Triggering OnFailure= dependencies of %s.", u->id); SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; - if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0) - log_error("Failed to enqueue OnFailure= job: %s", strerror(-r)); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL); + if (r < 0) + log_error_unit(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r)); } } +void unit_trigger_notify(Unit *u) { + Unit *other; + Iterator i; + + assert(u); + + SET_FOREACH(other, u->dependencies[UNIT_TRIGGERED_BY], i) + if (UNIT_VTABLE(other)->trigger_notify) + UNIT_VTABLE(other)->trigger_notify(other, u); +} + void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { + Manager *m; bool unexpected; assert(u); @@ -1243,10 +1346,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* Note that this is called for all low-level state changes, * even if they might map to the same high-level * UnitActiveState! That means that ns == os is OK an expected - * behaviour here. For example: if a mount point is remounted + * behavior here. For example: if a mount point is remounted * this function will be called too! */ - if (u->manager->n_reloading <= 0) { + m = u->manager; + + if (m->n_reloading <= 0) { dual_timestamp ts; dual_timestamp_get(&ts); @@ -1260,14 +1365,21 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su u->active_enter_timestamp = ts; else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) u->active_exit_timestamp = ts; - - timer_unit_notify(u, ns); - path_unit_notify(u, ns); } if (UNIT_IS_INACTIVE_OR_FAILED(ns)) cgroup_bonding_trim_list(u->cgroup_bondings, true); + 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)) { + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + m->n_on_console--; + else + m->n_on_console++; + } + } + if (u->job) { unexpected = false; @@ -1334,7 +1446,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } else unexpected = true; - if (u->manager->n_reloading <= 0) { + if (m->n_reloading <= 0) { /* If this state change happened without being * requested by a job, then let's retroactively start @@ -1355,8 +1467,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su check_unneeded_dependencies(u); if (ns != os && ns == UNIT_FAILED) { - log_notice("Unit %s entered failed state.", u->id); - unit_trigger_on_failure(u); + log_notice_unit(u->id, + "MESSAGE=Unit %s entered failed state.", u->id); + unit_start_on_failure(u); } } @@ -1367,18 +1480,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* The bus just might have become available, * hence try to connect to it, if we aren't * yet connected. */ - bus_init(u->manager, true); + bus_init(m, true); if (u->type == UNIT_SERVICE && !UNIT_IS_ACTIVE_OR_RELOADING(os) && - u->manager->n_reloading <= 0) { + m->n_reloading <= 0) { /* Write audit record if we have just finished starting up */ - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true); + manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true); u->in_audit = true; } if (!UNIT_IS_ACTIVE_OR_RELOADING(os)) - manager_send_unit_plymouth(u->manager, u); + manager_send_unit_plymouth(m, u); } else { @@ -1388,46 +1501,47 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->type == UNIT_SERVICE && UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os) && - u->manager->n_reloading <= 0) { + m->n_reloading <= 0) { /* Hmm, if there was no start record written * write it now, so that we always have a nice * pair */ if (!u->in_audit) { - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); + manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); if (ns == UNIT_INACTIVE) - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true); + manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true); } else /* Write audit record if we have just finished shutting down */ - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); + manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); u->in_audit = false; } } - manager_recheck_journal(u->manager); + manager_recheck_journal(m); + unit_trigger_notify(u); /* Maybe we finished startup and are now ready for being * stopped because unneeded? */ - unit_check_unneeded(u); + if (u->manager->n_reloading <= 0) + unit_check_unneeded(u); unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); } int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) { - struct epoll_event ev; + 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)); - zero(ev); - ev.data.ptr = w; - ev.events = events; - if (epoll_ctl(u->manager->epoll_fd, w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, @@ -1474,8 +1588,8 @@ 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, usec_t delay, Watch *w) { - struct itimerspec its; +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; @@ -1494,14 +1608,13 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { } else if (w->type == WATCH_INVALID) { ours = true; - if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) + fd = timerfd_create(clock_id, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) return -errno; } else assert_not_reached("Invalid watch type"); - zero(its); - - if (delay <= 0) { + 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; @@ -1509,8 +1622,8 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { flags = TFD_TIMER_ABSTIME; } else { - timespec_store(&its.it_value, delay); - flags = 0; + timespec_store(&its.it_value, usec); + flags = relative ? 0 : TFD_TIMER_ABSTIME; } /* This will also flush the elapse counter */ @@ -1518,11 +1631,10 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { goto fail; if (w->type == WATCH_INVALID) { - struct epoll_event ev; - - zero(ev); - ev.data.ptr = w; - ev.events = EPOLLIN; + struct epoll_event ev = { + .data.ptr = w, + .events = EPOLLIN, + }; if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) goto fail; @@ -1595,11 +1707,13 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen [UNIT_WANTS] = UNIT_WANTED_BY, [UNIT_REQUISITE] = UNIT_REQUIRED_BY, [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE, - [UNIT_BIND_TO] = UNIT_BOUND_BY, + [UNIT_BINDS_TO] = UNIT_BOUND_BY, + [UNIT_PART_OF] = UNIT_CONSISTS_OF, [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID, [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID, [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID, - [UNIT_BOUND_BY] = UNIT_BIND_TO, + [UNIT_BOUND_BY] = UNIT_BINDS_TO, + [UNIT_CONSISTS_OF] = UNIT_PART_OF, [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS, [UNIT_BEFORE] = UNIT_AFTER, @@ -1609,8 +1723,8 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen [UNIT_REFERENCED_BY] = UNIT_REFERENCES, [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY, [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, - [UNIT_PROPAGATE_RELOAD_TO] = UNIT_PROPAGATE_RELOAD_FROM, - [UNIT_PROPAGATE_RELOAD_FROM] = UNIT_PROPAGATE_RELOAD_TO + [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, + [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO, }; int r, q = 0, v = 0, w = 0; @@ -1692,6 +1806,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, assert(u); assert(name || path); + assert(p); if (!name) name = path_get_file_name(path); @@ -1704,13 +1819,13 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, if (u->instance) s = unit_name_replace_instance(name, u->instance); else { - char *i; + _cleanup_free_ char *i = NULL; - if (!(i = unit_name_to_prefix(u->id))) + i = unit_name_to_prefix(u->id); + if (!i) return NULL; s = unit_name_replace_instance(name, i); - free(i); } if (!s) @@ -1723,22 +1838,20 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { Unit *other; int r; - char *s; + _cleanup_free_ char *s = NULL; assert(u); assert(name || path); - if (!(name = resolve_template(u, name, path, &s))) + name = resolve_template(u, name, path, &s); + if (!name) return -ENOMEM; - if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) - goto finish; - - r = unit_add_dependency(u, d, other, add_reference); + r = manager_load_unit(u->manager, name, path, NULL, &other); + if (r < 0) + return r; -finish: - free(s); - return r; + return unit_add_dependency(u, d, other, add_reference); } int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { @@ -1806,30 +1919,12 @@ finish: } int set_unit_path(const char *p) { - char *cwd, *c; - int r; + _cleanup_free_ char *c = NULL; /* This is mostly for debug purposes */ - - if (path_is_absolute(p)) { - if (!(c = strdup(p))) - return -ENOMEM; - } else { - if (!(cwd = get_current_dir_name())) - return -errno; - - r = asprintf(&c, "%s/%s", cwd, p); - free(cwd); - - if (r < 0) - return -ENOMEM; - } - - if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) { - r = -errno; - free(c); - return r; - } + c = path_make_absolute_cwd(p); + if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) + return -errno; return 0; } @@ -1843,7 +1938,7 @@ char *unit_dbus_path(Unit *u) { return unit_dbus_path_from_name(u->id); } -int unit_add_cgroup(Unit *u, CGroupBonding *b) { +static int unit_add_cgroup(Unit *u, CGroupBonding *b) { int r; assert(u); @@ -1852,8 +1947,9 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { assert(b->path); if (!b->controller) { - if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER))) - return -ENOMEM; + b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER); + if (!b->controller) + return log_oom(); b->ours = true; } @@ -1867,7 +1963,8 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { l = hashmap_get(u->manager->cgroup_bondings, b->path); LIST_PREPEND(CGroupBonding, by_path, l, b); - if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) { + r = hashmap_replace(u->manager->cgroup_bondings, b->path, l); + if (r < 0) { LIST_REMOVE(CGroupBonding, by_path, l, b); return r; } @@ -1879,27 +1976,37 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { return 0; } -static char *default_cgroup_path(Unit *u) { - char *p; - +char *unit_default_cgroup_path(Unit *u) { assert(u); if (u->instance) { - char *t; + _cleanup_free_ char *t = NULL, *escaped_template = NULL, *escaped_instance = NULL; t = unit_name_template(u->id); if (!t) return NULL; - p = join(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL); - free(t); - } else - p = join(u->manager->cgroup_hierarchy, "/", u->id, NULL); + escaped_template = cg_escape(t); + if (!escaped_template) + return NULL; + + escaped_instance = cg_escape(u->id); + if (!escaped_instance) + return NULL; + + return strjoin(u->manager->cgroup_hierarchy, "/", escaped_template, "/", escaped_instance, NULL); + } else { + _cleanup_free_ char *escaped = NULL; + + escaped = cg_escape(u->id); + if (!escaped) + return NULL; - return p; + return strjoin(u->manager->cgroup_hierarchy, "/", escaped, NULL); + } } -int unit_add_cgroup_from_text(Unit *u, const char *name) { +int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) { char *controller = NULL, *path = NULL; CGroupBonding *b = NULL; bool ours = false; @@ -1908,11 +2015,12 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { assert(u); assert(name); - if ((r = cg_split_spec(name, &controller, &path)) < 0) + r = cg_split_spec(name, &controller, &path); + if (r < 0) return r; if (!path) { - path = default_cgroup_path(u); + path = unit_default_cgroup_path(u); ours = true; } @@ -1924,16 +2032,42 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { if (!path || !controller) { free(path); free(controller); - - return -ENOMEM; + return log_oom(); } - if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) { + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (b) { + if (streq(path, b->path)) { + free(path); + free(controller); + + if (ret) + *ret = b; + return 0; + } + + if (overwrite && !b->essential) { + free(controller); + + free(b->path); + b->path = path; + + b->ours = ours; + b->realized = false; + + if (ret) + *ret = b; + + return 1; + } + r = -EEXIST; + b = NULL; goto fail; } - if (!(b = new0(CGroupBonding, 1))) { + b = new0(CGroupBonding, 1); + if (!b) { r = -ENOMEM; goto fail; } @@ -1943,10 +2077,14 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { b->ours = ours; b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - if ((r = unit_add_cgroup(u, b)) < 0) + r = unit_add_cgroup(u, b); + if (r < 0) goto fail; - return 0; + if (ret) + *ret = b; + + return 1; fail: free(path); @@ -1962,28 +2100,35 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) { assert(u); + if (controller && !cg_controller_is_valid(controller, true)) + return -EINVAL; + if (!controller) controller = SYSTEMD_CGROUP_CONTROLLER; if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) return 0; - if (!(b = new0(CGroupBonding, 1))) + b = new0(CGroupBonding, 1); + if (!b) return -ENOMEM; - if (!(b->controller = strdup(controller))) + b->controller = strdup(controller); + if (!b->controller) goto fail; - if (!(b->path = default_cgroup_path(u))) + b->path = unit_default_cgroup_path(u); + if (!b->path) goto fail; b->ours = true; b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - if ((r = unit_add_cgroup(u, b)) < 0) + r = unit_add_cgroup(u, b); + if (r < 0) goto fail; - return 0; + return 1; fail: free(b->path); @@ -2006,7 +2151,8 @@ int unit_add_default_cgroups(Unit *u) { if (!u->manager->cgroup_hierarchy) return 0; - if ((r = unit_add_one_default_cgroup(u, NULL)) < 0) + r = unit_add_one_default_cgroup(u, NULL); + if (r < 0) return r; STRV_FOREACH(c, u->manager->default_controllers) @@ -2021,42 +2167,90 @@ int unit_add_default_cgroups(Unit *u) { CGroupBonding* unit_get_default_cgroup(Unit *u) { assert(u); - return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER); + return cgroup_bonding_find_list(u->cgroup_bondings, NULL); } -int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) { - int r; - char *c = NULL; +int unit_add_cgroup_attribute( + Unit *u, + const CGroupSemantics *semantics, + const char *controller, + const char *name, + const char *value, + CGroupAttribute **ret) { + + _cleanup_free_ char *c = NULL; CGroupAttribute *a; + int r; assert(u); - assert(name); assert(value); - if (!controller) { - const char *dot; + if (semantics) { + /* Semantics always take precedence */ + if (semantics->name) + name = semantics->name; - dot = strchr(name, '.'); - if (!dot) - return -EINVAL; + if (semantics->controller) + controller = semantics->controller; + } - c = strndup(name, dot - name); - if (!c) - return -ENOMEM; + if (!name) + return -EINVAL; + + if (!controller) { + r = cg_controller_from_attr(name, &c); + if (r < 0) + return -EINVAL; controller = c; } - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - r = -EINVAL; - goto finish; + if (!controller || + streq(controller, SYSTEMD_CGROUP_CONTROLLER) || + streq(controller, "systemd")) + return -EINVAL; + + if (!filename_is_safe(name)) + return -EINVAL; + + if (!cg_controller_is_valid(controller, false)) + return -EINVAL; + + /* Check if this attribute already exists. Note that we will + * explicitly check for the value here too, as there are + * attributes which accept multiple values. */ + a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name); + if (a) { + if (streq(value, a->value)) { + /* Exactly the same value is always OK, let's ignore this */ + if (ret) + *ret = a; + + return 0; + } + + if (semantics && !semantics->multiple) { + char *v; + + /* If this is a single-item entry, we can + * simply patch the existing attribute */ + + v = strdup(value); + if (!v) + return -ENOMEM; + + free(a->value); + a->value = v; + + if (ret) + *ret = a; + return 1; + } } a = new0(CGroupAttribute, 1); - if (!a) { - r = -ENOMEM; - goto finish; - } + if (!a) + return -ENOMEM; if (c) { a->controller = c; @@ -2072,58 +2266,54 @@ int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, free(a->name); free(a->value); free(a); - return -ENOMEM; } - a->map_callback = map_callback; + a->semantics = semantics; + a->unit = u; LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a); - r = 0; + if (ret) + *ret = a; -finish: - free(c); - return r; + return 1; } int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { - char *t; + _cleanup_free_ char *t = NULL; int r; assert(u); assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->id, type))) + t = unit_name_change_suffix(u->id, type); + if (!t) return -ENOMEM; assert(!unit_has_name(u, t)); r = manager_load_unit(u->manager, t, NULL, NULL, _found); - free(t); - assert(r < 0 || *_found != u); - return r; } int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { + _cleanup_free_ char *t = NULL; Unit *found; - char *t; assert(u); assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->id, type))) + 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); - free(t); - if (!found) return -ENOENT; @@ -2131,182 +2321,6 @@ int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { return 0; } -static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - return unit_name_to_prefix_and_instance(u->id); -} - -static char *specifier_prefix(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - return unit_name_to_prefix(u->id); -} - -static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) { - Unit *u = userdata; - char *p, *r; - - assert(u); - - if (!(p = unit_name_to_prefix(u->id))) - return NULL; - - r = unit_name_unescape(p); - free(p); - - return r; -} - -static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - if (u->instance) - return unit_name_unescape(u->instance); - - return strdup(""); -} - -static char *specifier_filename(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - if (u->instance) - return unit_name_path_unescape(u->instance); - - return unit_name_to_path(u->instance); -} - -static char *specifier_cgroup(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - return default_cgroup_path(u); -} - -static char *specifier_cgroup_root(char specifier, void *data, void *userdata) { - Unit *u = userdata; - char *p; - assert(u); - - if (specifier == 'r') - return strdup(u->manager->cgroup_hierarchy); - - if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0) - return strdup(""); - - if (streq(p, "/")) { - free(p); - return strdup(""); - } - - return p; -} - -static char *specifier_runtime(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - if (u->manager->running_as == MANAGER_USER) { - const char *e; - - e = getenv("XDG_RUNTIME_DIR"); - if (e) - return strdup(e); - } - - return strdup("/run"); -} - -char *unit_name_printf(Unit *u, const char* format) { - - /* - * This will use the passed string as format string and - * replace the following specifiers: - * - * %n: the full id of the unit (foo@bar.waldo) - * %N: the id of the unit without the suffix (foo@bar) - * %p: the prefix (foo) - * %i: the instance (bar) - */ - - const Specifier table[] = { - { 'n', specifier_string, u->id }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'i', specifier_string, u->instance }, - { 0, NULL, NULL } - }; - - assert(u); - assert(format); - - return specifier_printf(format, table, u); -} - -char *unit_full_printf(Unit *u, const char *format) { - - /* This is similar to unit_name_printf() but also supports - * unescaping. Also, adds a couple of additional codes: - * - * %c cgroup path of unit - * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711") - * %R parent of root cgroup path (e.g. "/usr/lennart/shared") - * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR) - */ - - const Specifier table[] = { - { 'n', specifier_string, u->id }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'P', specifier_prefix_unescaped, NULL }, - { 'i', specifier_string, u->instance }, - { 'I', specifier_instance_unescaped, NULL }, - { 'f', specifier_filename, NULL }, - { 'c', specifier_cgroup, NULL }, - { 'r', specifier_cgroup_root, NULL }, - { 'R', specifier_cgroup_root, NULL }, - { 't', specifier_runtime, NULL }, - { 0, NULL, NULL } - }; - - assert(u); - assert(format); - - return specifier_printf(format, table, u); -} - -char **unit_full_printf_strv(Unit *u, char **l) { - size_t n; - char **r, **i, **j; - - /* Applies unit_full_printf to every entry in l */ - - assert(u); - - n = strv_length(l); - if (!(r = new(char*, n+1))) - return NULL; - - for (i = l, j = r; *i; i++, j++) - if (!(*j = unit_full_printf(u, *i))) - goto fail; - - *j = NULL; - return r; - -fail: - for (j--; j >= r; j--) - free(*j); - - free(r); - - return NULL; -} - int unit_watch_bus_name(Unit *u, const char *name) { assert(u); assert(name); @@ -2330,7 +2344,7 @@ bool unit_can_serialize(Unit *u) { return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item; } -int unit_serialize(Unit *u, FILE *f, FDSet *fds) { +int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { int r; assert(u); @@ -2343,14 +2357,17 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds) { if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0) return r; - 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); + 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); + } } dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp); @@ -2454,6 +2471,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { job_free(j); return r; } + + if (j->state == JOB_RUNNING) + u->manager->n_running_jobs++; } else { /* legacy */ JobType type = job_type_from_string(v); @@ -2515,16 +2535,18 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { r = manager_load_unit(u->manager, e, NULL, NULL, &device); free(e); - if (r < 0) return r; - if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BIND_TO, device, true)) < 0) + r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true); + if (r < 0) return r; - if (wants) - if ((r = unit_add_dependency(device, UNIT_WANTS, u, false)) < 0) + if (wants) { + r = unit_add_dependency(device, UNIT_WANTS, u, false); + if (r < 0) return r; + } return 0; } @@ -2554,25 +2576,18 @@ int unit_coldplug(Unit *u) { return 0; } -void unit_status_printf(Unit *u, const char *status, const char *format, ...) { - va_list ap; - - assert(u); - assert(format); - - if (!manager_get_show_status(u->manager)) - return; - - if (!manager_is_booting_or_shutting_down(u->manager)) - return; - - va_start(ap, format); - status_vprintf(status, true, format, ap); - va_end(ap); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) { + manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u)); } +#pragma GCC diagnostic pop bool unit_need_daemon_reload(Unit *u) { + _cleanup_strv_free_ char **t = NULL; + char **path; struct stat st; + unsigned loaded_cnt, current_cnt; assert(u); @@ -2597,7 +2612,30 @@ bool unit_need_daemon_reload(Unit *u) { return true; } - return false; + t = unit_find_dropin_paths(u); + loaded_cnt = strv_length(t); + current_cnt = strv_length(u->dropin_paths); + + if (loaded_cnt == current_cnt) { + if (loaded_cnt == 0) + return false; + + if (strv_overlap(u->dropin_paths, t)) { + STRV_FOREACH(path, u->dropin_paths) { + zero(st); + if (stat(*path, &st) < 0) + return true; + + if (u->dropin_mtime > 0 && + timespec_load(&st.st_mtim) > u->dropin_mtime) + return true; + } + + return false; + } else + return true; + } else + return true; } void unit_reset_failed(Unit *u) { @@ -2616,21 +2654,30 @@ Unit *unit_following(Unit *u) { return NULL; } -bool unit_pending_inactive(Unit *u) { +bool unit_stop_pending(Unit *u) { assert(u); - /* Returns true if the unit is inactive or going down */ + /* This call does check the current state of the unit. It's + * hence useful to be called from state change calls of the + * unit itself, where the state isn't updated yet. This is + * different from unit_inactive_or_pending() which checks both + * the current state and for a queued job. */ - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) - return true; + return u->job && u->job->type == JOB_STOP; +} - if (u->job && u->job->type == JOB_STOP) +bool unit_inactive_or_pending(Unit *u) { + assert(u); + + /* Returns true if the unit is inactive or going down */ + + if (unit_stop_pending(u)) return true; return false; } -bool unit_pending_active(Unit *u) { +bool unit_active_or_pending(Unit *u) { assert(u); /* Returns true if the unit is active or going up */ @@ -2647,20 +2694,81 @@ bool unit_pending_active(Unit *u) { return false; } -int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error) { +int unit_kill(Unit *u, KillWho w, int signo, DBusError *error) { assert(u); assert(w >= 0 && w < _KILL_WHO_MAX); - assert(m >= 0 && m < _KILL_MODE_MAX); assert(signo > 0); assert(signo < _NSIG); - if (m == KILL_NONE) - return 0; - if (!UNIT_VTABLE(u)->kill) return -ENOTSUP; - return UNIT_VTABLE(u)->kill(u, w, m, signo, error); + return UNIT_VTABLE(u)->kill(u, w, signo, error); +} + +int unit_kill_common( + Unit *u, + KillWho who, + int signo, + pid_t main_pid, + pid_t control_pid, + DBusError *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)); + else + dbus_set_error(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)); + else + dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); + return -ESRCH; + } + + if (who == KILL_CONTROL || who == KILL_ALL) + if (control_pid > 0) + if (kill(control_pid, signo) < 0) + r = -errno; + + if (who == KILL_MAIN || who == KILL_ALL) + if (main_pid > 0) + if (kill(main_pid, signo) < 0) + r = -errno; + + if (who == KILL_ALL) { + _cleanup_set_free_ Set *pid_set = NULL; + int q; + + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) + return -ENOMEM; + + /* Exclude the control/main pid from being killed via the cgroup */ + if (control_pid > 0) { + q = set_put(pid_set, LONG_TO_PTR(control_pid)); + if (q < 0) + return q; + } + + if (main_pid > 0) { + q = set_put(pid_set, LONG_TO_PTR(main_pid)); + if (q < 0) + return q; + } + + q = cgroup_bonding_kill_list(u->cgroup_bondings, signo, false, false, pid_set, NULL); + if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) + r = q; + } + + return r; } int unit_following_set(Unit *u, Set **s) { @@ -2679,7 +2787,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 == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, path_get_file_name(u->fragment_path)); return u->unit_file_state; @@ -2746,6 +2854,193 @@ int unit_add_mount_links(Unit *u) { return 0; } +int unit_exec_context_defaults(Unit *u, ExecContext *c) { + unsigned i; + int r; + + assert(u); + assert(c); + + /* This only copies in the ones that need memory */ + + for (i = 0; i < RLIMIT_NLIMITS; i++) + if (u->manager->rlimit[i] && !c->rlimit[i]) { + c->rlimit[i] = newdup(struct rlimit, u->manager->rlimit[i], 1); + if (!c->rlimit[i]) + return -ENOMEM; + } + + if (u->manager->running_as == SYSTEMD_USER && + !c->working_directory) { + + r = get_home_dir(&c->working_directory); + if (r < 0) + return r; + } + + return 0; +} + +ExecContext *unit_get_exec_context(Unit *u) { + size_t offset; + assert(u); + + offset = UNIT_VTABLE(u)->exec_context_offset; + if (offset <= 0) + return NULL; + + return (ExecContext*) ((uint8_t*) u + offset); +} + +static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) { + char *p, *q; + int r; + + assert(u); + assert(name); + assert(_p); + assert(_q); + + if (u->manager->running_as == SYSTEMD_USER && runtime) + return -ENOTSUP; + + if (!filename_is_safe(name)) + return -EINVAL; + + if (u->manager->running_as == SYSTEMD_USER) { + _cleanup_free_ char *c = NULL; + + r = user_config_home(&c); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + p = strjoin(c, "/", u->id, ".d", NULL); + } else if (runtime) + p = strjoin("/run/systemd/system/", u->id, ".d", NULL); + else + p = strjoin("/etc/systemd/system/", u->id, ".d", NULL); + if (!p) + return -ENOMEM; + + q = strjoin(p, "/50-", name, ".conf", NULL); + if (!q) { + free(p); + return -ENOMEM; + } + + *_p = p; + *_q = q; + return 0; +} + +int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) { + _cleanup_free_ char *p = NULL, *q = NULL; + int r; + + assert(u); + + r = drop_in_file(u, runtime, name, &p, &q); + if (r < 0) + return r; + + mkdir_p(p, 0755); + return write_string_file_atomic_label(q, data); +} + +int unit_remove_drop_in(Unit *u, bool runtime, const char *name) { + _cleanup_free_ char *p = NULL, *q = NULL; + int r; + + assert(u); + + r = drop_in_file(u, runtime, name, &p, &q); + if (unlink(q) < 0) + r = -errno; + else + r = 0; + + rmdir(p); + return r; +} + +int unit_kill_context( + Unit *u, + KillContext *c, + bool sigkill, + pid_t main_pid, + pid_t control_pid, + bool main_pid_alien) { + + int sig, wait_for_exit = 0, r; + + assert(u); + assert(c); + + if (c->kill_mode == KILL_NONE) + return 0; + + sig = sigkill ? SIGKILL : c->kill_signal; + + if (main_pid > 0) { + r = kill_and_sigcont(main_pid, sig); + + if (r < 0 && r != -ESRCH) { + _cleanup_free_ char *comm = NULL; + get_process_comm(main_pid, &comm); + + log_warning_unit(u->id, "Failed to kill main process %li (%s): %s", + (long) main_pid, strna(comm), strerror(-r)); + } else + wait_for_exit = !main_pid_alien; + } + + if (control_pid > 0) { + r = kill_and_sigcont(control_pid, sig); + + if (r < 0 && r != -ESRCH) { + _cleanup_free_ char *comm = NULL; + get_process_comm(control_pid, &comm); + + log_warning_unit(u->id, + "Failed to kill control process %li (%s): %s", + (long) control_pid, strna(comm), strerror(-r)); + } else + wait_for_exit = true; + } + + if (c->kill_mode == KILL_CONTROL_GROUP) { + _cleanup_set_free_ Set *pid_set = NULL; + + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) + return -ENOMEM; + + /* Exclude the main/control pids from being killed via the cgroup */ + if (main_pid > 0) { + r = set_put(pid_set, LONG_TO_PTR(main_pid)); + if (r < 0) + return r; + } + + if (control_pid > 0) { + r = set_put(pid_set, LONG_TO_PTR(control_pid)); + if (r < 0) + return r; + } + + r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL); + if (r < 0) { + if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) + log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r)); + } else if (r > 0) + wait_for_exit = true; + } + + return wait_for_exit; +} + static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", [UNIT_RELOADING] = "reloading", @@ -2760,25 +3055,27 @@ DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = "Requires", [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable", - [UNIT_WANTS] = "Wants", [UNIT_REQUISITE] = "Requisite", [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable", + [UNIT_WANTS] = "Wants", + [UNIT_BINDS_TO] = "BindsTo", + [UNIT_PART_OF] = "PartOf", [UNIT_REQUIRED_BY] = "RequiredBy", [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable", - [UNIT_BIND_TO] = "BindTo", [UNIT_WANTED_BY] = "WantedBy", + [UNIT_BOUND_BY] = "BoundBy", + [UNIT_CONSISTS_OF] = "ConsistsOf", [UNIT_CONFLICTS] = "Conflicts", [UNIT_CONFLICTED_BY] = "ConflictedBy", - [UNIT_BOUND_BY] = "BoundBy", [UNIT_BEFORE] = "Before", [UNIT_AFTER] = "After", - [UNIT_REFERENCES] = "References", - [UNIT_REFERENCED_BY] = "ReferencedBy", [UNIT_ON_FAILURE] = "OnFailure", [UNIT_TRIGGERS] = "Triggers", [UNIT_TRIGGERED_BY] = "TriggeredBy", - [UNIT_PROPAGATE_RELOAD_TO] = "PropagateReloadTo", - [UNIT_PROPAGATE_RELOAD_FROM] = "PropagateReloadFrom" + [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", + [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", + [UNIT_REFERENCES] = "References", + [UNIT_REFERENCED_BY] = "ReferencedBy", }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);