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