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