Skip to content

Instantly share code, notes, and snippets.

@Qix-
Last active January 21, 2020 06:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Qix-/25f4a226627f84e9d622a1f1526864c9 to your computer and use it in GitHub Desktop.
Save Qix-/25f4a226627f84e9d622a1f1526864c9 to your computer and use it in GitHub Desktop.
Test openpty() (pseudo-terminal) integration with libuv
/*
Simple testbed for checking TTY status. Use with the other program if you'd like.
*/
#include <unistd.h>
#include <stdio.h>
static void check(const char *name, int fd) {
if (isatty(fd)) {
printf("%s IS a tty: %s\n", name, ttyname(fd));
} else {
printf("%s IS NOT a tty\n", name);
}
}
int main(void) {
check("stdin", 0);
check("stdout", 1);
check("stderr", 2);
return 0;
}
/*
Tests uv_spawn() with a process that uses a pseudo-terminal to write to stderr.
Fun tests include:
cc -o uv_pty uv_pty.c -luv_a
./uv_pty node -e 'console.log(process.stderr.isTTY); console.error("hello stderr");' 2<NUL
*/
#include <uv.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <util.h>
#include <sys/types.h>
#include <sys/uio.h>
extern char **environ;
static int uv_perror(int r, const char *msg) {
printf("%s: %s (%s)\n", msg, uv_strerror(r), uv_err_name(r));
return 1;
}
static void on_exit(uv_process_t *proc, long long stat, int sig) {
printf("\n(process %d exited with status %lld and sig %d)\n", proc->pid, stat, sig);
}
static void on_polled(uv_poll_t *poller, int status, int events) {
if (status != 0) {
uv_perror(status, "could not poll pty");
return;
}
if (events & UV_READABLE) {
char buf[1024];
ssize_t r = read((int) poller->data, buf, sizeof(buf));
if (r == 0) {
// EOF
puts("encountered eof");
int res = uv_poll_stop(poller);
if (res != 0) {
uv_perror(res, "could not stop poller upon EOF");
return;
}
} else if (r < 0) {
// error
perror("could not read from pty");
int res = uv_poll_stop(poller);
if (res != 0) {
uv_perror(res, "could not stop poller upon error");
}
return;
} else {
// success
printf("STDERR: %.*s\n", (int) r, buf);
}
}
if (events & UV_DISCONNECT) {
puts("encountered disconnect");
int res = uv_poll_stop(poller);
if (res != 0) {
uv_perror(res, "could not stop poller upon disconnect");
}
}
}
int main(int argc, char *argv[]) {
uv_loop_t *loop = uv_default_loop();
int amaster = 0;
int aslave = 0;
int r = openpty(
&amaster,
&aslave,
NULL,
NULL,
NULL
);
if (r == -1) {
perror("could not open pty");
return 1;
}
uv_poll_t poller;
poller.data = (void *) (size_t) amaster;
uv_poll_init(loop, &poller, amaster);
uv_poll_start(&poller, UV_READABLE | UV_DISCONNECT, &on_polled);
uv_unref((uv_handle_t *) &poller); // total hack. you should do proper cleaning up.
uv_process_options_t opts;
char **argv_n = malloc(sizeof(*argv_n) * argc);
for (int i = 0; i < argc; i++) {
argv_n[i] = argv[i + 1];
}
argv[argc] = 0;
opts.args = argv_n;
static char wdbuf[PATH_MAX];
opts.cwd = getcwd(wdbuf, sizeof(wdbuf));
opts.env = environ;
opts.exit_cb = &on_exit;
opts.file = argv_n[0];
opts.flags = UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
opts.stdio_count = 3;
static uv_stdio_container_t child_stdio[3];
child_stdio[0].flags = UV_IGNORE;
child_stdio[1].flags = UV_INHERIT_FD;
child_stdio[1].data.fd = 1;
child_stdio[2].flags = UV_INHERIT_FD;
child_stdio[2].data.fd = aslave;
opts.stdio = child_stdio;
uv_process_t proc;
r = uv_spawn(loop, &proc, &opts);
if (r != 0) {
return uv_perror(r, "could not spawn process");
}
return uv_run(loop, UV_RUN_DEFAULT);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment