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