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