]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
login1: Add "inhibited" state to Can* methods
authorAdrian Vovk <adrianvovk@gmail.com>
Mon, 19 Jan 2026 23:15:53 +0000 (18:15 -0500)
committerAdrian Vovk <adrianvovk@gmail.com>
Tue, 24 Feb 2026 01:03:06 +0000 (20:03 -0500)
Desktops cannot currently tell the difference between a power action
being set to challenge permanently (i.e. because the sysadmin wanted to
restrict the given user) and a power action being set to challenge
because it's temporarily inhibited.

Thus, a desktop might take an action that is valid in the first case but
not the second case. For instance: GNOME hides all of its automatic
suspend settings from the UI whenever a sleep inhibitor is active.

This now returns a new state: "inhibited". It communicates to the
desktop that the action is available normally, but at the moment the
desktop isn't allowed to perform the action due to an inhibitor.

Related: https://github.com/systemd/systemd/issues/37311

man/org.freedesktop.login1.xml
src/login/logind-dbus.c

index 464fdab108d8b7278a2bf9b2ac71cf286d1d5ea7..bc9525385933307f102163a78ff49463a3ed9292 100644 (file)
@@ -711,12 +711,19 @@ node /org/freedesktop/login1 {
       <function>CanRebootParameter()</function>, <function>CanRebootToFirmwareSetup()</function>,
       <function>CanRebootToBootLoaderMenu()</function>, and <function>CanRebootToBootLoaderEntry()</function>
       test whether the system supports the respective operation and whether the calling user is allowed to
-      execute it. Returns one of <literal>na</literal>, <literal>yes</literal>, <literal>no</literal>, and
-      <literal>challenge</literal>. If <literal>na</literal> is returned, the operation is not available because
-      hardware, kernel, or drivers do not support it. If <literal>yes</literal> is returned, the operation is
-      supported and the user may execute the operation without further authentication. If <literal>no</literal>
-      is returned, the operation is available but the user is not allowed to execute the operation. If
-      <literal>challenge</literal> is returned, the operation is available but only after authorization.</para>
+      execute it. Returns one of <literal>na</literal>, <literal>yes</literal>, <literal>no</literal>,
+      <literal>challenge</literal>, and <literal>inhibited</literal>. If <literal>na</literal> is returned,
+      the operation is not available because hardware, kernel, or drivers do not support it. If <literal>yes</literal>
+      is returned, the operation is supported and the user may execute the operation without further authentication.
+      If <literal>no</literal> is returned, the operation is available but the user is not allowed to execute
+      the operation. If <literal>challenge</literal> is returned, the operation is available but only after
+      authorization. If <literal>inhibited</literal> 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 <literal>inhibitor-blocked</literal> 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. <literal>challenge-inhibitor-blocked</literal> is similar:
+      the operation is normally available after authorization but a held inhibitor disallows the user from
+      executing the operation.</para>
 
       <para><function>ScheduleShutdown()</function> schedules a shutdown operation <varname>type</varname> at
       time <varname>usec</varname> in microseconds since the UNIX epoch. Alternatively, if
index 87cc4f14bc8683fca36044ca10b07bd4e8360311..7f7d9e70d2b529122f4d90a158cf781272a3a792 100644 (file)
@@ -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 (!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";
+                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 */
+                        }
+                }
         }
 
- finish:
         return sd_bus_reply_method_return(message, "s", result);
 }