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