PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/pty/pty.c

https://github.com/nazy/ruby
C | 635 lines | 481 code | 64 blank | 90 comment | 111 complexity | f8f6dcc445458f8ecc817a1921c56651 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0, 0BSD, Unlicense
  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. #include "ruby/ruby.h"
  32. #include "ruby/io.h"
  33. #include "ruby/util.h"
  34. #include <signal.h>
  35. #ifdef HAVE_SYS_STROPTS_H
  36. #include <sys/stropts.h>
  37. #endif
  38. #ifdef HAVE_UNISTD_H
  39. #include <unistd.h>
  40. #endif
  41. #define DEVICELEN 16
  42. #if !defined(HAVE_OPENPTY)
  43. #if defined(__hpux)
  44. static const
  45. char MasterDevice[] = "/dev/ptym/pty%s",
  46. SlaveDevice[] = "/dev/pty/tty%s",
  47. *const deviceNo[] = {
  48. "p0","p1","p2","p3","p4","p5","p6","p7",
  49. "p8","p9","pa","pb","pc","pd","pe","pf",
  50. "q0","q1","q2","q3","q4","q5","q6","q7",
  51. "q8","q9","qa","qb","qc","qd","qe","qf",
  52. "r0","r1","r2","r3","r4","r5","r6","r7",
  53. "r8","r9","ra","rb","rc","rd","re","rf",
  54. "s0","s1","s2","s3","s4","s5","s6","s7",
  55. "s8","s9","sa","sb","sc","sd","se","sf",
  56. "t0","t1","t2","t3","t4","t5","t6","t7",
  57. "t8","t9","ta","tb","tc","td","te","tf",
  58. "u0","u1","u2","u3","u4","u5","u6","u7",
  59. "u8","u9","ua","ub","uc","ud","ue","uf",
  60. "v0","v1","v2","v3","v4","v5","v6","v7",
  61. "v8","v9","va","vb","vc","vd","ve","vf",
  62. "w0","w1","w2","w3","w4","w5","w6","w7",
  63. "w8","w9","wa","wb","wc","wd","we","wf",
  64. 0,
  65. };
  66. #elif defined(_IBMESA) /* AIX/ESA */
  67. static const
  68. char MasterDevice[] = "/dev/ptyp%s",
  69. SlaveDevice[] = "/dev/ttyp%s",
  70. *const deviceNo[] = {
  71. "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
  72. "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
  73. "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
  74. "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
  75. "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
  76. "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
  77. "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
  78. "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
  79. "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
  80. "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
  81. "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
  82. "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
  83. "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
  84. "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
  85. "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
  86. "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff",
  87. };
  88. #elif !defined(HAVE_PTSNAME)
  89. static const
  90. char MasterDevice[] = "/dev/pty%s",
  91. SlaveDevice[] = "/dev/tty%s",
  92. *const deviceNo[] = {
  93. "p0","p1","p2","p3","p4","p5","p6","p7",
  94. "p8","p9","pa","pb","pc","pd","pe","pf",
  95. "q0","q1","q2","q3","q4","q5","q6","q7",
  96. "q8","q9","qa","qb","qc","qd","qe","qf",
  97. "r0","r1","r2","r3","r4","r5","r6","r7",
  98. "r8","r9","ra","rb","rc","rd","re","rf",
  99. "s0","s1","s2","s3","s4","s5","s6","s7",
  100. "s8","s9","sa","sb","sc","sd","se","sf",
  101. 0,
  102. };
  103. #endif
  104. #endif /* !defined(HAVE_OPENPTY) */
  105. #ifndef HAVE_SETEUID
  106. # ifdef HAVE_SETREUID
  107. # define seteuid(e) setreuid(-1, (e))
  108. # else /* NOT HAVE_SETREUID */
  109. # ifdef HAVE_SETRESUID
  110. # define seteuid(e) setresuid(-1, (e), -1)
  111. # else /* NOT HAVE_SETRESUID */
  112. /* I can't set euid. (;_;) */
  113. # endif /* HAVE_SETRESUID */
  114. # endif /* HAVE_SETREUID */
  115. #endif /* NO_SETEUID */
  116. static VALUE eChildExited;
  117. static VALUE
  118. echild_status(VALUE self)
  119. {
  120. return rb_ivar_get(self, rb_intern("status"));
  121. }
  122. struct pty_info {
  123. int fd;
  124. rb_pid_t child_pid;
  125. };
  126. static void getDevice(int*, int*, char [DEVICELEN], int);
  127. struct child_info {
  128. int master, slave;
  129. char *slavename;
  130. int argc;
  131. VALUE *argv;
  132. };
  133. static int
  134. chfunc(void *data, char *errbuf, size_t errbuf_len)
  135. {
  136. struct child_info *carg = data;
  137. int master = carg->master;
  138. int slave = carg->slave;
  139. int argc = carg->argc;
  140. VALUE *argv = carg->argv;
  141. #define ERROR_EXIT(str) do { \
  142. strlcpy(errbuf, str, errbuf_len); \
  143. return -1; \
  144. } while (0)
  145. /*
  146. * Set free from process group and controlling terminal
  147. */
  148. #ifdef HAVE_SETSID
  149. (void) setsid();
  150. #else /* HAS_SETSID */
  151. # ifdef HAVE_SETPGRP
  152. # ifdef SETGRP_VOID
  153. if (setpgrp() == -1)
  154. ERROR_EXIT("setpgrp()");
  155. # else /* SETGRP_VOID */
  156. if (setpgrp(0, getpid()) == -1)
  157. ERROR_EXIT("setpgrp()");
  158. {
  159. int i = open("/dev/tty", O_RDONLY);
  160. if (i < 0) ERROR_EXIT("/dev/tty");
  161. if (ioctl(i, TIOCNOTTY, (char *)0))
  162. ERROR_EXIT("ioctl(TIOCNOTTY)");
  163. close(i);
  164. }
  165. # endif /* SETGRP_VOID */
  166. # endif /* HAVE_SETPGRP */
  167. #endif /* HAS_SETSID */
  168. /*
  169. * obtain new controlling terminal
  170. */
  171. #if defined(TIOCSCTTY)
  172. close(master);
  173. (void) ioctl(slave, TIOCSCTTY, (char *)0);
  174. /* errors ignored for sun */
  175. #else
  176. close(slave);
  177. slave = open(carg->slavename, O_RDWR);
  178. if (slave < 0) {
  179. ERROR_EXIT("open: pty slave");
  180. }
  181. close(master);
  182. #endif
  183. dup2(slave,0);
  184. dup2(slave,1);
  185. dup2(slave,2);
  186. close(slave);
  187. #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
  188. seteuid(getuid());
  189. #endif
  190. return rb_f_exec(argc, argv);
  191. #undef ERROR_EXIT
  192. }
  193. static void
  194. establishShell(int argc, VALUE *argv, struct pty_info *info,
  195. char SlaveName[DEVICELEN])
  196. {
  197. int master,slave;
  198. rb_pid_t pid;
  199. char *p, *getenv();
  200. struct passwd *pwent;
  201. VALUE v;
  202. struct child_info carg;
  203. char errbuf[32];
  204. if (argc == 0) {
  205. const char *shellname;
  206. if ((p = getenv("SHELL")) != NULL) {
  207. shellname = p;
  208. }
  209. else {
  210. pwent = getpwuid(getuid());
  211. if (pwent && pwent->pw_shell)
  212. shellname = pwent->pw_shell;
  213. else
  214. shellname = "/bin/sh";
  215. }
  216. v = rb_str_new2(shellname);
  217. argc = 1;
  218. argv = &v;
  219. }
  220. getDevice(&master, &slave, SlaveName, 0);
  221. carg.master = master;
  222. carg.slave = slave;
  223. carg.slavename = SlaveName;
  224. carg.argc = argc;
  225. carg.argv = argv;
  226. errbuf[0] = '\0';
  227. pid = rb_fork_err(0, chfunc, &carg, Qnil, errbuf, sizeof(errbuf));
  228. if (pid < 0) {
  229. int e = errno;
  230. close(master);
  231. close(slave);
  232. errno = e;
  233. rb_sys_fail(errbuf[0] ? errbuf : "fork failed");
  234. }
  235. close(slave);
  236. info->child_pid = pid;
  237. info->fd = master;
  238. }
  239. static int
  240. no_mesg(char *slavedevice, int nomesg)
  241. {
  242. if (nomesg)
  243. return chmod(slavedevice, 0600);
  244. else
  245. return 0;
  246. }
  247. static int
  248. get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
  249. {
  250. #if defined(HAVE_POSIX_OPENPT)
  251. int masterfd = -1, slavefd = -1;
  252. char *slavedevice;
  253. struct sigaction dfl, old;
  254. dfl.sa_handler = SIG_DFL;
  255. dfl.sa_flags = 0;
  256. sigemptyset(&dfl.sa_mask);
  257. if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
  258. if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
  259. if (grantpt(masterfd) == -1) goto grantpt_error;
  260. if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
  261. if (unlockpt(masterfd) == -1) goto error;
  262. if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
  263. if (no_mesg(slavedevice, nomesg) == -1) goto error;
  264. if ((slavefd = open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
  265. #if defined I_PUSH && !defined linux
  266. if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
  267. if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
  268. if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error;
  269. #endif
  270. *master = masterfd;
  271. *slave = slavefd;
  272. strlcpy(SlaveName, slavedevice, DEVICELEN);
  273. return 0;
  274. grantpt_error:
  275. sigaction(SIGCHLD, &old, NULL);
  276. error:
  277. if (slavefd != -1) close(slavefd);
  278. if (masterfd != -1) close(masterfd);
  279. if (fail) {
  280. rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
  281. }
  282. return -1;
  283. #elif defined HAVE_OPENPTY
  284. /*
  285. * Use openpty(3) of 4.3BSD Reno and later,
  286. * or the same interface function.
  287. */
  288. if (openpty(master, slave, SlaveName,
  289. (struct termios *)0, (struct winsize *)0) == -1) {
  290. if (!fail) return -1;
  291. rb_raise(rb_eRuntimeError, "openpty() failed");
  292. }
  293. if (no_mesg(SlaveName, nomesg) == -1) {
  294. if (!fail) return -1;
  295. rb_raise(rb_eRuntimeError, "can't chmod slave pty");
  296. }
  297. return 0;
  298. #elif defined HAVE__GETPTY
  299. char *name;
  300. mode_t mode = nomesg ? 0600 : 0622;
  301. if (!(name = _getpty(master, O_RDWR, mode, 0))) {
  302. if (!fail) return -1;
  303. rb_raise(rb_eRuntimeError, "_getpty() failed");
  304. }
  305. *slave = open(name, O_RDWR);
  306. strlcpy(SlaveName, name, DEVICELEN);
  307. return 0;
  308. #elif defined(HAVE_PTSNAME)
  309. int masterfd = -1, slavefd = -1;
  310. char *slavedevice;
  311. void (*s)();
  312. extern char *ptsname(int);
  313. extern int unlockpt(int);
  314. extern int grantpt(int);
  315. if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
  316. s = signal(SIGCHLD, SIG_DFL);
  317. if(grantpt(masterfd) == -1) goto error;
  318. signal(SIGCHLD, s);
  319. if(unlockpt(masterfd) == -1) goto error;
  320. if((slavedevice = ptsname(masterfd)) == NULL) goto error;
  321. if (no_mesg(slavedevice, nomesg) == -1) goto error;
  322. if((slavefd = open(slavedevice, O_RDWR, 0)) == -1) goto error;
  323. #if defined I_PUSH && !defined linux
  324. if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
  325. if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
  326. ioctl(slavefd, I_PUSH, "ttcompat");
  327. #endif
  328. *master = masterfd;
  329. *slave = slavefd;
  330. strlcpy(SlaveName, slavedevice, DEVICELEN);
  331. return 0;
  332. error:
  333. if (slavefd != -1) close(slavefd);
  334. if (masterfd != -1) close(masterfd);
  335. if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
  336. return -1;
  337. #else
  338. int masterfd = -1, slavefd = -1;
  339. const char *const *p;
  340. char MasterName[DEVICELEN];
  341. for (p = deviceNo; *p != NULL; p++) {
  342. snprintf(MasterName, sizeof MasterName, MasterDevice, *p);
  343. if ((masterfd = open(MasterName,O_RDWR,0)) >= 0) {
  344. *master = masterfd;
  345. snprintf(SlaveName, DEVICELEN, SlaveDevice, *p);
  346. if ((slavefd = open(SlaveName,O_RDWR,0)) >= 0) {
  347. *slave = slavefd;
  348. if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
  349. if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
  350. return 0;
  351. }
  352. close(masterfd);
  353. }
  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 %s", SlaveName);
  359. return -1;
  360. #endif
  361. }
  362. static void
  363. getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
  364. {
  365. if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
  366. rb_gc();
  367. get_device_once(master, slave, SlaveName, nomesg, 1);
  368. }
  369. }
  370. static VALUE
  371. pty_close_pty(VALUE assoc)
  372. {
  373. VALUE io;
  374. int i;
  375. for (i = 0; i < 2; i++) {
  376. io = rb_ary_entry(assoc, i);
  377. if (TYPE(io) == T_FILE && 0 <= RFILE(io)->fptr->fd)
  378. rb_io_close(io);
  379. }
  380. return Qnil;
  381. }
  382. /*
  383. * call-seq:
  384. * PTY.open => [master_io, slave_file]
  385. * PTY.open {|master_io, slave_file| ... } => block value
  386. *
  387. * Allocates a pty (pseudo-terminal).
  388. *
  389. * It returns an array which contains an IO object and a File object.
  390. * The former is the master of the pty.
  391. * The latter is the slave of the pty.
  392. *
  393. * If a block is given, it yields the array instead of return.
  394. * The value of the block is returned.
  395. * master_io and slave_file is closed when return if they are not closed.
  396. *
  397. * The path name of the terminal device can be gotten by slave_file.path.
  398. *
  399. * PTY.open {|m, s|
  400. * p m #=> #<IO:masterpty:/dev/pts/1>
  401. * p s #=> #<File:/dev/pts/1>
  402. * p s.path #=> "/dev/pts/1"
  403. * }
  404. *
  405. * # Change the buffering type in factor command,
  406. * # assuming that factor uses stdio for stdout buffering.
  407. * # If IO.pipe is used instead of PTY.open,
  408. * # this code deadlocks because factor's stdout is fully buffered.
  409. * require 'io/console' # for IO#raw!
  410. * m, s = PTY.open
  411. * s.raw! # disable newline conversion.
  412. * r, w = IO.pipe
  413. * pid = spawn("factor", :in=>r, :out=>s)
  414. * r.close
  415. * s.close
  416. * w.puts "42"
  417. * p m.gets #=> "42: 2 3 7\n"
  418. * w.puts "144"
  419. * p m.gets #=> "144: 2 2 2 2 3 3\n"
  420. * w.close
  421. * # The result of read operation when pty slave is closed is platform dependnet.
  422. * ret = begin
  423. * m.gets # FreeBSD returns nil.
  424. * rescue Errno::EIO # GNU/Linux raises EIO.
  425. * nil
  426. * end
  427. * p ret #=> nil
  428. *
  429. */
  430. static VALUE
  431. pty_open(VALUE klass)
  432. {
  433. int master_fd, slave_fd;
  434. char slavename[DEVICELEN];
  435. VALUE master_io, slave_file;
  436. rb_io_t *master_fptr, *slave_fptr;
  437. VALUE assoc;
  438. getDevice(&master_fd, &slave_fd, slavename, 1);
  439. master_io = rb_obj_alloc(rb_cIO);
  440. MakeOpenFile(master_io, master_fptr);
  441. master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
  442. master_fptr->fd = master_fd;
  443. master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
  444. slave_file = rb_obj_alloc(rb_cFile);
  445. MakeOpenFile(slave_file, slave_fptr);
  446. slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY;
  447. slave_fptr->fd = slave_fd;
  448. slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
  449. assoc = rb_assoc_new(master_io, slave_file);
  450. if (rb_block_given_p()) {
  451. return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
  452. }
  453. return assoc;
  454. }
  455. static VALUE
  456. pty_detach_process(struct pty_info *info)
  457. {
  458. rb_detach_process(info->child_pid);
  459. return Qnil;
  460. }
  461. /*
  462. * call-seq:
  463. * PTY.spawn(command...) {|r, w, pid| ... } => nil
  464. * PTY.spawn(command...) => r, w, pid
  465. * PTY.getpty(command...) {|r, w, pid| ... } => nil
  466. * PTY.getpty(command...) => r, w, pid
  467. *
  468. * spawns the specified command on a newly allocated pty.
  469. *
  470. * The command's controlling tty is set to the slave device of the pty.
  471. * Also its standard input/output/error is redirected to the slave device.
  472. *
  473. * PTY.spawn returns two IO objects and PID.
  474. * PID is the process ID of the command.
  475. * The two IO objects are connected to the master device of the pty.
  476. * The first IO object is opened as read mode and
  477. * The second is opened as write mode.
  478. *
  479. * If a block is given, two IO objects and PID is yielded.
  480. *
  481. */
  482. static VALUE
  483. pty_getpty(int argc, VALUE *argv, VALUE self)
  484. {
  485. VALUE res;
  486. struct pty_info info;
  487. rb_io_t *wfptr,*rfptr;
  488. VALUE rport = rb_obj_alloc(rb_cFile);
  489. VALUE wport = rb_obj_alloc(rb_cFile);
  490. char SlaveName[DEVICELEN];
  491. MakeOpenFile(rport, rfptr);
  492. MakeOpenFile(wport, wfptr);
  493. establishShell(argc, argv, &info, SlaveName);
  494. rfptr->mode = rb_io_mode_flags("r");
  495. rfptr->fd = info.fd;
  496. rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
  497. wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC;
  498. wfptr->fd = dup(info.fd);
  499. if (wfptr->fd == -1)
  500. rb_sys_fail("dup()");
  501. wfptr->pathv = rfptr->pathv;
  502. res = rb_ary_new2(3);
  503. rb_ary_store(res,0,(VALUE)rport);
  504. rb_ary_store(res,1,(VALUE)wport);
  505. rb_ary_store(res,2,PIDT2NUM(info.child_pid));
  506. if (rb_block_given_p()) {
  507. rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
  508. return Qnil;
  509. }
  510. return res;
  511. }
  512. static void
  513. raise_from_check(pid_t pid, int status)
  514. {
  515. const char *state;
  516. char buf[1024];
  517. VALUE exc;
  518. #if defined(WIFSTOPPED)
  519. #elif defined(IF_STOPPED)
  520. #define WIFSTOPPED(status) IF_STOPPED(status)
  521. #else
  522. ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
  523. #endif /* WIFSTOPPED | IF_STOPPED */
  524. if (WIFSTOPPED(status)) { /* suspend */
  525. state = "stopped";
  526. }
  527. else if (kill(pid, 0) == 0) {
  528. state = "changed";
  529. }
  530. else {
  531. state = "exited";
  532. }
  533. snprintf(buf, sizeof(buf), "pty - %s: %ld", state, (long)pid);
  534. exc = rb_exc_new2(eChildExited, buf);
  535. rb_iv_set(exc, "status", rb_last_status_get());
  536. rb_exc_raise(exc);
  537. }
  538. /*
  539. * call-seq:
  540. * PTY.check(pid[, raise=false]) => Process::Status or nil
  541. *
  542. * checks the status of the child process specified by _pid_, and
  543. * returns +nil+ if the process is still alive and active. Otherwise,
  544. * returns +Process::Status+ about the process if _raise_ is false, or
  545. * +PTY::ChildExited+ exception is raised.
  546. */
  547. static VALUE
  548. pty_check(int argc, VALUE *argv, VALUE self)
  549. {
  550. VALUE pid, exc;
  551. pid_t cpid;
  552. int status;
  553. rb_scan_args(argc, argv, "11", &pid, &exc);
  554. cpid = rb_waitpid(NUM2PIDT(pid), &status, WNOHANG|WUNTRACED);
  555. if (cpid == -1) return Qnil;
  556. if (!RTEST(exc)) return rb_last_status_get();
  557. raise_from_check(cpid, status);
  558. return Qnil; /* not reached */
  559. }
  560. static VALUE cPTY;
  561. void
  562. Init_pty()
  563. {
  564. cPTY = rb_define_module("PTY");
  565. rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
  566. rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
  567. rb_define_singleton_method(cPTY,"check",pty_check,-1);
  568. rb_define_singleton_method(cPTY,"open",pty_open,0);
  569. eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
  570. rb_define_method(eChildExited,"status",echild_status,0);
  571. }