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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "dbus-common.h"
33 #define BUS_MANAGER_INTERFACE \
34 " <interface name=\"org.freedesktop.login1.Manager\">\n" \
35 " <method name=\"GetSession\">\n" \
36 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
37 " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
39 " <method name=\"GetSessionByPID\">\n" \
40 " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
41 " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
43 " <method name=\"GetUser\">\n" \
44 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
45 " <arg name=\"user\" type=\"o\" direction=\"out\"/>\n" \
47 " <method name=\"GetSeat\">\n" \
48 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
49 " <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n" \
51 " <method name=\"ListSessions\">\n" \
52 " <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
54 " <method name=\"ListUsers\">\n" \
55 " <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n" \
57 " <method name=\"ListSeats\">\n" \
58 " <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n" \
60 " <method name=\"CreateSession\">\n" \
61 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
62 " <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
63 " <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n" \
64 " <arg name=\"type\" type=\"s\" direction=\"in\"/>\n" \
65 " <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
66 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
67 " <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n" \
68 " <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n" \
69 " <arg name=\"display\" type=\"s\" direction=\"in\"/>\n" \
70 " <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n" \
71 " <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n" \
72 " <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n" \
73 " <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
74 " <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
75 " <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
76 " <arg name=\"id\" type=\"s\" direction=\"out\"/>\n" \
77 " <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
78 " <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
79 " <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \
80 " <arg name=\"seat\" type=\"s\" direction=\"out\"/>\n" \
81 " <arg name=\"vtnr\" type=\"u\" direction=\"out\"/>\n" \
83 " <method name=\"ReleaseSession\">\n" \
84 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
86 " <method name=\"ActivateSession\">\n" \
87 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
89 " <method name=\"ActivateSessionOnSeat\">\n" \
90 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
91 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
93 " <method name=\"LockSession\">\n" \
94 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
96 " <method name=\"UnlockSession\">\n" \
97 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
99 " <method name=\"KillSession\">\n" \
100 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
101 " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
102 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
104 " <method name=\"KillUser\">\n" \
105 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
106 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
108 " <method name=\"TerminateSession\">\n" \
109 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
111 " <method name=\"TerminateUser\">\n" \
112 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
114 " <method name=\"TerminateSeat\">\n" \
115 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
117 " <method name=\"SetUserLinger\">\n" \
118 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
119 " <arg name=\"b\" type=\"b\" direction=\"in\"/>\n" \
120 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
122 " <method name=\"AttachDevice\">\n" \
123 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
124 " <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n" \
125 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
127 " <method name=\"FlushDevices\">\n" \
128 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
130 " <method name=\"PowerOff\">\n" \
131 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
133 " <method name=\"Reboot\">\n" \
134 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
136 " <method name=\"CanPowerOff\">\n" \
137 " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
139 " <method name=\"CanReboot\">\n" \
140 " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
142 " <signal name=\"SessionNew\">\n" \
143 " <arg name=\"id\" type=\"s\"/>\n" \
144 " <arg name=\"path\" type=\"o\"/>\n" \
146 " <signal name=\"SessionRemoved\">\n" \
147 " <arg name=\"id\" type=\"s\"/>\n" \
148 " <arg name=\"path\" type=\"o\"/>\n" \
150 " <signal name=\"UserNew\">\n" \
151 " <arg name=\"uid\" type=\"u\"/>\n" \
152 " <arg name=\"path\" type=\"o\"/>\n" \
154 " <signal name=\"UserRemoved\">\n" \
155 " <arg name=\"uid\" type=\"u\"/>\n" \
156 " <arg name=\"path\" type=\"o\"/>\n" \
158 " <signal name=\"SeatNew\">\n" \
159 " <arg name=\"id\" type=\"s\"/>\n" \
160 " <arg name=\"path\" type=\"o\"/>\n" \
162 " <signal name=\"SeatRemoved\">\n" \
163 " <arg name=\"id\" type=\"s\"/>\n" \
164 " <arg name=\"path\" type=\"o\"/>\n" \
166 " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
167 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
168 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
169 " <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
170 " <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
171 " <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
172 " <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
173 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
174 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
175 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
178 #define INTROSPECTION_BEGIN \
179 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
181 BUS_MANAGER_INTERFACE \
182 BUS_PROPERTIES_INTERFACE \
184 BUS_INTROSPECTABLE_INTERFACE
186 #define INTROSPECTION_END \
189 #define INTERFACES_LIST \
190 BUS_GENERIC_INTERFACES_LIST \
191 "org.freedesktop.login1.Manager\0"
193 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
201 b = manager_get_idle_hint(m, NULL) > 0;
202 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
208 static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
217 manager_get_idle_hint(m, &t);
218 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
220 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
226 static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
227 Session *session = NULL;
229 const char *type, *class, *seat, *tty, *display, *remote_user, *remote_host, *service;
230 uint32_t uid, leader, audit_id = 0;
231 dbus_bool_t remote, kill_processes;
232 char **controllers = NULL, **reset_controllers = NULL;
236 DBusMessageIter iter;
241 DBusMessage *reply = NULL;
248 if (!dbus_message_iter_init(message, &iter) ||
249 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
252 dbus_message_iter_get_basic(&iter, &uid);
254 if (!dbus_message_iter_next(&iter) ||
255 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
258 dbus_message_iter_get_basic(&iter, &leader);
261 !dbus_message_iter_next(&iter) ||
262 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
265 dbus_message_iter_get_basic(&iter, &service);
267 if (!dbus_message_iter_next(&iter) ||
268 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
271 dbus_message_iter_get_basic(&iter, &type);
272 t = session_type_from_string(type);
275 !dbus_message_iter_next(&iter) ||
276 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
279 dbus_message_iter_get_basic(&iter, &class);
283 c = session_class_from_string(class);
286 !dbus_message_iter_next(&iter) ||
287 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
290 dbus_message_iter_get_basic(&iter, &seat);
295 s = hashmap_get(m->seats, seat);
300 if (!dbus_message_iter_next(&iter) ||
301 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
304 dbus_message_iter_get_basic(&iter, &vtnr);
306 if (!dbus_message_iter_next(&iter) ||
307 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
310 dbus_message_iter_get_basic(&iter, &tty);
312 if (tty_is_vc(tty)) {
317 else if (s != m->vtconsole)
320 v = vtnr_from_tty(tty);
323 return v < 0 ? v : -EINVAL;
327 else if (vtnr != (uint32_t) v)
330 } else if (!isempty(tty) && s && seat_is_vtconsole(s))
334 if (seat_can_multi_session(s)) {
335 if (vtnr <= 0 || vtnr > 63)
343 if (!dbus_message_iter_next(&iter) ||
344 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
347 dbus_message_iter_get_basic(&iter, &display);
349 if (!dbus_message_iter_next(&iter) ||
350 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
353 dbus_message_iter_get_basic(&iter, &remote);
355 if (!dbus_message_iter_next(&iter) ||
356 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
359 dbus_message_iter_get_basic(&iter, &remote_user);
361 if (!dbus_message_iter_next(&iter) ||
362 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
365 dbus_message_iter_get_basic(&iter, &remote_host);
367 if (!dbus_message_iter_next(&iter) ||
368 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
369 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
372 r = bus_parse_strv_iter(&iter, &controllers);
376 if (strv_contains(controllers, "systemd") ||
377 !dbus_message_iter_next(&iter) ||
378 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
379 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
384 r = bus_parse_strv_iter(&iter, &reset_controllers);
388 if (strv_contains(reset_controllers, "systemd") ||
389 !dbus_message_iter_next(&iter) ||
390 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
395 dbus_message_iter_get_basic(&iter, &kill_processes);
397 r = manager_add_user_by_uid(m, uid, &user);
401 audit_session_from_pid(leader, &audit_id);
404 asprintf(&id, "%lu", (unsigned long) audit_id);
411 session = hashmap_get(m->sessions, id);
416 fifo_fd = session_create_fifo(session);
422 /* Session already exists, client is probably
423 * something like "su" which changes uid but
424 * is still the same audit session */
426 reply = dbus_message_new_method_return(message);
432 p = session_bus_path(session);
438 seat = session->seat ? session->seat->id : "";
439 vtnr = session->vtnr;
440 b = dbus_message_append_args(
442 DBUS_TYPE_STRING, &session->id,
443 DBUS_TYPE_OBJECT_PATH, &p,
444 DBUS_TYPE_STRING, &session->user->runtime_path,
445 DBUS_TYPE_UNIX_FD, &fifo_fd,
446 DBUS_TYPE_STRING, &seat,
447 DBUS_TYPE_UINT32, &vtnr,
456 close_nointr_nofail(fifo_fd);
459 strv_free(controllers);
460 strv_free(reset_controllers);
468 asprintf(&id, "c%lu", ++m->session_counter);
475 } while (hashmap_get(m->sessions, id));
478 r = manager_add_session(m, user, id, &session);
483 session->leader = leader;
484 session->audit_id = audit_id;
487 session->remote = remote;
488 session->controllers = controllers;
489 session->reset_controllers = reset_controllers;
490 session->kill_processes = kill_processes;
491 session->vtnr = vtnr;
493 controllers = reset_controllers = NULL;
496 session->tty = strdup(tty);
503 if (!isempty(display)) {
504 session->display = strdup(display);
505 if (!session->display) {
511 if (!isempty(remote_user)) {
512 session->remote_user = strdup(remote_user);
513 if (!session->remote_user) {
519 if (!isempty(remote_host)) {
520 session->remote_host = strdup(remote_host);
521 if (!session->remote_host) {
527 if (!isempty(service)) {
528 session->service = strdup(service);
529 if (!session->service) {
535 fifo_fd = session_create_fifo(session);
542 r = seat_attach_session(s, session);
547 r = session_start(session);
551 reply = dbus_message_new_method_return(message);
557 p = session_bus_path(session);
563 seat = s ? s->id : "";
564 b = dbus_message_append_args(
566 DBUS_TYPE_STRING, &session->id,
567 DBUS_TYPE_OBJECT_PATH, &p,
568 DBUS_TYPE_STRING, &session->user->runtime_path,
569 DBUS_TYPE_UNIX_FD, &fifo_fd,
570 DBUS_TYPE_STRING, &seat,
571 DBUS_TYPE_UINT32, &vtnr,
580 close_nointr_nofail(fifo_fd);
586 strv_free(controllers);
587 strv_free(reset_controllers);
590 session_add_to_gc_queue(session);
593 user_add_to_gc_queue(user);
596 close_nointr_nofail(fifo_fd);
599 dbus_message_unref(reply);
604 static int trigger_device(Manager *m, struct udev_device *d) {
605 struct udev_enumerate *e;
606 struct udev_list_entry *first, *item;
611 e = udev_enumerate_new(m->udev);
618 if (udev_enumerate_add_match_parent(e, d) < 0) {
624 if (udev_enumerate_scan_devices(e) < 0) {
629 first = udev_enumerate_get_list_entry(e);
630 udev_list_entry_foreach(item, first) {
634 p = udev_list_entry_get_name(item);
636 t = strappend(p, "/uevent");
642 write_one_line_file(t, "change");
650 udev_enumerate_unref(e);
655 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
656 struct udev_device *d;
657 char *rule = NULL, *file = NULL;
658 const char *id_for_seat;
665 d = udev_device_new_from_syspath(m->udev, sysfs);
669 if (!udev_device_has_tag(d, "seat")) {
674 id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
680 if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
685 if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
690 mkdir_p("/etc/udev/rules.d", 0755);
691 r = write_one_line_file_atomic(file, rule);
695 r = trigger_device(m, d);
702 udev_device_unref(d);
707 static int flush_devices(Manager *m) {
712 d = opendir("/etc/udev/rules.d");
715 log_warning("Failed to open /etc/udev/rules.d: %m");
719 while ((de = readdir(d))) {
721 if (!dirent_is_file(de))
724 if (!startswith(de->d_name, "72-seat-"))
727 if (!endswith(de->d_name, ".rules"))
730 if (unlinkat(dirfd(d), de->d_name, 0) < 0)
731 log_warning("Failed to unlink %s: %m", de->d_name);
737 return trigger_device(m, NULL);
740 static int have_multiple_sessions(
741 DBusConnection *connection,
743 DBusMessage *message,
750 if (hashmap_size(m->sessions) > 1)
753 /* Hmm, there's only one session, but let's make sure it
754 * actually belongs to the user who is asking. If not, better
755 * be safe than sorry. */
757 s = hashmap_first(m->sessions);
761 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
762 if (ul == (unsigned long) -1)
765 return s->user->uid != ul;
771 static const BusProperty bus_login_manager_properties[] = {
772 { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true },
773 { "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
774 { "ResetControllers", bus_property_append_strv, "as", offsetof(Manager, reset_controllers), true },
775 { "NAutoVTs", bus_property_append_unsigned, "u", offsetof(Manager, n_autovts) },
776 { "KillOnlyUsers", bus_property_append_strv, "as", offsetof(Manager, kill_only_users), true },
777 { "KillExcludeUsers", bus_property_append_strv, "as", offsetof(Manager, kill_exclude_users), true },
778 { "KillUserProcesses", bus_property_append_bool, "b", offsetof(Manager, kill_user_processes) },
779 { "IdleHint", bus_manager_append_idle_hint, "b", 0 },
780 { "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 },
781 { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 },
785 static DBusHandlerResult manager_message_handler(
786 DBusConnection *connection,
787 DBusMessage *message,
790 Manager *m = userdata;
793 DBusMessage *reply = NULL;
800 dbus_error_init(&error);
802 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
808 if (!dbus_message_get_args(
811 DBUS_TYPE_STRING, &name,
813 return bus_send_error_reply(connection, message, &error, -EINVAL);
815 session = hashmap_get(m->sessions, name);
817 return bus_send_error_reply(connection, message, &error, -ENOENT);
819 reply = dbus_message_new_method_return(message);
823 p = session_bus_path(session);
827 b = dbus_message_append_args(
829 DBUS_TYPE_OBJECT_PATH, &p,
836 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) {
842 if (!dbus_message_get_args(
845 DBUS_TYPE_UINT32, &pid,
847 return bus_send_error_reply(connection, message, &error, -EINVAL);
849 r = manager_get_session_by_pid(m, pid, &session);
851 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
853 reply = dbus_message_new_method_return(message);
857 p = session_bus_path(session);
861 b = dbus_message_append_args(
863 DBUS_TYPE_OBJECT_PATH, &p,
870 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
876 if (!dbus_message_get_args(
879 DBUS_TYPE_UINT32, &uid,
881 return bus_send_error_reply(connection, message, &error, -EINVAL);
883 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
885 return bus_send_error_reply(connection, message, &error, -ENOENT);
887 reply = dbus_message_new_method_return(message);
891 p = user_bus_path(user);
895 b = dbus_message_append_args(
897 DBUS_TYPE_OBJECT_PATH, &p,
904 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
910 if (!dbus_message_get_args(
913 DBUS_TYPE_STRING, &name,
915 return bus_send_error_reply(connection, message, &error, -EINVAL);
917 seat = hashmap_get(m->seats, name);
919 return bus_send_error_reply(connection, message, &error, -ENOENT);
921 reply = dbus_message_new_method_return(message);
925 p = seat_bus_path(seat);
929 b = dbus_message_append_args(
931 DBUS_TYPE_OBJECT_PATH, &p,
938 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
942 DBusMessageIter iter, sub;
943 const char *empty = "";
945 reply = dbus_message_new_method_return(message);
949 dbus_message_iter_init_append(reply, &iter);
951 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
954 HASHMAP_FOREACH(session, m->sessions, i) {
955 DBusMessageIter sub2;
958 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
961 uid = session->user->uid;
963 p = session_bus_path(session);
967 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
968 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
969 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
970 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
971 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
978 if (!dbus_message_iter_close_container(&sub, &sub2))
982 if (!dbus_message_iter_close_container(&iter, &sub))
985 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
989 DBusMessageIter iter, sub;
991 reply = dbus_message_new_method_return(message);
995 dbus_message_iter_init_append(reply, &iter);
997 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
1000 HASHMAP_FOREACH(user, m->users, i) {
1001 DBusMessageIter sub2;
1004 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1009 p = user_bus_path(user);
1013 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1014 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
1015 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1022 if (!dbus_message_iter_close_container(&sub, &sub2))
1026 if (!dbus_message_iter_close_container(&iter, &sub))
1029 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
1033 DBusMessageIter iter, sub;
1035 reply = dbus_message_new_method_return(message);
1039 dbus_message_iter_init_append(reply, &iter);
1041 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
1044 HASHMAP_FOREACH(seat, m->seats, i) {
1045 DBusMessageIter sub2;
1047 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1050 p = seat_bus_path(seat);
1054 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
1055 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1062 if (!dbus_message_iter_close_container(&sub, &sub2))
1066 if (!dbus_message_iter_close_container(&iter, &sub))
1069 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
1071 r = bus_manager_create_session(m, message, &reply);
1073 /* Don't delay the work on OOM here, since it might be
1074 * triggered by a low RLIMIT_NOFILE here (since we
1075 * send a dupped fd to the client), and we'd rather
1076 * see this fail quickly then be retried later */
1079 return bus_send_error_reply(connection, message, &error, r);
1081 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ReleaseSession")) {
1085 if (!dbus_message_get_args(
1088 DBUS_TYPE_STRING, &name,
1090 return bus_send_error_reply(connection, message, &error, -EINVAL);
1092 session = hashmap_get(m->sessions, name);
1094 return bus_send_error_reply(connection, message, &error, -ENOENT);
1096 /* We use the FIFO to detect stray sessions where the
1097 process invoking PAM dies abnormally. We need to make
1098 sure that that process is not killed if at the clean
1099 end of the session it closes the FIFO. Hence, with
1100 this call explicitly turn off the FIFO logic, so that
1101 the PAM code can finish clean up on its own */
1102 session_remove_fifo(session);
1104 reply = dbus_message_new_method_return(message);
1108 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
1112 if (!dbus_message_get_args(
1115 DBUS_TYPE_STRING, &name,
1117 return bus_send_error_reply(connection, message, &error, -EINVAL);
1119 session = hashmap_get(m->sessions, name);
1121 return bus_send_error_reply(connection, message, &error, -ENOENT);
1123 r = session_activate(session);
1125 return bus_send_error_reply(connection, message, NULL, r);
1127 reply = dbus_message_new_method_return(message);
1131 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) {
1132 const char *session_name, *seat_name;
1136 /* Same as ActivateSession() but refuses to work if
1137 * the seat doesn't match */
1139 if (!dbus_message_get_args(
1142 DBUS_TYPE_STRING, &session_name,
1143 DBUS_TYPE_STRING, &seat_name,
1145 return bus_send_error_reply(connection, message, &error, -EINVAL);
1147 session = hashmap_get(m->sessions, session_name);
1149 return bus_send_error_reply(connection, message, &error, -ENOENT);
1151 seat = hashmap_get(m->seats, seat_name);
1153 return bus_send_error_reply(connection, message, &error, -ENOENT);
1155 if (session->seat != seat)
1156 return bus_send_error_reply(connection, message, &error, -EINVAL);
1158 r = session_activate(session);
1160 return bus_send_error_reply(connection, message, NULL, r);
1162 reply = dbus_message_new_method_return(message);
1166 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") ||
1167 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) {
1171 if (!dbus_message_get_args(
1174 DBUS_TYPE_STRING, &name,
1176 return bus_send_error_reply(connection, message, &error, -EINVAL);
1178 session = hashmap_get(m->sessions, name);
1180 return bus_send_error_reply(connection, message, &error, -ENOENT);
1182 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
1185 reply = dbus_message_new_method_return(message);
1189 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) {
1196 if (!dbus_message_get_args(
1199 DBUS_TYPE_STRING, &name,
1200 DBUS_TYPE_STRING, &swho,
1201 DBUS_TYPE_INT32, &signo,
1203 return bus_send_error_reply(connection, message, &error, -EINVAL);
1208 who = kill_who_from_string(swho);
1210 return bus_send_error_reply(connection, message, &error, -EINVAL);
1213 if (signo <= 0 || signo >= _NSIG)
1214 return bus_send_error_reply(connection, message, &error, -EINVAL);
1216 session = hashmap_get(m->sessions, name);
1218 return bus_send_error_reply(connection, message, &error, -ENOENT);
1220 r = session_kill(session, who, signo);
1222 return bus_send_error_reply(connection, message, NULL, r);
1224 reply = dbus_message_new_method_return(message);
1228 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) {
1233 if (!dbus_message_get_args(
1236 DBUS_TYPE_UINT32, &uid,
1237 DBUS_TYPE_INT32, &signo,
1239 return bus_send_error_reply(connection, message, &error, -EINVAL);
1241 if (signo <= 0 || signo >= _NSIG)
1242 return bus_send_error_reply(connection, message, &error, -EINVAL);
1244 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1246 return bus_send_error_reply(connection, message, &error, -ENOENT);
1248 r = user_kill(user, signo);
1250 return bus_send_error_reply(connection, message, NULL, r);
1252 reply = dbus_message_new_method_return(message);
1256 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
1260 if (!dbus_message_get_args(
1263 DBUS_TYPE_STRING, &name,
1265 return bus_send_error_reply(connection, message, &error, -EINVAL);
1267 session = hashmap_get(m->sessions, name);
1269 return bus_send_error_reply(connection, message, &error, -ENOENT);
1271 r = session_stop(session);
1273 return bus_send_error_reply(connection, message, NULL, r);
1275 reply = dbus_message_new_method_return(message);
1279 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1283 if (!dbus_message_get_args(
1286 DBUS_TYPE_UINT32, &uid,
1288 return bus_send_error_reply(connection, message, &error, -EINVAL);
1290 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1292 return bus_send_error_reply(connection, message, &error, -ENOENT);
1294 r = user_stop(user);
1296 return bus_send_error_reply(connection, message, NULL, r);
1298 reply = dbus_message_new_method_return(message);
1302 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1306 if (!dbus_message_get_args(
1309 DBUS_TYPE_STRING, &name,
1311 return bus_send_error_reply(connection, message, &error, -EINVAL);
1313 seat = hashmap_get(m->seats, name);
1315 return bus_send_error_reply(connection, message, &error, -ENOENT);
1317 r = seat_stop_sessions(seat);
1319 return bus_send_error_reply(connection, message, NULL, r);
1321 reply = dbus_message_new_method_return(message);
1325 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1328 dbus_bool_t b, interactive;
1331 if (!dbus_message_get_args(
1334 DBUS_TYPE_UINT32, &uid,
1335 DBUS_TYPE_BOOLEAN, &b,
1336 DBUS_TYPE_BOOLEAN, &interactive,
1338 return bus_send_error_reply(connection, message, &error, -EINVAL);
1343 return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
1345 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, NULL, &error);
1347 return bus_send_error_reply(connection, message, &error, r);
1349 mkdir_p("/var/lib/systemd", 0755);
1351 r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0);
1353 return bus_send_error_reply(connection, message, &error, r);
1355 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
1366 return bus_send_error_reply(connection, message, &error, r);
1368 if (manager_add_user_by_uid(m, uid, &u) >= 0)
1377 if (r < 0 && errno != ENOENT)
1378 return bus_send_error_reply(connection, message, &error, -errno);
1380 u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1382 user_add_to_gc_queue(u);
1385 reply = dbus_message_new_method_return(message);
1389 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
1390 const char *sysfs, *seat;
1391 dbus_bool_t interactive;
1393 if (!dbus_message_get_args(
1396 DBUS_TYPE_STRING, &seat,
1397 DBUS_TYPE_STRING, &sysfs,
1398 DBUS_TYPE_BOOLEAN, &interactive,
1400 return bus_send_error_reply(connection, message, &error, -EINVAL);
1402 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
1403 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1405 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, NULL, &error);
1407 return bus_send_error_reply(connection, message, &error, r);
1409 r = attach_device(m, seat, sysfs);
1411 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1413 reply = dbus_message_new_method_return(message);
1418 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
1419 dbus_bool_t interactive;
1421 if (!dbus_message_get_args(
1424 DBUS_TYPE_BOOLEAN, &interactive,
1426 return bus_send_error_reply(connection, message, &error, -EINVAL);
1428 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, NULL, &error);
1430 return bus_send_error_reply(connection, message, &error, r);
1432 r = flush_devices(m);
1434 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1436 reply = dbus_message_new_method_return(message);
1440 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
1441 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
1442 dbus_bool_t interactive;
1443 bool multiple_sessions;
1444 DBusMessage *forward, *freply;
1446 const char *mode = "replace";
1449 if (!dbus_message_get_args(
1452 DBUS_TYPE_BOOLEAN, &interactive,
1454 return bus_send_error_reply(connection, message, &error, -EINVAL);
1456 r = have_multiple_sessions(connection, m, message, &error);
1458 return bus_send_error_reply(connection, message, &error, r);
1460 multiple_sessions = r > 0;
1462 if (streq(dbus_message_get_member(message), "PowerOff")) {
1463 if (multiple_sessions)
1464 action = "org.freedesktop.login1.power-off-multiple-sessions";
1466 action = "org.freedesktop.login1.power-off";
1468 name = SPECIAL_POWEROFF_TARGET;
1470 if (multiple_sessions)
1471 action = "org.freedesktop.login1.reboot-multiple-sessions";
1473 action = "org.freedesktop.login1.reboot";
1475 name = SPECIAL_REBOOT_TARGET;
1478 r = verify_polkit(connection, message, action, interactive, NULL, &error);
1480 return bus_send_error_reply(connection, message, &error, r);
1482 forward = dbus_message_new_method_call(
1483 "org.freedesktop.systemd1",
1484 "/org/freedesktop/systemd1",
1485 "org.freedesktop.systemd1.Manager",
1488 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1490 if (!dbus_message_append_args(forward,
1491 DBUS_TYPE_STRING, &name,
1492 DBUS_TYPE_STRING, &mode,
1493 DBUS_TYPE_INVALID)) {
1494 dbus_message_unref(forward);
1495 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1498 freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error);
1499 dbus_message_unref(forward);
1502 return bus_send_error_reply(connection, message, &error, -EIO);
1504 dbus_message_unref(freply);
1506 reply = dbus_message_new_method_return(message);
1510 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff") ||
1511 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
1513 bool multiple_sessions, challenge, b;
1514 const char *t, *action;
1516 r = have_multiple_sessions(connection, m, message, &error);
1518 return bus_send_error_reply(connection, message, &error, r);
1520 multiple_sessions = r > 0;
1522 if (streq(dbus_message_get_member(message), "CanPowerOff")) {
1523 if (multiple_sessions)
1524 action = "org.freedesktop.login1.power-off-multiple-sessions";
1526 action = "org.freedesktop.login1.power-off";
1529 if (multiple_sessions)
1530 action = "org.freedesktop.login1.reboot-multiple-sessions";
1532 action = "org.freedesktop.login1.reboot";
1535 r = verify_polkit(connection, message, action, false, &challenge, &error);
1537 return bus_send_error_reply(connection, message, &error, r);
1539 reply = dbus_message_new_method_return(message);
1544 challenge ? "challenge" :
1547 b = dbus_message_append_args(
1549 DBUS_TYPE_STRING, &t,
1554 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1555 char *introspection = NULL;
1564 if (!(reply = dbus_message_new_method_return(message)))
1567 /* We roll our own introspection code here, instead of
1568 * relying on bus_default_message_handler() because we
1569 * need to generate our introspection string
1572 if (!(f = open_memstream(&introspection, &size)))
1575 fputs(INTROSPECTION_BEGIN, f);
1577 HASHMAP_FOREACH(seat, m->seats, i) {
1578 p = bus_path_escape(seat->id);
1581 fprintf(f, "<node name=\"seat/%s\"/>", p);
1586 HASHMAP_FOREACH(user, m->users, i)
1587 fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
1589 HASHMAP_FOREACH(session, m->sessions, i) {
1590 p = bus_path_escape(session->id);
1593 fprintf(f, "<node name=\"session/%s\"/>", p);
1598 fputs(INTROSPECTION_END, f);
1602 free(introspection);
1611 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1612 free(introspection);
1616 free(introspection);
1618 const BusBoundProperties bps[] = {
1619 { "org.freedesktop.login1.Manager", bus_login_manager_properties, m },
1622 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1626 if (!dbus_connection_send(connection, reply, NULL))
1629 dbus_message_unref(reply);
1632 return DBUS_HANDLER_RESULT_HANDLED;
1636 dbus_message_unref(reply);
1638 dbus_error_free(&error);
1640 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1643 const DBusObjectPathVTable bus_manager_vtable = {
1644 .message_function = manager_message_handler
1647 DBusHandlerResult bus_message_filter(
1648 DBusConnection *connection,
1649 DBusMessage *message,
1652 Manager *m = userdata;
1659 dbus_error_init(&error);
1661 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
1664 if (!dbus_message_get_args(message, &error,
1665 DBUS_TYPE_STRING, &cgroup,
1667 log_error("Failed to parse Released message: %s", bus_error_message(&error));
1669 manager_cgroup_notify_empty(m, cgroup);
1672 dbus_error_free(&error);
1674 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1677 int manager_send_changed(Manager *manager, const char *properties) {
1683 m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
1687 if (!dbus_connection_send(manager->bus, m, NULL))
1694 dbus_message_unref(m);