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