-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
job: Add options to spawn jobs connected to pseudo terminals
- Loading branch information
Showing
10 changed files
with
407 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// Some of the code came from st(http://st.suckless.org/) and libuv | ||
#include <stdbool.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include <unistd.h> | ||
#include <termios.h> | ||
#include <sys/types.h> | ||
#include <sys/wait.h> | ||
|
||
// openpty is not in POSIX, so headers are platform-specific | ||
#if defined(__linux) | ||
#include <pty.h> | ||
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) | ||
#include <util.h> | ||
#elif defined(__FreeBSD__) || defined(__DragonFly__) | ||
#include <libutil.h> | ||
#endif | ||
|
||
#include <uv.h> | ||
|
||
#include "nvim/os/uv_helpers.h" | ||
#include "nvim/os/job.h" | ||
#include "nvim/os/job_defs.h" | ||
#include "nvim/os/job_private.h" | ||
#include "nvim/os/pty_process.h" | ||
#include "nvim/memory.h" | ||
|
||
#ifdef INCLUDE_GENERATED_DECLARATIONS | ||
# include "os/pty_process.c.generated.h" | ||
#endif | ||
|
||
typedef struct { | ||
struct winsize winsize; | ||
uv_pipe_t proc_stdin, proc_stdout, proc_stderr; | ||
uv_signal_t schld; | ||
} PtyProcess; | ||
|
||
void pty_process_init(Job *job) | ||
{ | ||
PtyProcess *ptyproc = xmalloc(sizeof(PtyProcess)); | ||
|
||
if (job->opts.writable) { | ||
uv_pipe_init(uv_default_loop(), &ptyproc->proc_stdin, 0); | ||
ptyproc->proc_stdin.data = NULL; | ||
} | ||
|
||
if (job->opts.stdout_cb) { | ||
uv_pipe_init(uv_default_loop(), &ptyproc->proc_stdout, 0); | ||
ptyproc->proc_stdout.data = NULL; | ||
} | ||
|
||
if (job->opts.stderr_cb) { | ||
uv_pipe_init(uv_default_loop(), &ptyproc->proc_stderr, 0); | ||
ptyproc->proc_stderr.data = NULL; | ||
} | ||
|
||
job->proc_stdin = (uv_stream_t *)&ptyproc->proc_stdin; | ||
job->proc_stdout = (uv_stream_t *)&ptyproc->proc_stdout; | ||
job->proc_stderr = (uv_stream_t *)&ptyproc->proc_stderr; | ||
job->process = ptyproc; | ||
} | ||
|
||
void pty_process_destroy(Job *job) | ||
{ | ||
free(job->opts.term_name); | ||
free(job->process); | ||
job->process = NULL; | ||
} | ||
|
||
bool pty_process_spawn(Job *job) | ||
{ | ||
int master, slave; | ||
PtyProcess *ptyproc = job->process; | ||
ptyproc->winsize = (struct winsize){job->opts.height, job->opts.width, 0, 0}; | ||
|
||
if (openpty(&master, &slave, NULL, NULL, &ptyproc->winsize)) { | ||
return false; | ||
} | ||
|
||
int pid = fork(); | ||
|
||
if (pid < 0) { | ||
return false; | ||
} else if (pid == 0) { | ||
close(master); | ||
init_child(job, slave); | ||
abort(); | ||
} | ||
|
||
close(slave); | ||
// make sure the master file descriptor is non blocking | ||
fcntl(master, F_SETFL, fcntl(master, F_GETFL) | O_NONBLOCK); | ||
|
||
if (job->opts.writable) { | ||
uv_pipe_open(&ptyproc->proc_stdin, dup(master)); | ||
} | ||
|
||
if (job->opts.stdout_cb) { | ||
uv_pipe_open(&ptyproc->proc_stdout, dup(master)); | ||
} | ||
|
||
if (job->opts.stderr_cb) { | ||
uv_pipe_open(&ptyproc->proc_stderr, dup(master)); | ||
} | ||
|
||
close(master); | ||
uv_signal_init(uv_default_loop(), &ptyproc->schld); | ||
uv_signal_start(&ptyproc->schld, chld_handler, SIGCHLD); | ||
ptyproc->schld.data = job; | ||
job->pid = pid; | ||
return true; | ||
} | ||
|
||
void pty_process_close(Job *job) | ||
{ | ||
job_close_streams(job); | ||
job_decref(job); | ||
} | ||
|
||
static void init_child(Job *job, int slave) | ||
{ | ||
// child, become session leader | ||
setsid(); | ||
// close stdin/stdout/stderr | ||
close(STDIN_FILENO); | ||
close(STDOUT_FILENO); | ||
close(STDERR_FILENO); | ||
// and reopen connected to the slave side of the pty or to /dev/null if | ||
// it should be ignored | ||
if (job->opts.writable) { | ||
dup(slave); | ||
} else { | ||
open("/dev/null", O_RDONLY); | ||
} | ||
if (job->opts.stdout_cb) { | ||
dup(slave); | ||
} else { | ||
open("/dev/null", O_RDWR); | ||
} | ||
if (job->opts.stderr_cb) { | ||
dup(slave); | ||
} else { | ||
open("/dev/null", O_RDWR); | ||
} | ||
|
||
if (ioctl(slave, TIOCSCTTY, NULL) < 0) { | ||
fprintf(stderr, "ioctl TIOCSTTY failed: %s\n", strerror(errno)); | ||
abort(); | ||
} | ||
|
||
close(slave); | ||
// Call uv_close on every active handle | ||
uv_walk(uv_default_loop(), walk_cb, NULL); | ||
// Run the event loop until all handles are successfully closed | ||
while (uv_loop_close(uv_default_loop())) { | ||
uv_run(uv_default_loop(), UV_RUN_ONCE); | ||
} | ||
|
||
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", job->opts.term_name ? job->opts.term_name : "ansi", 1); | ||
execvp(job->opts.argv[0], job->opts.argv); | ||
fprintf(stderr, "execvp failed: %s\n", strerror(errno)); | ||
abort(); | ||
} | ||
|
||
static void walk_cb(uv_handle_t *handle, void *arg) { | ||
if (!uv_is_closing(handle)) { | ||
uv_close(handle, NULL); | ||
} | ||
} | ||
|
||
static void chld_handler(uv_signal_t *handle, int signum) | ||
{ | ||
Job *job = handle->data; | ||
int stat = 0; | ||
|
||
if (waitpid(job->pid, &stat, 0) < 0) { | ||
fprintf(stderr, "Waiting for pid %d failed: %s\n", job->pid, | ||
strerror(errno)); | ||
return; | ||
} | ||
|
||
if (WIFEXITED(stat)) { | ||
job->status = WEXITSTATUS(stat); | ||
pty_process_close(job); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#ifndef NVIM_OS_PTY_PROCESS_H | ||
#define NVIM_OS_PTY_PROCESS_H | ||
|
||
#ifdef INCLUDE_GENERATED_DECLARATIONS | ||
# include "os/pty_process.h.generated.h" | ||
#endif | ||
#endif // NVIM_OS_PTY_PROCESS_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
add_executable(tty-test tty-test.c) | ||
target_link_libraries(tty-test ${LIBUV_LIBRARIES}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.