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