ext/pty/pty.c

Go to the documentation of this file.
00001 #include        "ruby/config.h"
00002 #ifdef RUBY_EXTCONF_H
00003 #include RUBY_EXTCONF_H
00004 #endif
00005 #include        <stdlib.h>
00006 #include        <stdio.h>
00007 #include        <sys/types.h>
00008 #include        <sys/stat.h>
00009 #include        <sys/file.h>
00010 #include        <fcntl.h>
00011 #include        <errno.h>
00012 #include        <pwd.h>
00013 #ifdef HAVE_SYS_IOCTL_H
00014 #include        <sys/ioctl.h>
00015 #endif
00016 #ifdef HAVE_LIBUTIL_H
00017 #include        <libutil.h>
00018 #endif
00019 #ifdef HAVE_UTIL_H
00020 #include        <util.h>
00021 #endif
00022 #ifdef HAVE_PTY_H
00023 #include        <pty.h>
00024 #endif
00025 #ifdef HAVE_SYS_WAIT_H
00026 #include <sys/wait.h>
00027 #else
00028 #define WIFSTOPPED(status)    (((status) & 0xff) == 0x7f)
00029 #endif
00030 #include <ctype.h>
00031 
00032 #include "ruby/ruby.h"
00033 #include "ruby/io.h"
00034 #include "ruby/util.h"
00035 
00036 #include <signal.h>
00037 #ifdef HAVE_SYS_STROPTS_H
00038 #include <sys/stropts.h>
00039 #endif
00040 
00041 #ifdef HAVE_UNISTD_H
00042 #include <unistd.h>
00043 #endif
00044 
00045 #define DEVICELEN       16
00046 
00047 #if !defined(HAVE_OPENPTY)
00048 #if defined(__hpux)
00049 static const
00050 char    MasterDevice[] = "/dev/ptym/pty%s",
00051         SlaveDevice[] =  "/dev/pty/tty%s",
00052         *const deviceNo[] = {
00053                 "p0","p1","p2","p3","p4","p5","p6","p7",
00054                 "p8","p9","pa","pb","pc","pd","pe","pf",
00055                 "q0","q1","q2","q3","q4","q5","q6","q7",
00056                 "q8","q9","qa","qb","qc","qd","qe","qf",
00057                 "r0","r1","r2","r3","r4","r5","r6","r7",
00058                 "r8","r9","ra","rb","rc","rd","re","rf",
00059                 "s0","s1","s2","s3","s4","s5","s6","s7",
00060                 "s8","s9","sa","sb","sc","sd","se","sf",
00061                 "t0","t1","t2","t3","t4","t5","t6","t7",
00062                 "t8","t9","ta","tb","tc","td","te","tf",
00063                 "u0","u1","u2","u3","u4","u5","u6","u7",
00064                 "u8","u9","ua","ub","uc","ud","ue","uf",
00065                 "v0","v1","v2","v3","v4","v5","v6","v7",
00066                 "v8","v9","va","vb","vc","vd","ve","vf",
00067                 "w0","w1","w2","w3","w4","w5","w6","w7",
00068                 "w8","w9","wa","wb","wc","wd","we","wf",
00069                 0,
00070         };
00071 #elif defined(_IBMESA)  /* AIX/ESA */
00072 static const
00073 char    MasterDevice[] = "/dev/ptyp%s",
00074         SlaveDevice[] = "/dev/ttyp%s",
00075         *const deviceNo[] = {
00076 "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
00077 "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
00078 "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
00079 "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
00080 "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
00081 "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
00082 "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
00083 "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
00084 "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
00085 "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
00086 "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
00087 "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
00088 "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
00089 "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
00090 "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
00091 "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff",
00092                 };
00093 #elif !defined(HAVE_PTSNAME)
00094 static const
00095 char    MasterDevice[] = "/dev/pty%s",
00096         SlaveDevice[] = "/dev/tty%s",
00097         *const deviceNo[] = {
00098                 "p0","p1","p2","p3","p4","p5","p6","p7",
00099                 "p8","p9","pa","pb","pc","pd","pe","pf",
00100                 "q0","q1","q2","q3","q4","q5","q6","q7",
00101                 "q8","q9","qa","qb","qc","qd","qe","qf",
00102                 "r0","r1","r2","r3","r4","r5","r6","r7",
00103                 "r8","r9","ra","rb","rc","rd","re","rf",
00104                 "s0","s1","s2","s3","s4","s5","s6","s7",
00105                 "s8","s9","sa","sb","sc","sd","se","sf",
00106                 0,
00107         };
00108 #endif
00109 #endif /* !defined(HAVE_OPENPTY) */
00110 
00111 #ifndef HAVE_SETEUID
00112 # ifdef HAVE_SETREUID
00113 #  define seteuid(e)    setreuid(-1, (e))
00114 # else /* NOT HAVE_SETREUID */
00115 #  ifdef HAVE_SETRESUID
00116 #   define seteuid(e)   setresuid(-1, (e), -1)
00117 #  else /* NOT HAVE_SETRESUID */
00118     /* I can't set euid. (;_;) */
00119 #  endif /* HAVE_SETRESUID */
00120 # endif /* HAVE_SETREUID */
00121 #endif /* NO_SETEUID */
00122 
00123 static VALUE eChildExited;
00124 
00125 static VALUE
00126 echild_status(VALUE self)
00127 {
00128     return rb_ivar_get(self, rb_intern("status"));
00129 }
00130 
00131 struct pty_info {
00132     int fd;
00133     rb_pid_t child_pid;
00134 };
00135 
00136 static void getDevice(int*, int*, char [DEVICELEN], int);
00137 
00138 struct exec_info {
00139     int argc;
00140     VALUE *argv;
00141 };
00142 
00143 static VALUE
00144 pty_exec(VALUE v)
00145 {
00146     struct exec_info *arg = (struct exec_info *)v;
00147     return rb_f_exec(arg->argc, arg->argv);
00148 }
00149 
00150 struct child_info {
00151     int master, slave;
00152     char *slavename;
00153     int argc;
00154     VALUE *argv;
00155 };
00156 
00157 static int
00158 chfunc(void *data)
00159 {
00160     struct child_info *carg = data;
00161     int master = carg->master;
00162     int slave = carg->slave;
00163     int argc = carg->argc;
00164     VALUE *argv = carg->argv;
00165 
00166     struct exec_info arg;
00167     int status;
00168 
00169     rb_thread_atfork_before_exec();
00170 
00171     /*
00172      * Set free from process group and controlling terminal
00173      */
00174 #ifdef HAVE_SETSID
00175     (void) setsid();
00176 #else /* HAS_SETSID */
00177 # ifdef HAVE_SETPGRP
00178 #  ifdef SETGRP_VOID
00179     if (setpgrp() == -1)
00180         perror("setpgrp()");
00181 #  else /* SETGRP_VOID */
00182     if (setpgrp(0, getpid()) == -1)
00183         rb_sys_fail("setpgrp()");
00184     {
00185         int i = open("/dev/tty", O_RDONLY);
00186         if (i < 0) rb_sys_fail("/dev/tty");
00187         if (ioctl(i, TIOCNOTTY, (char *)0))
00188             perror("ioctl(TIOCNOTTY)");
00189         close(i);
00190     }
00191 #  endif /* SETGRP_VOID */
00192 # endif /* HAVE_SETPGRP */
00193 #endif /* HAS_SETSID */
00194 
00195     /*
00196      * obtain new controlling terminal
00197      */
00198 #if defined(TIOCSCTTY)
00199     close(master);
00200     (void) ioctl(slave, TIOCSCTTY, (char *)0);
00201     /* errors ignored for sun */
00202 #else
00203     close(slave);
00204     slave = open(carg->slavename, O_RDWR);
00205     if (slave < 0) {
00206         perror("open: pty slave");
00207         _exit(1);
00208     }
00209     close(master);
00210 #endif
00211     write(slave, "", 1);
00212     dup2(slave,0);
00213     dup2(slave,1);
00214     dup2(slave,2);
00215     close(slave);
00216 #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
00217     seteuid(getuid());
00218 #endif
00219 
00220     arg.argc = argc;
00221     arg.argv = argv;
00222     rb_protect(pty_exec, (VALUE)&arg, &status);
00223     sleep(1);
00224     _exit(1);
00225 }
00226 
00227 static void
00228 establishShell(int argc, VALUE *argv, struct pty_info *info,
00229                char SlaveName[DEVICELEN])
00230 {
00231     int                 master,slave;
00232     rb_pid_t            pid;
00233     char                *p, tmp, *getenv();
00234     struct passwd       *pwent;
00235     VALUE               v;
00236     struct child_info   carg;
00237 
00238     if (argc == 0) {
00239         const char *shellname;
00240 
00241         if ((p = getenv("SHELL")) != NULL) {
00242             shellname = p;
00243         }
00244         else {
00245             pwent = getpwuid(getuid());
00246             if (pwent && pwent->pw_shell)
00247                 shellname = pwent->pw_shell;
00248             else
00249                 shellname = "/bin/sh";
00250         }
00251         v = rb_str_new2(shellname);
00252         argc = 1;
00253         argv = &v;
00254     }
00255 
00256     getDevice(&master, &slave, SlaveName, 0);
00257 
00258     carg.master = master;
00259     carg.slave = slave;
00260     carg.slavename = SlaveName;
00261     carg.argc = argc;
00262     carg.argv = argv;
00263     pid = rb_fork(0, chfunc, &carg, Qnil);
00264 
00265     if (pid < 0) {
00266         close(master);
00267         close(slave);
00268         rb_sys_fail("fork failed");
00269     }
00270 
00271     read(master, &tmp, 1);
00272     close(slave);
00273 
00274     info->child_pid = pid;
00275     info->fd = master;
00276 }
00277 
00278 static int
00279 no_mesg(char *slavedevice, int nomesg)
00280 {
00281     if (nomesg)
00282         return chmod(slavedevice, 0600);
00283     else
00284         return 0;
00285 }
00286 
00287 static int
00288 get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
00289 {
00290 #if defined(HAVE_POSIX_OPENPT)
00291     int masterfd = -1, slavefd = -1;
00292     char *slavedevice;
00293     struct sigaction dfl, old;
00294 
00295     dfl.sa_handler = SIG_DFL;
00296     dfl.sa_flags = 0;
00297     sigemptyset(&dfl.sa_mask);
00298 
00299     if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
00300     if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
00301     if (grantpt(masterfd) == -1) goto grantpt_error;
00302     if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
00303     if (unlockpt(masterfd) == -1) goto error;
00304     if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
00305     if (no_mesg(slavedevice, nomesg) == -1) goto error;
00306     if ((slavefd = open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
00307 
00308 #if defined I_PUSH && !defined linux
00309     if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
00310     if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
00311     if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error;
00312 #endif
00313 
00314     *master = masterfd;
00315     *slave = slavefd;
00316     strlcpy(SlaveName, slavedevice, DEVICELEN);
00317     return 0;
00318 
00319   grantpt_error:
00320     sigaction(SIGCHLD, &old, NULL);
00321   error:
00322     if (slavefd != -1) close(slavefd);
00323     if (masterfd != -1) close(masterfd);
00324     if (fail) {
00325         rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
00326     }
00327     return -1;
00328 #elif defined HAVE_OPENPTY
00329 /*
00330  * Use openpty(3) of 4.3BSD Reno and later,
00331  * or the same interface function.
00332  */
00333     if (openpty(master, slave, SlaveName,
00334                 (struct termios *)0, (struct winsize *)0) == -1) {
00335         if (!fail) return -1;
00336         rb_raise(rb_eRuntimeError, "openpty() failed");
00337     }
00338     if (no_mesg(SlaveName, nomesg) == -1) {
00339         if (!fail) return -1;
00340         rb_raise(rb_eRuntimeError, "can't chmod slave pty");
00341     }
00342 
00343     return 0;
00344 
00345 #elif defined HAVE__GETPTY
00346     char *name;
00347     mode_t mode = nomesg ? 0600 : 0622;
00348 
00349     if (!(name = _getpty(master, O_RDWR, mode, 0))) {
00350         if (!fail) return -1;
00351         rb_raise(rb_eRuntimeError, "_getpty() failed");
00352     }
00353 
00354     *slave = open(name, O_RDWR);
00355     strlcpy(SlaveName, name, DEVICELEN);
00356 
00357     return 0;
00358 #elif defined(HAVE_PTSNAME)
00359     int  masterfd = -1, slavefd = -1;
00360     char *slavedevice;
00361     void (*s)();
00362 
00363     extern char *ptsname(int);
00364     extern int unlockpt(int);
00365     extern int grantpt(int);
00366 
00367     if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
00368     s = signal(SIGCHLD, SIG_DFL);
00369     if(grantpt(masterfd) == -1) goto error;
00370     signal(SIGCHLD, s);
00371     if(unlockpt(masterfd) == -1) goto error;
00372     if((slavedevice = ptsname(masterfd)) == NULL) goto error;
00373     if (no_mesg(slavedevice, nomesg) == -1) goto error;
00374     if((slavefd = open(slavedevice, O_RDWR, 0)) == -1) goto error;
00375 #if defined I_PUSH && !defined linux
00376     if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
00377     if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
00378     ioctl(slavefd, I_PUSH, "ttcompat");
00379 #endif
00380     *master = masterfd;
00381     *slave = slavefd;
00382     strlcpy(SlaveName, slavedevice, DEVICELEN);
00383     return 0;
00384 
00385   error:
00386     if (slavefd != -1) close(slavefd);
00387     if (masterfd != -1) close(masterfd);
00388     if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
00389     return -1;
00390 #else
00391     int  masterfd = -1, slavefd = -1;
00392     const char *const *p;
00393     char MasterName[DEVICELEN];
00394 
00395     for (p = deviceNo; *p != NULL; p++) {
00396         snprintf(MasterName, sizeof MasterName, MasterDevice, *p);
00397         if ((masterfd = open(MasterName,O_RDWR,0)) >= 0) {
00398             *master = masterfd;
00399             snprintf(SlaveName, DEVICELEN, SlaveDevice, *p);
00400             if ((slavefd = open(SlaveName,O_RDWR,0)) >= 0) {
00401                 *slave = slavefd;
00402                 if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
00403                 if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
00404                 return 0;
00405             }
00406             close(masterfd);
00407         }
00408     }
00409   error:
00410     if (slavefd != -1) close(slavefd);
00411     if (masterfd != -1) close(masterfd);
00412     if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName);
00413     return -1;
00414 #endif
00415 }
00416 
00417 static void
00418 getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
00419 {
00420     if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
00421         rb_gc();
00422         get_device_once(master, slave, SlaveName, nomesg, 1);
00423     }
00424 }
00425 
00426 static VALUE
00427 pty_close_pty(VALUE assoc)
00428 {
00429     VALUE io;
00430     int i;
00431 
00432     for (i = 0; i < 2; i++) {
00433         io = rb_ary_entry(assoc, i);
00434         if (TYPE(io) == T_FILE && 0 <= RFILE(io)->fptr->fd)
00435             rb_io_close(io);
00436     }
00437     return Qnil;
00438 }
00439 
00440 /*
00441  * call-seq:
00442  *   PTY.open   => [master_io, slave_file]
00443  *   PTY.open {|master_io, slave_file| ... }    => block value
00444  *
00445  * Allocates a pty (pseudo-terminal).
00446  *
00447  * It returns an array which contains an IO object and a File object.
00448  * The former is the master of the pty.
00449  * The latter is the slave of the pty.
00450  *
00451  * If a block is given, it yields the array instead of return.
00452  * The value of the block is returned.
00453  * master_io and slave_file is closed when return if they are not closed.
00454  *
00455  * The path name of the terminal device can be gotten by slave_file.path.
00456  *
00457  *   PTY.open {|m, s|
00458  *     p m      #=> #<IO:masterpty:/dev/pts/1>
00459  *     p s      #=> #<File:/dev/pts/1>
00460  *     p s.path #=> "/dev/pts/1"
00461  *   }
00462  *
00463  *   # Change the buffering type in factor command,
00464  *   # assuming that factor uses stdio for stdout buffering.
00465  *   # If IO.pipe is used instead of PTY.open,
00466  *   # this code deadlocks because factor's stdout is fully buffered.
00467  *   m, s = PTY.open
00468  *   system("stty raw", :in=>s) # disable newline conversion.
00469  *   r, w = IO.pipe
00470  *   pid = spawn("factor", :in=>r, :out=>s)
00471  *   r.close
00472  *   s.close
00473  *   w.puts "42"
00474  *   p m.gets #=> "42: 2 3 7\n"
00475  *   w.puts "144"
00476  *   p m.gets #=> "144: 2 2 2 2 3 3\n"
00477  *   w.close
00478  *   # The result of read operation when pty slave is closed is platform dependnet.
00479  *   ret = begin
00480  *           m.gets          # FreeBSD returns nil.
00481  *         rescue Errno::EIO # GNU/Linux raises EIO.
00482  *           nil
00483  *         end
00484  *   p ret #=> nil
00485  *
00486  */
00487 static VALUE
00488 pty_open(VALUE klass)
00489 {
00490     int master_fd, slave_fd;
00491     char slavename[DEVICELEN];
00492     VALUE master_io, slave_file;
00493     rb_io_t *master_fptr, *slave_fptr;
00494     VALUE assoc;
00495 
00496     getDevice(&master_fd, &slave_fd, slavename, 1);
00497 
00498     master_io = rb_obj_alloc(rb_cIO);
00499     MakeOpenFile(master_io, master_fptr);
00500     master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
00501     master_fptr->fd = master_fd;
00502     master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
00503 
00504     slave_file = rb_obj_alloc(rb_cFile);
00505     MakeOpenFile(slave_file, slave_fptr);
00506     slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY;
00507     slave_fptr->fd = slave_fd;
00508     slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
00509 
00510     assoc = rb_assoc_new(master_io, slave_file);
00511     if (rb_block_given_p()) {
00512         return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
00513     }
00514     return assoc;
00515 }
00516 
00517 static VALUE
00518 pty_detach_process(struct pty_info *info)
00519 {
00520     rb_detach_process(info->child_pid);
00521     return Qnil;
00522 }
00523 
00524 /*
00525  * call-seq:
00526  *   PTY.spawn(command...) {|r, w, pid| ... }   => nil
00527  *   PTY.spawn(command...)                      => r, w, pid
00528  *   PTY.getpty(command...) {|r, w, pid| ... }  => nil
00529  *   PTY.getpty(command...)                     => r, w, pid
00530  *
00531  * spawns the specified command on a newly allocated pty.
00532  *
00533  * The command's controlling tty is set to the slave device of the pty.
00534  * Also its standard input/output/error is redirected to the slave device.
00535  *
00536  * PTY.spawn returns two IO objects and PID.
00537  * PID is the process ID of the command.
00538  * The two IO objects are connected to the master device of the pty.
00539  * The first IO object is opened as read mode and
00540  * The second is opened as write mode.
00541  *
00542  * If a block is given, two IO objects and PID is yielded.
00543  *
00544  */
00545 static VALUE
00546 pty_getpty(int argc, VALUE *argv, VALUE self)
00547 {
00548     VALUE res;
00549     struct pty_info info;
00550     rb_io_t *wfptr,*rfptr;
00551     VALUE rport = rb_obj_alloc(rb_cFile);
00552     VALUE wport = rb_obj_alloc(rb_cFile);
00553     char SlaveName[DEVICELEN];
00554 
00555     MakeOpenFile(rport, rfptr);
00556     MakeOpenFile(wport, wfptr);
00557 
00558     establishShell(argc, argv, &info, SlaveName);
00559 
00560     rfptr->mode = rb_io_mode_flags("r");
00561     rfptr->fd = info.fd;
00562     rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
00563 
00564     wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC;
00565     wfptr->fd = dup(info.fd);
00566     if (wfptr->fd == -1)
00567         rb_sys_fail("dup()");
00568     wfptr->pathv = rfptr->pathv;
00569 
00570     res = rb_ary_new2(3);
00571     rb_ary_store(res,0,(VALUE)rport);
00572     rb_ary_store(res,1,(VALUE)wport);
00573     rb_ary_store(res,2,PIDT2NUM(info.child_pid));
00574 
00575     if (rb_block_given_p()) {
00576         rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
00577         return Qnil;
00578     }
00579     return res;
00580 }
00581 
00582 static void
00583 raise_from_check(pid_t pid, int status)
00584 {
00585     const char *state;
00586     char buf[1024];
00587     VALUE exc;
00588 
00589 #if defined(WIFSTOPPED)
00590 #elif defined(IF_STOPPED)
00591 #define WIFSTOPPED(status) IF_STOPPED(status)
00592 #else
00593 ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
00594 #endif /* WIFSTOPPED | IF_STOPPED */
00595     if (WIFSTOPPED(status)) { /* suspend */
00596         state = "stopped";
00597     }
00598     else if (kill(pid, 0) == 0) {
00599         state = "changed";
00600     }
00601     else {
00602         state = "exited";
00603     }
00604     snprintf(buf, sizeof(buf), "pty - %s: %ld", state, (long)pid);
00605     exc = rb_exc_new2(eChildExited, buf);
00606     rb_iv_set(exc, "status", rb_last_status_get());
00607     rb_exc_raise(exc);
00608 }
00609 
00610 /*
00611  * call-seq:
00612  *   PTY.check(pid[, raise=false])   => Process::Status or nil
00613  *
00614  * checks the status of the child process specified by _pid_, and
00615  * returns +nil+ if the process is still alive and active.  Otherwise,
00616  * returns +Process::Status+ about the process if _raise_ is false, or
00617  * +PTY::ChildExited+ exception is raised.
00618  */
00619 static VALUE
00620 pty_check(int argc, VALUE *argv, VALUE self)
00621 {
00622     VALUE pid, exc;
00623     pid_t cpid;
00624     int status;
00625 
00626     rb_scan_args(argc, argv, "11", &pid, &exc);
00627     cpid = rb_waitpid(NUM2PIDT(pid), &status, WNOHANG|WUNTRACED);
00628     if (cpid == -1) return Qnil;
00629 
00630     if (!RTEST(exc)) return rb_last_status_get();
00631     raise_from_check(cpid, status);
00632     return Qnil;                /* not reached */
00633 }
00634 
00635 static VALUE cPTY;
00636 
00637 void
00638 Init_pty()
00639 {
00640     cPTY = rb_define_module("PTY");
00641     rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
00642     rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
00643     rb_define_singleton_method(cPTY,"check",pty_check,-1);
00644     rb_define_singleton_method(cPTY,"open",pty_open,0);
00645 
00646     eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
00647     rb_define_method(eChildExited,"status",echild_status,0);
00648 }
00649 

Generated on Wed Aug 10 09:17:02 2011 for Ruby by  doxygen 1.4.7