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