]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-session-dbus.c
sd-bus: introduce sd_bus_slot objects encapsulating callbacks or vtables attached...
[thirdparty/systemd.git] / src / login / logind-session-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/capability.h>
25
26 #include "util.h"
27 #include "strv.h"
28 #include "bus-util.h"
29 #include "bus-errors.h"
30 #include "bus-label.h"
31
32 #include "logind.h"
33 #include "logind-session.h"
34 #include "logind-session-device.h"
35
36 static int property_get_user(
37 sd_bus *bus,
38 const char *path,
39 const char *interface,
40 const char *property,
41 sd_bus_message *reply,
42 void *userdata,
43 sd_bus_error *error) {
44
45 _cleanup_free_ char *p = NULL;
46 Session *s = userdata;
47
48 assert(bus);
49 assert(reply);
50 assert(s);
51
52 p = user_bus_path(s->user);
53 if (!p)
54 return -ENOMEM;
55
56 return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p);
57 }
58
59 static int property_get_name(
60 sd_bus *bus,
61 const char *path,
62 const char *interface,
63 const char *property,
64 sd_bus_message *reply,
65 void *userdata,
66 sd_bus_error *error) {
67
68 Session *s = userdata;
69
70 assert(bus);
71 assert(reply);
72 assert(s);
73
74 return sd_bus_message_append(reply, "s", s->user->name);
75 }
76
77 static int property_get_seat(
78 sd_bus *bus,
79 const char *path,
80 const char *interface,
81 const char *property,
82 sd_bus_message *reply,
83 void *userdata,
84 sd_bus_error *error) {
85
86 _cleanup_free_ char *p = NULL;
87 Session *s = userdata;
88
89 assert(bus);
90 assert(reply);
91 assert(s);
92
93 p = s->seat ? seat_bus_path(s->seat) : strdup("/");
94 if (!p)
95 return -ENOMEM;
96
97 return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p);
98 }
99
100 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType);
101 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass);
102
103 static int property_get_active(
104 sd_bus *bus,
105 const char *path,
106 const char *interface,
107 const char *property,
108 sd_bus_message *reply,
109 void *userdata,
110 sd_bus_error *error) {
111
112 Session *s = userdata;
113
114 assert(bus);
115 assert(reply);
116 assert(s);
117
118 return sd_bus_message_append(reply, "b", session_is_active(s));
119 }
120
121 static int property_get_state(
122 sd_bus *bus,
123 const char *path,
124 const char *interface,
125 const char *property,
126 sd_bus_message *reply,
127 void *userdata,
128 sd_bus_error *error) {
129
130 Session *s = userdata;
131
132 assert(bus);
133 assert(reply);
134 assert(s);
135
136 return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s)));
137 }
138
139 static int property_get_idle_hint(
140 sd_bus *bus,
141 const char *path,
142 const char *interface,
143 const char *property,
144 sd_bus_message *reply,
145 void *userdata,
146 sd_bus_error *error) {
147
148 Session *s = userdata;
149
150 assert(bus);
151 assert(reply);
152 assert(s);
153
154 return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0);
155 }
156
157 static int property_get_idle_since_hint(
158 sd_bus *bus,
159 const char *path,
160 const char *interface,
161 const char *property,
162 sd_bus_message *reply,
163 void *userdata,
164 sd_bus_error *error) {
165
166 Session *s = userdata;
167 dual_timestamp t;
168 uint64_t u;
169 int r;
170
171 assert(bus);
172 assert(reply);
173 assert(s);
174
175 r = session_get_idle_hint(s, &t);
176 if (r < 0)
177 return r;
178
179 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
180
181 return sd_bus_message_append(reply, "t", u);
182 }
183
184 static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
185 Session *s = userdata;
186 int r;
187
188 assert(bus);
189 assert(message);
190 assert(s);
191
192 r = session_stop(s, true);
193 if (r < 0)
194 return r;
195
196 return sd_bus_reply_method_return(message, NULL);
197 }
198
199 static int method_activate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
200 Session *s = userdata;
201 int r;
202
203 assert(bus);
204 assert(message);
205 assert(s);
206
207 r = session_activate(s);
208 if (r < 0)
209 return r;
210
211 return sd_bus_reply_method_return(message, NULL);
212 }
213
214 static int method_lock(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
215 Session *s = userdata;
216 int r;
217
218 assert(bus);
219 assert(message);
220 assert(s);
221
222 r = session_send_lock(s, streq(sd_bus_message_get_member(message), "Lock"));
223 if (r < 0)
224 return r;
225
226 return sd_bus_reply_method_return(message, NULL);
227 }
228
229 static int method_set_idle_hint(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
230 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
231 Session *s = userdata;
232 uid_t uid;
233 int r, b;
234
235 assert(bus);
236 assert(message);
237 assert(s);
238
239 r = sd_bus_message_read(message, "b", &b);
240 if (r < 0)
241 return r;
242
243 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_UID, &creds);
244 if (r < 0)
245 return r;
246
247 r = sd_bus_creds_get_uid(creds, &uid);
248 if (r < 0)
249 return r;
250
251 if (uid != 0 && uid != s->user->uid)
252 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session my set idle hint");
253
254 session_set_idle_hint(s, b);
255
256 return sd_bus_reply_method_return(message, NULL);
257 }
258
259 static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
260 Session *s = userdata;
261 const char *swho;
262 int32_t signo;
263 KillWho who;
264 int r;
265
266 assert(bus);
267 assert(message);
268 assert(s);
269
270 r = sd_bus_message_read(message, "si", &swho, &signo);
271 if (r < 0)
272 return r;
273
274 if (isempty(swho))
275 who = KILL_ALL;
276 else {
277 who = kill_who_from_string(swho);
278 if (who < 0)
279 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
280 }
281
282 if (signo <= 0 || signo >= _NSIG)
283 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
284
285 r = session_kill(s, who, signo);
286 if (r < 0)
287 return r;
288
289 return sd_bus_reply_method_return(message, NULL);
290 }
291
292 static int method_take_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
293 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
294 Session *s = userdata;
295 int r, force;
296 uid_t uid;
297
298 assert(bus);
299 assert(message);
300 assert(s);
301
302 r = sd_bus_message_read(message, "b", &force);
303 if (r < 0)
304 return r;
305
306 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_UID, &creds);
307 if (r < 0)
308 return r;
309
310 r = sd_bus_creds_get_uid(creds, &uid);
311 if (r < 0)
312 return r;
313
314 if (uid != 0 && (force || uid != s->user->uid))
315 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
316
317 r = session_set_controller(s, sd_bus_message_get_sender(message), force);
318 if (r < 0)
319 return r;
320
321 return sd_bus_reply_method_return(message, NULL);
322 }
323
324 static int method_release_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
325 Session *s = userdata;
326
327 assert(bus);
328 assert(message);
329 assert(s);
330
331 if (!session_is_controller(s, sd_bus_message_get_sender(message)))
332 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
333
334 session_drop_controller(s);
335
336 return sd_bus_reply_method_return(message, NULL);
337 }
338
339 static int method_take_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
340 Session *s = userdata;
341 uint32_t major, minor;
342 SessionDevice *sd;
343 dev_t dev;
344 int r;
345
346 assert(bus);
347 assert(message);
348 assert(s);
349
350 r = sd_bus_message_read(message, "uu", &major, &minor);
351 if (r < 0)
352 return r;
353
354 if (!session_is_controller(s, sd_bus_message_get_sender(message)))
355 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
356
357 dev = makedev(major, minor);
358 sd = hashmap_get(s->devices, &dev);
359 if (sd)
360 /* We don't allow retrieving a device multiple times.
361 * The related ReleaseDevice call is not ref-counted.
362 * The caller should use dup() if it requires more
363 * than one fd (it would be functionally
364 * equivalent). */
365 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
366
367 r = session_device_new(s, dev, &sd);
368 if (r < 0)
369 return r;
370
371 r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active);
372 if (r < 0)
373 session_device_free(sd);
374
375 return r;
376 }
377
378 static int method_release_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
379 Session *s = userdata;
380 uint32_t major, minor;
381 SessionDevice *sd;
382 dev_t dev;
383 int r;
384
385 assert(bus);
386 assert(message);
387 assert(s);
388
389 r = sd_bus_message_read(message, "uu", &major, &minor);
390 if (r < 0)
391 return r;
392
393 if (!session_is_controller(s, sd_bus_message_get_sender(message)))
394 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
395
396 dev = makedev(major, minor);
397 sd = hashmap_get(s->devices, &dev);
398 if (!sd)
399 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
400
401 session_device_free(sd);
402 return sd_bus_reply_method_return(message, NULL);
403 }
404
405 static int method_pause_device_complete(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
406 Session *s = userdata;
407 uint32_t major, minor;
408 SessionDevice *sd;
409 dev_t dev;
410 int r;
411
412 assert(bus);
413 assert(message);
414 assert(s);
415
416 r = sd_bus_message_read(message, "uu", &major, &minor);
417 if (r < 0)
418 return r;
419
420 if (!session_is_controller(s, sd_bus_message_get_sender(message)))
421 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
422
423 dev = makedev(major, minor);
424 sd = hashmap_get(s->devices, &dev);
425 if (!sd)
426 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
427
428 session_device_complete_pause(sd);
429
430 return sd_bus_reply_method_return(message, NULL);
431 }
432
433 const sd_bus_vtable session_vtable[] = {
434 SD_BUS_VTABLE_START(0),
435
436 SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST),
437 SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST),
438 SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
439 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
440 SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST),
441 SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST),
442 SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST),
443 SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST),
444 SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST),
445 SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST),
446 SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST),
447 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST),
448 SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST),
449 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST),
450 SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST),
451 SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST),
452 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST),
453 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST),
454 SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
455 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
456 SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
457 SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
458 SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
459
460 SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
461 SD_BUS_METHOD("Activate", NULL, NULL, method_activate, SD_BUS_VTABLE_UNPRIVILEGED),
462 SD_BUS_METHOD("Lock", NULL, NULL, method_lock, 0),
463 SD_BUS_METHOD("Unlock", NULL, NULL, method_lock, 0),
464 SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED),
465 SD_BUS_METHOD("Kill", "si", NULL, method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
466 SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED),
467 SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED),
468 SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED),
469 SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED),
470 SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED),
471
472 SD_BUS_SIGNAL("PauseDevice", "uus", 0),
473 SD_BUS_SIGNAL("ResumeDevice", "uuh", 0),
474 SD_BUS_SIGNAL("Lock", NULL, 0),
475 SD_BUS_SIGNAL("Unlock", NULL, 0),
476
477 SD_BUS_VTABLE_END
478 };
479
480 int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
481 Manager *m = userdata;
482 Session *session;
483 int r;
484
485 assert(bus);
486 assert(path);
487 assert(interface);
488 assert(found);
489 assert(m);
490
491 if (streq(path, "/org/freedesktop/login1/session/self")) {
492 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
493 sd_bus_message *message;
494 pid_t pid;
495
496 message = sd_bus_get_current_message(bus);
497 if (!message)
498 return 0;
499
500 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
501 if (r < 0)
502 return r;
503
504 r = sd_bus_creds_get_pid(creds, &pid);
505 if (r < 0)
506 return r;
507
508 r = manager_get_session_by_pid(m, pid, &session);
509 if (r <= 0)
510 return 0;
511 } else {
512 _cleanup_free_ char *e = NULL;
513 const char *p;
514
515 p = startswith(path, "/org/freedesktop/login1/session/");
516 if (!p)
517 return 0;
518
519 e = bus_label_unescape(p);
520 if (!e)
521 return -ENOMEM;
522
523 session = hashmap_get(m->sessions, e);
524 if (!session)
525 return 0;
526 }
527
528 *found = session;
529 return 1;
530 }
531
532 char *session_bus_path(Session *s) {
533 _cleanup_free_ char *t = NULL;
534
535 assert(s);
536
537 t = bus_label_escape(s->id);
538 if (!t)
539 return NULL;
540
541 return strappend("/org/freedesktop/login1/session/", t);
542 }
543
544 int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
545 _cleanup_strv_free_ char **l = NULL;
546 Manager *m = userdata;
547 Session *session;
548 Iterator i;
549 int r;
550
551 assert(bus);
552 assert(path);
553 assert(nodes);
554
555 HASHMAP_FOREACH(session, m->sessions, i) {
556 char *p;
557
558 p = session_bus_path(session);
559 if (!p)
560 return -ENOMEM;
561
562 r = strv_consume(&l, p);
563 if (r < 0)
564 return r;
565 }
566
567 *nodes = l;
568 l = NULL;
569
570 return 1;
571 }
572
573 int session_send_signal(Session *s, bool new_session) {
574 _cleanup_free_ char *p = NULL;
575
576 assert(s);
577
578 p = session_bus_path(s);
579 if (!p)
580 return -ENOMEM;
581
582 return sd_bus_emit_signal(
583 s->manager->bus,
584 "/org/freedesktop/login1",
585 "org.freedesktop.login1.Manager",
586 new_session ? "SessionNew" : "SessionRemoved",
587 "so", s->id, p);
588 }
589
590 int session_send_changed(Session *s, const char *properties, ...) {
591 _cleanup_free_ char *p = NULL;
592 char **l;
593
594 assert(s);
595
596 if (!s->started)
597 return 0;
598
599 p = session_bus_path(s);
600 if (!p)
601 return -ENOMEM;
602
603 l = strv_from_stdarg_alloca(properties);
604
605 return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l);
606 }
607
608 int session_send_lock(Session *s, bool lock) {
609 _cleanup_free_ char *p = NULL;
610
611 assert(s);
612
613 p = session_bus_path(s);
614 if (!p)
615 return -ENOMEM;
616
617 return sd_bus_emit_signal(
618 s->manager->bus,
619 p,
620 "org.freedesktop.login1.Session",
621 lock ? "Lock" : "Unlock",
622 NULL);
623 }
624
625 int session_send_lock_all(Manager *m, bool lock) {
626 Session *session;
627 Iterator i;
628 int r = 0;
629
630 assert(m);
631
632 HASHMAP_FOREACH(session, m->sessions, i) {
633 int k;
634
635 k = session_send_lock(session, lock);
636 if (k < 0)
637 r = k;
638 }
639
640 return r;
641 }
642
643 int session_send_create_reply(Session *s, sd_bus_error *error) {
644 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
645 _cleanup_close_ int fifo_fd = -1;
646 _cleanup_free_ char *p = NULL;
647
648 assert(s);
649
650 /* This is called after the session scope and the user service
651 * were successfully created, and finishes where
652 * bus_manager_create_session() left off. */
653
654 if (!s->create_message)
655 return 0;
656
657 if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job))
658 return 0;
659
660 c = s->create_message;
661 s->create_message = NULL;
662
663 if (error)
664 return sd_bus_reply_method_error(c, error);
665
666 fifo_fd = session_create_fifo(s);
667 if (fifo_fd < 0)
668 return fifo_fd;
669
670 /* Update the session state file before we notify the client
671 * about the result. */
672 session_save(s);
673
674 p = session_bus_path(s);
675 if (!p)
676 return -ENOMEM;
677
678 log_debug("Sending reply about created session: "
679 "id=%s object_path=%s uid=%u runtime_path=%s "
680 "session_fd=%d seat=%s vtnr=%u",
681 s->id,
682 p,
683 (uint32_t) s->user->uid,
684 s->user->runtime_path,
685 fifo_fd,
686 s->seat ? s->seat->id : "",
687 (uint32_t) s->vtnr);
688
689 return sd_bus_reply_method_return(
690 c, "soshusub",
691 s->id,
692 p,
693 s->user->runtime_path,
694 fifo_fd,
695 (uint32_t) s->user->uid,
696 s->seat ? s->seat->id : "",
697 (uint32_t) s->vtnr,
698 false);
699 }