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