aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/pty_process.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os/pty_process.c')
-rw-r--r--src/nvim/os/pty_process.c89
1 files changed, 75 insertions, 14 deletions
diff --git a/src/nvim/os/pty_process.c b/src/nvim/os/pty_process.c
index bd7247c741..c135efc6d3 100644
--- a/src/nvim/os/pty_process.c
+++ b/src/nvim/os/pty_process.c
@@ -20,6 +20,7 @@
#include <uv.h>
+#include "nvim/func_attr.h"
#include "nvim/os/job.h"
#include "nvim/os/job_defs.h"
#include "nvim/os/job_private.h"
@@ -37,9 +38,10 @@ typedef struct {
int tty_fd;
} PtyProcess;
-void pty_process_init(Job *job)
+void pty_process_init(Job *job) FUNC_ATTR_NONNULL_ALL
{
PtyProcess *ptyproc = xmalloc(sizeof(PtyProcess));
+ ptyproc->tty_fd = -1;
if (job->opts.writable) {
uv_pipe_init(uv_default_loop(), &ptyproc->proc_stdin, 0);
@@ -62,14 +64,35 @@ void pty_process_init(Job *job)
job->process = ptyproc;
}
-void pty_process_destroy(Job *job)
+void pty_process_destroy(Job *job) FUNC_ATTR_NONNULL_ALL
{
free(job->opts.term_name);
free(job->process);
job->process = NULL;
}
-bool pty_process_spawn(Job *job)
+static bool set_pipe_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 const unsigned int KILL_RETRIES = 5;
+static const unsigned int KILL_TIMEOUT = 2; // seconds
+
+bool pty_process_spawn(Job *job) FUNC_ATTR_NONNULL_ALL
{
int master;
PtyProcess *ptyproc = job->process;
@@ -88,18 +111,29 @@ bool pty_process_spawn(Job *job)
}
// make sure the master file descriptor is non blocking
- fcntl(master, F_SETFL, fcntl(master, F_GETFL) | O_NONBLOCK);
+ 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 (job->opts.writable) {
- uv_pipe_open(&ptyproc->proc_stdin, dup(master));
+ if (job->opts.writable
+ && !set_pipe_duplicating_descriptor(master, &ptyproc->proc_stdin)) {
+ goto error;
}
- if (job->opts.stdout_cb) {
- uv_pipe_open(&ptyproc->proc_stdout, dup(master));
+ if (job->opts.stdout_cb
+ && !set_pipe_duplicating_descriptor(master, &ptyproc->proc_stdout)) {
+ goto error;
}
- if (job->opts.stderr_cb) {
- uv_pipe_open(&ptyproc->proc_stderr, dup(master));
+ if (job->opts.stderr_cb
+ && !set_pipe_duplicating_descriptor(master, &ptyproc->proc_stderr)) {
+ goto error;
}
uv_signal_init(uv_default_loop(), &ptyproc->schld);
@@ -108,25 +142,52 @@ bool pty_process_spawn(Job *job)
ptyproc->tty_fd = master;
job->pid = pid;
return true;
+
+error:
+ close(master);
+
+ // terminate spawned process
+ kill(pid, SIGTERM);
+ int status, child;
+ unsigned int try = 0;
+ while (try++ < KILL_RETRIES && !(child = waitpid(pid, &status, WNOHANG))) {
+ sleep(KILL_TIMEOUT);
+ }
+ if (child != pid) {
+ kill(pid, SIGKILL);
+ }
+
+ return false;
}
-void pty_process_close(Job *job)
+void pty_process_close(Job *job) FUNC_ATTR_NONNULL_ALL
{
PtyProcess *ptyproc = job->process;
uv_signal_stop(&ptyproc->schld);
uv_close((uv_handle_t *)&ptyproc->schld, NULL);
+ pty_process_close_master(job);
job_close_streams(job);
job_decref(job);
}
+void pty_process_close_master(Job *job) FUNC_ATTR_NONNULL_ALL
+{
+ PtyProcess *ptyproc = job->process;
+ if (ptyproc->tty_fd >= 0) {
+ close(ptyproc->tty_fd);
+ ptyproc->tty_fd = -1;
+ }
+}
+
void pty_process_resize(Job *job, uint16_t width, uint16_t height)
+ FUNC_ATTR_NONNULL_ALL
{
PtyProcess *ptyproc = job->process;
ptyproc->winsize = (struct winsize){height, width, 0, 0};
ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize);
}
-static void init_child(Job *job)
+static void init_child(Job *job) FUNC_ATTR_NONNULL_ALL
{
unsetenv("COLUMNS");
unsetenv("LINES");
@@ -146,7 +207,7 @@ static void init_child(Job *job)
fprintf(stderr, "execvp failed: %s\n", strerror(errno));
}
-static void chld_handler(uv_signal_t *handle, int signum)
+static void chld_handler(uv_signal_t *handle, int signum) FUNC_ATTR_NONNULL_ALL
{
Job *job = handle->data;
int stat = 0;
@@ -171,7 +232,7 @@ static void chld_handler(uv_signal_t *handle, int signum)
pty_process_close(job);
}
-static void init_termios(struct termios *termios)
+static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
{
memset(termios, 0, sizeof(struct termios));
// Taken from pangoterm