Ruby: ext/pty/pty.c Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

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