Ruby  2.4.2p198(2017-09-14revision59899)
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 "internal.h"
39 #include "ruby/io.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 
66 
67 /* Returns the exit status of the child for which PTY#check
68  * raised this exception
69  */
70 static VALUE
72 {
73  return rb_ivar_get(self, rb_intern("status"));
74 }
75 
76 struct pty_info {
77  int fd;
78  rb_pid_t child_pid;
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");
118  rb_update_max_fd(i);
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  }
140  rb_update_max_fd(slave);
141  close(master);
142 #endif
143  dup2(slave,0);
144  dup2(slave,1);
145  dup2(slave,2);
146  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);
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 static int
228 get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
229 {
230 #if defined(HAVE_POSIX_OPENPT)
231  /* Unix98 PTY */
232  int masterfd = -1, slavefd = -1;
233  char *slavedevice;
234  struct sigaction dfl, old;
235 
236  dfl.sa_handler = SIG_DFL;
237  dfl.sa_flags = 0;
238  sigemptyset(&dfl.sa_mask);
239 
240 #if defined(__sun) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
241  /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
242  /* FreeBSD 9.2 or later supports O_CLOEXEC
243  * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */
244  if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
245  if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
246  if (grantpt(masterfd) == -1) goto grantpt_error;
247  rb_fd_fix_cloexec(masterfd);
248 #else
249  {
250  int flags = O_RDWR|O_NOCTTY;
251 # if defined(O_CLOEXEC)
252  /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally.
253  * So version dependency on GNU/Linux is same as O_CLOEXEC with open().
254  * O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */
255  flags |= O_CLOEXEC;
256 # endif
257  if ((masterfd = posix_openpt(flags)) == -1) goto error;
258  }
259  rb_fd_fix_cloexec(masterfd);
260  if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
261  if (grantpt(masterfd) == -1) goto grantpt_error;
262 #endif
263  if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
264  if (unlockpt(masterfd) == -1) goto error;
265  if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
266  if (no_mesg(slavedevice, nomesg) == -1) goto error;
267  if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
268  rb_update_max_fd(slavefd);
269 
270 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
271  if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
272  if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
273  if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error;
274 #endif
275 
276  *master = masterfd;
277  *slave = slavefd;
278  strlcpy(SlaveName, slavedevice, DEVICELEN);
279  return 0;
280 
281  grantpt_error:
282  sigaction(SIGCHLD, &old, NULL);
283  error:
284  if (slavefd != -1) close(slavefd);
285  if (masterfd != -1) close(masterfd);
286  if (fail) {
287  rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
288  }
289  return -1;
290 #elif defined HAVE_OPENPTY
291 /*
292  * Use openpty(3) of 4.3BSD Reno and later,
293  * or the same interface function.
294  */
295  if (openpty(master, slave, SlaveName,
296  (struct termios *)0, (struct winsize *)0) == -1) {
297  if (!fail) return -1;
298  rb_raise(rb_eRuntimeError, "openpty() failed");
299  }
300  rb_fd_fix_cloexec(*master);
301  rb_fd_fix_cloexec(*slave);
302  if (no_mesg(SlaveName, nomesg) == -1) {
303  if (!fail) return -1;
304  rb_raise(rb_eRuntimeError, "can't chmod slave pty");
305  }
306 
307  return 0;
308 
309 #elif defined HAVE__GETPTY
310  /* SGI IRIX */
311  char *name;
312  mode_t mode = nomesg ? 0600 : 0622;
313 
314  if (!(name = _getpty(master, O_RDWR, mode, 0))) {
315  if (!fail) return -1;
316  rb_raise(rb_eRuntimeError, "_getpty() failed");
317  }
318  rb_fd_fix_cloexec(*master);
319 
320  *slave = rb_cloexec_open(name, O_RDWR, 0);
321  /* error check? */
322  rb_update_max_fd(*slave);
323  strlcpy(SlaveName, name, DEVICELEN);
324 
325  return 0;
326 #elif defined(HAVE_PTSNAME)
327  /* System V */
328  int masterfd = -1, slavefd = -1;
329  char *slavedevice;
330  void (*s)();
331 
332  extern char *ptsname(int);
333  extern int unlockpt(int);
334  extern int grantpt(int);
335 
336 #if defined(__sun)
337  /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
338  if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
339  s = signal(SIGCHLD, SIG_DFL);
340  if(grantpt(masterfd) == -1) goto error;
341  rb_fd_fix_cloexec(masterfd);
342 #else
343  if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
344  rb_update_max_fd(masterfd);
345  s = signal(SIGCHLD, SIG_DFL);
346  if(grantpt(masterfd) == -1) goto error;
347 #endif
348  signal(SIGCHLD, s);
349  if(unlockpt(masterfd) == -1) goto error;
350  if((slavedevice = ptsname(masterfd)) == NULL) goto error;
351  if (no_mesg(slavedevice, nomesg) == -1) goto error;
352  if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error;
353  rb_update_max_fd(slavefd);
354 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
355  if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
356  if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
357  ioctl(slavefd, I_PUSH, "ttcompat");
358 #endif
359  *master = masterfd;
360  *slave = slavefd;
361  strlcpy(SlaveName, slavedevice, DEVICELEN);
362  return 0;
363 
364  error:
365  if (slavefd != -1) close(slavefd);
366  if (masterfd != -1) close(masterfd);
367  if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
368  return -1;
369 #else
370  /* BSD */
371  int masterfd = -1, slavefd = -1;
372  int i;
373  char MasterName[DEVICELEN];
374 
375 #if defined(__hpux)
376  static const char MasterDevice[] = "/dev/ptym/pty%s";
377  static const char SlaveDevice[] = "/dev/pty/tty%s";
378  static const char deviceNo[][3] = {
379  "p0","p1","p2","p3","p4","p5","p6","p7",
380  "p8","p9","pa","pb","pc","pd","pe","pf",
381  "q0","q1","q2","q3","q4","q5","q6","q7",
382  "q8","q9","qa","qb","qc","qd","qe","qf",
383  "r0","r1","r2","r3","r4","r5","r6","r7",
384  "r8","r9","ra","rb","rc","rd","re","rf",
385  "s0","s1","s2","s3","s4","s5","s6","s7",
386  "s8","s9","sa","sb","sc","sd","se","sf",
387  "t0","t1","t2","t3","t4","t5","t6","t7",
388  "t8","t9","ta","tb","tc","td","te","tf",
389  "u0","u1","u2","u3","u4","u5","u6","u7",
390  "u8","u9","ua","ub","uc","ud","ue","uf",
391  "v0","v1","v2","v3","v4","v5","v6","v7",
392  "v8","v9","va","vb","vc","vd","ve","vf",
393  "w0","w1","w2","w3","w4","w5","w6","w7",
394  "w8","w9","wa","wb","wc","wd","we","wf",
395  };
396 #elif defined(_IBMESA) /* AIX/ESA */
397  static const char MasterDevice[] = "/dev/ptyp%s";
398  static const char SlaveDevice[] = "/dev/ttyp%s";
399  static const char deviceNo[][3] = {
400  "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
401  "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
402  "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
403  "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
404  "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
405  "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
406  "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
407  "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
408  "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
409  "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
410  "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
411  "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
412  "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
413  "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
414  "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
415  "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff",
416  };
417 #else /* 4.2BSD */
418  static const char MasterDevice[] = "/dev/pty%s";
419  static const char SlaveDevice[] = "/dev/tty%s";
420  static const char deviceNo[][3] = {
421  "p0","p1","p2","p3","p4","p5","p6","p7",
422  "p8","p9","pa","pb","pc","pd","pe","pf",
423  "q0","q1","q2","q3","q4","q5","q6","q7",
424  "q8","q9","qa","qb","qc","qd","qe","qf",
425  "r0","r1","r2","r3","r4","r5","r6","r7",
426  "r8","r9","ra","rb","rc","rd","re","rf",
427  "s0","s1","s2","s3","s4","s5","s6","s7",
428  "s8","s9","sa","sb","sc","sd","se","sf",
429  };
430 #endif
431  for (i = 0; i < numberof(deviceNo); i++) {
432  const char *const devno = deviceNo[i];
433  snprintf(MasterName, sizeof MasterName, MasterDevice, devno);
434  if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) {
435  rb_update_max_fd(masterfd);
436  *master = masterfd;
437  snprintf(SlaveName, DEVICELEN, SlaveDevice, devno);
438  if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) {
439  rb_update_max_fd(slavefd);
440  *slave = slavefd;
441  if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
442  if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
443  return 0;
444  }
445  close(masterfd);
446  }
447  }
448  error:
449  if (slavefd != -1) close(slavefd);
450  if (masterfd != -1) close(masterfd);
451  if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName);
452  return -1;
453 #endif
454 }
455 
456 static void
457 getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
458 {
459  if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
460  rb_gc();
461  get_device_once(master, slave, SlaveName, nomesg, 1);
462  }
463 }
464 
465 static VALUE
467 {
468  VALUE io;
469  int i;
470 
471  for (i = 0; i < 2; i++) {
472  io = rb_ary_entry(assoc, i);
473  if (RB_TYPE_P(io, T_FILE) && 0 <= RFILE(io)->fptr->fd)
474  rb_io_close(io);
475  }
476  return Qnil;
477 }
478 
479 /*
480  * call-seq:
481  * PTY.open => [master_io, slave_file]
482  * PTY.open {|master_io, slave_file| ... } => block value
483  *
484  * Allocates a pty (pseudo-terminal).
485  *
486  * In the block form, yields two arguments <tt>master_io, slave_file</tt>
487  * and the value of the block is returned from +open+.
488  *
489  * The IO and File are both closed after the block completes if they haven't
490  * been already closed.
491  *
492  * PTY.open {|master, slave|
493  * p master #=> #<IO:masterpty:/dev/pts/1>
494  * p slave #=> #<File:/dev/pts/1>
495  * p slave.path #=> "/dev/pts/1"
496  * }
497  *
498  * In the non-block form, returns a two element array, <tt>[master_io,
499  * slave_file]</tt>.
500  *
501  * master, slave = PTY.open
502  * # do something with master for IO, or the slave file
503  *
504  * The arguments in both forms are:
505  *
506  * +master_io+:: the master of the pty, as an IO.
507  * +slave_file+:: the slave of the pty, as a File. The path to the
508  * terminal device is available via +slave_file.path+
509  *
510  * IO#raw! is usable to disable newline conversions:
511  *
512  * require 'io/console'
513  * PTY.open {|m, s|
514  * s.raw!
515  * ...
516  * }
517  *
518  */
519 static VALUE
521 {
522  int master_fd, slave_fd;
523  char slavename[DEVICELEN];
524  VALUE master_io, slave_file;
525  rb_io_t *master_fptr, *slave_fptr;
526  VALUE assoc;
527 
528  getDevice(&master_fd, &slave_fd, slavename, 1);
529 
530  master_io = rb_obj_alloc(rb_cIO);
531  MakeOpenFile(master_io, master_fptr);
532  master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
533  master_fptr->fd = master_fd;
534  master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
535 
536  slave_file = rb_obj_alloc(rb_cFile);
537  MakeOpenFile(slave_file, slave_fptr);
539  slave_fptr->fd = slave_fd;
540  slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
541 
542  assoc = rb_assoc_new(master_io, slave_file);
543  if (rb_block_given_p()) {
544  return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
545  }
546  return assoc;
547 }
548 
549 static VALUE
551 {
552 #ifdef WNOHANG
553  int st;
554  if (rb_waitpid(info->child_pid, &st, WNOHANG) <= 0)
555  return Qnil;
556 #endif
558  return Qnil;
559 }
560 
561 /*
562  * call-seq:
563  * PTY.spawn(command_line) { |r, w, pid| ... }
564  * PTY.spawn(command_line) => [r, w, pid]
565  * PTY.spawn(command, arguments, ...) { |r, w, pid| ... }
566  * PTY.spawn(command, arguments, ...) => [r, w, pid]
567  *
568  * Spawns the specified command on a newly allocated pty. You can also use the
569  * alias ::getpty.
570  *
571  * The command's controlling tty is set to the slave device of the pty
572  * and its standard input/output/error is redirected to the slave device.
573  *
574  * +command+ and +command_line+ are the full commands to run, given a String.
575  * Any additional +arguments+ will be passed to the command.
576  *
577  * === Return values
578  *
579  * In the non-block form this returns an array of size three,
580  * <tt>[r, w, pid]</tt>.
581  *
582  * In the block form these same values will be yielded to the block:
583  *
584  * +r+:: A readable IO that contains the command's
585  * standard output and standard error
586  * +w+:: A writable IO that is the command's standard input
587  * +pid+:: The process identifier for the command.
588  */
589 static VALUE
591 {
592  VALUE res;
593  struct pty_info info;
594  rb_io_t *wfptr,*rfptr;
595  VALUE rport = rb_obj_alloc(rb_cFile);
596  VALUE wport = rb_obj_alloc(rb_cFile);
597  char SlaveName[DEVICELEN];
598 
599  MakeOpenFile(rport, rfptr);
600  MakeOpenFile(wport, wfptr);
601 
602  establishShell(argc, argv, &info, SlaveName);
603 
604  rfptr->mode = rb_io_modestr_fmode("r");
605  rfptr->fd = info.fd;
606  rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
607 
608  wfptr->mode = rb_io_modestr_fmode("w") | FMODE_SYNC;
609  wfptr->fd = rb_cloexec_dup(info.fd);
610  if (wfptr->fd == -1)
611  rb_sys_fail("dup()");
612  rb_update_max_fd(wfptr->fd);
613  wfptr->pathv = rfptr->pathv;
614 
615  res = rb_ary_new2(3);
616  rb_ary_store(res,0,(VALUE)rport);
617  rb_ary_store(res,1,(VALUE)wport);
618  rb_ary_store(res,2,PIDT2NUM(info.child_pid));
619 
620  if (rb_block_given_p()) {
622  return Qnil;
623  }
624  return res;
625 }
626 
627 NORETURN(static void raise_from_check(rb_pid_t pid, int status));
628 static void
629 raise_from_check(rb_pid_t pid, int status)
630 {
631  const char *state;
632  VALUE msg;
633  VALUE exc;
634 
635 #if defined(WIFSTOPPED)
636 #elif defined(IF_STOPPED)
637 #define WIFSTOPPED(status) IF_STOPPED(status)
638 #else
639 ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
640 #endif /* WIFSTOPPED | IF_STOPPED */
641  if (WIFSTOPPED(status)) { /* suspend */
642  state = "stopped";
643  }
644  else if (kill(pid, 0) == 0) {
645  state = "changed";
646  }
647  else {
648  state = "exited";
649  }
650  msg = rb_sprintf("pty - %s: %ld", state, (long)pid);
651  exc = rb_exc_new_str(eChildExited, msg);
652  rb_iv_set(exc, "status", rb_last_status_get());
653  rb_exc_raise(exc);
654 }
655 
656 /*
657  * call-seq:
658  * PTY.check(pid, raise = false) => Process::Status or nil
659  * PTY.check(pid, true) => nil or raises PTY::ChildExited
660  *
661  * Checks the status of the child process specified by +pid+.
662  * Returns +nil+ if the process is still alive.
663  *
664  * If the process is not alive, and +raise+ was true, a PTY::ChildExited
665  * exception will be raised. Otherwise it will return a Process::Status
666  * instance.
667  *
668  * +pid+:: The process id of the process to check
669  * +raise+:: If +true+ and the process identified by +pid+ is no longer
670  * alive a PTY::ChildExited is raised.
671  *
672  */
673 static VALUE
675 {
676  VALUE pid, exc;
677  rb_pid_t cpid;
678  int status;
679  const int flag =
680 #ifdef WNOHANG
681  WNOHANG|
682 #endif
683 #ifdef WUNTRACED
684  WUNTRACED|
685 #endif
686  0;
687 
688  rb_scan_args(argc, argv, "11", &pid, &exc);
689  cpid = rb_waitpid(NUM2PIDT(pid), &status, flag);
690  if (cpid == -1 || cpid == 0) return Qnil;
691 
692  if (!RTEST(exc)) return rb_last_status_get();
693  raise_from_check(cpid, status);
694 
695  UNREACHABLE;
696 }
697 
698 static VALUE cPTY;
699 
700 /*
701  * Document-class: PTY::ChildExited
702  *
703  * Thrown when PTY::check is called for a pid that represents a process that
704  * has exited.
705  */
706 
707 /*
708  * Document-class: PTY
709  *
710  * Creates and managed pseudo terminals (PTYs). See also
711  * http://en.wikipedia.org/wiki/Pseudo_terminal
712  *
713  * PTY allows you to allocate new terminals using ::open or ::spawn a new
714  * terminal with a specific command.
715  *
716  * == Example
717  *
718  * In this example we will change the buffering type in the +factor+ command,
719  * assuming that factor uses stdio for stdout buffering.
720  *
721  * If IO.pipe is used instead of PTY.open, this code deadlocks because factor's
722  * stdout is fully buffered.
723  *
724  * # start by requiring the standard library PTY
725  * require 'pty'
726  *
727  * master, slave = PTY.open
728  * read, write = IO.pipe
729  * pid = spawn("factor", :in=>read, :out=>slave)
730  * read.close # we dont need the read
731  * slave.close # or the slave
732  *
733  * # pipe "42" to the factor command
734  * write.puts "42"
735  * # output the response from factor
736  * p master.gets #=> "42: 2 3 7\n"
737  *
738  * # pipe "144" to factor and print out the response
739  * write.puts "144"
740  * p master.gets #=> "144: 2 2 2 2 3 3\n"
741  * write.close # close the pipe
742  *
743  * # The result of read operation when pty slave is closed is platform
744  * # dependent.
745  * ret = begin
746  * master.gets # FreeBSD returns nil.
747  * rescue Errno::EIO # GNU/Linux raises EIO.
748  * nil
749  * end
750  * p ret #=> nil
751  *
752  * == License
753  *
754  * C) Copyright 1998 by Akinori Ito.
755  *
756  * This software may be redistributed freely for this purpose, in full
757  * or in part, provided that this entire copyright notice is included
758  * on any copies of this software and applications and derivations thereof.
759  *
760  * This software is provided on an "as is" basis, without warranty of any
761  * kind, either expressed or implied, as to any matter including, but not
762  * limited to warranty of fitness of purpose, or merchantability, or
763  * results obtained from use of this software.
764  */
765 
766 void
767 Init_pty(void)
768 {
769  cPTY = rb_define_module("PTY");
770  /* :nodoc: */
775 
778 }
void rb_gc(void)
Definition: gc.c:6656
#define WNOHANG
Definition: win32.h:128
int slave
Definition: pty.c:84
int fd
Definition: pty.c:77
VALUE rb_ary_entry(VALUE ary, long offset)
Definition: array.c:1196
Definition: pty.c:83
VALUE rb_detach_process(rb_pid_t pid)
Definition: process.c:1097
int ioctl(int, int,...)
Definition: win32.c:2755
#define FMODE_READWRITE
Definition: io.h:104
void rb_update_max_fd(int fd)
Definition: io.c:189
#define WIFSTOPPED(status)
Definition: pty.c:34
rb_uid_t getuid(void)
Definition: win32.c:2709
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1716
static VALUE pty_check(int argc, VALUE *argv, VALUE self)
Definition: pty.c:674
static void getDevice(int *, int *, char [DEVICELEN], int)
Definition: pty.c:457
#define NUM2PIDT(v)
Definition: ruby.h:326
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Definition: io.c:255
int master
Definition: pty.c:84
#define PIDT2NUM(v)
Definition: ruby.h:323
static VALUE pty_close_pty(VALUE assoc)
Definition: pty.c:466
Definition: io.h:62
int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen)
Definition: process.c:3150
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Definition: error.c:809
#define UNREACHABLE
Definition: ruby.h:46
VALUE rb_cFile
Definition: file.c:129
int kill(int, int)
Definition: win32.c:4724
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3138
VALUE execarg_obj
Definition: pty.c:86
int rb_io_modestr_fmode(const char *modestr)
Definition: io.c:4967
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:693
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2207
VALUE rb_ivar_get(VALUE, ID)
Definition: variable.c:1260
static int get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
Definition: pty.c:228
#define RB_GC_GUARD(v)
Definition: ruby.h:552
#define FMODE_DUPLEX
Definition: io.h:108
static VALUE pty_open(VALUE klass)
Definition: pty.c:520
VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell)
Definition: process.c:2257
Definition: pty.c:76
#define RFILE(obj)
Definition: ruby.h:1213
static void establishShell(int argc, VALUE *argv, struct pty_info *info, char SlaveName[DEVICELEN])
Definition: pty.c:156
#define rb_ary_new2
Definition: intern.h:90
int mode
Definition: io.h:65
struct rb_execarg * eargp
Definition: pty.c:87
void rb_exc_raise(VALUE mesg)
Definition: eval.c:620
#define RB_TYPE_P(obj, type)
Definition: ruby.h:527
static VALUE eChildExited
Definition: pty.c:65
#define fail()
#define O_CLOEXEC
struct rb_execarg * rb_execarg_get(VALUE execarg_obj)
Definition: process.c:2268
int rb_block_given_p(void)
Definition: eval.c:797
VALUE rb_eRuntimeError
Definition: error.c:761
#define snprintf
Definition: subst.h:6
static char msg[50]
Definition: strerror.c:8
int chown(const char *, int, int)
Definition: win32.c:4698
int fd
Definition: io.h:64
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)
void rb_ary_store(VALUE ary, long idx, VALUE val)
Definition: array.c:799
int argc
Definition: ruby.c:183
int seteuid(pid_t pid)
#define rb_str_new2
Definition: intern.h:857
VALUE rb_obj_alloc(VALUE)
Definition: object.c:1845
#define numberof(array)
Definition: etc.c:616
RUBY_EXTERN VALUE rb_cIO
Definition: ruby.h:1892
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1731
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1020
int errno
VALUE rb_sprintf(const char *format,...)
Definition: sprintf.c:1440
static int chfunc(void *data, char *errbuf, size_t errbuf_len)
Definition: pty.c:91
static VALUE cPTY
Definition: pty.c:698
void rb_execarg_parent_start(VALUE execarg_obj)
Definition: process.c:2457
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1919
rb_pid_t child_pid
Definition: pty.c:78
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:623
#define Qnil
Definition: ruby.h:438
#define mode_t
Definition: win32.h:119
VALUE rb_io_close(VALUE)
Definition: io.c:4469
unsigned long VALUE
Definition: ruby.h:85
RUBY_EXTERN size_t strlcpy(char *, const char *, size_t)
Definition: strlcpy.c:29
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
Definition: eval.c:923
VALUE rb_str_new_cstr(const char *)
Definition: string.c:770
void rb_sys_fail(const char *mesg)
Definition: error.c:2326
void rb_jump_tag(int tag)
Definition: eval.c:788
NORETURN(static void raise_from_check(rb_pid_t pid, int status))
#define getenv(name)
Definition: win32.c:71
char * slavename
Definition: pty.c:85
char * getlogin()
Definition: win32.c:867
static VALUE pty_getpty(int argc, VALUE *argv, VALUE self)
Definition: pty.c:590
void rb_fd_fix_cloexec(int fd)
Definition: io.c:231
VALUE pathv
Definition: io.h:68
#define RTEST(v)
Definition: ruby.h:450
void Init_pty(void)
Definition: pty.c:767
#define ERROR_EXIT(str)
#define T_FILE
Definition: ruby.h:502
#define DEVICELEN
Definition: pty.c:51
const char * name
Definition: nkf.c:208
static void raise_from_check(rb_pid_t pid, int status)
Definition: pty.c:629
static VALUE echild_status(VALUE self)
Definition: pty.c:71
#define FMODE_SYNC
Definition: io.h:106
#define FMODE_TTY
Definition: io.h:107
#define MakeOpenFile(obj, fp)
Definition: io.h:122
RUBY_EXTERN int dup2(int, int)
Definition: dup2.c:27
static VALUE pty_detach_process(struct pty_info *info)
Definition: pty.c:550
int rb_cloexec_dup(int oldfd)
Definition: io.c:281
rb_gid_t getgid(void)
Definition: win32.c:2723
VALUE rb_obj_freeze(VALUE)
Definition: object.c:1111
void rb_execarg_parent_end(VALUE execarg_obj)
Definition: process.c:2496
VALUE rb_define_module(const char *name)
Definition: class.c:768
#define rb_intern(str)
VALUE rb_last_status_get(void)
Definition: process.c:479
#define NULL
Definition: _sdbm.c:102
rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags)
Definition: process.c:902
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
char ** argv
Definition: ruby.c:184