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