diff --git a/NEWS b/NEWS
index a47f0f37a12..f0ddeb44321 100644
--- a/NEWS
+++ b/NEWS
@@ -64,6 +64,14 @@ CHANGES WITH 260 in spe:
directory or not, we make use of it unconditionally and have dropped
support for the old way using protofiles.
+ * The org.systemd.login1.Manager D-Bus interface has a minor API break.
+ The `CanPowerOff()`, `CanReboot()`, `CanSuspend()`, etc. family of
+ methods have introduced new return values which may break downstream
+ consumers, such as desktop environments. The new return values more
+ precisely communicate the status of inhibitors. This allows desktops
+ to differentiate between system administrator policy and
+ temporary restrictions imposed by inhibitors.
+
New system interfaces and components:
* The os-release(3) gained a new field FANCY_NAME= that is similar
diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml
index 464fdab108d..dc80e967177 100644
--- a/man/org.freedesktop.login1.xml
+++ b/man/org.freedesktop.login1.xml
@@ -276,7 +276,6 @@ node /org/freedesktop/login1 {
readonly t RuntimeDirectoryInodesMax = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t InhibitorsMax = ...;
- @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t NCurrentInhibitors = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t SessionsMax = ...;
@@ -711,12 +710,19 @@ node /org/freedesktop/login1 {
CanRebootParameter(), CanRebootToFirmwareSetup(),
CanRebootToBootLoaderMenu(), and CanRebootToBootLoaderEntry()
test whether the system supports the respective operation and whether the calling user is allowed to
- execute it. Returns one of na, yes, no, and
- challenge. If na is returned, the operation is not available because
- hardware, kernel, or drivers do not support it. If yes is returned, the operation is
- supported and the user may execute the operation without further authentication. If no
- is returned, the operation is available but the user is not allowed to execute the operation. If
- challenge is returned, the operation is available but only after authorization.
+ execute it. Returns one of na, yes, no,
+ challenge, and inhibited. If na is returned,
+ the operation is not available because hardware, kernel, or drivers do not support it. If yes
+ is returned, the operation is supported and the user may execute the operation without further authentication.
+ If no is returned, the operation is available but the user is not allowed to execute
+ the operation. If challenge is returned, the operation is available but only after
+ authorization. If inhibited is returned, the operation is normally available without
+ authorization but is currently inhibited. The operation is available only if inhibitors are ignored and
+ after authorization. If inhibitor-blocked is returned, the operation is normally
+ available without authorization but is currently inhibited. While the inhibitor remains active, the user
+ is not allowed to execute the operation. challenge-inhibitor-blocked is similar:
+ the operation is normally available after authorization but a held inhibitor disallows the user from
+ executing the operation.
ScheduleShutdown() schedules a shutdown operation type at
time usec in microseconds since the UNIX epoch. Alternatively, if
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 87cc4f14bc8..b50e69809fe 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -2927,7 +2927,6 @@ static int method_can_shutdown_or_sleep(
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
bool multiple_sessions, challenge, blocked, check_unit_state = true;
const HandleActionData *a;
- const char *result = NULL;
uid_t uid;
int r;
@@ -2984,12 +2983,27 @@ static int method_can_shutdown_or_sleep(
if (r < 0)
return r;
- if (!streq(load_state, "loaded")) {
- result = "no";
- goto finish;
- }
+ if (!streq(load_state, "loaded"))
+ return sd_bus_reply_method_return(message, "s", "no");
}
+ const char *result;
+ r = bus_test_polkit(
+ message,
+ a->polkit_action,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ &challenge,
+ error);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ result = "yes";
+ else if (challenge)
+ result = "challenge";
+ else
+ result = "no";
+
if (multiple_sessions) {
r = bus_test_polkit(
message,
@@ -3001,12 +3015,13 @@ static int method_can_shutdown_or_sleep(
if (r < 0)
return r;
- if (r > 0)
- result = "yes";
- else if (challenge)
- result = "challenge";
- else
- result = "no";
+ if (r == 0) {
+ if (challenge) {
+ if (streq(result, "yes")) /* Avoid upgrading no -> challenge */
+ result = "challenge";
+ } else
+ result = "no";
+ }
}
if (blocked) {
@@ -3020,39 +3035,21 @@ static int method_can_shutdown_or_sleep(
if (r < 0)
return r;
- if (r > 0) {
- if (!result)
- result = "yes";
- } else if (challenge) {
- if (!result || streq(result, "yes"))
- result = "challenge";
- } else
- result = "no";
+ if (r == 0) {
+ if (challenge) {
+ if (streq(result, "yes"))
+ result = "inhibited";
+ /* If result is already "challenge" or "no", the held inhibitor has no effect */
+ } else {
+ if (streq(result, "yes"))
+ result = "inhibitor-blocked";
+ else if (streq(result, "challenge"))
+ result = "challenge-inhibitor-blocked";
+ /* If the result is already "no", the held inhibitor has no effect */
+ }
+ }
}
- if (!multiple_sessions && !blocked) {
- /* If neither inhibit nor multiple sessions
- * apply then just check the normal policy */
-
- r = bus_test_polkit(
- message,
- a->polkit_action,
- /* details= */ NULL,
- /* good_user= */ UID_INVALID,
- &challenge,
- error);
- if (r < 0)
- return r;
-
- if (r > 0)
- result = "yes";
- else if (challenge)
- result = "challenge";
- else
- result = "no";
- }
-
- finish:
return sd_bus_reply_method_return(message, "s", result);
}
@@ -3963,7 +3960,7 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectoryInodesMax", "t", NULL, offsetof(Manager, runtime_dir_inodes), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_hashmap_size, offsetof(Manager, inhibitors), 0),
+ SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_hashmap_size, offsetof(Manager, inhibitors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0),
SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 2ef15588f28..b78d39d02d7 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -148,13 +148,11 @@ static int inhibitor_save(Inhibitor *i) {
}
static int bus_manager_send_inhibited_change(Inhibitor *i) {
- const char *property;
-
assert(i);
- property = IN_SET(i->mode, INHIBIT_BLOCK, INHIBIT_BLOCK_WEAK) ? "BlockInhibited" : "DelayInhibited";
-
- return manager_send_changed(i->manager, property);
+ return manager_send_changed(i->manager,
+ i->mode == INHIBIT_DELAY ? "DelayInhibited" : "BlockInhibited",
+ "NCurrentInhibitors");
}
int inhibitor_start(Inhibitor *i) {