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