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