1#include "ruby/config.h" 2#ifdef RUBY_EXTCONF_H 3#include RUBY_EXTCONF_H 4#endif 5#include <stdlib.h> 6#include <stdio.h> 7#include <sys/types.h> 8#include <sys/stat.h> 9#include <sys/file.h> 10#include <fcntl.h> 11#include <errno.h> 12#include <pwd.h> 13#ifdef HAVE_SYS_IOCTL_H 14#include <sys/ioctl.h> 15#endif 16#ifdef HAVE_LIBUTIL_H 17#include <libutil.h> 18#endif 19#ifdef HAVE_UTIL_H 20#include <util.h> 21#endif 22#ifdef HAVE_PTY_H 23#include <pty.h> 24#endif 25#ifdef HAVE_SYS_WAIT_H 26#include <sys/wait.h> 27#else 28#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f) 29#endif 30#include <ctype.h> 31 32#include "ruby/ruby.h" 33#include "ruby/io.h" 34#include "ruby/util.h" 35#include "internal.h" 36 37#include <signal.h> 38#ifdef HAVE_SYS_STROPTS_H 39#include <sys/stropts.h> 40#endif 41 42#ifdef HAVE_UNISTD_H 43#include <unistd.h> 44#endif 45 46#define DEVICELEN 16 47 48#ifndef HAVE_SETEUID 49# ifdef HAVE_SETREUID 50# define seteuid(e) setreuid(-1, (e)) 51# else /* NOT HAVE_SETREUID */ 52# ifdef HAVE_SETRESUID 53# define seteuid(e) setresuid(-1, (e), -1) 54# else /* NOT HAVE_SETRESUID */ 55 /* I can't set euid. (;_;) */ 56# endif /* HAVE_SETRESUID */ 57# endif /* HAVE_SETREUID */ 58#endif /* NO_SETEUID */ 59 60static VALUE eChildExited; 61 62/* Returns the exit status of the child for which PTY#check 63 * raised this exception 64 */ 65static VALUE 66echild_status(VALUE self) 67{ 68 return rb_ivar_get(self, rb_intern("status")); 69} 70 71struct pty_info { 72 int fd; 73 rb_pid_t child_pid; 74}; 75 76static void getDevice(int*, int*, char [DEVICELEN], int); 77 78struct child_info { 79 int master, slave; 80 char *slavename; 81 VALUE execarg_obj; 82 struct rb_execarg *eargp; 83}; 84 85static int 86chfunc(void *data, char *errbuf, size_t errbuf_len) 87{ 88 struct child_info *carg = data; 89 int master = carg->master; 90 int slave = carg->slave; 91 92#define ERROR_EXIT(str) do { \ 93 strlcpy(errbuf, (str), errbuf_len); \ 94 return -1; \ 95 } while (0) 96 97 /* 98 * Set free from process group and controlling terminal 99 */ 100#ifdef HAVE_SETSID 101 (void) setsid(); 102#else /* HAS_SETSID */ 103# ifdef HAVE_SETPGRP 104# ifdef SETGRP_VOID 105 if (setpgrp() == -1) 106 ERROR_EXIT("setpgrp()"); 107# else /* SETGRP_VOID */ 108 if (setpgrp(0, getpid()) == -1) 109 ERROR_EXIT("setpgrp()"); 110 { 111 int i = rb_cloexec_open("/dev/tty", O_RDONLY, 0); 112 if (i < 0) ERROR_EXIT("/dev/tty"); 113 rb_update_max_fd(i); 114 if (ioctl(i, TIOCNOTTY, (char *)0)) 115 ERROR_EXIT("ioctl(TIOCNOTTY)"); 116 close(i); 117 } 118# endif /* SETGRP_VOID */ 119# endif /* HAVE_SETPGRP */ 120#endif /* HAS_SETSID */ 121 122 /* 123 * obtain new controlling terminal 124 */ 125#if defined(TIOCSCTTY) 126 close(master); 127 (void) ioctl(slave, TIOCSCTTY, (char *)0); 128 /* errors ignored for sun */ 129#else 130 close(slave); 131 slave = rb_cloexec_open(carg->slavename, O_RDWR, 0); 132 if (slave < 0) { 133 ERROR_EXIT("open: pty slave"); 134 } 135 rb_update_max_fd(slave); 136 close(master); 137#endif 138 dup2(slave,0); 139 dup2(slave,1); 140 dup2(slave,2); 141 close(slave); 142#if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID) 143 seteuid(getuid()); 144#endif 145 146 return rb_exec_async_signal_safe(carg->eargp, errbuf, sizeof(errbuf_len)); 147#undef ERROR_EXIT 148} 149 150static void 151establishShell(int argc, VALUE *argv, struct pty_info *info, 152 char SlaveName[DEVICELEN]) 153{ 154 int master, slave, status = 0; 155 rb_pid_t pid; 156 char *p, *getenv(); 157 struct passwd *pwent; 158 VALUE v; 159 struct child_info carg; 160 char errbuf[32]; 161 162 if (argc == 0) { 163 const char *shellname; 164 165 if ((p = getenv("SHELL")) != NULL) { 166 shellname = p; 167 } 168 else { 169 pwent = getpwuid(getuid()); 170 if (pwent && pwent->pw_shell) 171 shellname = pwent->pw_shell; 172 else 173 shellname = "/bin/sh"; 174 } 175 v = rb_str_new2(shellname); 176 argc = 1; 177 argv = &v; 178 } 179 180 carg.execarg_obj = rb_execarg_new(argc, argv, 1); 181 carg.eargp = rb_execarg_get(carg.execarg_obj); 182 rb_execarg_fixup(carg.execarg_obj); 183 184 getDevice(&master, &slave, SlaveName, 0); 185 186 carg.master = master; 187 carg.slave = slave; 188 carg.slavename = SlaveName; 189 errbuf[0] = '\0'; 190 pid = rb_fork_async_signal_safe(&status, chfunc, &carg, Qnil, errbuf, sizeof(errbuf)); 191 192 if (pid < 0) { 193 int e = errno; 194 close(master); 195 close(slave); 196 errno = e; 197 if (status) rb_jump_tag(status); 198 rb_sys_fail(errbuf[0] ? errbuf : "fork failed"); 199 } 200 201 close(slave); 202 203 info->child_pid = pid; 204 info->fd = master; 205 206 RB_GC_GUARD(carg.execarg_obj); 207} 208 209static int 210no_mesg(char *slavedevice, int nomesg) 211{ 212 if (nomesg) 213 return chmod(slavedevice, 0600); 214 else 215 return 0; 216} 217 218static int 219get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail) 220{ 221#if defined(HAVE_POSIX_OPENPT) 222 /* Unix98 PTY */ 223 int masterfd = -1, slavefd = -1; 224 char *slavedevice; 225 struct sigaction dfl, old; 226 227 dfl.sa_handler = SIG_DFL; 228 dfl.sa_flags = 0; 229 sigemptyset(&dfl.sa_mask); 230 231#if defined(__sun) || defined(__FreeBSD__) 232 /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ 233 /* FreeBSD 8 supported O_CLOEXEC for posix_openpt, but FreeBSD 9 removed it. 234 * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */ 235 if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error; 236 if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; 237 if (grantpt(masterfd) == -1) goto grantpt_error; 238 rb_fd_fix_cloexec(masterfd); 239#else 240 { 241 int flags = O_RDWR|O_NOCTTY; 242# if defined(O_CLOEXEC) 243 /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally. 244 * So version dependency on GNU/Linux is same as O_CLOEXEC with open(). 245 * O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */ 246 flags |= O_CLOEXEC; 247# endif 248 if ((masterfd = posix_openpt(flags)) == -1) goto error; 249 } 250 rb_fd_fix_cloexec(masterfd); 251 if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; 252 if (grantpt(masterfd) == -1) goto grantpt_error; 253#endif 254 if (sigaction(SIGCHLD, &old, NULL) == -1) goto error; 255 if (unlockpt(masterfd) == -1) goto error; 256 if ((slavedevice = ptsname(masterfd)) == NULL) goto error; 257 if (no_mesg(slavedevice, nomesg) == -1) goto error; 258 if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error; 259 rb_update_max_fd(slavefd); 260 261#if defined(I_PUSH) && !defined(__linux__) 262 if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error; 263 if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error; 264 if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error; 265#endif 266 267 *master = masterfd; 268 *slave = slavefd; 269 strlcpy(SlaveName, slavedevice, DEVICELEN); 270 return 0; 271 272 grantpt_error: 273 sigaction(SIGCHLD, &old, NULL); 274 error: 275 if (slavefd != -1) close(slavefd); 276 if (masterfd != -1) close(masterfd); 277 if (fail) { 278 rb_raise(rb_eRuntimeError, "can't get Master/Slave device"); 279 } 280 return -1; 281#elif defined HAVE_OPENPTY 282/* 283 * Use openpty(3) of 4.3BSD Reno and later, 284 * or the same interface function. 285 */ 286 if (openpty(master, slave, SlaveName, 287 (struct termios *)0, (struct winsize *)0) == -1) { 288 if (!fail) return -1; 289 rb_raise(rb_eRuntimeError, "openpty() failed"); 290 } 291 rb_fd_fix_cloexec(*master); 292 rb_fd_fix_cloexec(*slave); 293 if (no_mesg(SlaveName, nomesg) == -1) { 294 if (!fail) return -1; 295 rb_raise(rb_eRuntimeError, "can't chmod slave pty"); 296 } 297 298 return 0; 299 300#elif defined HAVE__GETPTY 301 /* SGI IRIX */ 302 char *name; 303 mode_t mode = nomesg ? 0600 : 0622; 304 305 if (!(name = _getpty(master, O_RDWR, mode, 0))) { 306 if (!fail) return -1; 307 rb_raise(rb_eRuntimeError, "_getpty() failed"); 308 } 309 rb_fd_fix_cloexec(*master); 310 311 *slave = rb_cloexec_open(name, O_RDWR, 0); 312 /* error check? */ 313 rb_update_max_fd(*slave); 314 strlcpy(SlaveName, name, DEVICELEN); 315 316 return 0; 317#elif defined(HAVE_PTSNAME) 318 /* System V */ 319 int masterfd = -1, slavefd = -1; 320 char *slavedevice; 321 void (*s)(); 322 323 extern char *ptsname(int); 324 extern int unlockpt(int); 325 extern int grantpt(int); 326 327#if defined(__sun) 328 /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ 329 if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; 330 s = signal(SIGCHLD, SIG_DFL); 331 if(grantpt(masterfd) == -1) goto error; 332 rb_fd_fix_cloexec(masterfd); 333#else 334 if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; 335 rb_update_max_fd(masterfd); 336 s = signal(SIGCHLD, SIG_DFL); 337 if(grantpt(masterfd) == -1) goto error; 338#endif 339 signal(SIGCHLD, s); 340 if(unlockpt(masterfd) == -1) goto error; 341 if((slavedevice = ptsname(masterfd)) == NULL) goto error; 342 if (no_mesg(slavedevice, nomesg) == -1) goto error; 343 if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error; 344 rb_update_max_fd(slavefd); 345#if defined(I_PUSH) && !defined(__linux__) 346 if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error; 347 if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error; 348 ioctl(slavefd, I_PUSH, "ttcompat"); 349#endif 350 *master = masterfd; 351 *slave = slavefd; 352 strlcpy(SlaveName, slavedevice, DEVICELEN); 353 return 0; 354 355 error: 356 if (slavefd != -1) close(slavefd); 357 if (masterfd != -1) close(masterfd); 358 if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device"); 359 return -1; 360#else 361 /* BSD */ 362 int masterfd = -1, slavefd = -1; 363 const char *const *p; 364 char MasterName[DEVICELEN]; 365 366#if defined(__hpux) 367 static const char MasterDevice[] = "/dev/ptym/pty%s"; 368 static const char SlaveDevice[] = "/dev/pty/tty%s"; 369 static const char *const deviceNo[] = { 370 "p0","p1","p2","p3","p4","p5","p6","p7", 371 "p8","p9","pa","pb","pc","pd","pe","pf", 372 "q0","q1","q2","q3","q4","q5","q6","q7", 373 "q8","q9","qa","qb","qc","qd","qe","qf", 374 "r0","r1","r2","r3","r4","r5","r6","r7", 375 "r8","r9","ra","rb","rc","rd","re","rf", 376 "s0","s1","s2","s3","s4","s5","s6","s7", 377 "s8","s9","sa","sb","sc","sd","se","sf", 378 "t0","t1","t2","t3","t4","t5","t6","t7", 379 "t8","t9","ta","tb","tc","td","te","tf", 380 "u0","u1","u2","u3","u4","u5","u6","u7", 381 "u8","u9","ua","ub","uc","ud","ue","uf", 382 "v0","v1","v2","v3","v4","v5","v6","v7", 383 "v8","v9","va","vb","vc","vd","ve","vf", 384 "w0","w1","w2","w3","w4","w5","w6","w7", 385 "w8","w9","wa","wb","wc","wd","we","wf", 386 0 387 }; 388#elif defined(_IBMESA) /* AIX/ESA */ 389 static const char MasterDevice[] = "/dev/ptyp%s"; 390 static const char SlaveDevice[] = "/dev/ttyp%s"; 391 static const char *const deviceNo[] = { 392 "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f", 393 "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f", 394 "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f", 395 "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f", 396 "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f", 397 "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f", 398 "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f", 399 "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f", 400 "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f", 401 "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f", 402 "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af", 403 "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf", 404 "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf", 405 "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df", 406 "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef", 407 "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff", 408 0 409 }; 410#else /* 4.2BSD */ 411 static const char MasterDevice[] = "/dev/pty%s"; 412 static const char SlaveDevice[] = "/dev/tty%s"; 413 static const char *const deviceNo[] = { 414 "p0","p1","p2","p3","p4","p5","p6","p7", 415 "p8","p9","pa","pb","pc","pd","pe","pf", 416 "q0","q1","q2","q3","q4","q5","q6","q7", 417 "q8","q9","qa","qb","qc","qd","qe","qf", 418 "r0","r1","r2","r3","r4","r5","r6","r7", 419 "r8","r9","ra","rb","rc","rd","re","rf", 420 "s0","s1","s2","s3","s4","s5","s6","s7", 421 "s8","s9","sa","sb","sc","sd","se","sf", 422 0 423 }; 424#endif 425 for (p = deviceNo; *p != NULL; p++) { 426 snprintf(MasterName, sizeof MasterName, MasterDevice, *p); 427 if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) { 428 rb_update_max_fd(masterfd); 429 *master = masterfd; 430 snprintf(SlaveName, DEVICELEN, SlaveDevice, *p); 431 if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) { 432 rb_update_max_fd(slavefd); 433 *slave = slavefd; 434 if (chown(SlaveName, getuid(), getgid()) != 0) goto error; 435 if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error; 436 return 0; 437 } 438 close(masterfd); 439 } 440 } 441 error: 442 if (slavefd != -1) close(slavefd); 443 if (masterfd != -1) close(masterfd); 444 if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName); 445 return -1; 446#endif 447} 448 449static void 450getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg) 451{ 452 if (get_device_once(master, slave, SlaveName, nomesg, 0)) { 453 rb_gc(); 454 get_device_once(master, slave, SlaveName, nomesg, 1); 455 } 456} 457 458static VALUE 459pty_close_pty(VALUE assoc) 460{ 461 VALUE io; 462 int i; 463 464 for (i = 0; i < 2; i++) { 465 io = rb_ary_entry(assoc, i); 466 if (RB_TYPE_P(io, T_FILE) && 0 <= RFILE(io)->fptr->fd) 467 rb_io_close(io); 468 } 469 return Qnil; 470} 471 472/* 473 * call-seq: 474 * PTY.open => [master_io, slave_file] 475 * PTY.open {|master_io, slave_file| ... } => block value 476 * 477 * Allocates a pty (pseudo-terminal). 478 * 479 * In the block form, yields two arguments <tt>master_io, slave_file</tt> 480 * and the value of the block is returned from +open+. 481 * 482 * The IO and File are both closed after the block completes if they haven't 483 * been already closed. 484 * 485 * PTY.open {|master, slave| 486 * p master #=> #<IO:masterpty:/dev/pts/1> 487 * p slave #=> #<File:/dev/pts/1> 488 * p slave.path #=> "/dev/pts/1" 489 * } 490 * 491 * In the non-block form, returns a two element array, <tt>[master_io, 492 * slave_file]</tt>. 493 * 494 * master, slave = PTY.open 495 * # do something with master for IO, or the slave file 496 * 497 * The arguments in both forms are: 498 * 499 * +master_io+:: the master of the pty, as an IO. 500 * +slave_file+:: the slave of the pty, as a File. The path to the 501 * terminal device is available via +slave_file.path+ 502 * 503 */ 504static VALUE 505pty_open(VALUE klass) 506{ 507 int master_fd, slave_fd; 508 char slavename[DEVICELEN]; 509 VALUE master_io, slave_file; 510 rb_io_t *master_fptr, *slave_fptr; 511 VALUE assoc; 512 513 getDevice(&master_fd, &slave_fd, slavename, 1); 514 515 master_io = rb_obj_alloc(rb_cIO); 516 MakeOpenFile(master_io, master_fptr); 517 master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX; 518 master_fptr->fd = master_fd; 519 master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename)); 520 521 slave_file = rb_obj_alloc(rb_cFile); 522 MakeOpenFile(slave_file, slave_fptr); 523 slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY; 524 slave_fptr->fd = slave_fd; 525 slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename)); 526 527 assoc = rb_assoc_new(master_io, slave_file); 528 if (rb_block_given_p()) { 529 return rb_ensure(rb_yield, assoc, pty_close_pty, assoc); 530 } 531 return assoc; 532} 533 534static VALUE 535pty_detach_process(struct pty_info *info) 536{ 537 rb_detach_process(info->child_pid); 538 return Qnil; 539} 540 541/* 542 * call-seq: 543 * PTY.spawn(command_line) { |r, w, pid| ... } 544 * PTY.spawn(command_line) => [r, w, pid] 545 * PTY.spawn(command, arguments, ...) { |r, w, pid| ... } 546 * PTY.spawn(command, arguments, ...) => [r, w, pid] 547 * 548 * Spawns the specified command on a newly allocated pty. You can also use the 549 * alias ::getpty. 550 * 551 * The command's controlling tty is set to the slave device of the pty 552 * and its standard input/output/error is redirected to the slave device. 553 * 554 * +command+ and +command_line+ are the full commands to run, given a String. 555 * Any additional +arguments+ will be passed to the command. 556 * 557 * === Return values 558 * 559 * In the non-block form this returns an array of size three, 560 * <tt>[r, w, pid]</tt>. 561 * 562 * In the block form these same values will be yielded to the block: 563 * 564 * +r+:: A readable IO that that contains the command's 565 * standard output and standard error 566 * +w+:: A writable IO that is the command's standard input 567 * +pid+:: The process identifier for the command. 568 */ 569static VALUE 570pty_getpty(int argc, VALUE *argv, VALUE self) 571{ 572 VALUE res; 573 struct pty_info info; 574 rb_io_t *wfptr,*rfptr; 575 VALUE rport = rb_obj_alloc(rb_cFile); 576 VALUE wport = rb_obj_alloc(rb_cFile); 577 char SlaveName[DEVICELEN]; 578 579 MakeOpenFile(rport, rfptr); 580 MakeOpenFile(wport, wfptr); 581 582 establishShell(argc, argv, &info, SlaveName); 583 584 rfptr->mode = rb_io_mode_flags("r"); 585 rfptr->fd = info.fd; 586 rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName)); 587 588 wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC; 589 wfptr->fd = rb_cloexec_dup(info.fd); 590 if (wfptr->fd == -1) 591 rb_sys_fail("dup()"); 592 rb_update_max_fd(wfptr->fd); 593 wfptr->pathv = rfptr->pathv; 594 595 res = rb_ary_new2(3); 596 rb_ary_store(res,0,(VALUE)rport); 597 rb_ary_store(res,1,(VALUE)wport); 598 rb_ary_store(res,2,PIDT2NUM(info.child_pid)); 599 600 if (rb_block_given_p()) { 601 rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info); 602 return Qnil; 603 } 604 return res; 605} 606 607NORETURN(static void raise_from_check(pid_t pid, int status)); 608static void 609raise_from_check(pid_t pid, int status) 610{ 611 const char *state; 612 VALUE msg; 613 VALUE exc; 614 615#if defined(WIFSTOPPED) 616#elif defined(IF_STOPPED) 617#define WIFSTOPPED(status) IF_STOPPED(status) 618#else 619---->> Either IF_STOPPED or WIFSTOPPED is needed <<---- 620#endif /* WIFSTOPPED | IF_STOPPED */ 621 if (WIFSTOPPED(status)) { /* suspend */ 622 state = "stopped"; 623 } 624 else if (kill(pid, 0) == 0) { 625 state = "changed"; 626 } 627 else { 628 state = "exited"; 629 } 630 msg = rb_sprintf("pty - %s: %ld", state, (long)pid); 631 exc = rb_exc_new3(eChildExited, msg); 632 rb_iv_set(exc, "status", rb_last_status_get()); 633 rb_exc_raise(exc); 634} 635 636/* 637 * call-seq: 638 * PTY.check(pid, raise = false) => Process::Status or nil 639 * PTY.check(pid, true) => nil or raises PTY::ChildExited 640 * 641 * Checks the status of the child process specified by +pid+. 642 * Returns +nil+ if the process is still alive. 643 * 644 * If the process is not alive, and +raise+ was true, a PTY::ChildExited 645 * exception will be raised. Otherwise it will return a Process::Status 646 * instance. 647 * 648 * +pid+:: The process id of the process to check 649 * +raise+:: If +true+ and the process identified by +pid+ is no longer 650 * alive a PTY::ChildExited is raised. 651 * 652 */ 653static VALUE 654pty_check(int argc, VALUE *argv, VALUE self) 655{ 656 VALUE pid, exc; 657 pid_t cpid; 658 int status; 659 660 rb_scan_args(argc, argv, "11", &pid, &exc); 661 cpid = rb_waitpid(NUM2PIDT(pid), &status, WNOHANG|WUNTRACED); 662 if (cpid == -1 || cpid == 0) return Qnil; 663 664 if (!RTEST(exc)) return rb_last_status_get(); 665 raise_from_check(cpid, status); 666 667 UNREACHABLE; 668} 669 670static VALUE cPTY; 671 672/* 673 * Document-class: PTY::ChildExited 674 * 675 * Thrown when PTY::check is called for a pid that represents a process that 676 * has exited. 677 */ 678 679/* 680 * Document-class: PTY 681 * 682 * Creates and managed pseudo terminals (PTYs). See also 683 * http://en.wikipedia.org/wiki/Pseudo_terminal 684 * 685 * PTY allows you to allocate new terminals using ::open or ::spawn a new 686 * terminal with a specific command. 687 * 688 * == Example 689 * 690 * In this example we will change the buffering type in the +factor+ command, 691 * assuming that factor uses stdio for stdout buffering. 692 * 693 * If IO.pipe is used instead of PTY.open, this code deadlocks because factor's 694 * stdout is fully buffered. 695 * 696 * # start by requiring the standard library PTY 697 * require 'pty' 698 * 699 * master, slave = PTY.open 700 * read, write = IO.pipe 701 * pid = spawn("factor", :in=>read, :out=>slave) 702 * read.close # we dont need the read 703 * slave.close # or the slave 704 * 705 * # pipe "42" to the factor command 706 * write.puts "42" 707 * # output the response from factor 708 * p master.gets #=> "42: 2 3 7\n" 709 * 710 * # pipe "144" to factor and print out the response 711 * write.puts "144" 712 * p master.gets #=> "144: 2 2 2 2 3 3\n" 713 * write.close # close the pipe 714 * 715 * # The result of read operation when pty slave is closed is platform 716 * # dependent. 717 * ret = begin 718 * m.gets # FreeBSD returns nil. 719 * rescue Errno::EIO # GNU/Linux raises EIO. 720 * nil 721 * end 722 * p ret #=> nil 723 * 724 * == License 725 * 726 * C) Copyright 1998 by Akinori Ito. 727 * 728 * This software may be redistributed freely for this purpose, in full 729 * or in part, provided that this entire copyright notice is included 730 * on any copies of this software and applications and derivations thereof. 731 * 732 * This software is provided on an "as is" basis, without warranty of any 733 * kind, either expressed or implied, as to any matter including, but not 734 * limited to warranty of fitness of purpose, or merchantability, or 735 * results obtained from use of this software. 736 */ 737 738void 739Init_pty() 740{ 741 cPTY = rb_define_module("PTY"); 742 /* :nodoc */ 743 rb_define_module_function(cPTY,"getpty",pty_getpty,-1); 744 rb_define_module_function(cPTY,"spawn",pty_getpty,-1); 745 rb_define_singleton_method(cPTY,"check",pty_check,-1); 746 rb_define_singleton_method(cPTY,"open",pty_open,0); 747 748 eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError); 749 rb_define_method(eChildExited,"status",echild_status,0); 750} 751