installed pty
[VSoRC/.git] / node_modules / node-pty / src / unix / pty.cc
1 /**
2  * Copyright (c) 2012-2015, Christopher Jeffrey (MIT License)
3  * Copyright (c) 2017, Daniel Imms (MIT License)
4  *
5  * pty.cc:
6  *   This file is responsible for starting processes
7  *   with pseudo-terminal file descriptors.
8  *
9  * See:
10  *   man pty
11  *   man tty_ioctl
12  *   man termios
13  *   man forkpty
14  */
15
16 /**
17  * Includes
18  */
19
20 #include <nan.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/ioctl.h>
29 #include <sys/wait.h>
30 #include <fcntl.h>
31
32 /* forkpty */
33 /* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */
34 #if defined(__GLIBC__) || defined(__CYGWIN__)
35 #include <pty.h>
36 #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
37 #include <util.h>
38 #elif defined(__FreeBSD__)
39 #include <libutil.h>
40 #elif defined(__sun)
41 #include <stropts.h> /* for I_PUSH */
42 #else
43 #include <pty.h>
44 #endif
45
46 #include <termios.h> /* tcgetattr, tty_ioctl */
47
48 /* Some platforms name VWERASE and VDISCARD differently */
49 #if !defined(VWERASE) && defined(VWERSE)
50 #define VWERASE VWERSE
51 #endif
52 #if !defined(VDISCARD) && defined(VDISCRD)
53 #define VDISCARD        VDISCRD
54 #endif
55
56 /* environ for execvpe */
57 /* node/src/node_child_process.cc */
58 #if defined(__APPLE__) && !TARGET_OS_IPHONE
59 #include <crt_externs.h>
60 #define environ (*_NSGetEnviron())
61 #else
62 extern char **environ;
63 #endif
64
65 /* for pty_getproc */
66 #if defined(__linux__)
67 #include <stdio.h>
68 #include <stdint.h>
69 #elif defined(__APPLE__)
70 #include <sys/sysctl.h>
71 #include <libproc.h>
72 #endif
73
74 /**
75  * Structs
76  */
77
78 struct pty_baton {
79   Nan::Persistent<v8::Function> cb;
80   int exit_code;
81   int signal_code;
82   pid_t pid;
83   uv_async_t async;
84   uv_thread_t tid;
85 };
86
87 /**
88  * Methods
89  */
90
91 NAN_METHOD(PtyFork);
92 NAN_METHOD(PtyOpen);
93 NAN_METHOD(PtyResize);
94 NAN_METHOD(PtyGetProc);
95
96 /**
97  * Functions
98  */
99
100 static int
101 pty_execvpe(const char *, char **, char **);
102
103 static int
104 pty_nonblock(int);
105
106 static char *
107 pty_getproc(int, char *);
108
109 static int
110 pty_openpty(int *, int *, char *,
111             const struct termios *,
112             const struct winsize *);
113
114 static pid_t
115 pty_forkpty(int *, char *,
116             const struct termios *,
117             const struct winsize *);
118
119 static void
120 pty_waitpid(void *);
121
122 static void
123 pty_after_waitpid(uv_async_t *);
124
125 static void
126 pty_after_close(uv_handle_t *);
127
128 NAN_METHOD(PtyFork) {
129   Nan::HandleScope scope;
130
131   if (info.Length() != 10 ||
132       !info[0]->IsString() ||
133       !info[1]->IsArray() ||
134       !info[2]->IsArray() ||
135       !info[3]->IsString() ||
136       !info[4]->IsNumber() ||
137       !info[5]->IsNumber() ||
138       !info[6]->IsNumber() ||
139       !info[7]->IsNumber() ||
140       !info[8]->IsBoolean() ||
141       !info[9]->IsFunction()) {
142     return Nan::ThrowError(
143         "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, onexit)");
144   }
145
146   // Make sure the process still listens to SIGINT
147   signal(SIGINT, SIG_DFL);
148
149   // file
150   Nan::Utf8String file(info[0]);
151
152   // args
153   int i = 0;
154   v8::Local<v8::Array> argv_ = v8::Local<v8::Array>::Cast(info[1]);
155   int argc = argv_->Length();
156   int argl = argc + 1 + 1;
157   char **argv = new char*[argl];
158   argv[0] = strdup(*file);
159   argv[argl-1] = NULL;
160   for (; i < argc; i++) {
161     Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
162     argv[i+1] = strdup(*arg);
163   }
164
165   // env
166   i = 0;
167   v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
168   int envc = env_->Length();
169   char **env = new char*[envc+1];
170   env[envc] = NULL;
171   for (; i < envc; i++) {
172     Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
173     env[i] = strdup(*pair);
174   }
175
176   // cwd
177   Nan::Utf8String cwd_(info[3]);
178   char *cwd = strdup(*cwd_);
179
180   // size
181   struct winsize winp;
182   winp.ws_col = info[4]->IntegerValue(Nan::GetCurrentContext()).FromJust();
183   winp.ws_row = info[5]->IntegerValue(Nan::GetCurrentContext()).FromJust();
184   winp.ws_xpixel = 0;
185   winp.ws_ypixel = 0;
186
187   // termios
188   struct termios t = termios();
189   struct termios *term = &t;
190   term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
191   if (Nan::To<bool>(info[8]).FromJust()) {
192 #if defined(IUTF8)
193     term->c_iflag |= IUTF8;
194 #endif
195   }
196   term->c_oflag = OPOST | ONLCR;
197   term->c_cflag = CREAD | CS8 | HUPCL;
198   term->c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL;
199
200   term->c_cc[VEOF] = 4;
201   term->c_cc[VEOL] = -1;
202   term->c_cc[VEOL2] = -1;
203   term->c_cc[VERASE] = 0x7f;
204   term->c_cc[VWERASE] = 23;
205   term->c_cc[VKILL] = 21;
206   term->c_cc[VREPRINT] = 18;
207   term->c_cc[VINTR] = 3;
208   term->c_cc[VQUIT] = 0x1c;
209   term->c_cc[VSUSP] = 26;
210   term->c_cc[VSTART] = 17;
211   term->c_cc[VSTOP] = 19;
212   term->c_cc[VLNEXT] = 22;
213   term->c_cc[VDISCARD] = 15;
214   term->c_cc[VMIN] = 1;
215   term->c_cc[VTIME] = 0;
216
217   #if (__APPLE__)
218   term->c_cc[VDSUSP] = 25;
219   term->c_cc[VSTATUS] = 20;
220   #endif
221
222   cfsetispeed(term, B38400);
223   cfsetospeed(term, B38400);
224
225   // uid / gid
226   int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
227   int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
228
229   // fork the pty
230   int master = -1;
231   pid_t pid = pty_forkpty(&master, nullptr, term, &winp);
232
233   if (pid) {
234     for (i = 0; i < argl; i++) free(argv[i]);
235     delete[] argv;
236     for (i = 0; i < envc; i++) free(env[i]);
237     delete[] env;
238     free(cwd);
239   }
240
241   switch (pid) {
242     case -1:
243       return Nan::ThrowError("forkpty(3) failed.");
244     case 0:
245       if (strlen(cwd)) {
246         if (chdir(cwd) == -1) {
247           perror("chdir(2) failed.");
248           _exit(1);
249         }
250       }
251
252       if (uid != -1 && gid != -1) {
253         if (setgid(gid) == -1) {
254           perror("setgid(2) failed.");
255           _exit(1);
256         }
257         if (setuid(uid) == -1) {
258           perror("setuid(2) failed.");
259           _exit(1);
260         }
261       }
262
263       pty_execvpe(argv[0], argv, env);
264
265       perror("execvp(3) failed.");
266       _exit(1);
267     default:
268       if (pty_nonblock(master) == -1) {
269         return Nan::ThrowError("Could not set master fd to nonblocking.");
270       }
271
272       v8::Local<v8::Object> obj = Nan::New<v8::Object>();
273       Nan::Set(obj,
274         Nan::New<v8::String>("fd").ToLocalChecked(),
275         Nan::New<v8::Number>(master));
276       Nan::Set(obj,
277         Nan::New<v8::String>("pid").ToLocalChecked(),
278         Nan::New<v8::Number>(pid));
279       Nan::Set(obj,
280         Nan::New<v8::String>("pty").ToLocalChecked(),
281         Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
282
283       pty_baton *baton = new pty_baton();
284       baton->exit_code = 0;
285       baton->signal_code = 0;
286       baton->cb.Reset(v8::Local<v8::Function>::Cast(info[9]));
287       baton->pid = pid;
288       baton->async.data = baton;
289
290       uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
291
292       uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
293
294       return info.GetReturnValue().Set(obj);
295   }
296
297   return info.GetReturnValue().SetUndefined();
298 }
299
300 NAN_METHOD(PtyOpen) {
301   Nan::HandleScope scope;
302
303   if (info.Length() != 2 ||
304       !info[0]->IsNumber() ||
305       !info[1]->IsNumber()) {
306     return Nan::ThrowError("Usage: pty.open(cols, rows)");
307   }
308
309   // size
310   struct winsize winp;
311   winp.ws_col = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
312   winp.ws_row = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
313   winp.ws_xpixel = 0;
314   winp.ws_ypixel = 0;
315
316   // pty
317   int master, slave;
318   int ret = pty_openpty(&master, &slave, nullptr, NULL, &winp);
319
320   if (ret == -1) {
321     return Nan::ThrowError("openpty(3) failed.");
322   }
323
324   if (pty_nonblock(master) == -1) {
325     return Nan::ThrowError("Could not set master fd to nonblocking.");
326   }
327
328   if (pty_nonblock(slave) == -1) {
329     return Nan::ThrowError("Could not set slave fd to nonblocking.");
330   }
331
332   v8::Local<v8::Object> obj = Nan::New<v8::Object>();
333   Nan::Set(obj,
334     Nan::New<v8::String>("master").ToLocalChecked(),
335     Nan::New<v8::Number>(master));
336   Nan::Set(obj,
337     Nan::New<v8::String>("slave").ToLocalChecked(),
338     Nan::New<v8::Number>(slave));
339   Nan::Set(obj,
340     Nan::New<v8::String>("pty").ToLocalChecked(),
341     Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
342
343   return info.GetReturnValue().Set(obj);
344 }
345
346 NAN_METHOD(PtyResize) {
347   Nan::HandleScope scope;
348
349   if (info.Length() != 3 ||
350       !info[0]->IsNumber() ||
351       !info[1]->IsNumber() ||
352       !info[2]->IsNumber()) {
353     return Nan::ThrowError("Usage: pty.resize(fd, cols, rows)");
354   }
355
356   int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
357
358   struct winsize winp;
359   winp.ws_col = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
360   winp.ws_row = info[2]->IntegerValue(Nan::GetCurrentContext()).FromJust();
361   winp.ws_xpixel = 0;
362   winp.ws_ypixel = 0;
363
364   if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
365     switch (errno) {
366       case EBADF: return Nan::ThrowError("ioctl(2) failed, EBADF");
367       case EFAULT: return Nan::ThrowError("ioctl(2) failed, EFAULT");
368       case EINVAL: return Nan::ThrowError("ioctl(2) failed, EINVAL");
369       case ENOTTY: return Nan::ThrowError("ioctl(2) failed, ENOTTY");
370     }
371     return Nan::ThrowError("ioctl(2) failed");
372   }
373
374   return info.GetReturnValue().SetUndefined();
375 }
376
377 /**
378  * Foreground Process Name
379  */
380 NAN_METHOD(PtyGetProc) {
381   Nan::HandleScope scope;
382
383   if (info.Length() != 2 ||
384       !info[0]->IsNumber() ||
385       !info[1]->IsString()) {
386     return Nan::ThrowError("Usage: pty.process(fd, tty)");
387   }
388
389   int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
390
391   Nan::Utf8String tty_(info[1]);
392   char *tty = strdup(*tty_);
393   char *name = pty_getproc(fd, tty);
394   free(tty);
395
396   if (name == NULL) {
397     return info.GetReturnValue().SetUndefined();
398   }
399
400   v8::Local<v8::String> name_ = Nan::New<v8::String>(name).ToLocalChecked();
401   free(name);
402   return info.GetReturnValue().Set(name_);
403 }
404
405 /**
406  * execvpe
407  */
408
409 // execvpe(3) is not portable.
410 // http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html
411 static int
412 pty_execvpe(const char *file, char **argv, char **envp) {
413   char **old = environ;
414   environ = envp;
415   int ret = execvp(file, argv);
416   environ = old;
417   return ret;
418 }
419
420 /**
421  * Nonblocking FD
422  */
423
424 static int
425 pty_nonblock(int fd) {
426   int flags = fcntl(fd, F_GETFL, 0);
427   if (flags == -1) return -1;
428   return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
429 }
430
431 /**
432  * pty_waitpid
433  * Wait for SIGCHLD to read exit status.
434  */
435
436 static void
437 pty_waitpid(void *data) {
438   int ret;
439   int stat_loc;
440
441   pty_baton *baton = static_cast<pty_baton*>(data);
442
443   errno = 0;
444
445   if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) {
446     if (ret == -1 && errno == EINTR) {
447       return pty_waitpid(baton);
448     }
449     if (ret == -1 && errno == ECHILD) {
450       // XXX node v0.8.x seems to have this problem.
451       // waitpid is already handled elsewhere.
452       ;
453     } else {
454       assert(false);
455     }
456   }
457
458   if (WIFEXITED(stat_loc)) {
459     baton->exit_code = WEXITSTATUS(stat_loc); // errno?
460   }
461
462   if (WIFSIGNALED(stat_loc)) {
463     baton->signal_code = WTERMSIG(stat_loc);
464   }
465
466   uv_async_send(&baton->async);
467 }
468
469 /**
470  * pty_after_waitpid
471  * Callback after exit status has been read.
472  */
473
474 static void
475 pty_after_waitpid(uv_async_t *async) {
476   Nan::HandleScope scope;
477   pty_baton *baton = static_cast<pty_baton*>(async->data);
478
479   v8::Local<v8::Value> argv[] = {
480     Nan::New<v8::Integer>(baton->exit_code),
481     Nan::New<v8::Integer>(baton->signal_code),
482   };
483
484   v8::Local<v8::Function> cb = Nan::New<v8::Function>(baton->cb);
485   baton->cb.Reset();
486   memset(&baton->cb, -1, sizeof(baton->cb));
487   Nan::AsyncResource resource("pty_after_waitpid");
488   resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, 2, argv);
489
490   uv_close((uv_handle_t *)async, pty_after_close);
491 }
492
493 /**
494  * pty_after_close
495  * uv_close() callback - free handle data
496  */
497
498 static void
499 pty_after_close(uv_handle_t *handle) {
500   uv_async_t *async = (uv_async_t *)handle;
501   pty_baton *baton = static_cast<pty_baton*>(async->data);
502   delete baton;
503 }
504
505 /**
506  * pty_getproc
507  * Taken from tmux.
508  */
509
510 // Taken from: tmux (http://tmux.sourceforge.net/)
511 // Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
512 // Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>
513 // Copyright (c) 2009 Todd Carson <toc@daybefore.net>
514 //
515 // Permission to use, copy, modify, and distribute this software for any
516 // purpose with or without fee is hereby granted, provided that the above
517 // copyright notice and this permission notice appear in all copies.
518 //
519 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
520 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
521 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
522 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
523 // WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
524 // IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
525 // OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
526
527 #if defined(__linux__)
528
529 static char *
530 pty_getproc(int fd, char *tty) {
531   FILE *f;
532   char *path, *buf;
533   size_t len;
534   int ch;
535   pid_t pgrp;
536   int r;
537
538   if ((pgrp = tcgetpgrp(fd)) == -1) {
539     return NULL;
540   }
541
542   r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp);
543   if (r == -1 || path == NULL) return NULL;
544
545   if ((f = fopen(path, "r")) == NULL) {
546     free(path);
547     return NULL;
548   }
549
550   free(path);
551
552   len = 0;
553   buf = NULL;
554   while ((ch = fgetc(f)) != EOF) {
555     if (ch == '\0') break;
556     buf = (char *)realloc(buf, len + 2);
557     if (buf == NULL) return NULL;
558     buf[len++] = ch;
559   }
560
561   if (buf != NULL) {
562     buf[len] = '\0';
563   }
564
565   fclose(f);
566   return buf;
567 }
568
569 #elif defined(__APPLE__)
570
571 static char *
572 pty_getproc(int fd, char *tty) {
573   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
574   size_t size;
575   struct kinfo_proc kp;
576
577   if ((mib[3] = tcgetpgrp(fd)) == -1) {
578     return NULL;
579   }
580
581   size = sizeof kp;
582   if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
583     return NULL;
584   }
585
586   if (*kp.kp_proc.p_comm == '\0') {
587     return NULL;
588   }
589
590   return strdup(kp.kp_proc.p_comm);
591 }
592
593 #else
594
595 static char *
596 pty_getproc(int fd, char *tty) {
597   return NULL;
598 }
599
600 #endif
601
602 /**
603  * openpty(3) / forkpty(3)
604  */
605
606 static int
607 pty_openpty(int *amaster,
608             int *aslave,
609             char *name,
610             const struct termios *termp,
611             const struct winsize *winp) {
612 #if defined(__sun)
613   char *slave_name;
614   int slave;
615   int master = open("/dev/ptmx", O_RDWR | O_NOCTTY);
616   if (master == -1) return -1;
617   if (amaster) *amaster = master;
618
619   if (grantpt(master) == -1) goto err;
620   if (unlockpt(master) == -1) goto err;
621
622   slave_name = ptsname(master);
623   if (slave_name == NULL) goto err;
624   if (name) strcpy(name, slave_name);
625
626   slave = open(slave_name, O_RDWR | O_NOCTTY);
627   if (slave == -1) goto err;
628   if (aslave) *aslave = slave;
629
630   ioctl(slave, I_PUSH, "ptem");
631   ioctl(slave, I_PUSH, "ldterm");
632   ioctl(slave, I_PUSH, "ttcompat");
633
634   if (termp) tcsetattr(slave, TCSAFLUSH, termp);
635   if (winp) ioctl(slave, TIOCSWINSZ, winp);
636
637   return 0;
638
639 err:
640   close(master);
641   return -1;
642 #else
643   return openpty(amaster, aslave, name, (termios *)termp, (winsize *)winp);
644 #endif
645 }
646
647 static pid_t
648 pty_forkpty(int *amaster,
649             char *name,
650             const struct termios *termp,
651             const struct winsize *winp) {
652 #if defined(__sun)
653   int master, slave;
654
655   int ret = pty_openpty(&master, &slave, name, termp, winp);
656   if (ret == -1) return -1;
657   if (amaster) *amaster = master;
658
659   pid_t pid = fork();
660
661   switch (pid) {
662     case -1:
663       close(master);
664       close(slave);
665       return -1;
666     case 0:
667       close(master);
668
669       setsid();
670
671 #if defined(TIOCSCTTY)
672       // glibc does this
673       if (ioctl(slave, TIOCSCTTY, NULL) == -1) {
674         _exit(1);
675       }
676 #endif
677
678       dup2(slave, 0);
679       dup2(slave, 1);
680       dup2(slave, 2);
681
682       if (slave > 2) close(slave);
683
684       return 0;
685     default:
686       close(slave);
687       return pid;
688   }
689
690   return -1;
691 #else
692   return forkpty(amaster, name, (termios *)termp, (winsize *)winp);
693 #endif
694 }
695
696 /**
697  * Init
698  */
699
700 NAN_MODULE_INIT(init) {
701   Nan::HandleScope scope;
702   Nan::Export(target, "fork", PtyFork);
703   Nan::Export(target, "open", PtyOpen);
704   Nan::Export(target, "resize", PtyResize);
705   Nan::Export(target, "process", PtyGetProc);
706 }
707
708 NODE_MODULE(pty, init)