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