chiark / gitweb /
345df9f1cc2bf5601ba0e510bc0ab25dc1b81148
[elogind.git] / src / login / 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 Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 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   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser 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 "mkdir.h"
31 #include "path-util.h"
32 #include "polkit.h"
33 #include "special.h"
34 #include "sleep-config.h"
35 #include "systemd/sd-id128.h"
36 #include "systemd/sd-messages.h"
37 #include "fileio-label.h"
38 #include "label.h"
39 #include "utf8.h"
40 #include "unit-name.h"
41 #include "bus-errors.h"
42 #include "virt.h"
43
44 #define BUS_MANAGER_INTERFACE                                           \
45         " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
46         "  <method name=\"GetSession\">\n"                              \
47         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
48         "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
49         "  </method>\n"                                                 \
50         "  <method name=\"GetSessionByPID\">\n"                         \
51         "   <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n"          \
52         "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
53         "  </method>\n"                                                 \
54         "  <method name=\"GetUser\">\n"                                 \
55         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
56         "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
57         "  </method>\n"                                                 \
58         "  <method name=\"GetUserByPID\">\n"                            \
59         "   <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n"          \
60         "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
61         "  </method>\n"                                                 \
62         "  <method name=\"GetSeat\">\n"                                 \
63         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
64         "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
65         "  </method>\n"                                                 \
66         "  <method name=\"ListSessions\">\n"                            \
67         "   <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
68         "  </method>\n"                                                 \
69         "  <method name=\"ListUsers\">\n"                               \
70         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
71         "  </method>\n"                                                 \
72         "  <method name=\"ListSeats\">\n"                               \
73         "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
74         "  </method>\n"                                                 \
75         "  <method name=\"CreateSession\">\n"                           \
76         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
77         "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
78         "   <arg name=\"service\" type=\"s\" direction=\"in\"/>\n"      \
79         "   <arg name=\"type\" type=\"s\" direction=\"in\"/>\n"         \
80         "   <arg name=\"class\" type=\"s\" direction=\"in\"/>\n"        \
81         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
82         "   <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n"         \
83         "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
84         "   <arg name=\"display\" type=\"s\" direction=\"in\"/>\n"      \
85         "   <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n"       \
86         "   <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n"  \
87         "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
88         "   <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
89         "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
90         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
91         "   <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
92         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
93         "   <arg name=\"seat\" type=\"s\" direction=\"out\"/>\n"        \
94         "   <arg name=\"vtnr\" type=\"u\" direction=\"out\"/>\n"        \
95         "   <arg name=\"existing\" type=\"b\" direction=\"out\"/>\n"    \
96         "  </method>\n"                                                 \
97         "  <method name=\"ReleaseSession\">\n"                          \
98         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
99         "  </method>\n"                                                 \
100         "  <method name=\"ActivateSession\">\n"                         \
101         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
102         "  </method>\n"                                                 \
103         "  <method name=\"ActivateSessionOnSeat\">\n"                   \
104         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
105         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
106         "  </method>\n"                                                 \
107         "  <method name=\"LockSession\">\n"                             \
108         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
109         "  </method>\n"                                                 \
110         "  <method name=\"UnlockSession\">\n"                           \
111         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
112         "  </method>\n"                                                 \
113         "  <method name=\"LockSessions\"/>\n"                           \
114         "  <method name=\"UnlockSessions\"/>\n"                         \
115         "  <method name=\"KillSession\">\n"                             \
116         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
117         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
118         "   <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n"       \
119         "  </method>\n"                                                 \
120         "  <method name=\"KillUser\">\n"                                \
121         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
122         "   <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n"       \
123         "  </method>\n"                                                 \
124         "  <method name=\"TerminateSession\">\n"                        \
125         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
126         "  </method>\n"                                                 \
127         "  <method name=\"TerminateUser\">\n"                           \
128         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
129         "  </method>\n"                                                 \
130         "  <method name=\"TerminateSeat\">\n"                           \
131         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
132         "  </method>\n"                                                 \
133         "  <method name=\"SetUserLinger\">\n"                           \
134         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
135         "   <arg name=\"b\" type=\"b\" direction=\"in\"/>\n"            \
136         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
137         "  </method>\n"                                                 \
138         "  <method name=\"AttachDevice\">\n"                            \
139         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
140         "   <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n"        \
141         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
142         "  </method>\n"                                                 \
143         "  <method name=\"FlushDevices\">\n"                            \
144         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
145         "  </method>\n"                                                 \
146         "  <method name=\"PowerOff\">\n"                                \
147         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
148         "  </method>\n"                                                 \
149         "  <method name=\"Reboot\">\n"                                  \
150         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
151         "  </method>\n"                                                 \
152         "  <method name=\"Suspend\">\n"                                 \
153         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
154         "  </method>\n"                                                 \
155         "  <method name=\"Hibernate\">\n"                               \
156         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
157         "  </method>\n"                                                 \
158         "  <method name=\"HybridSleep\">\n"                             \
159         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
160         "  </method>\n"                                                 \
161         "  <method name=\"CanPowerOff\">\n"                             \
162         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
163         "  </method>\n"                                                 \
164         "  <method name=\"CanReboot\">\n"                               \
165         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
166         "  </method>\n"                                                 \
167         "  <method name=\"CanSuspend\">\n"                              \
168         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
169         "  </method>\n"                                                 \
170         "  <method name=\"CanHibernate\">\n"                            \
171         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
172         "  </method>\n"                                                 \
173         "  <method name=\"CanHybridSleep\">\n"                          \
174         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
175         "  </method>\n"                                                 \
176         "  <method name=\"Inhibit\">\n"                                 \
177         "   <arg name=\"what\" type=\"s\" direction=\"in\"/>\n"         \
178         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
179         "   <arg name=\"why\" type=\"s\" direction=\"in\"/>\n"          \
180         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
181         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
182         "  </method>\n"                                                 \
183         "  <method name=\"ListInhibitors\">\n"                          \
184         "   <arg name=\"inhibitors\" type=\"a(ssssuu)\" direction=\"out\"/>\n" \
185         "  </method>\n"                                                 \
186         "  <signal name=\"SessionNew\">\n"                              \
187         "   <arg name=\"id\" type=\"s\"/>\n"                            \
188         "   <arg name=\"path\" type=\"o\"/>\n"                          \
189         "  </signal>\n"                                                 \
190         "  <signal name=\"SessionRemoved\">\n"                          \
191         "   <arg name=\"id\" type=\"s\"/>\n"                            \
192         "   <arg name=\"path\" type=\"o\"/>\n"                          \
193         "  </signal>\n"                                                 \
194         "  <signal name=\"UserNew\">\n"                                 \
195         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
196         "   <arg name=\"path\" type=\"o\"/>\n"                          \
197         "  </signal>\n"                                                 \
198         "  <signal name=\"UserRemoved\">\n"                             \
199         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
200         "   <arg name=\"path\" type=\"o\"/>\n"                          \
201         "  </signal>\n"                                                 \
202         "  <signal name=\"SeatNew\">\n"                                 \
203         "   <arg name=\"id\" type=\"s\"/>\n"                            \
204         "   <arg name=\"path\" type=\"o\"/>\n"                          \
205         "  </signal>\n"                                                 \
206         "  <signal name=\"SeatRemoved\">\n"                             \
207         "   <arg name=\"id\" type=\"s\"/>\n"                            \
208         "   <arg name=\"path\" type=\"o\"/>\n"                          \
209         "  </signal>\n"                                                 \
210         "  <signal name=\"PrepareForShutdown\">\n"                      \
211         "   <arg name=\"active\" type=\"b\"/>\n"                        \
212         "  </signal>\n"                                                 \
213         "  <signal name=\"PrepareForSleep\">\n"                         \
214         "   <arg name=\"active\" type=\"b\"/>\n"                        \
215         "  </signal>\n"                                                 \
216         "  <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
217         "  <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
218         "  <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
219         "  <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
220         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
221         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
222         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
223         "  <property name=\"BlockInhibited\" type=\"s\" access=\"read\"/>\n" \
224         "  <property name=\"DelayInhibited\" type=\"s\" access=\"read\"/>\n" \
225         "  <property name=\"InhibitDelayMaxUSec\" type=\"t\" access=\"read\"/>\n" \
226         "  <property name=\"HandlePowerKey\" type=\"s\" access=\"read\"/>\n" \
227         "  <property name=\"HandleSuspendKey\" type=\"s\" access=\"read\"/>\n" \
228         "  <property name=\"HandleHibernateKey\" type=\"s\" access=\"read\"/>\n" \
229         "  <property name=\"HandleLidSwitch\" type=\"s\" access=\"read\"/>\n" \
230         "  <property name=\"IdleAction\" type=\"s\" access=\"read\"/>\n" \
231         "  <property name=\"IdleActionUSec\" type=\"t\" access=\"read\"/>\n" \
232         "  <property name=\"PreparingForShutdown\" type=\"b\" access=\"read\"/>\n" \
233         "  <property name=\"PreparingForSleep\" type=\"b\" access=\"read\"/>\n" \
234         " </interface>\n"
235
236 #define INTROSPECTION_BEGIN                                             \
237         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
238         "<node>\n"                                                      \
239         BUS_MANAGER_INTERFACE                                           \
240         BUS_PROPERTIES_INTERFACE                                        \
241         BUS_PEER_INTERFACE                                              \
242         BUS_INTROSPECTABLE_INTERFACE
243
244 #define INTROSPECTION_END                                               \
245         "</node>\n"
246
247 #define INTERFACES_LIST                              \
248         BUS_GENERIC_INTERFACES_LIST                  \
249         "org.freedesktop.login1.Manager\0"
250
251 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
252         Manager *m = data;
253         dbus_bool_t b;
254
255         assert(i);
256         assert(property);
257         assert(m);
258
259         b = manager_get_idle_hint(m, NULL) > 0;
260         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
261                 return -ENOMEM;
262
263         return 0;
264 }
265
266 static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
267         Manager *m = data;
268         dual_timestamp t;
269         uint64_t u;
270
271         assert(i);
272         assert(property);
273         assert(m);
274
275         manager_get_idle_hint(m, &t);
276         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
277
278         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
279                 return -ENOMEM;
280
281         return 0;
282 }
283
284 static int bus_manager_append_inhibited(DBusMessageIter *i, const char *property, void *data) {
285         Manager *m = data;
286         InhibitWhat w;
287         const char *p;
288
289         w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY);
290         p = inhibit_what_to_string(w);
291
292         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &p))
293                 return -ENOMEM;
294
295         return 0;
296 }
297
298 static int bus_manager_append_preparing(DBusMessageIter *i, const char *property, void *data) {
299         Manager *m = data;
300         dbus_bool_t b;
301
302         assert(i);
303         assert(property);
304
305         if (streq(property, "PreparingForShutdown"))
306                 b = !!(m->action_what & INHIBIT_SHUTDOWN);
307         else
308                 b = !!(m->action_what & INHIBIT_SLEEP);
309
310         dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b);
311         return 0;
312 }
313
314 static int bus_manager_create_session(Manager *m, DBusMessage *message) {
315
316         const char *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *service;
317         uint32_t uid, leader, audit_id = 0;
318         _cleanup_free_ char *id = NULL;
319         Session *session = NULL;
320         User *user = NULL;
321         Seat *seat = NULL;
322         DBusMessageIter iter;
323         dbus_bool_t remote;
324         uint32_t vtnr = 0;
325         SessionType t;
326         SessionClass c;
327         bool b;
328         int r;
329
330         assert(m);
331         assert(message);
332
333         if (!dbus_message_iter_init(message, &iter) ||
334             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
335                 return -EINVAL;
336
337         dbus_message_iter_get_basic(&iter, &uid);
338
339         if (!dbus_message_iter_next(&iter) ||
340             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
341                 return -EINVAL;
342
343         dbus_message_iter_get_basic(&iter, &leader);
344
345         if (!dbus_message_iter_next(&iter) ||
346             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
347                 return -EINVAL;
348
349         dbus_message_iter_get_basic(&iter, &service);
350
351         if (!dbus_message_iter_next(&iter) ||
352             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
353                 return -EINVAL;
354
355         dbus_message_iter_get_basic(&iter, &type);
356         if (isempty(type))
357                 t = _SESSION_TYPE_INVALID;
358         else {
359                 t = session_type_from_string(type);
360                 if (t < 0)
361                         return -EINVAL;
362         }
363
364         if (!dbus_message_iter_next(&iter) ||
365             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
366                 return -EINVAL;
367
368         dbus_message_iter_get_basic(&iter, &class);
369         if (isempty(class))
370                 c = _SESSION_CLASS_INVALID;
371         else {
372                 c = session_class_from_string(class);
373                 if (c < 0)
374                         return -EINVAL;
375         }
376
377         if (!dbus_message_iter_next(&iter) ||
378             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
379                 return -EINVAL;
380
381         dbus_message_iter_get_basic(&iter, &cseat);
382
383         if (isempty(cseat))
384                 seat = NULL;
385         else {
386                 seat = hashmap_get(m->seats, cseat);
387                 if (!seat)
388                         return -ENOENT;
389         }
390
391         if (!dbus_message_iter_next(&iter) ||
392             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
393                 return -EINVAL;
394
395         dbus_message_iter_get_basic(&iter, &vtnr);
396
397         if (!dbus_message_iter_next(&iter) ||
398             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
399                 return -EINVAL;
400
401         dbus_message_iter_get_basic(&iter, &tty);
402
403         if (tty_is_vc(tty)) {
404                 int v;
405
406                 if (!seat)
407                         seat = m->vtconsole;
408                 else if (seat != m->vtconsole)
409                         return -EINVAL;
410
411                 v = vtnr_from_tty(tty);
412
413                 if (v <= 0)
414                         return v < 0 ? v : -EINVAL;
415
416                 if (vtnr <= 0)
417                         vtnr = (uint32_t) v;
418                 else if (vtnr != (uint32_t) v)
419                         return -EINVAL;
420         } else if (tty_is_console(tty)) {
421
422                 if (!seat)
423                         seat = m->vtconsole;
424                 else if (seat != m->vtconsole)
425                         return -EINVAL;
426
427                 if (vtnr != 0)
428                         return -EINVAL;
429         }
430
431         if (seat) {
432                 if (seat_can_multi_session(seat)) {
433                         if (vtnr > 63)
434                                 return -EINVAL;
435                 } else {
436                         if (vtnr != 0)
437                                 return -EINVAL;
438                 }
439         }
440
441         if (!dbus_message_iter_next(&iter) ||
442             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
443                 return -EINVAL;
444
445         dbus_message_iter_get_basic(&iter, &display);
446
447         if (!dbus_message_iter_next(&iter) ||
448             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
449                 return -EINVAL;
450
451         if (t == _SESSION_TYPE_INVALID) {
452                 if (!isempty(display))
453                         t = SESSION_X11;
454                 else if (!isempty(tty))
455                         t = SESSION_TTY;
456                 else
457                         t = SESSION_UNSPECIFIED;
458         }
459
460         if (c == _SESSION_CLASS_INVALID) {
461                 if (!isempty(display) || !isempty(tty))
462                         c = SESSION_USER;
463                 else
464                         c = SESSION_BACKGROUND;
465         }
466
467         dbus_message_iter_get_basic(&iter, &remote);
468
469         if (!dbus_message_iter_next(&iter) ||
470             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
471                 return -EINVAL;
472
473         dbus_message_iter_get_basic(&iter, &remote_user);
474
475         if (!dbus_message_iter_next(&iter) ||
476             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
477                 return -EINVAL;
478
479         dbus_message_iter_get_basic(&iter, &remote_host);
480
481         if (leader <= 0) {
482                 leader = bus_get_unix_process_id(m->bus, dbus_message_get_sender(message), NULL);
483                 if (leader == 0)
484                         return -EINVAL;
485         }
486
487         r = manager_get_session_by_pid(m, leader, &session);
488         if (session) {
489                 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
490                 _cleanup_free_ char *path = NULL;
491                 _cleanup_close_ int fifo_fd = -1;
492                 dbus_bool_t exists;
493
494                 /* Session already exists, client is probably
495                  * something like "su" which changes uid but is still
496                  * the same session */
497
498                 fifo_fd = session_create_fifo(session);
499                 if (fifo_fd < 0) {
500                         r = fifo_fd;
501                         goto fail;
502                 }
503
504                 path = session_bus_path(session);
505                 if (!path) {
506                         r = -ENOMEM;
507                         goto fail;
508                 }
509
510                 reply = dbus_message_new_method_return(message);
511                 if (!reply) {
512                         r = -ENOMEM;
513                         goto fail;
514                 }
515
516                 cseat = session->seat ? session->seat->id : "";
517                 vtnr = session->vtnr;
518                 exists = true;
519
520                 b = dbus_message_append_args(
521                                 reply,
522                                 DBUS_TYPE_STRING, &session->id,
523                                 DBUS_TYPE_OBJECT_PATH, &path,
524                                 DBUS_TYPE_STRING, &session->user->runtime_path,
525                                 DBUS_TYPE_UNIX_FD, &fifo_fd,
526                                 DBUS_TYPE_STRING, &cseat,
527                                 DBUS_TYPE_UINT32, &vtnr,
528                                 DBUS_TYPE_BOOLEAN, &exists,
529                                 DBUS_TYPE_INVALID);
530                 if (!b) {
531                         r = -ENOMEM;
532                         goto fail;
533                 }
534
535                 if (!dbus_connection_send(m->bus, reply, NULL)) {
536                         r = -ENOMEM;
537                         goto fail;
538                 }
539
540                 return 0;
541         }
542
543         audit_session_from_pid(leader, &audit_id);
544         if (audit_id > 0) {
545                 /* Keep our session IDs and the audit session IDs in sync */
546
547                 if (asprintf(&id, "%lu", (unsigned long) audit_id) < 0) {
548                         r = -ENOMEM;
549                         goto fail;
550                 }
551
552                 /* Wut? There's already a session by this name and we
553                  * didn't find it above? Weird, then let's not trust
554                  * the audit data and let's better register a new
555                  * ID */
556                 if (hashmap_get(m->sessions, id)) {
557                         audit_id = 0;
558
559                         free(id);
560                         id = NULL;
561                 }
562         }
563
564         if (!id) {
565                 do {
566                         free(id);
567                         id = NULL;
568
569                         if (asprintf(&id, "c%lu", ++m->session_counter) < 0) {
570                                 r = -ENOMEM;
571                                 goto fail;
572                         }
573
574                 } while (hashmap_get(m->sessions, id));
575         }
576
577         r = manager_add_user_by_uid(m, uid, &user);
578         if (r < 0)
579                 goto fail;
580
581         r = manager_add_session(m, id, &session);
582         if (r < 0)
583                 goto fail;
584
585         session_set_user(session, user);
586
587         session->leader = leader;
588         session->audit_id = audit_id;
589         session->type = t;
590         session->class = c;
591         session->remote = remote;
592         session->vtnr = vtnr;
593
594         if (!isempty(tty)) {
595                 session->tty = strdup(tty);
596                 if (!session->tty) {
597                         r = -ENOMEM;
598                         goto fail;
599                 }
600         }
601
602         if (!isempty(display)) {
603                 session->display = strdup(display);
604                 if (!session->display) {
605                         r = -ENOMEM;
606                         goto fail;
607                 }
608         }
609
610         if (!isempty(remote_user)) {
611                 session->remote_user = strdup(remote_user);
612                 if (!session->remote_user) {
613                         r = -ENOMEM;
614                         goto fail;
615                 }
616         }
617
618         if (!isempty(remote_host)) {
619                 session->remote_host = strdup(remote_host);
620                 if (!session->remote_host) {
621                         r = -ENOMEM;
622                         goto fail;
623                 }
624         }
625
626         if (!isempty(service)) {
627                 session->service = strdup(service);
628                 if (!session->service) {
629                         r = -ENOMEM;
630                         goto fail;
631                 }
632         }
633
634         if (seat) {
635                 r = seat_attach_session(seat, session);
636                 if (r < 0)
637                         goto fail;
638         }
639
640         r = session_start(session);
641         if (r < 0)
642                 goto fail;
643
644         session->create_message = dbus_message_ref(message);
645
646         /* Now, let's wait until the slice unit and stuff got
647          * created. We send the reply back from
648          * session_send_create_reply().*/
649
650         return 0;
651
652 fail:
653         if (session)
654                 session_add_to_gc_queue(session);
655
656         if (user)
657                 user_add_to_gc_queue(user);
658
659         return r;
660 }
661
662 static int bus_manager_inhibit(
663                 Manager *m,
664                 DBusConnection *connection,
665                 DBusMessage *message,
666                 DBusError *error,
667                 DBusMessage **_reply) {
668
669         Inhibitor *i = NULL;
670         char *id = NULL;
671         const char *who, *why, *what, *mode;
672         pid_t pid;
673         InhibitWhat w;
674         InhibitMode mm;
675         unsigned long ul;
676         int r, fifo_fd = -1;
677         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
678
679         assert(m);
680         assert(connection);
681         assert(message);
682         assert(error);
683         assert(_reply);
684
685         if (!dbus_message_get_args(
686                             message,
687                             error,
688                             DBUS_TYPE_STRING, &what,
689                             DBUS_TYPE_STRING, &who,
690                             DBUS_TYPE_STRING, &why,
691                             DBUS_TYPE_STRING, &mode,
692                             DBUS_TYPE_INVALID)) {
693                 r = -EIO;
694                 goto fail;
695         }
696
697         w = inhibit_what_from_string(what);
698         if (w <= 0) {
699                 r = -EINVAL;
700                 goto fail;
701         }
702
703         mm = inhibit_mode_from_string(mode);
704         if (mm < 0) {
705                 r = -EINVAL;
706                 goto fail;
707         }
708
709         /* Delay is only supported for shutdown/sleep */
710         if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP))) {
711                 r = -EINVAL;
712                 goto fail;
713         }
714
715         /* Don't allow taking delay locks while we are already
716          * executing the operation. We shouldn't create the impression
717          * that the lock was successful if the machine is about to go
718          * down/suspend any moment. */
719         if (m->action_what & w) {
720                 r = -EALREADY;
721                 goto fail;
722         }
723
724         r = verify_polkit(connection, message,
725                           w == INHIBIT_SHUTDOWN             ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
726                           w == INHIBIT_SLEEP                ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep"    : "org.freedesktop.login1.inhibit-delay-sleep") :
727                           w == INHIBIT_IDLE                 ? "org.freedesktop.login1.inhibit-block-idle" :
728                           w == INHIBIT_HANDLE_POWER_KEY     ? "org.freedesktop.login1.inhibit-handle-power-key" :
729                           w == INHIBIT_HANDLE_SUSPEND_KEY   ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
730                           w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
731                                                               "org.freedesktop.login1.inhibit-handle-lid-switch",
732                           false, NULL, error);
733         if (r < 0)
734                 goto fail;
735
736         ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
737         if (ul == (unsigned long) -1) {
738                 r = -EIO;
739                 goto fail;
740         }
741
742         pid = bus_get_unix_process_id(connection, dbus_message_get_sender(message), error);
743         if (pid <= 0) {
744                 r = -EIO;
745                 goto fail;
746         }
747
748         do {
749                 free(id);
750                 id = NULL;
751
752                 if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0) {
753                         r = -ENOMEM;
754                         goto fail;
755                 }
756         } while (hashmap_get(m->inhibitors, id));
757
758         r = manager_add_inhibitor(m, id, &i);
759         free(id);
760
761         if (r < 0)
762                 goto fail;
763
764         i->what = w;
765         i->mode = mm;
766         i->pid = pid;
767         i->uid = (uid_t) ul;
768         i->why = strdup(why);
769         i->who = strdup(who);
770
771         if (!i->why || !i->who) {
772                 r = -ENOMEM;
773                 goto fail;
774         }
775
776         fifo_fd = inhibitor_create_fifo(i);
777         if (fifo_fd < 0) {
778                 r = fifo_fd;
779                 goto fail;
780         }
781
782         reply = dbus_message_new_method_return(message);
783         if (!reply) {
784                 r = -ENOMEM;
785                 goto fail;
786         }
787
788         if (!dbus_message_append_args(
789                             reply,
790                             DBUS_TYPE_UNIX_FD, &fifo_fd,
791                             DBUS_TYPE_INVALID)) {
792                 r = -ENOMEM;
793                 goto fail;
794         }
795
796         close_nointr_nofail(fifo_fd);
797         *_reply = reply;
798         reply = NULL;
799
800         inhibitor_start(i);
801
802         return 0;
803
804 fail:
805         if (i)
806                 inhibitor_free(i);
807
808         if (fifo_fd >= 0)
809                 close_nointr_nofail(fifo_fd);
810
811         return r;
812 }
813
814 static int trigger_device(Manager *m, struct udev_device *d) {
815         struct udev_enumerate *e;
816         struct udev_list_entry *first, *item;
817         int r;
818
819         assert(m);
820
821         e = udev_enumerate_new(m->udev);
822         if (!e) {
823                 r = -ENOMEM;
824                 goto finish;
825         }
826
827         if (d) {
828                 if (udev_enumerate_add_match_parent(e, d) < 0) {
829                         r = -EIO;
830                         goto finish;
831                 }
832         }
833
834         if (udev_enumerate_scan_devices(e) < 0) {
835                 r = -EIO;
836                 goto finish;
837         }
838
839         first = udev_enumerate_get_list_entry(e);
840         udev_list_entry_foreach(item, first) {
841                 char *t;
842                 const char *p;
843
844                 p = udev_list_entry_get_name(item);
845
846                 t = strappend(p, "/uevent");
847                 if (!t) {
848                         r = -ENOMEM;
849                         goto finish;
850                 }
851
852                 write_string_file(t, "change");
853                 free(t);
854         }
855
856         r = 0;
857
858 finish:
859         if (e)
860                 udev_enumerate_unref(e);
861
862         return r;
863 }
864
865 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
866         struct udev_device *d;
867         _cleanup_free_ char *rule = NULL, *file = NULL;
868         const char *id_for_seat;
869         int r;
870
871         assert(m);
872         assert(seat);
873         assert(sysfs);
874
875         d = udev_device_new_from_syspath(m->udev, sysfs);
876         if (!d)
877                 return -ENODEV;
878
879         if (!udev_device_has_tag(d, "seat")) {
880                 r = -ENODEV;
881                 goto finish;
882         }
883
884         id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
885         if (!id_for_seat) {
886                 r = -ENODEV;
887                 goto finish;
888         }
889
890         if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
891                 r = -ENOMEM;
892                 goto finish;
893         }
894
895         if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
896                 r = -ENOMEM;
897                 goto finish;
898         }
899
900         mkdir_p_label("/etc/udev/rules.d", 0755);
901         label_init("/etc");
902         r = write_string_file_atomic_label(file, rule);
903         if (r < 0)
904                 goto finish;
905
906         r = trigger_device(m, d);
907
908 finish:
909         if (d)
910                 udev_device_unref(d);
911
912         return r;
913 }
914
915 static int flush_devices(Manager *m) {
916         _cleanup_closedir_ DIR *d;
917
918         assert(m);
919
920         d = opendir("/etc/udev/rules.d");
921         if (!d) {
922                 if (errno != ENOENT)
923                         log_warning("Failed to open /etc/udev/rules.d: %m");
924         } else {
925                 struct dirent *de;
926
927                 while ((de = readdir(d))) {
928
929                         if (!dirent_is_file(de))
930                                 continue;
931
932                         if (!startswith(de->d_name, "72-seat-"))
933                                 continue;
934
935                         if (!endswith(de->d_name, ".rules"))
936                                 continue;
937
938                         if (unlinkat(dirfd(d), de->d_name, 0) < 0)
939                                 log_warning("Failed to unlink %s: %m", de->d_name);
940                 }
941         }
942
943         return trigger_device(m, NULL);
944 }
945
946 static int have_multiple_sessions(
947                 Manager *m,
948                 uid_t uid) {
949
950         Session *session;
951         Iterator i;
952
953         assert(m);
954
955         /* Check for other users' sessions. Greeter sessions do not
956          * count, and non-login sessions do not count either. */
957         HASHMAP_FOREACH(session, m->sessions, i)
958                 if (session->class == SESSION_USER &&
959                     session->user->uid != uid)
960                         return true;
961
962         return false;
963 }
964
965 static int bus_manager_log_shutdown(
966                 Manager *m,
967                 InhibitWhat w,
968                 const char *unit_name) {
969
970         const char *p, *q;
971
972         assert(m);
973         assert(unit_name);
974
975         if (w != INHIBIT_SHUTDOWN)
976                 return 0;
977
978         if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) {
979                 p = "MESSAGE=System is powering down.";
980                 q = "SHUTDOWN=power-off";
981         } else if (streq(unit_name, SPECIAL_HALT_TARGET)) {
982                 p = "MESSAGE=System is halting.";
983                 q = "SHUTDOWN=halt";
984         } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) {
985                 p = "MESSAGE=System is rebooting.";
986                 q = "SHUTDOWN=reboot";
987         } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) {
988                 p = "MESSAGE=System is rebooting with kexec.";
989                 q = "SHUTDOWN=kexec";
990         } else {
991                 p = "MESSAGE=System is shutting down.";
992                 q = NULL;
993         }
994
995         return log_struct(LOG_NOTICE, MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
996                           p,
997                           q, NULL);
998 }
999
1000 static int execute_shutdown_or_sleep(
1001                 Manager *m,
1002                 InhibitWhat w,
1003                 const char *unit_name,
1004                 DBusError *error) {
1005
1006         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1007         const char *mode = "replace-irreversibly", *p;
1008         int r;
1009         char *c;
1010
1011         assert(m);
1012         assert(w >= 0);
1013         assert(w < _INHIBIT_WHAT_MAX);
1014         assert(unit_name);
1015
1016         bus_manager_log_shutdown(m, w, unit_name);
1017
1018         r = bus_method_call_with_reply(
1019                         m->bus,
1020                         "org.freedesktop.systemd1",
1021                         "/org/freedesktop/systemd1",
1022                         "org.freedesktop.systemd1.Manager",
1023                         "StartUnit",
1024                         &reply,
1025                         error,
1026                         DBUS_TYPE_STRING, &unit_name,
1027                         DBUS_TYPE_STRING, &mode,
1028                         DBUS_TYPE_INVALID);
1029         if (r < 0)
1030                 return r;
1031
1032         if (!dbus_message_get_args(
1033                             reply,
1034                             error,
1035                             DBUS_TYPE_OBJECT_PATH, &p,
1036                             DBUS_TYPE_INVALID))
1037                 return -EINVAL;
1038
1039         c = strdup(p);
1040         if (!c)
1041                 return -ENOMEM;
1042
1043         m->action_unit = unit_name;
1044         free(m->action_job);
1045         m->action_job = c;
1046         m->action_what = w;
1047
1048         return 0;
1049 }
1050
1051 static int delay_shutdown_or_sleep(
1052                 Manager *m,
1053                 InhibitWhat w,
1054                 const char *unit_name) {
1055
1056         assert(m);
1057         assert(w >= 0);
1058         assert(w < _INHIBIT_WHAT_MAX);
1059         assert(unit_name);
1060
1061         m->action_timestamp = now(CLOCK_MONOTONIC);
1062         m->action_unit = unit_name;
1063         m->action_what = w;
1064
1065         return 0;
1066 }
1067
1068 static int bus_manager_can_shutdown_or_sleep(
1069                 Manager *m,
1070                 DBusConnection *connection,
1071                 DBusMessage *message,
1072                 InhibitWhat w,
1073                 const char *action,
1074                 const char *action_multiple_sessions,
1075                 const char *action_ignore_inhibit,
1076                 const char *sleep_verb,
1077                 DBusError *error,
1078                 DBusMessage **_reply) {
1079
1080         bool multiple_sessions, challenge, blocked, b;
1081         const char *result = NULL;
1082         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1083         int r;
1084         unsigned long ul;
1085
1086         assert(m);
1087         assert(connection);
1088         assert(message);
1089         assert(w >= 0);
1090         assert(w <= _INHIBIT_WHAT_MAX);
1091         assert(action);
1092         assert(action_multiple_sessions);
1093         assert(action_ignore_inhibit);
1094         assert(error);
1095         assert(_reply);
1096
1097         if (sleep_verb) {
1098                 r = can_sleep(sleep_verb);
1099                 if (r < 0)
1100                         return r;
1101                 if (r == 0) {
1102                         result = "na";
1103                         goto finish;
1104                 }
1105         }
1106
1107         ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
1108         if (ul == (unsigned long) -1)
1109                 return -EIO;
1110
1111         r = have_multiple_sessions(m, (uid_t) ul);
1112         if (r < 0)
1113                 return r;
1114
1115         multiple_sessions = r > 0;
1116         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, (uid_t) ul);
1117
1118         if (multiple_sessions) {
1119                 r = verify_polkit(connection, message, action_multiple_sessions, false, &challenge, error);
1120                 if (r < 0)
1121                         return r;
1122
1123                 if (r > 0)
1124                         result = "yes";
1125                 else if (challenge)
1126                         result = "challenge";
1127                 else
1128                         result = "no";
1129         }
1130
1131         if (blocked) {
1132                 r = verify_polkit(connection, message, action_ignore_inhibit, false, &challenge, error);
1133                 if (r < 0)
1134                         return r;
1135
1136                 if (r > 0 && !result)
1137                         result = "yes";
1138                 else if (challenge && (!result || streq(result, "yes")))
1139                         result = "challenge";
1140                 else
1141                         result = "no";
1142         }
1143
1144         if (!multiple_sessions && !blocked) {
1145                 /* If neither inhibit nor multiple sessions
1146                  * apply then just check the normal policy */
1147
1148                 r = verify_polkit(connection, message, action, false, &challenge, error);
1149                 if (r < 0)
1150                         return r;
1151
1152                 if (r > 0)
1153                         result = "yes";
1154                 else if (challenge)
1155                         result = "challenge";
1156                 else
1157                         result = "no";
1158         }
1159
1160 finish:
1161         reply = dbus_message_new_method_return(message);
1162         if (!reply)
1163                 return -ENOMEM;
1164
1165         b = dbus_message_append_args(
1166                         reply,
1167                         DBUS_TYPE_STRING, &result,
1168                         DBUS_TYPE_INVALID);
1169         if (!b)
1170                 return -ENOMEM;
1171
1172         *_reply = reply;
1173         reply = NULL;
1174         return 0;
1175 }
1176
1177 static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
1178         static const char * const signal_name[_INHIBIT_WHAT_MAX] = {
1179                 [INHIBIT_SHUTDOWN] = "PrepareForShutdown",
1180                 [INHIBIT_SLEEP] = "PrepareForSleep"
1181         };
1182
1183         dbus_bool_t active = _active;
1184         _cleanup_dbus_message_unref_ DBusMessage *message = NULL;
1185
1186         assert(m);
1187         assert(w >= 0);
1188         assert(w < _INHIBIT_WHAT_MAX);
1189         assert(signal_name[w]);
1190
1191         message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", signal_name[w]);
1192         if (!message)
1193                 return -ENOMEM;
1194
1195         if (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) ||
1196             !dbus_connection_send(m->bus, message, NULL))
1197                 return -ENOMEM;
1198
1199         return 0;
1200 }
1201
1202 int bus_manager_shutdown_or_sleep_now_or_later(
1203                 Manager *m,
1204                 const char *unit_name,
1205                 InhibitWhat w,
1206                 DBusError *error) {
1207
1208         bool delayed;
1209         int r;
1210
1211         assert(m);
1212         assert(unit_name);
1213         assert(w >= 0);
1214         assert(w <= _INHIBIT_WHAT_MAX);
1215         assert(!m->action_job);
1216
1217         /* Tell everybody to prepare for shutdown/sleep */
1218         send_prepare_for(m, w, true);
1219
1220         delayed =
1221                 m->inhibit_delay_max > 0 &&
1222                 manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0);
1223
1224         if (delayed)
1225                 /* Shutdown is delayed, keep in mind what we
1226                  * want to do, and start a timeout */
1227                 r = delay_shutdown_or_sleep(m, w, unit_name);
1228         else
1229                 /* Shutdown is not delayed, execute it
1230                  * immediately */
1231                 r = execute_shutdown_or_sleep(m, w, unit_name, error);
1232
1233         return r;
1234 }
1235
1236 static int bus_manager_do_shutdown_or_sleep(
1237                 Manager *m,
1238                 DBusConnection *connection,
1239                 DBusMessage *message,
1240                 const char *unit_name,
1241                 InhibitWhat w,
1242                 const char *action,
1243                 const char *action_multiple_sessions,
1244                 const char *action_ignore_inhibit,
1245                 const char *sleep_verb,
1246                 DBusError *error,
1247                 DBusMessage **_reply) {
1248
1249         dbus_bool_t interactive;
1250         bool multiple_sessions, blocked;
1251         DBusMessage *reply = NULL;
1252         int r;
1253         unsigned long ul;
1254
1255         assert(m);
1256         assert(connection);
1257         assert(message);
1258         assert(unit_name);
1259         assert(w >= 0);
1260         assert(w <= _INHIBIT_WHAT_MAX);
1261         assert(action);
1262         assert(action_multiple_sessions);
1263         assert(action_ignore_inhibit);
1264         assert(error);
1265         assert(_reply);
1266
1267         /* Don't allow multiple jobs being executed at the same time */
1268         if (m->action_what)
1269                 return -EALREADY;
1270
1271         if (!dbus_message_get_args(
1272                             message,
1273                             error,
1274                             DBUS_TYPE_BOOLEAN, &interactive,
1275                             DBUS_TYPE_INVALID))
1276                 return -EINVAL;
1277
1278         if (sleep_verb) {
1279                 r = can_sleep(sleep_verb);
1280                 if (r < 0)
1281                         return r;
1282
1283                 if (r == 0)
1284                         return -ENOTSUP;
1285         }
1286
1287         ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
1288         if (ul == (unsigned long) -1)
1289                 return -EIO;
1290
1291         r = have_multiple_sessions(m, (uid_t) ul);
1292         if (r < 0)
1293                 return r;
1294
1295         multiple_sessions = r > 0;
1296         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, (uid_t) ul);
1297
1298         if (multiple_sessions) {
1299                 r = verify_polkit(connection, message, action_multiple_sessions, interactive, NULL, error);
1300                 if (r < 0)
1301                         return r;
1302         }
1303
1304         if (blocked) {
1305                 r = verify_polkit(connection, message, action_ignore_inhibit, interactive, NULL, error);
1306                 if (r < 0)
1307                         return r;
1308         }
1309
1310         if (!multiple_sessions && !blocked) {
1311                 r = verify_polkit(connection, message, action, interactive, NULL, error);
1312                 if (r < 0)
1313                         return r;
1314         }
1315
1316         r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
1317         if (r < 0)
1318                 return r;
1319
1320         reply = dbus_message_new_method_return(message);
1321         if (!reply)
1322                 return -ENOMEM;
1323
1324         *_reply = reply;
1325         return 0;
1326 }
1327
1328 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_action, handle_action, HandleAction);
1329
1330 static const BusProperty bus_login_manager_properties[] = {
1331         { "NAutoVTs",               bus_property_append_unsigned,       "u",  offsetof(Manager, n_autovts)           },
1332         { "KillOnlyUsers",          bus_property_append_strv,           "as", offsetof(Manager, kill_only_users),    true },
1333         { "KillExcludeUsers",       bus_property_append_strv,           "as", offsetof(Manager, kill_exclude_users), true },
1334         { "KillUserProcesses",      bus_property_append_bool,           "b",  offsetof(Manager, kill_user_processes) },
1335         { "IdleHint",               bus_manager_append_idle_hint,       "b",  0 },
1336         { "IdleSinceHint",          bus_manager_append_idle_hint_since, "t",  0 },
1337         { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t",  0 },
1338         { "BlockInhibited",         bus_manager_append_inhibited,       "s",  0 },
1339         { "DelayInhibited",         bus_manager_append_inhibited,       "s",  0 },
1340         { "InhibitDelayMaxUSec",    bus_property_append_usec,           "t",  offsetof(Manager, inhibit_delay_max)   },
1341         { "HandlePowerKey",         bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_power_key)    },
1342         { "HandleSuspendKey",       bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_suspend_key)  },
1343         { "HandleHibernateKey",     bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_hibernate_key)},
1344         { "HandleLidSwitch",        bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_lid_switch)   },
1345         { "IdleAction",             bus_manager_append_handle_action,   "s",  offsetof(Manager, idle_action)         },
1346         { "IdleActionUSec",         bus_property_append_usec,           "t",  offsetof(Manager, idle_action_usec) },
1347         { "PreparingForShutdown",   bus_manager_append_preparing,       "b",  0 },
1348         { "PreparingForSleep",      bus_manager_append_preparing,       "b",  0 },
1349         { NULL, }
1350 };
1351
1352 static DBusHandlerResult manager_message_handler(
1353                 DBusConnection *connection,
1354                 DBusMessage *message,
1355                 void *userdata) {
1356
1357         Manager *m = userdata;
1358
1359         DBusError error;
1360         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1361         int r;
1362
1363         assert(connection);
1364         assert(message);
1365         assert(m);
1366
1367         dbus_error_init(&error);
1368
1369         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
1370                 const char *name;
1371                 char *p;
1372                 Session *session;
1373                 bool b;
1374
1375                 if (!dbus_message_get_args(
1376                                     message,
1377                                     &error,
1378                                     DBUS_TYPE_STRING, &name,
1379                                     DBUS_TYPE_INVALID))
1380                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1381
1382                 session = hashmap_get(m->sessions, name);
1383                 if (!session)
1384                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1385
1386                 reply = dbus_message_new_method_return(message);
1387                 if (!reply)
1388                         goto oom;
1389
1390                 p = session_bus_path(session);
1391                 if (!p)
1392                         goto oom;
1393
1394                 b = dbus_message_append_args(
1395                                 reply,
1396                                 DBUS_TYPE_OBJECT_PATH, &p,
1397                                 DBUS_TYPE_INVALID);
1398                 free(p);
1399
1400                 if (!b)
1401                         goto oom;
1402
1403         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) {
1404                 uint32_t pid;
1405                 char *p;
1406                 Session *session;
1407                 bool b;
1408
1409                 if (!dbus_message_get_args(
1410                                     message,
1411                                     &error,
1412                                     DBUS_TYPE_UINT32, &pid,
1413                                     DBUS_TYPE_INVALID))
1414                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1415
1416                 r = manager_get_session_by_pid(m, pid, &session);
1417                 if (r <= 0)
1418                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
1419
1420                 reply = dbus_message_new_method_return(message);
1421                 if (!reply)
1422                         goto oom;
1423
1424                 p = session_bus_path(session);
1425                 if (!p)
1426                         goto oom;
1427
1428                 b = dbus_message_append_args(
1429                                 reply,
1430                                 DBUS_TYPE_OBJECT_PATH, &p,
1431                                 DBUS_TYPE_INVALID);
1432                 free(p);
1433
1434                 if (!b)
1435                         goto oom;
1436
1437         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
1438                 uint32_t uid;
1439                 char *p;
1440                 User *user;
1441                 bool b;
1442
1443                 if (!dbus_message_get_args(
1444                                     message,
1445                                     &error,
1446                                     DBUS_TYPE_UINT32, &uid,
1447                                     DBUS_TYPE_INVALID))
1448                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1449
1450                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1451                 if (!user)
1452                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1453
1454                 reply = dbus_message_new_method_return(message);
1455                 if (!reply)
1456                         goto oom;
1457
1458                 p = user_bus_path(user);
1459                 if (!p)
1460                         goto oom;
1461
1462                 b = dbus_message_append_args(
1463                                 reply,
1464                                 DBUS_TYPE_OBJECT_PATH, &p,
1465                                 DBUS_TYPE_INVALID);
1466                 free(p);
1467
1468                 if (!b)
1469                         goto oom;
1470
1471         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUserByPID")) {
1472                 uint32_t pid;
1473                 char *p;
1474                 User *user;
1475                 bool b;
1476
1477                 if (!dbus_message_get_args(
1478                                     message,
1479                                     &error,
1480                                     DBUS_TYPE_UINT32, &pid,
1481                                     DBUS_TYPE_INVALID))
1482                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1483
1484                 r = manager_get_user_by_pid(m, pid, &user);
1485                 if (r <= 0)
1486                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
1487
1488                 reply = dbus_message_new_method_return(message);
1489                 if (!reply)
1490                         goto oom;
1491
1492                 p = user_bus_path(user);
1493                 if (!p)
1494                         goto oom;
1495
1496                 b = dbus_message_append_args(
1497                                 reply,
1498                                 DBUS_TYPE_OBJECT_PATH, &p,
1499                                 DBUS_TYPE_INVALID);
1500                 free(p);
1501
1502                 if (!b)
1503                         goto oom;
1504
1505         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
1506                 const char *name;
1507                 char *p;
1508                 Seat *seat;
1509                 bool b;
1510
1511                 if (!dbus_message_get_args(
1512                                     message,
1513                                     &error,
1514                                     DBUS_TYPE_STRING, &name,
1515                                     DBUS_TYPE_INVALID))
1516                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1517
1518                 seat = hashmap_get(m->seats, name);
1519                 if (!seat)
1520                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1521
1522                 reply = dbus_message_new_method_return(message);
1523                 if (!reply)
1524                         goto oom;
1525
1526                 p = seat_bus_path(seat);
1527                 if (!p)
1528                         goto oom;
1529
1530                 b = dbus_message_append_args(
1531                                 reply,
1532                                 DBUS_TYPE_OBJECT_PATH, &p,
1533                                 DBUS_TYPE_INVALID);
1534                 free(p);
1535
1536                 if (!b)
1537                         goto oom;
1538
1539         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
1540                 char *p;
1541                 Session *session;
1542                 Iterator i;
1543                 DBusMessageIter iter, sub;
1544                 const char *empty = "";
1545
1546                 reply = dbus_message_new_method_return(message);
1547                 if (!reply)
1548                         goto oom;
1549
1550                 dbus_message_iter_init_append(reply, &iter);
1551
1552                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
1553                         goto oom;
1554
1555                 HASHMAP_FOREACH(session, m->sessions, i) {
1556                         DBusMessageIter sub2;
1557                         uint32_t uid;
1558
1559                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1560                                 goto oom;
1561
1562                         uid = session->user->uid;
1563
1564                         p = session_bus_path(session);
1565                         if (!p)
1566                                 goto oom;
1567
1568                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
1569                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1570                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
1571                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
1572                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1573                                 free(p);
1574                                 goto oom;
1575                         }
1576
1577                         free(p);
1578
1579                         if (!dbus_message_iter_close_container(&sub, &sub2))
1580                                 goto oom;
1581                 }
1582
1583                 if (!dbus_message_iter_close_container(&iter, &sub))
1584                         goto oom;
1585
1586         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
1587                 User *user;
1588                 Iterator i;
1589                 DBusMessageIter iter, sub;
1590
1591                 reply = dbus_message_new_method_return(message);
1592                 if (!reply)
1593                         goto oom;
1594
1595                 dbus_message_iter_init_append(reply, &iter);
1596
1597                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
1598                         goto oom;
1599
1600                 HASHMAP_FOREACH(user, m->users, i) {
1601                         _cleanup_free_ char *p = NULL;
1602                         DBusMessageIter sub2;
1603                         uint32_t uid;
1604
1605                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1606                                 goto oom;
1607
1608                         uid = user->uid;
1609
1610                         p = user_bus_path(user);
1611                         if (!p)
1612                                 goto oom;
1613
1614                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1615                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
1616                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1617                                 free(p);
1618                                 goto oom;
1619                         }
1620
1621                         if (!dbus_message_iter_close_container(&sub, &sub2))
1622                                 goto oom;
1623                 }
1624
1625                 if (!dbus_message_iter_close_container(&iter, &sub))
1626                         goto oom;
1627
1628         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
1629                 Seat *seat;
1630                 Iterator i;
1631                 DBusMessageIter iter, sub;
1632
1633                 reply = dbus_message_new_method_return(message);
1634                 if (!reply)
1635                         goto oom;
1636
1637                 dbus_message_iter_init_append(reply, &iter);
1638
1639                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
1640                         goto oom;
1641
1642                 HASHMAP_FOREACH(seat, m->seats, i) {
1643                         _cleanup_free_ char *p = NULL;
1644                         DBusMessageIter sub2;
1645
1646                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1647                                 goto oom;
1648
1649                         p = seat_bus_path(seat);
1650                         if (!p)
1651                                 goto oom;
1652
1653                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
1654                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1655                                 free(p);
1656                                 goto oom;
1657                         }
1658
1659                         if (!dbus_message_iter_close_container(&sub, &sub2))
1660                                 goto oom;
1661                 }
1662
1663                 if (!dbus_message_iter_close_container(&iter, &sub))
1664                         goto oom;
1665
1666         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListInhibitors")) {
1667                 Inhibitor *inhibitor;
1668                 Iterator i;
1669                 DBusMessageIter iter, sub;
1670
1671                 reply = dbus_message_new_method_return(message);
1672                 if (!reply)
1673                         goto oom;
1674
1675                 dbus_message_iter_init_append(reply, &iter);
1676
1677                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssuu)", &sub))
1678                         goto oom;
1679
1680                 HASHMAP_FOREACH(inhibitor, m->inhibitors, i) {
1681                         DBusMessageIter sub2;
1682                         dbus_uint32_t uid, pid;
1683                         const char *what, *who, *why, *mode;
1684
1685                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1686                                 goto oom;
1687
1688                         what = strempty(inhibit_what_to_string(inhibitor->what));
1689                         who = strempty(inhibitor->who);
1690                         why = strempty(inhibitor->why);
1691                         mode = strempty(inhibit_mode_to_string(inhibitor->mode));
1692                         uid = (dbus_uint32_t) inhibitor->uid;
1693                         pid = (dbus_uint32_t) inhibitor->pid;
1694
1695                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &what) ||
1696                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &who) ||
1697                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &why) ||
1698                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &mode) ||
1699                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1700                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid))
1701                                 goto oom;
1702
1703                         if (!dbus_message_iter_close_container(&sub, &sub2))
1704                                 goto oom;
1705                 }
1706
1707                 if (!dbus_message_iter_close_container(&iter, &sub))
1708                         goto oom;
1709
1710
1711         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Inhibit")) {
1712
1713                 r = bus_manager_inhibit(m, connection, message, &error, &reply);
1714
1715                 if (r < 0)
1716                         return bus_send_error_reply(connection, message, &error, r);
1717
1718
1719         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
1720
1721                 r = bus_manager_create_session(m, message);
1722
1723                 /* Don't delay the work on OOM here, since it might be
1724                  * triggered by a low RLIMIT_NOFILE here (since we
1725                  * send a dupped fd to the client), and we'd rather
1726                  * see this fail quickly then be retried later */
1727
1728                 if (r < 0)
1729                         return bus_send_error_reply(connection, message, NULL, r);
1730
1731         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ReleaseSession")) {
1732                 const char *name;
1733                 Session *session;
1734
1735                 if (!dbus_message_get_args(
1736                                     message,
1737                                     &error,
1738                                     DBUS_TYPE_STRING, &name,
1739                                     DBUS_TYPE_INVALID))
1740                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1741
1742                 session = hashmap_get(m->sessions, name);
1743                 if (!session)
1744                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1745
1746                 /* We use the FIFO to detect stray sessions where the
1747                 process invoking PAM dies abnormally. We need to make
1748                 sure that that process is not killed if at the clean
1749                 end of the session it closes the FIFO. Hence, with
1750                 this call explicitly turn off the FIFO logic, so that
1751                 the PAM code can finish clean up on its own */
1752                 session_remove_fifo(session);
1753
1754                 reply = dbus_message_new_method_return(message);
1755                 if (!reply)
1756                         goto oom;
1757
1758         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
1759                 const char *name;
1760                 Session *session;
1761
1762                 if (!dbus_message_get_args(
1763                                     message,
1764                                     &error,
1765                                     DBUS_TYPE_STRING, &name,
1766                                     DBUS_TYPE_INVALID))
1767                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1768
1769                 session = hashmap_get(m->sessions, name);
1770                 if (!session)
1771                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1772
1773                 r = session_activate(session);
1774                 if (r < 0)
1775                         return bus_send_error_reply(connection, message, NULL, r);
1776
1777                 reply = dbus_message_new_method_return(message);
1778                 if (!reply)
1779                         goto oom;
1780
1781         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) {
1782                 const char *session_name, *seat_name;
1783                 Session *session;
1784                 Seat *seat;
1785
1786                 /* Same as ActivateSession() but refuses to work if
1787                  * the seat doesn't match */
1788
1789                 if (!dbus_message_get_args(
1790                                     message,
1791                                     &error,
1792                                     DBUS_TYPE_STRING, &session_name,
1793                                     DBUS_TYPE_STRING, &seat_name,
1794                                     DBUS_TYPE_INVALID))
1795                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1796
1797                 session = hashmap_get(m->sessions, session_name);
1798                 if (!session)
1799                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1800
1801                 seat = hashmap_get(m->seats, seat_name);
1802                 if (!seat)
1803                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1804
1805                 if (session->seat != seat)
1806                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1807
1808                 r = session_activate(session);
1809                 if (r < 0)
1810                         return bus_send_error_reply(connection, message, NULL, r);
1811
1812                 reply = dbus_message_new_method_return(message);
1813                 if (!reply)
1814                         goto oom;
1815
1816         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") ||
1817                    dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) {
1818                 const char *name;
1819                 Session *session;
1820
1821                 if (!dbus_message_get_args(
1822                                     message,
1823                                     &error,
1824                                     DBUS_TYPE_STRING, &name,
1825                                     DBUS_TYPE_INVALID))
1826                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1827
1828                 session = hashmap_get(m->sessions, name);
1829                 if (!session)
1830                         return bus_send_error_reply(connection, message, NULL, -ENOENT);
1831
1832                 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
1833                         goto oom;
1834
1835                 reply = dbus_message_new_method_return(message);
1836                 if (!reply)
1837                         goto oom;
1838
1839         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSessions") ||
1840                    dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSessions")) {
1841
1842                 r = session_send_lock_all(m, streq(dbus_message_get_member(message), "LockSessions"));
1843                 if (r < 0)
1844                         bus_send_error_reply(connection, message, NULL, r);
1845
1846                 reply = dbus_message_new_method_return(message);
1847                 if (!reply)
1848                         goto oom;
1849
1850         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) {
1851                 const char *swho;
1852                 int32_t signo;
1853                 KillWho who;
1854                 const char *name;
1855                 Session *session;
1856
1857                 if (!dbus_message_get_args(
1858                                     message,
1859                                     &error,
1860                                     DBUS_TYPE_STRING, &name,
1861                                     DBUS_TYPE_STRING, &swho,
1862                                     DBUS_TYPE_INT32, &signo,
1863                                     DBUS_TYPE_INVALID))
1864                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1865
1866                 if (isempty(swho))
1867                         who = KILL_ALL;
1868                 else {
1869                         who = kill_who_from_string(swho);
1870                         if (who < 0)
1871                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
1872                 }
1873
1874                 if (signo <= 0 || signo >= _NSIG)
1875                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1876
1877                 session = hashmap_get(m->sessions, name);
1878                 if (!session)
1879                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1880
1881                 r = session_kill(session, who, signo);
1882                 if (r < 0)
1883                         return bus_send_error_reply(connection, message, NULL, r);
1884
1885                 reply = dbus_message_new_method_return(message);
1886                 if (!reply)
1887                         goto oom;
1888
1889         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) {
1890                 uint32_t uid;
1891                 User *user;
1892                 int32_t signo;
1893
1894                 if (!dbus_message_get_args(
1895                                     message,
1896                                     &error,
1897                                     DBUS_TYPE_UINT32, &uid,
1898                                     DBUS_TYPE_INT32, &signo,
1899                                     DBUS_TYPE_INVALID))
1900                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1901
1902                 if (signo <= 0 || signo >= _NSIG)
1903                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1904
1905                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1906                 if (!user)
1907                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1908
1909                 r = user_kill(user, signo);
1910                 if (r < 0)
1911                         return bus_send_error_reply(connection, message, NULL, r);
1912
1913                 reply = dbus_message_new_method_return(message);
1914                 if (!reply)
1915                         goto oom;
1916
1917         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
1918                 const char *name;
1919                 Session *session;
1920
1921                 if (!dbus_message_get_args(
1922                                     message,
1923                                     &error,
1924                                     DBUS_TYPE_STRING, &name,
1925                                     DBUS_TYPE_INVALID))
1926                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1927
1928                 session = hashmap_get(m->sessions, name);
1929                 if (!session)
1930                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1931
1932                 r = session_stop(session);
1933                 if (r < 0)
1934                         return bus_send_error_reply(connection, message, NULL, r);
1935
1936                 reply = dbus_message_new_method_return(message);
1937                 if (!reply)
1938                         goto oom;
1939
1940         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1941                 uint32_t uid;
1942                 User *user;
1943
1944                 if (!dbus_message_get_args(
1945                                     message,
1946                                     &error,
1947                                     DBUS_TYPE_UINT32, &uid,
1948                                     DBUS_TYPE_INVALID))
1949                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1950
1951                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1952                 if (!user)
1953                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1954
1955                 r = user_stop(user);
1956                 if (r < 0)
1957                         return bus_send_error_reply(connection, message, NULL, r);
1958
1959                 reply = dbus_message_new_method_return(message);
1960                 if (!reply)
1961                         goto oom;
1962
1963         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1964                 const char *name;
1965                 Seat *seat;
1966
1967                 if (!dbus_message_get_args(
1968                                     message,
1969                                     &error,
1970                                     DBUS_TYPE_STRING, &name,
1971                                     DBUS_TYPE_INVALID))
1972                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1973
1974                 seat = hashmap_get(m->seats, name);
1975                 if (!seat)
1976                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1977
1978                 r = seat_stop_sessions(seat);
1979                 if (r < 0)
1980                         return bus_send_error_reply(connection, message, NULL, r);
1981
1982                 reply = dbus_message_new_method_return(message);
1983                 if (!reply)
1984                         goto oom;
1985
1986         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1987                 uint32_t uid;
1988                 struct passwd *pw;
1989                 dbus_bool_t b, interactive;
1990                 char *path;
1991
1992                 if (!dbus_message_get_args(
1993                                     message,
1994                                     &error,
1995                                     DBUS_TYPE_UINT32, &uid,
1996                                     DBUS_TYPE_BOOLEAN, &b,
1997                                     DBUS_TYPE_BOOLEAN, &interactive,
1998                                     DBUS_TYPE_INVALID))
1999                         return bus_send_error_reply(connection, message, &error, -EINVAL);
2000
2001                 errno = 0;
2002                 pw = getpwuid(uid);
2003                 if (!pw)
2004                         return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
2005
2006                 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, NULL, &error);
2007                 if (r < 0)
2008                         return bus_send_error_reply(connection, message, &error, r);
2009
2010                 mkdir_p_label("/var/lib/systemd", 0755);
2011
2012                 r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0);
2013                 if (r < 0)
2014                         return bus_send_error_reply(connection, message, &error, r);
2015
2016                 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
2017                 if (!path)
2018                         goto oom;
2019
2020                 if (b) {
2021                         User *u;
2022
2023                         r = touch(path);
2024                         free(path);
2025
2026                         if (r < 0)
2027                                 return bus_send_error_reply(connection, message, &error, r);
2028
2029                         if (manager_add_user_by_uid(m, uid, &u) >= 0)
2030                                 user_start(u);
2031
2032                 } else {
2033                         User *u;
2034
2035                         r = unlink(path);
2036                         free(path);
2037
2038                         if (r < 0 && errno != ENOENT)
2039                                 return bus_send_error_reply(connection, message, &error, -errno);
2040
2041                         u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
2042                         if (u)
2043                                 user_add_to_gc_queue(u);
2044                 }
2045
2046                 reply = dbus_message_new_method_return(message);
2047                 if (!reply)
2048                         goto oom;
2049
2050         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
2051                 const char *sysfs, *seat;
2052                 dbus_bool_t interactive;
2053
2054                 if (!dbus_message_get_args(
2055                                     message,
2056                                     &error,
2057                                     DBUS_TYPE_STRING, &seat,
2058                                     DBUS_TYPE_STRING, &sysfs,
2059                                     DBUS_TYPE_BOOLEAN, &interactive,
2060                                     DBUS_TYPE_INVALID))
2061                         return bus_send_error_reply(connection, message, &error, -EINVAL);
2062
2063                 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
2064                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
2065
2066                 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, NULL, &error);
2067                 if (r < 0)
2068                         return bus_send_error_reply(connection, message, &error, r);
2069
2070                 r = attach_device(m, seat, sysfs);
2071                 if (r < 0)
2072                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
2073
2074                 reply = dbus_message_new_method_return(message);
2075                 if (!reply)
2076                         goto oom;
2077
2078
2079         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
2080                 dbus_bool_t interactive;
2081
2082                 if (!dbus_message_get_args(
2083                                     message,
2084                                     &error,
2085                                     DBUS_TYPE_BOOLEAN, &interactive,
2086                                     DBUS_TYPE_INVALID))
2087                         return bus_send_error_reply(connection, message, &error, -EINVAL);
2088
2089                 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, NULL, &error);
2090                 if (r < 0)
2091                         return bus_send_error_reply(connection, message, &error, r);
2092
2093                 r = flush_devices(m);
2094                 if (r < 0)
2095                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
2096
2097                 reply = dbus_message_new_method_return(message);
2098                 if (!reply)
2099                         goto oom;
2100
2101         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff")) {
2102
2103                 r = bus_manager_do_shutdown_or_sleep(
2104                                 m, connection, message,
2105                                 SPECIAL_POWEROFF_TARGET,
2106                                 INHIBIT_SHUTDOWN,
2107                                 "org.freedesktop.login1.power-off",
2108                                 "org.freedesktop.login1.power-off-multiple-sessions",
2109                                 "org.freedesktop.login1.power-off-ignore-inhibit",
2110                                 NULL,
2111                                 &error, &reply);
2112                 if (r < 0)
2113                         return bus_send_error_reply(connection, message, &error, r);
2114         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
2115                 r = bus_manager_do_shutdown_or_sleep(
2116                                 m, connection, message,
2117                                 SPECIAL_REBOOT_TARGET,
2118                                 INHIBIT_SHUTDOWN,
2119                                 "org.freedesktop.login1.reboot",
2120                                 "org.freedesktop.login1.reboot-multiple-sessions",
2121                                 "org.freedesktop.login1.reboot-ignore-inhibit",
2122                                 NULL,
2123                                 &error, &reply);
2124                 if (r < 0)
2125                         return bus_send_error_reply(connection, message, &error, r);
2126
2127         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Suspend")) {
2128                 r = bus_manager_do_shutdown_or_sleep(
2129                                 m, connection, message,
2130                                 SPECIAL_SUSPEND_TARGET,
2131                                 INHIBIT_SLEEP,
2132                                 "org.freedesktop.login1.suspend",
2133                                 "org.freedesktop.login1.suspend-multiple-sessions",
2134                                 "org.freedesktop.login1.suspend-ignore-inhibit",
2135                                 "suspend",
2136                                 &error, &reply);
2137                 if (r < 0)
2138                         return bus_send_error_reply(connection, message, &error, r);
2139         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Hibernate")) {
2140                 r = bus_manager_do_shutdown_or_sleep(
2141                                 m, connection, message,
2142                                 SPECIAL_HIBERNATE_TARGET,
2143                                 INHIBIT_SLEEP,
2144                                 "org.freedesktop.login1.hibernate",
2145                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2146                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2147                                 "hibernate",
2148                                 &error, &reply);
2149                 if (r < 0)
2150                         return bus_send_error_reply(connection, message, &error, r);
2151
2152         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "HybridSleep")) {
2153                 r = bus_manager_do_shutdown_or_sleep(
2154                                 m, connection, message,
2155                                 SPECIAL_HYBRID_SLEEP_TARGET,
2156                                 INHIBIT_SLEEP,
2157                                 "org.freedesktop.login1.hibernate",
2158                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2159                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2160                                 "hybrid-sleep",
2161                                 &error, &reply);
2162                 if (r < 0)
2163                         return bus_send_error_reply(connection, message, &error, r);
2164
2165         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff")) {
2166
2167                 r = bus_manager_can_shutdown_or_sleep(
2168                                 m, connection, message,
2169                                 INHIBIT_SHUTDOWN,
2170                                 "org.freedesktop.login1.power-off",
2171                                 "org.freedesktop.login1.power-off-multiple-sessions",
2172                                 "org.freedesktop.login1.power-off-ignore-inhibit",
2173                                 NULL,
2174                                 &error, &reply);
2175                 if (r < 0)
2176                         return bus_send_error_reply(connection, message, &error, r);
2177         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
2178                 r = bus_manager_can_shutdown_or_sleep(
2179                                 m, connection, message,
2180                                 INHIBIT_SHUTDOWN,
2181                                 "org.freedesktop.login1.reboot",
2182                                 "org.freedesktop.login1.reboot-multiple-sessions",
2183                                 "org.freedesktop.login1.reboot-ignore-inhibit",
2184                                 NULL,
2185                                 &error, &reply);
2186                 if (r < 0)
2187                         return bus_send_error_reply(connection, message, &error, r);
2188
2189         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanSuspend")) {
2190                 r = bus_manager_can_shutdown_or_sleep(
2191                                 m, connection, message,
2192                                 INHIBIT_SLEEP,
2193                                 "org.freedesktop.login1.suspend",
2194                                 "org.freedesktop.login1.suspend-multiple-sessions",
2195                                 "org.freedesktop.login1.suspend-ignore-inhibit",
2196                                 "suspend",
2197                                 &error, &reply);
2198                 if (r < 0)
2199                         return bus_send_error_reply(connection, message, &error, r);
2200
2201         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHibernate")) {
2202                 r = bus_manager_can_shutdown_or_sleep(
2203                                 m, connection, message,
2204                                 INHIBIT_SLEEP,
2205                                 "org.freedesktop.login1.hibernate",
2206                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2207                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2208                                 "hibernate",
2209                                 &error, &reply);
2210                 if (r < 0)
2211                         return bus_send_error_reply(connection, message, &error, r);
2212
2213         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHybridSleep")) {
2214                 r = bus_manager_can_shutdown_or_sleep(
2215                                 m, connection, message,
2216                                 INHIBIT_SLEEP,
2217                                 "org.freedesktop.login1.hibernate",
2218                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2219                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2220                                 "hybrid-sleep",
2221                                 &error, &reply);
2222                 if (r < 0)
2223                         return bus_send_error_reply(connection, message, &error, r);
2224
2225         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
2226                 char *introspection = NULL;
2227                 FILE *f;
2228                 Iterator i;
2229                 Session *session;
2230                 Seat *seat;
2231                 User *user;
2232                 size_t size;
2233                 char *p;
2234
2235                 if (!(reply = dbus_message_new_method_return(message)))
2236                         goto oom;
2237
2238                 /* We roll our own introspection code here, instead of
2239                  * relying on bus_default_message_handler() because we
2240                  * need to generate our introspection string
2241                  * dynamically. */
2242
2243                 if (!(f = open_memstream(&introspection, &size)))
2244                         goto oom;
2245
2246                 fputs(INTROSPECTION_BEGIN, f);
2247
2248                 HASHMAP_FOREACH(seat, m->seats, i) {
2249                         p = bus_path_escape(seat->id);
2250
2251                         if (p) {
2252                                 fprintf(f, "<node name=\"seat/%s\"/>", p);
2253                                 free(p);
2254                         }
2255                 }
2256
2257                 HASHMAP_FOREACH(user, m->users, i)
2258                         fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
2259
2260                 HASHMAP_FOREACH(session, m->sessions, i) {
2261                         p = bus_path_escape(session->id);
2262
2263                         if (p) {
2264                                 fprintf(f, "<node name=\"session/%s\"/>", p);
2265                                 free(p);
2266                         }
2267                 }
2268
2269                 fputs(INTROSPECTION_END, f);
2270
2271                 if (ferror(f)) {
2272                         fclose(f);
2273                         free(introspection);
2274                         goto oom;
2275                 }
2276
2277                 fclose(f);
2278
2279                 if (!introspection)
2280                         goto oom;
2281
2282                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
2283                         free(introspection);
2284                         goto oom;
2285                 }
2286
2287                 free(introspection);
2288         } else {
2289                 const BusBoundProperties bps[] = {
2290                         { "org.freedesktop.login1.Manager", bus_login_manager_properties, m },
2291                         { NULL, }
2292                 };
2293                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
2294         }
2295
2296         if (reply) {
2297                 if (!bus_maybe_send_reply(connection, message, reply))
2298                         goto oom;
2299         }
2300
2301         return DBUS_HANDLER_RESULT_HANDLED;
2302
2303 oom:
2304         dbus_error_free(&error);
2305
2306         return DBUS_HANDLER_RESULT_NEED_MEMORY;
2307 }
2308
2309 const DBusObjectPathVTable bus_manager_vtable = {
2310         .message_function = manager_message_handler
2311 };
2312
2313 DBusHandlerResult bus_message_filter(
2314                 DBusConnection *connection,
2315                 DBusMessage *message,
2316                 void *userdata) {
2317
2318         Manager *m = userdata;
2319         DBusError error;
2320
2321         assert(m);
2322         assert(connection);
2323         assert(message);
2324
2325         dbus_error_init(&error);
2326
2327         log_debug("Got message: %s %s %s", strna(dbus_message_get_sender(message)), strna(dbus_message_get_interface(message)), strna(dbus_message_get_member(message)));
2328
2329         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
2330                 const char *path, *result, *unit;
2331                 uint32_t id;
2332
2333                 if (!dbus_message_get_args(message, &error,
2334                                            DBUS_TYPE_UINT32, &id,
2335                                            DBUS_TYPE_OBJECT_PATH, &path,
2336                                            DBUS_TYPE_STRING, &unit,
2337                                            DBUS_TYPE_STRING, &result,
2338                                            DBUS_TYPE_INVALID)) {
2339                         log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
2340                         goto finish;
2341                 }
2342
2343                 if (m->action_job && streq(m->action_job, path)) {
2344                         log_info("Operation finished.");
2345
2346                         /* Tell people that they now may take a lock again */
2347                         send_prepare_for(m, m->action_what, false);
2348
2349                         free(m->action_job);
2350                         m->action_job = NULL;
2351                         m->action_unit = NULL;
2352                         m->action_what = 0;
2353
2354                 } else {
2355                         Session *s;
2356                         User *u;
2357
2358                         s = hashmap_get(m->session_units, unit);
2359                         if (s) {
2360                                 if (streq_ptr(path, s->scope_job)) {
2361                                         free(s->scope_job);
2362                                         s->scope_job = NULL;
2363
2364                                         if (s->started) {
2365                                                 if (streq(result, "done"))
2366                                                         session_send_create_reply(s, NULL);
2367                                                 else {
2368                                                         dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
2369                                                         session_send_create_reply(s, &error);
2370                                                 }
2371                                         } else
2372                                                 session_save(s);
2373                                 }
2374
2375                                 session_add_to_gc_queue(s);
2376                         }
2377
2378                         u = hashmap_get(m->user_units, unit);
2379                         if (u) {
2380                                 if (streq_ptr(path, u->service_job)) {
2381                                         free(u->service_job);
2382                                         u->service_job = NULL;
2383                                 }
2384
2385                                 if (streq_ptr(path, u->slice_job)) {
2386                                         free(u->slice_job);
2387                                         u->slice_job = NULL;
2388                                 }
2389
2390                                 user_save(u);
2391                                 user_add_to_gc_queue(u);
2392                         }
2393                 }
2394
2395         } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
2396
2397                 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2398                 _cleanup_free_ char *unit = NULL;
2399                 const char *path;
2400
2401                 path = dbus_message_get_path(message);
2402                 if (!path)
2403                         goto finish;
2404
2405                 unit_name_from_dbus_path(path, &unit);
2406                 if (unit) {
2407                         Session *s;
2408                         User *u;
2409
2410                         s = hashmap_get(m->session_units, unit);
2411                         if (s)
2412                                 session_add_to_gc_queue(s);
2413
2414                         u = hashmap_get(m->user_units, unit);
2415                         if (u)
2416                                 user_add_to_gc_queue(u);
2417                 }
2418
2419         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
2420
2421                 const char *path, *unit;
2422                 Session *session;
2423                 User *user;
2424
2425                 if (!dbus_message_get_args(message, &error,
2426                                            DBUS_TYPE_STRING, &unit,
2427                                            DBUS_TYPE_OBJECT_PATH, &path,
2428                                            DBUS_TYPE_INVALID)) {
2429                         log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
2430                         goto finish;
2431                 }
2432
2433                 session = hashmap_get(m->session_units, unit);
2434                 if (session)
2435                          session_add_to_gc_queue(session);
2436
2437                 user = hashmap_get(m->user_units, unit);
2438                 if (user)
2439                         user_add_to_gc_queue(user);
2440
2441         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
2442                 dbus_bool_t b;
2443
2444                 if (!dbus_message_get_args(message, &error,
2445                                            DBUS_TYPE_BOOLEAN, &b,
2446                                            DBUS_TYPE_INVALID)) {
2447                         log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
2448                         goto finish;
2449                 }
2450
2451                 /* systemd finished reloading, let's recheck all our sessions */
2452                 if (!b) {
2453                         Session *session;
2454                         Iterator i;
2455
2456                         log_debug("System manager has been reloaded, rechecking sessions...");
2457
2458                         HASHMAP_FOREACH(session, m->sessions, i)
2459                                 session_add_to_gc_queue(session);
2460                 }
2461         }
2462
2463 finish:
2464         dbus_error_free(&error);
2465
2466         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2467 }
2468
2469 int manager_send_changed(Manager *manager, const char *properties) {
2470         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
2471
2472         assert(manager);
2473
2474         m = bus_properties_changed_new("/org/freedesktop/login1",
2475                                        "org.freedesktop.login1.Manager",
2476                                        properties);
2477         if (!m)
2478                 return -ENOMEM;
2479
2480         if (!dbus_connection_send(manager->bus, m, NULL))
2481                 return -ENOMEM;
2482
2483         return 0;
2484 }
2485
2486 int manager_dispatch_delayed(Manager *manager) {
2487         DBusError error;
2488         int r;
2489
2490         assert(manager);
2491
2492         if (manager->action_what == 0 || manager->action_job)
2493                 return 0;
2494
2495         /* Continue delay? */
2496         if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0)) {
2497
2498                 if (manager->action_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC))
2499                         return 0;
2500
2501                 log_info("Delay lock is active but inhibitor timeout is reached.");
2502         }
2503
2504         /* Actually do the operation */
2505         dbus_error_init(&error);
2506         r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
2507         if (r < 0) {
2508                 log_warning("Failed to send delayed message: %s", bus_error(&error, r));
2509                 dbus_error_free(&error);
2510
2511                 manager->action_unit = NULL;
2512                 manager->action_what = 0;
2513                 return r;
2514         }
2515
2516         return 1;
2517 }
2518
2519 int manager_start_scope(
2520                 Manager *manager,
2521                 const char *scope,
2522                 pid_t pid,
2523                 const char *slice,
2524                 const char *description,
2525                 const char *after,
2526                 const char *kill_mode,
2527                 DBusError *error,
2528                 char **job) {
2529
2530         const char *timeout_stop_property = "TimeoutStopUSec", *send_sighup_property = "SendSIGHUP", *pids_property = "PIDs";
2531         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
2532         DBusMessageIter iter, sub, sub2, sub3, sub4;
2533         uint64_t timeout = 500 * USEC_PER_MSEC;
2534         dbus_bool_t send_sighup = true;
2535         const char *fail = "fail";
2536         uint32_t u;
2537
2538         assert(manager);
2539         assert(scope);
2540         assert(pid > 1);
2541
2542         if (!slice)
2543                 slice = "";
2544
2545         m = dbus_message_new_method_call(
2546                         "org.freedesktop.systemd1",
2547                         "/org/freedesktop/systemd1",
2548                         "org.freedesktop.systemd1.Manager",
2549                         "StartTransientUnit");
2550         if (!m)
2551                 return log_oom();
2552
2553         dbus_message_iter_init_append(m, &iter);
2554
2555         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
2556             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
2557             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
2558                 return log_oom();
2559
2560         if (!isempty(slice)) {
2561                 const char *slice_property = "Slice";
2562
2563                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2564                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
2565                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
2566                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
2567                     !dbus_message_iter_close_container(&sub2, &sub3) ||
2568                     !dbus_message_iter_close_container(&sub, &sub2))
2569                         return log_oom();
2570         }
2571
2572         if (!isempty(description)) {
2573                 const char *description_property = "Description";
2574
2575                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2576                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
2577                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
2578                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
2579                     !dbus_message_iter_close_container(&sub2, &sub3) ||
2580                     !dbus_message_iter_close_container(&sub, &sub2))
2581                         return log_oom();
2582         }
2583
2584         if (!isempty(after)) {
2585                 const char *after_property = "After";
2586
2587                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2588                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &after_property) ||
2589                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "as", &sub3) ||
2590                     !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "s", &sub4) ||
2591                     !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_STRING, &after) ||
2592                     !dbus_message_iter_close_container(&sub3, &sub4) ||
2593                     !dbus_message_iter_close_container(&sub2, &sub3) ||
2594                     !dbus_message_iter_close_container(&sub, &sub2))
2595                         return log_oom();
2596         }
2597
2598         if (!isempty(kill_mode)) {
2599                 const char *kill_mode_property = "KillMode";
2600
2601                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2602                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &kill_mode_property) ||
2603                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
2604                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &kill_mode) ||
2605                     !dbus_message_iter_close_container(&sub2, &sub3) ||
2606                     !dbus_message_iter_close_container(&sub, &sub2))
2607                         return log_oom();
2608         }
2609
2610         /* cgroup empty notification is not available in containers
2611          * currently. To make this less problematic, let's shorten the
2612          * stop timeout for sessions, so that we don't wait
2613          * forever. */
2614
2615         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2616             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
2617             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
2618             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
2619             !dbus_message_iter_close_container(&sub2, &sub3) ||
2620             !dbus_message_iter_close_container(&sub, &sub2))
2621                 return log_oom();
2622
2623         /* Make sure that the session shells are terminated with
2624          * SIGHUP since bash and friends tend to ignore SIGTERM */
2625         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2626             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &send_sighup_property) ||
2627             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "b", &sub3) ||
2628             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_BOOLEAN, &send_sighup) ||
2629             !dbus_message_iter_close_container(&sub2, &sub3) ||
2630             !dbus_message_iter_close_container(&sub, &sub2))
2631                 return log_oom();
2632
2633         u = pid;
2634         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2635             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
2636             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
2637             !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
2638             !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
2639             !dbus_message_iter_close_container(&sub3, &sub4) ||
2640             !dbus_message_iter_close_container(&sub2, &sub3) ||
2641             !dbus_message_iter_close_container(&sub, &sub2))
2642                 return log_oom();
2643
2644         if (!dbus_message_iter_close_container(&iter, &sub))
2645                 return log_oom();
2646
2647         reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
2648         if (!reply)
2649                 return -EIO;
2650
2651         if (job) {
2652                 const char *j;
2653                 char *copy;
2654
2655                 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
2656                         return -EIO;
2657
2658                 copy = strdup(j);
2659                 if (!copy)
2660                         return -ENOMEM;
2661
2662                 *job = copy;
2663         }
2664
2665         return 0;
2666 }
2667
2668 int manager_start_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
2669         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2670         const char *fail = "fail";
2671         int r;
2672
2673         assert(manager);
2674         assert(unit);
2675
2676         r = bus_method_call_with_reply(
2677                         manager->bus,
2678                         "org.freedesktop.systemd1",
2679                         "/org/freedesktop/systemd1",
2680                         "org.freedesktop.systemd1.Manager",
2681                         "StartUnit",
2682                         &reply,
2683                         error,
2684                         DBUS_TYPE_STRING, &unit,
2685                         DBUS_TYPE_STRING, &fail,
2686                         DBUS_TYPE_INVALID);
2687         if (r < 0) {
2688                 log_error("Failed to start unit %s: %s", unit, bus_error(error, r));
2689                 return r;
2690         }
2691
2692         if (job) {
2693                 const char *j;
2694                 char *copy;
2695
2696                 if (!dbus_message_get_args(reply, error,
2697                                            DBUS_TYPE_OBJECT_PATH, &j,
2698                                            DBUS_TYPE_INVALID)) {
2699                         log_error("Failed to parse reply.");
2700                         return -EIO;
2701                 }
2702
2703                 copy = strdup(j);
2704                 if (!copy)
2705                         return -ENOMEM;
2706
2707                 *job = copy;
2708         }
2709
2710         return 0;
2711 }
2712
2713 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
2714         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2715         const char *fail = "fail";
2716         int r;
2717
2718         assert(manager);
2719         assert(unit);
2720
2721         r = bus_method_call_with_reply(
2722                         manager->bus,
2723                         "org.freedesktop.systemd1",
2724                         "/org/freedesktop/systemd1",
2725                         "org.freedesktop.systemd1.Manager",
2726                         "StopUnit",
2727                         &reply,
2728                         error,
2729                         DBUS_TYPE_STRING, &unit,
2730                         DBUS_TYPE_STRING, &fail,
2731                         DBUS_TYPE_INVALID);
2732         if (r < 0) {
2733                 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
2734                     dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
2735
2736                         if (job)
2737                                 *job = NULL;
2738
2739                         dbus_error_free(error);
2740                         return 0;
2741                 }
2742
2743                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
2744                 return r;
2745         }
2746
2747         if (job) {
2748                 const char *j;
2749                 char *copy;
2750
2751                 if (!dbus_message_get_args(reply, error,
2752                                            DBUS_TYPE_OBJECT_PATH, &j,
2753                                            DBUS_TYPE_INVALID)) {
2754                         log_error("Failed to parse reply.");
2755                         return -EIO;
2756                 }
2757
2758                 copy = strdup(j);
2759                 if (!copy)
2760                         return -ENOMEM;
2761
2762                 *job = copy;
2763         }
2764
2765         return 1;
2766 }
2767
2768 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
2769         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2770         const char *w;
2771         int r;
2772
2773         assert(manager);
2774         assert(unit);
2775
2776         w = who == KILL_LEADER ? "process" : "cgroup";
2777         assert_cc(sizeof(signo) == sizeof(int32_t));
2778
2779         r = bus_method_call_with_reply(
2780                         manager->bus,
2781                         "org.freedesktop.systemd1",
2782                         "/org/freedesktop/systemd1",
2783                         "org.freedesktop.systemd1.Manager",
2784                         "KillUnit",
2785                         &reply,
2786                         error,
2787                         DBUS_TYPE_STRING, &unit,
2788                         DBUS_TYPE_STRING, &w,
2789                         DBUS_TYPE_INT32, &signo,
2790                         DBUS_TYPE_INVALID);
2791         if (r < 0) {
2792                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
2793                 return r;
2794         }
2795
2796         return 0;
2797 }
2798
2799 int manager_unit_is_active(Manager *manager, const char *unit) {
2800
2801         const char *interface = "org.freedesktop.systemd1.Unit";
2802         const char *property = "ActiveState";
2803         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2804         _cleanup_free_ char *path = NULL;
2805         DBusMessageIter iter, sub;
2806         const char *state;
2807         DBusError error;
2808         int r;
2809
2810         assert(manager);
2811         assert(unit);
2812
2813         dbus_error_init(&error);
2814
2815         path = unit_dbus_path_from_name(unit);
2816         if (!path)
2817                 return -ENOMEM;
2818
2819         r = bus_method_call_with_reply(
2820                         manager->bus,
2821                         "org.freedesktop.systemd1",
2822                         path,
2823                         "org.freedesktop.DBus.Properties",
2824                         "Get",
2825                         &reply,
2826                         &error,
2827                         DBUS_TYPE_STRING, &interface,
2828                         DBUS_TYPE_STRING, &property,
2829                         DBUS_TYPE_INVALID);
2830         if (r < 0) {
2831                 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
2832                     dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
2833                         /* systemd might have droppped off
2834                          * momentarily, let's not make this an
2835                          * error */
2836
2837                         dbus_error_free(&error);
2838                         return true;
2839                 }
2840
2841                 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
2842                     dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
2843                         /* If the unit is already unloaded then it's
2844                          * not active */
2845
2846                         dbus_error_free(&error);
2847                         return false;
2848                 }
2849
2850                 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
2851                 dbus_error_free(&error);
2852                 return r;
2853         }
2854
2855         if (!dbus_message_iter_init(reply, &iter) ||
2856             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
2857                 log_error("Failed to parse reply.");
2858                 return -EINVAL;
2859         }
2860
2861         dbus_message_iter_recurse(&iter, &sub);
2862         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
2863                 log_error("Failed to parse reply.");
2864                 return -EINVAL;
2865         }
2866
2867         dbus_message_iter_get_basic(&sub, &state);
2868
2869         return !streq(state, "inactive") && !streq(state, "failed");
2870 }