Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

pty.c
Go to the documentation of this file.
1 #include "ruby/config.h"
2 #ifdef RUBY_EXTCONF_H
3 #include RUBY_EXTCONF_H
4 #endif
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sys/file.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #ifdef HAVE_PWD_H
13 #include <pwd.h>
14 #endif
15 #ifdef HAVE_SYS_IOCTL_H
16 #include <sys/ioctl.h>
17 #endif
18 #ifdef HAVE_LIBUTIL_H
19 #include <libutil.h>
20 #endif
21 #ifdef HAVE_UTIL_H
22 #include <util.h>
23 #endif
24 #ifdef HAVE_PTY_H
25 #include <pty.h>
26 #endif
27 #if defined(HAVE_SYS_PARAM_H)
28 /* for __FreeBSD_version */
29 # include <sys/param.h>
30 #endif
31 #ifdef HAVE_SYS_WAIT_H
32 #include <sys/wait.h>
33 #else
34 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
35 #endif
36 #include <ctype.h>
37
38 #include "ruby/io.h"
39 #include "internal.h"
40 #include "ruby/util.h"
41
42 #include <signal.h>
43 #ifdef HAVE_SYS_STROPTS_H
44 #include <sys/stropts.h>
45 #endif
46
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50
51 #define DEVICELEN 16
52
53 #ifndef HAVE_SETEUID
54 # ifdef HAVE_SETREUID
55 # define seteuid(e) setreuid(-1, (e))
56 # else /* NOT HAVE_SETREUID */
57 # ifdef HAVE_SETRESUID
58 # define seteuid(e) setresuid(-1, (e), -1)
59 # else /* NOT HAVE_SETRESUID */
60 /* I can't set euid. (;_;) */
61 # endif /* HAVE_SETRESUID */
62 # endif /* HAVE_SETREUID */
63 #endif /* NO_SETEUID */
64
65 static VALUE eChildExited;
66
67 /* Returns the exit status of the child for which PTY#check
68 * raised this exception
69 */
70 static VALUE
71 echild_status(VALUE self)
72 {
73 return rb_ivar_get(self, rb_intern("status"));
74 }
75
76 struct pty_info {
77 int fd;
78 rb_pid_t child_pid;
79 };
80
81 static void getDevice(int*, int*, char [DEVICELEN], int);
82
83 struct child_info {
84 int master, slave;
85 char *slavename;
86 VALUE execarg_obj;
87 struct rb_execarg *eargp;
88 };
89
90 static int
91 chfunc(void *data, char *errbuf, size_t errbuf_len)
92 {
93 struct child_info *carg = data;
94 int master = carg->master;
95 int slave = carg->slave;
96
97 #define ERROR_EXIT(str) do { \
98 strlcpy(errbuf, (str), errbuf_len); \
99 return -1; \
100 } while (0)
101
102 /*
103 * Set free from process group and controlling terminal
104 */
105 #ifdef HAVE_SETSID
106 (void) setsid();
107 #else /* HAS_SETSID */
108 # ifdef HAVE_SETPGRP
109 # ifdef SETGRP_VOID
110 if (setpgrp() == -1)
111 ERROR_EXIT("setpgrp()");
112 # else /* SETGRP_VOID */
113 if (setpgrp(0, getpid()) == -1)
114 ERROR_EXIT("setpgrp()");
115 {
116 int i = rb_cloexec_open("/dev/tty", O_RDONLY, 0);
117 if (i < 0) ERROR_EXIT("/dev/tty");
118 rb_update_max_fd(i);
119 if (ioctl(i, TIOCNOTTY, (char *)0))
120 ERROR_EXIT("ioctl(TIOCNOTTY)");
121 close(i);
122 }
123 # endif /* SETGRP_VOID */
124 # endif /* HAVE_SETPGRP */
125 #endif /* HAS_SETSID */
126
127 /*
128 * obtain new controlling terminal
129 */
130 #if defined(TIOCSCTTY)
131 close(master);
132 (void) ioctl(slave, TIOCSCTTY, (char *)0);
133 /* errors ignored for sun */
134 #else
135 close(slave);
136 slave = rb_cloexec_open(carg->slavename, O_RDWR, 0);
137 if (slave < 0) {
138 ERROR_EXIT("open: pty slave");
139 }
140 rb_update_max_fd(slave);
141 close(master);
142 #endif
143 dup2(slave,0);
144 dup2(slave,1);
145 dup2(slave,2);
146 if (slave < 0 || slave > 2) (void)!close(slave);
147 #if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
148 if (seteuid(getuid())) ERROR_EXIT("seteuid()");
149 #endif
150
151 return rb_exec_async_signal_safe(carg->eargp, errbuf, sizeof(errbuf_len));
152 #undef ERROR_EXIT
153 }
154
155 static void
156 establishShell(int argc, VALUE *argv, struct pty_info *info,
157 char SlaveName[DEVICELEN])
158 {
159 int master, slave, status = 0;
160 rb_pid_t pid;
161 char *p, *getenv();
162 VALUE v;
163 struct child_info carg;
164 char errbuf[32];
165
166 if (argc == 0) {
167 const char *shellname = "/bin/sh";
168
169 if ((p = getenv("SHELL")) != NULL) {
170 shellname = p;
171 }
172 else {
173 #if defined HAVE_PWD_H
174 const char *username = getenv("USER");
175 struct passwd *pwent = getpwnam(username ? username : getlogin());
176 if (pwent && pwent->pw_shell)
177 shellname = pwent->pw_shell;
178 #endif
179 }
180 v = rb_str_new2(shellname);
181 argc = 1;
182 argv = &v;
183 }
184
185 carg.execarg_obj = rb_execarg_new(argc, argv, 1, 0);
186 carg.eargp = rb_execarg_get(carg.execarg_obj);
187 rb_execarg_parent_start(carg.execarg_obj);
188
189 getDevice(&master, &slave, SlaveName, 0);
190
191 carg.master = master;
192 carg.slave = slave;
193 carg.slavename = SlaveName;
194 errbuf[0] = '\0';
195 pid = rb_fork_async_signal_safe(&status, chfunc, &carg, Qnil, errbuf, sizeof(errbuf));
196
197 if (pid < 0) {
198 int e = errno;
199 close(master);
200 close(slave);
201 rb_execarg_parent_end(carg.execarg_obj);
202 errno = e;
203 if (status) rb_jump_tag(status);
204 rb_sys_fail(errbuf[0] ? errbuf : "fork failed");
205 }
206
207 close(slave);
208 rb_execarg_parent_end(carg.execarg_obj);
209
210 info->child_pid = pid;
211 info->fd = master;
212
213 RB_GC_GUARD(carg.execarg_obj);
214 }
215
216 #if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME)
217 static int
218 no_mesg(char *slavedevice, int nomesg)
219 {
220 if (nomesg)
221 return chmod(slavedevice, 0600);
222 else
223 return 0;
224 }
225 #endif
226
227 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
228 static inline int
229 ioctl_I_PUSH(int fd, const char *const name)
230 {
231 int ret = 0;
232 # if defined(I_FIND)
233 ret = ioctl(fd, I_FIND, name);
234 # endif
235 if (ret == 0) {
236 ret = ioctl(fd, I_PUSH, name);
237 }
238 return ret;
239 }
240 #endif
241
242 static int
243 get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
244 {
245 #if defined(HAVE_POSIX_OPENPT)
246 /* Unix98 PTY */
247 int masterfd = -1, slavefd = -1;
248 char *slavedevice;
249
250 #if defined(__sun) || defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
251 /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
252 /* FreeBSD 9.2 or later supports O_CLOEXEC
253 * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */
254 if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
255 if (rb_grantpt(masterfd) == -1) goto error;
256 rb_fd_fix_cloexec(masterfd);
257 #else
258 {
259 int flags = O_RDWR|O_NOCTTY;
260 # if defined(O_CLOEXEC)
261 /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally.
262 * So version dependency on GNU/Linux is same as O_CLOEXEC with open().
263 * O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */
264 flags |= O_CLOEXEC;
265 # endif
266 if ((masterfd = posix_openpt(flags)) == -1) goto error;
267 }
268 rb_fd_fix_cloexec(masterfd);
269 if (rb_grantpt(masterfd) == -1) goto error;
270 #endif
271 if (unlockpt(masterfd) == -1) goto error;
272 if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
273 if (no_mesg(slavedevice, nomesg) == -1) goto error;
274 if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
275 rb_update_max_fd(slavefd);
276
277 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
278 if (ioctl_I_PUSH(slavefd, "ptem") == -1) goto error;
279 if (ioctl_I_PUSH(slavefd, "ldterm") == -1) goto error;
280 if (ioctl_I_PUSH(slavefd, "ttcompat") == -1) goto error;
281 #endif
282
283 *master = masterfd;
284 *slave = slavefd;
285 strlcpy(SlaveName, slavedevice, DEVICELEN);
286 return 0;
287
288 error:
289 if (slavefd != -1) close(slavefd);
290 if (masterfd != -1) close(masterfd);
291 if (fail) {
292 rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
293 }
294 return -1;
295 #elif defined HAVE_OPENPTY
296 /*
297 * Use openpty(3) of 4.3BSD Reno and later,
298 * or the same interface function.
299 */
300 if (openpty(master, slave, SlaveName,
301 (struct termios *)0, (struct winsize *)0) == -1) {
302 if (!fail) return -1;
303 rb_raise(rb_eRuntimeError, "openpty() failed");
304 }
305 rb_fd_fix_cloexec(*master);
306 rb_fd_fix_cloexec(*slave);
307 if (no_mesg(SlaveName, nomesg) == -1) {
308 if (!fail) return -1;
309 rb_raise(rb_eRuntimeError, "can't chmod slave pty");
310 }
311
312 return 0;
313
314 #elif defined HAVE__GETPTY
315 /* SGI IRIX */
316 char *name;
317 mode_t mode = nomesg ? 0600 : 0622;
318
319 if (!(name = _getpty(master, O_RDWR, mode, 0))) {
320 if (!fail) return -1;
321 rb_raise(rb_eRuntimeError, "_getpty() failed");
322 }
323 rb_fd_fix_cloexec(*master);
324
325 *slave = rb_cloexec_open(name, O_RDWR, 0);
326 /* error check? */
327 rb_update_max_fd(*slave);
328 strlcpy(SlaveName, name, DEVICELEN);
329
330 return 0;
331 #elif defined(HAVE_PTSNAME)
332 /* System V */
333 int masterfd = -1, slavefd = -1;
334 char *slavedevice;
335 void (*s)();
336
337 extern char *ptsname(int);
338 extern int unlockpt(int);
339
340 #if defined(__sun)
341 /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
342 if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
343 if(rb_grantpt(masterfd) == -1) goto error;
344 rb_fd_fix_cloexec(masterfd);
345 #else
346 if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
347 rb_update_max_fd(masterfd);
348 if(rb_grantpt(masterfd) == -1) goto error;
349 #endif
350 if(unlockpt(masterfd) == -1) goto error;
351 if((slavedevice = ptsname(masterfd)) == NULL) goto error;
352 if (no_mesg(slavedevice, nomesg) == -1) goto error;
353 if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error;
354 rb_update_max_fd(slavefd);
355 #if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
356 if(ioctl_I_PUSH(slavefd, "ptem") == -1) goto error;
357 if(ioctl_I_PUSH(slavefd, "ldterm") == -1) goto error;
358 ioctl_I_PUSH(slavefd, "ttcompat");
359 #endif
360 *master = masterfd;
361 *slave = slavefd;
362 strlcpy(SlaveName, slavedevice, DEVICELEN);
363 return 0;
364
365 error:
366 if (slavefd != -1) close(slavefd);
367 if (masterfd != -1) close(masterfd);
368 if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device");
369 return -1;
370 #else
371 /* BSD */
372 int masterfd = -1, slavefd = -1;
373 int i;
374 char MasterName[DEVICELEN];
375
376 #define HEX1(c) \
377 c"0",c"1",c"2",c"3",c"4",c"5",c"6",c"7", \
378 c"8",c"9",c"a",c"b",c"c",c"d",c"e",c"f"
379
380 #if defined(__hpux)
381 static const char MasterDevice[] = "/dev/ptym/pty%s";
382 static const char SlaveDevice[] = "/dev/pty/tty%s";
383 static const char deviceNo[][3] = {
384 HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"),
385 HEX1("t"), HEX1("u"), HEX1("v"), HEX1("w"),
386 };
387 #elif defined(_IBMESA) /* AIX/ESA */
388 static const char MasterDevice[] = "/dev/ptyp%s";
389 static const char SlaveDevice[] = "/dev/ttyp%s";
390 static const char deviceNo[][3] = {
391 HEX1("0"), HEX1("1"), HEX1("2"), HEX1("3"),
392 HEX1("4"), HEX1("5"), HEX1("6"), HEX1("7"),
393 HEX1("8"), HEX1("9"), HEX1("a"), HEX1("b"),
394 HEX1("c"), HEX1("d"), HEX1("e"), HEX1("f"),
395 };
396 #else /* 4.2BSD */
397 static const char MasterDevice[] = "/dev/pty%s";
398 static const char SlaveDevice[] = "/dev/tty%s";
399 static const char deviceNo[][3] = {
400 HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"),
401 };
402 #endif
403 #undef HEX1
404 for (i = 0; i < numberof(deviceNo); i++) {
405 const char *const devno = deviceNo[i];
406 snprintf(MasterName, sizeof MasterName, MasterDevice, devno);
407 if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) {
408 rb_update_max_fd(masterfd);
409 *master = masterfd;
410 snprintf(SlaveName, DEVICELEN, SlaveDevice, devno);
411 if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) {
412 rb_update_max_fd(slavefd);
413 *slave = slavefd;
414 if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
415 if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
416 return 0;
417 }
418 close(masterfd);
419 }
420 }
421 error:
422 if (slavefd != -1) close(slavefd);
423 if (masterfd != -1) close(masterfd);
424 if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName);
425 return -1;
426 #endif
427 }
428
429 static void
430 getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg)
431 {
432 if (get_device_once(master, slave, SlaveName, nomesg, 0)) {
433 rb_gc();
434 get_device_once(master, slave, SlaveName, nomesg, 1);
435 }
436 }
437
438 static VALUE
439 pty_close_pty(VALUE assoc)
440 {
441 VALUE io;
442 int i;
443
444 for (i = 0; i < 2; i++) {
445 io = rb_ary_entry(assoc, i);
446 if (RB_TYPE_P(io, T_FILE) && 0 <= RFILE(io)->fptr->fd)
447 rb_io_close(io);
448 }
449 return Qnil;
450 }
451
452 /*
453 * call-seq:
454 * PTY.open => [master_io, slave_file]
455 * PTY.open {|master_io, slave_file| ... } => block value
456 *
457 * Allocates a pty (pseudo-terminal).
458 *
459 * In the block form, yields two arguments <tt>master_io, slave_file</tt>
460 * and the value of the block is returned from +open+.
461 *
462 * The IO and File are both closed after the block completes if they haven't
463 * been already closed.
464 *
465 * PTY.open {|master, slave|
466 * p master #=> #<IO:masterpty:/dev/pts/1>
467 * p slave #=> #<File:/dev/pts/1>
468 * p slave.path #=> "/dev/pts/1"
469 * }
470 *
471 * In the non-block form, returns a two element array, <tt>[master_io,
472 * slave_file]</tt>.
473 *
474 * master, slave = PTY.open
475 * # do something with master for IO, or the slave file
476 *
477 * The arguments in both forms are:
478 *
479 * +master_io+:: the master of the pty, as an IO.
480 * +slave_file+:: the slave of the pty, as a File. The path to the
481 * terminal device is available via +slave_file.path+
482 *
483 * IO#raw! is usable to disable newline conversions:
484 *
485 * require 'io/console'
486 * PTY.open {|m, s|
487 * s.raw!
488 * ...
489 * }
490 *
491 */
492 static VALUE
493 pty_open(VALUE klass)
494 {
495 int master_fd, slave_fd;
496 char slavename[DEVICELEN];
497 VALUE master_io, slave_file;
498 rb_io_t *master_fptr, *slave_fptr;
499 VALUE assoc;
500
501 getDevice(&master_fd, &slave_fd, slavename, 1);
502
503 master_io = rb_obj_alloc(rb_cIO);
504 MakeOpenFile(master_io, master_fptr);
505 master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
506 master_fptr->fd = master_fd;
507 master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
508
509 slave_file = rb_obj_alloc(rb_cFile);
510 MakeOpenFile(slave_file, slave_fptr);
511 slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY;
512 slave_fptr->fd = slave_fd;
513 slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
514
515 assoc = rb_assoc_new(master_io, slave_file);
516 if (rb_block_given_p()) {
517 return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
518 }
519 return assoc;
520 }
521
522 static VALUE
523 pty_detach_process(VALUE v)
524 {
525 struct pty_info *info = (void *)v;
526 #ifdef WNOHANG
527 int st;
528 if (rb_waitpid(info->child_pid, &st, WNOHANG) <= 0)
529 return Qnil;
530 #endif
531 rb_detach_process(info->child_pid);
532 return Qnil;
533 }
534
535 /*
536 * call-seq:
537 * PTY.spawn(command_line) { |r, w, pid| ... }
538 * PTY.spawn(command_line) => [r, w, pid]
539 * PTY.spawn(command, arguments, ...) { |r, w, pid| ... }
540 * PTY.spawn(command, arguments, ...) => [r, w, pid]
541 *
542 * Spawns the specified command on a newly allocated pty. You can also use the
543 * alias ::getpty.
544 *
545 * The command's controlling tty is set to the slave device of the pty
546 * and its standard input/output/error is redirected to the slave device.
547 *
548 * +command+ and +command_line+ are the full commands to run, given a String.
549 * Any additional +arguments+ will be passed to the command.
550 *
551 * === Return values
552 *
553 * In the non-block form this returns an array of size three,
554 * <tt>[r, w, pid]</tt>.
555 *
556 * In the block form these same values will be yielded to the block:
557 *
558 * +r+:: A readable IO that contains the command's
559 * standard output and standard error
560 * +w+:: A writable IO that is the command's standard input
561 * +pid+:: The process identifier for the command.
562 */
563 static VALUE
564 pty_getpty(int argc, VALUE *argv, VALUE self)
565 {
566 VALUE res;
567 struct pty_info info;
568 rb_io_t *wfptr,*rfptr;
569 VALUE rport = rb_obj_alloc(rb_cFile);
570 VALUE wport = rb_obj_alloc(rb_cFile);
571 char SlaveName[DEVICELEN];
572
573 MakeOpenFile(rport, rfptr);
574 MakeOpenFile(wport, wfptr);
575
576 establishShell(argc, argv, &info, SlaveName);
577
578 rfptr->mode = rb_io_modestr_fmode("r");
579 rfptr->fd = info.fd;
580 rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
581
582 wfptr->mode = rb_io_modestr_fmode("w") | FMODE_SYNC;
583 wfptr->fd = rb_cloexec_dup(info.fd);
584 if (wfptr->fd == -1)
585 rb_sys_fail("dup()");
586 rb_update_max_fd(wfptr->fd);
587 wfptr->pathv = rfptr->pathv;
588
589 res = rb_ary_new2(3);
590 rb_ary_store(res,0,(VALUE)rport);
591 rb_ary_store(res,1,(VALUE)wport);
592 rb_ary_store(res,2,PIDT2NUM(info.child_pid));
593
594 if (rb_block_given_p()) {
595 rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
596 return Qnil;
597 }
598 return res;
599 }
600
601 NORETURN(static void raise_from_check(rb_pid_t pid, int status));
602 static void
603 raise_from_check(rb_pid_t pid, int status)
604 {
605 const char *state;
606 VALUE msg;
607 VALUE exc;
608
609 #if defined(WIFSTOPPED)
610 #elif defined(IF_STOPPED)
611 #define WIFSTOPPED(status) IF_STOPPED(status)
612 #else
613 ---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
614 #endif /* WIFSTOPPED | IF_STOPPED */
615 if (WIFSTOPPED(status)) { /* suspend */
616 state = "stopped";
617 }
618 else if (kill(pid, 0) == 0) {
619 state = "changed";
620 }
621 else {
622 state = "exited";
623 }
624 msg = rb_sprintf("pty - %s: %ld", state, (long)pid);
625 exc = rb_exc_new_str(eChildExited, msg);
626 rb_iv_set(exc, "status", rb_last_status_get());
627 rb_exc_raise(exc);
628 }
629
630 /*
631 * call-seq:
632 * PTY.check(pid, raise = false) => Process::Status or nil
633 * PTY.check(pid, true) => nil or raises PTY::ChildExited
634 *
635 * Checks the status of the child process specified by +pid+.
636 * Returns +nil+ if the process is still alive.
637 *
638 * If the process is not alive, and +raise+ was true, a PTY::ChildExited
639 * exception will be raised. Otherwise it will return a Process::Status
640 * instance.
641 *
642 * +pid+:: The process id of the process to check
643 * +raise+:: If +true+ and the process identified by +pid+ is no longer
644 * alive a PTY::ChildExited is raised.
645 *
646 */
647 static VALUE
648 pty_check(int argc, VALUE *argv, VALUE self)
649 {
650 VALUE pid, exc;
651 rb_pid_t cpid;
652 int status;
653 const int flag =
654 #ifdef WNOHANG
655 WNOHANG|
656 #endif
657 #ifdef WUNTRACED
658 WUNTRACED|
659 #endif
660 0;
661
662 rb_scan_args(argc, argv, "11", &pid, &exc);
663 cpid = rb_waitpid(NUM2PIDT(pid), &status, flag);
664 if (cpid == -1 || cpid == 0) return Qnil;
665
666 if (!RTEST(exc)) return rb_last_status_get();
667 raise_from_check(cpid, status);
668
669 UNREACHABLE_RETURN(Qnil);
670 }
671
672 static VALUE cPTY;
673
674 /*
675 * Document-class: PTY::ChildExited
676 *
677 * Thrown when PTY::check is called for a pid that represents a process that
678 * has exited.
679 */
680
681 /*
682 * Document-class: PTY
683 *
684 * Creates and manages pseudo terminals (PTYs). See also
685 * http://en.wikipedia.org/wiki/Pseudo_terminal
686 *
687 * PTY allows you to allocate new terminals using ::open or ::spawn a new
688 * terminal with a specific command.
689 *
690 * == Example
691 *
692 * In this example we will change the buffering type in the +factor+ command,
693 * assuming that factor uses stdio for stdout buffering.
694 *
695 * If IO.pipe is used instead of PTY.open, this code deadlocks because factor's
696 * stdout is fully buffered.
697 *
698 * # start by requiring the standard library PTY
699 * require 'pty'
700 *
701 * master, slave = PTY.open
702 * read, write = IO.pipe
703 * pid = spawn("factor", :in=>read, :out=>slave)
704 * read.close # we dont need the read
705 * slave.close # or the slave
706 *
707 * # pipe "42" to the factor command
708 * write.puts "42"
709 * # output the response from factor
710 * p master.gets #=> "42: 2 3 7\n"
711 *
712 * # pipe "144" to factor and print out the response
713 * write.puts "144"
714 * p master.gets #=> "144: 2 2 2 2 3 3\n"
715 * write.close # close the pipe
716 *
717 * # The result of read operation when pty slave is closed is platform
718 * # dependent.
719 * ret = begin
720 * master.gets # FreeBSD returns nil.
721 * rescue Errno::EIO # GNU/Linux raises EIO.
722 * nil
723 * end
724 * p ret #=> nil
725 *
726 * == License
727 *
728 * C) Copyright 1998 by Akinori Ito.
729 *
730 * This software may be redistributed freely for this purpose, in full
731 * or in part, provided that this entire copyright notice is included
732 * on any copies of this software and applications and derivations thereof.
733 *
734 * This software is provided on an "as is" basis, without warranty of any
735 * kind, either expressed or implied, as to any matter including, but not
736 * limited to warranty of fitness of purpose, or merchantability, or
737 * results obtained from use of this software.
738 */
739
740 void
741 Init_pty(void)
742 {
743 cPTY = rb_define_module("PTY");
744 /* :nodoc: */
745 rb_define_module_function(cPTY,"getpty",pty_getpty,-1);
746 rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
747 rb_define_singleton_method(cPTY,"check",pty_check,-1);
748 rb_define_singleton_method(cPTY,"open",pty_open,0);
749
750 eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
751 rb_define_method(eChildExited,"status",echild_status,0);
752 }
child_info::master
int master
Definition: pty.c:84
NORETURN
NORETURN(static void raise_from_check(rb_pid_t pid, int status))
rb_execarg_parent_end
void rb_execarg_parent_end(VALUE execarg_obj)
Definition: process.c:2855
void
void
Definition: rb_mjit_min_header-2.7.0.h:13273
ERROR_EXIT
#define ERROR_EXIT(str)
RFILE
#define RFILE(obj)
Definition: ruby.h:1276
rb_cloexec_dup
int rb_cloexec_dup(int oldfd)
Definition: io.c:318
rb_assoc_new
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:896
rb_str_new2
#define rb_str_new2
Definition: intern.h:903
rb_io_close
VALUE rb_io_close(VALUE)
Definition: io.c:4820
rb_exc_new_str
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Definition: error.c:972
posix_openpt
int posix_openpt(int)
close
int close(int __fildes)
rb_last_status_get
VALUE rb_last_status_get(void)
Definition: process.c:518
rb_execarg_parent_start
void rb_execarg_parent_start(VALUE execarg_obj)
Definition: process.c:2816
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:897
rb_scan_args
#define rb_scan_args(argc, argvp, fmt,...)
Definition: rb_mjit_min_header-2.7.0.h:6372
FMODE_TTY
#define FMODE_TTY
Definition: io.h:113
snprintf
int snprintf(char *__restrict, size_t, const char *__restrict,...) __attribute__((__format__(__printf__
O_CLOEXEC
#define O_CLOEXEC
Definition: dir.c:28
rb_io_t::pathv
VALUE pathv
Definition: io.h:72
rb_fork_async_signal_safe
rb_pid_t rb_fork_async_signal_safe(int *status, int(*chfunc)(void *, char *, size_t), void *charg, VALUE fds, char *errmsg, size_t errmsg_buflen)
pty_info::child_pid
rb_pid_t child_pid
Definition: pty.c:78
VALUE
unsigned long VALUE
Definition: ruby.h:102
rb_intern
#define rb_intern(str)
rb_ary_store
void rb_ary_store(VALUE ary, long idx, VALUE val)
Definition: array.c:1079
RB_TYPE_P
#define RB_TYPE_P(obj, type)
Definition: ruby.h:560
unlockpt
int unlockpt(int)
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:772
rb_fd_fix_cloexec
void rb_fd_fix_cloexec(int fd)
Definition: io.c:268
getenv
#define getenv(name)
Definition: win32.c:73
child_info::eargp
struct rb_execarg * eargp
Definition: pty.c:87
WIFSTOPPED
#define WIFSTOPPED(status)
Definition: pty.c:34
rb_define_singleton_method
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1755
rb_define_method
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1551
child_info::execarg_obj
VALUE execarg_obj
Definition: pty.c:86
HEX1
#define HEX1(c)
FMODE_DUPLEX
#define FMODE_DUPLEX
Definition: io.h:114
chown
int chown(const char *, int, int)
Definition: win32.c:4763
rb_io_t::fd
int fd
Definition: io.h:68
NULL
#define NULL
Definition: _sdbm.c:101
rb_waitpid
rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags)
Definition: process.c:1213
ptsname
char * ptsname(int)
error
const rb_iseq_t const char * error
Definition: rb_mjit_min_header-2.7.0.h:13506
rb_cFile
VALUE rb_cFile
Definition: file.c:159
v
int VALUE v
Definition: rb_mjit_min_header-2.7.0.h:12332
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2669
FMODE_SYNC
#define FMODE_SYNC
Definition: io.h:112
getuid
rb_uid_t getuid(void)
Definition: win32.c:2765
rb_ary_entry
VALUE rb_ary_entry(VALUE ary, long offset)
Definition: array.c:1512
rb_ivar_get
VALUE rb_ivar_get(VALUE, ID)
Definition: variable.c:1070
T_FILE
#define T_FILE
Definition: ruby.h:534
WNOHANG
#define WNOHANG
Definition: win32.h:128
i
uint32_t i
Definition: rb_mjit_min_header-2.7.0.h:5464
rb_execarg_new
VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt)
Definition: process.c:2633
child_info
Definition: pty.c:83
rb_jump_tag
void rb_jump_tag(int tag)
Continues the exception caught by rb_protect() and rb_eval_string_protect().
Definition: eval.c:883
rb_detach_process
VALUE rb_detach_process(rb_pid_t pid)
Definition: process.c:1423
child_info::slave
int slave
Definition: pty.c:84
rb_execarg_get
struct rb_execarg * rb_execarg_get(VALUE execarg_obj)
Definition: process.c:2608
rb_update_max_fd
void rb_update_max_fd(int fd)
Definition: io.c:218
rb_obj_freeze
VALUE rb_obj_freeze(VALUE)
Make the object unmodifiable.
Definition: object.c:1080
pty_info
Definition: pty.c:76
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:2793
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:920
Init_pty
void Init_pty(void)
Definition: pty.c:741
FMODE_READWRITE
#define FMODE_READWRITE
Definition: io.h:110
strlcpy
RUBY_EXTERN size_t strlcpy(char *, const char *, size_t)
Definition: strlcpy.c:29
rb_gc
void rb_gc(void)
Definition: gc.c:8679
chmod
int chmod(const char *__path, mode_t __mode)
PIDT2NUM
#define PIDT2NUM(v)
Definition: ruby.h:354
kill
int kill(int, int)
Definition: win32.c:4789
rb_str_new_cstr
#define rb_str_new_cstr(str)
Definition: rb_mjit_min_header-2.7.0.h:6117
getpid
pid_t getpid(void)
dup2
RUBY_EXTERN int dup2(int, int)
Definition: dup2.c:27
setpgrp
int setpgrp(void)
ioctl
int ioctl(int, int,...)
Definition: win32.c:2811
rb_define_module_function
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1771
rb_cloexec_open
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Definition: io.c:292
rb_ary_new2
#define rb_ary_new2
Definition: intern.h:103
rb_exc_raise
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition: eval.c:667
pty_info::fd
int fd
Definition: pty.c:77
internal.h
argv
char ** argv
Definition: ruby.c:223
rb_io_t::mode
int mode
Definition: io.h:69
rb_sprintf
VALUE rb_sprintf(const char *format,...)
Definition: sprintf.c:1197
klass
VALUE klass
Definition: rb_mjit_min_header-2.7.0.h:13254
rb_obj_alloc
VALUE rb_obj_alloc(VALUE)
Allocates an instance of klass.
Definition: object.c:1895
seteuid
int seteuid(uid_t __uid)
rb_execarg
Definition: internal.h:2019
rb_io_modestr_fmode
int rb_io_modestr_fmode(const char *modestr)
Definition: io.c:5465
fail
#define fail()
Definition: date_strptime.c:123
getlogin
char * getlogin()
Definition: win32.c:881
io.h
DEVICELEN
#define DEVICELEN
Definition: pty.c:51
argc
int argc
Definition: ruby.c:222
st
enum ruby_tag_type st
Definition: rb_mjit_min_header-2.7.0.h:11111
getgid
rb_gid_t getgid(void)
Definition: win32.c:2779
MakeOpenFile
#define MakeOpenFile(obj, fp)
Definition: io.h:129
errno
int errno
child_info::slavename
char * slavename
Definition: pty.c:85
WUNTRACED
#define WUNTRACED
Definition: rb_mjit_min_header-2.7.0.h:2493
rb_pid_t
#define rb_pid_t
Definition: rb_mjit_min_header-2.7.0.h:99
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:698
rb_yield
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
rb_ensure
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1114
Qnil
#define Qnil
Definition: ruby.h:469
setsid
pid_t setsid(void)
exc
const rb_iseq_t const VALUE exc
Definition: rb_mjit_min_header-2.7.0.h:13504
rb_exec_async_signal_safe
int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen)
Definition: process.c:3516
mode_t
__mode_t mode_t
Definition: rb_mjit_min_header-2.7.0.h:1331
util.h
rb_io_t
Definition: io.h:66
NUM2PIDT
#define NUM2PIDT(v)
Definition: ruby.h:357
RB_GC_GUARD
#define RB_GC_GUARD(v)
Definition: ruby.h:585
numberof
#define numberof(array)
Definition: etc.c:618
UNREACHABLE_RETURN
#define UNREACHABLE_RETURN(val)
Definition: ruby.h:59
rb_grantpt
int rb_grantpt(int fd)
Definition: signal.c:1597
RTEST
#define RTEST(v)
Definition: ruby.h:481
rb_iv_set
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3307
rb_cIO
RUBY_EXTERN VALUE rb_cIO
Definition: ruby.h:2030
name
const char * name
Definition: nkf.c:208