chiark / gitweb /
logind: after deserializatio readd systemd units to unit-to-object hashmap correctly
[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         return 0;
647
648 fail:
649         if (session)
650                 session_add_to_gc_queue(session);
651
652         if (user)
653                 user_add_to_gc_queue(user);
654
655         return r;
656 }
657
658 static int bus_manager_inhibit(
659                 Manager *m,
660                 DBusConnection *connection,
661                 DBusMessage *message,
662                 DBusError *error,
663                 DBusMessage **_reply) {
664
665         Inhibitor *i = NULL;
666         char *id = NULL;
667         const char *who, *why, *what, *mode;
668         pid_t pid;
669         InhibitWhat w;
670         InhibitMode mm;
671         unsigned long ul;
672         int r, fifo_fd = -1;
673         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
674
675         assert(m);
676         assert(connection);
677         assert(message);
678         assert(error);
679         assert(_reply);
680
681         if (!dbus_message_get_args(
682                             message,
683                             error,
684                             DBUS_TYPE_STRING, &what,
685                             DBUS_TYPE_STRING, &who,
686                             DBUS_TYPE_STRING, &why,
687                             DBUS_TYPE_STRING, &mode,
688                             DBUS_TYPE_INVALID)) {
689                 r = -EIO;
690                 goto fail;
691         }
692
693         w = inhibit_what_from_string(what);
694         if (w <= 0) {
695                 r = -EINVAL;
696                 goto fail;
697         }
698
699         mm = inhibit_mode_from_string(mode);
700         if (mm < 0) {
701                 r = -EINVAL;
702                 goto fail;
703         }
704
705         /* Delay is only supported for shutdown/sleep */
706         if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP))) {
707                 r = -EINVAL;
708                 goto fail;
709         }
710
711         /* Don't allow taking delay locks while we are already
712          * executing the operation. We shouldn't create the impression
713          * that the lock was successful if the machine is about to go
714          * down/suspend any moment. */
715         if (m->action_what & w) {
716                 r = -EALREADY;
717                 goto fail;
718         }
719
720         r = verify_polkit(connection, message,
721                           w == INHIBIT_SHUTDOWN             ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
722                           w == INHIBIT_SLEEP                ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep"    : "org.freedesktop.login1.inhibit-delay-sleep") :
723                           w == INHIBIT_IDLE                 ? "org.freedesktop.login1.inhibit-block-idle" :
724                           w == INHIBIT_HANDLE_POWER_KEY     ? "org.freedesktop.login1.inhibit-handle-power-key" :
725                           w == INHIBIT_HANDLE_SUSPEND_KEY   ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
726                           w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
727                                                               "org.freedesktop.login1.inhibit-handle-lid-switch",
728                           false, NULL, error);
729         if (r < 0)
730                 goto fail;
731
732         ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
733         if (ul == (unsigned long) -1) {
734                 r = -EIO;
735                 goto fail;
736         }
737
738         pid = bus_get_unix_process_id(connection, dbus_message_get_sender(message), error);
739         if (pid <= 0) {
740                 r = -EIO;
741                 goto fail;
742         }
743
744         do {
745                 free(id);
746                 id = NULL;
747
748                 if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0) {
749                         r = -ENOMEM;
750                         goto fail;
751                 }
752         } while (hashmap_get(m->inhibitors, id));
753
754         r = manager_add_inhibitor(m, id, &i);
755         free(id);
756
757         if (r < 0)
758                 goto fail;
759
760         i->what = w;
761         i->mode = mm;
762         i->pid = pid;
763         i->uid = (uid_t) ul;
764         i->why = strdup(why);
765         i->who = strdup(who);
766
767         if (!i->why || !i->who) {
768                 r = -ENOMEM;
769                 goto fail;
770         }
771
772         fifo_fd = inhibitor_create_fifo(i);
773         if (fifo_fd < 0) {
774                 r = fifo_fd;
775                 goto fail;
776         }
777
778         reply = dbus_message_new_method_return(message);
779         if (!reply) {
780                 r = -ENOMEM;
781                 goto fail;
782         }
783
784         if (!dbus_message_append_args(
785                             reply,
786                             DBUS_TYPE_UNIX_FD, &fifo_fd,
787                             DBUS_TYPE_INVALID)) {
788                 r = -ENOMEM;
789                 goto fail;
790         }
791
792         close_nointr_nofail(fifo_fd);
793         *_reply = reply;
794         reply = NULL;
795
796         inhibitor_start(i);
797
798         return 0;
799
800 fail:
801         if (i)
802                 inhibitor_free(i);
803
804         if (fifo_fd >= 0)
805                 close_nointr_nofail(fifo_fd);
806
807         return r;
808 }
809
810 static int trigger_device(Manager *m, struct udev_device *d) {
811         struct udev_enumerate *e;
812         struct udev_list_entry *first, *item;
813         int r;
814
815         assert(m);
816
817         e = udev_enumerate_new(m->udev);
818         if (!e) {
819                 r = -ENOMEM;
820                 goto finish;
821         }
822
823         if (d) {
824                 if (udev_enumerate_add_match_parent(e, d) < 0) {
825                         r = -EIO;
826                         goto finish;
827                 }
828         }
829
830         if (udev_enumerate_scan_devices(e) < 0) {
831                 r = -EIO;
832                 goto finish;
833         }
834
835         first = udev_enumerate_get_list_entry(e);
836         udev_list_entry_foreach(item, first) {
837                 char *t;
838                 const char *p;
839
840                 p = udev_list_entry_get_name(item);
841
842                 t = strappend(p, "/uevent");
843                 if (!t) {
844                         r = -ENOMEM;
845                         goto finish;
846                 }
847
848                 write_string_file(t, "change");
849                 free(t);
850         }
851
852         r = 0;
853
854 finish:
855         if (e)
856                 udev_enumerate_unref(e);
857
858         return r;
859 }
860
861 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
862         struct udev_device *d;
863         _cleanup_free_ char *rule = NULL, *file = NULL;
864         const char *id_for_seat;
865         int r;
866
867         assert(m);
868         assert(seat);
869         assert(sysfs);
870
871         d = udev_device_new_from_syspath(m->udev, sysfs);
872         if (!d)
873                 return -ENODEV;
874
875         if (!udev_device_has_tag(d, "seat")) {
876                 r = -ENODEV;
877                 goto finish;
878         }
879
880         id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
881         if (!id_for_seat) {
882                 r = -ENODEV;
883                 goto finish;
884         }
885
886         if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
887                 r = -ENOMEM;
888                 goto finish;
889         }
890
891         if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
892                 r = -ENOMEM;
893                 goto finish;
894         }
895
896         mkdir_p_label("/etc/udev/rules.d", 0755);
897         label_init("/etc");
898         r = write_string_file_atomic_label(file, rule);
899         if (r < 0)
900                 goto finish;
901
902         r = trigger_device(m, d);
903
904 finish:
905         if (d)
906                 udev_device_unref(d);
907
908         return r;
909 }
910
911 static int flush_devices(Manager *m) {
912         _cleanup_closedir_ DIR *d;
913
914         assert(m);
915
916         d = opendir("/etc/udev/rules.d");
917         if (!d) {
918                 if (errno != ENOENT)
919                         log_warning("Failed to open /etc/udev/rules.d: %m");
920         } else {
921                 struct dirent *de;
922
923                 while ((de = readdir(d))) {
924
925                         if (!dirent_is_file(de))
926                                 continue;
927
928                         if (!startswith(de->d_name, "72-seat-"))
929                                 continue;
930
931                         if (!endswith(de->d_name, ".rules"))
932                                 continue;
933
934                         if (unlinkat(dirfd(d), de->d_name, 0) < 0)
935                                 log_warning("Failed to unlink %s: %m", de->d_name);
936                 }
937         }
938
939         return trigger_device(m, NULL);
940 }
941
942 static int have_multiple_sessions(
943                 Manager *m,
944                 uid_t uid) {
945
946         Session *session;
947         Iterator i;
948
949         assert(m);
950
951         /* Check for other users' sessions. Greeter sessions do not
952          * count, and non-login sessions do not count either. */
953         HASHMAP_FOREACH(session, m->sessions, i)
954                 if (session->class == SESSION_USER &&
955                     session->user->uid != uid)
956                         return true;
957
958         return false;
959 }
960
961 static int bus_manager_log_shutdown(
962                 Manager *m,
963                 InhibitWhat w,
964                 const char *unit_name) {
965
966         const char *p, *q;
967
968         assert(m);
969         assert(unit_name);
970
971         if (w != INHIBIT_SHUTDOWN)
972                 return 0;
973
974         if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) {
975                 p = "MESSAGE=System is powering down.";
976                 q = "SHUTDOWN=power-off";
977         } else if (streq(unit_name, SPECIAL_HALT_TARGET)) {
978                 p = "MESSAGE=System is halting.";
979                 q = "SHUTDOWN=halt";
980         } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) {
981                 p = "MESSAGE=System is rebooting.";
982                 q = "SHUTDOWN=reboot";
983         } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) {
984                 p = "MESSAGE=System is rebooting with kexec.";
985                 q = "SHUTDOWN=kexec";
986         } else {
987                 p = "MESSAGE=System is shutting down.";
988                 q = NULL;
989         }
990
991         return log_struct(LOG_NOTICE, MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
992                           p,
993                           q, NULL);
994 }
995
996 static int execute_shutdown_or_sleep(
997                 Manager *m,
998                 InhibitWhat w,
999                 const char *unit_name,
1000                 DBusError *error) {
1001
1002         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1003         const char *mode = "replace-irreversibly", *p;
1004         int r;
1005         char *c;
1006
1007         assert(m);
1008         assert(w >= 0);
1009         assert(w < _INHIBIT_WHAT_MAX);
1010         assert(unit_name);
1011
1012         bus_manager_log_shutdown(m, w, unit_name);
1013
1014         r = bus_method_call_with_reply(
1015                         m->bus,
1016                         "org.freedesktop.systemd1",
1017                         "/org/freedesktop/systemd1",
1018                         "org.freedesktop.systemd1.Manager",
1019                         "StartUnit",
1020                         &reply,
1021                         error,
1022                         DBUS_TYPE_STRING, &unit_name,
1023                         DBUS_TYPE_STRING, &mode,
1024                         DBUS_TYPE_INVALID);
1025         if (r < 0)
1026                 return r;
1027
1028         if (!dbus_message_get_args(
1029                             reply,
1030                             error,
1031                             DBUS_TYPE_OBJECT_PATH, &p,
1032                             DBUS_TYPE_INVALID))
1033                 return -EINVAL;
1034
1035         c = strdup(p);
1036         if (!c)
1037                 return -ENOMEM;
1038
1039         m->action_unit = unit_name;
1040         free(m->action_job);
1041         m->action_job = c;
1042         m->action_what = w;
1043
1044         return 0;
1045 }
1046
1047 static int delay_shutdown_or_sleep(
1048                 Manager *m,
1049                 InhibitWhat w,
1050                 const char *unit_name) {
1051
1052         assert(m);
1053         assert(w >= 0);
1054         assert(w < _INHIBIT_WHAT_MAX);
1055         assert(unit_name);
1056
1057         m->action_timestamp = now(CLOCK_MONOTONIC);
1058         m->action_unit = unit_name;
1059         m->action_what = w;
1060
1061         return 0;
1062 }
1063
1064 static int bus_manager_can_shutdown_or_sleep(
1065                 Manager *m,
1066                 DBusConnection *connection,
1067                 DBusMessage *message,
1068                 InhibitWhat w,
1069                 const char *action,
1070                 const char *action_multiple_sessions,
1071                 const char *action_ignore_inhibit,
1072                 const char *sleep_verb,
1073                 DBusError *error,
1074                 DBusMessage **_reply) {
1075
1076         bool multiple_sessions, challenge, blocked, b;
1077         const char *result = NULL;
1078         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1079         int r;
1080         unsigned long ul;
1081
1082         assert(m);
1083         assert(connection);
1084         assert(message);
1085         assert(w >= 0);
1086         assert(w <= _INHIBIT_WHAT_MAX);
1087         assert(action);
1088         assert(action_multiple_sessions);
1089         assert(action_ignore_inhibit);
1090         assert(error);
1091         assert(_reply);
1092
1093         if (sleep_verb) {
1094                 r = can_sleep(sleep_verb);
1095                 if (r < 0)
1096                         return r;
1097                 if (r == 0) {
1098                         result = "na";
1099                         goto finish;
1100                 }
1101         }
1102
1103         ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
1104         if (ul == (unsigned long) -1)
1105                 return -EIO;
1106
1107         r = have_multiple_sessions(m, (uid_t) ul);
1108         if (r < 0)
1109                 return r;
1110
1111         multiple_sessions = r > 0;
1112         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, (uid_t) ul);
1113
1114         if (multiple_sessions) {
1115                 r = verify_polkit(connection, message, action_multiple_sessions, false, &challenge, error);
1116                 if (r < 0)
1117                         return r;
1118
1119                 if (r > 0)
1120                         result = "yes";
1121                 else if (challenge)
1122                         result = "challenge";
1123                 else
1124                         result = "no";
1125         }
1126
1127         if (blocked) {
1128                 r = verify_polkit(connection, message, action_ignore_inhibit, false, &challenge, error);
1129                 if (r < 0)
1130                         return r;
1131
1132                 if (r > 0 && !result)
1133                         result = "yes";
1134                 else if (challenge && (!result || streq(result, "yes")))
1135                         result = "challenge";
1136                 else
1137                         result = "no";
1138         }
1139
1140         if (!multiple_sessions && !blocked) {
1141                 /* If neither inhibit nor multiple sessions
1142                  * apply then just check the normal policy */
1143
1144                 r = verify_polkit(connection, message, action, false, &challenge, error);
1145                 if (r < 0)
1146                         return r;
1147
1148                 if (r > 0)
1149                         result = "yes";
1150                 else if (challenge)
1151                         result = "challenge";
1152                 else
1153                         result = "no";
1154         }
1155
1156 finish:
1157         reply = dbus_message_new_method_return(message);
1158         if (!reply)
1159                 return -ENOMEM;
1160
1161         b = dbus_message_append_args(
1162                         reply,
1163                         DBUS_TYPE_STRING, &result,
1164                         DBUS_TYPE_INVALID);
1165         if (!b)
1166                 return -ENOMEM;
1167
1168         *_reply = reply;
1169         reply = NULL;
1170         return 0;
1171 }
1172
1173 static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
1174         static const char * const signal_name[_INHIBIT_WHAT_MAX] = {
1175                 [INHIBIT_SHUTDOWN] = "PrepareForShutdown",
1176                 [INHIBIT_SLEEP] = "PrepareForSleep"
1177         };
1178
1179         dbus_bool_t active = _active;
1180         _cleanup_dbus_message_unref_ DBusMessage *message = NULL;
1181
1182         assert(m);
1183         assert(w >= 0);
1184         assert(w < _INHIBIT_WHAT_MAX);
1185         assert(signal_name[w]);
1186
1187         message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", signal_name[w]);
1188         if (!message)
1189                 return -ENOMEM;
1190
1191         if (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) ||
1192             !dbus_connection_send(m->bus, message, NULL))
1193                 return -ENOMEM;
1194
1195         return 0;
1196 }
1197
1198 int bus_manager_shutdown_or_sleep_now_or_later(
1199                 Manager *m,
1200                 const char *unit_name,
1201                 InhibitWhat w,
1202                 DBusError *error) {
1203
1204         bool delayed;
1205         int r;
1206
1207         assert(m);
1208         assert(unit_name);
1209         assert(w >= 0);
1210         assert(w <= _INHIBIT_WHAT_MAX);
1211         assert(!m->action_job);
1212
1213         /* Tell everybody to prepare for shutdown/sleep */
1214         send_prepare_for(m, w, true);
1215
1216         delayed =
1217                 m->inhibit_delay_max > 0 &&
1218                 manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0);
1219
1220         if (delayed)
1221                 /* Shutdown is delayed, keep in mind what we
1222                  * want to do, and start a timeout */
1223                 r = delay_shutdown_or_sleep(m, w, unit_name);
1224         else
1225                 /* Shutdown is not delayed, execute it
1226                  * immediately */
1227                 r = execute_shutdown_or_sleep(m, w, unit_name, error);
1228
1229         return r;
1230 }
1231
1232 static int bus_manager_do_shutdown_or_sleep(
1233                 Manager *m,
1234                 DBusConnection *connection,
1235                 DBusMessage *message,
1236                 const char *unit_name,
1237                 InhibitWhat w,
1238                 const char *action,
1239                 const char *action_multiple_sessions,
1240                 const char *action_ignore_inhibit,
1241                 const char *sleep_verb,
1242                 DBusError *error,
1243                 DBusMessage **_reply) {
1244
1245         dbus_bool_t interactive;
1246         bool multiple_sessions, blocked;
1247         DBusMessage *reply = NULL;
1248         int r;
1249         unsigned long ul;
1250
1251         assert(m);
1252         assert(connection);
1253         assert(message);
1254         assert(unit_name);
1255         assert(w >= 0);
1256         assert(w <= _INHIBIT_WHAT_MAX);
1257         assert(action);
1258         assert(action_multiple_sessions);
1259         assert(action_ignore_inhibit);
1260         assert(error);
1261         assert(_reply);
1262
1263         /* Don't allow multiple jobs being executed at the same time */
1264         if (m->action_what)
1265                 return -EALREADY;
1266
1267         if (!dbus_message_get_args(
1268                             message,
1269                             error,
1270                             DBUS_TYPE_BOOLEAN, &interactive,
1271                             DBUS_TYPE_INVALID))
1272                 return -EINVAL;
1273
1274         if (sleep_verb) {
1275                 r = can_sleep(sleep_verb);
1276                 if (r < 0)
1277                         return r;
1278
1279                 if (r == 0)
1280                         return -ENOTSUP;
1281         }
1282
1283         ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
1284         if (ul == (unsigned long) -1)
1285                 return -EIO;
1286
1287         r = have_multiple_sessions(m, (uid_t) ul);
1288         if (r < 0)
1289                 return r;
1290
1291         multiple_sessions = r > 0;
1292         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, (uid_t) ul);
1293
1294         if (multiple_sessions) {
1295                 r = verify_polkit(connection, message, action_multiple_sessions, interactive, NULL, error);
1296                 if (r < 0)
1297                         return r;
1298         }
1299
1300         if (blocked) {
1301                 r = verify_polkit(connection, message, action_ignore_inhibit, interactive, NULL, error);
1302                 if (r < 0)
1303                         return r;
1304         }
1305
1306         if (!multiple_sessions && !blocked) {
1307                 r = verify_polkit(connection, message, action, interactive, NULL, error);
1308                 if (r < 0)
1309                         return r;
1310         }
1311
1312         r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
1313         if (r < 0)
1314                 return r;
1315
1316         reply = dbus_message_new_method_return(message);
1317         if (!reply)
1318                 return -ENOMEM;
1319
1320         *_reply = reply;
1321         return 0;
1322 }
1323
1324 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_action, handle_action, HandleAction);
1325
1326 static const BusProperty bus_login_manager_properties[] = {
1327         { "NAutoVTs",               bus_property_append_unsigned,       "u",  offsetof(Manager, n_autovts)           },
1328         { "KillOnlyUsers",          bus_property_append_strv,           "as", offsetof(Manager, kill_only_users),    true },
1329         { "KillExcludeUsers",       bus_property_append_strv,           "as", offsetof(Manager, kill_exclude_users), true },
1330         { "KillUserProcesses",      bus_property_append_bool,           "b",  offsetof(Manager, kill_user_processes) },
1331         { "IdleHint",               bus_manager_append_idle_hint,       "b",  0 },
1332         { "IdleSinceHint",          bus_manager_append_idle_hint_since, "t",  0 },
1333         { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t",  0 },
1334         { "BlockInhibited",         bus_manager_append_inhibited,       "s",  0 },
1335         { "DelayInhibited",         bus_manager_append_inhibited,       "s",  0 },
1336         { "InhibitDelayMaxUSec",    bus_property_append_usec,           "t",  offsetof(Manager, inhibit_delay_max)   },
1337         { "HandlePowerKey",         bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_power_key)    },
1338         { "HandleSuspendKey",       bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_suspend_key)  },
1339         { "HandleHibernateKey",     bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_hibernate_key)},
1340         { "HandleLidSwitch",        bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_lid_switch)   },
1341         { "IdleAction",             bus_manager_append_handle_action,   "s",  offsetof(Manager, idle_action)         },
1342         { "IdleActionUSec",         bus_property_append_usec,           "t",  offsetof(Manager, idle_action_usec) },
1343         { "PreparingForShutdown",   bus_manager_append_preparing,       "b",  0 },
1344         { "PreparingForSleep",      bus_manager_append_preparing,       "b",  0 },
1345         { NULL, }
1346 };
1347
1348 static DBusHandlerResult manager_message_handler(
1349                 DBusConnection *connection,
1350                 DBusMessage *message,
1351                 void *userdata) {
1352
1353         Manager *m = userdata;
1354
1355         DBusError error;
1356         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1357         int r;
1358
1359         assert(connection);
1360         assert(message);
1361         assert(m);
1362
1363         dbus_error_init(&error);
1364
1365         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
1366                 const char *name;
1367                 char *p;
1368                 Session *session;
1369                 bool b;
1370
1371                 if (!dbus_message_get_args(
1372                                     message,
1373                                     &error,
1374                                     DBUS_TYPE_STRING, &name,
1375                                     DBUS_TYPE_INVALID))
1376                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1377
1378                 session = hashmap_get(m->sessions, name);
1379                 if (!session)
1380                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1381
1382                 reply = dbus_message_new_method_return(message);
1383                 if (!reply)
1384                         goto oom;
1385
1386                 p = session_bus_path(session);
1387                 if (!p)
1388                         goto oom;
1389
1390                 b = dbus_message_append_args(
1391                                 reply,
1392                                 DBUS_TYPE_OBJECT_PATH, &p,
1393                                 DBUS_TYPE_INVALID);
1394                 free(p);
1395
1396                 if (!b)
1397                         goto oom;
1398
1399         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) {
1400                 uint32_t pid;
1401                 char *p;
1402                 Session *session;
1403                 bool b;
1404
1405                 if (!dbus_message_get_args(
1406                                     message,
1407                                     &error,
1408                                     DBUS_TYPE_UINT32, &pid,
1409                                     DBUS_TYPE_INVALID))
1410                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1411
1412                 r = manager_get_session_by_pid(m, pid, &session);
1413                 if (r <= 0)
1414                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
1415
1416                 reply = dbus_message_new_method_return(message);
1417                 if (!reply)
1418                         goto oom;
1419
1420                 p = session_bus_path(session);
1421                 if (!p)
1422                         goto oom;
1423
1424                 b = dbus_message_append_args(
1425                                 reply,
1426                                 DBUS_TYPE_OBJECT_PATH, &p,
1427                                 DBUS_TYPE_INVALID);
1428                 free(p);
1429
1430                 if (!b)
1431                         goto oom;
1432
1433         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
1434                 uint32_t uid;
1435                 char *p;
1436                 User *user;
1437                 bool b;
1438
1439                 if (!dbus_message_get_args(
1440                                     message,
1441                                     &error,
1442                                     DBUS_TYPE_UINT32, &uid,
1443                                     DBUS_TYPE_INVALID))
1444                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1445
1446                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1447                 if (!user)
1448                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1449
1450                 reply = dbus_message_new_method_return(message);
1451                 if (!reply)
1452                         goto oom;
1453
1454                 p = user_bus_path(user);
1455                 if (!p)
1456                         goto oom;
1457
1458                 b = dbus_message_append_args(
1459                                 reply,
1460                                 DBUS_TYPE_OBJECT_PATH, &p,
1461                                 DBUS_TYPE_INVALID);
1462                 free(p);
1463
1464                 if (!b)
1465                         goto oom;
1466
1467         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUserByPID")) {
1468                 uint32_t pid;
1469                 char *p;
1470                 User *user;
1471                 bool b;
1472
1473                 if (!dbus_message_get_args(
1474                                     message,
1475                                     &error,
1476                                     DBUS_TYPE_UINT32, &pid,
1477                                     DBUS_TYPE_INVALID))
1478                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1479
1480                 r = manager_get_user_by_pid(m, pid, &user);
1481                 if (r <= 0)
1482                         return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
1483
1484                 reply = dbus_message_new_method_return(message);
1485                 if (!reply)
1486                         goto oom;
1487
1488                 p = user_bus_path(user);
1489                 if (!p)
1490                         goto oom;
1491
1492                 b = dbus_message_append_args(
1493                                 reply,
1494                                 DBUS_TYPE_OBJECT_PATH, &p,
1495                                 DBUS_TYPE_INVALID);
1496                 free(p);
1497
1498                 if (!b)
1499                         goto oom;
1500
1501         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
1502                 const char *name;
1503                 char *p;
1504                 Seat *seat;
1505                 bool b;
1506
1507                 if (!dbus_message_get_args(
1508                                     message,
1509                                     &error,
1510                                     DBUS_TYPE_STRING, &name,
1511                                     DBUS_TYPE_INVALID))
1512                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1513
1514                 seat = hashmap_get(m->seats, name);
1515                 if (!seat)
1516                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1517
1518                 reply = dbus_message_new_method_return(message);
1519                 if (!reply)
1520                         goto oom;
1521
1522                 p = seat_bus_path(seat);
1523                 if (!p)
1524                         goto oom;
1525
1526                 b = dbus_message_append_args(
1527                                 reply,
1528                                 DBUS_TYPE_OBJECT_PATH, &p,
1529                                 DBUS_TYPE_INVALID);
1530                 free(p);
1531
1532                 if (!b)
1533                         goto oom;
1534
1535         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
1536                 char *p;
1537                 Session *session;
1538                 Iterator i;
1539                 DBusMessageIter iter, sub;
1540                 const char *empty = "";
1541
1542                 reply = dbus_message_new_method_return(message);
1543                 if (!reply)
1544                         goto oom;
1545
1546                 dbus_message_iter_init_append(reply, &iter);
1547
1548                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
1549                         goto oom;
1550
1551                 HASHMAP_FOREACH(session, m->sessions, i) {
1552                         DBusMessageIter sub2;
1553                         uint32_t uid;
1554
1555                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1556                                 goto oom;
1557
1558                         uid = session->user->uid;
1559
1560                         p = session_bus_path(session);
1561                         if (!p)
1562                                 goto oom;
1563
1564                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
1565                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1566                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
1567                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
1568                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1569                                 free(p);
1570                                 goto oom;
1571                         }
1572
1573                         free(p);
1574
1575                         if (!dbus_message_iter_close_container(&sub, &sub2))
1576                                 goto oom;
1577                 }
1578
1579                 if (!dbus_message_iter_close_container(&iter, &sub))
1580                         goto oom;
1581
1582         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
1583                 User *user;
1584                 Iterator i;
1585                 DBusMessageIter iter, sub;
1586
1587                 reply = dbus_message_new_method_return(message);
1588                 if (!reply)
1589                         goto oom;
1590
1591                 dbus_message_iter_init_append(reply, &iter);
1592
1593                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
1594                         goto oom;
1595
1596                 HASHMAP_FOREACH(user, m->users, i) {
1597                         _cleanup_free_ char *p = NULL;
1598                         DBusMessageIter sub2;
1599                         uint32_t uid;
1600
1601                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1602                                 goto oom;
1603
1604                         uid = user->uid;
1605
1606                         p = user_bus_path(user);
1607                         if (!p)
1608                                 goto oom;
1609
1610                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1611                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
1612                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1613                                 free(p);
1614                                 goto oom;
1615                         }
1616
1617                         if (!dbus_message_iter_close_container(&sub, &sub2))
1618                                 goto oom;
1619                 }
1620
1621                 if (!dbus_message_iter_close_container(&iter, &sub))
1622                         goto oom;
1623
1624         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
1625                 Seat *seat;
1626                 Iterator i;
1627                 DBusMessageIter iter, sub;
1628
1629                 reply = dbus_message_new_method_return(message);
1630                 if (!reply)
1631                         goto oom;
1632
1633                 dbus_message_iter_init_append(reply, &iter);
1634
1635                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
1636                         goto oom;
1637
1638                 HASHMAP_FOREACH(seat, m->seats, i) {
1639                         _cleanup_free_ char *p = NULL;
1640                         DBusMessageIter sub2;
1641
1642                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1643                                 goto oom;
1644
1645                         p = seat_bus_path(seat);
1646                         if (!p)
1647                                 goto oom;
1648
1649                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
1650                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1651                                 free(p);
1652                                 goto oom;
1653                         }
1654
1655                         if (!dbus_message_iter_close_container(&sub, &sub2))
1656                                 goto oom;
1657                 }
1658
1659                 if (!dbus_message_iter_close_container(&iter, &sub))
1660                         goto oom;
1661
1662         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListInhibitors")) {
1663                 Inhibitor *inhibitor;
1664                 Iterator i;
1665                 DBusMessageIter iter, sub;
1666
1667                 reply = dbus_message_new_method_return(message);
1668                 if (!reply)
1669                         goto oom;
1670
1671                 dbus_message_iter_init_append(reply, &iter);
1672
1673                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssuu)", &sub))
1674                         goto oom;
1675
1676                 HASHMAP_FOREACH(inhibitor, m->inhibitors, i) {
1677                         DBusMessageIter sub2;
1678                         dbus_uint32_t uid, pid;
1679                         const char *what, *who, *why, *mode;
1680
1681                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1682                                 goto oom;
1683
1684                         what = strempty(inhibit_what_to_string(inhibitor->what));
1685                         who = strempty(inhibitor->who);
1686                         why = strempty(inhibitor->why);
1687                         mode = strempty(inhibit_mode_to_string(inhibitor->mode));
1688                         uid = (dbus_uint32_t) inhibitor->uid;
1689                         pid = (dbus_uint32_t) inhibitor->pid;
1690
1691                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &what) ||
1692                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &who) ||
1693                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &why) ||
1694                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &mode) ||
1695                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
1696                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid))
1697                                 goto oom;
1698
1699                         if (!dbus_message_iter_close_container(&sub, &sub2))
1700                                 goto oom;
1701                 }
1702
1703                 if (!dbus_message_iter_close_container(&iter, &sub))
1704                         goto oom;
1705
1706
1707         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Inhibit")) {
1708
1709                 r = bus_manager_inhibit(m, connection, message, &error, &reply);
1710
1711                 if (r < 0)
1712                         return bus_send_error_reply(connection, message, &error, r);
1713
1714
1715         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
1716
1717                 r = bus_manager_create_session(m, message);
1718
1719                 /* Don't delay the work on OOM here, since it might be
1720                  * triggered by a low RLIMIT_NOFILE here (since we
1721                  * send a dupped fd to the client), and we'd rather
1722                  * see this fail quickly then be retried later */
1723
1724                 if (r < 0)
1725                         return bus_send_error_reply(connection, message, NULL, r);
1726
1727         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ReleaseSession")) {
1728                 const char *name;
1729                 Session *session;
1730
1731                 if (!dbus_message_get_args(
1732                                     message,
1733                                     &error,
1734                                     DBUS_TYPE_STRING, &name,
1735                                     DBUS_TYPE_INVALID))
1736                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1737
1738                 session = hashmap_get(m->sessions, name);
1739                 if (!session)
1740                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1741
1742                 /* We use the FIFO to detect stray sessions where the
1743                 process invoking PAM dies abnormally. We need to make
1744                 sure that that process is not killed if at the clean
1745                 end of the session it closes the FIFO. Hence, with
1746                 this call explicitly turn off the FIFO logic, so that
1747                 the PAM code can finish clean up on its own */
1748                 session_remove_fifo(session);
1749
1750                 reply = dbus_message_new_method_return(message);
1751                 if (!reply)
1752                         goto oom;
1753
1754         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
1755                 const char *name;
1756                 Session *session;
1757
1758                 if (!dbus_message_get_args(
1759                                     message,
1760                                     &error,
1761                                     DBUS_TYPE_STRING, &name,
1762                                     DBUS_TYPE_INVALID))
1763                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1764
1765                 session = hashmap_get(m->sessions, name);
1766                 if (!session)
1767                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1768
1769                 r = session_activate(session);
1770                 if (r < 0)
1771                         return bus_send_error_reply(connection, message, NULL, r);
1772
1773                 reply = dbus_message_new_method_return(message);
1774                 if (!reply)
1775                         goto oom;
1776
1777         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) {
1778                 const char *session_name, *seat_name;
1779                 Session *session;
1780                 Seat *seat;
1781
1782                 /* Same as ActivateSession() but refuses to work if
1783                  * the seat doesn't match */
1784
1785                 if (!dbus_message_get_args(
1786                                     message,
1787                                     &error,
1788                                     DBUS_TYPE_STRING, &session_name,
1789                                     DBUS_TYPE_STRING, &seat_name,
1790                                     DBUS_TYPE_INVALID))
1791                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1792
1793                 session = hashmap_get(m->sessions, session_name);
1794                 if (!session)
1795                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1796
1797                 seat = hashmap_get(m->seats, seat_name);
1798                 if (!seat)
1799                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1800
1801                 if (session->seat != seat)
1802                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1803
1804                 r = session_activate(session);
1805                 if (r < 0)
1806                         return bus_send_error_reply(connection, message, NULL, r);
1807
1808                 reply = dbus_message_new_method_return(message);
1809                 if (!reply)
1810                         goto oom;
1811
1812         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") ||
1813                    dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) {
1814                 const char *name;
1815                 Session *session;
1816
1817                 if (!dbus_message_get_args(
1818                                     message,
1819                                     &error,
1820                                     DBUS_TYPE_STRING, &name,
1821                                     DBUS_TYPE_INVALID))
1822                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1823
1824                 session = hashmap_get(m->sessions, name);
1825                 if (!session)
1826                         return bus_send_error_reply(connection, message, NULL, -ENOENT);
1827
1828                 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
1829                         goto oom;
1830
1831                 reply = dbus_message_new_method_return(message);
1832                 if (!reply)
1833                         goto oom;
1834
1835         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSessions") ||
1836                    dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSessions")) {
1837
1838                 r = session_send_lock_all(m, streq(dbus_message_get_member(message), "LockSessions"));
1839                 if (r < 0)
1840                         bus_send_error_reply(connection, message, NULL, r);
1841
1842                 reply = dbus_message_new_method_return(message);
1843                 if (!reply)
1844                         goto oom;
1845
1846         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) {
1847                 const char *swho;
1848                 int32_t signo;
1849                 KillWho who;
1850                 const char *name;
1851                 Session *session;
1852
1853                 if (!dbus_message_get_args(
1854                                     message,
1855                                     &error,
1856                                     DBUS_TYPE_STRING, &name,
1857                                     DBUS_TYPE_STRING, &swho,
1858                                     DBUS_TYPE_INT32, &signo,
1859                                     DBUS_TYPE_INVALID))
1860                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1861
1862                 if (isempty(swho))
1863                         who = KILL_ALL;
1864                 else {
1865                         who = kill_who_from_string(swho);
1866                         if (who < 0)
1867                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
1868                 }
1869
1870                 if (signo <= 0 || signo >= _NSIG)
1871                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1872
1873                 session = hashmap_get(m->sessions, name);
1874                 if (!session)
1875                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1876
1877                 r = session_kill(session, who, signo);
1878                 if (r < 0)
1879                         return bus_send_error_reply(connection, message, NULL, r);
1880
1881                 reply = dbus_message_new_method_return(message);
1882                 if (!reply)
1883                         goto oom;
1884
1885         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) {
1886                 uint32_t uid;
1887                 User *user;
1888                 int32_t signo;
1889
1890                 if (!dbus_message_get_args(
1891                                     message,
1892                                     &error,
1893                                     DBUS_TYPE_UINT32, &uid,
1894                                     DBUS_TYPE_INT32, &signo,
1895                                     DBUS_TYPE_INVALID))
1896                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1897
1898                 if (signo <= 0 || signo >= _NSIG)
1899                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1900
1901                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1902                 if (!user)
1903                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1904
1905                 r = user_kill(user, signo);
1906                 if (r < 0)
1907                         return bus_send_error_reply(connection, message, NULL, r);
1908
1909                 reply = dbus_message_new_method_return(message);
1910                 if (!reply)
1911                         goto oom;
1912
1913         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
1914                 const char *name;
1915                 Session *session;
1916
1917                 if (!dbus_message_get_args(
1918                                     message,
1919                                     &error,
1920                                     DBUS_TYPE_STRING, &name,
1921                                     DBUS_TYPE_INVALID))
1922                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1923
1924                 session = hashmap_get(m->sessions, name);
1925                 if (!session)
1926                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1927
1928                 r = session_stop(session);
1929                 if (r < 0)
1930                         return bus_send_error_reply(connection, message, NULL, r);
1931
1932                 reply = dbus_message_new_method_return(message);
1933                 if (!reply)
1934                         goto oom;
1935
1936         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1937                 uint32_t uid;
1938                 User *user;
1939
1940                 if (!dbus_message_get_args(
1941                                     message,
1942                                     &error,
1943                                     DBUS_TYPE_UINT32, &uid,
1944                                     DBUS_TYPE_INVALID))
1945                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1946
1947                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1948                 if (!user)
1949                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1950
1951                 r = user_stop(user);
1952                 if (r < 0)
1953                         return bus_send_error_reply(connection, message, NULL, r);
1954
1955                 reply = dbus_message_new_method_return(message);
1956                 if (!reply)
1957                         goto oom;
1958
1959         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1960                 const char *name;
1961                 Seat *seat;
1962
1963                 if (!dbus_message_get_args(
1964                                     message,
1965                                     &error,
1966                                     DBUS_TYPE_STRING, &name,
1967                                     DBUS_TYPE_INVALID))
1968                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1969
1970                 seat = hashmap_get(m->seats, name);
1971                 if (!seat)
1972                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1973
1974                 r = seat_stop_sessions(seat);
1975                 if (r < 0)
1976                         return bus_send_error_reply(connection, message, NULL, r);
1977
1978                 reply = dbus_message_new_method_return(message);
1979                 if (!reply)
1980                         goto oom;
1981
1982         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1983                 uint32_t uid;
1984                 struct passwd *pw;
1985                 dbus_bool_t b, interactive;
1986                 char *path;
1987
1988                 if (!dbus_message_get_args(
1989                                     message,
1990                                     &error,
1991                                     DBUS_TYPE_UINT32, &uid,
1992                                     DBUS_TYPE_BOOLEAN, &b,
1993                                     DBUS_TYPE_BOOLEAN, &interactive,
1994                                     DBUS_TYPE_INVALID))
1995                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1996
1997                 errno = 0;
1998                 pw = getpwuid(uid);
1999                 if (!pw)
2000                         return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
2001
2002                 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, NULL, &error);
2003                 if (r < 0)
2004                         return bus_send_error_reply(connection, message, &error, r);
2005
2006                 mkdir_p_label("/var/lib/systemd", 0755);
2007
2008                 r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0);
2009                 if (r < 0)
2010                         return bus_send_error_reply(connection, message, &error, r);
2011
2012                 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
2013                 if (!path)
2014                         goto oom;
2015
2016                 if (b) {
2017                         User *u;
2018
2019                         r = touch(path);
2020                         free(path);
2021
2022                         if (r < 0)
2023                                 return bus_send_error_reply(connection, message, &error, r);
2024
2025                         if (manager_add_user_by_uid(m, uid, &u) >= 0)
2026                                 user_start(u);
2027
2028                 } else {
2029                         User *u;
2030
2031                         r = unlink(path);
2032                         free(path);
2033
2034                         if (r < 0 && errno != ENOENT)
2035                                 return bus_send_error_reply(connection, message, &error, -errno);
2036
2037                         u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
2038                         if (u)
2039                                 user_add_to_gc_queue(u);
2040                 }
2041
2042                 reply = dbus_message_new_method_return(message);
2043                 if (!reply)
2044                         goto oom;
2045
2046         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
2047                 const char *sysfs, *seat;
2048                 dbus_bool_t interactive;
2049
2050                 if (!dbus_message_get_args(
2051                                     message,
2052                                     &error,
2053                                     DBUS_TYPE_STRING, &seat,
2054                                     DBUS_TYPE_STRING, &sysfs,
2055                                     DBUS_TYPE_BOOLEAN, &interactive,
2056                                     DBUS_TYPE_INVALID))
2057                         return bus_send_error_reply(connection, message, &error, -EINVAL);
2058
2059                 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
2060                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
2061
2062                 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, NULL, &error);
2063                 if (r < 0)
2064                         return bus_send_error_reply(connection, message, &error, r);
2065
2066                 r = attach_device(m, seat, sysfs);
2067                 if (r < 0)
2068                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
2069
2070                 reply = dbus_message_new_method_return(message);
2071                 if (!reply)
2072                         goto oom;
2073
2074
2075         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
2076                 dbus_bool_t interactive;
2077
2078                 if (!dbus_message_get_args(
2079                                     message,
2080                                     &error,
2081                                     DBUS_TYPE_BOOLEAN, &interactive,
2082                                     DBUS_TYPE_INVALID))
2083                         return bus_send_error_reply(connection, message, &error, -EINVAL);
2084
2085                 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, NULL, &error);
2086                 if (r < 0)
2087                         return bus_send_error_reply(connection, message, &error, r);
2088
2089                 r = flush_devices(m);
2090                 if (r < 0)
2091                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
2092
2093                 reply = dbus_message_new_method_return(message);
2094                 if (!reply)
2095                         goto oom;
2096
2097         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff")) {
2098
2099                 r = bus_manager_do_shutdown_or_sleep(
2100                                 m, connection, message,
2101                                 SPECIAL_POWEROFF_TARGET,
2102                                 INHIBIT_SHUTDOWN,
2103                                 "org.freedesktop.login1.power-off",
2104                                 "org.freedesktop.login1.power-off-multiple-sessions",
2105                                 "org.freedesktop.login1.power-off-ignore-inhibit",
2106                                 NULL,
2107                                 &error, &reply);
2108                 if (r < 0)
2109                         return bus_send_error_reply(connection, message, &error, r);
2110         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
2111                 r = bus_manager_do_shutdown_or_sleep(
2112                                 m, connection, message,
2113                                 SPECIAL_REBOOT_TARGET,
2114                                 INHIBIT_SHUTDOWN,
2115                                 "org.freedesktop.login1.reboot",
2116                                 "org.freedesktop.login1.reboot-multiple-sessions",
2117                                 "org.freedesktop.login1.reboot-ignore-inhibit",
2118                                 NULL,
2119                                 &error, &reply);
2120                 if (r < 0)
2121                         return bus_send_error_reply(connection, message, &error, r);
2122
2123         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Suspend")) {
2124                 r = bus_manager_do_shutdown_or_sleep(
2125                                 m, connection, message,
2126                                 SPECIAL_SUSPEND_TARGET,
2127                                 INHIBIT_SLEEP,
2128                                 "org.freedesktop.login1.suspend",
2129                                 "org.freedesktop.login1.suspend-multiple-sessions",
2130                                 "org.freedesktop.login1.suspend-ignore-inhibit",
2131                                 "suspend",
2132                                 &error, &reply);
2133                 if (r < 0)
2134                         return bus_send_error_reply(connection, message, &error, r);
2135         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Hibernate")) {
2136                 r = bus_manager_do_shutdown_or_sleep(
2137                                 m, connection, message,
2138                                 SPECIAL_HIBERNATE_TARGET,
2139                                 INHIBIT_SLEEP,
2140                                 "org.freedesktop.login1.hibernate",
2141                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2142                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2143                                 "hibernate",
2144                                 &error, &reply);
2145                 if (r < 0)
2146                         return bus_send_error_reply(connection, message, &error, r);
2147
2148         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "HybridSleep")) {
2149                 r = bus_manager_do_shutdown_or_sleep(
2150                                 m, connection, message,
2151                                 SPECIAL_HYBRID_SLEEP_TARGET,
2152                                 INHIBIT_SLEEP,
2153                                 "org.freedesktop.login1.hibernate",
2154                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2155                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2156                                 "hybrid-sleep",
2157                                 &error, &reply);
2158                 if (r < 0)
2159                         return bus_send_error_reply(connection, message, &error, r);
2160
2161         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff")) {
2162
2163                 r = bus_manager_can_shutdown_or_sleep(
2164                                 m, connection, message,
2165                                 INHIBIT_SHUTDOWN,
2166                                 "org.freedesktop.login1.power-off",
2167                                 "org.freedesktop.login1.power-off-multiple-sessions",
2168                                 "org.freedesktop.login1.power-off-ignore-inhibit",
2169                                 NULL,
2170                                 &error, &reply);
2171                 if (r < 0)
2172                         return bus_send_error_reply(connection, message, &error, r);
2173         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
2174                 r = bus_manager_can_shutdown_or_sleep(
2175                                 m, connection, message,
2176                                 INHIBIT_SHUTDOWN,
2177                                 "org.freedesktop.login1.reboot",
2178                                 "org.freedesktop.login1.reboot-multiple-sessions",
2179                                 "org.freedesktop.login1.reboot-ignore-inhibit",
2180                                 NULL,
2181                                 &error, &reply);
2182                 if (r < 0)
2183                         return bus_send_error_reply(connection, message, &error, r);
2184
2185         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanSuspend")) {
2186                 r = bus_manager_can_shutdown_or_sleep(
2187                                 m, connection, message,
2188                                 INHIBIT_SLEEP,
2189                                 "org.freedesktop.login1.suspend",
2190                                 "org.freedesktop.login1.suspend-multiple-sessions",
2191                                 "org.freedesktop.login1.suspend-ignore-inhibit",
2192                                 "suspend",
2193                                 &error, &reply);
2194                 if (r < 0)
2195                         return bus_send_error_reply(connection, message, &error, r);
2196
2197         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHibernate")) {
2198                 r = bus_manager_can_shutdown_or_sleep(
2199                                 m, connection, message,
2200                                 INHIBIT_SLEEP,
2201                                 "org.freedesktop.login1.hibernate",
2202                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2203                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2204                                 "hibernate",
2205                                 &error, &reply);
2206                 if (r < 0)
2207                         return bus_send_error_reply(connection, message, &error, r);
2208
2209         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHybridSleep")) {
2210                 r = bus_manager_can_shutdown_or_sleep(
2211                                 m, connection, message,
2212                                 INHIBIT_SLEEP,
2213                                 "org.freedesktop.login1.hibernate",
2214                                 "org.freedesktop.login1.hibernate-multiple-sessions",
2215                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
2216                                 "hybrid-sleep",
2217                                 &error, &reply);
2218                 if (r < 0)
2219                         return bus_send_error_reply(connection, message, &error, r);
2220
2221         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
2222                 char *introspection = NULL;
2223                 FILE *f;
2224                 Iterator i;
2225                 Session *session;
2226                 Seat *seat;
2227                 User *user;
2228                 size_t size;
2229                 char *p;
2230
2231                 if (!(reply = dbus_message_new_method_return(message)))
2232                         goto oom;
2233
2234                 /* We roll our own introspection code here, instead of
2235                  * relying on bus_default_message_handler() because we
2236                  * need to generate our introspection string
2237                  * dynamically. */
2238
2239                 if (!(f = open_memstream(&introspection, &size)))
2240                         goto oom;
2241
2242                 fputs(INTROSPECTION_BEGIN, f);
2243
2244                 HASHMAP_FOREACH(seat, m->seats, i) {
2245                         p = bus_path_escape(seat->id);
2246
2247                         if (p) {
2248                                 fprintf(f, "<node name=\"seat/%s\"/>", p);
2249                                 free(p);
2250                         }
2251                 }
2252
2253                 HASHMAP_FOREACH(user, m->users, i)
2254                         fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
2255
2256                 HASHMAP_FOREACH(session, m->sessions, i) {
2257                         p = bus_path_escape(session->id);
2258
2259                         if (p) {
2260                                 fprintf(f, "<node name=\"session/%s\"/>", p);
2261                                 free(p);
2262                         }
2263                 }
2264
2265                 fputs(INTROSPECTION_END, f);
2266
2267                 if (ferror(f)) {
2268                         fclose(f);
2269                         free(introspection);
2270                         goto oom;
2271                 }
2272
2273                 fclose(f);
2274
2275                 if (!introspection)
2276                         goto oom;
2277
2278                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
2279                         free(introspection);
2280                         goto oom;
2281                 }
2282
2283                 free(introspection);
2284         } else {
2285                 const BusBoundProperties bps[] = {
2286                         { "org.freedesktop.login1.Manager", bus_login_manager_properties, m },
2287                         { NULL, }
2288                 };
2289                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
2290         }
2291
2292         if (reply) {
2293                 if (!bus_maybe_send_reply(connection, message, reply))
2294                         goto oom;
2295         }
2296
2297         return DBUS_HANDLER_RESULT_HANDLED;
2298
2299 oom:
2300         dbus_error_free(&error);
2301
2302         return DBUS_HANDLER_RESULT_NEED_MEMORY;
2303 }
2304
2305 const DBusObjectPathVTable bus_manager_vtable = {
2306         .message_function = manager_message_handler
2307 };
2308
2309 DBusHandlerResult bus_message_filter(
2310                 DBusConnection *connection,
2311                 DBusMessage *message,
2312                 void *userdata) {
2313
2314         Manager *m = userdata;
2315         DBusError error;
2316
2317         assert(m);
2318         assert(connection);
2319         assert(message);
2320
2321         dbus_error_init(&error);
2322
2323         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)));
2324
2325         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
2326                 const char *path, *result, *unit;
2327                 uint32_t id;
2328
2329                 if (!dbus_message_get_args(message, &error,
2330                                            DBUS_TYPE_UINT32, &id,
2331                                            DBUS_TYPE_OBJECT_PATH, &path,
2332                                            DBUS_TYPE_STRING, &unit,
2333                                            DBUS_TYPE_STRING, &result,
2334                                            DBUS_TYPE_INVALID)) {
2335                         log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
2336                         goto finish;
2337                 }
2338
2339                 if (m->action_job && streq(m->action_job, path)) {
2340                         log_info("Operation finished.");
2341
2342                         /* Tell people that they now may take a lock again */
2343                         send_prepare_for(m, m->action_what, false);
2344
2345                         free(m->action_job);
2346                         m->action_job = NULL;
2347                         m->action_unit = NULL;
2348                         m->action_what = 0;
2349
2350                 } else {
2351                         Session *s;
2352                         User *u;
2353
2354                         s = hashmap_get(m->session_units, unit);
2355                         if (s) {
2356                                 if (streq_ptr(path, s->scope_job)) {
2357                                         free(s->scope_job);
2358                                         s->scope_job = NULL;
2359                                         session_save(s);
2360
2361                                         if (s->started) {
2362                                                 if (streq(result, "done"))
2363                                                         session_send_create_reply(s, NULL);
2364                                                 else {
2365                                                         dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
2366                                                         session_send_create_reply(s, &error);
2367                                                 }
2368                                         }
2369                                 }
2370
2371                                 session_add_to_gc_queue(s);
2372                         }
2373
2374                         u = hashmap_get(m->user_units, unit);
2375                         if (u) {
2376                                 if (streq_ptr(path, u->service_job)) {
2377                                         free(u->service_job);
2378                                         u->service_job = NULL;
2379                                 }
2380
2381                                 if (streq_ptr(path, u->slice_job)) {
2382                                         free(u->slice_job);
2383                                         u->slice_job = NULL;
2384                                 }
2385
2386                                 user_save(u);
2387                                 user_add_to_gc_queue(u);
2388                         }
2389                 }
2390
2391         } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
2392
2393                 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2394                 _cleanup_free_ char *unit = NULL;
2395                 const char *path;
2396
2397                 path = dbus_message_get_path(message);
2398                 if (!path)
2399                         goto finish;
2400
2401                 unit_name_from_dbus_path(path, &unit);
2402                 if (unit) {
2403                         Session *s;
2404                         User *u;
2405
2406                         s = hashmap_get(m->session_units, unit);
2407                         if (s)
2408                                 session_add_to_gc_queue(s);
2409
2410                         u = hashmap_get(m->user_units, unit);
2411                         if (u)
2412                                 user_add_to_gc_queue(u);
2413                 }
2414         }
2415
2416 finish:
2417         dbus_error_free(&error);
2418
2419         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2420 }
2421
2422 int manager_send_changed(Manager *manager, const char *properties) {
2423         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
2424
2425         assert(manager);
2426
2427         m = bus_properties_changed_new("/org/freedesktop/login1",
2428                                        "org.freedesktop.login1.Manager",
2429                                        properties);
2430         if (!m)
2431                 return -ENOMEM;
2432
2433         if (!dbus_connection_send(manager->bus, m, NULL))
2434                 return -ENOMEM;
2435
2436         return 0;
2437 }
2438
2439 int manager_dispatch_delayed(Manager *manager) {
2440         DBusError error;
2441         int r;
2442
2443         assert(manager);
2444
2445         if (manager->action_what == 0 || manager->action_job)
2446                 return 0;
2447
2448         /* Continue delay? */
2449         if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0)) {
2450
2451                 if (manager->action_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC))
2452                         return 0;
2453
2454                 log_info("Delay lock is active but inhibitor timeout is reached.");
2455         }
2456
2457         /* Actually do the operation */
2458         dbus_error_init(&error);
2459         r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
2460         if (r < 0) {
2461                 log_warning("Failed to send delayed message: %s", bus_error(&error, r));
2462                 dbus_error_free(&error);
2463
2464                 manager->action_unit = NULL;
2465                 manager->action_what = 0;
2466                 return r;
2467         }
2468
2469         return 1;
2470 }
2471
2472 int manager_start_scope(
2473                 Manager *manager,
2474                 const char *scope,
2475                 pid_t pid,
2476                 const char *slice,
2477                 const char *description,
2478                 DBusError *error,
2479                 char **job) {
2480
2481         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
2482         DBusMessageIter iter, sub, sub2, sub3, sub4;
2483         const char *timeout_stop_property = "TimeoutStopUSec";
2484         const char *pids_property = "PIDs";
2485         uint64_t timeout = 500 * USEC_PER_MSEC;
2486         const char *fail = "fail";
2487         uint32_t u;
2488
2489         assert(manager);
2490         assert(scope);
2491         assert(pid > 1);
2492
2493         if (!slice)
2494                 slice = "";
2495
2496         m = dbus_message_new_method_call(
2497                         "org.freedesktop.systemd1",
2498                         "/org/freedesktop/systemd1",
2499                         "org.freedesktop.systemd1.Manager",
2500                         "StartTransientUnit");
2501         if (!m)
2502                 return log_oom();
2503
2504         dbus_message_iter_init_append(m, &iter);
2505
2506         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
2507             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
2508             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
2509                 return log_oom();
2510
2511         if (!isempty(slice)) {
2512                 const char *slice_property = "Slice";
2513
2514                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2515                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
2516                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
2517                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
2518                     !dbus_message_iter_close_container(&sub2, &sub3) ||
2519                     !dbus_message_iter_close_container(&sub, &sub2))
2520                         return log_oom();
2521         }
2522
2523         if (!isempty(description)) {
2524                 const char *description_property = "Description";
2525
2526                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2527                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
2528                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
2529                     !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
2530                     !dbus_message_iter_close_container(&sub2, &sub3) ||
2531                     !dbus_message_iter_close_container(&sub, &sub2))
2532                         return log_oom();
2533         }
2534
2535         /* cgroup empty notification is not available in containers
2536          * currently. To make this less problematic, let's shorten the
2537          * stop timeout for sessions, so that we don't wait
2538          * forever. */
2539
2540         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2541             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
2542             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
2543             !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
2544             !dbus_message_iter_close_container(&sub2, &sub3) ||
2545             !dbus_message_iter_close_container(&sub, &sub2))
2546                 return log_oom();
2547
2548         u = pid;
2549         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
2550             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
2551             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
2552             !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
2553             !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
2554             !dbus_message_iter_close_container(&sub3, &sub4) ||
2555             !dbus_message_iter_close_container(&sub2, &sub3) ||
2556             !dbus_message_iter_close_container(&sub, &sub2) ||
2557             !dbus_message_iter_close_container(&iter, &sub))
2558                 return log_oom();
2559
2560         reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
2561         if (!reply)
2562                 return -EIO;
2563
2564         if (job) {
2565                 const char *j;
2566                 char *copy;
2567
2568                 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
2569                         return -EIO;
2570
2571                 copy = strdup(j);
2572                 if (!copy)
2573                         return -ENOMEM;
2574
2575                 *job = copy;
2576         }
2577
2578         return 0;
2579 }
2580
2581 int manager_start_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
2582         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2583         const char *fail = "fail";
2584         int r;
2585
2586         assert(manager);
2587         assert(unit);
2588
2589         r = bus_method_call_with_reply(
2590                         manager->bus,
2591                         "org.freedesktop.systemd1",
2592                         "/org/freedesktop/systemd1",
2593                         "org.freedesktop.systemd1.Manager",
2594                         "StartUnit",
2595                         &reply,
2596                         error,
2597                         DBUS_TYPE_STRING, &unit,
2598                         DBUS_TYPE_STRING, &fail,
2599                         DBUS_TYPE_INVALID);
2600         if (r < 0) {
2601                 log_error("Failed to start unit %s: %s", unit, bus_error(error, r));
2602                 return r;
2603         }
2604
2605         if (job) {
2606                 const char *j;
2607                 char *copy;
2608
2609                 if (!dbus_message_get_args(reply, error,
2610                                            DBUS_TYPE_OBJECT_PATH, &j,
2611                                            DBUS_TYPE_INVALID)) {
2612                         log_error("Failed to parse reply.");
2613                         return -EIO;
2614                 }
2615
2616                 copy = strdup(j);
2617                 if (!copy)
2618                         return -ENOMEM;
2619
2620                 *job = copy;
2621         }
2622
2623         return 0;
2624 }
2625
2626 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
2627         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2628         const char *fail = "fail";
2629         int r;
2630
2631         assert(manager);
2632         assert(unit);
2633
2634         r = bus_method_call_with_reply(
2635                         manager->bus,
2636                         "org.freedesktop.systemd1",
2637                         "/org/freedesktop/systemd1",
2638                         "org.freedesktop.systemd1.Manager",
2639                         "StopUnit",
2640                         &reply,
2641                         error,
2642                         DBUS_TYPE_STRING, &unit,
2643                         DBUS_TYPE_STRING, &fail,
2644                         DBUS_TYPE_INVALID);
2645         if (r < 0) {
2646                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
2647                 return r;
2648         }
2649
2650         if (job) {
2651                 const char *j;
2652                 char *copy;
2653
2654                 if (!dbus_message_get_args(reply, error,
2655                                            DBUS_TYPE_OBJECT_PATH, &j,
2656                                            DBUS_TYPE_INVALID)) {
2657                         log_error("Failed to parse reply.");
2658                         return -EIO;
2659                 }
2660
2661                 copy = strdup(j);
2662                 if (!copy)
2663                         return -ENOMEM;
2664
2665                 *job = copy;
2666         }
2667
2668         return 0;
2669 }
2670
2671 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
2672         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2673         const char *w;
2674         int r;
2675
2676         assert(manager);
2677         assert(unit);
2678
2679         w = who == KILL_LEADER ? "process" : "cgroup";
2680         assert_cc(sizeof(signo) == sizeof(int32_t));
2681
2682         r = bus_method_call_with_reply(
2683                         manager->bus,
2684                         "org.freedesktop.systemd1",
2685                         "/org/freedesktop/systemd1",
2686                         "org.freedesktop.systemd1.Manager",
2687                         "KillUnit",
2688                         &reply,
2689                         error,
2690                         DBUS_TYPE_STRING, &unit,
2691                         DBUS_TYPE_STRING, &w,
2692                         DBUS_TYPE_INT32, &signo,
2693                         DBUS_TYPE_INVALID);
2694         if (r < 0) {
2695                 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
2696                 return r;
2697         }
2698
2699         return 0;
2700 }
2701
2702 int manager_unit_is_active(Manager *manager, const char *unit) {
2703
2704         const char *interface = "org.freedesktop.systemd1.Unit";
2705         const char *property = "ActiveState";
2706         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2707         _cleanup_free_ char *path = NULL;
2708         DBusMessageIter iter, sub;
2709         const char *state;
2710         DBusError error;
2711         int r;
2712
2713         assert(manager);
2714         assert(unit);
2715
2716         dbus_error_init(&error);
2717
2718         path = unit_dbus_path_from_name(unit);
2719         if (!path)
2720                 return -ENOMEM;
2721
2722         r = bus_method_call_with_reply(
2723                         manager->bus,
2724                         "org.freedesktop.systemd1",
2725                         path,
2726                         "org.freedesktop.DBus.Properties",
2727                         "Get",
2728                         &reply,
2729                         &error,
2730                         DBUS_TYPE_STRING, &interface,
2731                         DBUS_TYPE_STRING, &property,
2732                         DBUS_TYPE_INVALID);
2733
2734         if (r < 0) {
2735                 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
2736                 dbus_error_free(&error);
2737                 return r;
2738         }
2739
2740         if (!dbus_message_iter_init(reply, &iter) ||
2741             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
2742                 log_error("Failed to parse reply.");
2743                 return -EINVAL;
2744         }
2745
2746         dbus_message_iter_recurse(&iter, &sub);
2747         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
2748                 log_error("Failed to parse reply.");
2749                 return -EINVAL;
2750         }
2751
2752         dbus_message_iter_get_basic(&sub, &state);
2753
2754         return !streq(state, "inactive") && !streq(state, "failed");
2755 }