X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Funit.c;h=07978134de959889f213740bd55aaeffd80bb939;hp=f8be8b26a148bd9418348ad23ce48d7160ed5b9c;hb=52661efd21608dc7e0ac26b714a9254ed6180ddb;hpb=276c3e78ceae3727c797820ba80c762acc727f4e diff --git a/src/unit.c b/src/unit.c index f8be8b26a..07978134d 100644 --- a/src/unit.c +++ b/src/unit.c @@ -1,4 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. @@ -27,6 +27,7 @@ #include #include #include +#include #include "set.h" #include "unit.h" @@ -40,6 +41,7 @@ #include "dbus-unit.h" #include "special.h" #include "cgroup-util.h" +#include "missing.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -101,7 +103,7 @@ int unit_add_name(Unit *u, const char *text) { if (!s) return -ENOMEM; - if (!unit_name_is_valid(s)) { + if (!unit_name_is_valid(s, false)) { r = -EINVAL; goto fail; } @@ -234,6 +236,9 @@ bool unit_check_gc(Unit *u) { if (UNIT_VTABLE(u)->no_gc) return true; + if (u->meta.no_gc) + return true; + if (u->meta.job) return true; @@ -370,8 +375,9 @@ void unit_free(Unit *u) { set_free_free(u->meta.names); - free(u->meta.instance); + condition_free_list(u->meta.conditions); + free(u->meta.instance); free(u); } @@ -383,7 +389,7 @@ UnitActiveState unit_active_state(Unit *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. */ + * shortcut failed loading to UNIT_INACTIVE_FAILED. */ return UNIT_VTABLE(u)->active_state(u); } @@ -435,6 +441,7 @@ static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) { assert(other); assert(d < _UNIT_DEPENDENCY_MAX); + /* Fix backwards pointers */ SET_FOREACH(back, other->meta.dependencies[d], i) { UnitDependency k; @@ -474,13 +481,13 @@ int unit_merge(Unit *u, Unit *other) { return -EINVAL; if (other->meta.load_state != UNIT_STUB && - other->meta.load_state != UNIT_FAILED) + other->meta.load_state != UNIT_ERROR) return -EEXIST; if (other->meta.job) return -EEXIST; - if (!UNIT_IS_INACTIVE_OR_MAINTENANCE(unit_active_state(other))) + if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) return -EEXIST; /* Merge names */ @@ -572,7 +579,7 @@ const char *unit_description(Unit *u) { if (u->meta.description) return u->meta.description; - return u->meta.id; + return strna(u->meta.id); } void unit_dump(Unit *u, FILE *f, const char *prefix) { @@ -586,7 +593,9 @@ 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]; + Unit *following; assert(u); assert(u->meta.type >= 0); @@ -606,7 +615,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tActive Enter Timestamp: %s\n" "%s\tActive Exit Timestamp: %s\n" "%s\tInactive Enter Timestamp: %s\n" - "%s\tGC Check Good: %s\n", + "%s\tGC Check Good: %s\n" + "%s\tNeed Daemon Reload: %s\n", prefix, u->meta.id, prefix, unit_description(u), prefix, strna(u->meta.instance), @@ -616,14 +626,23 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->meta.active_enter_timestamp.realtime)), 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(unit_check_gc(u)), + prefix, yes_no(unit_need_daemon_reload(u))); SET_FOREACH(t, u->meta.names, i) fprintf(f, "%s\tName: %s\n", prefix, t); + if ((following = unit_following(u))) + fprintf(f, "%s\tFollowing: %s\n", prefix, following->meta.id); + 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)); + + condition_dump_list(u->meta.conditions, f, prefix); + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { Unit *other; @@ -635,12 +654,14 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%s\tRecursive Stop: %s\n" "%s\tStopWhenUnneeded: %s\n" - "%s\tOnlyByDependency: %s\n" + "%s\tRefuseManualStart: %s\n" + "%s\tRefuseManualStop: %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.only_by_dependency), + prefix, yes_no(u->meta.refuse_manual_start), + prefix, yes_no(u->meta.refuse_manual_stop), prefix, yes_no(u->meta.default_dependencies), prefix, yes_no(u->meta.ignore_dependency_failure)); @@ -655,6 +676,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%s\tMerged into: %s\n", prefix, u->meta.merged_into->meta.id); + else if (u->meta.load_state == UNIT_ERROR) + fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->meta.load_error)); + if (u->meta.job) job_dump(u->meta.job, f, prefix2); @@ -705,12 +729,44 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { return 0; } -/* Common implementation for multiple backends */ -int unit_load_nop(Unit *u) { +int unit_add_default_target_dependency(Unit *u, Unit *target) { assert(u); + assert(target); - if (u->meta.load_state == UNIT_STUB) - u->meta.load_state = UNIT_LOADED; + if (target->meta.type != UNIT_TARGET) + return 0; + + /* Only add the dependency if boths units are loaded, so that + * that loop check below is reliable */ + if (u->meta.load_state != UNIT_LOADED || + target->meta.load_state != UNIT_LOADED) + return 0; + + /* Don't create loops */ + if (set_get(target->meta.dependencies[UNIT_BEFORE], u)) + return 0; + + return unit_add_dependency(target, UNIT_AFTER, u, true); +} + +static int unit_add_default_dependencies(Unit *u) { + Unit *target; + Iterator i; + int r; + + assert(u); + + SET_FOREACH(target, u->meta.dependencies[UNIT_REQUIRED_BY], i) + if ((r = unit_add_default_target_dependency(u, target)) < 0) + return r; + + SET_FOREACH(target, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) + if ((r = unit_add_default_target_dependency(u, target)) < 0) + return r; + + SET_FOREACH(target, u->meta.dependencies[UNIT_WANTED_BY], i) + if ((r = unit_add_default_target_dependency(u, target)) < 0) + return r; return 0; } @@ -740,6 +796,11 @@ int unit_load(Unit *u) { goto fail; } + if (u->meta.load_state == UNIT_LOADED && + u->meta.default_dependencies) + if ((r = unit_add_default_dependencies(u)) < 0) + goto fail; + assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into); unit_add_to_dbus_queue(unit_follow_merge(u)); @@ -748,10 +809,11 @@ int unit_load(Unit *u) { return 0; fail: - u->meta.load_state = UNIT_FAILED; + u->meta.load_state = UNIT_ERROR; + u->meta.load_error = r; unit_add_to_dbus_queue(u); - log_notice("Failed to load configuration for %s: %s", u->meta.id, strerror(-r)); + log_debug("Failed to load configuration for %s: %s", u->meta.id, strerror(-r)); return r; } @@ -782,6 +844,12 @@ int unit_start(Unit *u) { if (!UNIT_VTABLE(u)->start) return -EBADR; + /* If the conditions failed, don't do anything at all */ + if (!condition_test_list(u->meta.conditions)) { + log_debug("Starting of %s requested but condition failed. Ignoring.", u->meta.id); + return -EALREADY; + } + /* We don't suppress calls to ->start() here when we are * already starting, to allow this request to be used as a * "hurry up" call, for example when the unit is in some "auto @@ -801,6 +869,13 @@ bool unit_can_start(Unit *u) { return !!UNIT_VTABLE(u)->start; } +bool unit_can_isolate(Unit *u) { + assert(u); + + return unit_can_start(u) && + u->meta.allow_isolate; +} + /* Errors: * -EBADR: This unit type does not support stopping. * -EALREADY: Unit is already stopped. @@ -812,7 +887,7 @@ int unit_stop(Unit *u) { assert(u); state = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_MAINTENANCE(state)) + if (UNIT_IS_INACTIVE_OR_FAILED(state)) return -EALREADY; if (!UNIT_VTABLE(u)->stop) @@ -842,10 +917,10 @@ int unit_reload(Unit *u) { return -EBADR; state = unit_active_state(u); - if (unit_active_state(u) == UNIT_RELOADING) + if (state == UNIT_RELOADING) return -EALREADY; - if (unit_active_state(u) != UNIT_ACTIVE) + if (state != UNIT_ACTIVE) return -ENOEXEC; unit_add_to_dbus_queue(u); @@ -864,7 +939,7 @@ bool unit_can_reload(Unit *u) { return UNIT_VTABLE(u)->can_reload(u); } -static void unit_check_uneeded(Unit *u) { +static void unit_check_unneeded(Unit *u) { Iterator i; Unit *other; @@ -923,6 +998,10 @@ static void retroactively_start_dependencies(Unit *u) { 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, NULL); + + SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTED_BY], i) + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) + manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); } static void retroactively_stop_dependencies(Unit *u) { @@ -942,19 +1021,19 @@ static void retroactively_stop_dependencies(Unit *u) { /* Garbage collect services that might not be needed anymore, if enabled */ SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_uneeded(other); + unit_check_unneeded(other); SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_uneeded(other); + unit_check_unneeded(other); SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_uneeded(other); + unit_check_unneeded(other); SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_uneeded(other); + unit_check_unneeded(other); SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_uneeded(other); + unit_check_unneeded(other); } void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { @@ -969,14 +1048,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { * 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 - * this function will be called too and the utmp code below - * relies on that! */ + * this function will be called too! */ dual_timestamp_get(&ts); - if (UNIT_IS_INACTIVE_OR_MAINTENANCE(os) && !UNIT_IS_INACTIVE_OR_MAINTENANCE(ns)) + if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns)) u->meta.inactive_exit_timestamp = ts; - else if (!UNIT_IS_INACTIVE_OR_MAINTENANCE(os) && UNIT_IS_INACTIVE_OR_MAINTENANCE(ns)) + else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns)) u->meta.inactive_enter_timestamp = ts; if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) @@ -984,10 +1062,7 @@ 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 (ns != os && ns == UNIT_MAINTENANCE) - log_notice("Unit %s entered maintenance state.", u->meta.id); - - if (UNIT_IS_INACTIVE_OR_MAINTENANCE(ns)) + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) cgroup_bonding_trim_list(u->meta.cgroup_bondings, true); timer_unit_notify(u, ns); @@ -1003,7 +1078,6 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { * failed previously due to EAGAIN. */ job_add_to_run_queue(u->meta.job); - /* Let's check whether this state change constitutes a * finished job, or maybe cotradicts a running job and * hence needs to invalidate jobs. */ @@ -1017,7 +1091,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState 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); + + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + job_finish_and_invalidate(u->meta.job, ns != UNIT_FAILED); } break; @@ -1030,7 +1106,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { job_finish_and_invalidate(u->meta.job, true); else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { unexpected = true; - job_finish_and_invalidate(u->meta.job, false); + + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + job_finish_and_invalidate(u->meta.job, ns != UNIT_FAILED); } } @@ -1040,7 +1118,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { case JOB_RESTART: case JOB_TRY_RESTART: - if (ns == UNIT_INACTIVE || ns == UNIT_MAINTENANCE) + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) job_finish_and_invalidate(u->meta.job, true); else if (u->meta.job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { unexpected = true; @@ -1063,20 +1141,29 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { * something is already activated. */ if (unexpected && u->meta.manager->n_deserializing <= 0) { - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) + if (UNIT_IS_INACTIVE_OR_FAILED(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_FAILED) { + 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 failed state.", u->meta.id); + } + /* Some names are special */ if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { - if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) { + 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(u->meta.manager); - } if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) /* The syslog daemon just might have become @@ -1084,17 +1171,17 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { * we aren't yet connected. */ log_open(); - if (u->meta.type == UNIT_MOUNT) - /* Another directory became available, let's - * check if that is enough to write our utmp - * entry. */ - manager_write_utmp_reboot(u->meta.manager); + if (u->meta.type == UNIT_SERVICE && + !UNIT_IS_ACTIVE_OR_RELOADING(os)) { + /* Write audit record if we have just finished starting up */ + manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_START, true); + u->meta.in_audit = true; + } - if (u->meta.type == UNIT_TARGET) - /* A target got activated, maybe this is a runlevel? */ - manager_write_utmp_runlevel(u->meta.manager, u); + if (!UNIT_IS_ACTIVE_OR_RELOADING(os)) + manager_send_unit_plymouth(u->meta.manager, u); - } else if (!UNIT_IS_ACTIVE_OR_RELOADING(ns)) { + } else { if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) /* The syslog daemon might just have @@ -1104,11 +1191,30 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { /* We don't care about D-Bus here, since we'll get an * asynchronous notification for it anyway. */ + + if (u->meta.type == UNIT_SERVICE && + UNIT_IS_INACTIVE_OR_FAILED(ns) && + !UNIT_IS_INACTIVE_OR_FAILED(os)) { + + /* Hmm, if there was no start record written + * write it now, so that we always have a nice + * pair */ + if (!u->meta.in_audit) { + manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); + + if (ns == UNIT_INACTIVE) + manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_STOP, true); + } else + /* Write audit record if we have just finished shutting down */ + manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); + + u->meta.in_audit = false; + } } /* Maybe we finished startup and are now ready for being * stopped because unneeded? */ - unit_check_uneeded(u); + unit_check_unneeded(u); unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); @@ -1179,18 +1285,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); @@ -1221,8 +1332,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; @@ -1241,7 +1352,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); @@ -1288,9 +1401,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID, [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID, [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID, - [UNIT_CONFLICTS] = UNIT_CONFLICTS, + [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, + [UNIT_CONFLICTED_BY] = 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 }; @@ -1298,9 +1413,11 @@ 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); + u = unit_follow_merge(u); + other = unit_follow_merge(other); + /* We won't allow dependencies on ourselves. We will not * consider them an error however. */ if (u == other) @@ -1314,10 +1431,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) @@ -1326,10 +1446,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) { @@ -1523,6 +1644,9 @@ char *unit_dbus_path(Unit *u) { assert(u); + if (!u->meta.id) + return NULL; + if (!(e = bus_path_escape(u->meta.id))) return NULL; @@ -1923,6 +2047,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { return -errno; } + char_array_0(line); l = strstrip(line); /* End marker */ @@ -2029,25 +2154,99 @@ void unit_status_printf(Unit *u, const char *format, ...) { va_end(ap); } -static const char* const unit_type_table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "service", - [UNIT_TIMER] = "timer", - [UNIT_SOCKET] = "socket", - [UNIT_TARGET] = "target", - [UNIT_DEVICE] = "device", - [UNIT_MOUNT] = "mount", - [UNIT_AUTOMOUNT] = "automount", - [UNIT_SNAPSHOT] = "snapshot", - [UNIT_SWAP] = "swap" -}; +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; +} + +void unit_reset_failed(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->reset_failed) + UNIT_VTABLE(u)->reset_failed(u); +} + +Unit *unit_following(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->following) + return UNIT_VTABLE(u)->following(u); + + return NULL; +} + +bool unit_pending_inactive(Unit *u) { + assert(u); + + /* Returns true if the unit is inactive or going down */ + + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) + return true; + + if (u->meta.job && u->meta.job->type == JOB_STOP) + return true; + + return false; +} + +bool unit_pending_active(Unit *u) { + assert(u); + + /* Returns true if the unit is inactive or going down */ + + if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) + return true; + + if (u->meta.job && + (u->meta.job->type == JOB_START || + u->meta.job->type == JOB_RELOAD_OR_START || + u->meta.job->type == JOB_RESTART)) + return true; -DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); + return false; +} + +UnitType unit_name_to_type(const char *n) { + UnitType t; + + assert(n); + + for (t = 0; t < _UNIT_TYPE_MAX; t++) + if (endswith(n, unit_vtable[t]->suffix)) + return t; + + return _UNIT_TYPE_INVALID; +} + +bool unit_name_is_valid(const char *n, bool template_ok) { + UnitType t; + + t = unit_name_to_type(n); + if (t < 0 || t >= _UNIT_TYPE_MAX) + return false; + + return unit_name_is_valid_no_type(n, template_ok); +} static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { [UNIT_STUB] = "stub", [UNIT_LOADED] = "loaded", - [UNIT_FAILED] = "failed", - [UNIT_MERGED] = "merged" + [UNIT_ERROR] = "error", + [UNIT_MERGED] = "merged", + [UNIT_MASKED] = "masked" }; DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); @@ -2056,7 +2255,7 @@ 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_FAILED] = "failed", [UNIT_ACTIVATING] = "activating", [UNIT_DEACTIVATING] = "deactivating" }; @@ -2073,10 +2272,12 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable", [UNIT_WANTED_BY] = "WantedBy", [UNIT_CONFLICTS] = "Conflicts", + [UNIT_CONFLICTED_BY] = "ConflictedBy", [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);