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