]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-seat-dbus.c
sd-bus: introduce sd_bus_slot objects encapsulating callbacks or vtables attached...
[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 #include <sys/capability.h>
25
26 #include "util.h"
27 #include "bus-util.h"
28 #include "strv.h"
29 #include "bus-errors.h"
30 #include "bus-label.h"
31 #include "logind.h"
32 #include "logind-seat.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 static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
198 Seat *s = userdata;
199 int r;
200
201 assert(bus);
202 assert(message);
203 assert(s);
204
205 r = seat_stop_sessions(s, true);
206 if (r < 0)
207 return r;
208
209 return sd_bus_reply_method_return(message, NULL);
210 }
211
212 static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
213 Seat *s = userdata;
214 const char *name;
215 Session *session;
216 int r;
217
218 assert(bus);
219 assert(message);
220 assert(s);
221
222 r = sd_bus_message_read(message, "s", &name);
223 if (r < 0)
224 return r;
225
226 session = hashmap_get(s->manager->sessions, name);
227 if (!session)
228 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name);
229
230 if (session->seat != s)
231 return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id);
232
233 r = session_activate(session);
234 if (r < 0)
235 return r;
236
237 return sd_bus_reply_method_return(message, NULL);
238 }
239
240 static int method_switch_to(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
241 Seat *s = userdata;
242 unsigned int to;
243 int r;
244
245 assert(bus);
246 assert(message);
247 assert(s);
248
249 r = sd_bus_message_read(message, "u", &to);
250 if (r < 0)
251 return r;
252
253 if (to <= 0)
254 return -EINVAL;
255
256 r = seat_switch_to(s, to);
257 if (r < 0)
258 return r;
259
260 return sd_bus_reply_method_return(message, NULL);
261 }
262
263 static int method_switch_to_next(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
264 Seat *s = userdata;
265 int r;
266
267 assert(bus);
268 assert(message);
269 assert(s);
270
271 r = seat_switch_to_next(s);
272 if (r < 0)
273 return r;
274
275 return sd_bus_reply_method_return(message, NULL);
276 }
277
278 static int method_switch_to_previous(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
279 Seat *s = userdata;
280 int r;
281
282 assert(bus);
283 assert(message);
284 assert(s);
285
286 r = seat_switch_to_previous(s);
287 if (r < 0)
288 return r;
289
290 return sd_bus_reply_method_return(message, NULL);
291 }
292
293 const sd_bus_vtable seat_vtable[] = {
294 SD_BUS_VTABLE_START(0),
295
296 SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST),
297 SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
298 SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST),
299 SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST),
300 SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
301 SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
302 SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
303 SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
304 SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
305
306 SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
307 SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
308 SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
309 SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
310 SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
311
312 SD_BUS_VTABLE_END
313 };
314
315 int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
316 Manager *m = userdata;
317 Seat *seat;
318 int r;
319
320 assert(bus);
321 assert(path);
322 assert(interface);
323 assert(found);
324 assert(m);
325
326 if (streq(path, "/org/freedesktop/login1/seat/self")) {
327 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
328 sd_bus_message *message;
329 Session *session;
330 pid_t pid;
331
332 message = sd_bus_get_current_message(bus);
333 if (!message)
334 return 0;
335
336 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
337 if (r < 0)
338 return r;
339
340 r = sd_bus_creds_get_pid(creds, &pid);
341 if (r < 0)
342 return r;
343
344 r = manager_get_session_by_pid(m, pid, &session);
345 if (r <= 0)
346 return 0;
347
348 if (!session->seat)
349 return 0;
350
351 seat = session->seat;
352 } else {
353 _cleanup_free_ char *e = NULL;
354 const char *p;
355
356 p = startswith(path, "/org/freedesktop/login1/seat/");
357 if (!p)
358 return 0;
359
360 e = bus_label_unescape(p);
361 if (!e)
362 return -ENOMEM;
363
364 seat = hashmap_get(m->seats, e);
365 if (!seat)
366 return 0;
367 }
368
369 *found = seat;
370 return 1;
371 }
372
373 char *seat_bus_path(Seat *s) {
374 _cleanup_free_ char *t = NULL;
375
376 assert(s);
377
378 t = bus_label_escape(s->id);
379 if (!t)
380 return NULL;
381
382 return strappend("/org/freedesktop/login1/seat/", t);
383 }
384
385 int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
386 _cleanup_strv_free_ char **l = NULL;
387 Manager *m = userdata;
388 Seat *seat;
389 Iterator i;
390 int r;
391
392 assert(bus);
393 assert(path);
394 assert(nodes);
395
396 HASHMAP_FOREACH(seat, m->seats, i) {
397 char *p;
398
399 p = seat_bus_path(seat);
400 if (!p)
401 return -ENOMEM;
402
403 r = strv_consume(&l, p);
404 if (r < 0)
405 return r;
406 }
407
408 *nodes = l;
409 l = NULL;
410
411 return 1;
412 }
413
414 int seat_send_signal(Seat *s, bool new_seat) {
415 _cleanup_free_ char *p = NULL;
416
417 assert(s);
418
419 p = seat_bus_path(s);
420 if (!p)
421 return -ENOMEM;
422
423 return sd_bus_emit_signal(
424 s->manager->bus,
425 "/org/freedesktop/login1",
426 "org.freedesktop.login1.Manager",
427 new_seat ? "SeatNew" : "SeatRemoved",
428 "so", s->id, p);
429 }
430
431 int seat_send_changed(Seat *s, const char *properties, ...) {
432 _cleanup_free_ char *p = NULL;
433 char **l;
434
435 assert(s);
436
437 if (!s->started)
438 return 0;
439
440 p = seat_bus_path(s);
441 if (!p)
442 return -ENOMEM;
443
444 l = strv_from_stdarg_alloca(properties);
445
446 return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l);
447 }