1 // Common functions that are unfortunately missing on illumos and
2 // Solaris, but often needed by other crates.
3 
4 use core::cmp::min;
5 use unix::solarish::*;
6 
7 const PTEM: &[u8] = b"ptem\0";
8 const LDTERM: &[u8] = b"ldterm\0";
9 
cfmakeraw(termios: *mut ::termios)10 pub unsafe fn cfmakeraw(termios: *mut ::termios) {
11     (*termios).c_iflag &=
12         !(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
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;
33 }
34 
cfsetspeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int35 pub unsafe fn cfsetspeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int {
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 }
42 
bail(fdm: ::c_int, fds: ::c_int) -> ::c_int43 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 
openpty( amain: *mut ::c_int, asubord: *mut ::c_int, name: *mut ::c_char, termp: *const termios, winp: *const ::winsize, ) -> ::c_int55 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:
94         if ::ioctl(fds, I_PUSH, PTEM.as_ptr()) < 0 || ::ioctl(fds, I_PUSH, LDTERM.as_ptr()) < 0 {
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 
forkpty( amain: *mut ::c_int, name: *mut ::c_char, termp: *const termios, winp: *const ::winsize, ) -> ::pid_t123 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 }
173 
getpwent_r( pwd: *mut passwd, buf: *mut ::c_char, buflen: ::size_t, result: *mut *mut passwd, ) -> ::c_int174 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 
getgrent_r( grp: *mut ::group, buf: *mut ::c_char, buflen: ::size_t, result: *mut *mut ::group, ) -> ::c_int198 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 }
221