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