Ruby  2.7.2p137(2020-10-01revision5445e0435260b449decf2ac16f9d09bae3cafe72)
pty.c
Go to the documentation of this file.
1 #include "ruby/config.h"
2 #ifdef RUBY_EXTCONF_H
3 #include RUBY_EXTCONF_H
4 #endif
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sys/file.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #ifdef HAVE_PWD_H
13 #include <pwd.h>
14 #endif
15 #ifdef HAVE_SYS_IOCTL_H
16 #include <sys/ioctl.h>
17 #endif
18 #ifdef HAVE_LIBUTIL_H
19 #include <libutil.h>
20 #endif
21 #ifdef HAVE_UTIL_H
22 #include <util.h>
23 #endif
24 #ifdef HAVE_PTY_H
25 #include <pty.h>
26 #endif
27 #if defined(HAVE_SYS_PARAM_H)
28  /* for __FreeBSD_version */
29 # include <sys/param.h>
30 #endif
31 #ifdef HAVE_SYS_WAIT_H
32 #include <sys/wait.h>
33 #else
34 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
35 #endif
36 #include <ctype.h>
37 
38 #include "ruby/io.h"
39 #include "internal.h"
40 #include "ruby/util.h"
41 
42 #include <signal.h>
43 #ifdef HAVE_SYS_STROPTS_H
44 #include <sys/stropts.h>
45 #endif
46 
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 
51 #define DEVICELEN 16
52 
53 #ifndef HAVE_SETEUID
54 # ifdef HAVE_SETREUID
55 # define seteuid(e) setreuid(-1, (e))
56 # else /* NOT HAVE_SETREUID */
57 # ifdef HAVE_SETRESUID
58 # define seteuid(e) setresuid(-1, (e), -1)
59 # else /* NOT HAVE_SETRESUID */
60  /* I can't set euid. (;_;) */
61 # endif /* HAVE_SETRESUID */
62 # endif /* HAVE_SETREUID */
63 #endif /* NO_SETEUID */
64 
65 static VALUE eChildExited;
66 
67 /* Returns the exit status of the child for which PTY#check
68  * raised this exception
69  */
70 static VALUE
71 echild_status(VALUE self)
72 {
73  return rb_ivar_get(self, rb_intern("status"));
74 }
75 
76 struct pty_info {
77  int fd;
79 };
80 
81 static void getDevice(int*, int*, char [DEVICELEN], int);
82 
83 struct child_info {
84  int master, slave;
85  char *slavename;
87  struct rb_execarg *eargp;
88 };
89 
90 static int
91 chfunc(void *data, char *errbuf, size_t errbuf_len)
92 {
93  struct child_info *carg = data;
94  int master = carg->master;
95  int slave = carg->slave;
96 
97 #define ERROR_EXIT(str) do { \
98  strlcpy(errbuf, (str), errbuf_len); \
99  return -1; \
100  } while (0)
101 
102  /*
103  * Set free from process group and controlling terminal
104  */
105 #ifdef HAVE_SETSID
106  (void) setsid();
107 #else /* HAS_SETSID */
108 # ifdef HAVE_SETPGRP
109 # ifdef SETGRP_VOID
110  if (setpgrp() == -1)
111  ERROR_EXIT("setpgrp()");
112 # else /* SETGRP_VOID */
113  if (setpgrp(0, getpid()) == -1)
114  ERROR_EXIT("setpgrp()");
115  {
116  int i = rb_cloexec_open("/dev/tty", O_RDONLY, 0);
117  if (i < 0) ERROR_EXIT("/dev/tty");
119  if (ioctl(i, TIOCNOTTY, (char *)0))
120  ERROR_EXIT("ioctl(TIOCNOTTY)");
121  close(i);
122  }
123 # endif /* SETGRP_VOID */
124 # endif /* HAVE_SETPGRP */
125 #endif /* HAS_SETSID */
126 
127  /*
128  * obtain new controlling terminal
129  */
130 #if defined(TIOCSCTTY)
131  close(master);
132  (void) ioctl(slave, TIOCSCTTY, (char *)0);
133  /* errors ignored for sun */
134 #else
135  close(slave);
136  slave = rb_cloexec_open(carg->slavename, O_RDWR, 0);
137  if (slave < 0) {
138  ERROR_EXIT("open: pty slave");
139  }
141  close(master);
142 #endif
143  dup2(slave,0);
144  dup2(slave,1);
145  dup2(slave,2);
146  if (slave < 0 || slave > 2) (void)!close(slave);
147 #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
148  if (seteuid(getuid())) ERROR_EXIT("seteuid()");
149 #endif
150 
151  return rb_exec_async_signal_safe(carg->eargp, errbuf, sizeof(errbuf_len));
152 #undef ERROR_EXIT
153 }
154 
155 static void
156 establishShell(int argc, VALUE *argv, struct pty_info *info,
157  char SlaveName[DEVICELEN])
158 {
159  int master, slave, status = 0;
160  rb_pid_t pid;
161  char *p, *getenv();
162  VALUE v;
163  struct child_info carg;
164  char errbuf[32];
165 
166  if (argc == 0) {
167  const char *shellname = "/bin/sh";
168 
169  if ((p = getenv("SHELL")) != NULL) {
170  shellname = p;
171  }
172  else {
173 #if defined HAVE_PWD_H
174  const char *username = getenv("USER");
175  struct passwd *pwent = getpwnam(username ? username : getlogin());
176  if (pwent && pwent->pw_shell)
177  shellname = pwent->pw_shell;
178 #endif
179  }
180  v = rb_str_new2(shellname);
181  argc = 1;
182  argv = &v;
183  }
184 
185  carg.execarg_obj = rb_execarg_new(argc, argv, 1, 0);
186  carg.eargp = rb_execarg_get(carg.execarg_obj);
188 
189  getDevice(&master, &slave, SlaveName, 0);
190 
191  carg.master = master;
192  carg.slave = slave;
193  carg.slavename = SlaveName;
194  errbuf[0] = '\0';
195  pid = rb_fork_async_signal_safe(&status, chfunc, &carg, Qnil, errbuf, sizeof(errbuf));
196 
197  if (pid < 0) {
198  int e = errno;
199  close(master);
200  close(slave);
202  errno = e;
203  if (status) rb_jump_tag(status);
204  rb_sys_fail(errbuf[0] ? errbuf : "fork failed");
205  }
206 
207  close(slave);
209 
210  info->child_pid = pid;
211  info->fd = master;
212 
213  RB_GC_GUARD(carg.execarg_obj);
214 }
215 
216 #if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME)
217 static int
218 no_mesg(char *slavedevice, int nomesg)
219 {
220  if (nomesg)
221  return chmod(slavedevice, 0600);
222  else
223  return 0;
224 }
225 #endif
226 
227 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
228 static inline int
229 ioctl_I_PUSH(int fd, const char *const name)
230 {
231  int ret = 0;
232 # if defined(I_FIND)
233  ret = ioctl(fd, I_FIND, name);
234 # endif
235  if (ret == 0) {
236  ret = ioctl(fd, I_PUSH, name);
237  }
238  return ret;
239 }
240 #endif
241 
242 static int
243 get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
244 {
245 #if defined(HAVE_POSIX_OPENPT)
246  /* Unix98 PTY */
247  int masterfd = -1, slavefd = -1;
248  char *slavedevice;
249 
250 #if defined(__sun) || defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
251  /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
252  /* FreeBSD 9.2 or later supports O_CLOEXEC
253  * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */
254  if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
255  if (rb_grantpt(masterfd) == -1) goto error;
256  rb_fd_fix_cloexec(masterfd);
257 #else
258  {
259  int flags = O_RDWR|O_NOCTTY;
260 # if defined(O_CLOEXEC)
261  /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally.
262  * So version dependency on GNU/Linux is same as O_CLOEXEC with open().
263  * O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */
264  flags |= O_CLOEXEC;
265 # endif
266  if ((masterfd = posix_openpt(flags)) == -1) goto error;
267  }
268  rb_fd_fix_cloexec(masterfd);
269  if (rb_grantpt(masterfd) == -1) goto error;
270 #endif
271  if (unlockpt(masterfd) == -1) goto error;
272  if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
273  if (no_mesg(slavedevice, nomesg) == -1) goto error;
274  if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
275  rb_update_max_fd(slavefd);
276 
277 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
278  if (ioctl_I_PUSH(slavefd, "ptem") == -1) goto error;
279  if (ioctl_I_PUSH(slavefd, "ldterm") == -1) goto error;
280  if (ioctl_I_PUSH(slavefd, "ttcompat") == -1) goto error;
281 #endif
282 
283  *master = masterfd;
284  *slave = slavefd;
285  strlcpy(SlaveName, slavedevice, DEVICELEN);
286  return 0;
287 
288  error:
289  if (slavefd != -1) close(slavefd);
290  if (masterfd != -1) close(masterfd);
291  if (fail) {
292  rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
293  }
294  return -1;
295 #elif defined HAVE_OPENPTY
296 /*
297  * Use openpty(3) of 4.3BSD Reno and later,
298  * or the same interface function.
299  */
300  if (openpty(master, slave, SlaveName,
301  (struct termios *)0, (struct winsize *)0) == -1) {
302  if (!fail) return -1;
303  rb_raise(rb_eRuntimeError, "openpty() failed");
304  }
305  rb_fd_fix_cloexec(*master);
306  rb_fd_fix_cloexec(*slave);
307  if (no_mesg(SlaveName, nomesg) == -1) {
308  if (!fail) return -1;
309  rb_raise(rb_eRuntimeError, "can't chmod slave pty");
310  }
311 
312  return 0;
313 
314 #elif defined HAVE__GETPTY
315  /* SGI IRIX */
316  char *name;
317  mode_t mode = nomesg ? 0600 : 0622;
318 
319  if (!(name = _getpty(master, O_RDWR, mode, 0))) {
320  if (!fail) return -1;
321  rb_raise(rb_eRuntimeError, "_getpty() failed");
322  }
323  rb_fd_fix_cloexec(*master);
324 
325  *slave = rb_cloexec_open(name, O_RDWR, 0);
326  /* error check? */
327  rb_update_max_fd(*slave);
328  strlcpy(SlaveName, name, DEVICELEN);
329 
330  return 0;
331 #elif defined(HAVE_PTSNAME)
332  /* System V */
333  int masterfd = -1, slavefd = -1;
334  char *slavedevice;
335  void (*s)();
336 
337  extern char *ptsname(int);
338  extern int unlockpt(int);
339 
340 #if defined(__sun)
341  /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
342  if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
343  if(rb_grantpt(masterfd) == -1) goto error;
344  rb_fd_fix_cloexec(masterfd);
345 #else
346  if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
347  rb_update_max_fd(masterfd);
348  if(rb_grantpt(masterfd) == -1) goto error;
349 #endif
350  if(unlockpt(masterfd) == -1) goto error;
351  if((slavedevice = ptsname(masterfd)) == NULL) goto error;
352  if (no_mesg(slavedevice, nomesg) == -1) goto error;
353  if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error;
354  rb_update_max_fd(slavefd);
355 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
356  if(ioctl_I_PUSH(slavefd, "ptem") == -1) goto error;
357  if(ioctl_I_PUSH(slavefd, "ldterm") == -1) goto error;
358  ioctl_I_PUSH(slavefd, "ttcompat");
359 #endif
360  *master = masterfd;
361  *slave = slavefd;
362  strlcpy(SlaveName, slavedevice, DEVICELEN);
363  return 0;
364 
365  error:
366  if (slavefd != -1) close(slavefd);
367  if (masterfd != -1) close(masterfd);
368  if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
369  return -1;
370 #else
371  /* BSD */
372  int masterfd = -1, slavefd = -1;
373  int i;
374  char MasterName[DEVICELEN];
375 
376 #define HEX1(c) \
377  c"0",c"1",c"2",c"3",c"4",c"5",c"6",c"7", \
378  c"8",c"9",c"a",c"b",c"c",c"d",c"e",c"f"
379 
380 #if defined(__hpux)
381  static const char MasterDevice[] = "/dev/ptym/pty%s";
382  static const char SlaveDevice[] = "/dev/pty/tty%s";
383  static const char deviceNo[][3] = {
384  HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"),
385  HEX1("t"), HEX1("u"), HEX1("v"), HEX1("w"),
386  };
387 #elif defined(_IBMESA) /* AIX/ESA */
388  static const char MasterDevice[] = "/dev/ptyp%s";
389  static const char SlaveDevice[] = "/dev/ttyp%s";
390  static const char deviceNo[][3] = {
391  HEX1("0"), HEX1("1"), HEX1("2"), HEX1("3"),
392  HEX1("4"), HEX1("5"), HEX1("6"), HEX1("7"),
393  HEX1("8"), HEX1("9"), HEX1("a"), HEX1("b"),
394  HEX1("c"), HEX1("d"), HEX1("e"), HEX1("f"),
395  };
396 #else /* 4.2BSD */
397  static const char MasterDevice[] = "/dev/pty%s";
398  static const char SlaveDevice[] = "/dev/tty%s";
399  static const char deviceNo[][3] = {
400  HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"),
401  };
402 #endif
403 #undef HEX1
404  for (i = 0; i < numberof(deviceNo); i++) {
405  const char *const devno = deviceNo[i];
406  snprintf(MasterName, sizeof MasterName, MasterDevice, devno);
407  if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) {
408  rb_update_max_fd(masterfd);
409  *master = masterfd;
410  snprintf(SlaveName, DEVICELEN, SlaveDevice, devno);
411  if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) {
412  rb_update_max_fd(slavefd);
413  *slave = slavefd;
414  if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
415  if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
416  return 0;
417  }
418  close(masterfd);
419  }
420  }
421  error:
422  if (slavefd != -1) close(slavefd);
423  if (masterfd != -1) close(masterfd);
424  if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName);
425  return -1;
426 #endif
427 }
428 
429 static void
430 getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
431 {
432  if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
433  rb_gc();
434  get_device_once(master, slave, SlaveName, nomesg, 1);
435  }
436 }
437 
438 static VALUE
439 pty_close_pty(VALUE assoc)
440 {
441  VALUE io;
442  int i;
443 
444  for (i = 0; i < 2; i++) {
445  io = rb_ary_entry(assoc, i);
446  if (RB_TYPE_P(io, T_FILE) && 0 <= RFILE(io)->fptr->fd)
447  rb_io_close(io);
448  }
449  return Qnil;
450 }
451 
452 /*
453  * call-seq:
454  * PTY.open => [master_io, slave_file]
455  * PTY.open {|master_io, slave_file| ... } => block value
456  *
457  * Allocates a pty (pseudo-terminal).
458  *
459  * In the block form, yields two arguments <tt>master_io, slave_file</tt>
460  * and the value of the block is returned from +open+.
461  *
462  * The IO and File are both closed after the block completes if they haven't
463  * been already closed.
464  *
465  * PTY.open {|master, slave|
466  * p master #=> #<IO:masterpty:/dev/pts/1>
467  * p slave #=> #<File:/dev/pts/1>
468  * p slave.path #=> "/dev/pts/1"
469  * }
470  *
471  * In the non-block form, returns a two element array, <tt>[master_io,
472  * slave_file]</tt>.
473  *
474  * master, slave = PTY.open
475  * # do something with master for IO, or the slave file
476  *
477  * The arguments in both forms are:
478  *
479  * +master_io+:: the master of the pty, as an IO.
480  * +slave_file+:: the slave of the pty, as a File. The path to the
481  * terminal device is available via +slave_file.path+
482  *
483  * IO#raw! is usable to disable newline conversions:
484  *
485  * require 'io/console'
486  * PTY.open {|m, s|
487  * s.raw!
488  * ...
489  * }
490  *
491  */
492 static VALUE
493 pty_open(VALUE klass)
494 {
495  int master_fd, slave_fd;
496  char slavename[DEVICELEN];
497  VALUE master_io, slave_file;
498  rb_io_t *master_fptr, *slave_fptr;
499  VALUE assoc;
500 
501  getDevice(&master_fd, &slave_fd, slavename, 1);
502 
503  master_io = rb_obj_alloc(rb_cIO);
504  MakeOpenFile(master_io, master_fptr);
505  master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
506  master_fptr->fd = master_fd;
507  master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
508 
509  slave_file = rb_obj_alloc(rb_cFile);
510  MakeOpenFile(slave_file, slave_fptr);
512  slave_fptr->fd = slave_fd;
513  slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
514 
515  assoc = rb_assoc_new(master_io, slave_file);
516  if (rb_block_given_p()) {
517  return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
518  }
519  return assoc;
520 }
521 
522 static VALUE
523 pty_detach_process(VALUE v)
524 {
525  struct pty_info *info = (void *)v;
526 #ifdef WNOHANG
527  int st;
528  if (rb_waitpid(info->child_pid, &st, WNOHANG) <= 0)
529  return Qnil;
530 #endif
532  return Qnil;
533 }
534 
535 /*
536  * call-seq:
537  * PTY.spawn(command_line) { |r, w, pid| ... }
538  * PTY.spawn(command_line) => [r, w, pid]
539  * PTY.spawn(command, arguments, ...) { |r, w, pid| ... }
540  * PTY.spawn(command, arguments, ...) => [r, w, pid]
541  *
542  * Spawns the specified command on a newly allocated pty. You can also use the
543  * alias ::getpty.
544  *
545  * The command's controlling tty is set to the slave device of the pty
546  * and its standard input/output/error is redirected to the slave device.
547  *
548  * +command+ and +command_line+ are the full commands to run, given a String.
549  * Any additional +arguments+ will be passed to the command.
550  *
551  * === Return values
552  *
553  * In the non-block form this returns an array of size three,
554  * <tt>[r, w, pid]</tt>.
555  *
556  * In the block form these same values will be yielded to the block:
557  *
558  * +r+:: A readable IO that contains the command's
559  * standard output and standard error
560  * +w+:: A writable IO that is the command's standard input
561  * +pid+:: The process identifier for the command.
562  */
563 static VALUE
564 pty_getpty(int argc, VALUE *argv, VALUE self)
565 {
566  VALUE res;
567  struct pty_info info;
568  rb_io_t *wfptr,*rfptr;
569  VALUE rport = rb_obj_alloc(rb_cFile);
570  VALUE wport = rb_obj_alloc(rb_cFile);
571  char SlaveName[DEVICELEN];
572 
573  MakeOpenFile(rport, rfptr);
574  MakeOpenFile(wport, wfptr);
575 
576  establishShell(argc, argv, &info, SlaveName);
577 
578  rfptr->mode = rb_io_modestr_fmode("r");
579  rfptr->fd = info.fd;
580  rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
581 
582  wfptr->mode = rb_io_modestr_fmode("w") | FMODE_SYNC;
583  wfptr->fd = rb_cloexec_dup(info.fd);
584  if (wfptr->fd == -1)
585  rb_sys_fail("dup()");
586  rb_update_max_fd(wfptr->fd);
587  wfptr->pathv = rfptr->pathv;
588 
589  res = rb_ary_new2(3);
590  rb_ary_store(res,0,(VALUE)rport);
591  rb_ary_store(res,1,(VALUE)wport);
592  rb_ary_store(res,2,PIDT2NUM(info.child_pid));
593 
594  if (rb_block_given_p()) {
595  rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
596  return Qnil;
597  }
598  return res;
599 }
600 
601 NORETURN(static void raise_from_check(rb_pid_t pid, int status));
602 static void
603 raise_from_check(rb_pid_t pid, int status)
604 {
605  const char *state;
606  VALUE msg;
607  VALUE exc;
608 
609 #if defined(WIFSTOPPED)
610 #elif defined(IF_STOPPED)
611 #define WIFSTOPPED(status) IF_STOPPED(status)
612 #else
613 ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
614 #endif /* WIFSTOPPED | IF_STOPPED */
615  if (WIFSTOPPED(status)) { /* suspend */
616  state = "stopped";
617  }
618  else if (kill(pid, 0) == 0) {
619  state = "changed";
620  }
621  else {
622  state = "exited";
623  }
624  msg = rb_sprintf("pty - %s: %ld", state, (long)pid);
625  exc = rb_exc_new_str(eChildExited, msg);
626  rb_iv_set(exc, "status", rb_last_status_get());
627  rb_exc_raise(exc);
628 }
629 
630 /*
631  * call-seq:
632  * PTY.check(pid, raise = false) => Process::Status or nil
633  * PTY.check(pid, true) => nil or raises PTY::ChildExited
634  *
635  * Checks the status of the child process specified by +pid+.
636  * Returns +nil+ if the process is still alive.
637  *
638  * If the process is not alive, and +raise+ was true, a PTY::ChildExited
639  * exception will be raised. Otherwise it will return a Process::Status
640  * instance.
641  *
642  * +pid+:: The process id of the process to check
643  * +raise+:: If +true+ and the process identified by +pid+ is no longer
644  * alive a PTY::ChildExited is raised.
645  *
646  */
647 static VALUE
648 pty_check(int argc, VALUE *argv, VALUE self)
649 {
650  VALUE pid, exc;
651  rb_pid_t cpid;
652  int status;
653  const int flag =
654 #ifdef WNOHANG
655  WNOHANG|
656 #endif
657 #ifdef WUNTRACED
658  WUNTRACED|
659 #endif
660  0;
661 
662  rb_scan_args(argc, argv, "11", &pid, &exc);
663  cpid = rb_waitpid(NUM2PIDT(pid), &status, flag);
664  if (cpid == -1 || cpid == 0) return Qnil;
665 
666  if (!RTEST(exc)) return rb_last_status_get();
667  raise_from_check(cpid, status);
668 
670 }
671 
672 static VALUE cPTY;
673 
674 /*
675  * Document-class: PTY::ChildExited
676  *
677  * Thrown when PTY::check is called for a pid that represents a process that
678  * has exited.
679  */
680 
681 /*
682  * Document-class: PTY
683  *
684  * Creates and manages pseudo terminals (PTYs). See also
685  * http://en.wikipedia.org/wiki/Pseudo_terminal
686  *
687  * PTY allows you to allocate new terminals using ::open or ::spawn a new
688  * terminal with a specific command.
689  *
690  * == Example
691  *
692  * In this example we will change the buffering type in the +factor+ command,
693  * assuming that factor uses stdio for stdout buffering.
694  *
695  * If IO.pipe is used instead of PTY.open, this code deadlocks because factor's
696  * stdout is fully buffered.
697  *
698  * # start by requiring the standard library PTY
699  * require 'pty'
700  *
701  * master, slave = PTY.open
702  * read, write = IO.pipe
703  * pid = spawn("factor", :in=>read, :out=>slave)
704  * read.close # we dont need the read
705  * slave.close # or the slave
706  *
707  * # pipe "42" to the factor command
708  * write.puts "42"
709  * # output the response from factor
710  * p master.gets #=> "42: 2 3 7\n"
711  *
712  * # pipe "144" to factor and print out the response
713  * write.puts "144"
714  * p master.gets #=> "144: 2 2 2 2 3 3\n"
715  * write.close # close the pipe
716  *
717  * # The result of read operation when pty slave is closed is platform
718  * # dependent.
719  * ret = begin
720  * master.gets # FreeBSD returns nil.
721  * rescue Errno::EIO # GNU/Linux raises EIO.
722  * nil
723  * end
724  * p ret #=> nil
725  *
726  * == License
727  *
728  * C) Copyright 1998 by Akinori Ito.
729  *
730  * This software may be redistributed freely for this purpose, in full
731  * or in part, provided that this entire copyright notice is included
732  * on any copies of this software and applications and derivations thereof.
733  *
734  * This software is provided on an "as is" basis, without warranty of any
735  * kind, either expressed or implied, as to any matter including, but not
736  * limited to warranty of fitness of purpose, or merchantability, or
737  * results obtained from use of this software.
738  */
739 
740 void
741 Init_pty(void)
742 {
743  cPTY = rb_define_module("PTY");
744  /* :nodoc: */
745  rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
746  rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
747  rb_define_singleton_method(cPTY,"check",pty_check,-1);
748  rb_define_singleton_method(cPTY,"open",pty_open,0);
749 
750  eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
751  rb_define_method(eChildExited,"status",echild_status,0);
752 }
child_info::master
int master
Definition: pty.c:84
NORETURN
NORETURN(static void raise_from_check(rb_pid_t pid, int status))
rb_execarg_parent_end
void rb_execarg_parent_end(VALUE execarg_obj)
Definition: process.c:2855
ERROR_EXIT
#define ERROR_EXIT(str)
error
const rb_iseq_t const char * error
Definition: rb_mjit_min_header-2.7.2.h:13554
RFILE
#define RFILE(obj)
Definition: ruby.h:1276
rb_cloexec_dup
int rb_cloexec_dup(int oldfd)
Definition: io.c:318
rb_assoc_new
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:896
rb_str_new2
#define rb_str_new2
Definition: intern.h:903
chmod
int chmod(const char *__path, mode_t __mode)
rb_io_close
VALUE rb_io_close(VALUE)
Definition: io.c:4820
rb_exc_new_str
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Definition: error.c:974
rb_last_status_get
VALUE rb_last_status_get(void)
Definition: process.c:518
rb_execarg_parent_start
void rb_execarg_parent_start(VALUE execarg_obj)
Definition: process.c:2816
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:898
FMODE_TTY
#define FMODE_TTY
Definition: io.h:113
O_CLOEXEC
#define O_CLOEXEC
Definition: dir.c:28
i
uint32_t i
Definition: rb_mjit_min_header-2.7.2.h:5499
rb_io_t::pathv
VALUE pathv
Definition: io.h:72
rb_fork_async_signal_safe
rb_pid_t rb_fork_async_signal_safe(int *status, int(*chfunc)(void *, char *, size_t), void *charg, VALUE fds, char *errmsg, size_t errmsg_buflen)
pty_info::child_pid
rb_pid_t child_pid
Definition: pty.c:78
VALUE
unsigned long VALUE
Definition: ruby.h:102
rb_intern
#define rb_intern(str)
rb_ary_store
void rb_ary_store(VALUE ary, long idx, VALUE val)
Definition: array.c:1079
RB_TYPE_P
#define RB_TYPE_P(obj, type)
Definition: ruby.h:560
WUNTRACED
#define WUNTRACED
Definition: rb_mjit_min_header-2.7.2.h:2531
rb_pid_t
#define rb_pid_t
Definition: rb_mjit_min_header-2.7.2.h:99
ptsname
char * ptsname(int)
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:772
seteuid
int seteuid(uid_t __uid)
rb_fd_fix_cloexec
void rb_fd_fix_cloexec(int fd)
Definition: io.c:268
getenv
#define getenv(name)
Definition: win32.c:73
child_info::eargp
struct rb_execarg * eargp
Definition: pty.c:87
exc
const rb_iseq_t const VALUE exc
Definition: rb_mjit_min_header-2.7.2.h:13552
WIFSTOPPED
#define WIFSTOPPED(status)
Definition: pty.c:34
rb_define_singleton_method
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1755
mode_t
__mode_t mode_t
Definition: rb_mjit_min_header-2.7.2.h:1334
rb_define_method
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1551
child_info::execarg_obj
VALUE execarg_obj
Definition: pty.c:86
HEX1
#define HEX1(c)
FMODE_DUPLEX
#define FMODE_DUPLEX
Definition: io.h:114
chown
int chown(const char *, int, int)
Definition: win32.c:4763
rb_io_t::fd
int fd
Definition: io.h:68
NULL
#define NULL
Definition: _sdbm.c:101
rb_waitpid
rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags)
Definition: process.c:1213
rb_cFile
VALUE rb_cFile
Definition: file.c:159
void
void
Definition: rb_mjit_min_header-2.7.2.h:13321
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
FMODE_SYNC
#define FMODE_SYNC
Definition: io.h:112
getuid
rb_uid_t getuid(void)
Definition: win32.c:2765
rb_ary_entry
VALUE rb_ary_entry(VALUE ary, long offset)
Definition: array.c:1512
rb_ivar_get
VALUE rb_ivar_get(VALUE, ID)
Definition: variable.c:1070
T_FILE
#define T_FILE
Definition: ruby.h:534
WNOHANG
#define WNOHANG
Definition: win32.h:128
posix_openpt
int posix_openpt(int)
snprintf
int snprintf(char *__restrict, size_t, const char *__restrict,...) __attribute__((__format__(__printf__
klass
VALUE klass
Definition: rb_mjit_min_header-2.7.2.h:13302
rb_execarg_new
VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt)
Definition: process.c:2633
child_info
Definition: pty.c:83
rb_jump_tag
void rb_jump_tag(int tag)
Continues the exception caught by rb_protect() and rb_eval_string_protect().
Definition: eval.c:884
rb_detach_process
VALUE rb_detach_process(rb_pid_t pid)
Definition: process.c:1423
st
enum ruby_tag_type st
Definition: rb_mjit_min_header-2.7.2.h:11159
child_info::slave
int slave
Definition: pty.c:84
getpid
pid_t getpid(void)
rb_execarg_get
struct rb_execarg * rb_execarg_get(VALUE execarg_obj)
Definition: process.c:2608
rb_update_max_fd
void rb_update_max_fd(int fd)
Definition: io.c:218
rb_obj_freeze
VALUE rb_obj_freeze(VALUE)
Make the object unmodifiable.
Definition: object.c:1080
pty_info
Definition: pty.c:76
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:2795
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:922
Init_pty
void Init_pty(void)
Definition: pty.c:741
FMODE_READWRITE
#define FMODE_READWRITE
Definition: io.h:110
strlcpy
RUBY_EXTERN size_t strlcpy(char *, const char *, size_t)
Definition: strlcpy.c:29
rb_gc
void rb_gc(void)
Definition: gc.c:8682
PIDT2NUM
#define PIDT2NUM(v)
Definition: ruby.h:354
kill
int kill(int, int)
Definition: win32.c:4789
rb_scan_args
#define rb_scan_args(argc, argvp, fmt,...)
Definition: rb_mjit_min_header-2.7.2.h:6407
dup2
RUBY_EXTERN int dup2(int, int)
Definition: dup2.c:27
ioctl
int ioctl(int, int,...)
Definition: win32.c:2811
rb_define_module_function
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1771
rb_cloexec_open
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Definition: io.c:292
rb_ary_new2
#define rb_ary_new2
Definition: intern.h:103
unlockpt
int unlockpt(int)
rb_exc_raise
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition: eval.c:668
pty_info::fd
int fd
Definition: pty.c:77
internal.h
argv
char ** argv
Definition: ruby.c:223
rb_io_t::mode
int mode
Definition: io.h:69
rb_sprintf
VALUE rb_sprintf(const char *format,...)
Definition: sprintf.c:1197
rb_obj_alloc
VALUE rb_obj_alloc(VALUE)
Allocates an instance of klass.
Definition: object.c:1895
rb_execarg
Definition: internal.h:2025
rb_io_modestr_fmode
int rb_io_modestr_fmode(const char *modestr)
Definition: io.c:5465
close
int close(int __fildes)
fail
#define fail()
Definition: date_strptime.c:123
getlogin
char * getlogin()
Definition: win32.c:881
io.h
DEVICELEN
#define DEVICELEN
Definition: pty.c:51
argc
int argc
Definition: ruby.c:222
getgid
rb_gid_t getgid(void)
Definition: win32.c:2779
MakeOpenFile
#define MakeOpenFile(obj, fp)
Definition: io.h:129
setsid
pid_t setsid(void)
errno
int errno
v
int VALUE v
Definition: rb_mjit_min_header-2.7.2.h:12380
child_info::slavename
char * slavename
Definition: pty.c:85
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:698
rb_yield
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
rb_str_new_cstr
#define rb_str_new_cstr(str)
Definition: rb_mjit_min_header-2.7.2.h:6152
rb_ensure
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1115
Qnil
#define Qnil
Definition: ruby.h:469
rb_exec_async_signal_safe
int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen)
Definition: process.c:3523
util.h
rb_io_t
Definition: io.h:66
NUM2PIDT
#define NUM2PIDT(v)
Definition: ruby.h:357
RB_GC_GUARD
#define RB_GC_GUARD(v)
Definition: ruby.h:585
numberof
#define numberof(array)
Definition: etc.c:618
UNREACHABLE_RETURN
#define UNREACHABLE_RETURN(val)
Definition: ruby.h:59
rb_grantpt
int rb_grantpt(int fd)
Definition: signal.c:1597
RTEST
#define RTEST(v)
Definition: ruby.h:481
rb_iv_set
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3318
rb_cIO
RUBY_EXTERN VALUE rb_cIO
Definition: ruby.h:2030
setpgrp
int setpgrp(void)
name
const char * name
Definition: nkf.c:208