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