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