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