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