]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-dbus.c
update TODO
[thirdparty/systemd.git] / src / login / logind-dbus.c
CommitLineData
3f49d45a
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
a185c5aa
LP
22#include <errno.h>
23#include <string.h>
98a28fef 24#include <unistd.h>
7f7bb946 25#include <pwd.h>
a185c5aa 26
3f49d45a
LP
27#include "logind.h"
28#include "dbus-common.h"
98a28fef 29#include "strv.h"
7f7bb946 30#include "polkit.h"
55af3897 31#include "special.h"
3f49d45a
LP
32
33#define BUS_MANAGER_INTERFACE \
34 " <interface name=\"org.freedesktop.login1.Manager\">\n" \
bef422ae 35 " <method name=\"GetSession\">\n" \
3f49d45a 36 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
bef422ae 37 " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
3f49d45a 38 " </method>\n" \
c4aa65e7
LP
39 " <method name=\"GetSessionByPID\">\n" \
40 " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
41 " <arg name=\"session\" type=\"o\" direction=\"out\"/>\n" \
42 " </method>\n" \
3f49d45a 43 " <method name=\"GetUser\">\n" \
bef422ae 44 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
3f49d45a
LP
45 " <arg name=\"user\" type=\"o\" direction=\"out\"/>\n" \
46 " </method>\n" \
bef422ae 47 " <method name=\"GetSeat\">\n" \
3f49d45a 48 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
bef422ae 49 " <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n" \
3f49d45a 50 " </method>\n" \
bef422ae 51 " <method name=\"ListSessions\">\n" \
e1c9c2d5 52 " <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
3f49d45a
LP
53 " </method>\n" \
54 " <method name=\"ListUsers\">\n" \
55 " <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n" \
56 " </method>\n" \
bef422ae
LP
57 " <method name=\"ListSeats\">\n" \
58 " <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n" \
3f49d45a
LP
59 " </method>\n" \
60 " <method name=\"CreateSession\">\n" \
61 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
62 " <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
4d6d6518 63 " <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n" \
3f49d45a
LP
64 " <arg name=\"type\" type=\"s\" direction=\"in\"/>\n" \
65 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
4d6d6518 66 " <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n" \
3f49d45a
LP
67 " <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n" \
68 " <arg name=\"display\" type=\"s\" direction=\"in\"/>\n" \
69 " <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n" \
70 " <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n" \
71 " <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n" \
72 " <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
73 " <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
98a28fef 74 " <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
3f49d45a
LP
75 " <arg name=\"id\" type=\"s\" direction=\"out\"/>\n" \
76 " <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
98a28fef 77 " <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
3f49d45a 78 " <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \
bbc73283
LP
79 " <arg name=\"seat\" type=\"s\" direction=\"out\"/>\n" \
80 " <arg name=\"vtnr\" type=\"u\" direction=\"out\"/>\n" \
3f49d45a 81 " </method>\n" \
a185c5aa
LP
82 " <method name=\"ActivateSession\">\n" \
83 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
84 " </method>\n" \
84c3361e
LP
85 " <method name=\"ActivateSessionOnSeat\">\n" \
86 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
87 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
88 " </method>\n" \
88e3dc90
LP
89 " <method name=\"LockSession\">\n" \
90 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
91 " </method>\n" \
92 " <method name=\"UnlockSession\">\n" \
93 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
94 " </method>\n" \
de07ab16
LP
95 " <method name=\"KillSession\">\n" \
96 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
cae5846b
LP
97 " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
98 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
de07ab16
LP
99 " </method>\n" \
100 " <method name=\"KillUser\">\n" \
101 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
cae5846b 102 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
de07ab16 103 " </method>\n" \
3f49d45a
LP
104 " <method name=\"TerminateSession\">\n" \
105 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
106 " </method>\n" \
107 " <method name=\"TerminateUser\">\n" \
bef422ae 108 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
3f49d45a
LP
109 " </method>\n" \
110 " <method name=\"TerminateSeat\">\n" \
111 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
112 " </method>\n" \
7f7bb946
LP
113 " <method name=\"SetUserLinger\">\n" \
114 " <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
115 " <arg name=\"b\" type=\"b\" direction=\"in\"/>\n" \
116 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
117 " </method>\n" \
47a26690
LP
118 " <method name=\"AttachDevice\">\n" \
119 " <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n" \
120 " <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n" \
121 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
122 " </method>\n" \
b668e064
LP
123 " <method name=\"FlushDevices\">\n" \
124 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
125 " </method>\n" \
55af3897
LP
126 " <method name=\"PowerOff\">\n" \
127 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
128 " </method>\n" \
129 " <method name=\"Reboot\">\n" \
130 " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
131 " </method>\n" \
89f13440
LP
132 " <method name=\"CanPowerOff\">\n" \
133 " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
134 " </method>\n" \
135 " <method name=\"CanReboot\">\n" \
136 " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
137 " </method>\n" \
3f49d45a
LP
138 " <signal name=\"SessionNew\">\n" \
139 " <arg name=\"id\" type=\"s\"/>\n" \
140 " <arg name=\"path\" type=\"o\"/>\n" \
141 " </signal>\n" \
142 " <signal name=\"SessionRemoved\">\n" \
143 " <arg name=\"id\" type=\"s\"/>\n" \
144 " <arg name=\"path\" type=\"o\"/>\n" \
145 " </signal>\n" \
146 " <signal name=\"UserNew\">\n" \
147 " <arg name=\"uid\" type=\"u\"/>\n" \
148 " <arg name=\"path\" type=\"o\"/>\n" \
149 " </signal>\n" \
150 " <signal name=\"UserRemoved\">\n" \
151 " <arg name=\"uid\" type=\"u\"/>\n" \
152 " <arg name=\"path\" type=\"o\"/>\n" \
153 " </signal>\n" \
154 " <signal name=\"SeatNew\">\n" \
155 " <arg name=\"id\" type=\"s\"/>\n" \
156 " <arg name=\"path\" type=\"o\"/>\n" \
157 " </signal>\n" \
158 " <signal name=\"SeatRemoved\">\n" \
159 " <arg name=\"id\" type=\"s\"/>\n" \
160 " <arg name=\"path\" type=\"o\"/>\n" \
161 " </signal>\n" \
162 " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
163 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
164 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
165 " <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
166 " <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
167 " <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
168 " <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
a185c5aa
LP
169 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
170 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
171 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
3f49d45a
LP
172 " </interface>\n"
173
174#define INTROSPECTION_BEGIN \
175 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
176 "<node>\n" \
177 BUS_MANAGER_INTERFACE \
178 BUS_PROPERTIES_INTERFACE \
179 BUS_PEER_INTERFACE \
180 BUS_INTROSPECTABLE_INTERFACE
181
182#define INTROSPECTION_END \
183 "</node>\n"
184
185#define INTERFACES_LIST \
186 BUS_GENERIC_INTERFACES_LIST \
187 "org.freedesktop.login1.Manager\0"
188
a185c5aa
LP
189static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
190 Manager *m = data;
77527da0 191 dbus_bool_t b;
a185c5aa
LP
192
193 assert(i);
194 assert(property);
195 assert(m);
196
77527da0 197 b = manager_get_idle_hint(m, NULL) > 0;
a185c5aa
LP
198 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
199 return -ENOMEM;
200
201 return 0;
202}
203
204static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
205 Manager *m = data;
206 dual_timestamp t;
207 uint64_t u;
208
209 assert(i);
210 assert(property);
211 assert(m);
212
213 manager_get_idle_hint(m, &t);
214 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
215
216 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
217 return -ENOMEM;
218
219 return 0;
220}
221
98a28fef
LP
222static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
223 Session *session = NULL;
224 User *user = NULL;
225 const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service;
226 uint32_t uid, leader, audit_id = 0;
227 dbus_bool_t remote, kill_processes;
228 char **controllers = NULL, **reset_controllers = NULL;
229 SessionType t;
230 Seat *s;
231 DBusMessageIter iter;
232 int r;
07714753 233 char *id = NULL, *p;
4d6d6518 234 uint32_t vtnr = 0;
932e3ee7 235 int fifo_fd = -1;
98a28fef
LP
236 DBusMessage *reply = NULL;
237 bool b;
238
239 assert(m);
240 assert(message);
241 assert(_reply);
242
243 if (!dbus_message_iter_init(message, &iter) ||
244 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
245 return -EINVAL;
246
247 dbus_message_iter_get_basic(&iter, &uid);
248
249 if (!dbus_message_iter_next(&iter) ||
250 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
251 return -EINVAL;
252
253 dbus_message_iter_get_basic(&iter, &leader);
254
255 if (leader <= 0 ||
256 !dbus_message_iter_next(&iter) ||
257 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
258 return -EINVAL;
259
260 dbus_message_iter_get_basic(&iter, &service);
261
262 if (!dbus_message_iter_next(&iter) ||
263 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
264 return -EINVAL;
265
266 dbus_message_iter_get_basic(&iter, &type);
267 t = session_type_from_string(type);
268
269 if (t < 0 ||
270 !dbus_message_iter_next(&iter) ||
271 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
272 return -EINVAL;
273
274 dbus_message_iter_get_basic(&iter, &seat);
275
276 if (isempty(seat))
277 s = NULL;
278 else {
279 s = hashmap_get(m->seats, seat);
280 if (!s)
281 return -ENOENT;
282 }
283
4d6d6518
LP
284 if (!dbus_message_iter_next(&iter) ||
285 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
286 return -EINVAL;
287
288 dbus_message_iter_get_basic(&iter, &vtnr);
289
98a28fef
LP
290 if (!dbus_message_iter_next(&iter) ||
291 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
292 return -EINVAL;
293
294 dbus_message_iter_get_basic(&iter, &tty);
295
296 if (tty_is_vc(tty)) {
4d6d6518 297 int v;
98a28fef
LP
298
299 if (!s)
300 s = m->vtconsole;
301 else if (s != m->vtconsole)
302 return -EINVAL;
303
4d6d6518
LP
304 v = vtnr_from_tty(tty);
305
306 if (v <= 0)
307 return v < 0 ? v : -EINVAL;
98a28fef
LP
308
309 if (vtnr <= 0)
4d6d6518
LP
310 vtnr = (uint32_t) v;
311 else if (vtnr != (uint32_t) v)
312 return -EINVAL;
98a28fef 313
bf100920 314 } else if (!isempty(tty) && s && seat_is_vtconsole(s))
98a28fef
LP
315 return -EINVAL;
316
4d6d6518 317 if (s) {
addedec4 318 if (seat_can_multi_session(s)) {
4d6d6518
LP
319 if (vtnr <= 0 || vtnr > 63)
320 return -EINVAL;
321 } else {
322 if (vtnr > 0)
323 return -EINVAL;
324 }
325 }
326
98a28fef
LP
327 if (!dbus_message_iter_next(&iter) ||
328 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
329 return -EINVAL;
330
331 dbus_message_iter_get_basic(&iter, &display);
332
333 if (!dbus_message_iter_next(&iter) ||
334 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
335 return -EINVAL;
336
337 dbus_message_iter_get_basic(&iter, &remote);
338
339 if (!dbus_message_iter_next(&iter) ||
340 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
341 return -EINVAL;
342
343 dbus_message_iter_get_basic(&iter, &remote_user);
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, &remote_host);
350
351 if (!dbus_message_iter_next(&iter) ||
352 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
353 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
354 return -EINVAL;
355
356 r = bus_parse_strv_iter(&iter, &controllers);
357 if (r < 0)
358 return -EINVAL;
359
25d93491
LP
360 if (strv_contains(controllers, "systemd") ||
361 !dbus_message_iter_next(&iter) ||
98a28fef
LP
362 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
363 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
364 r = -EINVAL;
365 goto fail;
366 }
367
368 r = bus_parse_strv_iter(&iter, &reset_controllers);
369 if (r < 0)
370 goto fail;
371
25d93491
LP
372 if (strv_contains(reset_controllers, "systemd") ||
373 !dbus_message_iter_next(&iter) ||
98a28fef
LP
374 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
375 r = -EINVAL;
376 goto fail;
377 }
378
379 dbus_message_iter_get_basic(&iter, &kill_processes);
380
381 r = manager_add_user_by_uid(m, uid, &user);
382 if (r < 0)
383 goto fail;
384
385 audit_session_from_pid(leader, &audit_id);
386
07714753 387 if (audit_id > 0) {
98a28fef 388 asprintf(&id, "%lu", (unsigned long) audit_id);
98a28fef 389
07714753
LP
390 if (!id) {
391 r = -ENOMEM;
392 goto fail;
393 }
98a28fef 394
21c390cc
LP
395 session = hashmap_get(m->sessions, id);
396
397 if (session) {
8ea913b2 398 free(id);
21c390cc 399
932e3ee7
LP
400 fifo_fd = session_create_fifo(session);
401 if (fifo_fd < 0) {
402 r = fifo_fd;
403 goto fail;
404 }
405
21c390cc
LP
406 /* Session already exists, client is probably
407 * something like "su" which changes uid but
408 * is still the same audit session */
409
410 reply = dbus_message_new_method_return(message);
411 if (!reply) {
412 r = -ENOMEM;
413 goto fail;
414 }
415
21c390cc
LP
416 p = session_bus_path(session);
417 if (!p) {
418 r = -ENOMEM;
419 goto fail;
420 }
421
3887b5ab
LP
422 seat = session->seat ? session->seat->id : "";
423 vtnr = session->vtnr;
21c390cc
LP
424 b = dbus_message_append_args(
425 reply,
426 DBUS_TYPE_STRING, &session->id,
427 DBUS_TYPE_OBJECT_PATH, &p,
428 DBUS_TYPE_STRING, &session->user->runtime_path,
932e3ee7 429 DBUS_TYPE_UNIX_FD, &fifo_fd,
3887b5ab
LP
430 DBUS_TYPE_STRING, &seat,
431 DBUS_TYPE_UINT32, &vtnr,
21c390cc
LP
432 DBUS_TYPE_INVALID);
433 free(p);
434
435 if (!b) {
436 r = -ENOMEM;
437 goto fail;
438 }
439
932e3ee7 440 close_nointr_nofail(fifo_fd);
21c390cc
LP
441 *_reply = reply;
442
8ea913b2
LP
443 strv_free(controllers);
444 strv_free(reset_controllers);
445
21c390cc 446 return 0;
07714753
LP
447 }
448
449 } else {
450 do {
451 free(id);
452 asprintf(&id, "c%lu", ++m->session_counter);
453
454 if (!id) {
455 r = -ENOMEM;
456 goto fail;
457 }
458
459 } while (hashmap_get(m->sessions, id));
98a28fef
LP
460 }
461
462 r = manager_add_session(m, user, id, &session);
463 free(id);
464 if (r < 0)
465 goto fail;
466
467 session->leader = leader;
468 session->audit_id = audit_id;
469 session->type = t;
470 session->remote = remote;
471 session->controllers = controllers;
472 session->reset_controllers = reset_controllers;
473 session->kill_processes = kill_processes;
474 session->vtnr = vtnr;
475
476 controllers = reset_controllers = NULL;
477
478 if (!isempty(tty)) {
479 session->tty = strdup(tty);
480 if (!session->tty) {
481 r = -ENOMEM;
482 goto fail;
483 }
484 }
485
486 if (!isempty(display)) {
487 session->display = strdup(display);
488 if (!session->display) {
489 r = -ENOMEM;
490 goto fail;
491 }
492 }
493
494 if (!isempty(remote_user)) {
495 session->remote_user = strdup(remote_user);
496 if (!session->remote_user) {
497 r = -ENOMEM;
498 goto fail;
499 }
500 }
501
502 if (!isempty(remote_host)) {
503 session->remote_host = strdup(remote_host);
504 if (!session->remote_host) {
505 r = -ENOMEM;
506 goto fail;
507 }
508 }
509
510 if (!isempty(service)) {
511 session->service = strdup(service);
512 if (!session->service) {
513 r = -ENOMEM;
514 goto fail;
515 }
516 }
517
932e3ee7
LP
518 fifo_fd = session_create_fifo(session);
519 if (fifo_fd < 0) {
520 r = fifo_fd;
98a28fef
LP
521 goto fail;
522 }
523
98a28fef
LP
524 if (s) {
525 r = seat_attach_session(s, session);
526 if (r < 0)
527 goto fail;
528 }
529
530 r = session_start(session);
531 if (r < 0)
532 goto fail;
533
534 reply = dbus_message_new_method_return(message);
535 if (!reply) {
536 r = -ENOMEM;
537 goto fail;
538 }
539
540 p = session_bus_path(session);
541 if (!p) {
542 r = -ENOMEM;
543 goto fail;
544 }
545
bbc73283 546 seat = s ? s->id : "";
98a28fef
LP
547 b = dbus_message_append_args(
548 reply,
549 DBUS_TYPE_STRING, &session->id,
550 DBUS_TYPE_OBJECT_PATH, &p,
551 DBUS_TYPE_STRING, &session->user->runtime_path,
932e3ee7 552 DBUS_TYPE_UNIX_FD, &fifo_fd,
bbc73283
LP
553 DBUS_TYPE_STRING, &seat,
554 DBUS_TYPE_UINT32, &vtnr,
98a28fef
LP
555 DBUS_TYPE_INVALID);
556 free(p);
557
558 if (!b) {
559 r = -ENOMEM;
560 goto fail;
561 }
562
932e3ee7 563 close_nointr_nofail(fifo_fd);
98a28fef
LP
564 *_reply = reply;
565
566 return 0;
567
568fail:
569 strv_free(controllers);
570 strv_free(reset_controllers);
571
572 if (session)
573 session_add_to_gc_queue(session);
574
575 if (user)
576 user_add_to_gc_queue(user);
577
932e3ee7
LP
578 if (fifo_fd >= 0)
579 close_nointr_nofail(fifo_fd);
98a28fef
LP
580
581 if (reply)
582 dbus_message_unref(reply);
583
584 return r;
585}
586
2eb916cd 587static int trigger_device(Manager *m, struct udev_device *d) {
b668e064
LP
588 struct udev_enumerate *e;
589 struct udev_list_entry *first, *item;
590 int r;
591
592 assert(m);
593
594 e = udev_enumerate_new(m->udev);
595 if (!e) {
596 r = -ENOMEM;
597 goto finish;
598 }
599
2eb916cd
LP
600 if (d) {
601 if (udev_enumerate_add_match_parent(e, d) < 0) {
602 r = -EIO;
603 goto finish;
604 }
605 }
606
b668e064
LP
607 if (udev_enumerate_scan_devices(e) < 0) {
608 r = -EIO;
609 goto finish;
610 }
611
612 first = udev_enumerate_get_list_entry(e);
613 udev_list_entry_foreach(item, first) {
614 char *t;
615 const char *p;
616
617 p = udev_list_entry_get_name(item);
618
b668e064
LP
619 t = strappend(p, "/uevent");
620 if (!t) {
621 r = -ENOMEM;
622 goto finish;
623 }
624
625 write_one_line_file(t, "change");
626 free(t);
627 }
628
629 r = 0;
630
631finish:
632 if (e)
633 udev_enumerate_unref(e);
634
635 return r;
636}
637
47a26690
LP
638static int attach_device(Manager *m, const char *seat, const char *sysfs) {
639 struct udev_device *d;
640 char *rule = NULL, *file = NULL;
c28fa3d3 641 const char *id_for_seat;
47a26690
LP
642 int r;
643
644 assert(m);
645 assert(seat);
646 assert(sysfs);
647
648 d = udev_device_new_from_syspath(m->udev, sysfs);
649 if (!d)
650 return -ENODEV;
651
309c2a2c 652 if (!udev_device_has_tag(d, "seat")) {
47a26690
LP
653 r = -ENODEV;
654 goto finish;
655 }
656
c28fa3d3
LP
657 id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
658 if (!id_for_seat) {
47a26690
LP
659 r = -ENODEV;
660 goto finish;
661 }
662
c28fa3d3 663 if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
47a26690
LP
664 r = -ENOMEM;
665 goto finish;
666 }
667
c28fa3d3 668 if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
47a26690
LP
669 r = -ENOMEM;
670 goto finish;
671 }
672
c28fa3d3 673 mkdir_p("/etc/udev/rules.d", 0755);
b5ef5549 674 r = write_one_line_file_atomic(file, rule);
a0a0c7f1
LP
675 if (r < 0)
676 goto finish;
677
2eb916cd 678 r = trigger_device(m, d);
47a26690
LP
679
680finish:
681 free(rule);
682 free(file);
683
684 if (d)
685 udev_device_unref(d);
686
687 return r;
688}
689
b668e064
LP
690static int flush_devices(Manager *m) {
691 DIR *d;
692
693 assert(m);
694
695 d = opendir("/etc/udev/rules.d");
696 if (!d) {
697 if (errno != ENOENT)
698 log_warning("Failed to open /etc/udev/rules.d: %m");
699 } else {
700 struct dirent *de;
701
702 while ((de = readdir(d))) {
703
704 if (!dirent_is_file(de))
705 continue;
706
707 if (!startswith(de->d_name, "72-seat-"))
708 continue;
709
710 if (!endswith(de->d_name, ".rules"))
711 continue;
712
713 if (unlinkat(dirfd(d), de->d_name, 0) < 0)
714 log_warning("Failed to unlink %s: %m", de->d_name);
715 }
716
717 closedir(d);
718 }
719
720 return trigger_device(m, NULL);
721}
722
89f13440
LP
723static int have_multiple_sessions(
724 DBusConnection *connection,
725 Manager *m,
726 DBusMessage *message,
727 DBusError *error) {
728
729 Session *s;
730
731 assert(m);
732
733 if (hashmap_size(m->sessions) > 1)
734 return true;
735
736 /* Hmm, there's only one session, but let's make sure it
737 * actually belongs to the user who is asking. If not, better
738 * be safe than sorry. */
739
740 s = hashmap_first(m->sessions);
741 if (s) {
742 unsigned long ul;
743
744 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
745 if (ul == (unsigned long) -1)
746 return -EIO;
747
748 return s->user->uid != ul;
749 }
750
751 return false;
752}
753
d200735e
MS
754static const BusProperty bus_login_manager_properties[] = {
755 { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true },
756 { "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
757 { "ResetControllers", bus_property_append_strv, "as", offsetof(Manager, reset_controllers), true },
758 { "NAutoVTs", bus_property_append_unsigned, "u", offsetof(Manager, n_autovts) },
759 { "KillOnlyUsers", bus_property_append_strv, "as", offsetof(Manager, kill_only_users), true },
760 { "KillExcludeUsers", bus_property_append_strv, "as", offsetof(Manager, kill_exclude_users), true },
761 { "KillUserProcesses", bus_property_append_bool, "b", offsetof(Manager, kill_user_processes) },
762 { "IdleHint", bus_manager_append_idle_hint, "b", 0 },
763 { "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 },
764 { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 },
765 { NULL, }
766};
767
3f49d45a
LP
768static DBusHandlerResult manager_message_handler(
769 DBusConnection *connection,
770 DBusMessage *message,
771 void *userdata) {
772
773 Manager *m = userdata;
774
3f49d45a
LP
775 DBusError error;
776 DBusMessage *reply = NULL;
bef422ae 777 int r;
3f49d45a
LP
778
779 assert(connection);
780 assert(message);
781 assert(m);
782
783 dbus_error_init(&error);
784
bef422ae
LP
785 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
786 const char *name;
787 char *p;
788 Session *session;
789 bool b;
790
791 if (!dbus_message_get_args(
792 message,
793 &error,
794 DBUS_TYPE_STRING, &name,
795 DBUS_TYPE_INVALID))
796 return bus_send_error_reply(connection, message, &error, -EINVAL);
797
798 session = hashmap_get(m->sessions, name);
799 if (!session)
800 return bus_send_error_reply(connection, message, &error, -ENOENT);
801
802 reply = dbus_message_new_method_return(message);
803 if (!reply)
804 goto oom;
805
806 p = session_bus_path(session);
c4aa65e7
LP
807 if (!p)
808 goto oom;
809
810 b = dbus_message_append_args(
811 reply,
812 DBUS_TYPE_OBJECT_PATH, &p,
813 DBUS_TYPE_INVALID);
814 free(p);
815
816 if (!b)
817 goto oom;
818
819 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSessionByPID")) {
820 uint32_t pid;
821 char *p;
822 Session *session;
823 bool b;
824
825 if (!dbus_message_get_args(
826 message,
827 &error,
828 DBUS_TYPE_UINT32, &pid,
829 DBUS_TYPE_INVALID))
830 return bus_send_error_reply(connection, message, &error, -EINVAL);
831
832 r = manager_get_session_by_pid(m, pid, &session);
833 if (r <= 0)
834 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
835
836 reply = dbus_message_new_method_return(message);
837 if (!reply)
838 goto oom;
839
840 p = session_bus_path(session);
bef422ae
LP
841 if (!p)
842 goto oom;
843
844 b = dbus_message_append_args(
845 reply,
846 DBUS_TYPE_OBJECT_PATH, &p,
847 DBUS_TYPE_INVALID);
848 free(p);
849
850 if (!b)
851 goto oom;
852
853 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
854 uint32_t uid;
855 char *p;
856 User *user;
857 bool b;
858
859 if (!dbus_message_get_args(
860 message,
861 &error,
862 DBUS_TYPE_UINT32, &uid,
863 DBUS_TYPE_INVALID))
864 return bus_send_error_reply(connection, message, &error, -EINVAL);
865
866 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
867 if (!user)
868 return bus_send_error_reply(connection, message, &error, -ENOENT);
869
870 reply = dbus_message_new_method_return(message);
871 if (!reply)
872 goto oom;
873
874 p = user_bus_path(user);
875 if (!p)
876 goto oom;
877
878 b = dbus_message_append_args(
879 reply,
880 DBUS_TYPE_OBJECT_PATH, &p,
881 DBUS_TYPE_INVALID);
882 free(p);
883
884 if (!b)
885 goto oom;
886
887 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
888 const char *name;
889 char *p;
890 Seat *seat;
891 bool b;
892
893 if (!dbus_message_get_args(
894 message,
895 &error,
896 DBUS_TYPE_STRING, &name,
897 DBUS_TYPE_INVALID))
898 return bus_send_error_reply(connection, message, &error, -EINVAL);
899
900 seat = hashmap_get(m->seats, name);
901 if (!seat)
902 return bus_send_error_reply(connection, message, &error, -ENOENT);
903
904 reply = dbus_message_new_method_return(message);
905 if (!reply)
906 goto oom;
907
908 p = seat_bus_path(seat);
909 if (!p)
910 goto oom;
911
912 b = dbus_message_append_args(
913 reply,
914 DBUS_TYPE_OBJECT_PATH, &p,
915 DBUS_TYPE_INVALID);
916 free(p);
917
918 if (!b)
919 goto oom;
920
e1c9c2d5
LP
921 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
922 char *p;
923 Session *session;
924 Iterator i;
925 DBusMessageIter iter, sub;
926 const char *empty = "";
927
928 reply = dbus_message_new_method_return(message);
929 if (!reply)
930 goto oom;
931
932 dbus_message_iter_init_append(reply, &iter);
933
dec15e92 934 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
e1c9c2d5
LP
935 goto oom;
936
937 HASHMAP_FOREACH(session, m->sessions, i) {
938 DBusMessageIter sub2;
939 uint32_t uid;
940
941 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
942 goto oom;
943
944 uid = session->user->uid;
945
946 p = session_bus_path(session);
947 if (!p)
948 goto oom;
949
950 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
951 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
952 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
953 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
954 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
955 free(p);
956 goto oom;
957 }
958
959 free(p);
960
961 if (!dbus_message_iter_close_container(&sub, &sub2))
962 goto oom;
963 }
964
965 if (!dbus_message_iter_close_container(&iter, &sub))
966 goto oom;
967
968 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
969 char *p;
970 User *user;
971 Iterator i;
972 DBusMessageIter iter, sub;
973
974 reply = dbus_message_new_method_return(message);
975 if (!reply)
976 goto oom;
977
978 dbus_message_iter_init_append(reply, &iter);
979
dec15e92 980 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
e1c9c2d5
LP
981 goto oom;
982
983 HASHMAP_FOREACH(user, m->users, i) {
984 DBusMessageIter sub2;
985 uint32_t uid;
986
987 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
988 goto oom;
989
990 uid = user->uid;
991
992 p = user_bus_path(user);
993 if (!p)
994 goto oom;
995
996 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
997 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
998 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
999 free(p);
1000 goto oom;
1001 }
1002
1003 free(p);
1004
1005 if (!dbus_message_iter_close_container(&sub, &sub2))
1006 goto oom;
1007 }
1008
1009 if (!dbus_message_iter_close_container(&iter, &sub))
1010 goto oom;
1011
1012 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
1013 char *p;
1014 Seat *seat;
1015 Iterator i;
1016 DBusMessageIter iter, sub;
1017
1018 reply = dbus_message_new_method_return(message);
1019 if (!reply)
1020 goto oom;
1021
1022 dbus_message_iter_init_append(reply, &iter);
1023
dec15e92 1024 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
e1c9c2d5
LP
1025 goto oom;
1026
1027 HASHMAP_FOREACH(seat, m->seats, i) {
1028 DBusMessageIter sub2;
1029
1030 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1031 goto oom;
1032
1033 p = seat_bus_path(seat);
1034 if (!p)
1035 goto oom;
1036
1037 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
1038 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
1039 free(p);
1040 goto oom;
1041 }
1042
1043 free(p);
1044
1045 if (!dbus_message_iter_close_container(&sub, &sub2))
1046 goto oom;
1047 }
1048
1049 if (!dbus_message_iter_close_container(&iter, &sub))
1050 goto oom;
1051
98a28fef
LP
1052 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
1053
1054 r = bus_manager_create_session(m, message, &reply);
688c56ff
LP
1055
1056 /* Don't delay the work on OOM here, since it might be
1057 * triggered by a low RLIMIT_NOFILE here (since we
1058 * send a dupped fd to the client), and we'd rather
1059 * see this fail quickly then be retried later */
98a28fef
LP
1060
1061 if (r < 0)
1062 return bus_send_error_reply(connection, message, &error, r);
1063
bef422ae
LP
1064 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
1065 const char *name;
1066 Session *session;
1067
1068 if (!dbus_message_get_args(
1069 message,
1070 &error,
1071 DBUS_TYPE_STRING, &name,
1072 DBUS_TYPE_INVALID))
1073 return bus_send_error_reply(connection, message, &error, -EINVAL);
1074
1075 session = hashmap_get(m->sessions, name);
1076 if (!session)
1077 return bus_send_error_reply(connection, message, &error, -ENOENT);
1078
1079 r = session_activate(session);
1080 if (r < 0)
1081 return bus_send_error_reply(connection, message, NULL, r);
1082
1083 reply = dbus_message_new_method_return(message);
1084 if (!reply)
1085 goto oom;
1086
84c3361e
LP
1087 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSessionOnSeat")) {
1088 const char *session_name, *seat_name;
1089 Session *session;
1090 Seat *seat;
1091
1092 /* Same as ActivateSession() but refuses to work if
1093 * the seat doesn't match */
1094
1095 if (!dbus_message_get_args(
1096 message,
1097 &error,
1098 DBUS_TYPE_STRING, &session_name,
1099 DBUS_TYPE_STRING, &seat_name,
1100 DBUS_TYPE_INVALID))
1101 return bus_send_error_reply(connection, message, &error, -EINVAL);
1102
1103 session = hashmap_get(m->sessions, session_name);
1104 if (!session)
1105 return bus_send_error_reply(connection, message, &error, -ENOENT);
1106
1107 seat = hashmap_get(m->seats, seat_name);
1108 if (!seat)
1109 return bus_send_error_reply(connection, message, &error, -ENOENT);
1110
1111 if (session->seat != seat)
1112 return bus_send_error_reply(connection, message, &error, -EINVAL);
1113
1114 r = session_activate(session);
1115 if (r < 0)
1116 return bus_send_error_reply(connection, message, NULL, r);
1117
1118 reply = dbus_message_new_method_return(message);
1119 if (!reply)
1120 goto oom;
1121
88e3dc90
LP
1122 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") ||
1123 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) {
1124 const char *name;
1125 Session *session;
1126
1127 if (!dbus_message_get_args(
1128 message,
1129 &error,
1130 DBUS_TYPE_STRING, &name,
1131 DBUS_TYPE_INVALID))
1132 return bus_send_error_reply(connection, message, &error, -EINVAL);
1133
1134 session = hashmap_get(m->sessions, name);
1135 if (!session)
1136 return bus_send_error_reply(connection, message, &error, -ENOENT);
1137
1138 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
1139 goto oom;
1140
1141 reply = dbus_message_new_method_return(message);
1142 if (!reply)
1143 goto oom;
1144
de07ab16
LP
1145 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillSession")) {
1146 const char *swho;
1147 int32_t signo;
1148 KillWho who;
1149 const char *name;
1150 Session *session;
1151
1152 if (!dbus_message_get_args(
1153 message,
1154 &error,
1155 DBUS_TYPE_STRING, &name,
1156 DBUS_TYPE_STRING, &swho,
1157 DBUS_TYPE_INT32, &signo,
1158 DBUS_TYPE_INVALID))
1159 return bus_send_error_reply(connection, message, &error, -EINVAL);
1160
1161 if (isempty(swho))
1162 who = KILL_ALL;
1163 else {
1164 who = kill_who_from_string(swho);
1165 if (who < 0)
1166 return bus_send_error_reply(connection, message, &error, -EINVAL);
1167 }
1168
1169 if (signo <= 0 || signo >= _NSIG)
1170 return bus_send_error_reply(connection, message, &error, -EINVAL);
1171
1172 session = hashmap_get(m->sessions, name);
1173 if (!session)
1174 return bus_send_error_reply(connection, message, &error, -ENOENT);
1175
1176 r = session_kill(session, who, signo);
1177 if (r < 0)
1178 return bus_send_error_reply(connection, message, NULL, r);
1179
1180 reply = dbus_message_new_method_return(message);
1181 if (!reply)
1182 goto oom;
1183
1184 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillUser")) {
1185 uint32_t uid;
1186 User *user;
1187 int32_t signo;
1188
1189 if (!dbus_message_get_args(
1190 message,
1191 &error,
1192 DBUS_TYPE_UINT32, &uid,
1193 DBUS_TYPE_INT32, &signo,
1194 DBUS_TYPE_INVALID))
1195 return bus_send_error_reply(connection, message, &error, -EINVAL);
1196
1197 if (signo <= 0 || signo >= _NSIG)
1198 return bus_send_error_reply(connection, message, &error, -EINVAL);
1199
1200 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1201 if (!user)
1202 return bus_send_error_reply(connection, message, &error, -ENOENT);
1203
1204 r = user_kill(user, signo);
1205 if (r < 0)
1206 return bus_send_error_reply(connection, message, NULL, r);
1207
1208 reply = dbus_message_new_method_return(message);
1209 if (!reply)
1210 goto oom;
1211
bef422ae
LP
1212 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
1213 const char *name;
1214 Session *session;
1215
1216 if (!dbus_message_get_args(
1217 message,
1218 &error,
1219 DBUS_TYPE_STRING, &name,
1220 DBUS_TYPE_INVALID))
1221 return bus_send_error_reply(connection, message, &error, -EINVAL);
1222
1223 session = hashmap_get(m->sessions, name);
1224 if (!session)
1225 return bus_send_error_reply(connection, message, &error, -ENOENT);
1226
1227 r = session_stop(session);
1228 if (r < 0)
1229 return bus_send_error_reply(connection, message, NULL, r);
1230
1231 reply = dbus_message_new_method_return(message);
1232 if (!reply)
1233 goto oom;
1234
1235 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1236 uint32_t uid;
1237 User *user;
1238
1239 if (!dbus_message_get_args(
1240 message,
1241 &error,
1242 DBUS_TYPE_UINT32, &uid,
1243 DBUS_TYPE_INVALID))
1244 return bus_send_error_reply(connection, message, &error, -EINVAL);
1245
1246 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1247 if (!user)
1248 return bus_send_error_reply(connection, message, &error, -ENOENT);
1249
1250 r = user_stop(user);
1251 if (r < 0)
1252 return bus_send_error_reply(connection, message, NULL, r);
1253
1254 reply = dbus_message_new_method_return(message);
1255 if (!reply)
1256 goto oom;
1257
1258 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1259 const char *name;
1260 Seat *seat;
1261
1262 if (!dbus_message_get_args(
1263 message,
1264 &error,
1265 DBUS_TYPE_STRING, &name,
1266 DBUS_TYPE_INVALID))
1267 return bus_send_error_reply(connection, message, &error, -EINVAL);
1268
1269 seat = hashmap_get(m->seats, name);
1270 if (!seat)
1271 return bus_send_error_reply(connection, message, &error, -ENOENT);
1272
1273 r = seat_stop_sessions(seat);
1274 if (r < 0)
1275 return bus_send_error_reply(connection, message, NULL, r);
1276
1277 reply = dbus_message_new_method_return(message);
1278 if (!reply)
1279 goto oom;
1280
7f7bb946
LP
1281 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1282 uint32_t uid;
1283 struct passwd *pw;
1284 dbus_bool_t b, interactive;
1285 char *path;
1286
1287 if (!dbus_message_get_args(
1288 message,
1289 &error,
1290 DBUS_TYPE_UINT32, &uid,
1291 DBUS_TYPE_BOOLEAN, &b,
1292 DBUS_TYPE_BOOLEAN, &interactive,
1293 DBUS_TYPE_INVALID))
1294 return bus_send_error_reply(connection, message, &error, -EINVAL);
1295
1296 errno = 0;
1297 pw = getpwuid(uid);
1298 if (!pw)
1299 return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
1300
89f13440 1301 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, NULL, &error);
7f7bb946
LP
1302 if (r < 0)
1303 return bus_send_error_reply(connection, message, &error, r);
1304
02b16a19
LP
1305 mkdir_p("/var/lib/systemd", 0755);
1306
7f7bb946
LP
1307 r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0);
1308 if (r < 0)
1309 return bus_send_error_reply(connection, message, &error, r);
1310
1311 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
1312 if (!path)
1313 goto oom;
1314
1315 if (b) {
38f3fc7d
LP
1316 User *u;
1317
7f7bb946
LP
1318 r = touch(path);
1319 free(path);
1320
1321 if (r < 0)
1322 return bus_send_error_reply(connection, message, &error, r);
38f3fc7d
LP
1323
1324 if (manager_add_user_by_uid(m, uid, &u) >= 0)
1325 user_start(u);
1326
7f7bb946 1327 } else {
38f3fc7d
LP
1328 User *u;
1329
7f7bb946
LP
1330 r = unlink(path);
1331 free(path);
1332
1333 if (r < 0 && errno != ENOENT)
1334 return bus_send_error_reply(connection, message, &error, -errno);
38f3fc7d
LP
1335
1336 u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1337 if (u)
1338 user_add_to_gc_queue(u);
7f7bb946
LP
1339 }
1340
1341 reply = dbus_message_new_method_return(message);
1342 if (!reply)
1343 goto oom;
1344
47a26690
LP
1345 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
1346 const char *sysfs, *seat;
1347 dbus_bool_t interactive;
1348
1349 if (!dbus_message_get_args(
1350 message,
1351 &error,
1352 DBUS_TYPE_STRING, &seat,
1353 DBUS_TYPE_STRING, &sysfs,
1354 DBUS_TYPE_BOOLEAN, &interactive,
1355 DBUS_TYPE_INVALID))
1356 return bus_send_error_reply(connection, message, &error, -EINVAL);
1357
1358 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
1359 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1360
89f13440 1361 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, NULL, &error);
47a26690
LP
1362 if (r < 0)
1363 return bus_send_error_reply(connection, message, &error, r);
1364
1365 r = attach_device(m, seat, sysfs);
1366 if (r < 0)
1367 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1368
1369 reply = dbus_message_new_method_return(message);
1370 if (!reply)
1371 goto oom;
7f7bb946 1372
b668e064
LP
1373
1374 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
1375 dbus_bool_t interactive;
1376
1377 if (!dbus_message_get_args(
1378 message,
1379 &error,
1380 DBUS_TYPE_BOOLEAN, &interactive,
1381 DBUS_TYPE_INVALID))
1382 return bus_send_error_reply(connection, message, &error, -EINVAL);
1383
89f13440 1384 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, NULL, &error);
b668e064
LP
1385 if (r < 0)
1386 return bus_send_error_reply(connection, message, &error, r);
1387
1388 r = flush_devices(m);
1389 if (r < 0)
1390 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1391
1392 reply = dbus_message_new_method_return(message);
1393 if (!reply)
1394 goto oom;
1395
55af3897
LP
1396 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
1397 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
1398 dbus_bool_t interactive;
1399 bool multiple_sessions;
1400 DBusMessage *forward, *freply;
1401 const char *name;
1402 const char *mode = "replace";
1403 const char *action;
1404
1405 if (!dbus_message_get_args(
1406 message,
1407 &error,
1408 DBUS_TYPE_BOOLEAN, &interactive,
1409 DBUS_TYPE_INVALID))
1410 return bus_send_error_reply(connection, message, &error, -EINVAL);
1411
89f13440
LP
1412 r = have_multiple_sessions(connection, m, message, &error);
1413 if (r < 0)
1414 return bus_send_error_reply(connection, message, &error, r);
55af3897 1415
89f13440 1416 multiple_sessions = r > 0;
55af3897
LP
1417
1418 if (streq(dbus_message_get_member(message), "PowerOff")) {
1419 if (multiple_sessions)
1420 action = "org.freedesktop.login1.power-off-multiple-sessions";
1421 else
1422 action = "org.freedesktop.login1.power-off";
1423
1424 name = SPECIAL_POWEROFF_TARGET;
1425 } else {
1426 if (multiple_sessions)
1427 action = "org.freedesktop.login1.reboot-multiple-sessions";
1428 else
1429 action = "org.freedesktop.login1.reboot";
1430
1431 name = SPECIAL_REBOOT_TARGET;
1432 }
1433
89f13440 1434 r = verify_polkit(connection, message, action, interactive, NULL, &error);
55af3897
LP
1435 if (r < 0)
1436 return bus_send_error_reply(connection, message, &error, r);
1437
1438 forward = dbus_message_new_method_call(
1439 "org.freedesktop.systemd1",
1440 "/org/freedesktop/systemd1",
1441 "org.freedesktop.systemd1.Manager",
1442 "StartUnit");
1443 if (!forward)
1444 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1445
1446 if (!dbus_message_append_args(forward,
1447 DBUS_TYPE_STRING, &name,
1448 DBUS_TYPE_STRING, &mode,
1449 DBUS_TYPE_INVALID)) {
1450 dbus_message_unref(forward);
1451 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
1452 }
1453
1454 freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error);
1455 dbus_message_unref(forward);
1456
1457 if (!freply)
1458 return bus_send_error_reply(connection, message, &error, -EIO);
1459
1460 dbus_message_unref(freply);
1461
1462 reply = dbus_message_new_method_return(message);
1463 if (!reply)
1464 goto oom;
1465
89f13440
LP
1466 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff") ||
1467 dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
1468
1469 bool multiple_sessions, challenge, b;
1470 const char *t, *action;
1471
1472 r = have_multiple_sessions(connection, m, message, &error);
1473 if (r < 0)
1474 return bus_send_error_reply(connection, message, &error, r);
1475
1476 multiple_sessions = r > 0;
1477
1478 if (streq(dbus_message_get_member(message), "CanPowerOff")) {
1479 if (multiple_sessions)
1480 action = "org.freedesktop.login1.power-off-multiple-sessions";
1481 else
1482 action = "org.freedesktop.login1.power-off";
1483
1484 } else {
1485 if (multiple_sessions)
1486 action = "org.freedesktop.login1.reboot-multiple-sessions";
1487 else
1488 action = "org.freedesktop.login1.reboot";
1489 }
1490
1491 r = verify_polkit(connection, message, action, false, &challenge, &error);
1492 if (r < 0)
1493 return bus_send_error_reply(connection, message, &error, r);
1494
1495 reply = dbus_message_new_method_return(message);
1496 if (!reply)
1497 goto oom;
1498
1499 t = r > 0 ? "yes" :
1500 challenge ? "challenge" :
1501 "no";
1502
1503 b = dbus_message_append_args(
1504 reply,
1505 DBUS_TYPE_STRING, &t,
1506 DBUS_TYPE_INVALID);
1507 if (!b)
1508 goto oom;
1509
bef422ae 1510 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
3f49d45a
LP
1511 char *introspection = NULL;
1512 FILE *f;
1513 Iterator i;
1514 Session *session;
1515 Seat *seat;
1516 User *user;
1517 size_t size;
1518 char *p;
1519
1520 if (!(reply = dbus_message_new_method_return(message)))
1521 goto oom;
1522
1523 /* We roll our own introspection code here, instead of
1524 * relying on bus_default_message_handler() because we
1525 * need to generate our introspection string
1526 * dynamically. */
1527
1528 if (!(f = open_memstream(&introspection, &size)))
1529 goto oom;
1530
1531 fputs(INTROSPECTION_BEGIN, f);
1532
1533 HASHMAP_FOREACH(seat, m->seats, i) {
1534 p = bus_path_escape(seat->id);
1535
1536 if (p) {
1537 fprintf(f, "<node name=\"seat/%s\"/>", p);
1538 free(p);
1539 }
1540 }
1541
1542 HASHMAP_FOREACH(user, m->users, i)
1543 fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
1544
1545 HASHMAP_FOREACH(session, m->sessions, i) {
1546 p = bus_path_escape(session->id);
1547
1548 if (p) {
1549 fprintf(f, "<node name=\"session/%s\"/>", p);
1550 free(p);
1551 }
1552 }
1553
1554 fputs(INTROSPECTION_END, f);
1555
1556 if (ferror(f)) {
1557 fclose(f);
1558 free(introspection);
1559 goto oom;
1560 }
1561
1562 fclose(f);
1563
1564 if (!introspection)
1565 goto oom;
1566
1567 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1568 free(introspection);
1569 goto oom;
1570 }
1571
1572 free(introspection);
d200735e
MS
1573 } else {
1574 const BusBoundProperties bps[] = {
1575 { "org.freedesktop.login1.Manager", bus_login_manager_properties, m },
1576 { NULL, }
1577 };
1578 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1579 }
3f49d45a
LP
1580
1581 if (reply) {
1582 if (!dbus_connection_send(connection, reply, NULL))
1583 goto oom;
1584
1585 dbus_message_unref(reply);
1586 }
1587
1588 return DBUS_HANDLER_RESULT_HANDLED;
1589
1590oom:
1591 if (reply)
1592 dbus_message_unref(reply);
1593
1594 dbus_error_free(&error);
1595
1596 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1597}
1598
1599const DBusObjectPathVTable bus_manager_vtable = {
1600 .message_function = manager_message_handler
1601};
9418f147 1602
1713813d
LP
1603DBusHandlerResult bus_message_filter(
1604 DBusConnection *connection,
1605 DBusMessage *message,
1606 void *userdata) {
1607
1608 Manager *m = userdata;
1609 DBusError error;
1610
1611 assert(m);
1612 assert(connection);
1613 assert(message);
1614
1615 dbus_error_init(&error);
1616
1617 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
1618 const char *cgroup;
1619
1620 if (!dbus_message_get_args(message, &error,
1621 DBUS_TYPE_STRING, &cgroup,
1622 DBUS_TYPE_INVALID))
1623 log_error("Failed to parse Released message: %s", bus_error_message(&error));
1624 else
1625 manager_cgroup_notify_empty(m, cgroup);
1626 }
1627
1628 dbus_error_free(&error);
1629
1630 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1631}
1632
9418f147
LP
1633int manager_send_changed(Manager *manager, const char *properties) {
1634 DBusMessage *m;
1635 int r = -ENOMEM;
1636
1637 assert(manager);
1638
1639 m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
1640 if (!m)
1641 goto finish;
1642
1643 if (!dbus_connection_send(manager->bus, m, NULL))
1644 goto finish;
1645
1646 r = 0;
1647
1648finish:
1649 if (m)
1650 dbus_message_unref(m);
1651
1652 return r;
1653}