]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-seat-dbus.c
util-lib: split out globbing related calls into glob-util.[ch]
[thirdparty/systemd.git] / src / login / logind-seat-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
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, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
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 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
337 sd_bus_message *message;
338 Session *session;
339 const char *name;
340
341 message = sd_bus_get_current_message(bus);
342 if (!message)
343 return 0;
344
345 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
346 if (r < 0)
347 return r;
348
349 r = sd_bus_creds_get_session(creds, &name);
350 if (r < 0)
351 return r;
352
353 session = hashmap_get(m->sessions, name);
354 if (!session)
355 return 0;
356
357 seat = session->seat;
358 } else {
359 _cleanup_free_ char *e = NULL;
360 const char *p;
361
362 p = startswith(path, "/org/freedesktop/login1/seat/");
363 if (!p)
364 return 0;
365
366 e = bus_label_unescape(p);
367 if (!e)
368 return -ENOMEM;
369
370 seat = hashmap_get(m->seats, e);
371 }
372
373 if (!seat)
374 return 0;
375
376 *found = seat;
377 return 1;
378 }
379
380 char *seat_bus_path(Seat *s) {
381 _cleanup_free_ char *t = NULL;
382
383 assert(s);
384
385 t = bus_label_escape(s->id);
386 if (!t)
387 return NULL;
388
389 return strappend("/org/freedesktop/login1/seat/", t);
390 }
391
392 int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
393 _cleanup_strv_free_ char **l = NULL;
394 sd_bus_message *message;
395 Manager *m = userdata;
396 Seat *seat;
397 Iterator i;
398 int r;
399
400 assert(bus);
401 assert(path);
402 assert(nodes);
403
404 HASHMAP_FOREACH(seat, m->seats, i) {
405 char *p;
406
407 p = seat_bus_path(seat);
408 if (!p)
409 return -ENOMEM;
410
411 r = strv_consume(&l, p);
412 if (r < 0)
413 return r;
414 }
415
416 message = sd_bus_get_current_message(bus);
417 if (message) {
418 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
419 const char *name;
420 Session *session;
421
422 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
423 if (r >= 0) {
424 r = sd_bus_creds_get_session(creds, &name);
425 if (r >= 0) {
426 session = hashmap_get(m->sessions, name);
427 if (session && session->seat) {
428 r = strv_extend(&l, "/org/freedesktop/login1/seat/self");
429 if (r < 0)
430 return r;
431 }
432 }
433 }
434 }
435
436 *nodes = l;
437 l = NULL;
438
439 return 1;
440 }
441
442 int seat_send_signal(Seat *s, bool new_seat) {
443 _cleanup_free_ char *p = NULL;
444
445 assert(s);
446
447 p = seat_bus_path(s);
448 if (!p)
449 return -ENOMEM;
450
451 return sd_bus_emit_signal(
452 s->manager->bus,
453 "/org/freedesktop/login1",
454 "org.freedesktop.login1.Manager",
455 new_seat ? "SeatNew" : "SeatRemoved",
456 "so", s->id, p);
457 }
458
459 int seat_send_changed(Seat *s, const char *properties, ...) {
460 _cleanup_free_ char *p = NULL;
461 char **l;
462
463 assert(s);
464
465 if (!s->started)
466 return 0;
467
468 p = seat_bus_path(s);
469 if (!p)
470 return -ENOMEM;
471
472 l = strv_from_stdarg_alloca(properties);
473
474 return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l);
475 }