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