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