chiark / gitweb /
seat: only mark main input device for seat assignments
[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         udev_device_get_is_initialized(d);
545
546         first = udev_device_get_tags_list_entry(d);
547         udev_list_entry_foreach(item, first)
548                 if (streq(udev_list_entry_get_name(item), tag))
549                         return true;
550
551         return false;
552 }
553
554 static int trigger_device(Manager *m, const char *prefix) {
555         struct udev_enumerate *e;
556         struct udev_list_entry *first, *item;
557         int r;
558
559         assert(m);
560
561         e = udev_enumerate_new(m->udev);
562         if (!e) {
563                 r = -ENOMEM;
564                 goto finish;
565         }
566
567         if (udev_enumerate_scan_devices(e) < 0) {
568                 r = -EIO;
569                 goto finish;
570         }
571
572         first = udev_enumerate_get_list_entry(e);
573         udev_list_entry_foreach(item, first) {
574                 char *t;
575                 const char *p;
576
577                 p = udev_list_entry_get_name(item);
578
579                 if (prefix && !path_startswith(p, prefix))
580                         continue;
581
582                 t = strappend(p, "/uevent");
583                 if (!t) {
584                         r = -ENOMEM;
585                         goto finish;
586                 }
587
588                 write_one_line_file(t, "change");
589                 free(t);
590         }
591
592         r = 0;
593
594 finish:
595         if (e)
596                 udev_enumerate_unref(e);
597
598         return r;
599 }
600
601 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
602         struct udev_device *d;
603         char *rule = NULL, *file = NULL;
604         const char *id_for_seat;
605         int r;
606
607         assert(m);
608         assert(seat);
609         assert(sysfs);
610
611         d = udev_device_new_from_syspath(m->udev, sysfs);
612         if (!d)
613                 return -ENODEV;
614
615         if (!device_has_tag(d, "seat")) {
616                 r = -ENODEV;
617                 goto finish;
618         }
619
620         id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
621         if (!id_for_seat) {
622                 r = -ENODEV;
623                 goto finish;
624         }
625
626         if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
627                 r = -ENOMEM;
628                 goto finish;
629         }
630
631         if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
632                 r = -ENOMEM;
633                 goto finish;
634         }
635
636         mkdir_p("/etc/udev/rules.d", 0755);
637         r = write_one_line_file_atomic(file, rule);
638         if (r < 0)
639                 goto finish;
640
641         r = trigger_device(m, sysfs);
642
643 finish:
644         free(rule);
645         free(file);
646
647         if (d)
648                 udev_device_unref(d);
649
650         return r;
651 }
652
653 static int flush_devices(Manager *m) {
654         DIR *d;
655
656         assert(m);
657
658         d = opendir("/etc/udev/rules.d");
659         if (!d) {
660                 if (errno != ENOENT)
661                         log_warning("Failed to open /etc/udev/rules.d: %m");
662         } else {
663                 struct dirent *de;
664
665                 while ((de = readdir(d))) {
666
667                         if (!dirent_is_file(de))
668                                 continue;
669
670                         if (!startswith(de->d_name, "72-seat-"))
671                                 continue;
672
673                         if (!endswith(de->d_name, ".rules"))
674                                 continue;
675
676                         if (unlinkat(dirfd(d), de->d_name, 0) < 0)
677                                 log_warning("Failed to unlink %s: %m", de->d_name);
678                 }
679
680                 closedir(d);
681         }
682
683         return trigger_device(m, NULL);
684 }
685
686 static DBusHandlerResult manager_message_handler(
687                 DBusConnection *connection,
688                 DBusMessage *message,
689                 void *userdata) {
690
691         Manager *m = userdata;
692
693         const BusProperty properties[] = {
694                 { "org.freedesktop.login1.Manager", "ControlGroupHierarchy",  bus_property_append_string,   "s",  m->cgroup_path          },
695                 { "org.freedesktop.login1.Manager", "Controllers",            bus_property_append_strv,     "as", m->controllers          },
696                 { "org.freedesktop.login1.Manager", "ResetControllers",       bus_property_append_strv,     "as", m->reset_controllers    },
697                 { "org.freedesktop.login1.Manager", "NAutoVTs",               bus_property_append_unsigned, "u",  &m->n_autovts           },
698                 { "org.freedesktop.login1.Manager", "KillOnlyUsers",          bus_property_append_strv,     "as", m->kill_only_users      },
699                 { "org.freedesktop.login1.Manager", "KillExcludeUsers",       bus_property_append_strv,     "as", m->kill_exclude_users   },
700                 { "org.freedesktop.login1.Manager", "KillUserProcesses",      bus_property_append_bool,     "b",  &m->kill_user_processes },
701                 { "org.freedesktop.login1.Manager", "IdleHint",               bus_manager_append_idle_hint, "b",  m                       },
702                 { "org.freedesktop.login1.Manager", "IdleSinceHint",          bus_manager_append_idle_hint_since, "t", m                  },
703                 { "org.freedesktop.login1.Manager", "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", m                  },
704                 { NULL, NULL, NULL, NULL, NULL }
705         };
706
707         DBusError error;
708         DBusMessage *reply = NULL;
709         int r;
710
711         assert(connection);
712         assert(message);
713         assert(m);
714
715         dbus_error_init(&error);
716
717         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
718                 const char *name;
719                 char *p;
720                 Session *session;
721                 bool b;
722
723                 if (!dbus_message_get_args(
724                                     message,
725                                     &error,
726                                     DBUS_TYPE_STRING, &name,
727                                     DBUS_TYPE_INVALID))
728                         return bus_send_error_reply(connection, message, &error, -EINVAL);
729
730                 session = hashmap_get(m->sessions, name);
731                 if (!session)
732                         return bus_send_error_reply(connection, message, &error, -ENOENT);
733
734                 reply = dbus_message_new_method_return(message);
735                 if (!reply)
736                         goto oom;
737
738                 p = session_bus_path(session);
739                 if (!p)
740                         goto oom;
741
742                 b = dbus_message_append_args(
743                                 reply,
744                                 DBUS_TYPE_OBJECT_PATH, &p,
745                                 DBUS_TYPE_INVALID);
746                 free(p);
747
748                 if (!b)
749                         goto oom;
750
751         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
752                 uint32_t uid;
753                 char *p;
754                 User *user;
755                 bool b;
756
757                 if (!dbus_message_get_args(
758                                     message,
759                                     &error,
760                                     DBUS_TYPE_UINT32, &uid,
761                                     DBUS_TYPE_INVALID))
762                         return bus_send_error_reply(connection, message, &error, -EINVAL);
763
764                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
765                 if (!user)
766                         return bus_send_error_reply(connection, message, &error, -ENOENT);
767
768                 reply = dbus_message_new_method_return(message);
769                 if (!reply)
770                         goto oom;
771
772                 p = user_bus_path(user);
773                 if (!p)
774                         goto oom;
775
776                 b = dbus_message_append_args(
777                                 reply,
778                                 DBUS_TYPE_OBJECT_PATH, &p,
779                                 DBUS_TYPE_INVALID);
780                 free(p);
781
782                 if (!b)
783                         goto oom;
784
785         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
786                 const char *name;
787                 char *p;
788                 Seat *seat;
789                 bool b;
790
791                 if (!dbus_message_get_args(
792                                     message,
793                                     &error,
794                                     DBUS_TYPE_STRING, &name,
795                                     DBUS_TYPE_INVALID))
796                         return bus_send_error_reply(connection, message, &error, -EINVAL);
797
798                 seat = hashmap_get(m->seats, name);
799                 if (!seat)
800                         return bus_send_error_reply(connection, message, &error, -ENOENT);
801
802                 reply = dbus_message_new_method_return(message);
803                 if (!reply)
804                         goto oom;
805
806                 p = seat_bus_path(seat);
807                 if (!p)
808                         goto oom;
809
810                 b = dbus_message_append_args(
811                                 reply,
812                                 DBUS_TYPE_OBJECT_PATH, &p,
813                                 DBUS_TYPE_INVALID);
814                 free(p);
815
816                 if (!b)
817                         goto oom;
818
819         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
820                 char *p;
821                 Session *session;
822                 Iterator i;
823                 DBusMessageIter iter, sub;
824                 const char *empty = "";
825
826                 reply = dbus_message_new_method_return(message);
827                 if (!reply)
828                         goto oom;
829
830                 dbus_message_iter_init_append(reply, &iter);
831
832                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
833                         goto oom;
834
835                 HASHMAP_FOREACH(session, m->sessions, i) {
836                         DBusMessageIter sub2;
837                         uint32_t uid;
838
839                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
840                                 goto oom;
841
842                         uid = session->user->uid;
843
844                         p = session_bus_path(session);
845                         if (!p)
846                                 goto oom;
847
848                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
849                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
850                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
851                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
852                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
853                                 free(p);
854                                 goto oom;
855                         }
856
857                         free(p);
858
859                         if (!dbus_message_iter_close_container(&sub, &sub2))
860                                 goto oom;
861                 }
862
863                 if (!dbus_message_iter_close_container(&iter, &sub))
864                         goto oom;
865
866         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
867                 char *p;
868                 User *user;
869                 Iterator i;
870                 DBusMessageIter iter, sub;
871
872                 reply = dbus_message_new_method_return(message);
873                 if (!reply)
874                         goto oom;
875
876                 dbus_message_iter_init_append(reply, &iter);
877
878                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
879                         goto oom;
880
881                 HASHMAP_FOREACH(user, m->users, i) {
882                         DBusMessageIter sub2;
883                         uint32_t uid;
884
885                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
886                                 goto oom;
887
888                         uid = user->uid;
889
890                         p = user_bus_path(user);
891                         if (!p)
892                                 goto oom;
893
894                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
895                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
896                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
897                                 free(p);
898                                 goto oom;
899                         }
900
901                         free(p);
902
903                         if (!dbus_message_iter_close_container(&sub, &sub2))
904                                 goto oom;
905                 }
906
907                 if (!dbus_message_iter_close_container(&iter, &sub))
908                         goto oom;
909
910         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
911                 char *p;
912                 Seat *seat;
913                 Iterator i;
914                 DBusMessageIter iter, sub;
915
916                 reply = dbus_message_new_method_return(message);
917                 if (!reply)
918                         goto oom;
919
920                 dbus_message_iter_init_append(reply, &iter);
921
922                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
923                         goto oom;
924
925                 HASHMAP_FOREACH(seat, m->seats, i) {
926                         DBusMessageIter sub2;
927
928                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
929                                 goto oom;
930
931                         p = seat_bus_path(seat);
932                         if (!p)
933                                 goto oom;
934
935                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
936                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
937                                 free(p);
938                                 goto oom;
939                         }
940
941                         free(p);
942
943                         if (!dbus_message_iter_close_container(&sub, &sub2))
944                                 goto oom;
945                 }
946
947                 if (!dbus_message_iter_close_container(&iter, &sub))
948                         goto oom;
949
950         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
951
952                 r = bus_manager_create_session(m, message, &reply);
953                 if (r == -ENOMEM)
954                         goto oom;
955
956                 if (r < 0)
957                         return bus_send_error_reply(connection, message, &error, r);
958
959         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
960                 const char *name;
961                 Session *session;
962
963                 if (!dbus_message_get_args(
964                                     message,
965                                     &error,
966                                     DBUS_TYPE_STRING, &name,
967                                     DBUS_TYPE_INVALID))
968                         return bus_send_error_reply(connection, message, &error, -EINVAL);
969
970                 session = hashmap_get(m->sessions, name);
971                 if (!session)
972                         return bus_send_error_reply(connection, message, &error, -ENOENT);
973
974                 r = session_activate(session);
975                 if (r < 0)
976                         return bus_send_error_reply(connection, message, NULL, r);
977
978                 reply = dbus_message_new_method_return(message);
979                 if (!reply)
980                         goto oom;
981
982         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
983                 const char *name;
984                 Session *session;
985
986                 if (!dbus_message_get_args(
987                                     message,
988                                     &error,
989                                     DBUS_TYPE_STRING, &name,
990                                     DBUS_TYPE_INVALID))
991                         return bus_send_error_reply(connection, message, &error, -EINVAL);
992
993                 session = hashmap_get(m->sessions, name);
994                 if (!session)
995                         return bus_send_error_reply(connection, message, &error, -ENOENT);
996
997                 r = session_stop(session);
998                 if (r < 0)
999                         return bus_send_error_reply(connection, message, NULL, r);
1000
1001                 reply = dbus_message_new_method_return(message);
1002                 if (!reply)
1003                         goto oom;
1004
1005         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1006                 uint32_t uid;
1007                 User *user;
1008
1009                 if (!dbus_message_get_args(
1010                                     message,
1011                                     &error,
1012                                     DBUS_TYPE_UINT32, &uid,
1013                                     DBUS_TYPE_INVALID))
1014                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1015
1016                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1017                 if (!user)
1018                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1019
1020                 r = user_stop(user);
1021                 if (r < 0)
1022                         return bus_send_error_reply(connection, message, NULL, r);
1023
1024                 reply = dbus_message_new_method_return(message);
1025                 if (!reply)
1026                         goto oom;
1027
1028         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1029                 const char *name;
1030                 Seat *seat;
1031
1032                 if (!dbus_message_get_args(
1033                                     message,
1034                                     &error,
1035                                     DBUS_TYPE_STRING, &name,
1036                                     DBUS_TYPE_INVALID))
1037                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1038
1039                 seat = hashmap_get(m->seats, name);
1040                 if (!seat)
1041                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1042
1043                 r = seat_stop_sessions(seat);
1044                 if (r < 0)
1045                         return bus_send_error_reply(connection, message, NULL, r);
1046
1047                 reply = dbus_message_new_method_return(message);
1048                 if (!reply)
1049                         goto oom;
1050
1051         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1052                 uint32_t uid;
1053                 struct passwd *pw;
1054                 dbus_bool_t b, interactive;
1055                 char *path;
1056
1057                 if (!dbus_message_get_args(
1058                                     message,
1059                                     &error,
1060                                     DBUS_TYPE_UINT32, &uid,
1061                                     DBUS_TYPE_BOOLEAN, &b,
1062                                     DBUS_TYPE_BOOLEAN, &interactive,
1063                                     DBUS_TYPE_INVALID))
1064                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1065
1066                 errno = 0;
1067                 pw = getpwuid(uid);
1068                 if (!pw)
1069                         return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
1070
1071                 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, &error);
1072                 if (r < 0)
1073                         return bus_send_error_reply(connection, message, &error, r);
1074
1075                 r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0);
1076                 if (r < 0)
1077                         return bus_send_error_reply(connection, message, &error, r);
1078
1079                 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
1080                 if (!path)
1081                         goto oom;
1082
1083                 if (b) {
1084                         User *u;
1085
1086                         r = touch(path);
1087                         free(path);
1088
1089                         if (r < 0)
1090                                 return bus_send_error_reply(connection, message, &error, r);
1091
1092                         if (manager_add_user_by_uid(m, uid, &u) >= 0)
1093                                 user_start(u);
1094
1095                 } else {
1096                         User *u;
1097
1098                         r = unlink(path);
1099                         free(path);
1100
1101                         if (r < 0 && errno != ENOENT)
1102                                 return bus_send_error_reply(connection, message, &error, -errno);
1103
1104                         u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1105                         if (u)
1106                                 user_add_to_gc_queue(u);
1107                 }
1108
1109                 reply = dbus_message_new_method_return(message);
1110                 if (!reply)
1111                         goto oom;
1112
1113         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
1114                 const char *sysfs, *seat;
1115                 dbus_bool_t interactive;
1116
1117                 if (!dbus_message_get_args(
1118                                     message,
1119                                     &error,
1120                                     DBUS_TYPE_STRING, &seat,
1121                                     DBUS_TYPE_STRING, &sysfs,
1122                                     DBUS_TYPE_BOOLEAN, &interactive,
1123                                     DBUS_TYPE_INVALID))
1124                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1125
1126                 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
1127                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1128
1129                 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, &error);
1130                 if (r < 0)
1131                         return bus_send_error_reply(connection, message, &error, r);
1132
1133                 r = attach_device(m, seat, sysfs);
1134                 if (r < 0)
1135                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1136
1137                 reply = dbus_message_new_method_return(message);
1138                 if (!reply)
1139                         goto oom;
1140
1141
1142         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
1143                 dbus_bool_t interactive;
1144
1145                 if (!dbus_message_get_args(
1146                                     message,
1147                                     &error,
1148                                     DBUS_TYPE_BOOLEAN, &interactive,
1149                                     DBUS_TYPE_INVALID))
1150                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1151
1152                 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, &error);
1153                 if (r < 0)
1154                         return bus_send_error_reply(connection, message, &error, r);
1155
1156                 r = flush_devices(m);
1157                 if (r < 0)
1158                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1159
1160                 reply = dbus_message_new_method_return(message);
1161                 if (!reply)
1162                         goto oom;
1163
1164         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1165                 char *introspection = NULL;
1166                 FILE *f;
1167                 Iterator i;
1168                 Session *session;
1169                 Seat *seat;
1170                 User *user;
1171                 size_t size;
1172                 char *p;
1173
1174                 if (!(reply = dbus_message_new_method_return(message)))
1175                         goto oom;
1176
1177                 /* We roll our own introspection code here, instead of
1178                  * relying on bus_default_message_handler() because we
1179                  * need to generate our introspection string
1180                  * dynamically. */
1181
1182                 if (!(f = open_memstream(&introspection, &size)))
1183                         goto oom;
1184
1185                 fputs(INTROSPECTION_BEGIN, f);
1186
1187                 HASHMAP_FOREACH(seat, m->seats, i) {
1188                         p = bus_path_escape(seat->id);
1189
1190                         if (p) {
1191                                 fprintf(f, "<node name=\"seat/%s\"/>", p);
1192                                 free(p);
1193                         }
1194                 }
1195
1196                 HASHMAP_FOREACH(user, m->users, i)
1197                         fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
1198
1199                 HASHMAP_FOREACH(session, m->sessions, i) {
1200                         p = bus_path_escape(session->id);
1201
1202                         if (p) {
1203                                 fprintf(f, "<node name=\"session/%s\"/>", p);
1204                                 free(p);
1205                         }
1206                 }
1207
1208                 fputs(INTROSPECTION_END, f);
1209
1210                 if (ferror(f)) {
1211                         fclose(f);
1212                         free(introspection);
1213                         goto oom;
1214                 }
1215
1216                 fclose(f);
1217
1218                 if (!introspection)
1219                         goto oom;
1220
1221                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1222                         free(introspection);
1223                         goto oom;
1224                 }
1225
1226                 free(introspection);
1227         } else
1228                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties);
1229
1230         if (reply) {
1231                 if (!dbus_connection_send(connection, reply, NULL))
1232                         goto oom;
1233
1234                 dbus_message_unref(reply);
1235         }
1236
1237         return DBUS_HANDLER_RESULT_HANDLED;
1238
1239 oom:
1240         if (reply)
1241                 dbus_message_unref(reply);
1242
1243         dbus_error_free(&error);
1244
1245         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1246 }
1247
1248 const DBusObjectPathVTable bus_manager_vtable = {
1249         .message_function = manager_message_handler
1250 };
1251
1252 DBusHandlerResult bus_message_filter(
1253                 DBusConnection *connection,
1254                 DBusMessage *message,
1255                 void *userdata) {
1256
1257         Manager *m = userdata;
1258         DBusError error;
1259
1260         assert(m);
1261         assert(connection);
1262         assert(message);
1263
1264         dbus_error_init(&error);
1265
1266         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
1267                 const char *cgroup;
1268
1269                 if (!dbus_message_get_args(message, &error,
1270                                            DBUS_TYPE_STRING, &cgroup,
1271                                            DBUS_TYPE_INVALID))
1272                         log_error("Failed to parse Released message: %s", bus_error_message(&error));
1273                 else
1274                         manager_cgroup_notify_empty(m, cgroup);
1275         }
1276
1277         dbus_error_free(&error);
1278
1279         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1280 }
1281
1282 int manager_send_changed(Manager *manager, const char *properties) {
1283         DBusMessage *m;
1284         int r = -ENOMEM;
1285
1286         assert(manager);
1287
1288         m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
1289         if (!m)
1290                 goto finish;
1291
1292         if (!dbus_connection_send(manager->bus, m, NULL))
1293                 goto finish;
1294
1295         r = 0;
1296
1297 finish:
1298         if (m)
1299                 dbus_message_unref(m);
1300
1301         return r;
1302 }