1 // Common functions that are unfortunately missing on illumos and
2 // Solaris, but often needed by other crates.
3
4 use unix::solarish::*;
5
6 const PTEM: &[u8] = b"ptem\0";
7 const LDTERM: &[u8] = b"ldterm\0";
8
cfmakeraw(termios: *mut ::termios)9 pub unsafe fn cfmakeraw(termios: *mut ::termios) {
10 (*termios).c_iflag &= !(IMAXBEL
11 | IGNBRK
12 | BRKINT
13 | PARMRK
14 | ISTRIP
15 | INLCR
16 | IGNCR
17 | ICRNL
18 | IXON);
19 (*termios).c_oflag &= !OPOST;
20 (*termios).c_lflag &= !(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
21 (*termios).c_cflag &= !(CSIZE | PARENB);
22 (*termios).c_cflag |= CS8;
23
24 // By default, most software expects a pending read to block until at
25 // least one byte becomes available. As per termio(7I), this requires
26 // setting the MIN and TIME parameters appropriately.
27 //
28 // As a somewhat unfortunate artefact of history, the MIN and TIME slots
29 // in the control character array overlap with the EOF and EOL slots used
30 // for canonical mode processing. Because the EOF character needs to be
31 // the ASCII EOT value (aka Control-D), it has the byte value 4. When
32 // switching to raw mode, this is interpreted as a MIN value of 4; i.e.,
33 // reads will block until at least four bytes have been input.
34 //
35 // Other platforms with a distinct MIN slot like Linux and FreeBSD appear
36 // to default to a MIN value of 1, so we'll force that value here:
37 (*termios).c_cc[VMIN] = 1;
38 (*termios).c_cc[VTIME] = 0;
39 }
40
cfsetspeed( termios: *mut ::termios, speed: ::speed_t, ) -> ::c_int41 pub unsafe fn cfsetspeed(
42 termios: *mut ::termios,
43 speed: ::speed_t,
44 ) -> ::c_int {
45 // Neither of these functions on illumos or Solaris actually ever
46 // return an error
47 ::cfsetispeed(termios, speed);
48 ::cfsetospeed(termios, speed);
49 0
50 }
51
bail(fdm: ::c_int, fds: ::c_int) -> ::c_int52 unsafe fn bail(fdm: ::c_int, fds: ::c_int) -> ::c_int {
53 let e = *___errno();
54 if fds >= 0 {
55 ::close(fds);
56 }
57 if fdm >= 0 {
58 ::close(fdm);
59 }
60 *___errno() = e;
61 return -1;
62 }
63
openpty( amain: *mut ::c_int, asubord: *mut ::c_int, name: *mut ::c_char, termp: *const termios, winp: *const ::winsize, ) -> ::c_int64 pub unsafe fn openpty(
65 amain: *mut ::c_int,
66 asubord: *mut ::c_int,
67 name: *mut ::c_char,
68 termp: *const termios,
69 winp: *const ::winsize,
70 ) -> ::c_int {
71 // Open the main pseudo-terminal device, making sure not to set it as the
72 // controlling terminal for this process:
73 let fdm = ::posix_openpt(O_RDWR | O_NOCTTY);
74 if fdm < 0 {
75 return -1;
76 }
77
78 // Set permissions and ownership on the subordinate device and unlock it:
79 if ::grantpt(fdm) < 0 || ::unlockpt(fdm) < 0 {
80 return bail(fdm, -1);
81 }
82
83 // Get the path name of the subordinate device:
84 let subordpath = ::ptsname(fdm);
85 if subordpath.is_null() {
86 return bail(fdm, -1);
87 }
88
89 // Open the subordinate device without setting it as the controlling
90 // terminal for this process:
91 let fds = ::open(subordpath, O_RDWR | O_NOCTTY);
92 if fds < 0 {
93 return bail(fdm, -1);
94 }
95
96 // Check if the STREAMS modules are already pushed:
97 let setup = ::ioctl(fds, I_FIND, LDTERM.as_ptr());
98 if setup < 0 {
99 return bail(fdm, fds);
100 } else if setup == 0 {
101 // The line discipline is not present, so push the appropriate STREAMS
102 // modules for the subordinate device:
103 if ::ioctl(fds, I_PUSH, PTEM.as_ptr()) < 0
104 || ::ioctl(fds, I_PUSH, LDTERM.as_ptr()) < 0
105 {
106 return bail(fdm, fds);
107 }
108 }
109
110 // If provided, set the terminal parameters:
111 if !termp.is_null() && ::tcsetattr(fds, TCSAFLUSH, termp) != 0 {
112 return bail(fdm, fds);
113 }
114
115 // If provided, set the window size:
116 if !winp.is_null() && ::ioctl(fds, TIOCSWINSZ, winp) < 0 {
117 return bail(fdm, fds);
118 }
119
120 // If the caller wants the name of the subordinate device, copy it out.
121 //
122 // Note that this is a terrible interface: there appears to be no standard
123 // upper bound on the copy length for this pointer. Nobody should pass
124 // anything but NULL here, preferring instead to use ptsname(3C) directly.
125 if !name.is_null() {
126 ::strcpy(name, subordpath);
127 }
128
129 *amain = fdm;
130 *asubord = fds;
131 0
132 }
133
forkpty( amain: *mut ::c_int, name: *mut ::c_char, termp: *const termios, winp: *const ::winsize, ) -> ::pid_t134 pub unsafe fn forkpty(
135 amain: *mut ::c_int,
136 name: *mut ::c_char,
137 termp: *const termios,
138 winp: *const ::winsize,
139 ) -> ::pid_t {
140 let mut fds = -1;
141
142 if openpty(amain, &mut fds, name, termp, winp) != 0 {
143 return -1;
144 }
145
146 let pid = ::fork();
147 if pid < 0 {
148 return bail(*amain, fds);
149 } else if pid > 0 {
150 // In the parent process, we close the subordinate device and return the
151 // process ID of the new child:
152 ::close(fds);
153 return pid;
154 }
155
156 // The rest of this function executes in the child process.
157
158 // Close the main side of the pseudo-terminal pair:
159 ::close(*amain);
160
161 // Use TIOCSCTTY to set the subordinate device as our controlling
162 // terminal. This will fail (with ENOTTY) if we are not the leader in
163 // our own session, so we call setsid() first. Finally, arrange for
164 // the pseudo-terminal to occupy the standard I/O descriptors.
165 if ::setsid() < 0
166 || ::ioctl(fds, TIOCSCTTY, 0) < 0
167 || ::dup2(fds, 0) < 0
168 || ::dup2(fds, 1) < 0
169 || ::dup2(fds, 2) < 0
170 {
171 // At this stage there are no particularly good ways to handle failure.
172 // Exit as abruptly as possible, using _exit() to avoid messing with any
173 // state still shared with the parent process.
174 ::_exit(EXIT_FAILURE);
175 }
176 // Close the inherited descriptor, taking care to avoid closing the standard
177 // descriptors by mistake:
178 if fds > 2 {
179 ::close(fds);
180 }
181
182 0
183 }
184