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