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
00052 # ifdef HAVE_SETRESUID
00053 # define seteuid(e) setresuid(-1, (e), -1)
00054 # else
00055
00056 # endif
00057 # endif
00058 #endif
00059
00060 static VALUE eChildExited;
00061
00062
00063
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
00099
00100 #ifdef HAVE_SETSID
00101 (void) setsid();
00102 #else
00103 # ifdef HAVE_SETPGRP
00104 # ifdef SETGRP_VOID
00105 if (setpgrp() == -1)
00106 ERROR_EXIT("setpgrp()");
00107 # else
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
00119 # endif
00120 #endif
00121
00122
00123
00124
00125 #if defined(TIOCSCTTY)
00126 close(master);
00127 (void) ioctl(slave, TIOCSCTTY, (char *)0);
00128
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
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
00233
00234
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
00244
00245
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
00284
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
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
00313 rb_update_max_fd(*slave);
00314 strlcpy(SlaveName, name, DEVICELEN);
00315
00316 return 0;
00317 #elif defined(HAVE_PTSNAME)
00318
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
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
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)
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
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
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
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
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
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 VALUE msg;
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
00621 if (WIFSTOPPED(status)) {
00622 state = "stopped";
00623 }
00624 else if (kill(pid, 0) == 0) {
00625 state = "changed";
00626 }
00627 else {
00628 state = "exited";
00629 }
00630 msg = rb_sprintf("pty - %s: %ld", state, (long)pid);
00631 exc = rb_exc_new3(eChildExited, msg);
00632 rb_iv_set(exc, "status", rb_last_status_get());
00633 rb_exc_raise(exc);
00634 }
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
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
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738 void
00739 Init_pty()
00740 {
00741 cPTY = rb_define_module("PTY");
00742
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