+ r = sd_bus_message_open_container(reply, 'a', "(susso)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(session, m->sessions, i) {
+ _cleanup_free_ char *p = NULL;
+
+ p = session_bus_path(session);
+ if (!p)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(reply, "(susso)",
+ session->id,
+ (uint32_t) session->user->uid,
+ session->user->name,
+ session->seat ? session->seat->id : "",
+ p);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *m = userdata;
+ User *user;
+ Iterator i;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(uso)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(user, m->users, i) {
+ _cleanup_free_ char *p = NULL;
+
+ p = user_bus_path(user);
+ if (!p)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(reply, "(uso)",
+ (uint32_t) user->uid,
+ user->name,
+ p);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *m = userdata;
+ Seat *seat;
+ Iterator i;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(so)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(seat, m->seats, i) {
+ _cleanup_free_ char *p = NULL;
+
+ p = seat_bus_path(seat);
+ if (!p)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(reply, "(so)", seat->id, p);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *m = userdata;
+ Inhibitor *inhibitor;
+ Iterator i;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(ssssuu)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(inhibitor, m->inhibitors, i) {
+
+ r = sd_bus_message_append(reply, "(ssssuu)",
+ strempty(inhibit_what_to_string(inhibitor->what)),
+ strempty(inhibitor->who),
+ strempty(inhibitor->why),
+ strempty(inhibit_mode_to_string(inhibitor->mode)),
+ (uint32_t) inhibitor->uid,
+ (uint32_t) inhibitor->pid);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
+ uint32_t audit_id = 0;
+ _cleanup_free_ char *id = NULL;
+ Session *session = NULL;
+ Manager *m = userdata;
+ User *user = NULL;
+ Seat *seat = NULL;
+ pid_t leader;
+ uid_t uid;
+ int remote;
+ uint32_t vtnr = 0;
+ SessionType t;
+ SessionClass c;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ 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);
+ if (r < 0)
+ return r;
+
+ if (!uid_is_valid(uid))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
+ if (leader < 0 || leader == 1)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
+
+ if (isempty(type))
+ t = _SESSION_TYPE_INVALID;
+ 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);
+ }
+
+ if (isempty(class))
+ c = _SESSION_CLASS_INVALID;
+ 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);
+ }
+
+ 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);
+ }
+
+ if (isempty(cseat))
+ seat = NULL;
+ 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);
+ }
+
+ if (tty_is_vc(tty)) {
+ int v;
+
+ 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);
+
+ 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);
+
+ if (!vtnr)
+ 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");
+
+ } 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");
+
+ if (vtnr != 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 || vtnr > 63)
+ 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");
+ }
+ }
+
+ r = sd_bus_message_enter_container(message, 'a', "(sv)");
+ if (r < 0)
+ return r;
+
+ if (t == _SESSION_TYPE_INVALID) {
+ if (!isempty(display))
+ t = SESSION_X11;
+ else if (!isempty(tty))
+ t = SESSION_TTY;
+ else
+ t = SESSION_UNSPECIFIED;
+ }
+
+ if (c == _SESSION_CLASS_INVALID) {
+ if (t == SESSION_UNSPECIFIED)
+ c = SESSION_BACKGROUND;
+ else
+ c = SESSION_USER;
+ }
+
+ if (leader == 0) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, (pid_t*) &leader);
+ if (r < 0)
+ return r;
+ }
+
+ r = manager_get_session_by_pid(m, leader, NULL);
+ if (r > 0)
+ return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session");
+
+ /*
+ * Old gdm and lightdm start the user-session on the same VT as
+ * the greeter session. But they destroy the greeter session
+ * after the user-session and want the user-session to take
+ * over the VT. We need to support this for
+ * backwards-compatibility, so make sure we allow new sessions
+ * on a VT that a greeter is running on. Furthermore, to allow
+ * re-logins, we have to allow a greeter to take over a used VT for
+ * the exact same reasons.
+ */
+ if (c != SESSION_GREETER &&
+ vtnr > 0 &&
+ vtnr < m->seat0->position_count &&
+ m->seat0->positions[vtnr] &&
+ m->seat0->positions[vtnr]->class != SESSION_GREETER)
+ 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);
+
+ audit_session_from_pid(leader, &audit_id);
+ if (audit_id > 0) {
+ /* Keep our session IDs and the audit session IDs in sync */
+
+ 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)) {
+ log_warning("Existing logind session ID %s used by new audit session, ignoring", id);
+ audit_id = 0;
+
+ id = mfree(id);
+ }
+ }
+
+ if (!id) {
+ do {
+ id = mfree(id);
+
+ if (asprintf(&id, "c%lu", ++m->session_counter) < 0)
+ return -ENOMEM;
+
+ } while (hashmap_get(m->sessions, id));
+ }
+
+ r = manager_add_user_by_uid(m, uid, &user);
+ if (r < 0)
+ goto fail;
+
+ r = manager_add_session(m, id, &session);
+ if (r < 0)
+ goto fail;
+
+ session_set_user(session, user);
+
+ session->leader = leader;
+ session->audit_id = audit_id;
+ session->type = t;
+ session->class = c;
+ session->remote = remote;
+ session->vtnr = vtnr;
+
+ if (!isempty(tty)) {
+ session->tty = strdup(tty);
+ if (!session->tty) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!isempty(display)) {
+ session->display = strdup(display);
+ if (!session->display) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!isempty(remote_user)) {
+ session->remote_user = strdup(remote_user);
+ if (!session->remote_user) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!isempty(remote_host)) {
+ session->remote_host = strdup(remote_host);
+ if (!session->remote_host) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!isempty(service)) {
+ session->service = strdup(service);
+ if (!session->service) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!isempty(desktop)) {
+ session->desktop = strdup(desktop);
+ if (!session->desktop) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (seat) {
+ r = seat_attach_session(seat, session);
+ if (r < 0)
+ goto fail;
+ }
+
+ r = session_start(session);
+ if (r < 0)
+ goto fail;
+
+ session->create_message = sd_bus_message_ref(message);
+
+ /* Here upstream systemd starts cgroups and the user systemd,
+ and arranges to reply asynchronously. We reply
+ directly. */
+
+ r = session_send_create_reply(session, NULL);
+ if (r < 0)
+ goto fail;
+
+ return 1;
+
+fail:
+ if (session)
+ session_add_to_gc_queue(session);
+
+ if (user)
+ user_add_to_gc_queue(user);
+
+ return r;
+}
+
+static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Session *session;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_get_session_from_creds(m, message, name, error, &session);
+ if (r < 0)
+ return r;
+
+ r = session_release(session);
+ if (r < 0)
+ return r;
+
+ session_add_to_gc_queue(session);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Session *session;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_get_session_from_creds(m, message, name, error, &session);
+ if (r < 0)
+ return r;
+
+ return bus_session_method_activate(message, session, error);
+}
+
+static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *session_name, *seat_name;
+ Manager *m = userdata;
+ Session *session;
+ Seat *seat;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ /* 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)
+ return r;
+
+ r = manager_get_session_from_creds(m, message, session_name, error, &session);
+ if (r < 0)
+ return r;
+
+ r = manager_get_seat_from_creds(m, message, seat_name, error, &seat);
+ if (r < 0)
+ 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);
+
+ r = session_activate(session);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_lock_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Session *session;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_get_session_from_creds(m, message, name, error, &session);
+ if (r < 0)
+ return r;
+
+ return bus_session_method_lock(message, session, error);
+}
+
+static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.lock-sessions",
+ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = session_send_lock_all(m, streq(sd_bus_message_get_member(message), "LockSessions"));
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_kill_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *name;
+ Manager *m = userdata;
+ Session *session;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_get_session_from_creds(m, message, name, error, &session);
+ if (r < 0)
+ return r;
+
+ return bus_session_method_kill(message, session, error);
+}
+
+static int method_kill_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ uint32_t uid;
+ User *user;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "u", &uid);
+ if (r < 0)
+ return r;
+
+ r = manager_get_user_from_creds(m, message, uid, error, &user);
+ if (r < 0)
+ return r;
+
+ return bus_user_method_kill(message, user, error);
+}
+
+static int method_terminate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Session *session;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_get_session_from_creds(m, message, name, error, &session);
+ if (r < 0)
+ return r;
+
+ return bus_session_method_terminate(message, session, error);
+}
+
+static int method_terminate_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ uint32_t uid;
+ User *user;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "u", &uid);
+ if (r < 0)
+ return r;
+
+ r = manager_get_user_from_creds(m, message, uid, error, &user);
+ if (r < 0)
+ return r;
+
+ return bus_user_method_terminate(message, user, error);
+}
+
+static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Seat *seat;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_get_seat_from_creds(m, message, name, error, &seat);
+ if (r < 0)
+ return r;
+
+ return bus_seat_method_terminate(message, seat, error);
+}
+
+static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *cc = NULL;
+ Manager *m = userdata;
+ int r, b, interactive;
+ struct passwd *pw;
+ const char *path;
+ uint32_t uid;
+ bool self = false;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "ubb", &uid, &b, &interactive);
+ if (r < 0)
+ return r;
+
+ if (uid == UID_INVALID) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+
+ /* Note that we get the owner UID of the session, not the actual client UID here! */
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_owner_uid(creds, &uid);
+ if (r < 0)
+ return r;
+
+ self = true;
+
+ } else if (!uid_is_valid(uid))
+ return -EINVAL;
+
+ errno = 0;
+ pw = getpwuid(uid);
+ if (!pw)
+ return errno > 0 ? -errno : -ENOENT;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger",
+ NULL,
+ interactive,
+ 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 */
+
+ mkdir_p_label("/var/lib/systemd", 0755);
+
+ r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0);
+ if (r < 0)
+ return r;
+
+ cc = cescape(pw->pw_name);
+ if (!cc)
+ return -ENOMEM;
+
+ path = strjoina("/var/lib/systemd/linger/", cc);
+ if (b) {
+ User *u;
+
+ r = touch(path);
+ if (r < 0)
+ return r;
+
+ if (manager_add_user_by_uid(m, uid, &u) >= 0)
+ user_start(u);
+
+ } else {
+ User *u;
+
+ r = unlink(path);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
+
+ u = hashmap_get(m->users, UID_TO_PTR(uid));
+ if (u)
+ user_add_to_gc_queue(u);
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int trigger_device(Manager *m, struct udev_device *d) {
+ _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+ struct udev_list_entry *first, *item;
+ int r;
+
+ assert(m);
+
+ e = udev_enumerate_new(m->udev);
+ if (!e)
+ return -ENOMEM;
+
+ if (d) {
+ r = udev_enumerate_add_match_parent(e, d);
+ if (r < 0)
+ return r;
+ }
+
+ r = udev_enumerate_scan_devices(e);
+ if (r < 0)
+ return r;
+
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first) {
+ _cleanup_free_ char *t = NULL;
+ const char *p;
+
+ p = udev_list_entry_get_name(item);
+
+ t = strappend(p, "/uevent");
+ if (!t)
+ return -ENOMEM;
+
+ write_string_file(t, "change", WRITE_STRING_FILE_CREATE);
+ }
+
+ return 0;
+}
+
+static int attach_device(Manager *m, const char *seat, const char *sysfs) {
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_free_ char *rule = NULL, *file = NULL;
+ const char *id_for_seat;
+ int r;
+
+ assert(m);
+ assert(seat);
+ assert(sysfs);
+
+ d = udev_device_new_from_syspath(m->udev, sysfs);
+ if (!d)
+ return -ENODEV;
+
+ if (!udev_device_has_tag(d, "seat"))
+ return -ENODEV;