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