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