Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
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
65static VALUE eChildExited;
66
67/* Returns the exit status of the child for which PTY#check
68 * raised this exception
69 */
70static VALUE
71echild_status(VALUE self)
72{
73 return rb_ivar_get(self, rb_intern("status"));
74}
75
76struct pty_info {
77 int fd;
79};
80
81static void getDevice(int*, int*, char [DEVICELEN], int);
82
83struct child_info {
85 char *slavename;
88};
89
90static int
91chfunc(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");
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 }
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
155static void
156establishShell(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);
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);
202 errno = e;
203 if (status) rb_jump_tag(status);
204 rb_sys_fail(errbuf[0] ? errbuf : "fork failed");
205 }
206
207 close(slave);
209
210 info->child_pid = pid;
211 info->fd = master;
212
214}
215
216#if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME)
217static int
218no_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)
228static inline int
229ioctl_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
242static int
243get_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
429static void
430getDevice(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
438static VALUE
439pty_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 */
492static VALUE
493pty_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);
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
522static VALUE
523pty_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
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 */
563static VALUE
564pty_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
601NORETURN(static void raise_from_check(rb_pid_t pid, int status));
602static void
603raise_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());
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 */
647static VALUE
648pty_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
670}
671
672static 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
740void
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}
int errno
#define fail()
#define O_CLOEXEC
Definition: dir.c:28
VALUE rb_define_class_under(VALUE, const char *, VALUE)
Defines a class under the namespace of outer.
Definition: class.c:711
VALUE rb_define_module(const char *)
Definition: class.c:785
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:898
VALUE rb_cFile
Definition: file.c:159
VALUE rb_cIO
Definition: ruby.h:2032
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition: eval.c:668
VALUE rb_eRuntimeError
Definition: error.c:922
VALUE rb_exc_new_str(VALUE, VALUE)
Definition: error.c:974
VALUE rb_ensure(VALUE(*)(VALUE), VALUE, VALUE(*)(VALUE), VALUE)
An equivalent to ensure clause.
Definition: eval.c:1115
void rb_jump_tag(int tag)
Continues the exception caught by rb_protect() and rb_eval_string_protect().
Definition: eval.c:884
void rb_sys_fail(const char *mesg)
Definition: error.c:2795
VALUE rb_obj_alloc(VALUE)
Allocates an instance of klass.
Definition: object.c:1895
VALUE rb_obj_freeze(VALUE)
Make the object unmodifiable.
Definition: object.c:1080
int rb_io_modestr_fmode(const char *modestr)
Definition: io.c:5469
#define FMODE_READWRITE
Definition: io.h:110
#define FMODE_TTY
Definition: io.h:113
#define FMODE_DUPLEX
Definition: io.h:114
#define MakeOpenFile(obj, fp)
Definition: io.h:129
#define FMODE_SYNC
Definition: io.h:112
const char * name
Definition: nkf.c:208
NORETURN(static void raise_from_check(rb_pid_t pid, int status))
void Init_pty(void)
Definition: pty.c:741
#define DEVICELEN
Definition: pty.c:51
#define HEX1(c)
#define WIFSTOPPED(status)
Definition: pty.c:34
#define ERROR_EXIT(str)
#define rb_str_new2
struct rb_execarg * rb_execarg_get(VALUE execarg_obj)
Definition: process.c:2636
#define NULL
#define T_FILE
VALUE rb_last_status_get(void)
Definition: process.c:546
#define RTEST(v)
int kill(pid_t, int)
Definition: win32.c:4819
#define NUM2PIDT(v)
enum ruby_tag_type st
VALUE rb_assoc_new(VALUE, VALUE)
Definition: array.c:896
char * ptsname(int)
#define PIDT2NUM(v)
int close(int __fildes)
uid_t getuid(void)
Definition: win32.c:2795
void rb_execarg_parent_end(VALUE execarg_obj)
Definition: process.c:2883
VALUE rb_ivar_get(VALUE, ID)
Definition: variable.c:1070
VALUE rb_io_close(VALUE)
Definition: io.c:4824
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)
pid_t setsid(void)
const rb_iseq_t const char * error
int snprintf(char *__restrict__, size_t, const char *__restrict__,...) __attribute__((__format__(__printf__
int unlockpt(int)
#define numberof(array)
VALUE rb_detach_process(pid_t pid)
int seteuid(uid_t __uid)
gid_t getgid(void)
Definition: win32.c:2809
void rb_update_max_fd(int fd)
Definition: io.c:218
size_t strlcpy(char *, const char *, size_t)
Definition: strlcpy.c:29
() void(cc->call !=vm_call_general)
const VALUE *pid_t rb_waitpid(pid_t pid, int *status, int flags)
uint32_t i
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Definition: io.c:292
void rb_gc(void)
Definition: gc.c:8695
int dup2(int __fildes, int __fildes2)
Definition: dup2.c:27
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3318
void rb_define_module_function(VALUE, const char *, VALUE(*)(), int)
int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen)
Definition: process.c:3551
void rb_define_singleton_method(VALUE, const char *, VALUE(*)(), int)
#define RB_GC_GUARD(v)
int VALUE v
#define rb_scan_args(argc, argvp, fmt,...)
#define rb_intern(str)
#define UNREACHABLE_RETURN(val)
int chmod(const char *__path, mode_t __mode)
void rb_fd_fix_cloexec(int fd)
Definition: io.c:268
#define rb_pid_t
VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt)
Definition: process.c:2661
void rb_execarg_parent_start(VALUE execarg_obj)
Definition: process.c:2844
#define Qnil
#define RB_TYPE_P(obj, type)
pid_t getpid(void)
#define RFILE(obj)
int rb_grantpt(int fd)
Definition: signal.c:1597
const VALUE * argv
int posix_openpt(int)
int setpgrp(void)
int rb_cloexec_dup(int oldfd)
Definition: io.c:318
VALUE rb_sprintf(const char *,...) __attribute__((format(printf
int chown(const char *__path, uid_t __owner, gid_t __group)
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
const rb_iseq_t const VALUE exc
__mode_t mode_t
void rb_define_method(VALUE, const char *, VALUE(*)(), int)
#define rb_ary_new2
#define WUNTRACED
#define rb_str_new_cstr(str)
void rb_ary_store(VALUE, long, VALUE)
Definition: array.c:1079
char * getlogin(void)
Definition: win32.c:911
#define WNOHANG
VALUE rb_ary_entry(VALUE, long)
Definition: array.c:1512
unsigned long VALUE
Definition: ruby.h:102
Definition: pty.c:83
struct rb_execarg * eargp
Definition: pty.c:87
VALUE execarg_obj
Definition: pty.c:86
int master
Definition: pty.c:84
char * slavename
Definition: pty.c:85
int slave
Definition: pty.c:84
Definition: pty.c:76
int fd
Definition: pty.c:77
rb_pid_t child_pid
Definition: pty.c:78
Definition: io.h:66
int fd
Definition: io.h:68
VALUE pathv
Definition: io.h:72
int mode
Definition: io.h:69
#define getenv(name)
Definition: win32.c:73
int ioctl(int, int,...)
Definition: win32.c:2841