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)
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
00110
00111 #ifndef HAVE_SETEUID
00112 # ifdef HAVE_SETREUID
00113 # define seteuid(e) setreuid(-1, (e))
00114 # else
00115 # ifdef HAVE_SETRESUID
00116 # define seteuid(e) setresuid(-1, (e), -1)
00117 # else
00118
00119 # endif
00120 # endif
00121 #endif
00122
00123 static VALUE eChildExited;
00124
00125
00126
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
00166
00167 #ifdef HAVE_SETSID
00168 (void) setsid();
00169 #else
00170 # ifdef HAVE_SETPGRP
00171 # ifdef SETGRP_VOID
00172 if (setpgrp() == -1)
00173 ERROR_EXIT("setpgrp()");
00174 # else
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
00186 # endif
00187 #endif
00188
00189
00190
00191
00192 #if defined(TIOCSCTTY)
00193 close(master);
00194 (void) ioctl(slave, TIOCSCTTY, (char *)0);
00195
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
00327
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
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
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
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 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
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
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 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 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;
00666 }
00667
00668 static VALUE cPTY;
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
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