X-Git-Url: https://git.josue.xyz/?p=VSoRC%2F.git;a=blobdiff_plain;f=node_modules%2Fnode-pty%2Fsrc%2Funix%2Fpty.cc;fp=node_modules%2Fnode-pty%2Fsrc%2Funix%2Fpty.cc;h=d2f4c1131d7e91c714682e336848d764cc947620;hp=0000000000000000000000000000000000000000;hb=e79e4a5a87f3e84f7c1777f10a954453a69bf540;hpb=4339da12467b75fb8b6ca831f4bf0081c485ed2c diff --git a/node_modules/node-pty/src/unix/pty.cc b/node_modules/node-pty/src/unix/pty.cc new file mode 100644 index 0000000..d2f4c11 --- /dev/null +++ b/node_modules/node-pty/src/unix/pty.cc @@ -0,0 +1,708 @@ +/** + * Copyright (c) 2012-2015, Christopher Jeffrey (MIT License) + * Copyright (c) 2017, Daniel Imms (MIT License) + * + * pty.cc: + * This file is responsible for starting processes + * with pseudo-terminal file descriptors. + * + * See: + * man pty + * man tty_ioctl + * man termios + * man forkpty + */ + +/** + * Includes + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* forkpty */ +/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */ +#if defined(__GLIBC__) || defined(__CYGWIN__) +#include +#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#elif defined(__FreeBSD__) +#include +#elif defined(__sun) +#include /* for I_PUSH */ +#else +#include +#endif + +#include /* tcgetattr, tty_ioctl */ + +/* Some platforms name VWERASE and VDISCARD differently */ +#if !defined(VWERASE) && defined(VWERSE) +#define VWERASE VWERSE +#endif +#if !defined(VDISCARD) && defined(VDISCRD) +#define VDISCARD VDISCRD +#endif + +/* environ for execvpe */ +/* node/src/node_child_process.cc */ +#if defined(__APPLE__) && !TARGET_OS_IPHONE +#include +#define environ (*_NSGetEnviron()) +#else +extern char **environ; +#endif + +/* for pty_getproc */ +#if defined(__linux__) +#include +#include +#elif defined(__APPLE__) +#include +#include +#endif + +/** + * Structs + */ + +struct pty_baton { + Nan::Persistent cb; + int exit_code; + int signal_code; + pid_t pid; + uv_async_t async; + uv_thread_t tid; +}; + +/** + * Methods + */ + +NAN_METHOD(PtyFork); +NAN_METHOD(PtyOpen); +NAN_METHOD(PtyResize); +NAN_METHOD(PtyGetProc); + +/** + * Functions + */ + +static int +pty_execvpe(const char *, char **, char **); + +static int +pty_nonblock(int); + +static char * +pty_getproc(int, char *); + +static int +pty_openpty(int *, int *, char *, + const struct termios *, + const struct winsize *); + +static pid_t +pty_forkpty(int *, char *, + const struct termios *, + const struct winsize *); + +static void +pty_waitpid(void *); + +static void +pty_after_waitpid(uv_async_t *); + +static void +pty_after_close(uv_handle_t *); + +NAN_METHOD(PtyFork) { + Nan::HandleScope scope; + + if (info.Length() != 10 || + !info[0]->IsString() || + !info[1]->IsArray() || + !info[2]->IsArray() || + !info[3]->IsString() || + !info[4]->IsNumber() || + !info[5]->IsNumber() || + !info[6]->IsNumber() || + !info[7]->IsNumber() || + !info[8]->IsBoolean() || + !info[9]->IsFunction()) { + return Nan::ThrowError( + "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, onexit)"); + } + + // Make sure the process still listens to SIGINT + signal(SIGINT, SIG_DFL); + + // file + Nan::Utf8String file(info[0]); + + // args + int i = 0; + v8::Local argv_ = v8::Local::Cast(info[1]); + int argc = argv_->Length(); + int argl = argc + 1 + 1; + char **argv = new char*[argl]; + argv[0] = strdup(*file); + argv[argl-1] = NULL; + for (; i < argc; i++) { + Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked()); + argv[i+1] = strdup(*arg); + } + + // env + i = 0; + v8::Local env_ = v8::Local::Cast(info[2]); + int envc = env_->Length(); + char **env = new char*[envc+1]; + env[envc] = NULL; + for (; i < envc; i++) { + Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked()); + env[i] = strdup(*pair); + } + + // cwd + Nan::Utf8String cwd_(info[3]); + char *cwd = strdup(*cwd_); + + // size + struct winsize winp; + winp.ws_col = info[4]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + winp.ws_row = info[5]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + winp.ws_xpixel = 0; + winp.ws_ypixel = 0; + + // termios + struct termios t = termios(); + struct termios *term = &t; + term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT; + if (Nan::To(info[8]).FromJust()) { +#if defined(IUTF8) + term->c_iflag |= IUTF8; +#endif + } + term->c_oflag = OPOST | ONLCR; + term->c_cflag = CREAD | CS8 | HUPCL; + term->c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL; + + term->c_cc[VEOF] = 4; + term->c_cc[VEOL] = -1; + term->c_cc[VEOL2] = -1; + term->c_cc[VERASE] = 0x7f; + term->c_cc[VWERASE] = 23; + term->c_cc[VKILL] = 21; + term->c_cc[VREPRINT] = 18; + term->c_cc[VINTR] = 3; + term->c_cc[VQUIT] = 0x1c; + term->c_cc[VSUSP] = 26; + term->c_cc[VSTART] = 17; + term->c_cc[VSTOP] = 19; + term->c_cc[VLNEXT] = 22; + term->c_cc[VDISCARD] = 15; + term->c_cc[VMIN] = 1; + term->c_cc[VTIME] = 0; + + #if (__APPLE__) + term->c_cc[VDSUSP] = 25; + term->c_cc[VSTATUS] = 20; + #endif + + cfsetispeed(term, B38400); + cfsetospeed(term, B38400); + + // uid / gid + int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + + // fork the pty + int master = -1; + pid_t pid = pty_forkpty(&master, nullptr, term, &winp); + + if (pid) { + for (i = 0; i < argl; i++) free(argv[i]); + delete[] argv; + for (i = 0; i < envc; i++) free(env[i]); + delete[] env; + free(cwd); + } + + switch (pid) { + case -1: + return Nan::ThrowError("forkpty(3) failed."); + case 0: + if (strlen(cwd)) { + if (chdir(cwd) == -1) { + perror("chdir(2) failed."); + _exit(1); + } + } + + if (uid != -1 && gid != -1) { + if (setgid(gid) == -1) { + perror("setgid(2) failed."); + _exit(1); + } + if (setuid(uid) == -1) { + perror("setuid(2) failed."); + _exit(1); + } + } + + pty_execvpe(argv[0], argv, env); + + perror("execvp(3) failed."); + _exit(1); + default: + if (pty_nonblock(master) == -1) { + return Nan::ThrowError("Could not set master fd to nonblocking."); + } + + v8::Local obj = Nan::New(); + Nan::Set(obj, + Nan::New("fd").ToLocalChecked(), + Nan::New(master)); + Nan::Set(obj, + Nan::New("pid").ToLocalChecked(), + Nan::New(pid)); + Nan::Set(obj, + Nan::New("pty").ToLocalChecked(), + Nan::New(ptsname(master)).ToLocalChecked()); + + pty_baton *baton = new pty_baton(); + baton->exit_code = 0; + baton->signal_code = 0; + baton->cb.Reset(v8::Local::Cast(info[9])); + baton->pid = pid; + baton->async.data = baton; + + uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid); + + uv_thread_create(&baton->tid, pty_waitpid, static_cast(baton)); + + return info.GetReturnValue().Set(obj); + } + + return info.GetReturnValue().SetUndefined(); +} + +NAN_METHOD(PtyOpen) { + Nan::HandleScope scope; + + if (info.Length() != 2 || + !info[0]->IsNumber() || + !info[1]->IsNumber()) { + return Nan::ThrowError("Usage: pty.open(cols, rows)"); + } + + // size + struct winsize winp; + winp.ws_col = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + winp.ws_row = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + winp.ws_xpixel = 0; + winp.ws_ypixel = 0; + + // pty + int master, slave; + int ret = pty_openpty(&master, &slave, nullptr, NULL, &winp); + + if (ret == -1) { + return Nan::ThrowError("openpty(3) failed."); + } + + if (pty_nonblock(master) == -1) { + return Nan::ThrowError("Could not set master fd to nonblocking."); + } + + if (pty_nonblock(slave) == -1) { + return Nan::ThrowError("Could not set slave fd to nonblocking."); + } + + v8::Local obj = Nan::New(); + Nan::Set(obj, + Nan::New("master").ToLocalChecked(), + Nan::New(master)); + Nan::Set(obj, + Nan::New("slave").ToLocalChecked(), + Nan::New(slave)); + Nan::Set(obj, + Nan::New("pty").ToLocalChecked(), + Nan::New(ptsname(master)).ToLocalChecked()); + + return info.GetReturnValue().Set(obj); +} + +NAN_METHOD(PtyResize) { + Nan::HandleScope scope; + + if (info.Length() != 3 || + !info[0]->IsNumber() || + !info[1]->IsNumber() || + !info[2]->IsNumber()) { + return Nan::ThrowError("Usage: pty.resize(fd, cols, rows)"); + } + + int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + + struct winsize winp; + winp.ws_col = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + winp.ws_row = info[2]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + winp.ws_xpixel = 0; + winp.ws_ypixel = 0; + + if (ioctl(fd, TIOCSWINSZ, &winp) == -1) { + switch (errno) { + case EBADF: return Nan::ThrowError("ioctl(2) failed, EBADF"); + case EFAULT: return Nan::ThrowError("ioctl(2) failed, EFAULT"); + case EINVAL: return Nan::ThrowError("ioctl(2) failed, EINVAL"); + case ENOTTY: return Nan::ThrowError("ioctl(2) failed, ENOTTY"); + } + return Nan::ThrowError("ioctl(2) failed"); + } + + return info.GetReturnValue().SetUndefined(); +} + +/** + * Foreground Process Name + */ +NAN_METHOD(PtyGetProc) { + Nan::HandleScope scope; + + if (info.Length() != 2 || + !info[0]->IsNumber() || + !info[1]->IsString()) { + return Nan::ThrowError("Usage: pty.process(fd, tty)"); + } + + int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(); + + Nan::Utf8String tty_(info[1]); + char *tty = strdup(*tty_); + char *name = pty_getproc(fd, tty); + free(tty); + + if (name == NULL) { + return info.GetReturnValue().SetUndefined(); + } + + v8::Local name_ = Nan::New(name).ToLocalChecked(); + free(name); + return info.GetReturnValue().Set(name_); +} + +/** + * execvpe + */ + +// execvpe(3) is not portable. +// http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html +static int +pty_execvpe(const char *file, char **argv, char **envp) { + char **old = environ; + environ = envp; + int ret = execvp(file, argv); + environ = old; + return ret; +} + +/** + * Nonblocking FD + */ + +static int +pty_nonblock(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) return -1; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +/** + * pty_waitpid + * Wait for SIGCHLD to read exit status. + */ + +static void +pty_waitpid(void *data) { + int ret; + int stat_loc; + + pty_baton *baton = static_cast(data); + + errno = 0; + + if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) { + if (ret == -1 && errno == EINTR) { + return pty_waitpid(baton); + } + if (ret == -1 && errno == ECHILD) { + // XXX node v0.8.x seems to have this problem. + // waitpid is already handled elsewhere. + ; + } else { + assert(false); + } + } + + if (WIFEXITED(stat_loc)) { + baton->exit_code = WEXITSTATUS(stat_loc); // errno? + } + + if (WIFSIGNALED(stat_loc)) { + baton->signal_code = WTERMSIG(stat_loc); + } + + uv_async_send(&baton->async); +} + +/** + * pty_after_waitpid + * Callback after exit status has been read. + */ + +static void +pty_after_waitpid(uv_async_t *async) { + Nan::HandleScope scope; + pty_baton *baton = static_cast(async->data); + + v8::Local argv[] = { + Nan::New(baton->exit_code), + Nan::New(baton->signal_code), + }; + + v8::Local cb = Nan::New(baton->cb); + baton->cb.Reset(); + memset(&baton->cb, -1, sizeof(baton->cb)); + Nan::AsyncResource resource("pty_after_waitpid"); + resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, 2, argv); + + uv_close((uv_handle_t *)async, pty_after_close); +} + +/** + * pty_after_close + * uv_close() callback - free handle data + */ + +static void +pty_after_close(uv_handle_t *handle) { + uv_async_t *async = (uv_async_t *)handle; + pty_baton *baton = static_cast(async->data); + delete baton; +} + +/** + * pty_getproc + * Taken from tmux. + */ + +// Taken from: tmux (http://tmux.sourceforge.net/) +// Copyright (c) 2009 Nicholas Marriott +// Copyright (c) 2009 Joshua Elsasser +// Copyright (c) 2009 Todd Carson +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER +// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#if defined(__linux__) + +static char * +pty_getproc(int fd, char *tty) { + FILE *f; + char *path, *buf; + size_t len; + int ch; + pid_t pgrp; + int r; + + if ((pgrp = tcgetpgrp(fd)) == -1) { + return NULL; + } + + r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp); + if (r == -1 || path == NULL) return NULL; + + if ((f = fopen(path, "r")) == NULL) { + free(path); + return NULL; + } + + free(path); + + len = 0; + buf = NULL; + while ((ch = fgetc(f)) != EOF) { + if (ch == '\0') break; + buf = (char *)realloc(buf, len + 2); + if (buf == NULL) return NULL; + buf[len++] = ch; + } + + if (buf != NULL) { + buf[len] = '\0'; + } + + fclose(f); + return buf; +} + +#elif defined(__APPLE__) + +static char * +pty_getproc(int fd, char *tty) { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 }; + size_t size; + struct kinfo_proc kp; + + if ((mib[3] = tcgetpgrp(fd)) == -1) { + return NULL; + } + + size = sizeof kp; + if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) { + return NULL; + } + + if (*kp.kp_proc.p_comm == '\0') { + return NULL; + } + + return strdup(kp.kp_proc.p_comm); +} + +#else + +static char * +pty_getproc(int fd, char *tty) { + return NULL; +} + +#endif + +/** + * openpty(3) / forkpty(3) + */ + +static int +pty_openpty(int *amaster, + int *aslave, + char *name, + const struct termios *termp, + const struct winsize *winp) { +#if defined(__sun) + char *slave_name; + int slave; + int master = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (master == -1) return -1; + if (amaster) *amaster = master; + + if (grantpt(master) == -1) goto err; + if (unlockpt(master) == -1) goto err; + + slave_name = ptsname(master); + if (slave_name == NULL) goto err; + if (name) strcpy(name, slave_name); + + slave = open(slave_name, O_RDWR | O_NOCTTY); + if (slave == -1) goto err; + if (aslave) *aslave = slave; + + ioctl(slave, I_PUSH, "ptem"); + ioctl(slave, I_PUSH, "ldterm"); + ioctl(slave, I_PUSH, "ttcompat"); + + if (termp) tcsetattr(slave, TCSAFLUSH, termp); + if (winp) ioctl(slave, TIOCSWINSZ, winp); + + return 0; + +err: + close(master); + return -1; +#else + return openpty(amaster, aslave, name, (termios *)termp, (winsize *)winp); +#endif +} + +static pid_t +pty_forkpty(int *amaster, + char *name, + const struct termios *termp, + const struct winsize *winp) { +#if defined(__sun) + int master, slave; + + int ret = pty_openpty(&master, &slave, name, termp, winp); + if (ret == -1) return -1; + if (amaster) *amaster = master; + + pid_t pid = fork(); + + switch (pid) { + case -1: + close(master); + close(slave); + return -1; + case 0: + close(master); + + setsid(); + +#if defined(TIOCSCTTY) + // glibc does this + if (ioctl(slave, TIOCSCTTY, NULL) == -1) { + _exit(1); + } +#endif + + dup2(slave, 0); + dup2(slave, 1); + dup2(slave, 2); + + if (slave > 2) close(slave); + + return 0; + default: + close(slave); + return pid; + } + + return -1; +#else + return forkpty(amaster, name, (termios *)termp, (winsize *)winp); +#endif +} + +/** + * Init + */ + +NAN_MODULE_INIT(init) { + Nan::HandleScope scope; + Nan::Export(target, "fork", PtyFork); + Nan::Export(target, "open", PtyOpen); + Nan::Export(target, "resize", PtyResize); + Nan::Export(target, "process", PtyGetProc); +} + +NODE_MODULE(pty, init)