Skip to main content

relibc/header/pty/
mod.rs

1//! `pty.h` implementation.
2//!
3//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/openpty.3.html>.
4
5use core::{mem, ptr, slice};
6
7use crate::{
8    header::{
9        bits_sigset_t, fcntl, limits, pthread, signal,
10        stdlib::{grantpt, posix_openpt, ptsname_r, unlockpt},
11        sys_ioctl, sys_wait, termios, unistd, utmp,
12    },
13    platform::{
14        self,
15        types::{c_char, c_int, c_void},
16    },
17};
18
19unsafe fn openpty_inner(name: &mut [u8]) -> Result<(c_int, c_int), ()> {
20    // TODO: wrap in auto-close struct
21    let master = unsafe { posix_openpt(fcntl::O_RDWR | fcntl::O_NOCTTY) };
22    if master < 0 {
23        return Err(());
24    }
25
26    let ret = grantpt(master);
27    if ret == -1 {
28        unistd::close(master);
29        return Err(());
30    }
31    let ret = unsafe { unlockpt(master) };
32    if ret == -1 {
33        unistd::close(master);
34        return Err(());
35    }
36
37    let ret = unsafe { ptsname_r(master, name.as_mut_ptr().cast(), name.len()) };
38    if ret < 0 {
39        unistd::close(master);
40        return Err(());
41    }
42
43    let slave = unsafe {
44        fcntl::open(
45            name.as_ptr().cast::<c_char>(),
46            fcntl::O_RDWR | fcntl::O_NOCTTY,
47            0,
48        )
49    };
50    if slave < 0 {
51        unistd::close(master);
52        return Err(());
53    }
54
55    Ok((master, slave))
56}
57
58/// See <https://www.man7.org/linux/man-pages/man3/openpty.3.html>.
59#[unsafe(no_mangle)]
60pub unsafe extern "C" fn openpty(
61    amaster: *mut c_int,
62    aslave: *mut c_int,
63    namep: *mut c_char,
64    termp: *const termios::termios,
65    winp: *const termios::winsize,
66) -> c_int {
67    let mut tmp_name = [0; limits::PATH_MAX];
68    let name = if !namep.is_null() {
69        unsafe { slice::from_raw_parts_mut(namep.cast::<u8>(), limits::PATH_MAX) }
70    } else {
71        &mut tmp_name
72    };
73
74    let (master, slave) = match unsafe { openpty_inner(name) } {
75        Ok(ok) => ok,
76        Err(()) => return -1,
77    };
78
79    if !termp.is_null() {
80        unsafe { termios::tcsetattr(slave, termios::TCSANOW, termp) };
81    }
82
83    if !winp.is_null() {
84        unsafe { sys_ioctl::ioctl(slave, sys_ioctl::TIOCSWINSZ, winp as *mut c_void) };
85    }
86
87    unsafe { *amaster = master };
88    unsafe { *aslave = slave };
89
90    0
91}
92
93/// See <https://www.man7.org/linux/man-pages/man3/openpty.3.html>.
94#[unsafe(no_mangle)]
95pub unsafe extern "C" fn forkpty(
96    pm: *mut c_int,
97    name: *mut c_char,
98    tio: *const termios::termios,
99    ws: *const termios::winsize,
100) -> c_int {
101    let mut m = 0;
102    let mut s = 0;
103    let mut ec = 0;
104    let mut p: [c_int; 2] = [0; 2];
105    let mut cs = 0;
106    let mut pid = -1;
107    let mut set = bits_sigset_t::sigset_t::default();
108    let mut oldset = bits_sigset_t::sigset_t::default();
109
110    if unsafe { openpty(&raw mut m, &raw mut s, name, tio, ws) } < 0 {
111        return -1;
112    }
113
114    unsafe { signal::sigfillset(&raw mut set) };
115    unsafe { signal::pthread_sigmask(signal::SIG_BLOCK, &raw const set, &raw mut oldset) };
116    unsafe { pthread::pthread_setcancelstate(pthread::PTHREAD_CANCEL_DISABLE, &raw mut cs) };
117
118    if unsafe { unistd::pipe2(p.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 {
119        unistd::close(s);
120    } else {
121        pid = unsafe { unistd::fork() };
122        if pid == 0 {
123            unistd::close(m);
124            unistd::close(p[0]);
125            if unsafe { utmp::login_tty(s) } != 0 {
126                unsafe {
127                    unistd::write(
128                        p[1],
129                        platform::ERRNO.as_ptr().cast(),
130                        mem::size_of_val(&platform::ERRNO),
131                    )
132                };
133                unistd::_exit(127);
134            }
135            unistd::close(p[1]);
136            unsafe { pthread::pthread_setcancelstate(cs, ptr::null_mut()) };
137            unsafe {
138                signal::pthread_sigmask(signal::SIG_SETMASK, &raw const oldset, ptr::null_mut())
139            };
140            return 0;
141        }
142
143        unistd::close(s);
144        unistd::close(p[1]);
145
146        if unsafe {
147            unistd::read(
148                p[0],
149                ptr::from_mut::<c_int>(&mut ec).cast::<c_void>(),
150                mem::size_of::<c_int>(),
151            )
152        } > 0
153        {
154            let mut status = 0;
155            unsafe { sys_wait::waitpid(pid, &raw mut status, 0) };
156            pid = -1;
157            platform::ERRNO.set(ec);
158        }
159        unistd::close(p[0]);
160    }
161    if pid > 0 {
162        unsafe { *pm = m };
163    } else {
164        unistd::close(m);
165    }
166    unsafe { pthread::pthread_setcancelstate(cs, ptr::null_mut()) };
167    unsafe { signal::pthread_sigmask(signal::SIG_SETMASK, &raw const oldset, ptr::null_mut()) };
168    pid
169}