Notifications from /proc/self/mountinfo are async, so if we stop a service (and while doing so get rid of the credentials mount point of it), then it will take a while until the notification reaches us and we actually scan the table again. In particular as we nowadays ratelimit notifications on the table, since it's so inefficient. And as I learnt the ratelimiting is actually quite regularly hit during shutdown, where a flurry of umount events are genreated. Hence, let's check if a mount point is actually a mountpoint before trying to unmount it. And if it isn't let's wait for the notification to come in. (This race might be triggred not just by us on ourselves btw: there are other daemons that unmount stuff when stopping where the race also exists, but might simply be harder to trigger: if during service shutdown these services remove some mount then they might collide with us doing the same. After all, we have the rule to unmount everything mounted automatically for you during shutdown.) In the long run we should also start making us of this when it becomes available: https://github.com/util-linux/util-linux/issues/2132 With that we can make issues like this go away entirely from our side of things at least. Fixes: #25527
331 lines
12 KiB
C
331 lines
12 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include "alloc-util.h"
|
|
#include "bus-label.h"
|
|
#include "string-table.h"
|
|
#include "unit-def.h"
|
|
#include "unit-name.h"
|
|
|
|
char *unit_dbus_path_from_name(const char *name) {
|
|
_cleanup_free_ char *e = NULL;
|
|
|
|
assert(name);
|
|
|
|
e = bus_label_escape(name);
|
|
if (!e)
|
|
return NULL;
|
|
|
|
return strjoin("/org/freedesktop/systemd1/unit/", e);
|
|
}
|
|
|
|
int unit_name_from_dbus_path(const char *path, char **name) {
|
|
const char *e;
|
|
char *n;
|
|
|
|
e = startswith(path, "/org/freedesktop/systemd1/unit/");
|
|
if (!e)
|
|
return -EINVAL;
|
|
|
|
n = bus_label_unescape(e);
|
|
if (!n)
|
|
return -ENOMEM;
|
|
|
|
*name = n;
|
|
return 0;
|
|
}
|
|
|
|
const char* unit_dbus_interface_from_type(UnitType t) {
|
|
|
|
static const char *const table[_UNIT_TYPE_MAX] = {
|
|
[UNIT_SERVICE] = "org.freedesktop.systemd1.Service",
|
|
[UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
|
|
[UNIT_TARGET] = "org.freedesktop.systemd1.Target",
|
|
[UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
|
|
[UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
|
|
[UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
|
|
[UNIT_SWAP] = "org.freedesktop.systemd1.Swap",
|
|
[UNIT_TIMER] = "org.freedesktop.systemd1.Timer",
|
|
[UNIT_PATH] = "org.freedesktop.systemd1.Path",
|
|
[UNIT_SLICE] = "org.freedesktop.systemd1.Slice",
|
|
[UNIT_SCOPE] = "org.freedesktop.systemd1.Scope",
|
|
};
|
|
|
|
if (t < 0)
|
|
return NULL;
|
|
if (t >= _UNIT_TYPE_MAX)
|
|
return NULL;
|
|
|
|
return table[t];
|
|
}
|
|
|
|
const char *unit_dbus_interface_from_name(const char *name) {
|
|
UnitType t;
|
|
|
|
t = unit_name_to_type(name);
|
|
if (t < 0)
|
|
return NULL;
|
|
|
|
return unit_dbus_interface_from_type(t);
|
|
}
|
|
|
|
static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
|
|
[UNIT_SERVICE] = "service",
|
|
[UNIT_SOCKET] = "socket",
|
|
[UNIT_TARGET] = "target",
|
|
[UNIT_DEVICE] = "device",
|
|
[UNIT_MOUNT] = "mount",
|
|
[UNIT_AUTOMOUNT] = "automount",
|
|
[UNIT_SWAP] = "swap",
|
|
[UNIT_TIMER] = "timer",
|
|
[UNIT_PATH] = "path",
|
|
[UNIT_SLICE] = "slice",
|
|
[UNIT_SCOPE] = "scope",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
|
|
|
|
static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
|
|
[UNIT_STUB] = "stub",
|
|
[UNIT_LOADED] = "loaded",
|
|
[UNIT_NOT_FOUND] = "not-found",
|
|
[UNIT_BAD_SETTING] = "bad-setting",
|
|
[UNIT_ERROR] = "error",
|
|
[UNIT_MERGED] = "merged",
|
|
[UNIT_MASKED] = "masked"
|
|
};
|
|
|
|
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_FAILED] = "failed",
|
|
[UNIT_ACTIVATING] = "activating",
|
|
[UNIT_DEACTIVATING] = "deactivating",
|
|
[UNIT_MAINTENANCE] = "maintenance",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
|
|
|
|
static const char* const freezer_state_table[_FREEZER_STATE_MAX] = {
|
|
[FREEZER_RUNNING] = "running",
|
|
[FREEZER_FREEZING] = "freezing",
|
|
[FREEZER_FROZEN] = "frozen",
|
|
[FREEZER_THAWING] = "thawing",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
|
|
|
|
static const char* const unit_marker_table[_UNIT_MARKER_MAX] = {
|
|
[UNIT_MARKER_NEEDS_RELOAD] = "needs-reload",
|
|
[UNIT_MARKER_NEEDS_RESTART] = "needs-restart",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(unit_marker, UnitMarker);
|
|
|
|
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
|
|
[AUTOMOUNT_DEAD] = "dead",
|
|
[AUTOMOUNT_WAITING] = "waiting",
|
|
[AUTOMOUNT_RUNNING] = "running",
|
|
[AUTOMOUNT_FAILED] = "failed"
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
|
|
|
|
static const char* const device_state_table[_DEVICE_STATE_MAX] = {
|
|
[DEVICE_DEAD] = "dead",
|
|
[DEVICE_TENTATIVE] = "tentative",
|
|
[DEVICE_PLUGGED] = "plugged",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
|
|
|
|
static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
|
|
[MOUNT_DEAD] = "dead",
|
|
[MOUNT_MOUNTING] = "mounting",
|
|
[MOUNT_MOUNTING_DONE] = "mounting-done",
|
|
[MOUNT_MOUNTED] = "mounted",
|
|
[MOUNT_REMOUNTING] = "remounting",
|
|
[MOUNT_UNMOUNTING] = "unmounting",
|
|
[MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
|
|
[MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
|
|
[MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
|
|
[MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
|
|
[MOUNT_UNMOUNTING_CATCHUP] = "unmounting-catchup",
|
|
[MOUNT_FAILED] = "failed",
|
|
[MOUNT_CLEANING] = "cleaning",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
|
|
|
|
static const char* const path_state_table[_PATH_STATE_MAX] = {
|
|
[PATH_DEAD] = "dead",
|
|
[PATH_WAITING] = "waiting",
|
|
[PATH_RUNNING] = "running",
|
|
[PATH_FAILED] = "failed"
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
|
|
|
|
static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
|
|
[SCOPE_DEAD] = "dead",
|
|
[SCOPE_START_CHOWN] = "start-chown",
|
|
[SCOPE_RUNNING] = "running",
|
|
[SCOPE_ABANDONED] = "abandoned",
|
|
[SCOPE_STOP_SIGTERM] = "stop-sigterm",
|
|
[SCOPE_STOP_SIGKILL] = "stop-sigkill",
|
|
[SCOPE_FAILED] = "failed",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
|
|
|
|
static const char* const service_state_table[_SERVICE_STATE_MAX] = {
|
|
[SERVICE_DEAD] = "dead",
|
|
[SERVICE_CONDITION] = "condition",
|
|
[SERVICE_START_PRE] = "start-pre",
|
|
[SERVICE_START] = "start",
|
|
[SERVICE_START_POST] = "start-post",
|
|
[SERVICE_RUNNING] = "running",
|
|
[SERVICE_EXITED] = "exited",
|
|
[SERVICE_RELOAD] = "reload",
|
|
[SERVICE_RELOAD_SIGNAL] = "reload-signal",
|
|
[SERVICE_RELOAD_NOTIFY] = "reload-notify",
|
|
[SERVICE_STOP] = "stop",
|
|
[SERVICE_STOP_WATCHDOG] = "stop-watchdog",
|
|
[SERVICE_STOP_SIGTERM] = "stop-sigterm",
|
|
[SERVICE_STOP_SIGKILL] = "stop-sigkill",
|
|
[SERVICE_STOP_POST] = "stop-post",
|
|
[SERVICE_FINAL_WATCHDOG] = "final-watchdog",
|
|
[SERVICE_FINAL_SIGTERM] = "final-sigterm",
|
|
[SERVICE_FINAL_SIGKILL] = "final-sigkill",
|
|
[SERVICE_FAILED] = "failed",
|
|
[SERVICE_DEAD_BEFORE_AUTO_RESTART] = "dead-before-auto-restart",
|
|
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = "failed-before-auto-restart",
|
|
[SERVICE_DEAD_RESOURCES_PINNED] = "dead-resources-pinned",
|
|
[SERVICE_AUTO_RESTART] = "auto-restart",
|
|
[SERVICE_CLEANING] = "cleaning",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
|
|
|
|
static const char* const slice_state_table[_SLICE_STATE_MAX] = {
|
|
[SLICE_DEAD] = "dead",
|
|
[SLICE_ACTIVE] = "active"
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
|
|
|
|
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
|
|
[SOCKET_DEAD] = "dead",
|
|
[SOCKET_START_PRE] = "start-pre",
|
|
[SOCKET_START_CHOWN] = "start-chown",
|
|
[SOCKET_START_POST] = "start-post",
|
|
[SOCKET_LISTENING] = "listening",
|
|
[SOCKET_RUNNING] = "running",
|
|
[SOCKET_STOP_PRE] = "stop-pre",
|
|
[SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
|
|
[SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
|
|
[SOCKET_STOP_POST] = "stop-post",
|
|
[SOCKET_FINAL_SIGTERM] = "final-sigterm",
|
|
[SOCKET_FINAL_SIGKILL] = "final-sigkill",
|
|
[SOCKET_FAILED] = "failed",
|
|
[SOCKET_CLEANING] = "cleaning",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
|
|
|
|
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
|
|
[SWAP_DEAD] = "dead",
|
|
[SWAP_ACTIVATING] = "activating",
|
|
[SWAP_ACTIVATING_DONE] = "activating-done",
|
|
[SWAP_ACTIVE] = "active",
|
|
[SWAP_DEACTIVATING] = "deactivating",
|
|
[SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
|
|
[SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
|
|
[SWAP_FAILED] = "failed",
|
|
[SWAP_CLEANING] = "cleaning",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
|
|
|
|
static const char* const target_state_table[_TARGET_STATE_MAX] = {
|
|
[TARGET_DEAD] = "dead",
|
|
[TARGET_ACTIVE] = "active"
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
|
|
|
|
static const char* const timer_state_table[_TIMER_STATE_MAX] = {
|
|
[TIMER_DEAD] = "dead",
|
|
[TIMER_WAITING] = "waiting",
|
|
[TIMER_RUNNING] = "running",
|
|
[TIMER_ELAPSED] = "elapsed",
|
|
[TIMER_FAILED] = "failed"
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
|
|
|
|
static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
|
|
[UNIT_REQUIRES] = "Requires",
|
|
[UNIT_REQUISITE] = "Requisite",
|
|
[UNIT_WANTS] = "Wants",
|
|
[UNIT_BINDS_TO] = "BindsTo",
|
|
[UNIT_PART_OF] = "PartOf",
|
|
[UNIT_UPHOLDS] = "Upholds",
|
|
[UNIT_REQUIRED_BY] = "RequiredBy",
|
|
[UNIT_REQUISITE_OF] = "RequisiteOf",
|
|
[UNIT_WANTED_BY] = "WantedBy",
|
|
[UNIT_BOUND_BY] = "BoundBy",
|
|
[UNIT_UPHELD_BY] = "UpheldBy",
|
|
[UNIT_CONSISTS_OF] = "ConsistsOf",
|
|
[UNIT_CONFLICTS] = "Conflicts",
|
|
[UNIT_CONFLICTED_BY] = "ConflictedBy",
|
|
[UNIT_BEFORE] = "Before",
|
|
[UNIT_AFTER] = "After",
|
|
[UNIT_ON_SUCCESS] = "OnSuccess",
|
|
[UNIT_ON_SUCCESS_OF] = "OnSuccessOf",
|
|
[UNIT_ON_FAILURE] = "OnFailure",
|
|
[UNIT_ON_FAILURE_OF] = "OnFailureOf",
|
|
[UNIT_TRIGGERS] = "Triggers",
|
|
[UNIT_TRIGGERED_BY] = "TriggeredBy",
|
|
[UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
|
|
[UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
|
|
[UNIT_PROPAGATES_STOP_TO] = "PropagatesStopTo",
|
|
[UNIT_STOP_PROPAGATED_FROM] = "StopPropagatedFrom",
|
|
[UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
|
|
[UNIT_REFERENCES] = "References",
|
|
[UNIT_REFERENCED_BY] = "ReferencedBy",
|
|
[UNIT_IN_SLICE] = "InSlice",
|
|
[UNIT_SLICE_OF] = "SliceOf",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
|
|
|
|
static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
|
|
[NOTIFY_NONE] = "none",
|
|
[NOTIFY_MAIN] = "main",
|
|
[NOTIFY_EXEC] = "exec",
|
|
[NOTIFY_ALL] = "all"
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
|
|
|
|
SpecialGlyph unit_active_state_to_glyph(UnitActiveState state) {
|
|
static const SpecialGlyph map[_UNIT_ACTIVE_STATE_MAX] = {
|
|
[UNIT_ACTIVE] = SPECIAL_GLYPH_BLACK_CIRCLE,
|
|
[UNIT_RELOADING] = SPECIAL_GLYPH_CIRCLE_ARROW,
|
|
[UNIT_INACTIVE] = SPECIAL_GLYPH_WHITE_CIRCLE,
|
|
[UNIT_FAILED] = SPECIAL_GLYPH_MULTIPLICATION_SIGN,
|
|
[UNIT_ACTIVATING] = SPECIAL_GLYPH_BLACK_CIRCLE,
|
|
[UNIT_DEACTIVATING] = SPECIAL_GLYPH_BLACK_CIRCLE,
|
|
[UNIT_MAINTENANCE] = SPECIAL_GLYPH_WHITE_CIRCLE,
|
|
};
|
|
|
|
if (state < 0)
|
|
return _SPECIAL_GLYPH_INVALID;
|
|
|
|
assert(state < _UNIT_ACTIVE_STATE_MAX);
|
|
return map[state];
|
|
}
|