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