Rust · 2557 bytes Raw Blame History
1 //! Virtual Terminal management
2 //!
3 //! Allocates and switches VTs for X server and sessions.
4
5 use anyhow::{Context, Result};
6 use std::fs::OpenOptions;
7 use std::os::unix::io::AsRawFd;
8
9 // VT ioctl constants
10 const VT_OPENQRY: libc::c_ulong = 0x5600;
11 const VT_ACTIVATE: libc::c_ulong = 0x5606;
12 const VT_WAITACTIVE: libc::c_ulong = 0x5607;
13
14 /// Find an unused VT
15 pub fn find_unused_vt() -> Result<u32> {
16 let console = OpenOptions::new()
17 .read(true)
18 .write(true)
19 .open("/dev/tty0")
20 .context("Failed to open /dev/tty0")?;
21
22 let mut vt: libc::c_int = 0;
23 let result = unsafe { libc::ioctl(console.as_raw_fd(), VT_OPENQRY, &mut vt) };
24
25 if result < 0 {
26 anyhow::bail!(
27 "VT_OPENQRY ioctl failed: {}",
28 std::io::Error::last_os_error()
29 );
30 }
31
32 if vt <= 0 {
33 anyhow::bail!("No unused VT available");
34 }
35
36 tracing::debug!("Found unused VT: {}", vt);
37 Ok(vt as u32)
38 }
39
40 /// Switch to a specific VT
41 pub fn switch_to_vt(vt: u32) -> Result<()> {
42 let console = OpenOptions::new()
43 .read(true)
44 .write(true)
45 .open("/dev/tty0")
46 .context("Failed to open /dev/tty0")?;
47
48 let fd = console.as_raw_fd();
49
50 // Activate the VT
51 let result = unsafe { libc::ioctl(fd, VT_ACTIVATE, vt as libc::c_int) };
52 if result < 0 {
53 anyhow::bail!(
54 "VT_ACTIVATE failed for VT {}: {}",
55 vt,
56 std::io::Error::last_os_error()
57 );
58 }
59
60 // Wait for it to become active
61 let result = unsafe { libc::ioctl(fd, VT_WAITACTIVE, vt as libc::c_int) };
62 if result < 0 {
63 anyhow::bail!(
64 "VT_WAITACTIVE failed for VT {}: {}",
65 vt,
66 std::io::Error::last_os_error()
67 );
68 }
69
70 tracing::info!("Switched to VT {}", vt);
71 Ok(())
72 }
73
74 /// Get the currently active VT
75 pub fn get_active_vt() -> Result<u32> {
76 #[repr(C)]
77 struct VtStat {
78 v_active: u16,
79 v_signal: u16,
80 v_state: u16,
81 }
82
83 const VT_GETSTATE: libc::c_ulong = 0x5603;
84
85 let console = OpenOptions::new()
86 .read(true)
87 .open("/dev/tty0")
88 .context("Failed to open /dev/tty0")?;
89
90 let mut stat = VtStat {
91 v_active: 0,
92 v_signal: 0,
93 v_state: 0,
94 };
95
96 let result = unsafe { libc::ioctl(console.as_raw_fd(), VT_GETSTATE, &mut stat) };
97
98 if result < 0 {
99 anyhow::bail!(
100 "VT_GETSTATE failed: {}",
101 std::io::Error::last_os_error()
102 );
103 }
104
105 Ok(stat.v_active as u32)
106 }
107