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