]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/login/logind-dbus.c
Merge pull request #12753 from jrouleau/fix/hibernate-resume-timeout
[thirdparty/systemd.git] / src / login / logind-dbus.c
index 795fc3b645bbc197028f2a26281a34a252ca211c..0e8925ab9449a29d7facea68a354e657345163e3 100644 (file)
@@ -3,6 +3,7 @@
 #include <errno.h>
 #include <pwd.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "sd-device.h"
 #include "fileio.h"
 #include "format-util.h"
 #include "fs-util.h"
+#include "logind-dbus.h"
+#include "logind-seat-dbus.h"
+#include "logind-session-dbus.h"
+#include "logind-user-dbus.h"
 #include "logind.h"
 #include "missing_capability.h"
 #include "mkdir.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "reboot-util.h"
 #include "selinux-util.h"
 #include "sleep-config.h"
 #include "special.h"
 #include "unit-name.h"
 #include "user-util.h"
 #include "utmp-wtmp.h"
+#include "virt.h"
 
-static int get_sender_session(Manager *m, sd_bus_message *message, sd_bus_error *error, Session **ret) {
+static int get_sender_session(
+                Manager *m,
+                sd_bus_message *message,
+                bool consult_display,
+                sd_bus_error *error,
+                Session **ret) {
 
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+        Session *session = NULL;
         const char *name;
-        Session *session;
         int r;
 
-        /* Get client login session.  This is not what you are looking for these days,
-         * as apps may instead belong to a user service unit.  This includes terminal
-         * emulators and hence command-line apps. */
-        r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
+        /* Acquire the sender's session. This first checks if the sending process is inside a session itself,
+         * and returns that. If not and 'consult_display' is true, this returns the display session of the
+         * owning user of the caller. */
+
+        r = sd_bus_query_sender_creds(message,
+                                      SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT|
+                                      (consult_display ? SD_BUS_CREDS_OWNER_UID : 0), &creds);
         if (r < 0)
                 return r;
 
         r = sd_bus_creds_get_session(creds, &name);
-        if (r == -ENXIO)
-                goto err_no_session;
-        if (r < 0)
-                return r;
+        if (r < 0) {
+                if (r != -ENXIO)
+                        return r;
+
+                if (consult_display) {
+                        uid_t uid;
+
+                        r = sd_bus_creds_get_owner_uid(creds, &uid);
+                        if (r < 0) {
+                                if (r != -ENXIO)
+                                        return r;
+                        } else {
+                                User *user;
+
+                                user = hashmap_get(m->users, UID_TO_PTR(uid));
+                                if (user)
+                                        session = user->display;
+                        }
+                }
+        } else
+                session = hashmap_get(m->sessions, name);
 
-        session = hashmap_get(m->sessions, name);
         if (!session)
-                goto err_no_session;
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
+                                         consult_display ?
+                                         "Caller does not belong to any known session and doesn't own any suitable session." :
+                                         "Caller does not belong to any known session.");
 
         *ret = session;
         return 0;
-
-err_no_session:
-        return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
-                                 "Caller does not belong to any known session");
 }
 
-int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
+int manager_get_session_from_creds(
+                Manager *m,
+                sd_bus_message *message,
+                const char *name,
+                sd_bus_error *error,
+                Session **ret) {
+
         Session *session;
 
         assert(m);
         assert(message);
         assert(ret);
 
-        if (isempty(name))
-                return get_sender_session(m, message, error, ret);
+        if (SEAT_IS_SELF(name)) /* the caller's own session */
+                return get_sender_session(m, message, false, error, ret);
+        if (SEAT_IS_AUTO(name)) /* The caller's own session if they have one, otherwise their user's display session */
+                return get_sender_session(m, message, true, error, ret);
 
         session = hashmap_get(m->sessions, name);
         if (!session)
@@ -94,7 +132,6 @@ int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const ch
 }
 
 static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *error, User **ret) {
-
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
         uid_t uid;
         User *user;
@@ -106,20 +143,20 @@ static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *er
                 return r;
 
         r = sd_bus_creds_get_owner_uid(creds, &uid);
-        if (r == -ENXIO)
-                goto err_no_user;
-        if (r < 0)
-                return r;
+        if (r < 0) {
+                if (r != -ENXIO)
+                        return r;
+
+                user = NULL;
+        } else
+                user = hashmap_get(m->users, UID_TO_PTR(uid));
 
-        user = hashmap_get(m->users, UID_TO_PTR(uid));
         if (!user)
-                goto err_no_user;
+                return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID,
+                                         "Caller does not belong to any logged in or lingering user");
 
         *ret = user;
         return 0;
-
-err_no_user:
-        return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "Caller does not belong to any logged in user or lingering user");
 }
 
 int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) {
@@ -134,13 +171,20 @@ int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid,
 
         user = hashmap_get(m->users, UID_TO_PTR(uid));
         if (!user)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "User ID "UID_FMT" is not logged in or lingering", uid);
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER,
+                                         "User ID "UID_FMT" is not logged in or lingering", uid);
 
         *ret = user;
         return 0;
 }
 
-int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) {
+int manager_get_seat_from_creds(
+                Manager *m,
+                sd_bus_message *message,
+                const char *name,
+                sd_bus_error *error,
+                Seat **ret) {
+
         Seat *seat;
         int r;
 
@@ -148,16 +192,17 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char
         assert(message);
         assert(ret);
 
-        if (isempty(name)) {
+        if (SEAT_IS_SELF(name) || SEAT_IS_AUTO(name)) {
                 Session *session;
 
-                r = manager_get_session_from_creds(m, message, NULL, error, &session);
+                /* Use these special seat names as session names */
+                r = manager_get_session_from_creds(m, message, name, error, &session);
                 if (r < 0)
                         return r;
 
                 seat = session->seat;
                 if (!seat)
-                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat.");
+                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session '%s' has no seat.", session->id);
         } else {
                 seat = hashmap_get(m->seats, name);
                 if (!seat)
@@ -368,7 +413,8 @@ static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd
                         return r;
 
                 if (!session)
-                        return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, "PID "PID_FMT" does not belong to any known session", pid);
+                        return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
+                                                 "PID "PID_FMT" does not belong to any known session", pid);
         }
 
         p = session_bus_path(session);
@@ -646,7 +692,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
         assert_cc(sizeof(pid_t) == sizeof(uint32_t));
         assert_cc(sizeof(uid_t) == sizeof(uint32_t));
 
-        r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host);
+        r = sd_bus_message_read(message, "uusssssussbss",
+                                &uid, &leader, &service, &type, &class, &desktop, &cseat,
+                                &vtnr, &tty, &display, &remote, &remote_user, &remote_host);
         if (r < 0)
                 return r;
 
@@ -660,7 +708,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
         else {
                 t = session_type_from_string(type);
                 if (t < 0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session type %s", type);
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "Invalid session type %s", type);
         }
 
         if (isempty(class))
@@ -668,14 +717,16 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
         else {
                 c = session_class_from_string(class);
                 if (c < 0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session class %s", class);
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "Invalid session class %s", class);
         }
 
         if (isempty(desktop))
                 desktop = NULL;
         else {
                 if (!string_is_safe(desktop))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid desktop string %s", desktop);
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "Invalid desktop string %s", desktop);
         }
 
         if (isempty(cseat))
@@ -683,7 +734,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
         else {
                 seat = hashmap_get(m->seats, cseat);
                 if (!seat)
-                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", cseat);
+                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT,
+                                                 "No seat '%s' known", cseat);
         }
 
         if (tty_is_vc(tty)) {
@@ -692,35 +744,42 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 if (!seat)
                         seat = m->seat0;
                 else if (seat != m->seat0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
 
                 v = vtnr_from_tty(tty);
                 if (v <= 0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty);
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "Cannot determine VT number from virtual console TTY %s", tty);
 
                 if (vtnr == 0)
                         vtnr = (uint32_t) v;
                 else if (vtnr != (uint32_t) v)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match");
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "Specified TTY and VT number do not match");
 
         } else if (tty_is_console(tty)) {
 
                 if (!seat)
                         seat = m->seat0;
                 else if (seat != m->seat0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but seat is not seat0");
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "Console TTY specified but seat is not seat0");
 
                 if (vtnr != 0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but VT number is not 0");
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                 "Console TTY specified but VT number is not 0");
         }
 
         if (seat) {
                 if (seat_has_vts(seat)) {
                         if (vtnr <= 0 || vtnr > 63)
-                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "VT number out of range");
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                         "VT number out of range");
                 } else {
                         if (vtnr != 0)
-                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat has no VTs but VT number not 0");
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                         "Seat has no VTs but VT number not 0");
                 }
         }
 
@@ -752,13 +811,14 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                         return r;
         }
 
-        /* Check if we are already in a logind session. Or if we are in user@.service which is a special PAM session
-         * that avoids creating a logind session. */
+        /* Check if we are already in a logind session. Or if we are in user@.service
+         * which is a special PAM session that avoids creating a logind session. */
         r = manager_get_user_by_pid(m, leader, NULL);
         if (r < 0)
                 return r;
         if (r > 0)
-                return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice");
+                return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY,
+                                         "Already running in a session or user slice");
 
         /*
          * Old gdm and lightdm start the user-session on the same VT as
@@ -778,7 +838,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
 
         if (hashmap_size(m->sessions) >= m->sessions_max)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
+                                         "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.",
+                                         m->sessions_max);
 
         (void) audit_session_from_pid(leader, &audit_id);
         if (audit_session_is_valid(audit_id)) {
@@ -787,11 +849,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 if (asprintf(&id, "%"PRIu32, audit_id) < 0)
                         return -ENOMEM;
 
-                /* Wut? There's already a session by this name and we
-                 * didn't find it above? Weird, then let's not trust
-                 * the audit data and let's better register a new
-                 * ID */
-                if (hashmap_get(m->sessions, id)) {
+                /* Wut? There's already a session by this name and we didn't find it above? Weird, then let's
+                 * not trust the audit data and let's better register a new ID */
+                if (hashmap_contains(m->sessions, id)) {
                         log_warning("Existing logind session ID %s used by new audit session, ignoring.", id);
                         audit_id = AUDIT_SESSION_INVALID;
                         id = mfree(id);
@@ -805,10 +865,14 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                         if (asprintf(&id, "c%lu", ++m->session_counter) < 0)
                                 return -ENOMEM;
 
-                } while (hashmap_get(m->sessions, id));
+                } while (hashmap_contains(m->sessions, id));
         }
 
-        /* If we are not watching utmp aleady, try again */
+        /* The generated names should not clash with 'auto' or 'self' */
+        assert(!SESSION_IS_SELF(id));
+        assert(!SESSION_IS_AUTO(id));
+
+        /* If we are not watching utmp already, try again */
         manager_reconnect_utmp(m);
 
         r = manager_add_user_by_uid(m, uid, &user);
@@ -968,8 +1032,7 @@ static int method_activate_session_on_seat(sd_bus_message *message, void *userda
         assert(message);
         assert(m);
 
-        /* Same as ActivateSession() but refuses to work if
-         * the seat doesn't match */
+        /* Same as ActivateSession() but refuses to work if the seat doesn't match */
 
         r = sd_bus_message_read(message, "ss", &session_name, &seat_name);
         if (r < 0)
@@ -984,7 +1047,8 @@ static int method_activate_session_on_seat(sd_bus_message *message, void *userda
                 return r;
 
         if (session->seat != seat)
-                return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", session_name, seat_name);
+                return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT,
+                                         "Session %s not on seat %s", session_name, seat_name);
 
         r = session_activate(session);
         if (r < 0)
@@ -1195,8 +1259,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
         if (r == 0)
                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
 
-        mkdir_p_label("/var/lib/systemd", 0755);
-
+        (void) mkdir_p_label("/var/lib/systemd", 0755);
         r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0, MKDIR_WARN_MODE);
         if (r < 0)
                 return r;
@@ -1345,11 +1408,22 @@ static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_
         if (r < 0)
                 return r;
 
+        if (!path_is_normalized(sysfs))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", sysfs);
         if (!path_startswith(sysfs, "/sys"))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not in /sys", sysfs);
 
-        if (!seat_name_is_valid(seat))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat %s is not valid", seat);
+        if (SEAT_IS_SELF(seat) || SEAT_IS_AUTO(seat)) {
+                Seat *found;
+
+                r = manager_get_seat_from_creds(m, message, seat, error, &found);
+                if (r < 0)
+                        return r;
+
+                seat = found->id;
+
+        } else if (!seat_name_is_valid(seat)) /* Note that a seat does not have to exist yet for this operation to succeed */
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat name %s is not valid", seat);
 
         r = bus_verify_polkit_async(
                         message,
@@ -1793,7 +1867,8 @@ static int method_do_shutdown_or_sleep(
 
         /* Don't allow multiple jobs being executed at the same time */
         if (m->action_what > 0)
-                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
+                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
+                                         "There's already a shutdown or sleep operation in progress");
 
         if (sleep_verb) {
                 r = can_sleep(sleep_verb);
@@ -2408,6 +2483,97 @@ static int method_can_suspend_then_hibernate(sd_bus_message *message, void *user
                         error);
 }
 
+static int property_get_reboot_parameter(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        _cleanup_free_ char *parameter = NULL;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(userdata);
+
+        r = read_reboot_parameter(&parameter);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_append(reply, "s", parameter);
+}
+
+static int method_set_reboot_parameter(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+        const char *arg;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "s", &arg);
+        if (r < 0)
+                return r;
+
+        r = detect_container();
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
+                                         "Reboot parameter not supported in containers, refusing.");
+
+        r = bus_verify_polkit_async(message,
+                                    CAP_SYS_ADMIN,
+                                    "org.freedesktop.login1.set-reboot-parameter",
+                                    NULL,
+                                    false,
+                                    UID_INVALID,
+                                    &m->polkit_registry,
+                                    error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        r = update_reboot_parameter_and_warn(arg, false);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_can_reboot_parameter(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = detect_container();
+        if (r < 0)
+                return r;
+        if (r > 0) /* Inside containers, specifying a reboot parameter, doesn't make much sense */
+                return sd_bus_reply_method_return(message, "s", "na");
+
+        return return_test_polkit(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.login1.set-reboot-parameter",
+                        NULL,
+                        UID_INVALID,
+                        error);
+}
+
 static int property_get_reboot_to_firmware_setup(
                 sd_bus *bus,
                 const char *path,
@@ -3034,22 +3200,26 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
 
         w = inhibit_what_from_string(what);
         if (w <= 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid what specification %s", what);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Invalid what specification %s", what);
 
         mm = inhibit_mode_from_string(mode);
         if (mm < 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid mode specification %s", mode);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Invalid mode specification %s", mode);
 
         /* Delay is only supported for shutdown/sleep */
         if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP)))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delay inhibitors only supported for shutdown and sleep");
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Delay inhibitors only supported for shutdown and sleep");
 
         /* Don't allow taking delay locks while we are already
          * executing the operation. We shouldn't create the impression
          * that the lock was successful if the machine is about to go
          * down/suspend any moment. */
         if (m->action_what & w)
-                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running");
+                return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
+                                         "The operation inhibition has been requested for is already running");
 
         r = bus_verify_polkit_async(
                         message,
@@ -3084,7 +3254,9 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
                 return r;
 
         if (hashmap_size(m->inhibitors) >= m->inhibitors_max)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
+                                         "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.",
+                                         m->inhibitors_max);
 
         do {
                 id = mfree(id);
@@ -3137,6 +3309,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RebootParameter", "s", property_get_reboot_parameter, 0, 0),
         SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, 0),
         SD_BUS_PROPERTY("RebootToBootLoaderMenu", "t", property_get_reboot_to_boot_loader_menu, 0, 0),
         SD_BUS_PROPERTY("RebootToBootLoaderEntry", "s", property_get_reboot_to_boot_loader_entry, 0, 0),
@@ -3213,6 +3386,8 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("CanRebootParameter", NULL, "s", method_can_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("SetRebootParameter", "s", NULL, method_set_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanRebootToBootLoaderMenu", NULL, "s", method_can_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -3243,7 +3418,8 @@ static int session_jobs_reply(Session *s, const char *unit, const char *result)
         if (result && !streq(result, "done")) {
                 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
 
-                sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit '%s' failed with '%s'", unit, result);
+                sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED,
+                                  "Start job for unit '%s' failed with '%s'", unit, result);
                 return session_send_create_reply(s, &e);
         }