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