chiark / gitweb /
bus: let's simplify things by getting rid of unnecessary bus parameters
[elogind.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                 sd_bus_error *error,
41                 void *userdata) {
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                 sd_bus_error *error,
64                 void *userdata) {
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                 sd_bus_error *error,
82                 void *userdata) {
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                 sd_bus_error *error,
108                 void *userdata) {
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                 sd_bus_error *error,
126                 void *userdata) {
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                 sd_bus_error *error,
144                 void *userdata) {
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                 sd_bus_error *error,
162                 void *userdata) {
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) {
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 sd_bus_reply_method_errno(message, r, NULL);
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) {
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 sd_bus_reply_method_errno(message, r, NULL);
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) {
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 sd_bus_reply_method_errno(message, r, NULL);
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) {
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 sd_bus_reply_method_errno(message, r, NULL);
239
240         r = sd_bus_get_owner_uid(bus, sd_bus_message_get_sender(message), &uid);
241         if (r < 0)
242                 return sd_bus_reply_method_errno(message, r, NULL);
243
244         if (uid != 0 && uid != s->user->uid)
245                 return sd_bus_reply_method_errorf(message, 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) {
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 sd_bus_reply_method_errno(message, r, NULL);
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_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
273         }
274
275         if (signo <= 0 || signo >= _NSIG)
276                 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
277
278         r = session_kill(s, who, signo);
279         if (r < 0)
280                 return sd_bus_reply_method_errno(message, r, NULL);
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) {
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 sd_bus_reply_method_errno(message, r, NULL);
297
298         r = sd_bus_get_owner_uid(bus, sd_bus_message_get_sender(message), &uid);
299         if (r < 0)
300                 return sd_bus_reply_method_errno(message, r, NULL);
301
302         if (uid != 0 && (force || uid != s->user->uid))
303                 return sd_bus_reply_method_errorf(message, 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 sd_bus_reply_method_errno(message, r, NULL);
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) {
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_reply_method_errorf(message, 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) {
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 sd_bus_reply_method_errno(message, r, NULL);
341
342         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
343                 return sd_bus_reply_method_errorf(message, 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_reply_method_errorf(message, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
354
355         r = session_device_new(s, dev, &sd);
356         if (r < 0)
357                 return sd_bus_reply_method_errno(message, r, NULL);
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) {
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 sd_bus_reply_method_errno(message, r, NULL);
380
381         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
382                 return sd_bus_reply_method_errorf(message, 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_reply_method_errorf(message, 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) {
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 sd_bus_reply_method_errno(message, r, NULL);
407
408         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
409                 return sd_bus_reply_method_errorf(message, 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_reply_method_errorf(message, 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 **found, void *userdata) {
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, char ***nodes, void *userdata) {
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 }