1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "dbus-common.h"
34 #define BUS_MANAGER_INTERFACE \
35 " <interface name=\"org.freedesktop.login1.Manager\">\n" \
36 " <method name=\"GetSession\">\n" \
37 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
38 " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
40 " <method name=\"GetSessionByPID\">\n" \
41 " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
42 " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
44 " <method name=\"GetUser\">\n" \
45 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
46 " <arg name=\"user\" type=\"o\" direction=\"out\"/>\n" \
48 " <method name=\"GetSeat\">\n" \
49 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
50 " <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n" \
52 " <method name=\"ListSessions\">\n" \
53 " <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
55 " <method name=\"ListUsers\">\n" \
56 " <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n" \
58 " <method name=\"ListSeats\">\n" \
59 " <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n" \
61 " <method name=\"CreateSession\">\n" \
62 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
63 " <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
64 " <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n" \
65 " <arg name=\"type\" type=\"s\" direction=\"in\"/>\n" \
66 " <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
67 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
68 " <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n" \
69 " <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n" \
70 " <arg name=\"display\" type=\"s\" direction=\"in\"/>\n" \
71 " <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n" \
72 " <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n" \
73 " <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n" \
74 " <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
75 " <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
76 " <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
77 " <arg name=\"id\" type=\"s\" direction=\"out\"/>\n" \
78 " <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
79 " <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
80 " <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \
81 " <arg name=\"seat\" type=\"s\" direction=\"out\"/>\n" \
82 " <arg name=\"vtnr\" type=\"u\" direction=\"out\"/>\n" \
84 " <method name=\"ReleaseSession\">\n" \
85 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
87 " <method name=\"ActivateSession\">\n" \
88 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
90 " <method name=\"ActivateSessionOnSeat\">\n" \
91 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
92 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
94 " <method name=\"LockSession\">\n" \
95 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
97 " <method name=\"UnlockSession\">\n" \
98 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
100 " <method name=\"KillSession\">\n" \
101 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
102 " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
103 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
105 " <method name=\"KillUser\">\n" \
106 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
107 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
109 " <method name=\"TerminateSession\">\n" \
110 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
112 " <method name=\"TerminateUser\">\n" \
113 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
115 " <method name=\"TerminateSeat\">\n" \
116 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
118 " <method name=\"SetUserLinger\">\n" \
119 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
120 " <arg name=\"b\" type=\"b\" direction=\"in\"/>\n" \
121 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
123 " <method name=\"AttachDevice\">\n" \
124 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
125 " <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n" \
126 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
128 " <method name=\"FlushDevices\">\n" \
129 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
131 " <method name=\"PowerOff\">\n" \
132 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
134 " <method name=\"Reboot\">\n" \
135 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
137 " <method name=\"CanPowerOff\">\n" \
138 " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
140 " <method name=\"CanReboot\">\n" \
141 " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
143 " <signal name=\"SessionNew\">\n" \
144 " <arg name=\"id\" type=\"s\"/>\n" \
145 " <arg name=\"path\" type=\"o\"/>\n" \
147 " <signal name=\"SessionRemoved\">\n" \
148 " <arg name=\"id\" type=\"s\"/>\n" \
149 " <arg name=\"path\" type=\"o\"/>\n" \
151 " <signal name=\"UserNew\">\n" \
152 " <arg name=\"uid\" type=\"u\"/>\n" \
153 " <arg name=\"path\" type=\"o\"/>\n" \
155 " <signal name=\"UserRemoved\">\n" \
156 " <arg name=\"uid\" type=\"u\"/>\n" \
157 " <arg name=\"path\" type=\"o\"/>\n" \
159 " <signal name=\"SeatNew\">\n" \
160 " <arg name=\"id\" type=\"s\"/>\n" \
161 " <arg name=\"path\" type=\"o\"/>\n" \
163 " <signal name=\"SeatRemoved\">\n" \
164 " <arg name=\"id\" type=\"s\"/>\n" \
165 " <arg name=\"path\" type=\"o\"/>\n" \
167 " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
168 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
169 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
170 " <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
171 " <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
172 " <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
173 " <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
174 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
175 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
176 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
179 #define INTROSPECTION_BEGIN \
180 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
182 BUS_MANAGER_INTERFACE \
183 BUS_PROPERTIES_INTERFACE \
185 BUS_INTROSPECTABLE_INTERFACE
187 #define INTROSPECTION_END \
190 #define INTERFACES_LIST \
191 BUS_GENERIC_INTERFACES_LIST \
192 "org.freedesktop.login1.Manager\0"
194 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
202 b = manager_get_idle_hint(m, NULL) > 0;
203 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
209 static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
218 manager_get_idle_hint(m, &t);
219 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
221 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
227 static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
228 Session *session = NULL;
230 const char *type, *class, *seat, *tty, *display, *remote_user, *remote_host, *service;
231 uint32_t uid, leader, audit_id = 0;
232 dbus_bool_t remote, kill_processes;
233 char **controllers = NULL, **reset_controllers = NULL;
237 DBusMessageIter iter;
242 DBusMessage *reply = NULL;
249 if (!dbus_message_iter_init(message, &iter) ||
250 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
253 dbus_message_iter_get_basic(&iter, &uid);
255 if (!dbus_message_iter_next(&iter) ||
256 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
259 dbus_message_iter_get_basic(&iter, &leader);
262 !dbus_message_iter_next(&iter) ||
263 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
266 dbus_message_iter_get_basic(&iter, &service);
268 if (!dbus_message_iter_next(&iter) ||
269 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
272 dbus_message_iter_get_basic(&iter, &type);
273 t = session_type_from_string(type);
276 !dbus_message_iter_next(&iter) ||
277 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
280 dbus_message_iter_get_basic(&iter, &class);
284 c = session_class_from_string(class);
287 !dbus_message_iter_next(&iter) ||
288 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
291 dbus_message_iter_get_basic(&iter, &seat);
296 s = hashmap_get(m->seats, seat);
301 if (!dbus_message_iter_next(&iter) ||
302 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
305 dbus_message_iter_get_basic(&iter, &vtnr);
307 if (!dbus_message_iter_next(&iter) ||
308 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
311 dbus_message_iter_get_basic(&iter, &tty);
313 if (tty_is_vc(tty)) {
318 else if (s != m->vtconsole)
321 v = vtnr_from_tty(tty);
324 return v < 0 ? v : -EINVAL;
328 else if (vtnr != (uint32_t) v)
331 } else if (!isempty(tty) && s && seat_is_vtconsole(s))
335 if (seat_can_multi_session(s)) {
336 if (vtnr <= 0 || vtnr > 63)
344 if (!dbus_message_iter_next(&iter) ||
345 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
348 dbus_message_iter_get_basic(&iter, &display);
350 if (!dbus_message_iter_next(&iter) ||
351 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
354 dbus_message_iter_get_basic(&iter, &remote);
356 if (!dbus_message_iter_next(&iter) ||
357 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
360 dbus_message_iter_get_basic(&iter, &remote_user);
362 if (!dbus_message_iter_next(&iter) ||
363 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
366 dbus_message_iter_get_basic(&iter, &remote_host);
368 if (!dbus_message_iter_next(&iter) ||
369 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
370 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
373 r = bus_parse_strv_iter(&iter, &controllers);
377 if (strv_contains(controllers, "systemd") ||
378 !dbus_message_iter_next(&iter) ||
379 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
380 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
385 r = bus_parse_strv_iter(&iter, &reset_controllers);
389 if (strv_contains(reset_controllers, "systemd") ||
390 !dbus_message_iter_next(&iter) ||
391 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
396 dbus_message_iter_get_basic(&iter, &kill_processes);
398 r = manager_add_user_by_uid(m, uid, &user);
402 audit_session_from_pid(leader, &audit_id);
405 asprintf(&id, "%lu", (unsigned long) audit_id);
412 session = hashmap_get(m->sessions, id);
417 fifo_fd = session_create_fifo(session);
423 /* Session already exists, client is probably
424 * something like "su" which changes uid but
425 * is still the same audit session */
427 reply = dbus_message_new_method_return(message);
433 p = session_bus_path(session);
439 seat = session->seat ? session->seat->id : "";
440 vtnr = session->vtnr;
441 b = dbus_message_append_args(
443 DBUS_TYPE_STRING, &session->id,
444 DBUS_TYPE_OBJECT_PATH, &p,
445 DBUS_TYPE_STRING, &session->user->runtime_path,
446 DBUS_TYPE_UNIX_FD, &fifo_fd,
447 DBUS_TYPE_STRING, &seat,
448 DBUS_TYPE_UINT32, &vtnr,
457 close_nointr_nofail(fifo_fd);
460 strv_free(controllers);
461 strv_free(reset_controllers);
469 asprintf(&id, "c%lu", ++m->session_counter);
476 } while (hashmap_get(m->sessions, id));
479 r = manager_add_session(m, user, id, &session);
484 session->leader = leader;
485 session->audit_id = audit_id;
488 session->remote = remote;
489 session->controllers = controllers;
490 session->reset_controllers = reset_controllers;
491 session->kill_processes = kill_processes;
492 session->vtnr = vtnr;
494 controllers = reset_controllers = NULL;
497 session->tty = strdup(tty);
504 if (!isempty(display)) {
505 session->display = strdup(display);
506 if (!session->display) {
512 if (!isempty(remote_user)) {
513 session->remote_user = strdup(remote_user);
514 if (!session->remote_user) {
520 if (!isempty(remote_host)) {
521 session->remote_host = strdup(remote_host);
522 if (!session->remote_host) {
528 if (!isempty(service)) {
529 session->service = strdup(service);
530 if (!session->service) {
536 fifo_fd = session_create_fifo(session);
543 r = seat_attach_session(s, session);
548 r = session_start(session);
552 reply = dbus_message_new_method_return(message);
558 p = session_bus_path(session);
564 seat = s ? s->id : "";
565 b = dbus_message_append_args(
567 DBUS_TYPE_STRING, &session->id,
568 DBUS_TYPE_OBJECT_PATH, &p,
569 DBUS_TYPE_STRING, &session->user->runtime_path,
570 DBUS_TYPE_UNIX_FD, &fifo_fd,
571 DBUS_TYPE_STRING, &seat,
572 DBUS_TYPE_UINT32, &vtnr,
581 close_nointr_nofail(fifo_fd);
587 strv_free(controllers);
588 strv_free(reset_controllers);
591 session_add_to_gc_queue(session);
594 user_add_to_gc_queue(user);
597 close_nointr_nofail(fifo_fd);
600 dbus_message_unref(reply);
605 static int trigger_device(Manager *m, struct udev_device *d) {
606 struct udev_enumerate *e;
607 struct udev_list_entry *first, *item;
612 e = udev_enumerate_new(m->udev);
619 if (udev_enumerate_add_match_parent(e, d) < 0) {
625 if (udev_enumerate_scan_devices(e) < 0) {
630 first = udev_enumerate_get_list_entry(e);
631 udev_list_entry_foreach(item, first) {
635 p = udev_list_entry_get_name(item);
637 t = strappend(p, "/uevent");
643 write_one_line_file(t, "change");
651 udev_enumerate_unref(e);
656 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
657 struct udev_device *d;
658 char *rule = NULL, *file = NULL;
659 const char *id_for_seat;
666 d = udev_device_new_from_syspath(m->udev, sysfs);
670 if (!udev_device_has_tag(d, "seat")) {
675 id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
681 if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
686 if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
691 mkdir_p("/etc/udev/rules.d", 0755);
692 r = write_one_line_file_atomic(file, rule);
696 r = trigger_device(m, d);
703 udev_device_unref(d);
708 static int flush_devices(Manager *m) {
713 d = opendir("/etc/udev/rules.d");
716 log_warning("Failed to open /etc/udev/rules.d: %m");
720 while ((de = readdir(d))) {
722 if (!dirent_is_file(de))
725 if (!startswith(de->d_name, "72-seat-"))
728 if (!endswith(de->d_name, ".rules"))
731 if (unlinkat(dirfd(d), de->d_name, 0) < 0)
732 log_warning("Failed to unlink %s: %m", de->d_name);
738 return trigger_device(m, NULL);
741 static int have_multiple_sessions(
742 DBusConnection *connection,
744 DBusMessage *message,
751 if (hashmap_size(m->sessions) > 1)
754 /* Hmm, there's only one session, but let's make sure it
755 * actually belongs to the user who is asking. If not, better
756 * be safe than sorry. */
758 s = hashmap_first(m->sessions);
762 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
763 if (ul == (unsigned long) -1)
766 return s->user->uid != ul;
772 static const BusProperty bus_login_manager_properties[] = {
773 { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true },
774 { "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
775 { "ResetControllers", bus_property_append_strv, "as", offsetof(Manager, reset_controllers), true },
776 { "NAutoVTs", bus_property_append_unsigned, "u", offsetof(Manager, n_autovts) },
777 { "KillOnlyUsers", bus_property_append_strv, "as", offsetof(Manager, kill_only_users), true },
778 { "KillExcludeUsers", bus_property_append_strv, "as", offsetof(Manager, kill_exclude_users), true },
779 { "KillUserProcesses", bus_property_append_bool, "b", offsetof(Manager, kill_user_processes) },
780 { "IdleHint", bus_manager_append_idle_hint, "b", 0 },
781 { "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 },
782 { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 },
786 static DBusHandlerResult manager_message_handler(
787 DBusConnection *connection,
788 DBusMessage *message,
791 Manager *m = userdata;
794 DBusMessage *reply = NULL;
801 dbus_error_init(&error);
803 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
809 if (!dbus_message_get_args(
812 DBUS_TYPE_STRING, &name,
814 return bus_send_error_reply(connection, message, &error, -EINVAL);
816 session = hashmap_get(m->sessions, name);
818 return bus_send_error_reply(connection, message, &error, -ENOENT);
820 reply = dbus_message_new_method_return(message);
824 p = session_bus_path(session);
828 b = dbus_message_append_args(
830 DBUS_TYPE_OBJECT_PATH, &p,
837 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) {
843 if (!dbus_message_get_args(
846 DBUS_TYPE_UINT32, &pid,
848 return bus_send_error_reply(connection, message, &error, -EINVAL);
850 r = manager_get_session_by_pid(m, pid, &session);
852 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
854 reply = dbus_message_new_method_return(message);
858 p = session_bus_path(session);
862 b = dbus_message_append_args(
864 DBUS_TYPE_OBJECT_PATH, &p,
871 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
877 if (!dbus_message_get_args(
880 DBUS_TYPE_UINT32, &uid,
882 return bus_send_error_reply(connection, message, &error, -EINVAL);
884 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
886 return bus_send_error_reply(connection, message, &error, -ENOENT);
888 reply = dbus_message_new_method_return(message);
892 p = user_bus_path(user);
896 b = dbus_message_append_args(
898 DBUS_TYPE_OBJECT_PATH, &p,
905 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
911 if (!dbus_message_get_args(
914 DBUS_TYPE_STRING, &name,
916 return bus_send_error_reply(connection, message, &error, -EINVAL);
918 seat = hashmap_get(m->seats, name);
920 return bus_send_error_reply(connection, message, &error, -ENOENT);
922 reply = dbus_message_new_method_return(message);
926 p = seat_bus_path(seat);
930 b = dbus_message_append_args(
932 DBUS_TYPE_OBJECT_PATH, &p,
939 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
943 DBusMessageIter iter, sub;
944 const char *empty = "";
946 reply = dbus_message_new_method_return(message);
950 dbus_message_iter_init_append(reply, &iter);
952 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
955 HASHMAP_FOREACH(session, m->sessions, i) {
956 DBusMessageIter sub2;
959 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
962 uid = session->user->uid;
964 p = session_bus_path(session);
968 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
969 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
970 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
971 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
972 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
979 if (!dbus_message_iter_close_container(&sub, &sub2))
983 if (!dbus_message_iter_close_container(&iter, &sub))
986 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
990 DBusMessageIter iter, sub;
992 reply = dbus_message_new_method_return(message);
996 dbus_message_iter_init_append(reply, &iter);
998 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
1001 HASHMAP_FOREACH(user, m->users, i) {
1002 DBusMessageIter sub2;
1005 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1010 p = user_bus_path(user);
1014 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1015 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
1016 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1023 if (!dbus_message_iter_close_container(&sub, &sub2))
1027 if (!dbus_message_iter_close_container(&iter, &sub))
1030 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
1034 DBusMessageIter iter, sub;
1036 reply = dbus_message_new_method_return(message);
1040 dbus_message_iter_init_append(reply, &iter);
1042 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
1045 HASHMAP_FOREACH(seat, m->seats, i) {
1046 DBusMessageIter sub2;
1048 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1051 p = seat_bus_path(seat);
1055 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
1056 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1063 if (!dbus_message_iter_close_container(&sub, &sub2))
1067 if (!dbus_message_iter_close_container(&iter, &sub))
1070 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
1072 r = bus_manager_create_session(m, message, &reply);
1074 /* Don't delay the work on OOM here, since it might be
1075 * triggered by a low RLIMIT_NOFILE here (since we
1076 * send a dupped fd to the client), and we'd rather
1077 * see this fail quickly then be retried later */
1080 return bus_send_error_reply(connection, message, &error, r);
1082 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ReleaseSession")) {
1086 if (!dbus_message_get_args(
1089 DBUS_TYPE_STRING, &name,
1091 return bus_send_error_reply(connection, message, &error, -EINVAL);
1093 session = hashmap_get(m->sessions, name);
1095 return bus_send_error_reply(connection, message, &error, -ENOENT);
1097 /* We use the FIFO to detect stray sessions where the
1098 process invoking PAM dies abnormally. We need to make
1099 sure that that process is not killed if at the clean
1100 end of the session it closes the FIFO. Hence, with
1101 this call explicitly turn off the FIFO logic, so that
1102 the PAM code can finish clean up on its own */
1103 session_remove_fifo(session);
1105 reply = dbus_message_new_method_return(message);
1109 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
1113 if (!dbus_message_get_args(
1116 DBUS_TYPE_STRING, &name,
1118 return bus_send_error_reply(connection, message, &error, -EINVAL);
1120 session = hashmap_get(m->sessions, name);
1122 return bus_send_error_reply(connection, message, &error, -ENOENT);
1124 r = session_activate(session);
1126 return bus_send_error_reply(connection, message, NULL, r);
1128 reply = dbus_message_new_method_return(message);
1132 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) {
1133 const char *session_name, *seat_name;
1137 /* Same as ActivateSession() but refuses to work if
1138 * the seat doesn't match */
1140 if (!dbus_message_get_args(
1143 DBUS_TYPE_STRING, &session_name,
1144 DBUS_TYPE_STRING, &seat_name,
1146 return bus_send_error_reply(connection, message, &error, -EINVAL);
1148 session = hashmap_get(m->sessions, session_name);
1150 return bus_send_error_reply(connection, message, &error, -ENOENT);
1152 seat = hashmap_get(m->seats, seat_name);
1154 return bus_send_error_reply(connection, message, &error, -ENOENT);
1156 if (session->seat != seat)
1157 return bus_send_error_reply(connection, message, &error, -EINVAL);
1159 r = session_activate(session);
1161 return bus_send_error_reply(connection, message, NULL, r);
1163 reply = dbus_message_new_method_return(message);
1167 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") ||
1168 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) {
1172 if (!dbus_message_get_args(
1175 DBUS_TYPE_STRING, &name,
1177 return bus_send_error_reply(connection, message, &error, -EINVAL);
1179 session = hashmap_get(m->sessions, name);
1181 return bus_send_error_reply(connection, message, &error, -ENOENT);
1183 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
1186 reply = dbus_message_new_method_return(message);
1190 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) {
1197 if (!dbus_message_get_args(
1200 DBUS_TYPE_STRING, &name,
1201 DBUS_TYPE_STRING, &swho,
1202 DBUS_TYPE_INT32, &signo,
1204 return bus_send_error_reply(connection, message, &error, -EINVAL);
1209 who = kill_who_from_string(swho);
1211 return bus_send_error_reply(connection, message, &error, -EINVAL);
1214 if (signo <= 0 || signo >= _NSIG)
1215 return bus_send_error_reply(connection, message, &error, -EINVAL);
1217 session = hashmap_get(m->sessions, name);
1219 return bus_send_error_reply(connection, message, &error, -ENOENT);
1221 r = session_kill(session, who, signo);
1223 return bus_send_error_reply(connection, message, NULL, r);
1225 reply = dbus_message_new_method_return(message);
1229 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) {
1234 if (!dbus_message_get_args(
1237 DBUS_TYPE_UINT32, &uid,
1238 DBUS_TYPE_INT32, &signo,
1240 return bus_send_error_reply(connection, message, &error, -EINVAL);
1242 if (signo <= 0 || signo >= _NSIG)
1243 return bus_send_error_reply(connection, message, &error, -EINVAL);
1245 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1247 return bus_send_error_reply(connection, message, &error, -ENOENT);
1249 r = user_kill(user, signo);
1251 return bus_send_error_reply(connection, message, NULL, r);
1253 reply = dbus_message_new_method_return(message);
1257 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
1261 if (!dbus_message_get_args(
1264 DBUS_TYPE_STRING, &name,
1266 return bus_send_error_reply(connection, message, &error, -EINVAL);
1268 session = hashmap_get(m->sessions, name);
1270 return bus_send_error_reply(connection, message, &error, -ENOENT);
1272 r = session_stop(session);
1274 return bus_send_error_reply(connection, message, NULL, r);
1276 reply = dbus_message_new_method_return(message);
1280 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1284 if (!dbus_message_get_args(
1287 DBUS_TYPE_UINT32, &uid,
1289 return bus_send_error_reply(connection, message, &error, -EINVAL);
1291 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1293 return bus_send_error_reply(connection, message, &error, -ENOENT);
1295 r = user_stop(user);
1297 return bus_send_error_reply(connection, message, NULL, r);
1299 reply = dbus_message_new_method_return(message);
1303 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1307 if (!dbus_message_get_args(
1310 DBUS_TYPE_STRING, &name,
1312 return bus_send_error_reply(connection, message, &error, -EINVAL);
1314 seat = hashmap_get(m->seats, name);
1316 return bus_send_error_reply(connection, message, &error, -ENOENT);
1318 r = seat_stop_sessions(seat);
1320 return bus_send_error_reply(connection, message, NULL, r);
1322 reply = dbus_message_new_method_return(message);
1326 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1329 dbus_bool_t b, interactive;
1332 if (!dbus_message_get_args(
1335 DBUS_TYPE_UINT32, &uid,
1336 DBUS_TYPE_BOOLEAN, &b,
1337 DBUS_TYPE_BOOLEAN, &interactive,
1339 return bus_send_error_reply(connection, message, &error, -EINVAL);
1344 return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
1346 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, NULL, &error);
1348 return bus_send_error_reply(connection, message, &error, r);
1350 mkdir_p("/var/lib/systemd", 0755);
1352 r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0);
1354 return bus_send_error_reply(connection, message, &error, r);
1356 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
1367 return bus_send_error_reply(connection, message, &error, r);
1369 if (manager_add_user_by_uid(m, uid, &u) >= 0)
1378 if (r < 0 && errno != ENOENT)
1379 return bus_send_error_reply(connection, message, &error, -errno);
1381 u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1383 user_add_to_gc_queue(u);
1386 reply = dbus_message_new_method_return(message);
1390 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
1391 const char *sysfs, *seat;
1392 dbus_bool_t interactive;
1394 if (!dbus_message_get_args(
1397 DBUS_TYPE_STRING, &seat,
1398 DBUS_TYPE_STRING, &sysfs,
1399 DBUS_TYPE_BOOLEAN, &interactive,
1401 return bus_send_error_reply(connection, message, &error, -EINVAL);
1403 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
1404 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1406 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, NULL, &error);
1408 return bus_send_error_reply(connection, message, &error, r);
1410 r = attach_device(m, seat, sysfs);
1412 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1414 reply = dbus_message_new_method_return(message);
1419 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
1420 dbus_bool_t interactive;
1422 if (!dbus_message_get_args(
1425 DBUS_TYPE_BOOLEAN, &interactive,
1427 return bus_send_error_reply(connection, message, &error, -EINVAL);
1429 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, NULL, &error);
1431 return bus_send_error_reply(connection, message, &error, r);
1433 r = flush_devices(m);
1435 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1437 reply = dbus_message_new_method_return(message);
1441 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
1442 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
1443 dbus_bool_t interactive;
1444 bool multiple_sessions;
1445 DBusMessage *forward, *freply;
1447 const char *mode = "replace";
1450 if (!dbus_message_get_args(
1453 DBUS_TYPE_BOOLEAN, &interactive,
1455 return bus_send_error_reply(connection, message, &error, -EINVAL);
1457 r = have_multiple_sessions(connection, m, message, &error);
1459 return bus_send_error_reply(connection, message, &error, r);
1461 multiple_sessions = r > 0;
1463 if (streq(dbus_message_get_member(message), "PowerOff")) {
1464 if (multiple_sessions)
1465 action = "org.freedesktop.login1.power-off-multiple-sessions";
1467 action = "org.freedesktop.login1.power-off";
1469 name = SPECIAL_POWEROFF_TARGET;
1471 if (multiple_sessions)
1472 action = "org.freedesktop.login1.reboot-multiple-sessions";
1474 action = "org.freedesktop.login1.reboot";
1476 name = SPECIAL_REBOOT_TARGET;
1479 r = verify_polkit(connection, message, action, interactive, NULL, &error);
1481 return bus_send_error_reply(connection, message, &error, r);
1483 forward = dbus_message_new_method_call(
1484 "org.freedesktop.systemd1",
1485 "/org/freedesktop/systemd1",
1486 "org.freedesktop.systemd1.Manager",
1489 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1491 if (!dbus_message_append_args(forward,
1492 DBUS_TYPE_STRING, &name,
1493 DBUS_TYPE_STRING, &mode,
1494 DBUS_TYPE_INVALID)) {
1495 dbus_message_unref(forward);
1496 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1499 freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error);
1500 dbus_message_unref(forward);
1503 return bus_send_error_reply(connection, message, &error, -EIO);
1505 dbus_message_unref(freply);
1507 reply = dbus_message_new_method_return(message);
1511 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff") ||
1512 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
1514 bool multiple_sessions, challenge, b;
1515 const char *t, *action;
1517 r = have_multiple_sessions(connection, m, message, &error);
1519 return bus_send_error_reply(connection, message, &error, r);
1521 multiple_sessions = r > 0;
1523 if (streq(dbus_message_get_member(message), "CanPowerOff")) {
1524 if (multiple_sessions)
1525 action = "org.freedesktop.login1.power-off-multiple-sessions";
1527 action = "org.freedesktop.login1.power-off";
1530 if (multiple_sessions)
1531 action = "org.freedesktop.login1.reboot-multiple-sessions";
1533 action = "org.freedesktop.login1.reboot";
1536 r = verify_polkit(connection, message, action, false, &challenge, &error);
1538 return bus_send_error_reply(connection, message, &error, r);
1540 reply = dbus_message_new_method_return(message);
1545 challenge ? "challenge" :
1548 b = dbus_message_append_args(
1550 DBUS_TYPE_STRING, &t,
1555 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1556 char *introspection = NULL;
1565 if (!(reply = dbus_message_new_method_return(message)))
1568 /* We roll our own introspection code here, instead of
1569 * relying on bus_default_message_handler() because we
1570 * need to generate our introspection string
1573 if (!(f = open_memstream(&introspection, &size)))
1576 fputs(INTROSPECTION_BEGIN, f);
1578 HASHMAP_FOREACH(seat, m->seats, i) {
1579 p = bus_path_escape(seat->id);
1582 fprintf(f, "<node name=\"seat/%s\"/>", p);
1587 HASHMAP_FOREACH(user, m->users, i)
1588 fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
1590 HASHMAP_FOREACH(session, m->sessions, i) {
1591 p = bus_path_escape(session->id);
1594 fprintf(f, "<node name=\"session/%s\"/>", p);
1599 fputs(INTROSPECTION_END, f);
1603 free(introspection);
1612 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1613 free(introspection);
1617 free(introspection);
1619 const BusBoundProperties bps[] = {
1620 { "org.freedesktop.login1.Manager", bus_login_manager_properties, m },
1623 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1627 if (!dbus_connection_send(connection, reply, NULL))
1630 dbus_message_unref(reply);
1633 return DBUS_HANDLER_RESULT_HANDLED;
1637 dbus_message_unref(reply);
1639 dbus_error_free(&error);
1641 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1644 const DBusObjectPathVTable bus_manager_vtable = {
1645 .message_function = manager_message_handler
1648 DBusHandlerResult bus_message_filter(
1649 DBusConnection *connection,
1650 DBusMessage *message,
1653 Manager *m = userdata;
1660 dbus_error_init(&error);
1662 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
1665 if (!dbus_message_get_args(message, &error,
1666 DBUS_TYPE_STRING, &cgroup,
1668 log_error("Failed to parse Released message: %s", bus_error_message(&error));
1670 manager_cgroup_notify_empty(m, cgroup);
1673 dbus_error_free(&error);
1675 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1678 int manager_send_changed(Manager *manager, const char *properties) {
1684 m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
1688 if (!dbus_connection_send(manager->bus, m, NULL))
1695 dbus_message_unref(m);