1use 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 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#[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#[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}