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