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 #if defined(HAVE_SYS_PARAM_H)
00026   /* for __FreeBSD_version */
00027 # include <sys/param.h>
00028 #endif
00029 #ifdef HAVE_SYS_WAIT_H
00030 #include <sys/wait.h>
00031 #else
00032 #define WIFSTOPPED(status)    (((status) & 0xff) == 0x7f)
00033 #endif
00034 #include <ctype.h>
00035 
00036 #include "ruby/ruby.h"
00037 #include "ruby/io.h"
00038 #include "ruby/util.h"
00039 #include "internal.h"
00040 
00041 #include <signal.h>
00042 #ifdef HAVE_SYS_STROPTS_H
00043 #include <sys/stropts.h>
00044 #endif
00045 
00046 #ifdef HAVE_UNISTD_H
00047 #include <unistd.h>
00048 #endif
00049 
00050 #define DEVICELEN       16
00051 
00052 #ifndef HAVE_SETEUID
00053 # ifdef HAVE_SETREUID
00054 #  define seteuid(e)    setreuid(-1, (e))
00055 # else /* NOT HAVE_SETREUID */
00056 #  ifdef HAVE_SETRESUID
00057 #   define seteuid(e)   setresuid(-1, (e), -1)
00058 #  else /* NOT HAVE_SETRESUID */
00059     /* I can't set euid. (;_;) */
00060 #  endif /* HAVE_SETRESUID */
00061 # endif /* HAVE_SETREUID */
00062 #endif /* NO_SETEUID */
00063 
00064 static VALUE eChildExited;
00065 
00066 /* Returns the exit status of the child for which PTY#check
00067  * raised this exception
00068  */
00069 static VALUE
00070 echild_status(VALUE self)
00071 {
00072     return rb_ivar_get(self, rb_intern("status"));
00073 }
00074 
00075 struct pty_info {
00076     int fd;
00077     rb_pid_t child_pid;
00078 };
00079 
00080 static void getDevice(int*, int*, char [DEVICELEN], int);
00081 
00082 struct child_info {
00083     int master, slave;
00084     char *slavename;
00085     VALUE execarg_obj;
00086     struct rb_execarg *eargp;
00087 };
00088 
00089 static int
00090 chfunc(void *data, char *errbuf, size_t errbuf_len)
00091 {
00092     struct child_info *carg = data;
00093     int master = carg->master;
00094     int slave = carg->slave;
00095 
00096 #define ERROR_EXIT(str) do { \
00097         strlcpy(errbuf, (str), errbuf_len); \
00098         return -1; \
00099     } while (0)
00100 
00101     /*
00102      * Set free from process group and controlling terminal
00103      */
00104 #ifdef HAVE_SETSID
00105     (void) setsid();
00106 #else /* HAS_SETSID */
00107 # ifdef HAVE_SETPGRP
00108 #  ifdef SETGRP_VOID
00109     if (setpgrp() == -1)
00110         ERROR_EXIT("setpgrp()");
00111 #  else /* SETGRP_VOID */
00112     if (setpgrp(0, getpid()) == -1)
00113         ERROR_EXIT("setpgrp()");
00114     {
00115         int i = rb_cloexec_open("/dev/tty", O_RDONLY, 0);
00116         if (i < 0) ERROR_EXIT("/dev/tty");
00117         rb_update_max_fd(i);
00118         if (ioctl(i, TIOCNOTTY, (char *)0))
00119             ERROR_EXIT("ioctl(TIOCNOTTY)");
00120         close(i);
00121     }
00122 #  endif /* SETGRP_VOID */
00123 # endif /* HAVE_SETPGRP */
00124 #endif /* HAS_SETSID */
00125 
00126     /*
00127      * obtain new controlling terminal
00128      */
00129 #if defined(TIOCSCTTY)
00130     close(master);
00131     (void) ioctl(slave, TIOCSCTTY, (char *)0);
00132     /* errors ignored for sun */
00133 #else
00134     close(slave);
00135     slave = rb_cloexec_open(carg->slavename, O_RDWR, 0);
00136     if (slave < 0) {
00137         ERROR_EXIT("open: pty slave");
00138     }
00139     rb_update_max_fd(slave);
00140     close(master);
00141 #endif
00142     dup2(slave,0);
00143     dup2(slave,1);
00144     dup2(slave,2);
00145     close(slave);
00146 #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
00147     if (seteuid(getuid())) ERROR_EXIT("seteuid()");
00148 #endif
00149 
00150     return rb_exec_async_signal_safe(carg->eargp, errbuf, sizeof(errbuf_len));
00151 #undef ERROR_EXIT
00152 }
00153 
00154 static void
00155 establishShell(int argc, VALUE *argv, struct pty_info *info,
00156                char SlaveName[DEVICELEN])
00157 {
00158     int                 master, slave, status = 0;
00159     rb_pid_t            pid;
00160     char                *p, *getenv();
00161     struct passwd       *pwent;
00162     VALUE               v;
00163     struct child_info   carg;
00164     char                errbuf[32];
00165 
00166     if (argc == 0) {
00167         const char *shellname;
00168 
00169         if ((p = getenv("SHELL")) != NULL) {
00170             shellname = p;
00171         }
00172         else {
00173             pwent = getpwuid(getuid());
00174             if (pwent && pwent->pw_shell)
00175                 shellname = pwent->pw_shell;
00176             else
00177                 shellname = "/bin/sh";
00178         }
00179         v = rb_str_new2(shellname);
00180         argc = 1;
00181         argv = &v;
00182     }
00183 
00184     carg.execarg_obj = rb_execarg_new(argc, argv, 1);
00185     carg.eargp = rb_execarg_get(carg.execarg_obj);
00186     rb_execarg_fixup(carg.execarg_obj);
00187 
00188     getDevice(&master, &slave, SlaveName, 0);
00189 
00190     carg.master = master;
00191     carg.slave = slave;
00192     carg.slavename = SlaveName;
00193     errbuf[0] = '\0';
00194     pid = rb_fork_async_signal_safe(&status, chfunc, &carg, Qnil, errbuf, sizeof(errbuf));
00195 
00196     if (pid < 0) {
00197         int e = errno;
00198         close(master);
00199         close(slave);
00200         errno = e;
00201         if (status) rb_jump_tag(status);
00202         rb_sys_fail(errbuf[0] ? errbuf : "fork failed");
00203     }
00204 
00205     close(slave);
00206 
00207     info->child_pid = pid;
00208     info->fd = master;
00209 
00210     RB_GC_GUARD(carg.execarg_obj);
00211 }
00212 
00213 static int
00214 no_mesg(char *slavedevice, int nomesg)
00215 {
00216     if (nomesg)
00217         return chmod(slavedevice, 0600);
00218     else
00219         return 0;
00220 }
00221 
00222 static int
00223 get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
00224 {
00225 #if defined(HAVE_POSIX_OPENPT)
00226     /* Unix98 PTY */
00227     int masterfd = -1, slavefd = -1;
00228     char *slavedevice;
00229     struct sigaction dfl, old;
00230 
00231     dfl.sa_handler = SIG_DFL;
00232     dfl.sa_flags = 0;
00233     sigemptyset(&dfl.sa_mask);
00234 
00235 #if defined(__sun) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
00236     /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set.  [ruby-dev:44688] */
00237     /* FreeBSD 9.2 or later supports O_CLOEXEC
00238      * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */
00239     if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
00240     if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
00241     if (grantpt(masterfd) == -1) goto grantpt_error;
00242     rb_fd_fix_cloexec(masterfd);
00243 #else
00244     {
00245         int flags = O_RDWR|O_NOCTTY;
00246 # if defined(O_CLOEXEC)
00247         /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally.
00248          * So version dependency on GNU/Linux is same as O_CLOEXEC with open().
00249          * O_CLOEXEC is available since Linux 2.6.23.  Linux 2.6.18 silently ignore it. */
00250         flags |= O_CLOEXEC;
00251 # endif
00252         if ((masterfd = posix_openpt(flags)) == -1) goto error;
00253     }
00254     rb_fd_fix_cloexec(masterfd);
00255     if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
00256     if (grantpt(masterfd) == -1) goto grantpt_error;
00257 #endif
00258     if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
00259     if (unlockpt(masterfd) == -1) goto error;
00260     if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
00261     if (no_mesg(slavedevice, nomesg) == -1) goto error;
00262     if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
00263     rb_update_max_fd(slavefd);
00264 
00265 #if defined(I_PUSH) && !defined(__linux__)
00266     if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
00267     if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
00268     if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error;
00269 #endif
00270 
00271     *master = masterfd;
00272     *slave = slavefd;
00273     strlcpy(SlaveName, slavedevice, DEVICELEN);
00274     return 0;
00275 
00276   grantpt_error:
00277     sigaction(SIGCHLD, &old, NULL);
00278   error:
00279     if (slavefd != -1) close(slavefd);
00280     if (masterfd != -1) close(masterfd);
00281     if (fail) {
00282         rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
00283     }
00284     return -1;
00285 #elif defined HAVE_OPENPTY
00286 /*
00287  * Use openpty(3) of 4.3BSD Reno and later,
00288  * or the same interface function.
00289  */
00290     if (openpty(master, slave, SlaveName,
00291                 (struct termios *)0, (struct winsize *)0) == -1) {
00292         if (!fail) return -1;
00293         rb_raise(rb_eRuntimeError, "openpty() failed");
00294     }
00295     rb_fd_fix_cloexec(*master);
00296     rb_fd_fix_cloexec(*slave);
00297     if (no_mesg(SlaveName, nomesg) == -1) {
00298         if (!fail) return -1;
00299         rb_raise(rb_eRuntimeError, "can't chmod slave pty");
00300     }
00301 
00302     return 0;
00303 
00304 #elif defined HAVE__GETPTY
00305     /* SGI IRIX */
00306     char *name;
00307     mode_t mode = nomesg ? 0600 : 0622;
00308 
00309     if (!(name = _getpty(master, O_RDWR, mode, 0))) {
00310         if (!fail) return -1;
00311         rb_raise(rb_eRuntimeError, "_getpty() failed");
00312     }
00313     rb_fd_fix_cloexec(*master);
00314 
00315     *slave = rb_cloexec_open(name, O_RDWR, 0);
00316     /* error check? */
00317     rb_update_max_fd(*slave);
00318     strlcpy(SlaveName, name, DEVICELEN);
00319 
00320     return 0;
00321 #elif defined(HAVE_PTSNAME)
00322     /* System V */
00323     int  masterfd = -1, slavefd = -1;
00324     char *slavedevice;
00325     void (*s)();
00326 
00327     extern char *ptsname(int);
00328     extern int unlockpt(int);
00329     extern int grantpt(int);
00330 
00331 #if defined(__sun)
00332     /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set.  [ruby-dev:44688] */
00333     if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
00334     s = signal(SIGCHLD, SIG_DFL);
00335     if(grantpt(masterfd) == -1) goto error;
00336     rb_fd_fix_cloexec(masterfd);
00337 #else
00338     if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
00339     rb_update_max_fd(masterfd);
00340     s = signal(SIGCHLD, SIG_DFL);
00341     if(grantpt(masterfd) == -1) goto error;
00342 #endif
00343     signal(SIGCHLD, s);
00344     if(unlockpt(masterfd) == -1) goto error;
00345     if((slavedevice = ptsname(masterfd)) == NULL) goto error;
00346     if (no_mesg(slavedevice, nomesg) == -1) goto error;
00347     if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error;
00348     rb_update_max_fd(slavefd);
00349 #if defined(I_PUSH) && !defined(__linux__)
00350     if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
00351     if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
00352     ioctl(slavefd, I_PUSH, "ttcompat");
00353 #endif
00354     *master = masterfd;
00355     *slave = slavefd;
00356     strlcpy(SlaveName, slavedevice, DEVICELEN);
00357     return 0;
00358 
00359   error:
00360     if (slavefd != -1) close(slavefd);
00361     if (masterfd != -1) close(masterfd);
00362     if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
00363     return -1;
00364 #else
00365     /* BSD */
00366     int  masterfd = -1, slavefd = -1;
00367     const char *const *p;
00368     char MasterName[DEVICELEN];
00369 
00370 #if defined(__hpux)
00371     static const char MasterDevice[] = "/dev/ptym/pty%s";
00372     static const char SlaveDevice[] =  "/dev/pty/tty%s";
00373     static const char *const deviceNo[] = {
00374     "p0","p1","p2","p3","p4","p5","p6","p7",
00375     "p8","p9","pa","pb","pc","pd","pe","pf",
00376     "q0","q1","q2","q3","q4","q5","q6","q7",
00377     "q8","q9","qa","qb","qc","qd","qe","qf",
00378     "r0","r1","r2","r3","r4","r5","r6","r7",
00379     "r8","r9","ra","rb","rc","rd","re","rf",
00380     "s0","s1","s2","s3","s4","s5","s6","s7",
00381     "s8","s9","sa","sb","sc","sd","se","sf",
00382     "t0","t1","t2","t3","t4","t5","t6","t7",
00383     "t8","t9","ta","tb","tc","td","te","tf",
00384     "u0","u1","u2","u3","u4","u5","u6","u7",
00385     "u8","u9","ua","ub","uc","ud","ue","uf",
00386     "v0","v1","v2","v3","v4","v5","v6","v7",
00387     "v8","v9","va","vb","vc","vd","ve","vf",
00388     "w0","w1","w2","w3","w4","w5","w6","w7",
00389     "w8","w9","wa","wb","wc","wd","we","wf",
00390     0
00391     };
00392 #elif defined(_IBMESA)  /* AIX/ESA */
00393     static const char MasterDevice[] = "/dev/ptyp%s";
00394     static const char SlaveDevice[] = "/dev/ttyp%s";
00395     static const char *const deviceNo[] = {
00396     "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
00397     "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
00398     "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
00399     "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
00400     "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
00401     "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
00402     "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
00403     "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
00404     "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
00405     "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
00406     "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
00407     "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
00408     "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
00409     "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
00410     "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
00411     "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff",
00412     0
00413     };
00414 #else /* 4.2BSD */
00415     static const char MasterDevice[] = "/dev/pty%s";
00416     static const char SlaveDevice[] = "/dev/tty%s";
00417     static const char *const deviceNo[] = {
00418     "p0","p1","p2","p3","p4","p5","p6","p7",
00419     "p8","p9","pa","pb","pc","pd","pe","pf",
00420     "q0","q1","q2","q3","q4","q5","q6","q7",
00421     "q8","q9","qa","qb","qc","qd","qe","qf",
00422     "r0","r1","r2","r3","r4","r5","r6","r7",
00423     "r8","r9","ra","rb","rc","rd","re","rf",
00424     "s0","s1","s2","s3","s4","s5","s6","s7",
00425     "s8","s9","sa","sb","sc","sd","se","sf",
00426     0
00427     };
00428 #endif
00429     for (p = deviceNo; *p != NULL; p++) {
00430         snprintf(MasterName, sizeof MasterName, MasterDevice, *p);
00431         if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) {
00432             rb_update_max_fd(masterfd);
00433             *master = masterfd;
00434             snprintf(SlaveName, DEVICELEN, SlaveDevice, *p);
00435             if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) {
00436                 rb_update_max_fd(slavefd);
00437                 *slave = slavefd;
00438                 if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
00439                 if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
00440                 return 0;
00441             }
00442             close(masterfd);
00443         }
00444     }
00445   error:
00446     if (slavefd != -1) close(slavefd);
00447     if (masterfd != -1) close(masterfd);
00448     if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName);
00449     return -1;
00450 #endif
00451 }
00452 
00453 static void
00454 getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
00455 {
00456     if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
00457         rb_gc();
00458         get_device_once(master, slave, SlaveName, nomesg, 1);
00459     }
00460 }
00461 
00462 static VALUE
00463 pty_close_pty(VALUE assoc)
00464 {
00465     VALUE io;
00466     int i;
00467 
00468     for (i = 0; i < 2; i++) {
00469         io = rb_ary_entry(assoc, i);
00470         if (RB_TYPE_P(io, T_FILE) && 0 <= RFILE(io)->fptr->fd)
00471             rb_io_close(io);
00472     }
00473     return Qnil;
00474 }
00475 
00476 /*
00477  * call-seq:
00478  *   PTY.open => [master_io, slave_file]
00479  *   PTY.open {|master_io, slave_file| ... } => block value
00480  *
00481  * Allocates a pty (pseudo-terminal).
00482  *
00483  * In the block form, yields two arguments <tt>master_io, slave_file</tt>
00484  * and the value of the block is returned from +open+.
00485  *
00486  * The IO and File are both closed after the block completes if they haven't
00487  * been already closed.
00488  *
00489  *   PTY.open {|master, slave|
00490  *     p master      #=> #<IO:masterpty:/dev/pts/1>
00491  *     p slave      #=> #<File:/dev/pts/1>
00492  *     p slave.path #=> "/dev/pts/1"
00493  *   }
00494  *
00495  * In the non-block form, returns a two element array, <tt>[master_io,
00496  * slave_file]</tt>.
00497  *
00498  *   master, slave = PTY.open
00499  *   # do something with master for IO, or the slave file
00500  *
00501  * The arguments in both forms are:
00502  *
00503  * +master_io+::    the master of the pty, as an IO.
00504  * +slave_file+::   the slave of the pty, as a File.  The path to the
00505  *                  terminal device is available via +slave_file.path+
00506  *
00507  */
00508 static VALUE
00509 pty_open(VALUE klass)
00510 {
00511     int master_fd, slave_fd;
00512     char slavename[DEVICELEN];
00513     VALUE master_io, slave_file;
00514     rb_io_t *master_fptr, *slave_fptr;
00515     VALUE assoc;
00516 
00517     getDevice(&master_fd, &slave_fd, slavename, 1);
00518 
00519     master_io = rb_obj_alloc(rb_cIO);
00520     MakeOpenFile(master_io, master_fptr);
00521     master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
00522     master_fptr->fd = master_fd;
00523     master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
00524 
00525     slave_file = rb_obj_alloc(rb_cFile);
00526     MakeOpenFile(slave_file, slave_fptr);
00527     slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY;
00528     slave_fptr->fd = slave_fd;
00529     slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
00530 
00531     assoc = rb_assoc_new(master_io, slave_file);
00532     if (rb_block_given_p()) {
00533         return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
00534     }
00535     return assoc;
00536 }
00537 
00538 static VALUE
00539 pty_detach_process(struct pty_info *info)
00540 {
00541     rb_detach_process(info->child_pid);
00542     return Qnil;
00543 }
00544 
00545 /*
00546  * call-seq:
00547  *   PTY.spawn(command_line)  { |r, w, pid| ... }
00548  *   PTY.spawn(command_line)  => [r, w, pid]
00549  *   PTY.spawn(command, arguments, ...)  { |r, w, pid| ... }
00550  *   PTY.spawn(command, arguments, ...)  => [r, w, pid]
00551  *
00552  * Spawns the specified command on a newly allocated pty. You can also use the
00553  * alias ::getpty.
00554  *
00555  * The command's controlling tty is set to the slave device of the pty
00556  * and its standard input/output/error is redirected to the slave device.
00557  *
00558  * +command+ and +command_line+ are the full commands to run, given a String.
00559  * Any additional +arguments+ will be passed to the command.
00560  *
00561  * === Return values
00562  *
00563  * In the non-block form this returns an array of size three,
00564  * <tt>[r, w, pid]</tt>.
00565  *
00566  * In the block form these same values will be yielded to the block:
00567  *
00568  * +r+:: A readable IO that that contains the command's
00569  *       standard output and standard error
00570  * +w+:: A writable IO that is the command's standard input
00571  * +pid+:: The process identifier for the command.
00572  */
00573 static VALUE
00574 pty_getpty(int argc, VALUE *argv, VALUE self)
00575 {
00576     VALUE res;
00577     struct pty_info info;
00578     rb_io_t *wfptr,*rfptr;
00579     VALUE rport = rb_obj_alloc(rb_cFile);
00580     VALUE wport = rb_obj_alloc(rb_cFile);
00581     char SlaveName[DEVICELEN];
00582 
00583     MakeOpenFile(rport, rfptr);
00584     MakeOpenFile(wport, wfptr);
00585 
00586     establishShell(argc, argv, &info, SlaveName);
00587 
00588     rfptr->mode = rb_io_mode_flags("r");
00589     rfptr->fd = info.fd;
00590     rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
00591 
00592     wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC;
00593     wfptr->fd = rb_cloexec_dup(info.fd);
00594     if (wfptr->fd == -1)
00595         rb_sys_fail("dup()");
00596     rb_update_max_fd(wfptr->fd);
00597     wfptr->pathv = rfptr->pathv;
00598 
00599     res = rb_ary_new2(3);
00600     rb_ary_store(res,0,(VALUE)rport);
00601     rb_ary_store(res,1,(VALUE)wport);
00602     rb_ary_store(res,2,PIDT2NUM(info.child_pid));
00603 
00604     if (rb_block_given_p()) {
00605         rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
00606         return Qnil;
00607     }
00608     return res;
00609 }
00610 
00611 NORETURN(static void raise_from_check(rb_pid_t pid, int status));
00612 static void
00613 raise_from_check(rb_pid_t pid, int status)
00614 {
00615     const char *state;
00616     VALUE msg;
00617     VALUE exc;
00618 
00619 #if defined(WIFSTOPPED)
00620 #elif defined(IF_STOPPED)
00621 #define WIFSTOPPED(status) IF_STOPPED(status)
00622 #else
00623 ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
00624 #endif /* WIFSTOPPED | IF_STOPPED */
00625     if (WIFSTOPPED(status)) { /* suspend */
00626         state = "stopped";
00627     }
00628     else if (kill(pid, 0) == 0) {
00629         state = "changed";
00630     }
00631     else {
00632         state = "exited";
00633     }
00634     msg = rb_sprintf("pty - %s: %ld", state, (long)pid);
00635     exc = rb_exc_new_str(eChildExited, msg);
00636     rb_iv_set(exc, "status", rb_last_status_get());
00637     rb_exc_raise(exc);
00638 }
00639 
00640 /*
00641  * call-seq:
00642  *   PTY.check(pid, raise = false) => Process::Status or nil
00643  *   PTY.check(pid, true)          => nil or raises PTY::ChildExited
00644  *
00645  * Checks the status of the child process specified by +pid+.
00646  * Returns +nil+ if the process is still alive.
00647  *
00648  * If the process is not alive, and +raise+ was true, a PTY::ChildExited
00649  * exception will be raised. Otherwise it will return a Process::Status
00650  * instance.
00651  *
00652  * +pid+:: The process id of the process to check
00653  * +raise+:: If +true+ and the process identified by +pid+ is no longer
00654  *           alive a PTY::ChildExited is raised.
00655  *
00656  */
00657 static VALUE
00658 pty_check(int argc, VALUE *argv, VALUE self)
00659 {
00660     VALUE pid, exc;
00661     rb_pid_t cpid;
00662     int status;
00663 
00664     rb_scan_args(argc, argv, "11", &pid, &exc);
00665     cpid = rb_waitpid(NUM2PIDT(pid), &status, WNOHANG|WUNTRACED);
00666     if (cpid == -1 || cpid == 0) return Qnil;
00667 
00668     if (!RTEST(exc)) return rb_last_status_get();
00669     raise_from_check(cpid, status);
00670 
00671     UNREACHABLE;
00672 }
00673 
00674 static VALUE cPTY;
00675 
00676 /*
00677  * Document-class: PTY::ChildExited
00678  *
00679  * Thrown when PTY::check is called for a pid that represents a process that
00680  * has exited.
00681  */
00682 
00683 /*
00684  * Document-class: PTY
00685  *
00686  * Creates and managed pseudo terminals (PTYs).  See also
00687  * http://en.wikipedia.org/wiki/Pseudo_terminal
00688  *
00689  * PTY allows you to allocate new terminals using ::open or ::spawn a new
00690  * terminal with a specific command.
00691  *
00692  * == Example
00693  *
00694  * In this example we will change the buffering type in the +factor+ command,
00695  * assuming that factor uses stdio for stdout buffering.
00696  *
00697  * If IO.pipe is used instead of PTY.open, this code deadlocks because factor's
00698  * stdout is fully buffered.
00699  *
00700  *   # start by requiring the standard library PTY
00701  *   require 'pty'
00702  *
00703  *   master, slave = PTY.open
00704  *   read, write = IO.pipe
00705  *   pid = spawn("factor", :in=>read, :out=>slave)
00706  *   read.close     # we dont need the read
00707  *   slave.close    # or the slave
00708  *
00709  *   # pipe "42" to the factor command
00710  *   write.puts "42"
00711  *   # output the response from factor
00712  *   p master.gets #=> "42: 2 3 7\n"
00713  *
00714  *   # pipe "144" to factor and print out the response
00715  *   write.puts "144"
00716  *   p master.gets #=> "144: 2 2 2 2 3 3\n"
00717  *   write.close # close the pipe
00718  *
00719  *   # The result of read operation when pty slave is closed is platform
00720  *   # dependent.
00721  *   ret = begin
00722  *           m.gets          # FreeBSD returns nil.
00723  *         rescue Errno::EIO # GNU/Linux raises EIO.
00724  *           nil
00725  *         end
00726  *   p ret #=> nil
00727  *
00728  * == License
00729  *
00730  *  C) Copyright 1998 by Akinori Ito.
00731  *
00732  *  This software may be redistributed freely for this purpose, in full
00733  *  or in part, provided that this entire copyright notice is included
00734  *  on any copies of this software and applications and derivations thereof.
00735  *
00736  *  This software is provided on an "as is" basis, without warranty of any
00737  *  kind, either expressed or implied, as to any matter including, but not
00738  *  limited to warranty of fitness of purpose, or merchantability, or
00739  *  results obtained from use of this software.
00740  */
00741 
00742 void
00743 Init_pty()
00744 {
00745     cPTY = rb_define_module("PTY");
00746     /* :nodoc */
00747     rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
00748     rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
00749     rb_define_singleton_method(cPTY,"check",pty_check,-1);
00750     rb_define_singleton_method(cPTY,"open",pty_open,0);
00751 
00752     eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
00753     rb_define_method(eChildExited,"status",echild_status,0);
00754 }
00755 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7