chiark / gitweb /
7d81500426dc0e7bbc5fed8c153a2f08b6bab719
[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 #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 }