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