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