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