chiark / gitweb /
logind: use pipe fd to detect when a session is dead
[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
26 #include "logind.h"
27 #include "dbus-common.h"
28 #include "strv.h"
29
30 #define BUS_MANAGER_INTERFACE                                           \
31         " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
32         "  <method name=\"GetSession\">\n"                              \
33         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
34         "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
35         "  </method>\n"                                                 \
36         "  <method name=\"GetUser\">\n"                                 \
37         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
38         "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
39         "  </method>\n"                                                 \
40         "  <method name=\"GetSeat\">\n"                                 \
41         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
42         "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
43         "  </method>\n"                                                 \
44         "  <method name=\"ListSessions\">\n"                            \
45         "   <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
46         "  </method>\n"                                                 \
47         "  <method name=\"ListUsers\">\n"                               \
48         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
49         "  </method>\n"                                                 \
50         "  <method name=\"ListSeats\">\n"                               \
51         "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
52         "  </method>\n"                                                 \
53         "  <method name=\"CreateSession\">\n"                           \
54         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
55         "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
56         "   <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n"         \
57         "   <arg name=\"type\" type=\"s\" direction=\"in\"/>\n"         \
58         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
59         "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
60         "   <arg name=\"display\" type=\"s\" direction=\"in\"/>\n"      \
61         "   <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n"       \
62         "   <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n"  \
63         "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
64         "   <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
65         "   <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
66         "   <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
67         "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
68         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
69         "   <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
70         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
71         "  </method>\n"                                                 \
72         "  <method name=\"ActivateSession\">\n"                         \
73         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
74         "  </method>\n"                                                 \
75         "  <method name=\"TerminateSession\">\n"                        \
76         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
77         "  </method>\n"                                                 \
78         "  <method name=\"TerminateUser\">\n"                           \
79         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
80         "  </method>\n"                                                 \
81         "  <method name=\"TerminateSeat\">\n"                           \
82         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
83         "  </method>\n"                                                 \
84         "  <signal name=\"SessionNew\">\n"                              \
85         "   <arg name=\"id\" type=\"s\"/>\n"                            \
86         "   <arg name=\"path\" type=\"o\"/>\n"                          \
87         "  </signal>\n"                                                 \
88         "  <signal name=\"SessionRemoved\">\n"                          \
89         "   <arg name=\"id\" type=\"s\"/>\n"                            \
90         "   <arg name=\"path\" type=\"o\"/>\n"                          \
91         "  </signal>\n"                                                 \
92         "  <signal name=\"UserNew\">\n"                                 \
93         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
94         "   <arg name=\"path\" type=\"o\"/>\n"                          \
95         "  </signal>\n"                                                 \
96         "  <signal name=\"UserRemoved\">\n"                             \
97         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
98         "   <arg name=\"path\" type=\"o\"/>\n"                          \
99         "  </signal>\n"                                                 \
100         "  <signal name=\"SeatNew\">\n"                                 \
101         "   <arg name=\"id\" type=\"s\"/>\n"                            \
102         "   <arg name=\"path\" type=\"o\"/>\n"                          \
103         "  </signal>\n"                                                 \
104         "  <signal name=\"SeatRemoved\">\n"                             \
105         "   <arg name=\"id\" type=\"s\"/>\n"                            \
106         "   <arg name=\"path\" type=\"o\"/>\n"                          \
107         "  </signal>\n"                                                 \
108         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
109         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
110         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
111         "  <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
112         "  <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
113         "  <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
114         "  <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
115         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
116         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
117         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
118         " </interface>\n"
119
120 #define INTROSPECTION_BEGIN                                             \
121         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
122         "<node>\n"                                                      \
123         BUS_MANAGER_INTERFACE                                           \
124         BUS_PROPERTIES_INTERFACE                                        \
125         BUS_PEER_INTERFACE                                              \
126         BUS_INTROSPECTABLE_INTERFACE
127
128 #define INTROSPECTION_END                                               \
129         "</node>\n"
130
131 #define INTERFACES_LIST                              \
132         BUS_GENERIC_INTERFACES_LIST                  \
133         "org.freedesktop.login1.Manager\0"
134
135 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
136         Manager *m = data;
137         dbus_bool_t b;
138
139         assert(i);
140         assert(property);
141         assert(m);
142
143         b = manager_get_idle_hint(m, NULL) > 0;
144         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
145                 return -ENOMEM;
146
147         return 0;
148 }
149
150 static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
151         Manager *m = data;
152         dual_timestamp t;
153         uint64_t u;
154
155         assert(i);
156         assert(property);
157         assert(m);
158
159         manager_get_idle_hint(m, &t);
160         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
161
162         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
163                 return -ENOMEM;
164
165         return 0;
166 }
167
168 static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
169         Session *session = NULL;
170         User *user = NULL;
171         const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service;
172         uint32_t uid, leader, audit_id = 0;
173         dbus_bool_t remote, kill_processes;
174         char **controllers = NULL, **reset_controllers = NULL;
175         SessionType t;
176         Seat *s;
177         DBusMessageIter iter;
178         int r;
179         char *id = NULL, *p;
180         int vtnr = -1;
181         int pipe_fds[2] = { -1, -1 };
182         DBusMessage *reply = NULL;
183         bool b;
184
185         assert(m);
186         assert(message);
187         assert(_reply);
188
189         if (!dbus_message_iter_init(message, &iter) ||
190             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
191                 return -EINVAL;
192
193         dbus_message_iter_get_basic(&iter, &uid);
194
195         if (!dbus_message_iter_next(&iter) ||
196             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
197                 return -EINVAL;
198
199         dbus_message_iter_get_basic(&iter, &leader);
200
201         if (leader <= 0 ||
202             !dbus_message_iter_next(&iter) ||
203             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
204                 return -EINVAL;
205
206         dbus_message_iter_get_basic(&iter, &service);
207
208         if (!dbus_message_iter_next(&iter) ||
209             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
210                 return -EINVAL;
211
212         dbus_message_iter_get_basic(&iter, &type);
213         t = session_type_from_string(type);
214
215         if (t < 0 ||
216             !dbus_message_iter_next(&iter) ||
217             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
218                 return -EINVAL;
219
220         dbus_message_iter_get_basic(&iter, &seat);
221
222         if (isempty(seat))
223                 s = NULL;
224         else {
225                 s = hashmap_get(m->seats, seat);
226                 if (!s)
227                         return -ENOENT;
228         }
229
230         if (!dbus_message_iter_next(&iter) ||
231             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
232                 return -EINVAL;
233
234         dbus_message_iter_get_basic(&iter, &tty);
235
236         if (tty_is_vc(tty)) {
237
238                 if (!s)
239                         s = m->vtconsole;
240                 else if (s != m->vtconsole)
241                         return -EINVAL;
242
243                 vtnr = vtnr_from_tty(tty);
244
245                 if (vtnr <= 0)
246                         return vtnr < 0 ? vtnr : -EINVAL;
247
248         } else if (s == m->vtconsole)
249                 return -EINVAL;
250
251         if (!dbus_message_iter_next(&iter) ||
252             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
253                 return -EINVAL;
254
255         dbus_message_iter_get_basic(&iter, &display);
256
257         if (!dbus_message_iter_next(&iter) ||
258             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
259                 return -EINVAL;
260
261         dbus_message_iter_get_basic(&iter, &remote);
262
263         if (!dbus_message_iter_next(&iter) ||
264             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
265                 return -EINVAL;
266
267         dbus_message_iter_get_basic(&iter, &remote_user);
268
269         if (!dbus_message_iter_next(&iter) ||
270             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
271                 return -EINVAL;
272
273         dbus_message_iter_get_basic(&iter, &remote_host);
274
275         if (!dbus_message_iter_next(&iter) ||
276             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
277             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
278                 return -EINVAL;
279
280         r = bus_parse_strv_iter(&iter, &controllers);
281         if (r < 0)
282                 return -EINVAL;
283
284         if (!dbus_message_iter_next(&iter) ||
285             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
286             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
287                 r = -EINVAL;
288                 goto fail;
289         }
290
291         r = bus_parse_strv_iter(&iter, &reset_controllers);
292         if (r < 0)
293                 goto fail;
294
295         if (!dbus_message_iter_next(&iter) ||
296             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
297                 r = -EINVAL;
298                 goto fail;
299         }
300
301         dbus_message_iter_get_basic(&iter, &kill_processes);
302
303         r = manager_add_user_by_uid(m, uid, &user);
304         if (r < 0)
305                 goto fail;
306
307         audit_session_from_pid(leader, &audit_id);
308
309         if (audit_id > 0) {
310                 asprintf(&id, "%lu", (unsigned long) audit_id);
311
312                 if (!id) {
313                         r = -ENOMEM;
314                         goto fail;
315                 }
316
317                 session = hashmap_get(m->sessions, id);
318
319                 if (session) {
320
321                         /* Session already exists, client is probably
322                          * something like "su" which changes uid but
323                          * is still the same audit session */
324
325                         reply = dbus_message_new_method_return(message);
326                         if (!reply) {
327                                 r = -ENOMEM;
328                                 goto fail;
329                         }
330
331                         /* Create a throw-away fd */
332                         if (pipe(pipe_fds) < 0) {
333                                 r = -errno;
334                                 goto fail;
335                         }
336
337                         close_nointr_nofail(pipe_fds[0]);
338                         pipe_fds[0] = -1;
339
340                         p = session_bus_path(session);
341                         if (!p) {
342                                 r = -ENOMEM;
343                                 goto fail;
344                         }
345
346                         b = dbus_message_append_args(
347                                         reply,
348                                         DBUS_TYPE_STRING, &session->id,
349                                         DBUS_TYPE_OBJECT_PATH, &p,
350                                         DBUS_TYPE_STRING, &session->user->runtime_path,
351                                         DBUS_TYPE_UNIX_FD, &pipe_fds[1],
352                                         DBUS_TYPE_INVALID);
353                         free(p);
354
355                         if (!b) {
356                                 r = -ENOMEM;
357                                 goto fail;
358                         }
359
360                         close_nointr_nofail(pipe_fds[1]);
361                         *_reply = reply;
362
363                         return 0;
364                 }
365
366         } else {
367                 do {
368                         free(id);
369                         asprintf(&id, "c%lu", ++m->session_counter);
370
371                         if (!id) {
372                                 r = -ENOMEM;
373                                 goto fail;
374                         }
375
376                 } while (hashmap_get(m->sessions, id));
377         }
378
379         r = manager_add_session(m, user, id, &session);
380         free(id);
381         if (r < 0)
382                 goto fail;
383
384         session->leader = leader;
385         session->audit_id = audit_id;
386         session->type = t;
387         session->remote = remote;
388         session->controllers = controllers;
389         session->reset_controllers = reset_controllers;
390         session->kill_processes = kill_processes;
391         session->vtnr = vtnr;
392
393         controllers = reset_controllers = NULL;
394
395         if (!isempty(tty)) {
396                 session->tty = strdup(tty);
397                 if (!session->tty) {
398                         r = -ENOMEM;
399                         goto fail;
400                 }
401         }
402
403         if (!isempty(display)) {
404                 session->display = strdup(display);
405                 if (!session->display) {
406                         r = -ENOMEM;
407                         goto fail;
408                 }
409         }
410
411         if (!isempty(remote_user)) {
412                 session->remote_user = strdup(remote_user);
413                 if (!session->remote_user) {
414                         r = -ENOMEM;
415                         goto fail;
416                 }
417         }
418
419         if (!isempty(remote_host)) {
420                 session->remote_host = strdup(remote_host);
421                 if (!session->remote_host) {
422                         r = -ENOMEM;
423                         goto fail;
424                 }
425         }
426
427         if (!isempty(service)) {
428                 session->service = strdup(service);
429                 if (!session->service) {
430                         r = -ENOMEM;
431                         goto fail;
432                 }
433         }
434
435         if (pipe(pipe_fds) < 0) {
436                 r = -errno;
437                 goto fail;
438         }
439
440         r = session_set_pipe_fd(session, pipe_fds[0]);
441         if (r < 0)
442                 goto fail;
443         pipe_fds[0] = -1;
444
445         if (s) {
446                 r = seat_attach_session(s, session);
447                 if (r < 0)
448                         goto fail;
449         }
450
451         r = session_start(session);
452         if (r < 0)
453                 goto fail;
454
455         reply = dbus_message_new_method_return(message);
456         if (!reply) {
457                 r = -ENOMEM;
458                 goto fail;
459         }
460
461         p = session_bus_path(session);
462         if (!p) {
463                 r = -ENOMEM;
464                 goto fail;
465         }
466
467         b = dbus_message_append_args(
468                         reply,
469                         DBUS_TYPE_STRING, &session->id,
470                         DBUS_TYPE_OBJECT_PATH, &p,
471                         DBUS_TYPE_STRING, &session->user->runtime_path,
472                         DBUS_TYPE_UNIX_FD, &pipe_fds[1],
473                         DBUS_TYPE_INVALID);
474         free(p);
475
476         if (!b) {
477                 r = -ENOMEM;
478                 goto fail;
479         }
480
481         close_nointr_nofail(pipe_fds[1]);
482         *_reply = reply;
483
484         return 0;
485
486 fail:
487         strv_free(controllers);
488         strv_free(reset_controllers);
489
490         if (session)
491                 session_add_to_gc_queue(session);
492
493         if (user)
494                 user_add_to_gc_queue(user);
495
496         close_pipe(pipe_fds);
497
498         if (reply)
499                 dbus_message_unref(reply);
500
501         return r;
502 }
503
504 static DBusHandlerResult manager_message_handler(
505                 DBusConnection *connection,
506                 DBusMessage *message,
507                 void *userdata) {
508
509         Manager *m = userdata;
510
511         const BusProperty properties[] = {
512                 { "org.freedesktop.login1.Manager", "ControlGroupHierarchy",  bus_property_append_string,   "s",  m->cgroup_path          },
513                 { "org.freedesktop.login1.Manager", "Controllers",            bus_property_append_strv,     "as", m->controllers          },
514                 { "org.freedesktop.login1.Manager", "NAutoVTs",               bus_property_append_unsigned, "u",  &m->n_autovts           },
515                 { "org.freedesktop.login1.Manager", "KillOnlyUsers",          bus_property_append_strv,     "as", m->kill_only_users      },
516                 { "org.freedesktop.login1.Manager", "KillExcludeUsers",       bus_property_append_strv,     "as", m->kill_exclude_users   },
517                 { "org.freedesktop.login1.Manager", "KillUserProcesses",      bus_property_append_bool,     "b",  &m->kill_user_processes },
518                 { "org.freedesktop.login1.Manager", "IdleHint",               bus_manager_append_idle_hint, "b",  m                       },
519                 { "org.freedesktop.login1.Manager", "IdleSinceHint",          bus_manager_append_idle_hint_since, "t", m                  },
520                 { "org.freedesktop.login1.Manager", "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", m                  },
521                 { NULL, NULL, NULL, NULL, NULL }
522         };
523
524         DBusError error;
525         DBusMessage *reply = NULL;
526         int r;
527
528         assert(connection);
529         assert(message);
530         assert(m);
531
532         dbus_error_init(&error);
533
534         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
535                 const char *name;
536                 char *p;
537                 Session *session;
538                 bool b;
539
540                 if (!dbus_message_get_args(
541                                     message,
542                                     &error,
543                                     DBUS_TYPE_STRING, &name,
544                                     DBUS_TYPE_INVALID))
545                         return bus_send_error_reply(connection, message, &error, -EINVAL);
546
547                 session = hashmap_get(m->sessions, name);
548                 if (!session)
549                         return bus_send_error_reply(connection, message, &error, -ENOENT);
550
551                 reply = dbus_message_new_method_return(message);
552                 if (!reply)
553                         goto oom;
554
555                 p = session_bus_path(session);
556                 if (!p)
557                         goto oom;
558
559                 b = dbus_message_append_args(
560                                 reply,
561                                 DBUS_TYPE_OBJECT_PATH, &p,
562                                 DBUS_TYPE_INVALID);
563                 free(p);
564
565                 if (!b)
566                         goto oom;
567
568         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
569                 uint32_t uid;
570                 char *p;
571                 User *user;
572                 bool b;
573
574                 if (!dbus_message_get_args(
575                                     message,
576                                     &error,
577                                     DBUS_TYPE_UINT32, &uid,
578                                     DBUS_TYPE_INVALID))
579                         return bus_send_error_reply(connection, message, &error, -EINVAL);
580
581                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
582                 if (!user)
583                         return bus_send_error_reply(connection, message, &error, -ENOENT);
584
585                 reply = dbus_message_new_method_return(message);
586                 if (!reply)
587                         goto oom;
588
589                 p = user_bus_path(user);
590                 if (!p)
591                         goto oom;
592
593                 b = dbus_message_append_args(
594                                 reply,
595                                 DBUS_TYPE_OBJECT_PATH, &p,
596                                 DBUS_TYPE_INVALID);
597                 free(p);
598
599                 if (!b)
600                         goto oom;
601
602         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
603                 const char *name;
604                 char *p;
605                 Seat *seat;
606                 bool b;
607
608                 if (!dbus_message_get_args(
609                                     message,
610                                     &error,
611                                     DBUS_TYPE_STRING, &name,
612                                     DBUS_TYPE_INVALID))
613                         return bus_send_error_reply(connection, message, &error, -EINVAL);
614
615                 seat = hashmap_get(m->seats, name);
616                 if (!seat)
617                         return bus_send_error_reply(connection, message, &error, -ENOENT);
618
619                 reply = dbus_message_new_method_return(message);
620                 if (!reply)
621                         goto oom;
622
623                 p = seat_bus_path(seat);
624                 if (!p)
625                         goto oom;
626
627                 b = dbus_message_append_args(
628                                 reply,
629                                 DBUS_TYPE_OBJECT_PATH, &p,
630                                 DBUS_TYPE_INVALID);
631                 free(p);
632
633                 if (!b)
634                         goto oom;
635
636         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
637                 char *p;
638                 Session *session;
639                 Iterator i;
640                 DBusMessageIter iter, sub;
641                 const char *empty = "";
642
643                 reply = dbus_message_new_method_return(message);
644                 if (!reply)
645                         goto oom;
646
647                 dbus_message_iter_init_append(reply, &iter);
648
649                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
650                         goto oom;
651
652                 HASHMAP_FOREACH(session, m->sessions, i) {
653                         DBusMessageIter sub2;
654                         uint32_t uid;
655
656                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
657                                 goto oom;
658
659                         uid = session->user->uid;
660
661                         p = session_bus_path(session);
662                         if (!p)
663                                 goto oom;
664
665                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
666                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
667                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
668                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
669                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
670                                 free(p);
671                                 goto oom;
672                         }
673
674                         free(p);
675
676                         if (!dbus_message_iter_close_container(&sub, &sub2))
677                                 goto oom;
678                 }
679
680                 if (!dbus_message_iter_close_container(&iter, &sub))
681                         goto oom;
682
683         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
684                 char *p;
685                 User *user;
686                 Iterator i;
687                 DBusMessageIter iter, sub;
688
689                 reply = dbus_message_new_method_return(message);
690                 if (!reply)
691                         goto oom;
692
693                 dbus_message_iter_init_append(reply, &iter);
694
695                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
696                         goto oom;
697
698                 HASHMAP_FOREACH(user, m->users, i) {
699                         DBusMessageIter sub2;
700                         uint32_t uid;
701
702                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
703                                 goto oom;
704
705                         uid = user->uid;
706
707                         p = user_bus_path(user);
708                         if (!p)
709                                 goto oom;
710
711                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
712                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
713                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
714                                 free(p);
715                                 goto oom;
716                         }
717
718                         free(p);
719
720                         if (!dbus_message_iter_close_container(&sub, &sub2))
721                                 goto oom;
722                 }
723
724                 if (!dbus_message_iter_close_container(&iter, &sub))
725                         goto oom;
726
727         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
728                 char *p;
729                 Seat *seat;
730                 Iterator i;
731                 DBusMessageIter iter, sub;
732
733                 reply = dbus_message_new_method_return(message);
734                 if (!reply)
735                         goto oom;
736
737                 dbus_message_iter_init_append(reply, &iter);
738
739                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
740                         goto oom;
741
742                 HASHMAP_FOREACH(seat, m->seats, i) {
743                         DBusMessageIter sub2;
744
745                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
746                                 goto oom;
747
748                         p = seat_bus_path(seat);
749                         if (!p)
750                                 goto oom;
751
752                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
753                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
754                                 free(p);
755                                 goto oom;
756                         }
757
758                         free(p);
759
760                         if (!dbus_message_iter_close_container(&sub, &sub2))
761                                 goto oom;
762                 }
763
764                 if (!dbus_message_iter_close_container(&iter, &sub))
765                         goto oom;
766
767         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
768
769                 r = bus_manager_create_session(m, message, &reply);
770                 if (r == -ENOMEM)
771                         goto oom;
772
773                 if (r < 0)
774                         return bus_send_error_reply(connection, message, &error, r);
775
776         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
777                 const char *name;
778                 Session *session;
779
780                 if (!dbus_message_get_args(
781                                     message,
782                                     &error,
783                                     DBUS_TYPE_STRING, &name,
784                                     DBUS_TYPE_INVALID))
785                         return bus_send_error_reply(connection, message, &error, -EINVAL);
786
787                 session = hashmap_get(m->sessions, name);
788                 if (!session)
789                         return bus_send_error_reply(connection, message, &error, -ENOENT);
790
791                 r = session_activate(session);
792                 if (r < 0)
793                         return bus_send_error_reply(connection, message, NULL, r);
794
795                 reply = dbus_message_new_method_return(message);
796                 if (!reply)
797                         goto oom;
798
799         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
800                 const char *name;
801                 Session *session;
802
803                 if (!dbus_message_get_args(
804                                     message,
805                                     &error,
806                                     DBUS_TYPE_STRING, &name,
807                                     DBUS_TYPE_INVALID))
808                         return bus_send_error_reply(connection, message, &error, -EINVAL);
809
810                 session = hashmap_get(m->sessions, name);
811                 if (!session)
812                         return bus_send_error_reply(connection, message, &error, -ENOENT);
813
814                 r = session_stop(session);
815                 if (r < 0)
816                         return bus_send_error_reply(connection, message, NULL, r);
817
818                 reply = dbus_message_new_method_return(message);
819                 if (!reply)
820                         goto oom;
821
822         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
823                 uint32_t uid;
824                 User *user;
825
826                 if (!dbus_message_get_args(
827                                     message,
828                                     &error,
829                                     DBUS_TYPE_UINT32, &uid,
830                                     DBUS_TYPE_INVALID))
831                         return bus_send_error_reply(connection, message, &error, -EINVAL);
832
833                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
834                 if (!user)
835                         return bus_send_error_reply(connection, message, &error, -ENOENT);
836
837                 r = user_stop(user);
838                 if (r < 0)
839                         return bus_send_error_reply(connection, message, NULL, r);
840
841                 reply = dbus_message_new_method_return(message);
842                 if (!reply)
843                         goto oom;
844
845         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
846                 const char *name;
847                 Seat *seat;
848
849                 if (!dbus_message_get_args(
850                                     message,
851                                     &error,
852                                     DBUS_TYPE_STRING, &name,
853                                     DBUS_TYPE_INVALID))
854                         return bus_send_error_reply(connection, message, &error, -EINVAL);
855
856                 seat = hashmap_get(m->seats, name);
857                 if (!seat)
858                         return bus_send_error_reply(connection, message, &error, -ENOENT);
859
860                 r = seat_stop_sessions(seat);
861                 if (r < 0)
862                         return bus_send_error_reply(connection, message, NULL, r);
863
864                 reply = dbus_message_new_method_return(message);
865                 if (!reply)
866                         goto oom;
867
868         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
869                 char *introspection = NULL;
870                 FILE *f;
871                 Iterator i;
872                 Session *session;
873                 Seat *seat;
874                 User *user;
875                 size_t size;
876                 char *p;
877
878                 if (!(reply = dbus_message_new_method_return(message)))
879                         goto oom;
880
881                 /* We roll our own introspection code here, instead of
882                  * relying on bus_default_message_handler() because we
883                  * need to generate our introspection string
884                  * dynamically. */
885
886                 if (!(f = open_memstream(&introspection, &size)))
887                         goto oom;
888
889                 fputs(INTROSPECTION_BEGIN, f);
890
891                 HASHMAP_FOREACH(seat, m->seats, i) {
892                         p = bus_path_escape(seat->id);
893
894                         if (p) {
895                                 fprintf(f, "<node name=\"seat/%s\"/>", p);
896                                 free(p);
897                         }
898                 }
899
900                 HASHMAP_FOREACH(user, m->users, i)
901                         fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
902
903                 HASHMAP_FOREACH(session, m->sessions, i) {
904                         p = bus_path_escape(session->id);
905
906                         if (p) {
907                                 fprintf(f, "<node name=\"session/%s\"/>", p);
908                                 free(p);
909                         }
910                 }
911
912                 fputs(INTROSPECTION_END, f);
913
914                 if (ferror(f)) {
915                         fclose(f);
916                         free(introspection);
917                         goto oom;
918                 }
919
920                 fclose(f);
921
922                 if (!introspection)
923                         goto oom;
924
925                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
926                         free(introspection);
927                         goto oom;
928                 }
929
930                 free(introspection);
931         } else
932                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties);
933
934         if (reply) {
935                 if (!dbus_connection_send(connection, reply, NULL))
936                         goto oom;
937
938                 dbus_message_unref(reply);
939         }
940
941         return DBUS_HANDLER_RESULT_HANDLED;
942
943 oom:
944         if (reply)
945                 dbus_message_unref(reply);
946
947         dbus_error_free(&error);
948
949         return DBUS_HANDLER_RESULT_NEED_MEMORY;
950 }
951
952 const DBusObjectPathVTable bus_manager_vtable = {
953         .message_function = manager_message_handler
954 };
955
956 DBusHandlerResult bus_message_filter(
957                 DBusConnection *connection,
958                 DBusMessage *message,
959                 void *userdata) {
960
961         Manager *m = userdata;
962         DBusError error;
963
964         assert(m);
965         assert(connection);
966         assert(message);
967
968         dbus_error_init(&error);
969
970         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
971                 const char *cgroup;
972
973                 if (!dbus_message_get_args(message, &error,
974                                            DBUS_TYPE_STRING, &cgroup,
975                                            DBUS_TYPE_INVALID))
976                         log_error("Failed to parse Released message: %s", bus_error_message(&error));
977                 else
978                         manager_cgroup_notify_empty(m, cgroup);
979         }
980
981         dbus_error_free(&error);
982
983         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
984 }
985
986 int manager_send_changed(Manager *manager, const char *properties) {
987         DBusMessage *m;
988         int r = -ENOMEM;
989
990         assert(manager);
991
992         m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
993         if (!m)
994                 goto finish;
995
996         if (!dbus_connection_send(manager->bus, m, NULL))
997                 goto finish;
998
999         r = 0;
1000
1001 finish:
1002         if (m)
1003                 dbus_message_unref(m);
1004
1005         return r;
1006 }