1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "sd-messages.h"
10 #include "alloc-util.h"
11 #include "devnode-acl.h"
12 #include "errno-util.h"
15 #include "format-util.h"
16 #include "logind-seat-dbus.h"
17 #include "logind-seat.h"
18 #include "logind-session-dbus.h"
19 #include "mkdir-label.h"
20 #include "parse-util.h"
21 #include "path-util.h"
22 #include "stdio-util.h"
23 #include "string-util.h"
24 #include "terminal-util.h"
25 #include "tmpfile-util.h"
27 int seat_new(Seat
** ret
, Manager
*m
, const char *id
) {
28 _cleanup_(seat_freep
) Seat
*s
= NULL
;
35 if (!seat_name_is_valid(id
))
46 s
->state_file
= path_join("/run/systemd/seats", id
);
50 s
->id
= basename(s
->state_file
);
52 r
= hashmap_put(m
->seats
, s
->id
, s
);
60 Seat
* seat_free(Seat
*s
) {
65 LIST_REMOVE(gc_queue
, s
->manager
->seat_gc_queue
, s
);
68 session_free(s
->sessions
);
73 device_free(s
->devices
);
75 hashmap_remove(s
->manager
->seats
, s
->id
);
83 int seat_save(Seat
*s
) {
84 _cleanup_free_
char *temp_path
= NULL
;
85 _cleanup_fclose_
FILE *f
= NULL
;
93 r
= mkdir_safe_label("/run/systemd/seats", 0755, 0, 0, MKDIR_WARN_MODE
);
97 r
= fopen_temporary(s
->state_file
, &f
, &temp_path
);
101 (void) fchmod(fileno(f
), 0644);
104 "# This is private data. Do not parse.\n"
106 "CAN_MULTI_SESSION=1\n"
108 "CAN_GRAPHICAL=%i\n",
111 seat_can_graphical(s
));
114 assert(s
->active
->user
);
118 "ACTIVE_UID="UID_FMT
"\n",
120 s
->active
->user
->user_record
->uid
);
124 fputs("SESSIONS=", f
);
125 LIST_FOREACH(sessions_by_seat
, i
, s
->sessions
) {
129 i
->sessions_by_seat_next
? ' ' : '\n');
133 LIST_FOREACH(sessions_by_seat
, i
, s
->sessions
)
136 i
->user
->user_record
->uid
,
137 i
->sessions_by_seat_next
? ' ' : '\n');
140 r
= fflush_and_check(f
);
144 if (rename(temp_path
, s
->state_file
) < 0) {
152 (void) unlink(s
->state_file
);
155 (void) unlink(temp_path
);
157 return log_error_errno(r
, "Failed to save seat data %s: %m", s
->state_file
);
160 int seat_load(Seat
*s
) {
163 /* There isn't actually anything to read here ... */
168 static int vt_allocate(unsigned vtnr
) {
169 char p
[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned)];
170 _cleanup_close_
int fd
= -EBADF
;
174 xsprintf(p
, "/dev/tty%u", vtnr
);
175 fd
= open_terminal(p
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
182 int seat_preallocate_vts(Seat
*s
) {
189 if (s
->manager
->n_autovts
<= 0)
192 if (!seat_has_vts(s
))
195 log_debug("Preallocating VTs...");
197 for (i
= 1; i
<= s
->manager
->n_autovts
; i
++) {
202 r
= log_error_errno(q
, "Failed to preallocate VT %u: %m", i
);
208 int seat_apply_acls(Seat
*s
, Session
*old_active
) {
213 r
= devnode_acl_all(s
->id
,
215 !!old_active
, old_active
? old_active
->user
->user_record
->uid
: 0,
216 !!s
->active
, s
->active
? s
->active
->user
->user_record
->uid
: 0);
219 return log_error_errno(r
, "Failed to apply ACLs: %m");
224 int seat_set_active(Seat
*s
, Session
*session
) {
228 assert(!session
|| session
->seat
== s
);
230 if (session
== s
->active
)
233 old_active
= s
->active
;
237 session_device_pause_all(old_active
);
238 session_send_changed(old_active
, "Active", NULL
);
241 (void) seat_apply_acls(s
, old_active
);
243 if (session
&& session
->started
) {
244 session_send_changed(session
, "Active", NULL
);
245 session_device_resume_all(session
);
248 if (!session
|| session
->started
)
249 seat_send_changed(s
, "ActiveSession", NULL
);
254 session_save(session
);
255 user_save(session
->user
);
259 session_save(old_active
);
260 if (!session
|| session
->user
!= old_active
->user
)
261 user_save(old_active
->user
);
267 static Session
* seat_get_position(Seat
*s
, unsigned pos
) {
270 if (pos
>= MALLOC_ELEMENTSOF(s
->positions
))
273 return s
->positions
[pos
];
276 int seat_switch_to(Seat
*s
, unsigned num
) {
279 /* Public session positions skip 0 (there is only F1-F12). Maybe it
280 * will get reassigned in the future, so return error for now. */
284 session
= seat_get_position(s
, num
);
286 /* allow switching to unused VTs to trigger auto-activate */
287 if (seat_has_vts(s
) && num
< 64)
293 return session_activate(session
);
296 int seat_switch_to_next(Seat
*s
) {
300 if (MALLOC_ELEMENTSOF(s
->positions
) == 0)
304 if (s
->active
&& s
->active
->position
> 0)
305 start
= s
->active
->position
;
307 for (i
= start
+ 1; i
< MALLOC_ELEMENTSOF(s
->positions
); ++i
) {
308 session
= seat_get_position(s
, i
);
310 return session_activate(session
);
313 for (i
= 1; i
< start
; ++i
) {
314 session
= seat_get_position(s
, i
);
316 return session_activate(session
);
322 int seat_switch_to_previous(Seat
*s
) {
323 if (MALLOC_ELEMENTSOF(s
->positions
) == 0)
326 size_t start
= s
->active
&& s
->active
->position
> 0 ? s
->active
->position
: 1;
328 for (size_t i
= start
- 1; i
> 0; i
--) {
329 Session
*session
= seat_get_position(s
, i
);
331 return session_activate(session
);
334 for (size_t i
= MALLOC_ELEMENTSOF(s
->positions
) - 1; i
> start
; i
--) {
335 Session
*session
= seat_get_position(s
, i
);
337 return session_activate(session
);
343 int seat_active_vt_changed(Seat
*s
, unsigned vtnr
) {
344 Session
*new_active
= NULL
;
350 if (!seat_has_vts(s
))
353 log_debug("VT changed to %u", vtnr
);
355 /* we might have earlier closing sessions on the same VT, so try to
356 * find a running one first */
357 LIST_FOREACH(sessions_by_seat
, i
, s
->sessions
)
358 if (i
->vtnr
== vtnr
&& !i
->stopping
) {
364 /* no running one? then we can't decide which one is the
365 * active one, let the first one win */
366 LIST_FOREACH(sessions_by_seat
, i
, s
->sessions
)
367 if (i
->vtnr
== vtnr
) {
372 r
= seat_set_active(s
, new_active
);
373 manager_spawn_autovt(s
->manager
, vtnr
);
378 int seat_read_active_vt(Seat
*s
) {
385 if (!seat_has_vts(s
))
388 if (lseek(s
->manager
->console_active_fd
, SEEK_SET
, 0) < 0)
389 return log_error_errno(errno
, "lseek on console_active_fd failed: %m");
392 k
= read(s
->manager
->console_active_fd
, t
, sizeof(t
)-1);
394 return log_error_errno(errno
?: EIO
,
395 "Failed to read current console: %s", STRERROR_OR_EOF(errno
));
400 vtnr
= vtnr_from_tty(t
);
402 log_error_errno(vtnr
, "Hm, /sys/class/tty/tty0/active is badly formatted: %m");
406 return seat_active_vt_changed(s
, vtnr
);
409 int seat_start(Seat
*s
) {
416 "MESSAGE_ID=" SD_MESSAGE_SEAT_START_STR
,
418 LOG_MESSAGE("New seat %s.", s
->id
));
420 /* Initialize VT magic stuff */
421 seat_preallocate_vts(s
);
423 /* Read current VT */
424 seat_read_active_vt(s
);
431 seat_send_signal(s
, true);
436 int seat_stop(Seat
*s
, bool force
) {
443 "MESSAGE_ID=" SD_MESSAGE_SEAT_STOP_STR
,
445 LOG_MESSAGE("Removed seat %s.", s
->id
));
447 r
= seat_stop_sessions(s
, force
);
449 (void) unlink(s
->state_file
);
450 seat_add_to_gc_queue(s
);
453 seat_send_signal(s
, false);
460 int seat_stop_sessions(Seat
*s
, bool force
) {
465 LIST_FOREACH(sessions_by_seat
, session
, s
->sessions
) {
466 k
= session_stop(session
, force
);
474 void seat_evict_position(Seat
*s
, Session
*session
) {
475 unsigned pos
= session
->position
;
477 session
->position
= 0;
482 if (pos
< MALLOC_ELEMENTSOF(s
->positions
) && s
->positions
[pos
] == session
) {
483 s
->positions
[pos
] = NULL
;
485 /* There might be another session claiming the same
486 * position (eg., during gdm->session transition), so let's look
487 * for it and set it on the free slot. */
488 LIST_FOREACH(sessions_by_seat
, iter
, s
->sessions
)
489 if (iter
->position
== pos
&& session_get_state(iter
) != SESSION_CLOSING
) {
490 s
->positions
[pos
] = iter
;
496 void seat_claim_position(Seat
*s
, Session
*session
, unsigned pos
) {
497 /* with VTs, the position is always the same as the VTnr */
501 if (!GREEDY_REALLOC0(s
->positions
, pos
+ 1))
504 seat_evict_position(s
, session
);
506 session
->position
= pos
;
508 s
->positions
[pos
] = session
;
511 static void seat_assign_position(Seat
*s
, Session
*session
) {
514 if (session
->position
> 0)
517 for (pos
= 1; pos
< MALLOC_ELEMENTSOF(s
->positions
); ++pos
)
518 if (!s
->positions
[pos
])
521 seat_claim_position(s
, session
, pos
);
524 int seat_attach_session(Seat
*s
, Session
*session
) {
527 assert(!session
->seat
);
529 if (!seat_has_vts(s
) != !session
->vtnr
)
533 LIST_PREPEND(sessions_by_seat
, s
->sessions
, session
);
534 seat_assign_position(s
, session
);
536 /* On seats with VTs, the VT logic defines which session is active. On
537 * seats without VTs, we automatically activate new sessions. */
538 if (!seat_has_vts(s
))
539 seat_set_active(s
, session
);
544 void seat_complete_switch(Seat
*s
) {
549 /* if no session-switch is pending or if it got canceled, do nothing */
550 if (!s
->pending_switch
)
553 session
= TAKE_PTR(s
->pending_switch
);
555 seat_set_active(s
, session
);
558 bool seat_has_vts(Seat
*s
) {
561 return seat_is_seat0(s
) && s
->manager
->console_active_fd
>= 0;
564 bool seat_is_seat0(Seat
*s
) {
567 return s
->manager
->seat0
== s
;
570 bool seat_can_tty(Seat
*s
) {
573 return seat_has_vts(s
);
576 bool seat_has_master_device(Seat
*s
) {
579 /* device list is ordered by "master" flag */
580 return !!s
->devices
&& s
->devices
->master
;
583 bool seat_can_graphical(Seat
*s
) {
586 return seat_has_master_device(s
);
589 int seat_get_idle_hint(Seat
*s
, dual_timestamp
*t
) {
590 bool idle_hint
= true;
591 dual_timestamp ts
= DUAL_TIMESTAMP_NULL
;
595 LIST_FOREACH(sessions_by_seat
, session
, s
->sessions
) {
599 ih
= session_get_idle_hint(session
, &k
);
605 if (k
.monotonic
> ts
.monotonic
)
611 } else if (idle_hint
) {
613 if (k
.monotonic
> ts
.monotonic
)
624 bool seat_may_gc(Seat
*s
, bool drop_not_started
) {
627 if (drop_not_started
&& !s
->started
)
630 if (seat_is_seat0(s
))
633 return !seat_has_master_device(s
);
636 void seat_add_to_gc_queue(Seat
*s
) {
642 LIST_PREPEND(gc_queue
, s
->manager
->seat_gc_queue
, s
);
643 s
->in_gc_queue
= true;
646 static bool seat_name_valid_char(char c
) {
653 bool seat_name_is_valid(const char *name
) {
658 if (!startswith(name
, "seat"))
664 for (p
= name
; *p
; p
++)
665 if (!seat_name_valid_char(*p
))
668 if (strlen(name
) > 255)