1 /* pseudotty - open pseudo-terminal and print name of slave device to
2 standard output. Read and ignore any data sent to terminal. This
3 is so we can run tests interactively without messing up the screen.
4
5 Copyright 2014-2023 Free Software Foundation, Inc.
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 Originally written by Gavin Smith. */
21
22 #define _XOPEN_SOURCE 600
23 /* for posix_openpt */
24
25 #include <config.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <sys/ioctl.h>
32 #include <sys/types.h>
33 #include <sys/select.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #if defined __sun || defined __hpux /* Solaris, HP-UX */
38 #include <stropts.h>
39 #endif
40
41 #include "termdep.h"
42
43 /* Used by "error" function. */
44 const char *program_name = "pseudotty";
45
46 int
47 main (int argc, char *argv[])
48 {
49 int master, slave, control;
50 char *name;
51 fd_set read_set;
52
53 error (0, 0, "getting pty master fd");
54 master = posix_openpt (O_RDWR);
55 if (master == -1)
56 exit (1);
57
58 error (0, 0, "unlocking slave device");
59 if (grantpt (master) < 0 || unlockpt (master) < 0)
60 exit (1);
61 error (0, 0, "getting file name of slave device...");
62 name = ptsname (master);
63 if (!name)
64 exit (1);
65 error (0, 0, "%s", name);
66
67 error (0, 0, "opening slave device");
68 slave = open (name, O_RDWR);
69 if (slave == -1)
70 exit (1);
71
72 #if defined __sun || defined __hpux /* Solaris, HP-UX */
73 if (isastream (slave))
74 {
75 error (0, 0, "performing STREAMS ioctl's on slave");
76 if (ioctl (slave, I_PUSH, "ptem") < 0
77 || ioctl (slave, I_PUSH, "ldterm") < 0
78 # if defined __sun
79 || ioctl (slave, I_PUSH, "ttcompat") < 0
80 # endif
81 )
82 error (1, 0, "STREAMS ioctl's failed");
83 }
84 #endif
85
86 #if defined (HAVE_TERMIOS_H)
87 {
88 struct termios t;
89 long int disable;
90 disable = fpathconf (slave, _PC_VDISABLE);
91 if (tcgetattr (slave, &t) == -1)
92 error (0, 0, "error calling tcgetattr");
93 else
94 {
95 t.c_cc[VSTART] = disable; /* C-q */
96 t.c_cc[VSTOP] = disable; /* C-s */
97 t.c_cc[VKILL] = disable; /* C-u */
98 t.c_cc[VINTR] = disable; /* C-c */
99 t.c_lflag &= (~ICANON & ~ECHO);
100 t.c_cc[VMIN] = 1;
101 t.c_cc[VTIME] = 0;
102 if (tcsetattr (slave, TCSANOW, &t) == -1)
103 error (0, 0, "error calling tcsetattr");
104 }
105 }
106 #endif
107
108 #if defined (TIOCSWINSZ)
109 {
110 struct winsize ws;
111 ws.ws_col = ws.ws_row = 0;
112
113 error (0, 0, "attempting to set window size");
114 if (ioctl (master, TIOCSWINSZ, &ws) == 0)
115 error (0, 0, "...succeeded");
116 else
117 error (0, 0, "...failed");
118 }
119 #endif
120
121 printf ("%s\n", name);
122 if (fclose (stdout) != 0)
123 error (1, 0, "error closing stdout: aborting");
124
125 error (0, 0, "opening control channel");
126 control = open (argv[1], O_RDONLY);
127 if (control == -1)
128 error (1, 0, "error opening control channel: aborting");
129
130
131 FD_ZERO (&read_set);
132
133 error (0, 0, "entering main loop");
134 while (1)
135 {
136 FD_SET (master, &read_set);
137 FD_SET (control, &read_set);
138
139 select (FD_SETSIZE, &read_set, 0, 0, 0);
140
141 if (FD_ISSET (control, &read_set))
142 {
143 char c;
144 int success;
145 errno = 0;
146 while (1)
147 {
148 error (0, 0, "trying to read");
149 success = read (control, &c, 1);
150 if (success < 0)
151 {
152 if (errno != EINTR)
153 error (1, errno, "read error on control channel");
154 }
155 else if (success == 0)
156 {
157 error (1, 0, "end of file on control channel");
158 }
159 else if (success == 1)
160 {
161 error (0, 0, "read byte 0x%02X", c);
162 break;
163 }
164 }
165
166 /* Feed any read bytes to the program being controlled. */
167 do
168 {
169 success = write (master, &c, 1);
170 if (success == 0)
171 {
172 error (0, 0, "couldn't send byte!");
173 sleep (1);
174 continue;
175 }
176 }
177 while (success == -1 && errno == EINTR);
178
179 if (success != 1)
180 {
181 /* The controlled process has probably exited, or been killed. */
182 error (0, 0, "couldn't send byte (giving up)");
183 sleep (1);
184 }
185 }
186
187 if (FD_ISSET (master, &read_set))
188 {
189 char c;
190 int success;
191 errno = 0;
192 do
193 {
194 success = read (master, &c, 1);
195 }
196 while (success == -1 && errno == EINTR);
197
198 if (success == -1)
199 {
200 /* The controlled process has probably exited, or been killed. */
201 error (0, 0, "read error on master fd");
202 sleep (1);
203 }
204 }
205 }
206
207 return 0; /* NOTREACHED */
208 }