X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Funit.c;h=348d139586950637438d9ff58c5eb1a3b1af900d;hp=bb2b8c2fe010cb917b2f1df95b622528b332cf81;hb=faf919f1ebebdfc13f769bb6585e64e7ad4b301b;hpb=cca098b09535f58c638ed41310be60504be49bc4 diff --git a/src/unit.c b/src/unit.c index bb2b8c2fe..348d13958 100644 --- a/src/unit.c +++ b/src/unit.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "set.h" #include "unit.h" @@ -38,6 +39,8 @@ #include "unit-name.h" #include "specifier.h" #include "dbus-unit.h" +#include "special.h" +#include "cgroup-util.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -68,6 +71,7 @@ Unit *unit_new(Manager *m) { u->meta.manager = m; u->meta.type = _UNIT_TYPE_INVALID; u->meta.deserialized_job = _JOB_TYPE_INVALID; + u->meta.default_dependencies = true; return u; } @@ -81,7 +85,7 @@ bool unit_has_name(Unit *u, const char *name) { int unit_add_name(Unit *u, const char *text) { UnitType t; - char *s = NULL, *i = NULL; + char *s, *i = NULL; int r; assert(u); @@ -116,7 +120,9 @@ int unit_add_name(Unit *u, const char *text) { if (i && unit_vtable[t]->no_instances) goto fail; - if (u->meta.type != _UNIT_TYPE_INVALID && !streq_ptr(u->meta.instance, i)) { + /* Ensure that this unit is either instanced or not instanced, + * but not both. */ + if (u->meta.type != _UNIT_TYPE_INVALID && !u->meta.instance != !i) { r = -EINVAL; goto fail; } @@ -168,7 +174,8 @@ fail: } int unit_choose_id(Unit *u, const char *name) { - char *s, *t = NULL; + char *s, *t = NULL, *i; + int r; assert(u); assert(name); @@ -191,7 +198,14 @@ int unit_choose_id(Unit *u, const char *name) { if (!s) return -ENOENT; + if ((r = unit_name_to_instance(s, &i)) < 0) + return r; + u->meta.id = s; + + free(u->meta.instance); + u->meta.instance = i; + unit_add_to_dbus_queue(u); return 0; @@ -280,7 +294,8 @@ void unit_add_to_dbus_queue(Unit *u) { if (u->meta.load_state == UNIT_STUB || u->meta.in_dbus_queue) return; - if (set_isempty(u->meta.manager->subscribed)) { + /* Shortcut things if nobody cares */ + if (!bus_has_subscriber(u->meta.manager)) { u->meta.sent_dbus_new_signal = true; return; } @@ -319,10 +334,19 @@ void unit_free(Unit *u) { bus_unit_send_removed_signal(u); - /* Detach from next 'bigger' objects */ + if (u->meta.load_state != UNIT_STUB) + if (UNIT_VTABLE(u)->done) + UNIT_VTABLE(u)->done(u); + SET_FOREACH(t, u->meta.names, i) hashmap_remove_value(u->meta.manager->units, t, u); + if (u->meta.job) + job_free(u->meta.job); + + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + bidi_set_free(u, u->meta.dependencies[d]); + if (u->meta.type != _UNIT_TYPE_INVALID) LIST_REMOVE(Meta, units_per_type, u->meta.manager->units_per_type[u->meta.type], &u->meta); @@ -340,25 +364,12 @@ void unit_free(Unit *u) { u->meta.manager->n_in_gc_queue--; } - /* Free data and next 'smaller' objects */ - if (u->meta.job) - job_free(u->meta.job); - - if (u->meta.load_state != UNIT_STUB) - if (UNIT_VTABLE(u)->done) - UNIT_VTABLE(u)->done(u); - cgroup_bonding_free_list(u->meta.cgroup_bondings); - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - bidi_set_free(u, u->meta.dependencies[d]); - free(u->meta.description); free(u->meta.fragment_path); - while ((t = set_steal_first(u->meta.names))) - free(t); - set_free(u->meta.names); + set_free_free(u->meta.names); free(u->meta.instance); @@ -368,8 +379,12 @@ void unit_free(Unit *u) { UnitActiveState unit_active_state(Unit *u) { assert(u); - if (u->meta.load_state != UNIT_LOADED) - return UNIT_INACTIVE; + if (u->meta.load_state == UNIT_MERGED) + return unit_active_state(unit_follow_merge(u)); + + /* After a reload it might happen that a unit is not correctly + * loaded but still has a process around. That's why we won't + * shortcut failed loading to UNIT_INACTIVE_MAINTENANCE. */ return UNIT_VTABLE(u)->active_state(u); } @@ -404,10 +419,7 @@ static void merge_names(Unit *u, Unit *other) { complete_move(&u->meta.names, &other->meta.names); - while ((t = set_steal_first(other->meta.names))) - free(t); - - set_free(other->meta.names); + set_free_free(other->meta.names); other->meta.names = NULL; other->meta.id = NULL; @@ -459,7 +471,7 @@ int unit_merge(Unit *u, Unit *other) { if (u->meta.type != other->meta.type) return -EINVAL; - if (!streq_ptr(u->meta.instance, other->meta.instance)) + if (!u->meta.instance != !other->meta.instance) return -EINVAL; if (other->meta.load_state != UNIT_STUB && @@ -469,7 +481,7 @@ int unit_merge(Unit *u, Unit *other) { if (other->meta.job) return -EEXIST; - if (unit_active_state(other) != UNIT_INACTIVE) + if (!UNIT_IS_INACTIVE_OR_MAINTENANCE(unit_active_state(other))) return -EEXIST; /* Merge names */ @@ -548,7 +560,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0) return r; - if (u->meta.manager->running_as != MANAGER_SESSION) + if (u->meta.manager->running_as == MANAGER_SYSTEM) if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0) return r; @@ -575,7 +587,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX], timestamp3[FORMAT_TIMESTAMP_MAX], - timestamp4[FORMAT_TIMESTAMP_MAX]; + timestamp4[FORMAT_TIMESTAMP_MAX], + timespan[FORMAT_TIMESPAN_MAX]; assert(u); assert(u->meta.type >= 0); @@ -596,7 +609,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tActive Exit Timestamp: %s\n" "%s\tInactive Enter Timestamp: %s\n" "%s\tGC Check Good: %s\n" - "%s\tOnly By Dependency: %s\n", + "%s\tNeed Daemon Reload: %s\n", prefix, u->meta.id, prefix, unit_description(u), prefix, strna(u->meta.instance), @@ -607,7 +620,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->meta.active_exit_timestamp.realtime)), prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->meta.inactive_enter_timestamp.realtime)), prefix, yes_no(unit_check_gc(u)), - prefix, yes_no(u->meta.only_by_dependency)); + prefix, yes_no(unit_need_daemon_reload(u))); SET_FOREACH(t, u->meta.names, i) fprintf(f, "%s\tName: %s\n", prefix, t); @@ -615,6 +628,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { if (u->meta.fragment_path) fprintf(f, "%s\tFragment Path: %s\n", prefix, u->meta.fragment_path); + if (u->meta.job_timeout > 0) + fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->meta.job_timeout)); + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { Unit *other; @@ -625,9 +641,15 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { if (u->meta.load_state == UNIT_LOADED) { fprintf(f, "%s\tRecursive Stop: %s\n" - "%s\tStop When Unneeded: %s\n", + "%s\tStopWhenUnneeded: %s\n" + "%s\tOnlyByDependency: %s\n" + "%s\tDefaultDependencies: %s\n" + "%s\tIgnoreDependencyFailure: %s\n", prefix, yes_no(u->meta.recursive_stop), - prefix, yes_no(u->meta.stop_when_unneeded)); + prefix, yes_no(u->meta.stop_when_unneeded), + prefix, yes_no(u->meta.only_by_dependency), + prefix, yes_no(u->meta.default_dependencies), + prefix, yes_no(u->meta.ignore_dependency_failure)); LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings) fprintf(f, "%s\tControlGroup: %s:%s\n", @@ -736,7 +758,7 @@ fail: u->meta.load_state = UNIT_FAILED; unit_add_to_dbus_queue(u); - log_debug("Failed to load configuration for %s: %s", u->meta.id, strerror(-r)); + log_notice("Failed to load configuration for %s: %s", u->meta.id, strerror(-r)); return r; } @@ -752,6 +774,9 @@ int unit_start(Unit *u) { assert(u); + if (u->meta.load_state != UNIT_LOADED) + return -EINVAL; + /* If this is already (being) started, then this will * succeed. Note that this will even succeed if this unit is * not startable by the user. This is relied on to detect when @@ -771,6 +796,9 @@ int unit_start(Unit *u) { * before it will start again. */ unit_add_to_dbus_queue(u); + + unit_status_printf(u, "Starting %s...\n", unit_description(u)); + return UNIT_VTABLE(u)->start(u); } @@ -791,13 +819,16 @@ int unit_stop(Unit *u) { assert(u); state = unit_active_state(u); - if (state == UNIT_INACTIVE) + if (UNIT_IS_INACTIVE_OR_MAINTENANCE(state)) return -EALREADY; if (!UNIT_VTABLE(u)->stop) return -EBADR; unit_add_to_dbus_queue(u); + + unit_status_printf(u, "Stopping %s...\n", unit_description(u)); + return UNIT_VTABLE(u)->stop(u); } @@ -811,11 +842,14 @@ int unit_reload(Unit *u) { assert(u); + if (u->meta.load_state != UNIT_LOADED) + return -EINVAL; + if (!unit_can_reload(u)) return -EBADR; state = unit_active_state(u); - if (unit_active_state(u) == UNIT_ACTIVE_RELOADING) + if (unit_active_state(u) == UNIT_RELOADING) return -EALREADY; if (unit_active_state(u) != UNIT_ACTIVE) @@ -864,10 +898,10 @@ static void unit_check_uneeded(Unit *u) { if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) return; - log_debug("Service %s is not needed anymore. Stopping.", u->meta.id); + log_info("Service %s is not needed anymore. Stopping.", u->meta.id); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ - manager_add_job(u->meta.manager, JOB_STOP, u, JOB_FAIL, true, NULL); + manager_add_job(u->meta.manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); } static void retroactively_start_dependencies(Unit *u) { @@ -879,23 +913,23 @@ static void retroactively_start_dependencies(Unit *u) { SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i) if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); + manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); + manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i) if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); + manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i) if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); + manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i) if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); + manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); } static void retroactively_stop_dependencies(Unit *u) { @@ -909,7 +943,7 @@ static void retroactively_stop_dependencies(Unit *u) { /* Pull down units need us recursively if enabled */ SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); + manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); } /* Garbage collect services that might not be needed anymore, if enabled */ @@ -931,8 +965,8 @@ static void retroactively_stop_dependencies(Unit *u) { } void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { - bool unexpected = false; - timestamp ts; + dual_timestamp ts; + bool unexpected; assert(u); assert(os < _UNIT_ACTIVE_STATE_MAX); @@ -945,11 +979,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { * this function will be called too and the utmp code below * relies on that! */ - timestamp_get(&ts); + dual_timestamp_get(&ts); - if (os == UNIT_INACTIVE && ns != UNIT_INACTIVE) + if (UNIT_IS_INACTIVE_OR_MAINTENANCE(os) && !UNIT_IS_INACTIVE_OR_MAINTENANCE(ns)) u->meta.inactive_exit_timestamp = ts; - else if (os != UNIT_INACTIVE && ns == UNIT_INACTIVE) + else if (!UNIT_IS_INACTIVE_OR_MAINTENANCE(os) && UNIT_IS_INACTIVE_OR_MAINTENANCE(ns)) u->meta.inactive_enter_timestamp = ts; if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) @@ -957,10 +991,14 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) u->meta.active_exit_timestamp = ts; + if (UNIT_IS_INACTIVE_OR_MAINTENANCE(ns)) + cgroup_bonding_trim_list(u->meta.cgroup_bondings, true); + timer_unit_notify(u, ns); path_unit_notify(u, ns); if (u->meta.job) { + unexpected = false; if (u->meta.job->state == JOB_WAITING) @@ -969,77 +1007,88 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { * failed previously due to EAGAIN. */ job_add_to_run_queue(u->meta.job); - else { - assert(u->meta.job->state == JOB_RUNNING); - - /* Let's check whether this state change - * constitutes a finished job, or maybe - * cotradicts a running job and hence needs to - * invalidate jobs. */ + /* Let's check whether this state change constitutes a + * finished job, or maybe cotradicts a running job and + * hence needs to invalidate jobs. */ - switch (u->meta.job->type) { + switch (u->meta.job->type) { - case JOB_START: - case JOB_VERIFY_ACTIVE: + case JOB_START: + case JOB_VERIFY_ACTIVE: - if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) - job_finish_and_invalidate(u->meta.job, true); - else if (ns != UNIT_ACTIVATING) { - unexpected = true; - job_finish_and_invalidate(u->meta.job, false); - } + if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) + job_finish_and_invalidate(u->meta.job, true); + else if (u->meta.job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { + unexpected = true; + job_finish_and_invalidate(u->meta.job, false); + } - break; + break; - case JOB_RELOAD: - case JOB_RELOAD_OR_START: + case JOB_RELOAD: + case JOB_RELOAD_OR_START: + if (u->meta.job->state == JOB_RUNNING) { if (ns == UNIT_ACTIVE) job_finish_and_invalidate(u->meta.job, true); - else if (ns != UNIT_ACTIVATING && ns != UNIT_ACTIVE_RELOADING) { + else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { unexpected = true; job_finish_and_invalidate(u->meta.job, false); } + } - break; + break; - case JOB_STOP: - case JOB_RESTART: - case JOB_TRY_RESTART: + case JOB_STOP: + case JOB_RESTART: + case JOB_TRY_RESTART: - if (ns == UNIT_INACTIVE) - job_finish_and_invalidate(u->meta.job, true); - else if (ns != UNIT_DEACTIVATING) { - unexpected = true; - job_finish_and_invalidate(u->meta.job, false); - } + if (ns == UNIT_INACTIVE || ns == UNIT_MAINTENANCE) + job_finish_and_invalidate(u->meta.job, true); + else if (u->meta.job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { + unexpected = true; + job_finish_and_invalidate(u->meta.job, false); + } - break; + break; - default: - assert_not_reached("Job type unknown"); - } + default: + assert_not_reached("Job type unknown"); } - } + + } else + unexpected = true; /* If this state change happened without being requested by a - * job, then let's retroactively start or stop dependencies */ + * job, then let's retroactively start or stop + * dependencies. We skip that step when deserializing, since + * we don't want to create any additional jobs just because + * something is already activated. */ - if (unexpected) { + if (unexpected && u->meta.manager->n_deserializing <= 0) { if (UNIT_IS_INACTIVE_OR_DEACTIVATING(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) retroactively_start_dependencies(u); else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) retroactively_stop_dependencies(u); } + if (ns != os && ns == UNIT_MAINTENANCE) { + Iterator i; + Unit *other; + + SET_FOREACH(other, u->meta.dependencies[UNIT_ON_FAILURE], i) + manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); + + log_notice("Unit %s entered maintenance state.", u->meta.id); + } + /* Some names are special */ if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) { /* The bus just might have become available, * hence try to connect to it, if we aren't * yet connected. */ - bus_init_system(u->meta.manager); - bus_init_api(u->meta.manager); + bus_init(u->meta.manager); } if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) @@ -1126,14 +1175,14 @@ int unit_watch_pid(Unit *u, pid_t pid) { /* Watch a specific PID. We only support one unit watching * each PID for now. */ - return hashmap_put(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u); + return hashmap_put(u->meta.manager->watch_pids, LONG_TO_PTR(pid), u); } void unit_unwatch_pid(Unit *u, pid_t pid) { assert(u); assert(pid >= 1); - hashmap_remove_value(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u); + hashmap_remove_value(u->meta.manager->watch_pids, LONG_TO_PTR(pid), u); } int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { @@ -1143,18 +1192,23 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { assert(u); assert(w); - assert(w->type == WATCH_INVALID || (w->type == WATCH_TIMER && w->data.unit == u)); + 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_TIMER) { + if (w->type == WATCH_UNIT_TIMER) { + assert(w->data.unit == u); + assert(w->fd >= 0); + ours = false; fd = w->fd; - } else { + } else if (w->type == WATCH_INVALID) { + ours = true; if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) return -errno; - } + } else + assert_not_reached("Invalid watch type"); zero(its); @@ -1185,8 +1239,8 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { goto fail; } + w->type = WATCH_UNIT_TIMER; w->fd = fd; - w->type = WATCH_TIMER; w->data.unit = u; return 0; @@ -1205,7 +1259,9 @@ void unit_unwatch_timer(Unit *u, Watch *w) { if (w->type == WATCH_INVALID) return; - assert(w->type == WATCH_TIMER && w->data.unit == u); + assert(w->type == WATCH_UNIT_TIMER); + assert(w->data.unit == u); + assert(w->fd >= 0); assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); close_nointr_nofail(w->fd); @@ -1255,6 +1311,7 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen [UNIT_CONFLICTS] = UNIT_CONFLICTS, [UNIT_BEFORE] = UNIT_AFTER, [UNIT_AFTER] = UNIT_BEFORE, + [UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID, [UNIT_REFERENCES] = UNIT_REFERENCED_BY, [UNIT_REFERENCED_BY] = UNIT_REFERENCES }; @@ -1262,7 +1319,6 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen assert(u); assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX); - assert(inverse_table[d] != _UNIT_DEPENDENCY_INVALID); assert(other); /* We won't allow dependencies on ourselves. We will not @@ -1278,10 +1334,13 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen return -EINVAL; } - if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0 || - (r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) + if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) return r; + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) + if ((r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) + return r; + if (add_reference) if ((r = set_ensure_allocated(&u->meta.dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 || (r = set_ensure_allocated(&other->meta.dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0) @@ -1290,10 +1349,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen if ((q = set_put(u->meta.dependencies[d], other)) < 0) return q; - if ((v = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) { - r = v; - goto fail; - } + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) + if ((v = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) { + r = v; + goto fail; + } if (add_reference) { if ((w = set_put(u->meta.dependencies[UNIT_REFERENCES], other)) < 0) { @@ -1321,6 +1381,20 @@ fail: return r; } +int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) { + int r; + + assert(u); + + if ((r = unit_add_dependency(u, d, other, add_reference)) < 0) + return r; + + if ((r = unit_add_dependency(u, e, other, add_reference)) < 0) + return r; + + return 0; +} + static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) { char *s; @@ -1365,7 +1439,7 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con if (!(name = resolve_template(u, name, path, &s))) return -ENOMEM; - if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0) + if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) goto finish; r = unit_add_dependency(u, d, other, add_reference); @@ -1375,6 +1449,27 @@ finish: return r; } +int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { + Unit *other; + int r; + char *s; + + assert(u); + assert(name || path); + + if (!(name = resolve_template(u, name, path, &s))) + return -ENOMEM; + + if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) + goto finish; + + r = unit_add_two_dependencies(u, d, e, other, add_reference); + +finish: + free(s); + return r; +} + int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { Unit *other; int r; @@ -1386,7 +1481,7 @@ int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *n if (!(name = resolve_template(u, name, path, &s))) return -ENOMEM; - if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0) + if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) goto finish; r = unit_add_dependency(other, d, u, add_reference); @@ -1396,6 +1491,28 @@ finish: return r; } +int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { + Unit *other; + int r; + char *s; + + assert(u); + assert(name || path); + + if (!(name = resolve_template(u, name, path, &s))) + return -ENOMEM; + + if ((r = manager_load_unit(u->meta.manager, name, path, NULL, &other)) < 0) + goto finish; + + if ((r = unit_add_two_dependencies(other, d, e, u, add_reference)) < 0) + goto finish; + +finish: + free(s); + return r; +} + int set_unit_path(const char *p) { char *cwd, *c; int r; @@ -1433,12 +1550,9 @@ char *unit_dbus_path(Unit *u) { if (!(e = bus_path_escape(u->meta.id))) return NULL; - if (asprintf(&p, "/org/freedesktop/systemd1/unit/%s", e) < 0) { - free(e); - return NULL; - } - + p = strappend("/org/freedesktop/systemd1/unit/", e); free(e); + return p; } @@ -1448,8 +1562,13 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { assert(u); assert(b); + assert(b->path); + if (!b->controller) + if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER))) + return -ENOMEM; + /* Ensure this hasn't been added yet */ assert(!b->unit); @@ -1488,7 +1607,6 @@ static char *default_cgroup_path(Unit *u) { } int unit_add_cgroup_from_text(Unit *u, const char *name) { - size_t n; char *controller = NULL, *path = NULL; CGroupBonding *b = NULL; int r; @@ -1496,38 +1614,20 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { assert(u); assert(name); - /* Detect controller name */ - n = strcspn(name, ":"); - - if (name[n] == 0 || - (name[n] == ':' && name[n+1] == 0)) { - - /* Only controller name, no path? */ - - if (!(path = default_cgroup_path(u))) - return -ENOMEM; - - } else { - const char *p; - - /* Controller name, and path. */ - p = name+n+1; + if ((r = cg_split_spec(name, &controller, &path)) < 0) + return r; - if (!path_is_absolute(p)) - return -EINVAL; + if (!path) + path = default_cgroup_path(u); - if (!(path = strdup(p))) - return -ENOMEM; - } + if (!controller) + controller = strdup(SYSTEMD_CGROUP_CONTROLLER); - if (n > 0) - controller = strndup(name, n); - else - controller = strdup(u->meta.manager->cgroup_controller); + if (!path || !controller) { + free(path); + free(controller); - if (!controller) { - r = -ENOMEM; - goto fail; + return -ENOMEM; } if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) { @@ -1572,9 +1672,6 @@ int unit_add_default_cgroup(Unit *u) { if (!(b = new0(CGroupBonding, 1))) return -ENOMEM; - if (!(b->controller = strdup(u->meta.manager->cgroup_controller))) - goto fail; - if (!(b->path = default_cgroup_path(u))) goto fail; @@ -1597,7 +1694,7 @@ fail: CGroupBonding* unit_get_default_cgroup(Unit *u) { assert(u); - return cgroup_bonding_find_list(u->meta.cgroup_bondings, u->meta.manager->cgroup_controller); + return cgroup_bonding_find_list(u->meta.cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER); } int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { @@ -1613,7 +1710,7 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { assert(!unit_has_name(u, t)); - r = manager_load_unit(u->meta.manager, t, NULL, _found); + r = manager_load_unit(u->meta.manager, t, NULL, NULL, _found); free(t); assert(r < 0 || *_found != u); @@ -1898,16 +1995,13 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { if (!(e = unit_name_build_escape(what+1, NULL, ".device"))) return -ENOMEM; - r = manager_load_unit(u->meta.manager, e, NULL, &device); + r = manager_load_unit(u->meta.manager, e, NULL, NULL, &device); free(e); if (r < 0) return r; - if ((r = unit_add_dependency(u, UNIT_AFTER, device, true)) < 0) - return r; - - if ((r = unit_add_dependency(u, UNIT_REQUIRES, device, true)) < 0) + if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, device, true)) < 0) return r; if (wants) @@ -1927,7 +2021,7 @@ int unit_coldplug(Unit *u) { return r; if (u->meta.deserialized_job >= 0) { - if ((r = manager_add_job(u->meta.manager, u->meta.deserialized_job, u, JOB_FAIL, false, NULL)) < 0) + if ((r = manager_add_job(u->meta.manager, u->meta.deserialized_job, u, JOB_FAIL, false, NULL, NULL)) < 0) return r; u->meta.deserialized_job = _JOB_TYPE_INVALID; @@ -1936,6 +2030,47 @@ int unit_coldplug(Unit *u) { return 0; } +void unit_status_printf(Unit *u, const char *format, ...) { + va_list ap; + + assert(u); + assert(format); + + if (!UNIT_VTABLE(u)->show_status) + return; + + if (u->meta.manager->running_as != MANAGER_SYSTEM) + return; + + if (!u->meta.manager->show_status) + return; + + if (!manager_is_booting_or_shutting_down(u->meta.manager)) + return; + + va_start(ap, format); + status_vprintf(format, ap); + va_end(ap); +} + +bool unit_need_daemon_reload(Unit *u) { + struct stat st; + + assert(u); + + if (!u->meta.fragment_path) + return false; + + zero(st); + if (stat(u->meta.fragment_path, &st) < 0) + /* What, cannot access this anymore? */ + return true; + + return + u->meta.fragment_mtime && + timespec_load(&st.st_mtim) != u->meta.fragment_mtime; +} + static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "service", [UNIT_TIMER] = "timer", @@ -1961,7 +2096,9 @@ DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", + [UNIT_RELOADING] = "reloading", [UNIT_INACTIVE] = "inactive", + [UNIT_MAINTENANCE] = "maintenance", [UNIT_ACTIVATING] = "activating", [UNIT_DEACTIVATING] = "deactivating" }; @@ -1981,7 +2118,8 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_BEFORE] = "Before", [UNIT_AFTER] = "After", [UNIT_REFERENCES] = "References", - [UNIT_REFERENCED_BY] = "ReferencedBy" + [UNIT_REFERENCED_BY] = "ReferencedBy", + [UNIT_ON_FAILURE] = "OnFailure" }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);