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