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