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