chiark / gitweb /
tree-wide: drop license boilerplate
[elogind.git] / src / login / logind-seat-dbus.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2011 Lennart Poettering
6 ***/
7
8 #include <errno.h>
9 #include <string.h>
10
11 #include "alloc-util.h"
12 #include "bus-common-errors.h"
13 #include "bus-label.h"
14 #include "bus-util.h"
15 #include "logind-seat.h"
16 #include "logind.h"
17 #include "strv.h"
18 #include "user-util.h"
19 #include "util.h"
20
21 static int property_get_active_session(
22                 sd_bus *bus,
23                 const char *path,
24                 const char *interface,
25                 const char *property,
26                 sd_bus_message *reply,
27                 void *userdata,
28                 sd_bus_error *error) {
29
30         _cleanup_free_ char *p = NULL;
31         Seat *s = userdata;
32
33         assert(bus);
34         assert(reply);
35         assert(s);
36
37         p = s->active ? session_bus_path(s->active) : strdup("/");
38         if (!p)
39                 return -ENOMEM;
40
41         return sd_bus_message_append(reply, "(so)", s->active ? s->active->id : "", p);
42 }
43
44 static int property_get_can_multi_session(
45                 sd_bus *bus,
46                 const char *path,
47                 const char *interface,
48                 const char *property,
49                 sd_bus_message *reply,
50                 void *userdata,
51                 sd_bus_error *error) {
52
53         Seat *s = userdata;
54
55         assert(bus);
56         assert(reply);
57         assert(s);
58
59         return sd_bus_message_append(reply, "b", seat_can_multi_session(s));
60 }
61
62 static int property_get_can_tty(
63                 sd_bus *bus,
64                 const char *path,
65                 const char *interface,
66                 const char *property,
67                 sd_bus_message *reply,
68                 void *userdata,
69                 sd_bus_error *error) {
70
71         Seat *s = userdata;
72
73         assert(bus);
74         assert(reply);
75         assert(s);
76
77         return sd_bus_message_append(reply, "b", seat_can_tty(s));
78 }
79
80 static int property_get_can_graphical(
81                 sd_bus *bus,
82                 const char *path,
83                 const char *interface,
84                 const char *property,
85                 sd_bus_message *reply,
86                 void *userdata,
87                 sd_bus_error *error) {
88
89         Seat *s = userdata;
90
91         assert(bus);
92         assert(reply);
93         assert(s);
94
95         return sd_bus_message_append(reply, "b", seat_can_graphical(s));
96 }
97
98 static int property_get_sessions(
99                 sd_bus *bus,
100                 const char *path,
101                 const char *interface,
102                 const char *property,
103                 sd_bus_message *reply,
104                 void *userdata,
105                 sd_bus_error *error) {
106
107         Seat *s = userdata;
108         Session *session;
109         int r;
110
111         assert(bus);
112         assert(reply);
113         assert(s);
114
115         r = sd_bus_message_open_container(reply, 'a', "(so)");
116         if (r < 0)
117                 return r;
118
119         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
120                 _cleanup_free_ char *p = NULL;
121
122                 p = session_bus_path(session);
123                 if (!p)
124                         return -ENOMEM;
125
126                 r = sd_bus_message_append(reply, "(so)", session->id, p);
127                 if (r < 0)
128                         return r;
129
130         }
131
132         r = sd_bus_message_close_container(reply);
133         if (r < 0)
134                 return r;
135
136         return 1;
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         Seat *s = userdata;
149
150         assert(bus);
151         assert(reply);
152         assert(s);
153
154         return sd_bus_message_append(reply, "b", seat_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         Seat *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 = seat_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 int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
185         Seat *s = userdata;
186         int r;
187
188         assert(message);
189         assert(s);
190
191         r = bus_verify_polkit_async(
192                         message,
193                         CAP_KILL,
194                         "org.freedesktop.login1.manage",
195                         NULL,
196                         false,
197                         UID_INVALID,
198                         &s->manager->polkit_registry,
199                         error);
200         if (r < 0)
201                 return r;
202         if (r == 0)
203                 return 1; /* Will call us back */
204
205         r = seat_stop_sessions(s, true);
206         if (r < 0)
207                 return r;
208
209         return sd_bus_reply_method_return(message, NULL);
210 }
211
212 static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
213         Seat *s = userdata;
214         const char *name;
215         Session *session;
216         int r;
217
218         assert(message);
219         assert(s);
220
221         r = sd_bus_message_read(message, "s", &name);
222         if (r < 0)
223                 return r;
224
225         session = hashmap_get(s->manager->sessions, name);
226         if (!session)
227                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name);
228
229         if (session->seat != s)
230                 return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id);
231
232         r = session_activate(session);
233         if (r < 0)
234                 return r;
235
236         return sd_bus_reply_method_return(message, NULL);
237 }
238
239 static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) {
240         Seat *s = userdata;
241         unsigned int to;
242         int r;
243
244         assert(message);
245         assert(s);
246
247         r = sd_bus_message_read(message, "u", &to);
248         if (r < 0)
249                 return r;
250
251         if (to <= 0)
252                 return -EINVAL;
253
254         r = seat_switch_to(s, to);
255         if (r < 0)
256                 return r;
257
258         return sd_bus_reply_method_return(message, NULL);
259 }
260
261 static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) {
262         Seat *s = userdata;
263         int r;
264
265         assert(message);
266         assert(s);
267
268         r = seat_switch_to_next(s);
269         if (r < 0)
270                 return r;
271
272         return sd_bus_reply_method_return(message, NULL);
273 }
274
275 static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) {
276         Seat *s = userdata;
277         int r;
278
279         assert(message);
280         assert(s);
281
282         r = seat_switch_to_previous(s);
283         if (r < 0)
284                 return r;
285
286         return sd_bus_reply_method_return(message, NULL);
287 }
288
289 const sd_bus_vtable seat_vtable[] = {
290         SD_BUS_VTABLE_START(0),
291
292         SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST),
293         SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
294         SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST),
295         SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST),
296         SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
297         SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
298         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
299         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
300         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
301
302         SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
303         SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
304         SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
305         SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
306         SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
307
308         SD_BUS_VTABLE_END
309 };
310
311 int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
312         Manager *m = userdata;
313         Seat *seat;
314         int r;
315
316         assert(bus);
317         assert(path);
318         assert(interface);
319         assert(found);
320         assert(m);
321
322         if (streq(path, "/org/freedesktop/login1/seat/self")) {
323                 sd_bus_message *message;
324
325                 message = sd_bus_get_current_message(bus);
326                 if (!message)
327                         return 0;
328
329                 r = manager_get_seat_from_creds(m, message, NULL, error, &seat);
330                 if (r < 0)
331                         return r;
332         } else {
333                 _cleanup_free_ char *e = NULL;
334                 const char *p;
335
336                 p = startswith(path, "/org/freedesktop/login1/seat/");
337                 if (!p)
338                         return 0;
339
340                 e = bus_label_unescape(p);
341                 if (!e)
342                         return -ENOMEM;
343
344                 seat = hashmap_get(m->seats, e);
345                 if (!seat)
346                         return 0;
347         }
348
349         *found = seat;
350         return 1;
351 }
352
353 char *seat_bus_path(Seat *s) {
354         _cleanup_free_ char *t = NULL;
355
356         assert(s);
357
358         t = bus_label_escape(s->id);
359         if (!t)
360                 return NULL;
361
362         return strappend("/org/freedesktop/login1/seat/", t);
363 }
364
365 int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
366         _cleanup_strv_free_ char **l = NULL;
367         sd_bus_message *message;
368         Manager *m = userdata;
369         Seat *seat;
370         Iterator i;
371         int r;
372
373         assert(bus);
374         assert(path);
375         assert(nodes);
376
377         HASHMAP_FOREACH(seat, m->seats, i) {
378                 char *p;
379
380                 p = seat_bus_path(seat);
381                 if (!p)
382                         return -ENOMEM;
383
384                 r = strv_consume(&l, p);
385                 if (r < 0)
386                         return r;
387         }
388
389         message = sd_bus_get_current_message(bus);
390         if (message) {
391                 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
392                 const char *name;
393                 Session *session;
394
395                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
396                 if (r >= 0) {
397                         r = sd_bus_creds_get_session(creds, &name);
398                         if (r >= 0) {
399                                 session = hashmap_get(m->sessions, name);
400                                 if (session && session->seat) {
401                                         r = strv_extend(&l, "/org/freedesktop/login1/seat/self");
402                                         if (r < 0)
403                                                 return r;
404                                 }
405                         }
406                 }
407         }
408
409         *nodes = TAKE_PTR(l);
410
411         return 1;
412 }
413
414 int seat_send_signal(Seat *s, bool new_seat) {
415         _cleanup_free_ char *p = NULL;
416
417         assert(s);
418
419         p = seat_bus_path(s);
420         if (!p)
421                 return -ENOMEM;
422
423         return sd_bus_emit_signal(
424                         s->manager->bus,
425                         "/org/freedesktop/login1",
426                         "org.freedesktop.login1.Manager",
427                         new_seat ? "SeatNew" : "SeatRemoved",
428                         "so", s->id, p);
429 }
430
431 int seat_send_changed(Seat *s, const char *properties, ...) {
432         _cleanup_free_ char *p = NULL;
433         char **l;
434
435         assert(s);
436
437         if (!s->started)
438                 return 0;
439
440         p = seat_bus_path(s);
441         if (!p)
442                 return -ENOMEM;
443
444         l = strv_from_stdarg_alloca(properties);
445
446         return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l);
447 }