Ruby 1.9.3p327(2012-11-10revision37606)
|
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 /* Returns the exit status of the child for which PTY#check 00126 * raised this exception 00127 */ 00128 static VALUE 00129 echild_status(VALUE self) 00130 { 00131 return rb_ivar_get(self, rb_intern("status")); 00132 } 00133 00134 struct pty_info { 00135 int fd; 00136 rb_pid_t child_pid; 00137 }; 00138 00139 static void getDevice(int*, int*, char [DEVICELEN], int); 00140 00141 struct child_info { 00142 int master, slave; 00143 char *slavename; 00144 int argc; 00145 VALUE *argv; 00146 }; 00147 00148 static int 00149 chfunc(void *data, char *errbuf, size_t errbuf_len) 00150 { 00151 struct child_info *carg = data; 00152 int master = carg->master; 00153 int slave = carg->slave; 00154 int argc = carg->argc; 00155 VALUE *argv = carg->argv; 00156 00157 #define ERROR_EXIT(str) do { \ 00158 strlcpy(errbuf, (str), errbuf_len); \ 00159 return -1; \ 00160 } while (0) 00161 00162 rb_thread_atfork_before_exec(); 00163 00164 /* 00165 * Set free from process group and controlling terminal 00166 */ 00167 #ifdef HAVE_SETSID 00168 (void) setsid(); 00169 #else /* HAS_SETSID */ 00170 # ifdef HAVE_SETPGRP 00171 # ifdef SETGRP_VOID 00172 if (setpgrp() == -1) 00173 ERROR_EXIT("setpgrp()"); 00174 # else /* SETGRP_VOID */ 00175 if (setpgrp(0, getpid()) == -1) 00176 ERROR_EXIT("setpgrp()"); 00177 { 00178 int i = open("/dev/tty", O_RDONLY); 00179 if (i < 0) ERROR_EXIT("/dev/tty"); 00180 rb_update_max_fd(i); 00181 if (ioctl(i, TIOCNOTTY, (char *)0)) 00182 ERROR_EXIT("ioctl(TIOCNOTTY)"); 00183 close(i); 00184 } 00185 # endif /* SETGRP_VOID */ 00186 # endif /* HAVE_SETPGRP */ 00187 #endif /* HAS_SETSID */ 00188 00189 /* 00190 * obtain new controlling terminal 00191 */ 00192 #if defined(TIOCSCTTY) 00193 close(master); 00194 (void) ioctl(slave, TIOCSCTTY, (char *)0); 00195 /* errors ignored for sun */ 00196 #else 00197 close(slave); 00198 slave = open(carg->slavename, O_RDWR); 00199 if (slave < 0) { 00200 ERROR_EXIT("open: pty slave"); 00201 } 00202 rb_update_max_fd(slave); 00203 close(master); 00204 #endif 00205 dup2(slave,0); 00206 dup2(slave,1); 00207 dup2(slave,2); 00208 close(slave); 00209 #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID) 00210 seteuid(getuid()); 00211 #endif 00212 00213 rb_f_exec(argc, argv); 00214 return 0; 00215 #undef ERROR_EXIT 00216 } 00217 00218 static void 00219 establishShell(int argc, VALUE *argv, struct pty_info *info, 00220 char SlaveName[DEVICELEN]) 00221 { 00222 int master,slave; 00223 rb_pid_t pid; 00224 char *p, *getenv(); 00225 struct passwd *pwent; 00226 VALUE v; 00227 struct child_info carg; 00228 char errbuf[32]; 00229 00230 if (argc == 0) { 00231 const char *shellname; 00232 00233 if ((p = getenv("SHELL")) != NULL) { 00234 shellname = p; 00235 } 00236 else { 00237 pwent = getpwuid(getuid()); 00238 if (pwent && pwent->pw_shell) 00239 shellname = pwent->pw_shell; 00240 else 00241 shellname = "/bin/sh"; 00242 } 00243 v = rb_str_new2(shellname); 00244 argc = 1; 00245 argv = &v; 00246 } 00247 00248 getDevice(&master, &slave, SlaveName, 0); 00249 00250 carg.master = master; 00251 carg.slave = slave; 00252 carg.slavename = SlaveName; 00253 carg.argc = argc; 00254 carg.argv = argv; 00255 errbuf[0] = '\0'; 00256 pid = rb_fork_err(0, chfunc, &carg, Qnil, errbuf, sizeof(errbuf)); 00257 00258 if (pid < 0) { 00259 int e = errno; 00260 close(master); 00261 close(slave); 00262 errno = e; 00263 rb_sys_fail(errbuf[0] ? errbuf : "fork failed"); 00264 } 00265 00266 close(slave); 00267 00268 info->child_pid = pid; 00269 info->fd = master; 00270 } 00271 00272 static int 00273 no_mesg(char *slavedevice, int nomesg) 00274 { 00275 if (nomesg) 00276 return chmod(slavedevice, 0600); 00277 else 00278 return 0; 00279 } 00280 00281 static int 00282 get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail) 00283 { 00284 #if defined(HAVE_POSIX_OPENPT) 00285 int masterfd = -1, slavefd = -1; 00286 char *slavedevice; 00287 struct sigaction dfl, old; 00288 00289 dfl.sa_handler = SIG_DFL; 00290 dfl.sa_flags = 0; 00291 sigemptyset(&dfl.sa_mask); 00292 00293 if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error; 00294 rb_update_max_fd(masterfd); 00295 if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; 00296 if (grantpt(masterfd) == -1) goto grantpt_error; 00297 if (sigaction(SIGCHLD, &old, NULL) == -1) goto error; 00298 if (unlockpt(masterfd) == -1) goto error; 00299 if ((slavedevice = ptsname(masterfd)) == NULL) goto error; 00300 if (no_mesg(slavedevice, nomesg) == -1) goto error; 00301 if ((slavefd = open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error; 00302 rb_update_max_fd(slavefd); 00303 00304 #if defined I_PUSH && !defined linux 00305 if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error; 00306 if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error; 00307 if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error; 00308 #endif 00309 00310 *master = masterfd; 00311 *slave = slavefd; 00312 strlcpy(SlaveName, slavedevice, DEVICELEN); 00313 return 0; 00314 00315 grantpt_error: 00316 sigaction(SIGCHLD, &old, NULL); 00317 error: 00318 if (slavefd != -1) close(slavefd); 00319 if (masterfd != -1) close(masterfd); 00320 if (fail) { 00321 rb_raise(rb_eRuntimeError, "can't get Master/Slave device"); 00322 } 00323 return -1; 00324 #elif defined HAVE_OPENPTY 00325 /* 00326 * Use openpty(3) of 4.3BSD Reno and later, 00327 * or the same interface function. 00328 */ 00329 if (openpty(master, slave, SlaveName, 00330 (struct termios *)0, (struct winsize *)0) == -1) { 00331 if (!fail) return -1; 00332 rb_raise(rb_eRuntimeError, "openpty() failed"); 00333 } 00334 rb_update_max_fd(*master); 00335 rb_update_max_fd(*slave); 00336 if (no_mesg(SlaveName, nomesg) == -1) { 00337 if (!fail) return -1; 00338 rb_raise(rb_eRuntimeError, "can't chmod slave pty"); 00339 } 00340 00341 return 0; 00342 00343 #elif defined HAVE__GETPTY 00344 char *name; 00345 mode_t mode = nomesg ? 0600 : 0622; 00346 00347 if (!(name = _getpty(master, O_RDWR, mode, 0))) { 00348 if (!fail) return -1; 00349 rb_raise(rb_eRuntimeError, "_getpty() failed"); 00350 } 00351 rb_update_max_fd(*master); 00352 00353 *slave = open(name, O_RDWR); 00354 /* error check? */ 00355 rb_update_max_fd(*slave); 00356 strlcpy(SlaveName, name, DEVICELEN); 00357 00358 return 0; 00359 #elif defined(HAVE_PTSNAME) 00360 int masterfd = -1, slavefd = -1; 00361 char *slavedevice; 00362 void (*s)(); 00363 00364 extern char *ptsname(int); 00365 extern int unlockpt(int); 00366 extern int grantpt(int); 00367 00368 if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; 00369 rb_update_max_fd(masterfd); 00370 s = signal(SIGCHLD, SIG_DFL); 00371 if(grantpt(masterfd) == -1) goto error; 00372 signal(SIGCHLD, s); 00373 if(unlockpt(masterfd) == -1) goto error; 00374 if((slavedevice = ptsname(masterfd)) == NULL) goto error; 00375 if (no_mesg(slavedevice, nomesg) == -1) goto error; 00376 if((slavefd = open(slavedevice, O_RDWR, 0)) == -1) goto error; 00377 rb_update_max_fd(slavefd); 00378 #if defined I_PUSH && !defined linux 00379 if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error; 00380 if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error; 00381 ioctl(slavefd, I_PUSH, "ttcompat"); 00382 #endif 00383 *master = masterfd; 00384 *slave = slavefd; 00385 strlcpy(SlaveName, slavedevice, DEVICELEN); 00386 return 0; 00387 00388 error: 00389 if (slavefd != -1) close(slavefd); 00390 if (masterfd != -1) close(masterfd); 00391 if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device"); 00392 return -1; 00393 #else 00394 int masterfd = -1, slavefd = -1; 00395 const char *const *p; 00396 char MasterName[DEVICELEN]; 00397 00398 for (p = deviceNo; *p != NULL; p++) { 00399 snprintf(MasterName, sizeof MasterName, MasterDevice, *p); 00400 if ((masterfd = open(MasterName,O_RDWR,0)) >= 0) { 00401 rb_update_max_fd(masterfd); 00402 *master = masterfd; 00403 snprintf(SlaveName, DEVICELEN, SlaveDevice, *p); 00404 if ((slavefd = open(SlaveName,O_RDWR,0)) >= 0) { 00405 rb_update_max_fd(slavefd); 00406 *slave = slavefd; 00407 if (chown(SlaveName, getuid(), getgid()) != 0) goto error; 00408 if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error; 00409 return 0; 00410 } 00411 close(masterfd); 00412 } 00413 } 00414 error: 00415 if (slavefd != -1) close(slavefd); 00416 if (masterfd != -1) close(masterfd); 00417 if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName); 00418 return -1; 00419 #endif 00420 } 00421 00422 static void 00423 getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg) 00424 { 00425 if (get_device_once(master, slave, SlaveName, nomesg, 0)) { 00426 rb_gc(); 00427 get_device_once(master, slave, SlaveName, nomesg, 1); 00428 } 00429 } 00430 00431 static VALUE 00432 pty_close_pty(VALUE assoc) 00433 { 00434 VALUE io; 00435 int i; 00436 00437 for (i = 0; i < 2; i++) { 00438 io = rb_ary_entry(assoc, i); 00439 if (TYPE(io) == T_FILE && 0 <= RFILE(io)->fptr->fd) 00440 rb_io_close(io); 00441 } 00442 return Qnil; 00443 } 00444 00445 /* 00446 * call-seq: 00447 * PTY.open => [master_io, slave_file] 00448 * PTY.open {|master_io, slave_file| ... } => block value 00449 * 00450 * Allocates a pty (pseudo-terminal). 00451 * 00452 * In the non-block form, returns a two element array, <tt>[master_io, 00453 * slave_file]</tt>. 00454 * 00455 * In the block form, yields two arguments <tt>master_io, slave_file</tt> 00456 * and the value of the block is returned from +open+. 00457 * 00458 * The IO and File are both closed after the block completes if they haven't 00459 * been already closed. 00460 * 00461 * The arguments in both forms are: 00462 * 00463 * <tt>master_io</tt>:: the master of the pty, as an IO. 00464 * <tt>slave_file</tt>:: the slave of the pty, as a File. The path to the 00465 * terminal device is available via 00466 * <tt>slave_file.path</tt> 00467 * 00468 * === Example 00469 * 00470 * PTY.open {|m, s| 00471 * p m #=> #<IO:masterpty:/dev/pts/1> 00472 * p s #=> #<File:/dev/pts/1> 00473 * p s.path #=> "/dev/pts/1" 00474 * } 00475 * 00476 * # Change the buffering type in factor command, 00477 * # assuming that factor uses stdio for stdout buffering. 00478 * # If IO.pipe is used instead of PTY.open, 00479 * # this code deadlocks because factor's stdout is fully buffered. 00480 * require 'io/console' # for IO#raw! 00481 * m, s = PTY.open 00482 * s.raw! # disable newline conversion. 00483 * r, w = IO.pipe 00484 * pid = spawn("factor", :in=>r, :out=>s) 00485 * r.close 00486 * s.close 00487 * w.puts "42" 00488 * p m.gets #=> "42: 2 3 7\n" 00489 * w.puts "144" 00490 * p m.gets #=> "144: 2 2 2 2 3 3\n" 00491 * w.close 00492 * # The result of read operation when pty slave is closed is platform 00493 * # dependent. 00494 * ret = begin 00495 * m.gets # FreeBSD returns nil. 00496 * rescue Errno::EIO # GNU/Linux raises EIO. 00497 * nil 00498 * end 00499 * p ret #=> nil 00500 * 00501 */ 00502 static VALUE 00503 pty_open(VALUE klass) 00504 { 00505 int master_fd, slave_fd; 00506 char slavename[DEVICELEN]; 00507 VALUE master_io, slave_file; 00508 rb_io_t *master_fptr, *slave_fptr; 00509 VALUE assoc; 00510 00511 getDevice(&master_fd, &slave_fd, slavename, 1); 00512 00513 master_io = rb_obj_alloc(rb_cIO); 00514 MakeOpenFile(master_io, master_fptr); 00515 master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX; 00516 master_fptr->fd = master_fd; 00517 master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename)); 00518 00519 slave_file = rb_obj_alloc(rb_cFile); 00520 MakeOpenFile(slave_file, slave_fptr); 00521 slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY; 00522 slave_fptr->fd = slave_fd; 00523 slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename)); 00524 00525 assoc = rb_assoc_new(master_io, slave_file); 00526 if (rb_block_given_p()) { 00527 return rb_ensure(rb_yield, assoc, pty_close_pty, assoc); 00528 } 00529 return assoc; 00530 } 00531 00532 static VALUE 00533 pty_detach_process(struct pty_info *info) 00534 { 00535 rb_detach_process(info->child_pid); 00536 return Qnil; 00537 } 00538 00539 /* 00540 * call-seq: 00541 * PTY.spawn(command_line) { |r, w, pid| ... } 00542 * PTY.spawn(command_line) => [r, w, pid] 00543 * PTY.spawn(command, args, ...) { |r, w, pid| ... } 00544 * PTY.spawn(command, args, ...) => [r, w, pid] 00545 * PTY.getpty(command_line) { |r, w, pid| ... } 00546 * PTY.getpty(command_line) => [r, w, pid] 00547 * PTY.getpty(command, args, ...) { |r, w, pid| ... } 00548 * PTY.getpty(command, args, ...) => [r, w, pid] 00549 * 00550 * Spawns the specified command on a newly allocated pty. 00551 * 00552 * The command's controlling tty is set to the slave device of the pty 00553 * and its standard input/output/error is redirected to the slave device. 00554 * 00555 * <tt>command_line</tt>:: The full command line to run 00556 * <tt>command</tt>:: The command to run, as a String. 00557 * <tt>args</tt>:: Zero or more arguments, as Strings, representing 00558 * the arguments to +command+ 00559 * 00560 * In the non-block form this returns an array of size three, 00561 * <tt>[r, w, pid]</tt>. In the block form the block will be called with 00562 * these as arguments, <tt>|r,w,pid|</tt>: 00563 * 00564 * +r+:: An IO that can be read from that contains the command's 00565 * standard output and standard error 00566 * +w+:: An IO that can be written to that is the command's 00567 * standard input 00568 * +pid+:: The process identifier for the command. 00569 */ 00570 static VALUE 00571 pty_getpty(int argc, VALUE *argv, VALUE self) 00572 { 00573 VALUE res; 00574 struct pty_info info; 00575 rb_io_t *wfptr,*rfptr; 00576 VALUE rport = rb_obj_alloc(rb_cFile); 00577 VALUE wport = rb_obj_alloc(rb_cFile); 00578 char SlaveName[DEVICELEN]; 00579 00580 MakeOpenFile(rport, rfptr); 00581 MakeOpenFile(wport, wfptr); 00582 00583 establishShell(argc, argv, &info, SlaveName); 00584 00585 rfptr->mode = rb_io_mode_flags("r"); 00586 rfptr->fd = info.fd; 00587 rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName)); 00588 00589 wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC; 00590 wfptr->fd = dup(info.fd); 00591 if (wfptr->fd == -1) 00592 rb_sys_fail("dup()"); 00593 rb_update_max_fd(wfptr->fd); 00594 wfptr->pathv = rfptr->pathv; 00595 00596 res = rb_ary_new2(3); 00597 rb_ary_store(res,0,(VALUE)rport); 00598 rb_ary_store(res,1,(VALUE)wport); 00599 rb_ary_store(res,2,PIDT2NUM(info.child_pid)); 00600 00601 if (rb_block_given_p()) { 00602 rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info); 00603 return Qnil; 00604 } 00605 return res; 00606 } 00607 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. If the process 00643 * is not alive, will return a <tt>Process::Status</tt> or raise 00644 * a <tt>PTY::ChildExited</tt> (if +raise+ was true). 00645 * 00646 * +pid+:: The process id of the process to check 00647 * +raise+:: If true and the process identified by +pid+ is no longer 00648 * alive a <tt>PTY::ChildExited</tt> is raised. 00649 * 00650 * Returns nil or a <tt>Process::Status</tt> when +raise+ is false. 00651 */ 00652 static VALUE 00653 pty_check(int argc, VALUE *argv, VALUE self) 00654 { 00655 VALUE pid, exc; 00656 pid_t cpid; 00657 int status; 00658 00659 rb_scan_args(argc, argv, "11", &pid, &exc); 00660 cpid = rb_waitpid(NUM2PIDT(pid), &status, WNOHANG|WUNTRACED); 00661 if (cpid == -1 || cpid == 0) return Qnil; 00662 00663 if (!RTEST(exc)) return rb_last_status_get(); 00664 raise_from_check(cpid, status); 00665 return Qnil; /* not reached */ 00666 } 00667 00668 static VALUE cPTY; 00669 00670 /* 00671 * Document-class: PTY::ChildExited 00672 * 00673 * Thrown when PTY#check is called for a pid that represents a process that 00674 * has exited. 00675 */ 00676 00677 /* 00678 * Document-class: PTY 00679 * 00680 * Creates and managed pseudo terminals (PTYs). See also 00681 * http://en.wikipedia.org/wiki/Pseudo_terminal 00682 */ 00683 00684 void 00685 Init_pty() 00686 { 00687 cPTY = rb_define_module("PTY"); 00688 rb_define_module_function(cPTY,"getpty",pty_getpty,-1); 00689 rb_define_module_function(cPTY,"spawn",pty_getpty,-1); 00690 rb_define_singleton_method(cPTY,"check",pty_check,-1); 00691 rb_define_singleton_method(cPTY,"open",pty_open,0); 00692 00693 eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError); 00694 rb_define_method(eChildExited,"status",echild_status,0); 00695 } 00696