]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-seat-dbus.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.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 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <string.h>
23
24 #include "alloc-util.h"
25 #include "bus-common-errors.h"
26 #include "bus-label.h"
27 #include "bus-util.h"
28 #include "logind-seat.h"
29 #include "logind.h"
30 #include "strv.h"
31 #include "user-util.h"
32 #include "util.h"
33
34 static int property_get_active_session(
35 sd_bus *bus,
36 const char *path,
37 const char *interface,
38 const char *property,
39 sd_bus_message *reply,
40 void *userdata,
41 sd_bus_error *error) {
42
43 _cleanup_free_ char *p = NULL;
44 Seat *s = userdata;
45
46 assert(bus);
47 assert(reply);
48 assert(s);
49
50 p = s->active ? session_bus_path(s->active) : strdup("/");
51 if (!p)
52 return -ENOMEM;
53
54 return sd_bus_message_append(reply, "(so)", s->active ? s->active->id : "", p);
55 }
56
57 static int property_get_can_multi_session(
58 sd_bus *bus,
59 const char *path,
60 const char *interface,
61 const char *property,
62 sd_bus_message *reply,
63 void *userdata,
64 sd_bus_error *error) {
65
66 Seat *s = userdata;
67
68 assert(bus);
69 assert(reply);
70 assert(s);
71
72 return sd_bus_message_append(reply, "b", seat_can_multi_session(s));
73 }
74
75 static int property_get_can_tty(
76 sd_bus *bus,
77 const char *path,
78 const char *interface,
79 const char *property,
80 sd_bus_message *reply,
81 void *userdata,
82 sd_bus_error *error) {
83
84 Seat *s = userdata;
85
86 assert(bus);
87 assert(reply);
88 assert(s);
89
90 return sd_bus_message_append(reply, "b", seat_can_tty(s));
91 }
92
93 static int property_get_can_graphical(
94 sd_bus *bus,
95 const char *path,
96 const char *interface,
97 const char *property,
98 sd_bus_message *reply,
99 void *userdata,
100 sd_bus_error *error) {
101
102 Seat *s = userdata;
103
104 assert(bus);
105 assert(reply);
106 assert(s);
107
108 return sd_bus_message_append(reply, "b", seat_can_graphical(s));
109 }
110
111 static int property_get_sessions(
112 sd_bus *bus,
113 const char *path,
114 const char *interface,
115 const char *property,
116 sd_bus_message *reply,
117 void *userdata,
118 sd_bus_error *error) {
119
120 Seat *s = userdata;
121 Session *session;
122 int r;
123
124 assert(bus);
125 assert(reply);
126 assert(s);
127
128 r = sd_bus_message_open_container(reply, 'a', "(so)");
129 if (r < 0)
130 return r;
131
132 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
133 _cleanup_free_ char *p = NULL;
134
135 p = session_bus_path(session);
136 if (!p)
137 return -ENOMEM;
138
139 r = sd_bus_message_append(reply, "(so)", session->id, p);
140 if (r < 0)
141 return r;
142
143 }
144
145 r = sd_bus_message_close_container(reply);
146 if (r < 0)
147 return r;
148
149 return 1;
150 }
151
152 static int property_get_idle_hint(
153 sd_bus *bus,
154 const char *path,
155 const char *interface,
156 const char *property,
157 sd_bus_message *reply,
158 void *userdata,
159 sd_bus_error *error) {
160
161 Seat *s = userdata;
162
163 assert(bus);
164 assert(reply);
165 assert(s);
166
167 return sd_bus_message_append(reply, "b", seat_get_idle_hint(s, NULL) > 0);
168 }
169
170 static int property_get_idle_since_hint(
171 sd_bus *bus,
172 const char *path,
173 const char *interface,
174 const char *property,
175 sd_bus_message *reply,
176 void *userdata,
177 sd_bus_error *error) {
178
179 Seat *s = userdata;
180 dual_timestamp t;
181 uint64_t u;
182 int r;
183
184 assert(bus);
185 assert(reply);
186 assert(s);
187
188 r = seat_get_idle_hint(s, &t);
189 if (r < 0)
190 return r;
191
192 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
193
194 return sd_bus_message_append(reply, "t", u);
195 }
196
197 int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
198 Seat *s = userdata;
199 int r;
200
201 assert(message);
202 assert(s);
203
204 r = bus_verify_polkit_async(
205 message,
206 CAP_KILL,
207 "org.freedesktop.login1.manage",
208 NULL,
209 false,
210 UID_INVALID,
211 &s->manager->polkit_registry,
212 error);
213 if (r < 0)
214 return r;
215 if (r == 0)
216 return 1; /* Will call us back */
217
218 r = seat_stop_sessions(s, true);
219 if (r < 0)
220 return r;
221
222 return sd_bus_reply_method_return(message, NULL);
223 }
224
225 static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
226 Seat *s = userdata;
227 const char *name;
228 Session *session;
229 int r;
230
231 assert(message);
232 assert(s);
233
234 r = sd_bus_message_read(message, "s", &name);
235 if (r < 0)
236 return r;
237
238 session = hashmap_get(s->manager->sessions, name);
239 if (!session)
240 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name);
241
242 if (session->seat != s)
243 return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id);
244
245 r = session_activate(session);
246 if (r < 0)
247 return r;
248
249 return sd_bus_reply_method_return(message, NULL);
250 }
251
252 static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) {
253 Seat *s = userdata;
254 unsigned int to;
255 int r;
256
257 assert(message);
258 assert(s);
259
260 r = sd_bus_message_read(message, "u", &to);
261 if (r < 0)
262 return r;
263
264 if (to <= 0)
265 return -EINVAL;
266
267 r = seat_switch_to(s, to);
268 if (r < 0)
269 return r;
270
271 return sd_bus_reply_method_return(message, NULL);
272 }
273
274 static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) {
275 Seat *s = userdata;
276 int r;
277
278 assert(message);
279 assert(s);
280
281 r = seat_switch_to_next(s);
282 if (r < 0)
283 return r;
284
285 return sd_bus_reply_method_return(message, NULL);
286 }
287
288 static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) {
289 Seat *s = userdata;
290 int r;
291
292 assert(message);
293 assert(s);
294
295 r = seat_switch_to_previous(s);
296 if (r < 0)
297 return r;
298
299 return sd_bus_reply_method_return(message, NULL);
300 }
301
302 const sd_bus_vtable seat_vtable[] = {
303 SD_BUS_VTABLE_START(0),
304
305 SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST),
306 SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
307 SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST),
308 SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST),
309 SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
310 SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
311 SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
312 SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
313 SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
314
315 SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
316 SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
317 SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
318 SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
319 SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
320
321 SD_BUS_VTABLE_END
322 };
323
324 int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
325 Manager *m = userdata;
326 Seat *seat;
327 int r;
328
329 assert(bus);
330 assert(path);
331 assert(interface);
332 assert(found);
333 assert(m);
334
335 if (streq(path, "/org/freedesktop/login1/seat/self")) {
336 sd_bus_message *message;
337
338 message = sd_bus_get_current_message(bus);
339 if (!message)
340 return 0;
341
342 r = manager_get_seat_from_creds(m, message, NULL, error, &seat);
343 if (r < 0)
344 return r;
345 } else {
346 _cleanup_free_ char *e = NULL;
347 const char *p;
348
349 p = startswith(path, "/org/freedesktop/login1/seat/");
350 if (!p)
351 return 0;
352
353 e = bus_label_unescape(p);
354 if (!e)
355 return -ENOMEM;
356
357 seat = hashmap_get(m->seats, e);
358 if (!seat)
359 return 0;
360 }
361
362 *found = seat;
363 return 1;
364 }
365
366 char *seat_bus_path(Seat *s) {
367 _cleanup_free_ char *t = NULL;
368
369 assert(s);
370
371 t = bus_label_escape(s->id);
372 if (!t)
373 return NULL;
374
375 return strappend("/org/freedesktop/login1/seat/", t);
376 }
377
378 int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
379 _cleanup_strv_free_ char **l = NULL;
380 sd_bus_message *message;
381 Manager *m = userdata;
382 Seat *seat;
383 Iterator i;
384 int r;
385
386 assert(bus);
387 assert(path);
388 assert(nodes);
389
390 HASHMAP_FOREACH(seat, m->seats, i) {
391 char *p;
392
393 p = seat_bus_path(seat);
394 if (!p)
395 return -ENOMEM;
396
397 r = strv_consume(&l, p);
398 if (r < 0)
399 return r;
400 }
401
402 message = sd_bus_get_current_message(bus);
403 if (message) {
404 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
405 const char *name;
406 Session *session;
407
408 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
409 if (r >= 0) {
410 r = sd_bus_creds_get_session(creds, &name);
411 if (r >= 0) {
412 session = hashmap_get(m->sessions, name);
413 if (session && session->seat) {
414 r = strv_extend(&l, "/org/freedesktop/login1/seat/self");
415 if (r < 0)
416 return r;
417 }
418 }
419 }
420 }
421
422 *nodes = l;
423 l = NULL;
424
425 return 1;
426 }
427
428 int seat_send_signal(Seat *s, bool new_seat) {
429 _cleanup_free_ char *p = NULL;
430
431 assert(s);
432
433 p = seat_bus_path(s);
434 if (!p)
435 return -ENOMEM;
436
437 return sd_bus_emit_signal(
438 s->manager->bus,
439 "/org/freedesktop/login1",
440 "org.freedesktop.login1.Manager",
441 new_seat ? "SeatNew" : "SeatRemoved",
442 "so", s->id, p);
443 }
444
445 int seat_send_changed(Seat *s, const char *properties, ...) {
446 _cleanup_free_ char *p = NULL;
447 char **l;
448
449 assert(s);
450
451 if (!s->started)
452 return 0;
453
454 p = seat_bus_path(s);
455 if (!p)
456 return -ENOMEM;
457
458 l = strv_from_stdarg_alloca(properties);
459
460 return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l);
461 }