1 /*
2 *
3 * Copyright (c) International Business Machines Corp., 2002
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /* 12/23/2002 Port to LTP robbiew@us.ibm.com */
21 /* 06/30/2001 Port to Linux nsharoff@us.ibm.com */
22
23 #define _GNU_SOURCE
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/wait.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <termios.h>
33 #include <unistd.h>
34
35 #include "test.h"
36 #include "safe_macros.h"
37 #include "lapi/ioctl.h"
38
39 char *TCID = "pty01"; /* Test program identifier. */
40 int TST_TOTAL = 5; /* Total number of test cases. */
41 /**************/
42
43 /*
44 * pty master clone device
45 */
46 #define MASTERCLONE "/dev/ptmx"
47
48 /*
49 * string for testing read/write on ptys
50 */
51 #define STRING "Linux Test Project\n"
52
53 /*
54 * test buffer size
55 */
56 #define TESTSIZE 1024
57
58 /*
59 * mode we expect grantpt() to leave pty as
60 */
61 #define PTY_MODE 020622
62
63 /*
64 * number of procs for parallel test
65 */
66 #define NUMPROCS 15
67
68 /*
69 * test slave locking
70 */
test1(void)71 static int test1(void)
72 {
73 int masterfd; /* master pty fd */
74 int slavefd; /* slave pty fd */
75 char *slavename;
76 struct stat st;
77 char buf[TESTSIZE];
78
79 masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
80
81 slavename = ptsname(masterfd);
82 if (slavename == NULL) {
83 tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
84 }
85
86 if (grantpt(masterfd) != 0) {
87 tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
88 }
89
90 if (stat(slavename, &st) != 0) {
91 tst_brkm(TBROK | TERRNO, NULL, "stat(%s) failed", slavename);
92 }
93 if (st.st_uid != getuid()) {
94 tst_brkm(TBROK, NULL, "uid mismatch");
95 }
96
97 /* grantpt() is a no-op in bionic. */
98 #ifndef __BIONIC__
99 if (st.st_mode != (S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP)) {
100 tst_brkm(TBROK, NULL, "mode mismatch (mode=%o)", st.st_mode);
101 }
102 #endif
103
104 slavefd = open(slavename, O_RDWR);
105 if (slavefd >= 0) {
106 tst_brkm(TBROK, NULL, "open didn't fail as expected!");
107 }
108
109 if (unlockpt(masterfd) != 0) {
110 tst_brkm(TBROK | TERRNO, NULL, "unlockpt() failed");
111 }
112
113 slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
114
115 /*
116 * test writing to the master / reading from the slave
117 */
118 if (write(masterfd, STRING, strlen(STRING)) != strlen(STRING)) {
119 /*
120 * XXX: the errno printout might be garbage, but better to be
121 * safe than sorry..
122 */
123 tst_brkm(TFAIL | TERRNO, NULL, "write to master");
124 }
125
126 if (read(slavefd, buf, strlen(STRING)) != strlen(STRING)) {
127 /* XXX: Same as write above.. */
128 tst_brkm(TFAIL | TERRNO, NULL, "read from slave");
129 }
130 if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
131 tst_brkm(TFAIL, NULL,
132 "strings are different (STRING = '%s' != buf = '%s')",
133 STRING, buf);
134 }
135
136 /*
137 * test writing to the slave / reading from the master
138 */
139 if (write(slavefd, STRING, strlen(STRING)) != strlen(STRING)) {
140 /* XXX: Same as write above.. */
141 tst_brkm(TFAIL | TERRNO, NULL, "write to slave");
142 }
143
144 if (read(masterfd, buf, strlen(STRING)) != strlen(STRING)) {
145 /* XXX: Same as write above.. */
146 tst_brkm(TFAIL | TERRNO, NULL, "read from master");
147 }
148 if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
149 tst_brkm(TFAIL, NULL,
150 "strings are different (STRING = '%s' != buf = '%s').",
151 STRING, buf);
152 }
153
154 /*
155 * try an invalid ioctl on the slave...
156 */
157 if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
158 tst_brkm(TFAIL, NULL,
159 "invalid slave TIOCGWINSZ ioctl succeeded.. it should "
160 "have failed");
161 }
162
163 /*
164 * try an invalid ioctl on the master...
165 */
166 if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
167 tst_brkm(TFAIL, NULL,
168 "invalid master TIOCGWINSZ ioctl succeeded.. it should "
169 "have failed");
170 }
171
172 /*
173 * close pty fds
174 */
175 if (close(slavefd) != 0) {
176 tst_brkm(TBROK | TERRNO, NULL, "close of slave");
177 }
178 if (close(masterfd) != 0) {
179 tst_brkm(TBROK | TERRNO, NULL, "close of master");
180 }
181 tst_resm(TPASS, "test1");
182 /** NOTREACHED **/
183 return 0;
184 }
185
186 /*
187 * test slave operations with closed master
188 */
test2(void)189 static void test2(void)
190 {
191 int masterfd; /* master pty fd */
192 int slavefd; /* slave pty fd */
193 int i;
194 char *slavename;
195 char c;
196
197 masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
198
199 slavename = ptsname(masterfd);
200 if (slavename == NULL) {
201 tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
202 }
203
204 if (grantpt(masterfd) != 0) {
205 tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
206 }
207
208 if (unlockpt(masterfd) != 0) {
209 tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
210 }
211
212 slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
213
214 /*
215 * close pty fds. See what happens when we close the master
216 * first.
217 */
218 if (close(masterfd) != 0) {
219 tst_brkm(TBROK | TERRNO, NULL, "close()");
220 }
221
222 errno = 0;
223 if ((i = read(slavefd, &c, 1)) == 1) {
224 tst_brkm(TFAIL, NULL,
225 "reading from slave fd should have failed, but didn't"
226 "(read '%c')", c);
227 }
228
229 if ((i = write(slavefd, &c, 1)) == 1) {
230 tst_brkm(TFAIL, NULL,
231 "writing to slave fd should have failed, but didn't");
232 }
233
234 if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
235 tst_brkm(TFAIL, NULL,
236 "trying TIOCGWINSZ on slave fd should have failed, "
237 "but didn't");
238 }
239
240 if (close(slavefd) != 0) {
241 tst_brkm(TBROK, NULL, "close");
242 }
243 tst_resm(TPASS, "test2");
244 }
245
246 /*
247 * test operations on master with closed slave
248 */
test3(void)249 static void test3(void)
250 {
251 int masterfd; /* master pty fd */
252
253 masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
254
255 if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
256 tst_brkm(TFAIL | TERRNO, NULL,
257 "trying TIOCGWINSZ on master with no open slave "
258 "succeeded unexpectedly");
259 }
260 tst_resm(TPASS, "test3");
261 }
262
263 /*
264 * test multiple opens on slave side of pty
265 */
test4(void)266 static void test4(void)
267 {
268 int masterfd; /* master pty fd */
269 int slavefd; /* slave pty fd */
270 int slavefd2;
271 int slavefd3;
272 char *slavename;
273
274 masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
275
276 slavename = ptsname(masterfd);
277 if (slavename == NULL) {
278 tst_brkm(TBROK, NULL, "ptsname() call failed");
279 }
280
281 if (grantpt(masterfd) != 0) {
282 tst_brkm(TBROK, NULL, "grantpt() call failed");
283 }
284
285 if (unlockpt(masterfd) != 0) {
286 tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
287 }
288
289 slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
290
291 slavefd2 = open(slavename, O_RDWR);
292 if (slavefd < 0) {
293 tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (again)",
294 slavename);
295 }
296
297 slavefd3 = open(slavename, O_RDWR);
298 if (slavefd < 0) {
299 tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (once more)",
300 slavename);
301 }
302
303 /*
304 * close pty fds.
305 */
306 if (close(slavefd) != 0) {
307 tst_brkm(TBROK | TERRNO, NULL, "close slave");
308 }
309
310 if (close(slavefd2) != 0) {
311 tst_brkm(TBROK, NULL, "close slave again");
312 }
313
314 if (close(slavefd3) != 0) {
315 tst_brkm(TBROK, NULL, "close slave once more");
316 }
317
318 if (close(masterfd) != 0) {
319 tst_brkm(TBROK, NULL, "close master");
320 }
321 tst_resm(TPASS, "test4");
322 }
323
324 /*
325 * test opening/closing lots of ptys in parallel. We may run out
326 * of ptys for this test depending on how the system is configured,
327 * but that's not a fatal error.
328 */
test5(void)329 static void test5(void)
330 {
331 int masterfd; /* master pty fd */
332 char *slavename;
333 int status;
334 int i;
335
336 for (i = 0; i < NUMPROCS; ++i) {
337 switch (fork()) {
338 case -1:
339 tst_brkm(TBROK, NULL, "fork()");
340 break;
341 case 0:
342 masterfd = open(MASTERCLONE, O_RDWR);
343 if (masterfd < 0) {
344 printf("proc %d: opening %s failed: %s",
345 i, MASTERCLONE, strerror(errno));
346 exit(1);
347 }
348 if (grantpt(masterfd) != 0) {
349 printf("proc %d: grantpt() call failed: %s",
350 i, strerror(errno));
351 exit(1);
352 }
353 slavename = ptsname(masterfd);
354 if (slavename == NULL) {
355 printf("proc %d: ptsname() call failed: %s",
356 i, strerror(errno));
357 exit(1);
358 }
359 sleep(10);
360 if (close(masterfd) != 0) {
361 printf("proc %d: close failed: %s",
362 i, strerror(errno));
363 exit(1);
364 }
365 exit(0);
366 default:
367 break;
368 }
369 }
370 while (wait(&status) > 0) {
371 if (status) {
372 tst_brkm(TFAIL, NULL,
373 "child exited with non-zero status %d",
374 status);
375 }
376 }
377 tst_resm(TPASS, "test5");
378 }
379
380 /*
381 * main test driver
382 */
main(void)383 int main(void)
384 {
385 test1();
386 test2();
387 test3();
388 test4();
389 test5();
390
391 /*
392 * all done
393 */
394 tst_exit();
395 }
396