chiark / gitweb /
a50db630a0f42db5077cc2d2d189febde26e768e
[elogind.git] / src / login / logind-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <pwd.h>
26
27 #include "logind.h"
28 #include "dbus-common.h"
29 #include "strv.h"
30 #include "polkit.h"
31 #include "special.h"
32
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"     \
38         "  </method>\n"                                                 \
39         "  <method name=\"GetSessionByPID\">\n"                         \
40         "   <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n"          \
41         "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
42         "  </method>\n"                                                 \
43         "  <method name=\"GetUser\">\n"                                 \
44         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
45         "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
46         "  </method>\n"                                                 \
47         "  <method name=\"GetSeat\">\n"                                 \
48         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
49         "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
50         "  </method>\n"                                                 \
51         "  <method name=\"ListSessions\">\n"                            \
52         "   <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
53         "  </method>\n"                                                 \
54         "  <method name=\"ListUsers\">\n"                               \
55         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
56         "  </method>\n"                                                 \
57         "  <method name=\"ListSeats\">\n"                               \
58         "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
59         "  </method>\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=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
66         "   <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n"         \
67         "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
68         "   <arg name=\"display\" type=\"s\" direction=\"in\"/>\n"      \
69         "   <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n"       \
70         "   <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n"  \
71         "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
72         "   <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
73         "   <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
74         "   <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
75         "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
76         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
77         "   <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
78         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
79         "   <arg name=\"seat\" type=\"s\" direction=\"out\"/>\n"        \
80         "   <arg name=\"vtnr\" type=\"u\" direction=\"out\"/>\n"        \
81         "  </method>\n"                                                 \
82         "  <method name=\"ActivateSession\">\n"                         \
83         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
84         "  </method>\n"                                                 \
85         "  <method name=\"ActivateSessionOnSeat\">\n"                   \
86         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
87         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
88         "  </method>\n"                                                 \
89         "  <method name=\"LockSession\">\n"                             \
90         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
91         "  </method>\n"                                                 \
92         "  <method name=\"UnlockSession\">\n"                           \
93         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
94         "  </method>\n"                                                 \
95         "  <method name=\"KillSession\">\n"                             \
96         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
97         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
98         "   <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n"       \
99         "  </method>\n"                                                 \
100         "  <method name=\"KillUser\">\n"                                \
101         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
102         "   <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n"       \
103         "  </method>\n"                                                 \
104         "  <method name=\"TerminateSession\">\n"                        \
105         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
106         "  </method>\n"                                                 \
107         "  <method name=\"TerminateUser\">\n"                           \
108         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
109         "  </method>\n"                                                 \
110         "  <method name=\"TerminateSeat\">\n"                           \
111         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
112         "  </method>\n"                                                 \
113         "  <method name=\"SetUserLinger\">\n"                           \
114         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
115         "   <arg name=\"b\" type=\"b\" direction=\"in\"/>\n"            \
116         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
117         "  </method>\n"                                                 \
118         "  <method name=\"AttachDevice\">\n"                            \
119         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
120         "   <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n"        \
121         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
122         "  </method>\n"                                                 \
123         "  <method name=\"FlushDevices\">\n"                            \
124         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
125         "  </method>\n"                                                 \
126         "  <method name=\"PowerOff\">\n"                                \
127         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
128         "  </method>\n"                                                 \
129         "  <method name=\"Reboot\">\n"                                  \
130         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
131         "  </method>\n"                                                 \
132         "  <signal name=\"SessionNew\">\n"                              \
133         "   <arg name=\"id\" type=\"s\"/>\n"                            \
134         "   <arg name=\"path\" type=\"o\"/>\n"                          \
135         "  </signal>\n"                                                 \
136         "  <signal name=\"SessionRemoved\">\n"                          \
137         "   <arg name=\"id\" type=\"s\"/>\n"                            \
138         "   <arg name=\"path\" type=\"o\"/>\n"                          \
139         "  </signal>\n"                                                 \
140         "  <signal name=\"UserNew\">\n"                                 \
141         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
142         "   <arg name=\"path\" type=\"o\"/>\n"                          \
143         "  </signal>\n"                                                 \
144         "  <signal name=\"UserRemoved\">\n"                             \
145         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
146         "   <arg name=\"path\" type=\"o\"/>\n"                          \
147         "  </signal>\n"                                                 \
148         "  <signal name=\"SeatNew\">\n"                                 \
149         "   <arg name=\"id\" type=\"s\"/>\n"                            \
150         "   <arg name=\"path\" type=\"o\"/>\n"                          \
151         "  </signal>\n"                                                 \
152         "  <signal name=\"SeatRemoved\">\n"                             \
153         "   <arg name=\"id\" type=\"s\"/>\n"                            \
154         "   <arg name=\"path\" type=\"o\"/>\n"                          \
155         "  </signal>\n"                                                 \
156         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
157         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
158         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
159         "  <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
160         "  <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
161         "  <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
162         "  <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
163         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
164         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
165         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
166         " </interface>\n"
167
168 #define INTROSPECTION_BEGIN                                             \
169         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
170         "<node>\n"                                                      \
171         BUS_MANAGER_INTERFACE                                           \
172         BUS_PROPERTIES_INTERFACE                                        \
173         BUS_PEER_INTERFACE                                              \
174         BUS_INTROSPECTABLE_INTERFACE
175
176 #define INTROSPECTION_END                                               \
177         "</node>\n"
178
179 #define INTERFACES_LIST                              \
180         BUS_GENERIC_INTERFACES_LIST                  \
181         "org.freedesktop.login1.Manager\0"
182
183 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
184         Manager *m = data;
185         dbus_bool_t b;
186
187         assert(i);
188         assert(property);
189         assert(m);
190
191         b = manager_get_idle_hint(m, NULL) > 0;
192         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
193                 return -ENOMEM;
194
195         return 0;
196 }
197
198 static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
199         Manager *m = data;
200         dual_timestamp t;
201         uint64_t u;
202
203         assert(i);
204         assert(property);
205         assert(m);
206
207         manager_get_idle_hint(m, &t);
208         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
209
210         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
211                 return -ENOMEM;
212
213         return 0;
214 }
215
216 static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
217         Session *session = NULL;
218         User *user = NULL;
219         const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service;
220         uint32_t uid, leader, audit_id = 0;
221         dbus_bool_t remote, kill_processes;
222         char **controllers = NULL, **reset_controllers = NULL;
223         SessionType t;
224         Seat *s;
225         DBusMessageIter iter;
226         int r;
227         char *id = NULL, *p;
228         uint32_t vtnr = 0;
229         int fifo_fd = -1;
230         DBusMessage *reply = NULL;
231         bool b;
232
233         assert(m);
234         assert(message);
235         assert(_reply);
236
237         if (!dbus_message_iter_init(message, &iter) ||
238             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
239                 return -EINVAL;
240
241         dbus_message_iter_get_basic(&iter, &uid);
242
243         if (!dbus_message_iter_next(&iter) ||
244             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
245                 return -EINVAL;
246
247         dbus_message_iter_get_basic(&iter, &leader);
248
249         if (leader <= 0 ||
250             !dbus_message_iter_next(&iter) ||
251             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
252                 return -EINVAL;
253
254         dbus_message_iter_get_basic(&iter, &service);
255
256         if (!dbus_message_iter_next(&iter) ||
257             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
258                 return -EINVAL;
259
260         dbus_message_iter_get_basic(&iter, &type);
261         t = session_type_from_string(type);
262
263         if (t < 0 ||
264             !dbus_message_iter_next(&iter) ||
265             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
266                 return -EINVAL;
267
268         dbus_message_iter_get_basic(&iter, &seat);
269
270         if (isempty(seat))
271                 s = NULL;
272         else {
273                 s = hashmap_get(m->seats, seat);
274                 if (!s)
275                         return -ENOENT;
276         }
277
278         if (!dbus_message_iter_next(&iter) ||
279             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
280                 return -EINVAL;
281
282         dbus_message_iter_get_basic(&iter, &vtnr);
283
284         if (!dbus_message_iter_next(&iter) ||
285             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
286                 return -EINVAL;
287
288         dbus_message_iter_get_basic(&iter, &tty);
289
290         if (tty_is_vc(tty)) {
291                 int v;
292
293                 if (!s)
294                         s = m->vtconsole;
295                 else if (s != m->vtconsole)
296                         return -EINVAL;
297
298                 v = vtnr_from_tty(tty);
299
300                 if (v <= 0)
301                         return v < 0 ? v : -EINVAL;
302
303                 if (vtnr <= 0)
304                         vtnr = (uint32_t) v;
305                 else if (vtnr != (uint32_t) v)
306                         return -EINVAL;
307
308         } else if (!isempty(tty) && s && seat_is_vtconsole(s))
309                 return -EINVAL;
310
311         if (s) {
312                 if (seat_can_multi_session(s)) {
313                         if (vtnr <= 0 || vtnr > 63)
314                                 return -EINVAL;
315                 } else {
316                         if (vtnr > 0)
317                                 return -EINVAL;
318                 }
319         }
320
321         if (!dbus_message_iter_next(&iter) ||
322             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
323                 return -EINVAL;
324
325         dbus_message_iter_get_basic(&iter, &display);
326
327         if (!dbus_message_iter_next(&iter) ||
328             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
329                 return -EINVAL;
330
331         dbus_message_iter_get_basic(&iter, &remote);
332
333         if (!dbus_message_iter_next(&iter) ||
334             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
335                 return -EINVAL;
336
337         dbus_message_iter_get_basic(&iter, &remote_user);
338
339         if (!dbus_message_iter_next(&iter) ||
340             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
341                 return -EINVAL;
342
343         dbus_message_iter_get_basic(&iter, &remote_host);
344
345         if (!dbus_message_iter_next(&iter) ||
346             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
347             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
348                 return -EINVAL;
349
350         r = bus_parse_strv_iter(&iter, &controllers);
351         if (r < 0)
352                 return -EINVAL;
353
354         if (strv_contains(controllers, "systemd") ||
355             !dbus_message_iter_next(&iter) ||
356             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
357             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
358                 r = -EINVAL;
359                 goto fail;
360         }
361
362         r = bus_parse_strv_iter(&iter, &reset_controllers);
363         if (r < 0)
364                 goto fail;
365
366         if (strv_contains(reset_controllers, "systemd") ||
367             !dbus_message_iter_next(&iter) ||
368             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
369                 r = -EINVAL;
370                 goto fail;
371         }
372
373         dbus_message_iter_get_basic(&iter, &kill_processes);
374
375         r = manager_add_user_by_uid(m, uid, &user);
376         if (r < 0)
377                 goto fail;
378
379         audit_session_from_pid(leader, &audit_id);
380
381         if (audit_id > 0) {
382                 asprintf(&id, "%lu", (unsigned long) audit_id);
383
384                 if (!id) {
385                         r = -ENOMEM;
386                         goto fail;
387                 }
388
389                 session = hashmap_get(m->sessions, id);
390
391                 if (session) {
392                         free(id);
393
394                         fifo_fd = session_create_fifo(session);
395                         if (fifo_fd < 0) {
396                                 r = fifo_fd;
397                                 goto fail;
398                         }
399
400                         /* Session already exists, client is probably
401                          * something like "su" which changes uid but
402                          * is still the same audit session */
403
404                         reply = dbus_message_new_method_return(message);
405                         if (!reply) {
406                                 r = -ENOMEM;
407                                 goto fail;
408                         }
409
410                         p = session_bus_path(session);
411                         if (!p) {
412                                 r = -ENOMEM;
413                                 goto fail;
414                         }
415
416                         seat = session->seat ? session->seat->id : "";
417                         vtnr = session->vtnr;
418                         b = dbus_message_append_args(
419                                         reply,
420                                         DBUS_TYPE_STRING, &session->id,
421                                         DBUS_TYPE_OBJECT_PATH, &p,
422                                         DBUS_TYPE_STRING, &session->user->runtime_path,
423                                         DBUS_TYPE_UNIX_FD, &fifo_fd,
424                                         DBUS_TYPE_STRING, &seat,
425                                         DBUS_TYPE_UINT32, &vtnr,
426                                         DBUS_TYPE_INVALID);
427                         free(p);
428
429                         if (!b) {
430                                 r = -ENOMEM;
431                                 goto fail;
432                         }
433
434                         close_nointr_nofail(fifo_fd);
435                         *_reply = reply;
436
437                         strv_free(controllers);
438                         strv_free(reset_controllers);
439
440                         return 0;
441                 }
442
443         } else {
444                 do {
445                         free(id);
446                         asprintf(&id, "c%lu", ++m->session_counter);
447
448                         if (!id) {
449                                 r = -ENOMEM;
450                                 goto fail;
451                         }
452
453                 } while (hashmap_get(m->sessions, id));
454         }
455
456         r = manager_add_session(m, user, id, &session);
457         free(id);
458         if (r < 0)
459                 goto fail;
460
461         session->leader = leader;
462         session->audit_id = audit_id;
463         session->type = t;
464         session->remote = remote;
465         session->controllers = controllers;
466         session->reset_controllers = reset_controllers;
467         session->kill_processes = kill_processes;
468         session->vtnr = vtnr;
469
470         controllers = reset_controllers = NULL;
471
472         if (!isempty(tty)) {
473                 session->tty = strdup(tty);
474                 if (!session->tty) {
475                         r = -ENOMEM;
476                         goto fail;
477                 }
478         }
479
480         if (!isempty(display)) {
481                 session->display = strdup(display);
482                 if (!session->display) {
483                         r = -ENOMEM;
484                         goto fail;
485                 }
486         }
487
488         if (!isempty(remote_user)) {
489                 session->remote_user = strdup(remote_user);
490                 if (!session->remote_user) {
491                         r = -ENOMEM;
492                         goto fail;
493                 }
494         }
495
496         if (!isempty(remote_host)) {
497                 session->remote_host = strdup(remote_host);
498                 if (!session->remote_host) {
499                         r = -ENOMEM;
500                         goto fail;
501                 }
502         }
503
504         if (!isempty(service)) {
505                 session->service = strdup(service);
506                 if (!session->service) {
507                         r = -ENOMEM;
508                         goto fail;
509                 }
510         }
511
512         fifo_fd = session_create_fifo(session);
513         if (fifo_fd < 0) {
514                 r = fifo_fd;
515                 goto fail;
516         }
517
518         if (s) {
519                 r = seat_attach_session(s, session);
520                 if (r < 0)
521                         goto fail;
522         }
523
524         r = session_start(session);
525         if (r < 0)
526                 goto fail;
527
528         reply = dbus_message_new_method_return(message);
529         if (!reply) {
530                 r = -ENOMEM;
531                 goto fail;
532         }
533
534         p = session_bus_path(session);
535         if (!p) {
536                 r = -ENOMEM;
537                 goto fail;
538         }
539
540         seat = s ? s->id : "";
541         b = dbus_message_append_args(
542                         reply,
543                         DBUS_TYPE_STRING, &session->id,
544                         DBUS_TYPE_OBJECT_PATH, &p,
545                         DBUS_TYPE_STRING, &session->user->runtime_path,
546                         DBUS_TYPE_UNIX_FD, &fifo_fd,
547                         DBUS_TYPE_STRING, &seat,
548                         DBUS_TYPE_UINT32, &vtnr,
549                         DBUS_TYPE_INVALID);
550         free(p);
551
552         if (!b) {
553                 r = -ENOMEM;
554                 goto fail;
555         }
556
557         close_nointr_nofail(fifo_fd);
558         *_reply = reply;
559
560         return 0;
561
562 fail:
563         strv_free(controllers);
564         strv_free(reset_controllers);
565
566         if (session)
567                 session_add_to_gc_queue(session);
568
569         if (user)
570                 user_add_to_gc_queue(user);
571
572         if (fifo_fd >= 0)
573                 close_nointr_nofail(fifo_fd);
574
575         if (reply)
576                 dbus_message_unref(reply);
577
578         return r;
579 }
580
581 static int trigger_device(Manager *m, struct udev_device *d) {
582         struct udev_enumerate *e;
583         struct udev_list_entry *first, *item;
584         int r;
585
586         assert(m);
587
588         e = udev_enumerate_new(m->udev);
589         if (!e) {
590                 r = -ENOMEM;
591                 goto finish;
592         }
593
594         if (d) {
595                 if (udev_enumerate_add_match_parent(e, d) < 0) {
596                         r = -EIO;
597                         goto finish;
598                 }
599         }
600
601         if (udev_enumerate_scan_devices(e) < 0) {
602                 r = -EIO;
603                 goto finish;
604         }
605
606         first = udev_enumerate_get_list_entry(e);
607         udev_list_entry_foreach(item, first) {
608                 char *t;
609                 const char *p;
610
611                 p = udev_list_entry_get_name(item);
612
613                 t = strappend(p, "/uevent");
614                 if (!t) {
615                         r = -ENOMEM;
616                         goto finish;
617                 }
618
619                 write_one_line_file(t, "change");
620                 free(t);
621         }
622
623         r = 0;
624
625 finish:
626         if (e)
627                 udev_enumerate_unref(e);
628
629         return r;
630 }
631
632 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
633         struct udev_device *d;
634         char *rule = NULL, *file = NULL;
635         const char *id_for_seat;
636         int r;
637
638         assert(m);
639         assert(seat);
640         assert(sysfs);
641
642         d = udev_device_new_from_syspath(m->udev, sysfs);
643         if (!d)
644                 return -ENODEV;
645
646         if (!udev_device_has_tag(d, "seat")) {
647                 r = -ENODEV;
648                 goto finish;
649         }
650
651         id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
652         if (!id_for_seat) {
653                 r = -ENODEV;
654                 goto finish;
655         }
656
657         if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
658                 r = -ENOMEM;
659                 goto finish;
660         }
661
662         if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
663                 r = -ENOMEM;
664                 goto finish;
665         }
666
667         mkdir_p("/etc/udev/rules.d", 0755);
668         r = write_one_line_file_atomic(file, rule);
669         if (r < 0)
670                 goto finish;
671
672         r = trigger_device(m, d);
673
674 finish:
675         free(rule);
676         free(file);
677
678         if (d)
679                 udev_device_unref(d);
680
681         return r;
682 }
683
684 static int flush_devices(Manager *m) {
685         DIR *d;
686
687         assert(m);
688
689         d = opendir("/etc/udev/rules.d");
690         if (!d) {
691                 if (errno != ENOENT)
692                         log_warning("Failed to open /etc/udev/rules.d: %m");
693         } else {
694                 struct dirent *de;
695
696                 while ((de = readdir(d))) {
697
698                         if (!dirent_is_file(de))
699                                 continue;
700
701                         if (!startswith(de->d_name, "72-seat-"))
702                                 continue;
703
704                         if (!endswith(de->d_name, ".rules"))
705                                 continue;
706
707                         if (unlinkat(dirfd(d), de->d_name, 0) < 0)
708                                 log_warning("Failed to unlink %s: %m", de->d_name);
709                 }
710
711                 closedir(d);
712         }
713
714         return trigger_device(m, NULL);
715 }
716
717 static const BusProperty bus_login_manager_properties[] = {
718         { "ControlGroupHierarchy",  bus_property_append_string,         "s",  offsetof(Manager, cgroup_path),        true },
719         { "Controllers",            bus_property_append_strv,           "as", offsetof(Manager, controllers),        true },
720         { "ResetControllers",       bus_property_append_strv,           "as", offsetof(Manager, reset_controllers),  true },
721         { "NAutoVTs",               bus_property_append_unsigned,       "u",  offsetof(Manager, n_autovts)           },
722         { "KillOnlyUsers",          bus_property_append_strv,           "as", offsetof(Manager, kill_only_users),    true },
723         { "KillExcludeUsers",       bus_property_append_strv,           "as", offsetof(Manager, kill_exclude_users), true },
724         { "KillUserProcesses",      bus_property_append_bool,           "b",  offsetof(Manager, kill_user_processes) },
725         { "IdleHint",               bus_manager_append_idle_hint,       "b",  0 },
726         { "IdleSinceHint",          bus_manager_append_idle_hint_since, "t",  0 },
727         { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t",  0 },
728         { NULL, }
729 };
730
731 static DBusHandlerResult manager_message_handler(
732                 DBusConnection *connection,
733                 DBusMessage *message,
734                 void *userdata) {
735
736         Manager *m = userdata;
737
738         DBusError error;
739         DBusMessage *reply = NULL;
740         int r;
741
742         assert(connection);
743         assert(message);
744         assert(m);
745
746         dbus_error_init(&error);
747
748         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
749                 const char *name;
750                 char *p;
751                 Session *session;
752                 bool b;
753
754                 if (!dbus_message_get_args(
755                                     message,
756                                     &error,
757                                     DBUS_TYPE_STRING, &name,
758                                     DBUS_TYPE_INVALID))
759                         return bus_send_error_reply(connection, message, &error, -EINVAL);
760
761                 session = hashmap_get(m->sessions, name);
762                 if (!session)
763                         return bus_send_error_reply(connection, message, &error, -ENOENT);
764
765                 reply = dbus_message_new_method_return(message);
766                 if (!reply)
767                         goto oom;
768
769                 p = session_bus_path(session);
770                 if (!p)
771                         goto oom;
772
773                 b = dbus_message_append_args(
774                                 reply,
775                                 DBUS_TYPE_OBJECT_PATH, &p,
776                                 DBUS_TYPE_INVALID);
777                 free(p);
778
779                 if (!b)
780                         goto oom;
781
782         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) {
783                 uint32_t pid;
784                 char *p;
785                 Session *session;
786                 bool b;
787
788                 if (!dbus_message_get_args(
789                                     message,
790                                     &error,
791                                     DBUS_TYPE_UINT32, &pid,
792                                     DBUS_TYPE_INVALID))
793                         return bus_send_error_reply(connection, message, &error, -EINVAL);
794
795                 r = manager_get_session_by_pid(m, pid, &session);
796                 if (r <= 0)
797                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
798
799                 reply = dbus_message_new_method_return(message);
800                 if (!reply)
801                         goto oom;
802
803                 p = session_bus_path(session);
804                 if (!p)
805                         goto oom;
806
807                 b = dbus_message_append_args(
808                                 reply,
809                                 DBUS_TYPE_OBJECT_PATH, &p,
810                                 DBUS_TYPE_INVALID);
811                 free(p);
812
813                 if (!b)
814                         goto oom;
815
816         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
817                 uint32_t uid;
818                 char *p;
819                 User *user;
820                 bool b;
821
822                 if (!dbus_message_get_args(
823                                     message,
824                                     &error,
825                                     DBUS_TYPE_UINT32, &uid,
826                                     DBUS_TYPE_INVALID))
827                         return bus_send_error_reply(connection, message, &error, -EINVAL);
828
829                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
830                 if (!user)
831                         return bus_send_error_reply(connection, message, &error, -ENOENT);
832
833                 reply = dbus_message_new_method_return(message);
834                 if (!reply)
835                         goto oom;
836
837                 p = user_bus_path(user);
838                 if (!p)
839                         goto oom;
840
841                 b = dbus_message_append_args(
842                                 reply,
843                                 DBUS_TYPE_OBJECT_PATH, &p,
844                                 DBUS_TYPE_INVALID);
845                 free(p);
846
847                 if (!b)
848                         goto oom;
849
850         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
851                 const char *name;
852                 char *p;
853                 Seat *seat;
854                 bool b;
855
856                 if (!dbus_message_get_args(
857                                     message,
858                                     &error,
859                                     DBUS_TYPE_STRING, &name,
860                                     DBUS_TYPE_INVALID))
861                         return bus_send_error_reply(connection, message, &error, -EINVAL);
862
863                 seat = hashmap_get(m->seats, name);
864                 if (!seat)
865                         return bus_send_error_reply(connection, message, &error, -ENOENT);
866
867                 reply = dbus_message_new_method_return(message);
868                 if (!reply)
869                         goto oom;
870
871                 p = seat_bus_path(seat);
872                 if (!p)
873                         goto oom;
874
875                 b = dbus_message_append_args(
876                                 reply,
877                                 DBUS_TYPE_OBJECT_PATH, &p,
878                                 DBUS_TYPE_INVALID);
879                 free(p);
880
881                 if (!b)
882                         goto oom;
883
884         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
885                 char *p;
886                 Session *session;
887                 Iterator i;
888                 DBusMessageIter iter, sub;
889                 const char *empty = "";
890
891                 reply = dbus_message_new_method_return(message);
892                 if (!reply)
893                         goto oom;
894
895                 dbus_message_iter_init_append(reply, &iter);
896
897                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
898                         goto oom;
899
900                 HASHMAP_FOREACH(session, m->sessions, i) {
901                         DBusMessageIter sub2;
902                         uint32_t uid;
903
904                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
905                                 goto oom;
906
907                         uid = session->user->uid;
908
909                         p = session_bus_path(session);
910                         if (!p)
911                                 goto oom;
912
913                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
914                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
915                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
916                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
917                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
918                                 free(p);
919                                 goto oom;
920                         }
921
922                         free(p);
923
924                         if (!dbus_message_iter_close_container(&sub, &sub2))
925                                 goto oom;
926                 }
927
928                 if (!dbus_message_iter_close_container(&iter, &sub))
929                         goto oom;
930
931         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
932                 char *p;
933                 User *user;
934                 Iterator i;
935                 DBusMessageIter iter, sub;
936
937                 reply = dbus_message_new_method_return(message);
938                 if (!reply)
939                         goto oom;
940
941                 dbus_message_iter_init_append(reply, &iter);
942
943                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
944                         goto oom;
945
946                 HASHMAP_FOREACH(user, m->users, i) {
947                         DBusMessageIter sub2;
948                         uint32_t uid;
949
950                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
951                                 goto oom;
952
953                         uid = user->uid;
954
955                         p = user_bus_path(user);
956                         if (!p)
957                                 goto oom;
958
959                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
960                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
961                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
962                                 free(p);
963                                 goto oom;
964                         }
965
966                         free(p);
967
968                         if (!dbus_message_iter_close_container(&sub, &sub2))
969                                 goto oom;
970                 }
971
972                 if (!dbus_message_iter_close_container(&iter, &sub))
973                         goto oom;
974
975         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
976                 char *p;
977                 Seat *seat;
978                 Iterator i;
979                 DBusMessageIter iter, sub;
980
981                 reply = dbus_message_new_method_return(message);
982                 if (!reply)
983                         goto oom;
984
985                 dbus_message_iter_init_append(reply, &iter);
986
987                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
988                         goto oom;
989
990                 HASHMAP_FOREACH(seat, m->seats, i) {
991                         DBusMessageIter sub2;
992
993                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
994                                 goto oom;
995
996                         p = seat_bus_path(seat);
997                         if (!p)
998                                 goto oom;
999
1000                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
1001                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1002                                 free(p);
1003                                 goto oom;
1004                         }
1005
1006                         free(p);
1007
1008                         if (!dbus_message_iter_close_container(&sub, &sub2))
1009                                 goto oom;
1010                 }
1011
1012                 if (!dbus_message_iter_close_container(&iter, &sub))
1013                         goto oom;
1014
1015         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
1016
1017                 r = bus_manager_create_session(m, message, &reply);
1018
1019                 /* Don't delay the work on OOM here, since it might be
1020                  * triggered by a low RLIMIT_NOFILE here (since we
1021                  * send a dupped fd to the client), and we'd rather
1022                  * see this fail quickly then be retried later */
1023
1024                 if (r < 0)
1025                         return bus_send_error_reply(connection, message, &error, r);
1026
1027         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
1028                 const char *name;
1029                 Session *session;
1030
1031                 if (!dbus_message_get_args(
1032                                     message,
1033                                     &error,
1034                                     DBUS_TYPE_STRING, &name,
1035                                     DBUS_TYPE_INVALID))
1036                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1037
1038                 session = hashmap_get(m->sessions, name);
1039                 if (!session)
1040                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1041
1042                 r = session_activate(session);
1043                 if (r < 0)
1044                         return bus_send_error_reply(connection, message, NULL, r);
1045
1046                 reply = dbus_message_new_method_return(message);
1047                 if (!reply)
1048                         goto oom;
1049
1050         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) {
1051                 const char *session_name, *seat_name;
1052                 Session *session;
1053                 Seat *seat;
1054
1055                 /* Same as ActivateSession() but refuses to work if
1056                  * the seat doesn't match */
1057
1058                 if (!dbus_message_get_args(
1059                                     message,
1060                                     &error,
1061                                     DBUS_TYPE_STRING, &session_name,
1062                                     DBUS_TYPE_STRING, &seat_name,
1063                                     DBUS_TYPE_INVALID))
1064                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1065
1066                 session = hashmap_get(m->sessions, session_name);
1067                 if (!session)
1068                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1069
1070                 seat = hashmap_get(m->seats, seat_name);
1071                 if (!seat)
1072                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1073
1074                 if (session->seat != seat)
1075                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1076
1077                 r = session_activate(session);
1078                 if (r < 0)
1079                         return bus_send_error_reply(connection, message, NULL, r);
1080
1081                 reply = dbus_message_new_method_return(message);
1082                 if (!reply)
1083                         goto oom;
1084
1085         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") ||
1086                    dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) {
1087                 const char *name;
1088                 Session *session;
1089
1090                 if (!dbus_message_get_args(
1091                                     message,
1092                                     &error,
1093                                     DBUS_TYPE_STRING, &name,
1094                                     DBUS_TYPE_INVALID))
1095                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1096
1097                 session = hashmap_get(m->sessions, name);
1098                 if (!session)
1099                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1100
1101                 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
1102                         goto oom;
1103
1104                 reply = dbus_message_new_method_return(message);
1105                 if (!reply)
1106                         goto oom;
1107
1108         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) {
1109                 const char *swho;
1110                 int32_t signo;
1111                 KillWho who;
1112                 const char *name;
1113                 Session *session;
1114
1115                 if (!dbus_message_get_args(
1116                                     message,
1117                                     &error,
1118                                     DBUS_TYPE_STRING, &name,
1119                                     DBUS_TYPE_STRING, &swho,
1120                                     DBUS_TYPE_INT32, &signo,
1121                                     DBUS_TYPE_INVALID))
1122                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1123
1124                 if (isempty(swho))
1125                         who = KILL_ALL;
1126                 else {
1127                         who = kill_who_from_string(swho);
1128                         if (who < 0)
1129                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
1130                 }
1131
1132                 if (signo <= 0 || signo >= _NSIG)
1133                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1134
1135                 session = hashmap_get(m->sessions, name);
1136                 if (!session)
1137                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1138
1139                 r = session_kill(session, who, signo);
1140                 if (r < 0)
1141                         return bus_send_error_reply(connection, message, NULL, r);
1142
1143                 reply = dbus_message_new_method_return(message);
1144                 if (!reply)
1145                         goto oom;
1146
1147         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) {
1148                 uint32_t uid;
1149                 User *user;
1150                 int32_t signo;
1151
1152                 if (!dbus_message_get_args(
1153                                     message,
1154                                     &error,
1155                                     DBUS_TYPE_UINT32, &uid,
1156                                     DBUS_TYPE_INT32, &signo,
1157                                     DBUS_TYPE_INVALID))
1158                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1159
1160                 if (signo <= 0 || signo >= _NSIG)
1161                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1162
1163                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1164                 if (!user)
1165                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1166
1167                 r = user_kill(user, signo);
1168                 if (r < 0)
1169                         return bus_send_error_reply(connection, message, NULL, r);
1170
1171                 reply = dbus_message_new_method_return(message);
1172                 if (!reply)
1173                         goto oom;
1174
1175         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
1176                 const char *name;
1177                 Session *session;
1178
1179                 if (!dbus_message_get_args(
1180                                     message,
1181                                     &error,
1182                                     DBUS_TYPE_STRING, &name,
1183                                     DBUS_TYPE_INVALID))
1184                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1185
1186                 session = hashmap_get(m->sessions, name);
1187                 if (!session)
1188                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1189
1190                 r = session_stop(session);
1191                 if (r < 0)
1192                         return bus_send_error_reply(connection, message, NULL, r);
1193
1194                 reply = dbus_message_new_method_return(message);
1195                 if (!reply)
1196                         goto oom;
1197
1198         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1199                 uint32_t uid;
1200                 User *user;
1201
1202                 if (!dbus_message_get_args(
1203                                     message,
1204                                     &error,
1205                                     DBUS_TYPE_UINT32, &uid,
1206                                     DBUS_TYPE_INVALID))
1207                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1208
1209                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1210                 if (!user)
1211                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1212
1213                 r = user_stop(user);
1214                 if (r < 0)
1215                         return bus_send_error_reply(connection, message, NULL, r);
1216
1217                 reply = dbus_message_new_method_return(message);
1218                 if (!reply)
1219                         goto oom;
1220
1221         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1222                 const char *name;
1223                 Seat *seat;
1224
1225                 if (!dbus_message_get_args(
1226                                     message,
1227                                     &error,
1228                                     DBUS_TYPE_STRING, &name,
1229                                     DBUS_TYPE_INVALID))
1230                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1231
1232                 seat = hashmap_get(m->seats, name);
1233                 if (!seat)
1234                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1235
1236                 r = seat_stop_sessions(seat);
1237                 if (r < 0)
1238                         return bus_send_error_reply(connection, message, NULL, r);
1239
1240                 reply = dbus_message_new_method_return(message);
1241                 if (!reply)
1242                         goto oom;
1243
1244         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1245                 uint32_t uid;
1246                 struct passwd *pw;
1247                 dbus_bool_t b, interactive;
1248                 char *path;
1249
1250                 if (!dbus_message_get_args(
1251                                     message,
1252                                     &error,
1253                                     DBUS_TYPE_UINT32, &uid,
1254                                     DBUS_TYPE_BOOLEAN, &b,
1255                                     DBUS_TYPE_BOOLEAN, &interactive,
1256                                     DBUS_TYPE_INVALID))
1257                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1258
1259                 errno = 0;
1260                 pw = getpwuid(uid);
1261                 if (!pw)
1262                         return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
1263
1264                 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, &error);
1265                 if (r < 0)
1266                         return bus_send_error_reply(connection, message, &error, r);
1267
1268                 mkdir_p("/var/lib/systemd", 0755);
1269
1270                 r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0);
1271                 if (r < 0)
1272                         return bus_send_error_reply(connection, message, &error, r);
1273
1274                 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
1275                 if (!path)
1276                         goto oom;
1277
1278                 if (b) {
1279                         User *u;
1280
1281                         r = touch(path);
1282                         free(path);
1283
1284                         if (r < 0)
1285                                 return bus_send_error_reply(connection, message, &error, r);
1286
1287                         if (manager_add_user_by_uid(m, uid, &u) >= 0)
1288                                 user_start(u);
1289
1290                 } else {
1291                         User *u;
1292
1293                         r = unlink(path);
1294                         free(path);
1295
1296                         if (r < 0 && errno != ENOENT)
1297                                 return bus_send_error_reply(connection, message, &error, -errno);
1298
1299                         u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1300                         if (u)
1301                                 user_add_to_gc_queue(u);
1302                 }
1303
1304                 reply = dbus_message_new_method_return(message);
1305                 if (!reply)
1306                         goto oom;
1307
1308         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
1309                 const char *sysfs, *seat;
1310                 dbus_bool_t interactive;
1311
1312                 if (!dbus_message_get_args(
1313                                     message,
1314                                     &error,
1315                                     DBUS_TYPE_STRING, &seat,
1316                                     DBUS_TYPE_STRING, &sysfs,
1317                                     DBUS_TYPE_BOOLEAN, &interactive,
1318                                     DBUS_TYPE_INVALID))
1319                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1320
1321                 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
1322                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1323
1324                 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, &error);
1325                 if (r < 0)
1326                         return bus_send_error_reply(connection, message, &error, r);
1327
1328                 r = attach_device(m, seat, sysfs);
1329                 if (r < 0)
1330                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1331
1332                 reply = dbus_message_new_method_return(message);
1333                 if (!reply)
1334                         goto oom;
1335
1336
1337         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
1338                 dbus_bool_t interactive;
1339
1340                 if (!dbus_message_get_args(
1341                                     message,
1342                                     &error,
1343                                     DBUS_TYPE_BOOLEAN, &interactive,
1344                                     DBUS_TYPE_INVALID))
1345                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1346
1347                 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, &error);
1348                 if (r < 0)
1349                         return bus_send_error_reply(connection, message, &error, r);
1350
1351                 r = flush_devices(m);
1352                 if (r < 0)
1353                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1354
1355                 reply = dbus_message_new_method_return(message);
1356                 if (!reply)
1357                         goto oom;
1358
1359         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
1360                    dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
1361                 dbus_bool_t interactive;
1362                 bool multiple_sessions;
1363                 DBusMessage *forward, *freply;
1364                 const char *name;
1365                 const char *mode = "replace";
1366                 const char *action;
1367
1368                 if (!dbus_message_get_args(
1369                                     message,
1370                                     &error,
1371                                     DBUS_TYPE_BOOLEAN, &interactive,
1372                                     DBUS_TYPE_INVALID))
1373                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1374
1375                 multiple_sessions = hashmap_size(m->sessions) > 1;
1376
1377                 if (!multiple_sessions) {
1378                         Session *s;
1379
1380                         /* Hmm, there's only one session, but let's
1381                          * make sure it actually belongs to the user
1382                          * who is asking. If not, better be safe than
1383                          * sorry. */
1384
1385                         s = hashmap_first(m->sessions);
1386                         if (s) {
1387                                 unsigned long ul;
1388
1389                                 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
1390                                 if (ul == (unsigned long) -1)
1391                                         return bus_send_error_reply(connection, message, &error, -EIO);
1392
1393                                 multiple_sessions = s->user->uid != ul;
1394                         }
1395                 }
1396
1397                 if (streq(dbus_message_get_member(message), "PowerOff")) {
1398                         if (multiple_sessions)
1399                                 action = "org.freedesktop.login1.power-off-multiple-sessions";
1400                         else
1401                                 action = "org.freedesktop.login1.power-off";
1402
1403                         name = SPECIAL_POWEROFF_TARGET;
1404                 } else {
1405                         if (multiple_sessions)
1406                                 action = "org.freedesktop.login1.reboot-multiple-sessions";
1407                         else
1408                                 action = "org.freedesktop.login1.reboot";
1409
1410                         name = SPECIAL_REBOOT_TARGET;
1411                 }
1412
1413                 r = verify_polkit(connection, message, action, interactive, &error);
1414                 if (r < 0)
1415                         return bus_send_error_reply(connection, message, &error, r);
1416
1417                 forward = dbus_message_new_method_call(
1418                               "org.freedesktop.systemd1",
1419                               "/org/freedesktop/systemd1",
1420                               "org.freedesktop.systemd1.Manager",
1421                               "StartUnit");
1422                 if (!forward)
1423                         return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1424
1425                 if (!dbus_message_append_args(forward,
1426                                               DBUS_TYPE_STRING, &name,
1427                                               DBUS_TYPE_STRING, &mode,
1428                                               DBUS_TYPE_INVALID)) {
1429                         dbus_message_unref(forward);
1430                         return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1431                 }
1432
1433                 freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error);
1434                 dbus_message_unref(forward);
1435
1436                 if (!freply)
1437                         return bus_send_error_reply(connection, message, &error, -EIO);
1438
1439                 dbus_message_unref(freply);
1440
1441                 reply = dbus_message_new_method_return(message);
1442                 if (!reply)
1443                         goto oom;
1444
1445         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1446                 char *introspection = NULL;
1447                 FILE *f;
1448                 Iterator i;
1449                 Session *session;
1450                 Seat *seat;
1451                 User *user;
1452                 size_t size;
1453                 char *p;
1454
1455                 if (!(reply = dbus_message_new_method_return(message)))
1456                         goto oom;
1457
1458                 /* We roll our own introspection code here, instead of
1459                  * relying on bus_default_message_handler() because we
1460                  * need to generate our introspection string
1461                  * dynamically. */
1462
1463                 if (!(f = open_memstream(&introspection, &size)))
1464                         goto oom;
1465
1466                 fputs(INTROSPECTION_BEGIN, f);
1467
1468                 HASHMAP_FOREACH(seat, m->seats, i) {
1469                         p = bus_path_escape(seat->id);
1470
1471                         if (p) {
1472                                 fprintf(f, "<node name=\"seat/%s\"/>", p);
1473                                 free(p);
1474                         }
1475                 }
1476
1477                 HASHMAP_FOREACH(user, m->users, i)
1478                         fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
1479
1480                 HASHMAP_FOREACH(session, m->sessions, i) {
1481                         p = bus_path_escape(session->id);
1482
1483                         if (p) {
1484                                 fprintf(f, "<node name=\"session/%s\"/>", p);
1485                                 free(p);
1486                         }
1487                 }
1488
1489                 fputs(INTROSPECTION_END, f);
1490
1491                 if (ferror(f)) {
1492                         fclose(f);
1493                         free(introspection);
1494                         goto oom;
1495                 }
1496
1497                 fclose(f);
1498
1499                 if (!introspection)
1500                         goto oom;
1501
1502                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1503                         free(introspection);
1504                         goto oom;
1505                 }
1506
1507                 free(introspection);
1508         } else {
1509                 const BusBoundProperties bps[] = {
1510                         { "org.freedesktop.login1.Manager", bus_login_manager_properties, m },
1511                         { NULL, }
1512                 };
1513                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1514         }
1515
1516         if (reply) {
1517                 if (!dbus_connection_send(connection, reply, NULL))
1518                         goto oom;
1519
1520                 dbus_message_unref(reply);
1521         }
1522
1523         return DBUS_HANDLER_RESULT_HANDLED;
1524
1525 oom:
1526         if (reply)
1527                 dbus_message_unref(reply);
1528
1529         dbus_error_free(&error);
1530
1531         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1532 }
1533
1534 const DBusObjectPathVTable bus_manager_vtable = {
1535         .message_function = manager_message_handler
1536 };
1537
1538 DBusHandlerResult bus_message_filter(
1539                 DBusConnection *connection,
1540                 DBusMessage *message,
1541                 void *userdata) {
1542
1543         Manager *m = userdata;
1544         DBusError error;
1545
1546         assert(m);
1547         assert(connection);
1548         assert(message);
1549
1550         dbus_error_init(&error);
1551
1552         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
1553                 const char *cgroup;
1554
1555                 if (!dbus_message_get_args(message, &error,
1556                                            DBUS_TYPE_STRING, &cgroup,
1557                                            DBUS_TYPE_INVALID))
1558                         log_error("Failed to parse Released message: %s", bus_error_message(&error));
1559                 else
1560                         manager_cgroup_notify_empty(m, cgroup);
1561         }
1562
1563         dbus_error_free(&error);
1564
1565         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1566 }
1567
1568 int manager_send_changed(Manager *manager, const char *properties) {
1569         DBusMessage *m;
1570         int r = -ENOMEM;
1571
1572         assert(manager);
1573
1574         m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
1575         if (!m)
1576                 goto finish;
1577
1578         if (!dbus_connection_send(manager->bus, m, NULL))
1579                 goto finish;
1580
1581         r = 0;
1582
1583 finish:
1584         if (m)
1585                 dbus_message_unref(m);
1586
1587         return r;
1588 }