chiark / gitweb /
logind: add man page for configuration file
[elogind.git] / src / 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
32 #define BUS_MANAGER_INTERFACE                                           \
33         " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
34         "  <method name=\"GetSession\">\n"                              \
35         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
36         "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
37         "  </method>\n"                                                 \
38         "  <method name=\"GetUser\">\n"                                 \
39         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
40         "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
41         "  </method>\n"                                                 \
42         "  <method name=\"GetSeat\">\n"                                 \
43         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
44         "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
45         "  </method>\n"                                                 \
46         "  <method name=\"ListSessions\">\n"                            \
47         "   <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
48         "  </method>\n"                                                 \
49         "  <method name=\"ListUsers\">\n"                               \
50         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
51         "  </method>\n"                                                 \
52         "  <method name=\"ListSeats\">\n"                               \
53         "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
54         "  </method>\n"                                                 \
55         "  <method name=\"CreateSession\">\n"                           \
56         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
57         "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
58         "   <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n"       \
59         "   <arg name=\"type\" type=\"s\" direction=\"in\"/>\n"         \
60         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
61         "   <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n"         \
62         "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
63         "   <arg name=\"display\" type=\"s\" direction=\"in\"/>\n"      \
64         "   <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n"       \
65         "   <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n"  \
66         "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
67         "   <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
68         "   <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
69         "   <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
70         "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
71         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
72         "   <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
73         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
74         "  </method>\n"                                                 \
75         "  <method name=\"ActivateSession\">\n"                         \
76         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
77         "  </method>\n"                                                 \
78         "  <method name=\"TerminateSession\">\n"                        \
79         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
80         "  </method>\n"                                                 \
81         "  <method name=\"TerminateUser\">\n"                           \
82         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
83         "  </method>\n"                                                 \
84         "  <method name=\"TerminateSeat\">\n"                           \
85         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
86         "  </method>\n"                                                 \
87         "  <method name=\"SetUserLinger\">\n"                           \
88         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
89         "   <arg name=\"b\" type=\"b\" direction=\"in\"/>\n"            \
90         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
91         "  </method>\n"                                                 \
92         "  <method name=\"AttachDevice\">\n"                            \
93         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
94         "   <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n"        \
95         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
96         "  </method>\n"                                                 \
97         "  <signal name=\"SessionNew\">\n"                              \
98         "   <arg name=\"id\" type=\"s\"/>\n"                            \
99         "   <arg name=\"path\" type=\"o\"/>\n"                          \
100         "  </signal>\n"                                                 \
101         "  <signal name=\"SessionRemoved\">\n"                          \
102         "   <arg name=\"id\" type=\"s\"/>\n"                            \
103         "   <arg name=\"path\" type=\"o\"/>\n"                          \
104         "  </signal>\n"                                                 \
105         "  <signal name=\"UserNew\">\n"                                 \
106         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
107         "   <arg name=\"path\" type=\"o\"/>\n"                          \
108         "  </signal>\n"                                                 \
109         "  <signal name=\"UserRemoved\">\n"                             \
110         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
111         "   <arg name=\"path\" type=\"o\"/>\n"                          \
112         "  </signal>\n"                                                 \
113         "  <signal name=\"SeatNew\">\n"                                 \
114         "   <arg name=\"id\" type=\"s\"/>\n"                            \
115         "   <arg name=\"path\" type=\"o\"/>\n"                          \
116         "  </signal>\n"                                                 \
117         "  <signal name=\"SeatRemoved\">\n"                             \
118         "   <arg name=\"id\" type=\"s\"/>\n"                            \
119         "   <arg name=\"path\" type=\"o\"/>\n"                          \
120         "  </signal>\n"                                                 \
121         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
122         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
123         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
124         "  <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
125         "  <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
126         "  <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
127         "  <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
128         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
129         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
130         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
131         " </interface>\n"
132
133 #define INTROSPECTION_BEGIN                                             \
134         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
135         "<node>\n"                                                      \
136         BUS_MANAGER_INTERFACE                                           \
137         BUS_PROPERTIES_INTERFACE                                        \
138         BUS_PEER_INTERFACE                                              \
139         BUS_INTROSPECTABLE_INTERFACE
140
141 #define INTROSPECTION_END                                               \
142         "</node>\n"
143
144 #define INTERFACES_LIST                              \
145         BUS_GENERIC_INTERFACES_LIST                  \
146         "org.freedesktop.login1.Manager\0"
147
148 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
149         Manager *m = data;
150         dbus_bool_t b;
151
152         assert(i);
153         assert(property);
154         assert(m);
155
156         b = manager_get_idle_hint(m, NULL) > 0;
157         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
158                 return -ENOMEM;
159
160         return 0;
161 }
162
163 static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
164         Manager *m = data;
165         dual_timestamp t;
166         uint64_t u;
167
168         assert(i);
169         assert(property);
170         assert(m);
171
172         manager_get_idle_hint(m, &t);
173         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
174
175         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
176                 return -ENOMEM;
177
178         return 0;
179 }
180
181 static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
182         Session *session = NULL;
183         User *user = NULL;
184         const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service;
185         uint32_t uid, leader, audit_id = 0;
186         dbus_bool_t remote, kill_processes;
187         char **controllers = NULL, **reset_controllers = NULL;
188         SessionType t;
189         Seat *s;
190         DBusMessageIter iter;
191         int r;
192         char *id = NULL, *p;
193         uint32_t vtnr = 0;
194         int fifo_fd = -1;
195         DBusMessage *reply = NULL;
196         bool b;
197
198         assert(m);
199         assert(message);
200         assert(_reply);
201
202         if (!dbus_message_iter_init(message, &iter) ||
203             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
204                 return -EINVAL;
205
206         dbus_message_iter_get_basic(&iter, &uid);
207
208         if (!dbus_message_iter_next(&iter) ||
209             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
210                 return -EINVAL;
211
212         dbus_message_iter_get_basic(&iter, &leader);
213
214         if (leader <= 0 ||
215             !dbus_message_iter_next(&iter) ||
216             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
217                 return -EINVAL;
218
219         dbus_message_iter_get_basic(&iter, &service);
220
221         if (!dbus_message_iter_next(&iter) ||
222             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
223                 return -EINVAL;
224
225         dbus_message_iter_get_basic(&iter, &type);
226         t = session_type_from_string(type);
227
228         if (t < 0 ||
229             !dbus_message_iter_next(&iter) ||
230             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
231                 return -EINVAL;
232
233         dbus_message_iter_get_basic(&iter, &seat);
234
235         if (isempty(seat))
236                 s = NULL;
237         else {
238                 s = hashmap_get(m->seats, seat);
239                 if (!s)
240                         return -ENOENT;
241         }
242
243         if (!dbus_message_iter_next(&iter) ||
244             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
245                 return -EINVAL;
246
247         dbus_message_iter_get_basic(&iter, &vtnr);
248
249         if (!dbus_message_iter_next(&iter) ||
250             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
251                 return -EINVAL;
252
253         dbus_message_iter_get_basic(&iter, &tty);
254
255         if (tty_is_vc(tty)) {
256                 int v;
257
258                 if (!s)
259                         s = m->vtconsole;
260                 else if (s != m->vtconsole)
261                         return -EINVAL;
262
263                 v = vtnr_from_tty(tty);
264
265                 if (v <= 0)
266                         return v < 0 ? v : -EINVAL;
267
268                 if (vtnr <= 0)
269                         vtnr = (uint32_t) v;
270                 else if (vtnr != (uint32_t) v)
271                         return -EINVAL;
272
273         } else if (!isempty(tty) && s && seat_is_vtconsole(s))
274                 return -EINVAL;
275
276         if (s) {
277                 if (seat_is_vtconsole(s)) {
278                         if (vtnr <= 0 || vtnr > 63)
279                                 return -EINVAL;
280                 } else {
281                         if (vtnr > 0)
282                                 return -EINVAL;
283                 }
284         }
285
286         if (!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, &display);
291
292         if (!dbus_message_iter_next(&iter) ||
293             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
294                 return -EINVAL;
295
296         dbus_message_iter_get_basic(&iter, &remote);
297
298         if (!dbus_message_iter_next(&iter) ||
299             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
300                 return -EINVAL;
301
302         dbus_message_iter_get_basic(&iter, &remote_user);
303
304         if (!dbus_message_iter_next(&iter) ||
305             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
306                 return -EINVAL;
307
308         dbus_message_iter_get_basic(&iter, &remote_host);
309
310         if (!dbus_message_iter_next(&iter) ||
311             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
312             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
313                 return -EINVAL;
314
315         r = bus_parse_strv_iter(&iter, &controllers);
316         if (r < 0)
317                 return -EINVAL;
318
319         if (strv_contains(controllers, "systemd") ||
320             !dbus_message_iter_next(&iter) ||
321             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
322             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
323                 r = -EINVAL;
324                 goto fail;
325         }
326
327         r = bus_parse_strv_iter(&iter, &reset_controllers);
328         if (r < 0)
329                 goto fail;
330
331         if (strv_contains(reset_controllers, "systemd") ||
332             !dbus_message_iter_next(&iter) ||
333             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
334                 r = -EINVAL;
335                 goto fail;
336         }
337
338         dbus_message_iter_get_basic(&iter, &kill_processes);
339
340         r = manager_add_user_by_uid(m, uid, &user);
341         if (r < 0)
342                 goto fail;
343
344         audit_session_from_pid(leader, &audit_id);
345
346         if (audit_id > 0) {
347                 asprintf(&id, "%lu", (unsigned long) audit_id);
348
349                 if (!id) {
350                         r = -ENOMEM;
351                         goto fail;
352                 }
353
354                 session = hashmap_get(m->sessions, id);
355
356                 if (session) {
357
358                         fifo_fd = session_create_fifo(session);
359                         if (fifo_fd < 0) {
360                                 r = fifo_fd;
361                                 goto fail;
362                         }
363
364                         /* Session already exists, client is probably
365                          * something like "su" which changes uid but
366                          * is still the same audit session */
367
368                         reply = dbus_message_new_method_return(message);
369                         if (!reply) {
370                                 r = -ENOMEM;
371                                 goto fail;
372                         }
373
374                         p = session_bus_path(session);
375                         if (!p) {
376                                 r = -ENOMEM;
377                                 goto fail;
378                         }
379
380                         b = dbus_message_append_args(
381                                         reply,
382                                         DBUS_TYPE_STRING, &session->id,
383                                         DBUS_TYPE_OBJECT_PATH, &p,
384                                         DBUS_TYPE_STRING, &session->user->runtime_path,
385                                         DBUS_TYPE_UNIX_FD, &fifo_fd,
386                                         DBUS_TYPE_INVALID);
387                         free(p);
388
389                         if (!b) {
390                                 r = -ENOMEM;
391                                 goto fail;
392                         }
393
394                         close_nointr_nofail(fifo_fd);
395                         *_reply = reply;
396
397                         return 0;
398                 }
399
400         } else {
401                 do {
402                         free(id);
403                         asprintf(&id, "c%lu", ++m->session_counter);
404
405                         if (!id) {
406                                 r = -ENOMEM;
407                                 goto fail;
408                         }
409
410                 } while (hashmap_get(m->sessions, id));
411         }
412
413         r = manager_add_session(m, user, id, &session);
414         free(id);
415         if (r < 0)
416                 goto fail;
417
418         session->leader = leader;
419         session->audit_id = audit_id;
420         session->type = t;
421         session->remote = remote;
422         session->controllers = controllers;
423         session->reset_controllers = reset_controllers;
424         session->kill_processes = kill_processes;
425         session->vtnr = vtnr;
426
427         controllers = reset_controllers = NULL;
428
429         if (!isempty(tty)) {
430                 session->tty = strdup(tty);
431                 if (!session->tty) {
432                         r = -ENOMEM;
433                         goto fail;
434                 }
435         }
436
437         if (!isempty(display)) {
438                 session->display = strdup(display);
439                 if (!session->display) {
440                         r = -ENOMEM;
441                         goto fail;
442                 }
443         }
444
445         if (!isempty(remote_user)) {
446                 session->remote_user = strdup(remote_user);
447                 if (!session->remote_user) {
448                         r = -ENOMEM;
449                         goto fail;
450                 }
451         }
452
453         if (!isempty(remote_host)) {
454                 session->remote_host = strdup(remote_host);
455                 if (!session->remote_host) {
456                         r = -ENOMEM;
457                         goto fail;
458                 }
459         }
460
461         if (!isempty(service)) {
462                 session->service = strdup(service);
463                 if (!session->service) {
464                         r = -ENOMEM;
465                         goto fail;
466                 }
467         }
468
469         fifo_fd = session_create_fifo(session);
470         if (fifo_fd < 0) {
471                 r = fifo_fd;
472                 goto fail;
473         }
474
475         if (s) {
476                 r = seat_attach_session(s, session);
477                 if (r < 0)
478                         goto fail;
479         }
480
481         r = session_start(session);
482         if (r < 0)
483                 goto fail;
484
485         reply = dbus_message_new_method_return(message);
486         if (!reply) {
487                 r = -ENOMEM;
488                 goto fail;
489         }
490
491         p = session_bus_path(session);
492         if (!p) {
493                 r = -ENOMEM;
494                 goto fail;
495         }
496
497         b = dbus_message_append_args(
498                         reply,
499                         DBUS_TYPE_STRING, &session->id,
500                         DBUS_TYPE_OBJECT_PATH, &p,
501                         DBUS_TYPE_STRING, &session->user->runtime_path,
502                         DBUS_TYPE_UNIX_FD, &fifo_fd,
503                         DBUS_TYPE_INVALID);
504         free(p);
505
506         if (!b) {
507                 r = -ENOMEM;
508                 goto fail;
509         }
510
511         close_nointr_nofail(fifo_fd);
512         *_reply = reply;
513
514         return 0;
515
516 fail:
517         strv_free(controllers);
518         strv_free(reset_controllers);
519
520         if (session)
521                 session_add_to_gc_queue(session);
522
523         if (user)
524                 user_add_to_gc_queue(user);
525
526         if (fifo_fd >= 0)
527                 close_nointr_nofail(fifo_fd);
528
529         if (reply)
530                 dbus_message_unref(reply);
531
532         return r;
533 }
534
535 static bool device_has_tag(struct udev_device *d, const char *tag) {
536         struct udev_list_entry *first, *item;
537
538         assert(d);
539         assert(tag);
540
541         udev_device_get_is_initialized(d);
542
543         first = udev_device_get_tags_list_entry(d);
544         udev_list_entry_foreach(item, first)
545                 if (streq(udev_list_entry_get_name(item), tag))
546                         return true;
547
548         return false;
549 }
550
551 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
552         struct udev_device *d;
553         char *rule = NULL, *file = NULL;
554         const char *id_for_seat;
555         int r;
556         struct udev_enumerate *e;
557         struct udev_list_entry *first, *item;
558
559         assert(m);
560         assert(seat);
561         assert(sysfs);
562
563         d = udev_device_new_from_syspath(m->udev, sysfs);
564         if (!d)
565                 return -ENODEV;
566
567         if (!device_has_tag(d, "seat")) {
568                 r = -ENODEV;
569                 goto finish;
570         }
571
572         id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
573         if (!id_for_seat) {
574                 r = -ENODEV;
575                 goto finish;
576         }
577
578         if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
579                 r = -ENOMEM;
580                 goto finish;
581         }
582
583         if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
584                 r = -ENOMEM;
585                 goto finish;
586         }
587
588         mkdir_p("/etc/udev/rules.d", 0755);
589         r = write_one_line_file(file, rule);
590         if (r < 0)
591                 goto finish;
592
593         e = udev_enumerate_new(m->udev);
594         if (!e) {
595                 r = -ENOMEM;
596                 goto finish;
597         }
598
599         if (udev_enumerate_scan_devices(e) < 0) {
600                 r = -EIO;
601                 goto finish;
602         }
603
604         first = udev_enumerate_get_list_entry(e);
605         udev_list_entry_foreach(item, first) {
606                 char *t;
607                 const char *p;
608
609                 p = udev_list_entry_get_name(item);
610                 if (!path_startswith(p, sysfs))
611                         continue;
612
613                 t = strappend(p, "/uevent");
614                 if (!t) {
615                         r = -ENOMEM;
616                         goto finish;
617                 }
618
619                 write_one_line_file(t, "change");
620                 free(t);
621         }
622
623 finish:
624         free(rule);
625         free(file);
626
627         if (d)
628                 udev_device_unref(d);
629
630         if (e)
631                 udev_enumerate_unref(e);
632
633         return r;
634 }
635
636 static DBusHandlerResult manager_message_handler(
637                 DBusConnection *connection,
638                 DBusMessage *message,
639                 void *userdata) {
640
641         Manager *m = userdata;
642
643         const BusProperty properties[] = {
644                 { "org.freedesktop.login1.Manager", "ControlGroupHierarchy",  bus_property_append_string,   "s",  m->cgroup_path          },
645                 { "org.freedesktop.login1.Manager", "Controllers",            bus_property_append_strv,     "as", m->controllers          },
646                 { "org.freedesktop.login1.Manager", "ResetControllers",       bus_property_append_strv,     "as", m->reset_controllers    },
647                 { "org.freedesktop.login1.Manager", "NAutoVTs",               bus_property_append_unsigned, "u",  &m->n_autovts           },
648                 { "org.freedesktop.login1.Manager", "KillOnlyUsers",          bus_property_append_strv,     "as", m->kill_only_users      },
649                 { "org.freedesktop.login1.Manager", "KillExcludeUsers",       bus_property_append_strv,     "as", m->kill_exclude_users   },
650                 { "org.freedesktop.login1.Manager", "KillUserProcesses",      bus_property_append_bool,     "b",  &m->kill_user_processes },
651                 { "org.freedesktop.login1.Manager", "IdleHint",               bus_manager_append_idle_hint, "b",  m                       },
652                 { "org.freedesktop.login1.Manager", "IdleSinceHint",          bus_manager_append_idle_hint_since, "t", m                  },
653                 { "org.freedesktop.login1.Manager", "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", m                  },
654                 { NULL, NULL, NULL, NULL, NULL }
655         };
656
657         DBusError error;
658         DBusMessage *reply = NULL;
659         int r;
660
661         assert(connection);
662         assert(message);
663         assert(m);
664
665         dbus_error_init(&error);
666
667         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
668                 const char *name;
669                 char *p;
670                 Session *session;
671                 bool b;
672
673                 if (!dbus_message_get_args(
674                                     message,
675                                     &error,
676                                     DBUS_TYPE_STRING, &name,
677                                     DBUS_TYPE_INVALID))
678                         return bus_send_error_reply(connection, message, &error, -EINVAL);
679
680                 session = hashmap_get(m->sessions, name);
681                 if (!session)
682                         return bus_send_error_reply(connection, message, &error, -ENOENT);
683
684                 reply = dbus_message_new_method_return(message);
685                 if (!reply)
686                         goto oom;
687
688                 p = session_bus_path(session);
689                 if (!p)
690                         goto oom;
691
692                 b = dbus_message_append_args(
693                                 reply,
694                                 DBUS_TYPE_OBJECT_PATH, &p,
695                                 DBUS_TYPE_INVALID);
696                 free(p);
697
698                 if (!b)
699                         goto oom;
700
701         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
702                 uint32_t uid;
703                 char *p;
704                 User *user;
705                 bool b;
706
707                 if (!dbus_message_get_args(
708                                     message,
709                                     &error,
710                                     DBUS_TYPE_UINT32, &uid,
711                                     DBUS_TYPE_INVALID))
712                         return bus_send_error_reply(connection, message, &error, -EINVAL);
713
714                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
715                 if (!user)
716                         return bus_send_error_reply(connection, message, &error, -ENOENT);
717
718                 reply = dbus_message_new_method_return(message);
719                 if (!reply)
720                         goto oom;
721
722                 p = user_bus_path(user);
723                 if (!p)
724                         goto oom;
725
726                 b = dbus_message_append_args(
727                                 reply,
728                                 DBUS_TYPE_OBJECT_PATH, &p,
729                                 DBUS_TYPE_INVALID);
730                 free(p);
731
732                 if (!b)
733                         goto oom;
734
735         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
736                 const char *name;
737                 char *p;
738                 Seat *seat;
739                 bool b;
740
741                 if (!dbus_message_get_args(
742                                     message,
743                                     &error,
744                                     DBUS_TYPE_STRING, &name,
745                                     DBUS_TYPE_INVALID))
746                         return bus_send_error_reply(connection, message, &error, -EINVAL);
747
748                 seat = hashmap_get(m->seats, name);
749                 if (!seat)
750                         return bus_send_error_reply(connection, message, &error, -ENOENT);
751
752                 reply = dbus_message_new_method_return(message);
753                 if (!reply)
754                         goto oom;
755
756                 p = seat_bus_path(seat);
757                 if (!p)
758                         goto oom;
759
760                 b = dbus_message_append_args(
761                                 reply,
762                                 DBUS_TYPE_OBJECT_PATH, &p,
763                                 DBUS_TYPE_INVALID);
764                 free(p);
765
766                 if (!b)
767                         goto oom;
768
769         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
770                 char *p;
771                 Session *session;
772                 Iterator i;
773                 DBusMessageIter iter, sub;
774                 const char *empty = "";
775
776                 reply = dbus_message_new_method_return(message);
777                 if (!reply)
778                         goto oom;
779
780                 dbus_message_iter_init_append(reply, &iter);
781
782                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
783                         goto oom;
784
785                 HASHMAP_FOREACH(session, m->sessions, i) {
786                         DBusMessageIter sub2;
787                         uint32_t uid;
788
789                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
790                                 goto oom;
791
792                         uid = session->user->uid;
793
794                         p = session_bus_path(session);
795                         if (!p)
796                                 goto oom;
797
798                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
799                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
800                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
801                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
802                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
803                                 free(p);
804                                 goto oom;
805                         }
806
807                         free(p);
808
809                         if (!dbus_message_iter_close_container(&sub, &sub2))
810                                 goto oom;
811                 }
812
813                 if (!dbus_message_iter_close_container(&iter, &sub))
814                         goto oom;
815
816         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
817                 char *p;
818                 User *user;
819                 Iterator i;
820                 DBusMessageIter iter, sub;
821
822                 reply = dbus_message_new_method_return(message);
823                 if (!reply)
824                         goto oom;
825
826                 dbus_message_iter_init_append(reply, &iter);
827
828                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
829                         goto oom;
830
831                 HASHMAP_FOREACH(user, m->users, i) {
832                         DBusMessageIter sub2;
833                         uint32_t uid;
834
835                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
836                                 goto oom;
837
838                         uid = user->uid;
839
840                         p = user_bus_path(user);
841                         if (!p)
842                                 goto oom;
843
844                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
845                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
846                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
847                                 free(p);
848                                 goto oom;
849                         }
850
851                         free(p);
852
853                         if (!dbus_message_iter_close_container(&sub, &sub2))
854                                 goto oom;
855                 }
856
857                 if (!dbus_message_iter_close_container(&iter, &sub))
858                         goto oom;
859
860         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
861                 char *p;
862                 Seat *seat;
863                 Iterator i;
864                 DBusMessageIter iter, sub;
865
866                 reply = dbus_message_new_method_return(message);
867                 if (!reply)
868                         goto oom;
869
870                 dbus_message_iter_init_append(reply, &iter);
871
872                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
873                         goto oom;
874
875                 HASHMAP_FOREACH(seat, m->seats, i) {
876                         DBusMessageIter sub2;
877
878                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
879                                 goto oom;
880
881                         p = seat_bus_path(seat);
882                         if (!p)
883                                 goto oom;
884
885                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
886                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
887                                 free(p);
888                                 goto oom;
889                         }
890
891                         free(p);
892
893                         if (!dbus_message_iter_close_container(&sub, &sub2))
894                                 goto oom;
895                 }
896
897                 if (!dbus_message_iter_close_container(&iter, &sub))
898                         goto oom;
899
900         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
901
902                 r = bus_manager_create_session(m, message, &reply);
903                 if (r == -ENOMEM)
904                         goto oom;
905
906                 if (r < 0)
907                         return bus_send_error_reply(connection, message, &error, r);
908
909         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
910                 const char *name;
911                 Session *session;
912
913                 if (!dbus_message_get_args(
914                                     message,
915                                     &error,
916                                     DBUS_TYPE_STRING, &name,
917                                     DBUS_TYPE_INVALID))
918                         return bus_send_error_reply(connection, message, &error, -EINVAL);
919
920                 session = hashmap_get(m->sessions, name);
921                 if (!session)
922                         return bus_send_error_reply(connection, message, &error, -ENOENT);
923
924                 r = session_activate(session);
925                 if (r < 0)
926                         return bus_send_error_reply(connection, message, NULL, r);
927
928                 reply = dbus_message_new_method_return(message);
929                 if (!reply)
930                         goto oom;
931
932         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
933                 const char *name;
934                 Session *session;
935
936                 if (!dbus_message_get_args(
937                                     message,
938                                     &error,
939                                     DBUS_TYPE_STRING, &name,
940                                     DBUS_TYPE_INVALID))
941                         return bus_send_error_reply(connection, message, &error, -EINVAL);
942
943                 session = hashmap_get(m->sessions, name);
944                 if (!session)
945                         return bus_send_error_reply(connection, message, &error, -ENOENT);
946
947                 r = session_stop(session);
948                 if (r < 0)
949                         return bus_send_error_reply(connection, message, NULL, r);
950
951                 reply = dbus_message_new_method_return(message);
952                 if (!reply)
953                         goto oom;
954
955         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
956                 uint32_t uid;
957                 User *user;
958
959                 if (!dbus_message_get_args(
960                                     message,
961                                     &error,
962                                     DBUS_TYPE_UINT32, &uid,
963                                     DBUS_TYPE_INVALID))
964                         return bus_send_error_reply(connection, message, &error, -EINVAL);
965
966                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
967                 if (!user)
968                         return bus_send_error_reply(connection, message, &error, -ENOENT);
969
970                 r = user_stop(user);
971                 if (r < 0)
972                         return bus_send_error_reply(connection, message, NULL, r);
973
974                 reply = dbus_message_new_method_return(message);
975                 if (!reply)
976                         goto oom;
977
978         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
979                 const char *name;
980                 Seat *seat;
981
982                 if (!dbus_message_get_args(
983                                     message,
984                                     &error,
985                                     DBUS_TYPE_STRING, &name,
986                                     DBUS_TYPE_INVALID))
987                         return bus_send_error_reply(connection, message, &error, -EINVAL);
988
989                 seat = hashmap_get(m->seats, name);
990                 if (!seat)
991                         return bus_send_error_reply(connection, message, &error, -ENOENT);
992
993                 r = seat_stop_sessions(seat);
994                 if (r < 0)
995                         return bus_send_error_reply(connection, message, NULL, r);
996
997                 reply = dbus_message_new_method_return(message);
998                 if (!reply)
999                         goto oom;
1000
1001         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1002                 uint32_t uid;
1003                 struct passwd *pw;
1004                 dbus_bool_t b, interactive;
1005                 char *path;
1006
1007                 if (!dbus_message_get_args(
1008                                     message,
1009                                     &error,
1010                                     DBUS_TYPE_UINT32, &uid,
1011                                     DBUS_TYPE_BOOLEAN, &b,
1012                                     DBUS_TYPE_BOOLEAN, &interactive,
1013                                     DBUS_TYPE_INVALID))
1014                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1015
1016                 errno = 0;
1017                 pw = getpwuid(uid);
1018                 if (!pw)
1019                         return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
1020
1021                 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, &error);
1022                 if (r < 0)
1023                         return bus_send_error_reply(connection, message, &error, r);
1024
1025                 r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0);
1026                 if (r < 0)
1027                         return bus_send_error_reply(connection, message, &error, r);
1028
1029                 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
1030                 if (!path)
1031                         goto oom;
1032
1033                 if (b) {
1034                         User *u;
1035
1036                         r = touch(path);
1037                         free(path);
1038
1039                         if (r < 0)
1040                                 return bus_send_error_reply(connection, message, &error, r);
1041
1042                         if (manager_add_user_by_uid(m, uid, &u) >= 0)
1043                                 user_start(u);
1044
1045                 } else {
1046                         User *u;
1047
1048                         r = unlink(path);
1049                         free(path);
1050
1051                         if (r < 0 && errno != ENOENT)
1052                                 return bus_send_error_reply(connection, message, &error, -errno);
1053
1054                         u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1055                         if (u)
1056                                 user_add_to_gc_queue(u);
1057                 }
1058
1059                 reply = dbus_message_new_method_return(message);
1060                 if (!reply)
1061                         goto oom;
1062
1063         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
1064                 const char *sysfs, *seat;
1065                 dbus_bool_t interactive;
1066
1067                 if (!dbus_message_get_args(
1068                                     message,
1069                                     &error,
1070                                     DBUS_TYPE_STRING, &seat,
1071                                     DBUS_TYPE_STRING, &sysfs,
1072                                     DBUS_TYPE_BOOLEAN, &interactive,
1073                                     DBUS_TYPE_INVALID))
1074                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1075
1076                 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
1077                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1078
1079                 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, &error);
1080                 if (r < 0)
1081                         return bus_send_error_reply(connection, message, &error, r);
1082
1083                 r = attach_device(m, seat, sysfs);
1084                 if (r < 0)
1085                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1086
1087                 reply = dbus_message_new_method_return(message);
1088                 if (!reply)
1089                         goto oom;
1090
1091         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1092                 char *introspection = NULL;
1093                 FILE *f;
1094                 Iterator i;
1095                 Session *session;
1096                 Seat *seat;
1097                 User *user;
1098                 size_t size;
1099                 char *p;
1100
1101                 if (!(reply = dbus_message_new_method_return(message)))
1102                         goto oom;
1103
1104                 /* We roll our own introspection code here, instead of
1105                  * relying on bus_default_message_handler() because we
1106                  * need to generate our introspection string
1107                  * dynamically. */
1108
1109                 if (!(f = open_memstream(&introspection, &size)))
1110                         goto oom;
1111
1112                 fputs(INTROSPECTION_BEGIN, f);
1113
1114                 HASHMAP_FOREACH(seat, m->seats, i) {
1115                         p = bus_path_escape(seat->id);
1116
1117                         if (p) {
1118                                 fprintf(f, "<node name=\"seat/%s\"/>", p);
1119                                 free(p);
1120                         }
1121                 }
1122
1123                 HASHMAP_FOREACH(user, m->users, i)
1124                         fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
1125
1126                 HASHMAP_FOREACH(session, m->sessions, i) {
1127                         p = bus_path_escape(session->id);
1128
1129                         if (p) {
1130                                 fprintf(f, "<node name=\"session/%s\"/>", p);
1131                                 free(p);
1132                         }
1133                 }
1134
1135                 fputs(INTROSPECTION_END, f);
1136
1137                 if (ferror(f)) {
1138                         fclose(f);
1139                         free(introspection);
1140                         goto oom;
1141                 }
1142
1143                 fclose(f);
1144
1145                 if (!introspection)
1146                         goto oom;
1147
1148                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1149                         free(introspection);
1150                         goto oom;
1151                 }
1152
1153                 free(introspection);
1154         } else
1155                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties);
1156
1157         if (reply) {
1158                 if (!dbus_connection_send(connection, reply, NULL))
1159                         goto oom;
1160
1161                 dbus_message_unref(reply);
1162         }
1163
1164         return DBUS_HANDLER_RESULT_HANDLED;
1165
1166 oom:
1167         if (reply)
1168                 dbus_message_unref(reply);
1169
1170         dbus_error_free(&error);
1171
1172         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1173 }
1174
1175 const DBusObjectPathVTable bus_manager_vtable = {
1176         .message_function = manager_message_handler
1177 };
1178
1179 DBusHandlerResult bus_message_filter(
1180                 DBusConnection *connection,
1181                 DBusMessage *message,
1182                 void *userdata) {
1183
1184         Manager *m = userdata;
1185         DBusError error;
1186
1187         assert(m);
1188         assert(connection);
1189         assert(message);
1190
1191         dbus_error_init(&error);
1192
1193         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
1194                 const char *cgroup;
1195
1196                 if (!dbus_message_get_args(message, &error,
1197                                            DBUS_TYPE_STRING, &cgroup,
1198                                            DBUS_TYPE_INVALID))
1199                         log_error("Failed to parse Released message: %s", bus_error_message(&error));
1200                 else
1201                         manager_cgroup_notify_empty(m, cgroup);
1202         }
1203
1204         dbus_error_free(&error);
1205
1206         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1207 }
1208
1209 int manager_send_changed(Manager *manager, const char *properties) {
1210         DBusMessage *m;
1211         int r = -ENOMEM;
1212
1213         assert(manager);
1214
1215         m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
1216         if (!m)
1217                 goto finish;
1218
1219         if (!dbus_connection_send(manager->bus, m, NULL))
1220                 goto finish;
1221
1222         r = 0;
1223
1224 finish:
1225         if (m)
1226                 dbus_message_unref(m);
1227
1228         return r;
1229 }