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