Ruby 1.9.2p136(2010-12-25revision30365)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

# 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