X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Funit.c;h=5a451c57c2a038c1cc297c7d3658f96527ab206e;hp=e46182adad420a8ffb1144fc2cc80da3c8f0411c;hb=3e24da51b9eebc950d3d0d39f87cfcf406b281dd;hpb=45fb0699c45d2e042e04a53e3ea00501e3f65f59 diff --git a/src/unit.c b/src/unit.c index e46182ada..5a451c57c 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. @@ -41,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, @@ -235,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; @@ -384,7 +388,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); } @@ -436,6 +440,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; @@ -475,13 +480,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 */ @@ -587,7 +592,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); @@ -624,9 +631,15 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { 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)); + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { Unit *other; @@ -638,12 +651,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)); @@ -658,6 +673,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); @@ -708,12 +726,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; } @@ -743,6 +793,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)); @@ -751,10 +806,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; } @@ -804,6 +860,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. @@ -815,7 +878,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) @@ -845,10 +908,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); @@ -926,6 +989,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) { @@ -972,14 +1039,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)) @@ -987,10 +1053,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); @@ -1006,7 +1069,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. */ @@ -1020,7 +1082,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; @@ -1033,7 +1097,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); } } @@ -1043,7 +1109,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; @@ -1066,20 +1132,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 @@ -1087,17 +1162,14 @@ 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_TARGET) - /* A target got activated, maybe this is a runlevel? */ - manager_write_utmp_runlevel(u->meta.manager, u); + 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, 1); + u->meta.in_audit = true; + } - } else if (!UNIT_IS_ACTIVE_OR_RELOADING(ns)) { + } else { if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) /* The syslog daemon might just have @@ -1107,6 +1179,25 @@ 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 @@ -1182,18 +1273,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); @@ -1224,8 +1320,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; @@ -1244,7 +1340,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); @@ -1291,9 +1389,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 }; @@ -1301,7 +1401,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 @@ -1317,10 +1416,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) @@ -1329,10 +1431,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) { @@ -1526,6 +1629,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; @@ -1926,6 +2032,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { return -errno; } + char_array_0(line); l = strstrip(line); /* End marker */ @@ -2050,24 +2157,40 @@ bool unit_need_daemon_reload(Unit *u) { 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", - [UNIT_SOCKET] = "socket", - [UNIT_TARGET] = "target", - [UNIT_DEVICE] = "device", - [UNIT_MOUNT] = "mount", - [UNIT_AUTOMOUNT] = "automount", - [UNIT_SNAPSHOT] = "snapshot", - [UNIT_SWAP] = "swap" -}; +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; +} -DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); +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; +} static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { [UNIT_STUB] = "stub", [UNIT_LOADED] = "loaded", - [UNIT_FAILED] = "failed", + [UNIT_ERROR] = "error", [UNIT_MERGED] = "merged" }; @@ -2077,7 +2200,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" }; @@ -2094,10 +2217,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);