]>
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, | |
137 | CAP_KILL, | |
138 | "org.freedesktop.login1.manage", | |
403ed0e5 | 139 | NULL, |
c529695e LP |
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 | ||
bda62573 | 149 | r = seat_stop_sessions(s, /* force = */ true); |
cc377381 | 150 | if (r < 0) |
ebcf1f97 | 151 | return r; |
a185c5aa | 152 | |
df2d202e | 153 | return sd_bus_reply_method_return(message, NULL); |
a185c5aa LP |
154 | } |
155 | ||
19070062 | 156 | static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
99534007 | 157 | Seat *s = ASSERT_PTR(userdata); |
cc377381 LP |
158 | const char *name; |
159 | Session *session; | |
160 | int r; | |
a185c5aa | 161 | |
cc377381 | 162 | assert(message); |
a185c5aa | 163 | |
cc377381 LP |
164 | r = sd_bus_message_read(message, "s", &name); |
165 | if (r < 0) | |
ebcf1f97 | 166 | return r; |
a185c5aa | 167 | |
cc377381 LP |
168 | session = hashmap_get(s->manager->sessions, name); |
169 | if (!session) | |
ebcf1f97 | 170 | return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); |
a185c5aa | 171 | |
cc377381 | 172 | if (session->seat != s) |
ebcf1f97 | 173 | return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id); |
a185c5aa | 174 | |
7820a56c | 175 | r = check_polkit_chvt(message, s->manager, error); |
4acf0cfd LP |
176 | if (r < 0) |
177 | return r; | |
178 | if (r == 0) | |
179 | return 1; /* Will call us back */ | |
180 | ||
cc377381 LP |
181 | r = session_activate(session); |
182 | if (r < 0) | |
ebcf1f97 | 183 | return r; |
3f49d45a | 184 | |
df2d202e | 185 | return sd_bus_reply_method_return(message, NULL); |
3f49d45a LP |
186 | } |
187 | ||
19070062 | 188 | static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
99534007 | 189 | Seat *s = ASSERT_PTR(userdata); |
14cb109d | 190 | unsigned to; |
49e6fdbf DH |
191 | int r; |
192 | ||
49e6fdbf | 193 | assert(message); |
49e6fdbf DH |
194 | |
195 | r = sd_bus_message_read(message, "u", &to); | |
196 | if (r < 0) | |
197 | return r; | |
198 | ||
199 | if (to <= 0) | |
1b09b81c | 200 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal"); |
4acf0cfd | 201 | |
7820a56c | 202 | r = check_polkit_chvt(message, s->manager, error); |
4acf0cfd LP |
203 | if (r < 0) |
204 | return r; | |
205 | if (r == 0) | |
206 | return 1; /* Will call us back */ | |
49e6fdbf DH |
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 | ||
19070062 | 215 | static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
99534007 | 216 | Seat *s = ASSERT_PTR(userdata); |
49e6fdbf DH |
217 | int r; |
218 | ||
49e6fdbf | 219 | assert(message); |
49e6fdbf | 220 | |
7820a56c | 221 | r = check_polkit_chvt(message, s->manager, error); |
4acf0cfd LP |
222 | if (r < 0) |
223 | return r; | |
224 | if (r == 0) | |
225 | return 1; /* Will call us back */ | |
226 | ||
49e6fdbf DH |
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 | ||
19070062 | 234 | static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
99534007 | 235 | Seat *s = ASSERT_PTR(userdata); |
49e6fdbf DH |
236 | int r; |
237 | ||
49e6fdbf | 238 | assert(message); |
49e6fdbf | 239 | |
7820a56c | 240 | r = check_polkit_chvt(message, s->manager, error); |
4acf0cfd LP |
241 | if (r < 0) |
242 | return r; | |
243 | if (r == 0) | |
244 | return 1; /* Will call us back */ | |
245 | ||
49e6fdbf DH |
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 | ||
c2b178d3 | 253 | static int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { |
3b92c086 LP |
254 | _cleanup_free_ char *e = NULL; |
255 | sd_bus_message *message; | |
99534007 | 256 | Manager *m = ASSERT_PTR(userdata); |
3b92c086 | 257 | const char *p; |
cc377381 | 258 | Seat *seat; |
927b1649 | 259 | int r; |
a185c5aa | 260 | |
cc377381 LP |
261 | assert(bus); |
262 | assert(path); | |
263 | assert(interface); | |
264 | assert(found); | |
a185c5aa | 265 | |
3b92c086 LP |
266 | p = startswith(path, "/org/freedesktop/login1/seat/"); |
267 | if (!p) | |
268 | return 0; | |
927b1649 | 269 | |
3b92c086 LP |
270 | e = bus_label_unescape(p); |
271 | if (!e) | |
272 | return -ENOMEM; | |
927b1649 | 273 | |
3b92c086 | 274 | message = sd_bus_get_current_message(bus); |
927b1649 | 275 | |
3b92c086 LP |
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; | |
927b1649 | 280 | } |
3b92c086 LP |
281 | if (r < 0) |
282 | return r; | |
a185c5aa | 283 | |
cc377381 LP |
284 | *found = seat; |
285 | return 1; | |
286 | } | |
a185c5aa | 287 | |
cc377381 LP |
288 | char *seat_bus_path(Seat *s) { |
289 | _cleanup_free_ char *t = NULL; | |
a185c5aa | 290 | |
cc377381 | 291 | assert(s); |
a185c5aa | 292 | |
a6278b88 | 293 | t = bus_label_escape(s->id); |
cc377381 LP |
294 | if (!t) |
295 | return NULL; | |
a185c5aa | 296 | |
b910cc72 | 297 | return strjoin("/org/freedesktop/login1/seat/", t); |
3f49d45a LP |
298 | } |
299 | ||
c2b178d3 | 300 | static int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { |
cc377381 | 301 | _cleanup_strv_free_ char **l = NULL; |
ca56b0a6 | 302 | sd_bus_message *message; |
3f49d45a | 303 | Manager *m = userdata; |
cc377381 | 304 | Seat *seat; |
3f49d45a LP |
305 | int r; |
306 | ||
cc377381 LP |
307 | assert(bus); |
308 | assert(path); | |
309 | assert(nodes); | |
3f49d45a | 310 | |
90e74a66 | 311 | HASHMAP_FOREACH(seat, m->seats) { |
cc377381 | 312 | char *p; |
3f49d45a | 313 | |
cc377381 LP |
314 | p = seat_bus_path(seat); |
315 | if (!p) | |
316 | return -ENOMEM; | |
3f49d45a | 317 | |
6e18964d ZJS |
318 | r = strv_consume(&l, p); |
319 | if (r < 0) | |
cc377381 | 320 | return r; |
3f49d45a LP |
321 | } |
322 | ||
ca56b0a6 DH |
323 | message = sd_bus_get_current_message(bus); |
324 | if (message) { | |
4afd3348 | 325 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; |
ca56b0a6 | 326 | |
3b92c086 | 327 | r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); |
ca56b0a6 | 328 | if (r >= 0) { |
3b92c086 LP |
329 | bool may_auto = false; |
330 | const char *name; | |
331 | ||
ca56b0a6 DH |
332 | r = sd_bus_creds_get_session(creds, &name); |
333 | if (r >= 0) { | |
3b92c086 LP |
334 | Session *session; |
335 | ||
ca56b0a6 DH |
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; | |
3b92c086 LP |
341 | |
342 | may_auto = true; | |
ca56b0a6 DH |
343 | } |
344 | } | |
3b92c086 LP |
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 | } | |
ca56b0a6 DH |
363 | } |
364 | } | |
b298e984 | 365 | |
1cc6c93a | 366 | *nodes = TAKE_PTR(l); |
cc377381 | 367 | return 1; |
3f49d45a | 368 | } |
da119395 LP |
369 | |
370 | int seat_send_signal(Seat *s, bool new_seat) { | |
ce0fc5f5 | 371 | _cleanup_free_ char *p = NULL; |
da119395 LP |
372 | |
373 | assert(s); | |
374 | ||
da119395 LP |
375 | p = seat_bus_path(s); |
376 | if (!p) | |
4654e558 | 377 | return -ENOMEM; |
da119395 | 378 | |
cc377381 LP |
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); | |
da119395 | 385 | } |
9418f147 | 386 | |
cc377381 | 387 | int seat_send_changed(Seat *s, const char *properties, ...) { |
ce0fc5f5 | 388 | _cleanup_free_ char *p = NULL; |
cc377381 | 389 | char **l; |
9418f147 LP |
390 | |
391 | assert(s); | |
392 | ||
ed18b08b LP |
393 | if (!s->started) |
394 | return 0; | |
395 | ||
9418f147 LP |
396 | p = seat_bus_path(s); |
397 | if (!p) | |
398 | return -ENOMEM; | |
399 | ||
cc377381 | 400 | l = strv_from_stdarg_alloca(properties); |
9418f147 | 401 | |
cc377381 | 402 | return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l); |
9418f147 | 403 | } |
c2b178d3 ZJS |
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 | ||
a6293b05 NK |
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), | |
c2b178d3 ZJS |
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 | }; |