1// Common functions that are unfortunately missing on illumos and
2// Solaris, but often needed by other crates.
3
4use core::cmp::min;
5use unix::solarish::*;
6
7const PTEM: &[u8] = b"ptem\0";
8const LDTERM: &[u8] = b"ldterm\0";
9
10pub 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
35pub 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
43unsafe 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
55pub 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
123pub 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
174pub 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
198pub 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