-/*-*- Mode: C; c-basic-offset: 8 -*-*/
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
#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,
if (UNIT_VTABLE(u)->no_gc)
return true;
+ if (u->meta.no_gc)
+ return true;
+
if (u->meta.job)
return true;
/* 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);
}
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 */
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);
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;
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));
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);
return 0;
}
-/* Common implementation for multiple backends */
-int unit_load_nop(Unit *u) {
- assert(u);
-
- if (u->meta.load_state == UNIT_STUB)
- u->meta.load_state = UNIT_LOADED;
-
- return 0;
-}
-
int unit_load(Unit *u) {
int r;
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;
}
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.
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)
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);
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) {
* 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))
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);
* 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. */
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;
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);
}
}
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;
* 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
* 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
/* 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
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);
goto fail;
}
+ w->type = WATCH_UNIT_TIMER;
w->fd = fd;
- w->type = WATCH_TIMER;
w->data.unit = u;
return 0;
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);
[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
};
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
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)
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) {
assert(u);
+ if (!u->meta.id)
+ return NULL;
+
if (!(e = bus_path_escape(u->meta.id)))
return NULL;
return -errno;
}
+ char_array_0(line);
l = strstrip(line);
/* End marker */
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);
-DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
+ 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;
+}
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"
};
[UNIT_ACTIVE] = "active",
[UNIT_RELOADING] = "reloading",
[UNIT_INACTIVE] = "inactive",
- [UNIT_MAINTENANCE] = "maintenance",
+ [UNIT_FAILED] = "failed",
[UNIT_ACTIVATING] = "activating",
[UNIT_DEACTIVATING] = "deactivating"
};
[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);