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