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