From 6ed9d47a6e0a28d163e1cf78e521e1282b725a87 Mon Sep 17 00:00:00 2001 From: Rui Abreu Ferreira Date: Sun, 5 Jun 2016 01:02:56 +0100 Subject: pty_process: split into plat-specific files (#3976) --- src/nvim/CMakeLists.txt | 3 + src/nvim/eval.c | 2 +- src/nvim/event/process.c | 2 +- src/nvim/event/pty_process.c | 240 ----------------------------------------- src/nvim/event/pty_process.h | 30 ------ src/nvim/os/pty_process.h | 9 ++ src/nvim/os/pty_process_unix.c | 239 ++++++++++++++++++++++++++++++++++++++++ src/nvim/os/pty_process_unix.h | 31 ++++++ src/nvim/os/pty_process_win.h | 28 +++++ 9 files changed, 312 insertions(+), 272 deletions(-) delete mode 100644 src/nvim/event/pty_process.c delete mode 100644 src/nvim/event/pty_process.h create mode 100644 src/nvim/os/pty_process.h create mode 100644 src/nvim/os/pty_process_unix.c create mode 100644 src/nvim/os/pty_process_unix.h create mode 100644 src/nvim/os/pty_process_win.h (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 5a3db74903..4aaae5172f 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -78,6 +78,9 @@ foreach(sfile ${NEOVIM_SOURCES}) if(${f} MATCHES "^(regexp_nfa.c)$") list(APPEND to_remove ${sfile}) endif() + if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$") + list(APPEND to_remove ${sfile}) + endif() endforeach() list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 84c96752c7..3cd53b841d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -77,7 +77,7 @@ #include "nvim/eval/decode.h" #include "nvim/os/os.h" #include "nvim/event/libuv_process.h" -#include "nvim/event/pty_process.h" +#include "nvim/os/pty_process.h" #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/event/time.h" diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 9bb62891c7..0a4cbe724e 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -9,7 +9,7 @@ #include "nvim/event/wstream.h" #include "nvim/event/process.h" #include "nvim/event/libuv_process.h" -#include "nvim/event/pty_process.h" +#include "nvim/os/pty_process.h" #include "nvim/globals.h" #include "nvim/log.h" diff --git a/src/nvim/event/pty_process.c b/src/nvim/event/pty_process.c deleted file mode 100644 index 8eef72f12f..0000000000 --- a/src/nvim/event/pty_process.c +++ /dev/null @@ -1,240 +0,0 @@ -// Some of the code came from pangoterm and libuv -#include -#include -#include - -#include -#include -#include -#include -#include - -// forkpty is not in POSIX, so headers are platform-specific -#if defined(__FreeBSD__) -# include -#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) -# include -#else -# include -#endif - -#include - -#include "nvim/lib/klist.h" - -#include "nvim/event/loop.h" -#include "nvim/event/rstream.h" -#include "nvim/event/wstream.h" -#include "nvim/event/process.h" -#include "nvim/event/pty_process.h" -#include "nvim/log.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/pty_process.c.generated.h" -#endif - -bool pty_process_spawn(PtyProcess *ptyproc) - FUNC_ATTR_NONNULL_ALL -{ - static struct termios termios; - if (!termios.c_cflag) { - init_termios(&termios); - } - - Process *proc = (Process *)ptyproc; - assert(!proc->err); - uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); - ptyproc->winsize = (struct winsize){ptyproc->height, ptyproc->width, 0, 0}; - uv_disable_stdio_inheritance(); - int master; - int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize); - - if (pid < 0) { - ELOG("forkpty failed: %s", strerror(errno)); - return false; - } else if (pid == 0) { - init_child(ptyproc); - abort(); - } - - // make sure the master file descriptor is non blocking - int master_status_flags = fcntl(master, F_GETFL); - if (master_status_flags == -1) { - ELOG("Failed to get master descriptor status flags: %s", strerror(errno)); - goto error; - } - if (fcntl(master, F_SETFL, master_status_flags | O_NONBLOCK) == -1) { - ELOG("Failed to make master descriptor non-blocking: %s", strerror(errno)); - goto error; - } - - if (proc->in && !set_duplicating_descriptor(master, &proc->in->uv.pipe)) { - goto error; - } - if (proc->out && !set_duplicating_descriptor(master, &proc->out->uv.pipe)) { - goto error; - } - - ptyproc->tty_fd = master; - proc->pid = pid; - return true; - -error: - close(master); - kill(pid, SIGKILL); - waitpid(pid, NULL, 0); - return false; -} - -void pty_process_resize(PtyProcess *ptyproc, uint16_t width, - uint16_t height) - FUNC_ATTR_NONNULL_ALL -{ - ptyproc->winsize = (struct winsize){height, width, 0, 0}; - ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize); -} - -void pty_process_close(PtyProcess *ptyproc) - FUNC_ATTR_NONNULL_ALL -{ - pty_process_close_master(ptyproc); - Process *proc = (Process *)ptyproc; - if (proc->internal_close_cb) { - proc->internal_close_cb(proc); - } -} - -void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL -{ - if (ptyproc->tty_fd >= 0) { - close(ptyproc->tty_fd); - ptyproc->tty_fd = -1; - } -} - -void pty_process_teardown(Loop *loop) -{ - uv_signal_stop(&loop->children_watcher); -} - -static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL -{ - unsetenv("COLUMNS"); - unsetenv("LINES"); - unsetenv("TERMCAP"); - unsetenv("COLORTERM"); - unsetenv("COLORFGBG"); - - signal(SIGCHLD, SIG_DFL); - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGALRM, SIG_DFL); - - setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); - execvp(ptyproc->process.argv[0], ptyproc->process.argv); - fprintf(stderr, "execvp failed: %s\n", strerror(errno)); -} - -static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL -{ - // Taken from pangoterm - termios->c_iflag = ICRNL|IXON; - termios->c_oflag = OPOST|ONLCR; -#ifdef TAB0 - termios->c_oflag |= TAB0; -#endif - termios->c_cflag = CS8|CREAD; - termios->c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK; - - cfsetspeed(termios, 38400); - -#ifdef IUTF8 - termios->c_iflag |= IUTF8; -#endif -#ifdef NL0 - termios->c_oflag |= NL0; -#endif -#ifdef CR0 - termios->c_oflag |= CR0; -#endif -#ifdef BS0 - termios->c_oflag |= BS0; -#endif -#ifdef VT0 - termios->c_oflag |= VT0; -#endif -#ifdef FF0 - termios->c_oflag |= FF0; -#endif -#ifdef ECHOCTL - termios->c_lflag |= ECHOCTL; -#endif -#ifdef ECHOKE - termios->c_lflag |= ECHOKE; -#endif - - termios->c_cc[VINTR] = 0x1f & 'C'; - termios->c_cc[VQUIT] = 0x1f & '\\'; - termios->c_cc[VERASE] = 0x7f; - termios->c_cc[VKILL] = 0x1f & 'U'; - termios->c_cc[VEOF] = 0x1f & 'D'; - termios->c_cc[VEOL] = _POSIX_VDISABLE; - termios->c_cc[VEOL2] = _POSIX_VDISABLE; - termios->c_cc[VSTART] = 0x1f & 'Q'; - termios->c_cc[VSTOP] = 0x1f & 'S'; - termios->c_cc[VSUSP] = 0x1f & 'Z'; - termios->c_cc[VREPRINT] = 0x1f & 'R'; - termios->c_cc[VWERASE] = 0x1f & 'W'; - termios->c_cc[VLNEXT] = 0x1f & 'V'; - termios->c_cc[VMIN] = 1; - termios->c_cc[VTIME] = 0; -} - -static bool set_duplicating_descriptor(int fd, uv_pipe_t *pipe) - FUNC_ATTR_NONNULL_ALL -{ - int fd_dup = dup(fd); - if (fd_dup < 0) { - ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno)); - return false; - } - int uv_result = uv_pipe_open(pipe, fd_dup); - if (uv_result) { - ELOG("Failed to set pipe to descriptor %d: %s", - fd_dup, uv_strerror(uv_result)); - close(fd_dup); - return false; - } - return true; -} - -static void chld_handler(uv_signal_t *handle, int signum) -{ - int stat = 0; - int pid; - - do { - pid = waitpid(-1, &stat, WNOHANG); - } while (pid < 0 && errno == EINTR); - - if (pid <= 0) { - return; - } - - Loop *loop = handle->loop->data; - - kl_iter(WatcherPtr, loop->children, current) { - Process *proc = (*current)->data; - if (proc->pid == pid) { - if (WIFEXITED(stat)) { - proc->status = WEXITSTATUS(stat); - } else if (WIFSIGNALED(stat)) { - proc->status = WTERMSIG(stat); - } - proc->internal_exit_cb(proc); - break; - } - } -} diff --git a/src/nvim/event/pty_process.h b/src/nvim/event/pty_process.h deleted file mode 100644 index 446d7fd3c8..0000000000 --- a/src/nvim/event/pty_process.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef NVIM_EVENT_PTY_PROCESS_H -#define NVIM_EVENT_PTY_PROCESS_H - -#include - -#include "nvim/event/process.h" - -typedef struct pty_process { - Process process; - char *term_name; - uint16_t width, height; - struct winsize winsize; - int tty_fd; -} PtyProcess; - -static inline PtyProcess pty_process_init(Loop *loop, void *data) -{ - PtyProcess rv; - rv.process = process_init(loop, kProcessTypePty, data); - rv.term_name = NULL; - rv.width = 80; - rv.height = 24; - rv.tty_fd = -1; - return rv; -} - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/pty_process.h.generated.h" -#endif -#endif // NVIM_EVENT_PTY_PROCESS_H diff --git a/src/nvim/os/pty_process.h b/src/nvim/os/pty_process.h new file mode 100644 index 0000000000..94923499ca --- /dev/null +++ b/src/nvim/os/pty_process.h @@ -0,0 +1,9 @@ +#ifndef NVIM_OS_PTY_PROCESS_H +#define NVIM_OS_PTY_PROCESS_H + +#ifdef WIN32 +# include "nvim/os/pty_process_win.h" +#else +# include "nvim/os/pty_process_unix.h" +#endif +#endif // NVIM_OS_PTY_PROCESS_H diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c new file mode 100644 index 0000000000..d0a38e663b --- /dev/null +++ b/src/nvim/os/pty_process_unix.c @@ -0,0 +1,239 @@ +// Some of the code came from pangoterm and libuv +#include +#include +#include + +#include +#include +#include +#include +#include + +// forkpty is not in POSIX, so headers are platform-specific +#if defined(__FreeBSD__) +# include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) +# include +#else +# include +#endif + +#include + +#include "nvim/lib/klist.h" + +#include "nvim/event/loop.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" +#include "nvim/event/process.h" +#include "nvim/os/pty_process_unix.h" +#include "nvim/log.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_unix.c.generated.h" +#endif + +bool pty_process_spawn(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + static struct termios termios; + if (!termios.c_cflag) { + init_termios(&termios); + } + + Process *proc = (Process *)ptyproc; + assert(!proc->err); + uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); + ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 }; + uv_disable_stdio_inheritance(); + int master; + int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize); + + if (pid < 0) { + ELOG("forkpty failed: %s", strerror(errno)); + return false; + } else if (pid == 0) { + init_child(ptyproc); + abort(); + } + + // make sure the master file descriptor is non blocking + int master_status_flags = fcntl(master, F_GETFL); + if (master_status_flags == -1) { + ELOG("Failed to get master descriptor status flags: %s", strerror(errno)); + goto error; + } + if (fcntl(master, F_SETFL, master_status_flags | O_NONBLOCK) == -1) { + ELOG("Failed to make master descriptor non-blocking: %s", strerror(errno)); + goto error; + } + + if (proc->in && !set_duplicating_descriptor(master, &proc->in->uv.pipe)) { + goto error; + } + if (proc->out && !set_duplicating_descriptor(master, &proc->out->uv.pipe)) { + goto error; + } + + ptyproc->tty_fd = master; + proc->pid = pid; + return true; + +error: + close(master); + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); + return false; +} + +void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) + FUNC_ATTR_NONNULL_ALL +{ + ptyproc->winsize = (struct winsize){ height, width, 0, 0 }; + ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize); +} + +void pty_process_close(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + pty_process_close_master(ptyproc); + Process *proc = (Process *)ptyproc; + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); + } +} + +void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->tty_fd >= 0) { + close(ptyproc->tty_fd); + ptyproc->tty_fd = -1; + } +} + +void pty_process_teardown(Loop *loop) +{ + uv_signal_stop(&loop->children_watcher); +} + +static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL +{ + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + unsetenv("COLORTERM"); + unsetenv("COLORFGBG"); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); + execvp(ptyproc->process.argv[0], ptyproc->process.argv); + fprintf(stderr, "execvp failed: %s\n", strerror(errno)); +} + +static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL +{ + // Taken from pangoterm + termios->c_iflag = ICRNL|IXON; + termios->c_oflag = OPOST|ONLCR; +#ifdef TAB0 + termios->c_oflag |= TAB0; +#endif + termios->c_cflag = CS8|CREAD; + termios->c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK; + + cfsetspeed(termios, 38400); + +#ifdef IUTF8 + termios->c_iflag |= IUTF8; +#endif +#ifdef NL0 + termios->c_oflag |= NL0; +#endif +#ifdef CR0 + termios->c_oflag |= CR0; +#endif +#ifdef BS0 + termios->c_oflag |= BS0; +#endif +#ifdef VT0 + termios->c_oflag |= VT0; +#endif +#ifdef FF0 + termios->c_oflag |= FF0; +#endif +#ifdef ECHOCTL + termios->c_lflag |= ECHOCTL; +#endif +#ifdef ECHOKE + termios->c_lflag |= ECHOKE; +#endif + + termios->c_cc[VINTR] = 0x1f & 'C'; + termios->c_cc[VQUIT] = 0x1f & '\\'; + termios->c_cc[VERASE] = 0x7f; + termios->c_cc[VKILL] = 0x1f & 'U'; + termios->c_cc[VEOF] = 0x1f & 'D'; + termios->c_cc[VEOL] = _POSIX_VDISABLE; + termios->c_cc[VEOL2] = _POSIX_VDISABLE; + termios->c_cc[VSTART] = 0x1f & 'Q'; + termios->c_cc[VSTOP] = 0x1f & 'S'; + termios->c_cc[VSUSP] = 0x1f & 'Z'; + termios->c_cc[VREPRINT] = 0x1f & 'R'; + termios->c_cc[VWERASE] = 0x1f & 'W'; + termios->c_cc[VLNEXT] = 0x1f & 'V'; + termios->c_cc[VMIN] = 1; + termios->c_cc[VTIME] = 0; +} + +static bool set_duplicating_descriptor(int fd, uv_pipe_t *pipe) + FUNC_ATTR_NONNULL_ALL +{ + int fd_dup = dup(fd); + if (fd_dup < 0) { + ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno)); + return false; + } + int uv_result = uv_pipe_open(pipe, fd_dup); + if (uv_result) { + ELOG("Failed to set pipe to descriptor %d: %s", + fd_dup, uv_strerror(uv_result)); + close(fd_dup); + return false; + } + return true; +} + +static void chld_handler(uv_signal_t *handle, int signum) +{ + int stat = 0; + int pid; + + do { + pid = waitpid(-1, &stat, WNOHANG); + } while (pid < 0 && errno == EINTR); + + if (pid <= 0) { + return; + } + + Loop *loop = handle->loop->data; + + kl_iter(WatcherPtr, loop->children, current) { + Process *proc = (*current)->data; + if (proc->pid == pid) { + if (WIFEXITED(stat)) { + proc->status = WEXITSTATUS(stat); + } else if (WIFSIGNALED(stat)) { + proc->status = WTERMSIG(stat); + } + proc->internal_exit_cb(proc); + break; + } + } +} diff --git a/src/nvim/os/pty_process_unix.h b/src/nvim/os/pty_process_unix.h new file mode 100644 index 0000000000..f7c57b3839 --- /dev/null +++ b/src/nvim/os/pty_process_unix.h @@ -0,0 +1,31 @@ +#ifndef NVIM_OS_PTY_PROCESS_UNIX_H +#define NVIM_OS_PTY_PROCESS_UNIX_H + +#include + +#include "nvim/event/process.h" + +typedef struct pty_process { + Process process; + char *term_name; + uint16_t width, height; + struct winsize winsize; + int tty_fd; +} PtyProcess; + +static inline PtyProcess pty_process_init(Loop *loop, void *data) +{ + PtyProcess rv; + rv.process = process_init(loop, kProcessTypePty, data); + rv.term_name = NULL; + rv.width = 80; + rv.height = 24; + rv.tty_fd = -1; + return rv; +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_unix.h.generated.h" +#endif + +#endif // NVIM_OS_PTY_PROCESS_UNIX_H diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h new file mode 100644 index 0000000000..20cc589925 --- /dev/null +++ b/src/nvim/os/pty_process_win.h @@ -0,0 +1,28 @@ +#ifndef NVIM_OS_PTY_PROCESS_WIN_H +#define NVIM_OS_PTY_PROCESS_WIN_H + +#include "nvim/event/libuv_process.h" + +typedef struct pty_process { + Process process; + char *term_name; + uint16_t width, height; +} PtyProcess; + +#define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) +#define pty_process_close(job) libuv_process_close((LibuvProcess *)job) +#define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) +#define pty_process_resize(job, width, height) +#define pty_process_teardown(loop) + +static inline PtyProcess pty_process_init(Loop *loop, void *data) +{ + PtyProcess rv; + rv.process = process_init(loop, kProcessTypePty, data); + rv.term_name = NULL; + rv.width = 80; + rv.height = 24; + return rv; +} + +#endif // NVIM_OS_PTY_PROCESS_WIN_H -- cgit