pty_proc_unix.c (11220B)
1 // Some of the code came from pangoterm and libuv 2 3 #include <assert.h> 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <signal.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/ioctl.h> 10 #include <sys/wait.h> 11 #include <uv.h> 12 13 // forkpty is not in POSIX, so headers are platform-specific 14 #if defined(__FreeBSD__) || defined(__DragonFly__) 15 # include <libutil.h> 16 // TODO(bfredl): this is available on darwin, but there is an issue with cross-compile headers 17 #elif defined(__APPLE__) && !defined(HAVE_FORKPTY) 18 int forkpty(int *, char *, const struct termios *, const struct winsize *); 19 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 20 # include <util.h> 21 #elif defined(__sun) 22 # include <fcntl.h> 23 # include <signal.h> 24 # include <sys/stream.h> 25 # include <sys/syscall.h> 26 # include <unistd.h> 27 #else 28 # include <pty.h> 29 #endif 30 31 #ifdef __APPLE__ 32 # include <crt_externs.h> 33 #endif 34 #ifdef __linux__ 35 # include <poll.h> 36 #endif 37 38 #include "auto/config.h" 39 #include "klib/kvec.h" 40 #include "nvim/eval/typval.h" 41 #include "nvim/event/defs.h" 42 #include "nvim/event/loop.h" 43 #include "nvim/event/proc.h" 44 #include "nvim/log.h" 45 #include "nvim/os/fs.h" 46 #include "nvim/os/os_defs.h" 47 #include "nvim/os/pty_proc.h" 48 #include "nvim/os/pty_proc_unix.h" 49 #include "nvim/types_defs.h" 50 51 #include "os/pty_proc_unix.c.generated.h" 52 53 #if !defined(HAVE_FORKPTY) && !defined(__APPLE__) 54 55 // this header defines STR, just as nvim.h, but it is defined as ('S'<<8), 56 // to avoid #undef STR, #undef STR, #define STR ('S'<<8) just delay the 57 // inclusion of the header even though it gets include out of order. 58 59 # if !defined(__HAIKU__) 60 # include <sys/stropts.h> 61 # else 62 # define I_PUSH 0 // XXX: find the actual value 63 # endif 64 65 static int vim_openpty(int *amaster, int *aslave, char *name, struct termios *termp, 66 struct winsize *winp) 67 { 68 int slave = -1; 69 int master = open("/dev/ptmx", O_RDWR); 70 if (master == -1) { 71 goto error; 72 } 73 74 // grantpt will invoke a setuid program to change permissions 75 // and might fail if SIGCHLD handler is set, temporarily reset 76 // while running 77 void (*sig_saved)(int) = signal(SIGCHLD, SIG_DFL); 78 int res = grantpt(master); 79 signal(SIGCHLD, sig_saved); 80 81 if (res == -1 || unlockpt(master) == -1) { 82 goto error; 83 } 84 85 char *slave_name = ptsname(master); 86 if (slave_name == NULL) { 87 goto error; 88 } 89 90 slave = open(slave_name, O_RDWR|O_NOCTTY); 91 if (slave == -1) { 92 goto error; 93 } 94 95 // ptem emulates a terminal when used on a pseudo terminal driver, 96 // must be pushed before ldterm 97 ioctl(slave, I_PUSH, "ptem"); 98 // ldterm provides most of the termio terminal interface 99 ioctl(slave, I_PUSH, "ldterm"); 100 // ttcompat compatibility with older terminal ioctls 101 ioctl(slave, I_PUSH, "ttcompat"); 102 103 if (termp) { 104 tcsetattr(slave, TCSAFLUSH, termp); 105 } 106 if (winp) { 107 ioctl(slave, TIOCSWINSZ, winp); 108 } 109 110 *amaster = master; 111 *aslave = slave; 112 // ignoring name, not passed and size is unknown in the API 113 114 return 0; 115 116 error: 117 if (slave != -1) { 118 close(slave); 119 } 120 if (master != -1) { 121 close(master); 122 } 123 return -1; 124 } 125 126 static int vim_login_tty(int fd) 127 { 128 setsid(); 129 if (ioctl(fd, TIOCSCTTY, NULL) == -1) { 130 return -1; 131 } 132 133 dup2(fd, STDIN_FILENO); 134 dup2(fd, STDOUT_FILENO); 135 dup2(fd, STDERR_FILENO); 136 if (fd > STDERR_FILENO) { 137 close(fd); 138 } 139 140 return 0; 141 } 142 143 pid_t vim_forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp) 144 { 145 int master, slave; 146 if (vim_openpty(&master, &slave, name, termp, winp) == -1) { 147 return -1; 148 } 149 150 pid_t pid = fork(); 151 switch (pid) { 152 case -1: 153 close(master); 154 close(slave); 155 return -1; 156 case 0: 157 close(master); 158 vim_login_tty(slave); 159 return 0; 160 default: 161 close(slave); 162 *amaster = master; 163 return pid; 164 } 165 } 166 # define forkpty vim_forkpty 167 #endif 168 169 /// @returns zero on success, or negative error code 170 int pty_proc_spawn(PtyProc *ptyproc) 171 FUNC_ATTR_NONNULL_ALL 172 { 173 // termios initialized at first use 174 static struct termios termios_default; 175 if (!termios_default.c_cflag) { 176 init_termios(&termios_default); 177 } 178 179 int status = 0; // zero or negative error code (libuv convention) 180 Proc *proc = (Proc *)ptyproc; 181 assert(proc->err.s.closed); 182 uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); 183 ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 }; 184 uv_disable_stdio_inheritance(); 185 int master; 186 int pid = forkpty(&master, NULL, &termios_default, &ptyproc->winsize); 187 188 if (pid < 0) { 189 status = -errno; 190 ELOG("forkpty failed: %s", strerror(errno)); 191 return status; 192 } else if (pid == 0) { 193 init_child(ptyproc); // never returns 194 } 195 196 // make sure the master file descriptor is non blocking 197 int master_status_flags = fcntl(master, F_GETFL); 198 if (master_status_flags == -1) { 199 status = -errno; 200 ELOG("Failed to get master descriptor status flags: %s", strerror(errno)); 201 goto error; 202 } 203 if (fcntl(master, F_SETFL, master_status_flags | O_NONBLOCK) == -1) { 204 status = -errno; 205 ELOG("Failed to make master descriptor non-blocking: %s", strerror(errno)); 206 goto error; 207 } 208 209 // Other jobs and providers should not get a copy of this file descriptor. 210 if (os_set_cloexec(master) == -1) { 211 status = -errno; 212 ELOG("Failed to set CLOEXEC on ptmx file descriptor"); 213 goto error; 214 } 215 216 if (!proc->in.closed 217 && (status = set_duplicating_descriptor(master, &proc->in.uv.pipe))) { 218 goto error; 219 } 220 // The stream_init() call in proc_spawn() will initialize proc->out.s.uv.poll. 221 222 ptyproc->tty_fd = master; 223 proc->pid = pid; 224 return 0; 225 226 error: 227 close(master); 228 kill(pid, SIGKILL); 229 waitpid(pid, NULL, 0); 230 return status; 231 } 232 233 const char *pty_proc_tty_name(PtyProc *ptyproc) 234 { 235 return ptsname(ptyproc->tty_fd); 236 } 237 238 void pty_proc_resize(PtyProc *ptyproc, uint16_t width, uint16_t height) 239 FUNC_ATTR_NONNULL_ALL 240 { 241 ptyproc->winsize = (struct winsize){ height, width, 0, 0 }; 242 ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize); 243 } 244 245 void pty_proc_resume(PtyProc *ptyproc) 246 FUNC_ATTR_NONNULL_ALL 247 { 248 // Send SIGCONT to the entire process group, as some shells (e.g. fish) don't 249 // propagate SIGCONT to suspended child processes. 250 killpg(((Proc *)ptyproc)->pid, SIGCONT); 251 } 252 253 /// On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending 254 /// work on kernel workqueue, so use an explcit poll() before that. #37982 255 /// Note that poll() only flushes pending work if no data is immediately available, 256 /// so this function is needed before every libuv poll in flush_stream(). 257 void pty_proc_flush_master(PtyProc *ptyproc) 258 FUNC_ATTR_NONNULL_ALL 259 { 260 #ifdef __linux__ 261 struct pollfd pollfd = { .fd = ptyproc->tty_fd, .events = POLLIN }; 262 int n = 0; 263 do { 264 n = poll(&pollfd, 1, 0); 265 } while (n < 0 && errno == EINTR); 266 #endif 267 } 268 269 void pty_proc_close(PtyProc *ptyproc) 270 FUNC_ATTR_NONNULL_ALL 271 { 272 pty_proc_close_master(ptyproc); 273 Proc *proc = (Proc *)ptyproc; 274 if (proc->internal_close_cb) { 275 proc->internal_close_cb(proc); 276 } 277 } 278 279 void pty_proc_close_master(PtyProc *ptyproc) 280 FUNC_ATTR_NONNULL_ALL 281 { 282 if (ptyproc->tty_fd >= 0) { 283 close(ptyproc->tty_fd); 284 ptyproc->tty_fd = -1; 285 } 286 } 287 288 void pty_proc_teardown(Loop *loop) 289 { 290 uv_signal_stop(&loop->children_watcher); 291 } 292 293 static void init_child(PtyProc *ptyproc) 294 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NORETURN 295 { 296 #if defined(HAVE__NSGETENVIRON) 297 # define environ (*_NSGetEnviron()) 298 #else 299 extern char **environ; 300 #endif 301 // New session/process-group. #6530 302 setsid(); 303 304 signal(SIGCHLD, SIG_DFL); 305 signal(SIGHUP, SIG_DFL); 306 signal(SIGINT, SIG_DFL); 307 signal(SIGQUIT, SIG_DFL); 308 signal(SIGTERM, SIG_DFL); 309 signal(SIGALRM, SIG_DFL); 310 311 Proc *proc = (Proc *)ptyproc; 312 int err = 0; 313 // Don't use os_chdir() as that may buffer UI events unnecessarily. 314 if (proc->cwd && (err = uv_chdir(proc->cwd)) != 0) { 315 ELOG("chdir(%s) failed: %s", proc->cwd, uv_strerror(err)); 316 _exit(122); 317 } 318 319 const char *prog = proc_get_exepath(proc); 320 321 assert(proc->env); 322 environ = tv_dict_to_env(proc->env); 323 execvp(prog, proc->argv); 324 ELOG("execvp(%s) failed: %s", prog, strerror(errno)); 325 326 _exit(122); // 122 is EXEC_FAILED in the Vim source. 327 } 328 329 static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL 330 { 331 // Taken from pangoterm 332 termios->c_iflag = ICRNL|IXON; 333 termios->c_oflag = OPOST|ONLCR; 334 #ifdef TAB0 335 termios->c_oflag |= TAB0; 336 #endif 337 termios->c_cflag = CS8|CREAD; 338 termios->c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK; 339 340 // not using cfsetspeed, not available on all platforms 341 cfsetispeed(termios, 38400); 342 cfsetospeed(termios, 38400); 343 344 #ifdef IUTF8 345 termios->c_iflag |= IUTF8; 346 #endif 347 #ifdef NL0 348 termios->c_oflag |= NL0; 349 #endif 350 #ifdef CR0 351 termios->c_oflag |= CR0; 352 #endif 353 #ifdef BS0 354 termios->c_oflag |= BS0; 355 #endif 356 #ifdef VT0 357 termios->c_oflag |= VT0; 358 #endif 359 #ifdef FF0 360 termios->c_oflag |= FF0; 361 #endif 362 #ifdef ECHOCTL 363 termios->c_lflag |= ECHOCTL; 364 #endif 365 #ifdef ECHOKE 366 termios->c_lflag |= ECHOKE; 367 #endif 368 369 termios->c_cc[VINTR] = 0x1f & 'C'; 370 termios->c_cc[VQUIT] = 0x1f & '\\'; 371 termios->c_cc[VERASE] = 0x7f; 372 termios->c_cc[VKILL] = 0x1f & 'U'; 373 termios->c_cc[VEOF] = 0x1f & 'D'; 374 termios->c_cc[VEOL] = _POSIX_VDISABLE; 375 termios->c_cc[VEOL2] = _POSIX_VDISABLE; 376 termios->c_cc[VSTART] = 0x1f & 'Q'; 377 termios->c_cc[VSTOP] = 0x1f & 'S'; 378 termios->c_cc[VSUSP] = 0x1f & 'Z'; 379 #if !defined(__HAIKU__) 380 termios->c_cc[VREPRINT] = 0x1f & 'R'; 381 termios->c_cc[VWERASE] = 0x1f & 'W'; 382 termios->c_cc[VLNEXT] = 0x1f & 'V'; 383 #endif 384 termios->c_cc[VMIN] = 1; 385 termios->c_cc[VTIME] = 0; 386 } 387 388 static int set_duplicating_descriptor(int fd, uv_pipe_t *pipe) 389 FUNC_ATTR_NONNULL_ALL 390 { 391 int status = 0; // zero or negative error code (libuv convention) 392 int fd_dup = dup(fd); 393 if (fd_dup < 0) { 394 status = -errno; 395 ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno)); 396 return status; 397 } 398 399 if (os_set_cloexec(fd_dup) == -1) { 400 status = -errno; 401 ELOG("Failed to set CLOEXEC on duplicate fd"); 402 goto error; 403 } 404 405 status = uv_pipe_open(pipe, fd_dup); 406 if (status) { 407 ELOG("Failed to set pipe to descriptor %d: %s", 408 fd_dup, uv_strerror(status)); 409 goto error; 410 } 411 return status; 412 413 error: 414 close(fd_dup); 415 return status; 416 } 417 418 static void chld_handler(uv_signal_t *handle, int signum) 419 { 420 int stat = 0; 421 int pid; 422 423 Loop *loop = handle->loop->data; 424 425 for (size_t i = 0; i < kv_size(loop->children); i++) { 426 Proc *proc = kv_A(loop->children, i); 427 do { 428 pid = waitpid(proc->pid, &stat, WNOHANG|WUNTRACED|WCONTINUED); 429 } while (pid < 0 && errno == EINTR); 430 431 if (pid <= 0) { 432 continue; 433 } 434 435 if (WIFSTOPPED(stat)) { 436 proc->state_cb(proc, true, proc->data); 437 continue; 438 } 439 if (WIFCONTINUED(stat)) { 440 proc->state_cb(proc, false, proc->data); 441 continue; 442 } 443 444 if (WIFEXITED(stat)) { 445 proc->status = WEXITSTATUS(stat); 446 } else if (WIFSIGNALED(stat)) { 447 proc->status = 128 + WTERMSIG(stat); 448 } 449 proc->internal_exit_cb(proc); 450 } 451 } 452 453 PtyProc pty_proc_init(Loop *loop, void *data) 454 { 455 PtyProc rv = { 0 }; 456 rv.proc = proc_init(loop, kProcTypePty, data); 457 rv.width = 80; 458 rv.height = 24; 459 rv.tty_fd = -1; 460 return rv; 461 }