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