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 static VALUE
00126 echild_status(VALUE self)
00127 {
00128 return rb_ivar_get(self, rb_intern("status"));
00129 }
00130
00131 struct pty_info {
00132 int fd;
00133 rb_pid_t child_pid;
00134 };
00135
00136 static void getDevice(int*, int*, char [DEVICELEN], int);
00137
00138 struct exec_info {
00139 int argc;
00140 VALUE *argv;
00141 };
00142
00143 static VALUE
00144 pty_exec(VALUE v)
00145 {
00146 struct exec_info *arg = (struct exec_info *)v;
00147 return rb_f_exec(arg->argc, arg->argv);
00148 }
00149
00150 struct child_info {
00151 int master, slave;
00152 char *slavename;
00153 int argc;
00154 VALUE *argv;
00155 };
00156
00157 static int
00158 chfunc(void *data)
00159 {
00160 struct child_info *carg = data;
00161 int master = carg->master;
00162 int slave = carg->slave;
00163 int argc = carg->argc;
00164 VALUE *argv = carg->argv;
00165
00166 struct exec_info arg;
00167 int status;
00168
00169
00170
00171
00172 #ifdef HAVE_SETSID
00173 (void) setsid();
00174 #else
00175 # ifdef HAVE_SETPGRP
00176 # ifdef SETGRP_VOID
00177 if (setpgrp() == -1)
00178 perror("setpgrp()");
00179 # else
00180 if (setpgrp(0, getpid()) == -1)
00181 rb_sys_fail("setpgrp()");
00182 {
00183 int i = open("/dev/tty", O_RDONLY);
00184 if (i < 0) rb_sys_fail("/dev/tty");
00185 if (ioctl(i, TIOCNOTTY, (char *)0))
00186 perror("ioctl(TIOCNOTTY)");
00187 close(i);
00188 }
00189 # endif
00190 # endif
00191 #endif
00192
00193
00194
00195
00196 #if defined(TIOCSCTTY)
00197 close(master);
00198 (void) ioctl(slave, TIOCSCTTY, (char *)0);
00199
00200 #else
00201 close(slave);
00202 slave = open(carg->slavename, O_RDWR);
00203 if (slave < 0) {
00204 perror("open: pty slave");
00205 _exit(1);
00206 }
00207 close(master);
00208 #endif
00209 write(slave, "", 1);
00210 dup2(slave,0);
00211 dup2(slave,1);
00212 dup2(slave,2);
00213 close(slave);
00214 #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
00215 seteuid(getuid());
00216 #endif
00217
00218 arg.argc = argc;
00219 arg.argv = argv;
00220 rb_protect(pty_exec, (VALUE)&arg, &status);
00221 sleep(1);
00222 _exit(1);
00223 }
00224
00225 static void
00226 establishShell(int argc, VALUE *argv, struct pty_info *info,
00227 char SlaveName[DEVICELEN])
00228 {
00229 int master,slave;
00230 rb_pid_t pid;
00231 char *p, tmp, *getenv();
00232 struct passwd *pwent;
00233 VALUE v;
00234 struct child_info carg;
00235
00236 if (argc == 0) {
00237 const char *shellname;
00238
00239 if ((p = getenv("SHELL")) != NULL) {
00240 shellname = p;
00241 }
00242 else {
00243 pwent = getpwuid(getuid());
00244 if (pwent && pwent->pw_shell)
00245 shellname = pwent->pw_shell;
00246 else
00247 shellname = "/bin/sh";
00248 }
00249 v = rb_str_new2(shellname);
00250 argc = 1;
00251 argv = &v;
00252 }
00253
00254 getDevice(&master, &slave, SlaveName, 0);
00255
00256 carg.master = master;
00257 carg.slave = slave;
00258 carg.slavename = SlaveName;
00259 carg.argc = argc;
00260 carg.argv = argv;
00261 pid = rb_fork(0, chfunc, &carg, Qnil);
00262
00263 if (pid < 0) {
00264 close(master);
00265 close(slave);
00266 rb_sys_fail("fork failed");
00267 }
00268
00269 read(master, &tmp, 1);
00270 close(slave);
00271
00272 info->child_pid = pid;
00273 info->fd = master;
00274 }
00275
00276 static int
00277 no_mesg(char *slavedevice, int nomesg)
00278 {
00279 if (nomesg)
00280 return chmod(slavedevice, 0600);
00281 else
00282 return 0;
00283 }
00284
00285 static int
00286 get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
00287 {
00288 #if defined(HAVE_POSIX_OPENPT)
00289 int masterfd = -1, slavefd = -1;
00290 char *slavedevice;
00291 struct sigaction dfl, old;
00292
00293 dfl.sa_handler = SIG_DFL;
00294 dfl.sa_flags = 0;
00295 sigemptyset(&dfl.sa_mask);
00296
00297 if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
00298 if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
00299 if (grantpt(masterfd) == -1) goto grantpt_error;
00300 if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
00301 if (unlockpt(masterfd) == -1) goto error;
00302 if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
00303 if (no_mesg(slavedevice, nomesg) == -1) goto error;
00304 if ((slavefd = open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
00305
00306 #if defined I_PUSH && !defined linux
00307 if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
00308 if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
00309 if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error;
00310 #endif
00311
00312 *master = masterfd;
00313 *slave = slavefd;
00314 strlcpy(SlaveName, slavedevice, DEVICELEN);
00315 return 0;
00316
00317 grantpt_error:
00318 sigaction(SIGCHLD, &old, NULL);
00319 error:
00320 if (slavefd != -1) close(slavefd);
00321 if (masterfd != -1) close(masterfd);
00322 if (fail) {
00323 rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
00324 }
00325 return -1;
00326 #elif defined HAVE_OPENPTY
00327
00328
00329
00330
00331 if (openpty(master, slave, SlaveName,
00332 (struct termios *)0, (struct winsize *)0) == -1) {
00333 if (!fail) return -1;
00334 rb_raise(rb_eRuntimeError, "openpty() failed");
00335 }
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
00352 *slave = open(name, O_RDWR);
00353 strlcpy(SlaveName, name, DEVICELEN);
00354
00355 return 0;
00356 #elif defined(HAVE_PTSNAME)
00357 int masterfd = -1, slavefd = -1;
00358 char *slavedevice;
00359 void (*s)();
00360
00361 extern char *ptsname(int);
00362 extern int unlockpt(int);
00363 extern int grantpt(int);
00364
00365 if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
00366 s = signal(SIGCHLD, SIG_DFL);
00367 if(grantpt(masterfd) == -1) goto error;
00368 signal(SIGCHLD, s);
00369 if(unlockpt(masterfd) == -1) goto error;
00370 if((slavedevice = ptsname(masterfd)) == NULL) goto error;
00371 if (no_mesg(slavedevice, nomesg) == -1) goto error;
00372 if((slavefd = open(slavedevice, O_RDWR, 0)) == -1) goto error;
00373 #if defined I_PUSH && !defined linux
00374 if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
00375 if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
00376 ioctl(slavefd, I_PUSH, "ttcompat");
00377 #endif
00378 *master = masterfd;
00379 *slave = slavefd;
00380 strlcpy(SlaveName, slavedevice, DEVICELEN);
00381 return 0;
00382
00383 error:
00384 if (slavefd != -1) close(slavefd);
00385 if (masterfd != -1) close(masterfd);
00386 if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
00387 return -1;
00388 #else
00389 int masterfd = -1, slavefd = -1;
00390 const char *const *p;
00391 char MasterName[DEVICELEN];
00392
00393 for (p = deviceNo; *p != NULL; p++) {
00394 snprintf(MasterName, sizeof MasterName, MasterDevice, *p);
00395 if ((masterfd = open(MasterName,O_RDWR,0)) >= 0) {
00396 *master = masterfd;
00397 snprintf(SlaveName, DEVICELEN, SlaveDevice, *p);
00398 if ((slavefd = open(SlaveName,O_RDWR,0)) >= 0) {
00399 *slave = slavefd;
00400 if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
00401 if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
00402 return 0;
00403 }
00404 close(masterfd);
00405 }
00406 }
00407 error:
00408 if (slavefd != -1) close(slavefd);
00409 if (masterfd != -1) close(masterfd);
00410 if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName);
00411 return -1;
00412 #endif
00413 }
00414
00415 static void
00416 getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
00417 {
00418 if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
00419 rb_gc();
00420 get_device_once(master, slave, SlaveName, nomesg, 1);
00421 }
00422 }
00423
00424 static VALUE
00425 pty_close_pty(VALUE assoc)
00426 {
00427 VALUE io;
00428 int i;
00429
00430 for (i = 0; i < 2; i++) {
00431 io = rb_ary_entry(assoc, i);
00432 if (TYPE(io) == T_FILE && 0 <= RFILE(io)->fptr->fd)
00433 rb_io_close(io);
00434 }
00435 return Qnil;
00436 }
00437
00438
00439
00440
00441
00442
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 static VALUE
00486 pty_open(VALUE klass)
00487 {
00488 int master_fd, slave_fd;
00489 char slavename[DEVICELEN];
00490 VALUE master_io, slave_file;
00491 rb_io_t *master_fptr, *slave_fptr;
00492 VALUE assoc;
00493
00494 getDevice(&master_fd, &slave_fd, slavename, 1);
00495
00496 master_io = rb_obj_alloc(rb_cIO);
00497 MakeOpenFile(master_io, master_fptr);
00498 master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
00499 master_fptr->fd = master_fd;
00500 master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
00501
00502 slave_file = rb_obj_alloc(rb_cFile);
00503 MakeOpenFile(slave_file, slave_fptr);
00504 slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY;
00505 slave_fptr->fd = slave_fd;
00506 slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
00507
00508 assoc = rb_assoc_new(master_io, slave_file);
00509 if (rb_block_given_p()) {
00510 return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
00511 }
00512 return assoc;
00513 }
00514
00515 static VALUE
00516 pty_detach_process(struct pty_info *info)
00517 {
00518 rb_detach_process(info->child_pid);
00519 return Qnil;
00520 }
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543 static VALUE
00544 pty_getpty(int argc, VALUE *argv, VALUE self)
00545 {
00546 VALUE res;
00547 struct pty_info info;
00548 rb_io_t *wfptr,*rfptr;
00549 VALUE rport = rb_obj_alloc(rb_cFile);
00550 VALUE wport = rb_obj_alloc(rb_cFile);
00551 char SlaveName[DEVICELEN];
00552
00553 MakeOpenFile(rport, rfptr);
00554 MakeOpenFile(wport, wfptr);
00555
00556 establishShell(argc, argv, &info, SlaveName);
00557
00558 rfptr->mode = rb_io_mode_flags("r");
00559 rfptr->fd = info.fd;
00560 rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
00561
00562 wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC;
00563 wfptr->fd = dup(info.fd);
00564 if (wfptr->fd == -1)
00565 rb_sys_fail("dup()");
00566 wfptr->pathv = rfptr->pathv;
00567
00568 res = rb_ary_new2(3);
00569 rb_ary_store(res,0,(VALUE)rport);
00570 rb_ary_store(res,1,(VALUE)wport);
00571 rb_ary_store(res,2,PIDT2NUM(info.child_pid));
00572
00573 if (rb_block_given_p()) {
00574 rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
00575 return Qnil;
00576 }
00577 return res;
00578 }
00579
00580 static void
00581 raise_from_check(pid_t pid, int status)
00582 {
00583 const char *state;
00584 char buf[1024];
00585 VALUE exc;
00586
00587 #if defined(WIFSTOPPED)
00588 #elif defined(IF_STOPPED)
00589 #define WIFSTOPPED(status) IF_STOPPED(status)
00590 #else
00591 ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
00592 #endif
00593 if (WIFSTOPPED(status)) {
00594 state = "stopped";
00595 }
00596 else if (kill(pid, 0) == 0) {
00597 state = "changed";
00598 }
00599 else {
00600 state = "exited";
00601 }
00602 snprintf(buf, sizeof(buf), "pty - %s: %ld", state, (long)pid);
00603 exc = rb_exc_new2(eChildExited, buf);
00604 rb_iv_set(exc, "status", rb_last_status_get());
00605 rb_exc_raise(exc);
00606 }
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617 static VALUE
00618 pty_check(int argc, VALUE *argv, VALUE self)
00619 {
00620 VALUE pid, exc;
00621 pid_t cpid;
00622 int status;
00623
00624 rb_scan_args(argc, argv, "11", &pid, &exc);
00625 cpid = rb_waitpid(NUM2PIDT(pid), &status, WNOHANG|WUNTRACED);
00626 if (cpid == -1) return Qnil;
00627
00628 if (!RTEST(exc)) return rb_last_status_get();
00629 raise_from_check(cpid, status);
00630 return Qnil;
00631 }
00632
00633 static VALUE cPTY;
00634
00635 void
00636 Init_pty()
00637 {
00638 cPTY = rb_define_module("PTY");
00639 rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
00640 rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
00641 rb_define_singleton_method(cPTY,"check",pty_check,-1);
00642 rb_define_singleton_method(cPTY,"open",pty_open,0);
00643
00644 eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
00645 rb_define_method(eChildExited,"status",echild_status,0);
00646 }
00647