DEFINITIONS
This source file includes following functions.
- seteuid
- seteuid
- echild_status
- pty_syswait
- pty_exec
- establishShell
- pty_kill_child
- getDevice
- getDevice
- getDevice
- freeDevice
- pty_getpty
- pty_protect
- pty_reset_signal
- Init_pty
1 #include "config.h"
2 #include <stdio.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <sys/file.h>
6 #include <fcntl.h>
7 #include <errno.h>
8 #include <pwd.h>
9 #if !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY)
10 #include <sys/ioctl.h>
11 #endif
12 #ifdef HAVE_LIBUTIL_H
13 #include <libutil.h>
14 #endif
15 #ifdef HAVE_PTY_H
16 #include <pty.h>
17 #endif
18 #ifdef HAVE_SYS_WAIT_H
19 #include <sys/wait.h>
20 #else
21 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
22 #endif
23 #include <ctype.h>
24
25 #include "ruby.h"
26 #include "rubyio.h"
27
28 #include <signal.h>
29 #ifdef HAVE_SYS_STROPTS_H
30 #include <sys/stropts.h>
31 #endif
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #define DEVICELEN 16
38
39 #if !defined(HAVE_OPENPTY)
40 #ifdef __hpux
41 static
42 char *MasterDevice = "/dev/ptym/pty%s",
43 *SlaveDevice = "/dev/pty/tty%s",
44 *deviceNo[] = {
45 "p0","p1","p2","p3","p4","p5","p6","p7",
46 "p8","p9","pa","pb","pc","pd","pe","pf",
47 "q0","q1","q2","q3","q4","q5","q6","q7",
48 "q8","q9","qa","qb","qc","qd","qe","qf",
49 "r0","r1","r2","r3","r4","r5","r6","r7",
50 "r8","r9","ra","rb","rc","rd","re","rf",
51 "s0","s1","s2","s3","s4","s5","s6","s7",
52 "s8","s9","sa","sb","sc","sd","se","sf",
53 "t0","t1","t2","t3","t4","t5","t6","t7",
54 "t8","t9","ta","tb","tc","td","te","tf",
55 "u0","u1","u2","u3","u4","u5","u6","u7",
56 "u8","u9","ua","ub","uc","ud","ue","uf",
57 "v0","v1","v2","v3","v4","v5","v6","v7",
58 "v8","v9","va","vb","vc","vd","ve","vf",
59 "w0","w1","w2","w3","w4","w5","w6","w7",
60 "w8","w9","wa","wb","wc","wd","we","wf",
61 0,
62 };
63 #else
64 #ifdef _IBMESA
65 static
66 char *MasterDevice = "/dev/ptyp%s",
67 *SlaveDevice = "/dev/ttyp%s",
68 *deviceNo[] = {
69 "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
70 "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
71 "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
72 "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
73 "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
74 "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
75 "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
76 "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
77 "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
78 "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
79 "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
80 "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
81 "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
82 "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
83 "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
84 "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff",
85 };
86 #else
87 static
88 char *MasterDevice = "/dev/pty%s",
89 *SlaveDevice = "/dev/tty%s",
90 *deviceNo[] = {
91 "p0","p1","p2","p3","p4","p5","p6","p7",
92 "p8","p9","pa","pb","pc","pd","pe","pf",
93 "q0","q1","q2","q3","q4","q5","q6","q7",
94 "q8","q9","qa","qb","qc","qd","qe","qf",
95 "r0","r1","r2","r3","r4","r5","r6","r7",
96 "r8","r9","ra","rb","rc","rd","re","rf",
97 "s0","s1","s2","s3","s4","s5","s6","s7",
98 "s8","s9","sa","sb","sc","sd","se","sf",
99 0,
100 };
101 #endif
102 #endif
103 #endif
104
105 static char SlaveName[DEVICELEN];
106
107 extern int errno;
108
109 #ifndef HAVE_SETEUID
110 # ifdef HAVE_SETREUID
111 # define seteuid(e) setreuid(-1, (e))
112 # else
113 # ifdef HAVE_SETRESUID
114 # define seteuid(e) setresuid(-1, (e), -1)
115 # else
116
117 # endif
118 # endif
119 #endif
120
121 static VALUE eChildExited;
122
123 static VALUE
124 echild_status(self)
125 VALUE self;
126 {
127 return rb_ivar_get(self, rb_intern("status"));
128 }
129
130 struct pty_info {
131 int fd;
132 pid_t child_pid;
133 VALUE thread;
134 };
135
136 static VALUE
137 pty_syswait(info)
138 struct pty_info *info;
139 {
140 extern VALUE rb_last_status;
141 int cpid, status;
142 char buf[1024];
143 VALUE exc, st;
144 char *state = "changed";
145
146 cpid = rb_waitpid(info->child_pid, &status, WUNTRACED);
147 st = rb_last_status;
148
149 if (cpid == 0 || cpid == -1)
150 return Qnil;
151
152 #ifdef IF_STOPPED
153 if (IF_STOPPED(status)) {
154 state = "stopped";
155 }
156 #else
157 #ifdef WIFSTOPPED
158 if (WIFSTOPPED(status)) {
159 state = "stopped";
160 }
161 #else
162 ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
163 #endif
164 #endif
165 if (WIFEXITED(status)) {
166 state = "exit";
167 }
168
169 snprintf(buf, sizeof(buf), "pty - %s: %d", state, cpid);
170 exc = rb_exc_new2(eChildExited, buf);
171 rb_iv_set(exc, "status", st);
172 rb_funcall(info->thread, rb_intern("raise"), 1, exc);
173 return Qnil;
174 }
175
176 static void getDevice _((int*, int*));
177
178 struct exec_info {
179 int argc;
180 VALUE *argv;
181 };
182
183 static VALUE
184 pty_exec(arg)
185 struct exec_info *arg;
186 {
187 return rb_f_exec(arg->argc, arg->argv);
188 }
189
190 static void
191 establishShell(argc, argv, info)
192 int argc;
193 VALUE *argv;
194 struct pty_info *info;
195 {
196 static int i,master,slave,currentPid;
197 char *p,*getenv();
198 struct passwd *pwent;
199 VALUE v;
200 struct exec_info arg;
201 int status;
202
203 if (argc == 0) {
204 char *shellname;
205
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);
221
222 info->thread = rb_thread_current();
223 currentPid = getpid();
224 if((i = fork()) < 0) {
225 rb_sys_fail("fork failed");
226 }
227
228 if(i == 0) {
229 currentPid = getpid();
230
231
232
233
234 #ifdef HAVE_SETSID
235 (void) setsid();
236 #else
237 # ifdef HAVE_SETPGRP
238 # ifdef SETGRP_VOID
239 if (setpgrp() == -1)
240 perror("setpgrp()");
241 # else
242 if (setpgrp(0, currentPid) == -1)
243 rb_sys_fail("setpgrp()");
244 if ((i = open("/dev/tty", O_RDONLY)) < 0)
245 rb_sys_fail("/dev/tty");
246 else {
247 if (ioctl(i, TIOCNOTTY, (char *)0))
248 perror("ioctl(TIOCNOTTY)");
249 close(i);
250 }
251 # endif
252 # endif
253 #endif
254
255
256
257
258 #if defined(TIOCSCTTY)
259 close(master);
260 (void) ioctl(slave, TIOCSCTTY, (char *)0);
261
262 #else
263 close(slave);
264 slave = open(SlaveName, O_RDWR);
265 if (slave < 0) {
266 perror("open: pty slave");
267 _exit(1);
268 }
269 close(master);
270 #endif
271 dup2(slave,0);
272 dup2(slave,1);
273 dup2(slave,2);
274 close(slave);
275 #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
276 seteuid(getuid());
277 #endif
278
279 arg.argc = argc;
280 arg.argv = argv;
281 rb_protect(pty_exec, (VALUE)&arg, &status);
282 sleep(1);
283 _exit(1);
284 }
285
286 close(slave);
287
288 info->child_pid = i;
289 info->fd = master;
290 }
291
292 static VALUE
293 pty_kill_child(info)
294 struct pty_info *info;
295 {
296 if (rb_funcall(info->thread, rb_intern("alive?"), 0, 0) == Qtrue &&
297 kill(info->child_pid, 0) == 0) {
298 rb_thread_schedule();
299 if (kill(info->child_pid, SIGTERM) == 0) {
300 rb_thread_schedule();
301 if (kill(info->child_pid, 0) == 0) {
302 kill(info->child_pid, SIGINT);
303 rb_thread_schedule();
304 if (kill(info->child_pid, 0) == 0)
305 kill(info->child_pid, SIGKILL);
306 }
307 }
308 }
309 rb_funcall(info->thread, rb_intern("join"), 0, 0);
310 return Qnil;
311 }
312
313
314 #ifdef HAVE_OPENPTY
315
316
317
318
319 static void
320 getDevice(master,slave)
321 int *master,*slave;
322 {
323 if (openpty(master, slave, SlaveName,
324 (struct termios *)0, (struct winsize *)0) == -1) {
325 rb_raise(rb_eRuntimeError, "openpty() failed");
326 }
327 }
328 #else
329 #ifdef HAVE__GETPTY
330 static void
331 getDevice(master,slave)
332 int *master,*slave;
333 {
334 char *name;
335
336 if (!(name = _getpty(master, O_RDWR, 0622, 0))) {
337 rb_raise(rb_eRuntimeError, "_getpty() failed");
338 }
339
340 *slave = open(name, O_RDWR);
341 strcpy(SlaveName, name);
342 }
343 #else
344 static void
345 getDevice(master,slave)
346 int *master,*slave;
347 {
348 char **p;
349 int i,j;
350 char MasterName[DEVICELEN];
351
352 #ifdef HAVE_PTSNAME
353 char *pn;
354 void (*s)();
355
356 extern char *ptsname(int);
357 extern int unlockpt(int);
358 extern int grantpt(int);
359
360 if((i = open("/dev/ptmx", O_RDWR, 0)) != -1) {
361 s = signal(SIGCHLD, SIG_DFL);
362 if(grantpt(i) != -1) {
363 signal(SIGCHLD, s);
364 if(unlockpt(i) != -1) {
365 if((pn = ptsname(i)) != NULL) {
366 if((j = open(pn, O_RDWR, 0)) != -1) {
367 #if defined I_PUSH && !defined linux
368 if(ioctl(j, I_PUSH, "ptem") != -1) {
369 if(ioctl(j, I_PUSH, "ldterm") != -1) {
370 #endif
371 *master = i;
372 *slave = j;
373 strcpy(SlaveName, pn);
374 return;
375 #if defined I_PUSH && !defined linux
376 }
377 }
378 #endif
379 }
380 }
381 }
382 }
383 close(i);
384 }
385 rb_raise(rb_eRuntimeError, "Cannot get Master/Slave device");
386 #else
387 for (p = deviceNo; *p != NULL; p++) {
388 sprintf(MasterName,MasterDevice,*p);
389 if ((i = open(MasterName,O_RDWR,0)) >= 0) {
390 *master = i;
391 sprintf(SlaveName,SlaveDevice,*p);
392 if ((j = open(SlaveName,O_RDWR,0)) >= 0) {
393 *slave = j;
394 chown(SlaveName, getuid(), getgid());
395 chmod(SlaveName, 0622);
396 return;
397 }
398 close(i);
399 }
400 }
401 rb_raise(rb_eRuntimeError, "Cannot get %s", SlaveName);
402 #endif
403 }
404 #endif
405 #endif
406
407 static void
408 freeDevice()
409 {
410 chmod(SlaveName, 0666);
411 chown(SlaveName, 0, 0);
412 }
413
414
415 static VALUE
416 pty_getpty(argc, argv, self)
417 int argc;
418 VALUE *argv;
419 VALUE self;
420 {
421 VALUE res, th;
422 struct pty_info info, thinfo;
423 OpenFile *wfptr,*rfptr;
424 VALUE rport = rb_obj_alloc(rb_cFile);
425 VALUE wport = rb_obj_alloc(rb_cFile);
426
427 MakeOpenFile(rport, rfptr);
428 MakeOpenFile(wport, wfptr);
429
430 establishShell(argc, argv, &info);
431
432 rfptr->mode = rb_io_mode_flags("r");
433 rfptr->f = fdopen(info.fd, "r");
434 rfptr->path = strdup(SlaveName);
435
436 wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC;
437 wfptr->f = fdopen(dup(info.fd), "w");
438 wfptr->path = strdup(SlaveName);
439
440 res = rb_ary_new2(3);
441 rb_ary_store(res,0,(VALUE)rport);
442 rb_ary_store(res,1,(VALUE)wport);
443 rb_ary_store(res,2,INT2FIX(info.child_pid));
444
445 th = rb_thread_create(pty_syswait, (void*)&info);
446 thinfo.thread = th;
447 thinfo.child_pid = info.child_pid;
448
449 if (rb_block_given_p()) {
450 rb_ensure(rb_yield, res, pty_kill_child, (VALUE)&thinfo);
451 return Qnil;
452 }
453 return res;
454 }
455
456
457 static VALUE
458 pty_protect(self)
459 VALUE self;
460 {
461 rb_warn("PTY::protect_signal is no longer needed");
462 rb_yield(Qnil);
463 return self;
464 }
465
466
467 static VALUE
468 pty_reset_signal(self)
469 VALUE self;
470 {
471 rb_warn("PTY::reset_signal is no longer needed");
472 return self;
473 }
474
475 static VALUE cPTY;
476
477 void
478 Init_pty()
479 {
480 cPTY = rb_define_module("PTY");
481 rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
482 rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
483 rb_define_module_function(cPTY,"protect_signal",pty_protect,0);
484 rb_define_module_function(cPTY,"reset_signal",pty_reset_signal,0);
485
486 eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
487 rb_define_method(eChildExited,"status",echild_status,0);
488 }