• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *   Copyright (c) International Business Machines  Corp., 2001
3  *   Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
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 /*
21  * NAME
22  *	ioctl02.c
23  *
24  * DESCRIPTION
25  *	Testcase to test the TCGETA, and TCSETA ioctl implementations for
26  *	the tty driver
27  *
28  * ALGORITHM
29  *	In this test, the parent and child open the parentty and the childtty
30  *	respectively.  After opening the childtty the child flushes the stream
31  *	and sends a SIGUSR1 to the parent (thereby asking it to continue its
32  *	testing). The parent, which was waiting for this signal to arrive, now
33  *	starts the testing. It issues a TCGETA ioctl to get all the tty
34  *	parameters. It then changes them to known values by issuing a TCSETA
35  *	ioctl.  Then the parent issues a TCGETA ioctl again and compares the
36  *	received values with what it had set earlier. The test fails if TCGETA
37  *	or TCSETA fails, or if the received values don't match those that were
38  *	set. The parent does all the testing, the requirement of the child
39  *	process is to moniter the testing done by the parent, and hence the
40  *	child just waits for the parent.
41  *
42  * USAGE:  <for command-line>
43  *  ioctl02 -D /dev/tty[0-9] [-c n] [-f] [-i n] [-I x] [-P x] [-t]
44  *     where,  -c n : Run n copies concurrently.
45  *             -f   : Turn off functionality Testing.
46  *             -i n : Execute test n times.
47  *             -I x : Execute test for x seconds.
48  *             -P x : Pause for x seconds between iterations.
49  *             -t   : Turn on syscall timing.
50  *
51  * HISTORY
52  *	07/2001 Ported by Wayne Boyer
53  *
54  * RESTRICTIONS
55  *	test must be run with the -D option
56  *	test may have to be run as root depending on the tty permissions
57  */
58 
59 #include <stdio.h>
60 #include <fcntl.h>
61 #include <signal.h>
62 #include <errno.h>
63 #include <sys/wait.h>
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <termios.h>
67 #include "test.h"
68 #include "safe_macros.h"
69 #include "lapi/ioctl.h"
70 
71 #define	CNUL	0
72 
73 char *TCID = "ioctl02";
74 int TST_TOTAL = 1;
75 
76 static struct termio termio, save_io;
77 
78 static char *parenttty, *childtty;
79 static int parentfd, childfd;
80 static int parentpid, childpid;
81 static volatile int sigterm, sigusr1, sigusr2;
82 static int closed = 1;
83 
84 static int do_child_setup(void);
85 static int do_parent_setup(void);
86 static int run_ptest(void);
87 static int run_ctest(void);
88 static int chk_tty_parms();
89 static void setup(void);
90 static void cleanup(void);
91 static void help(void);
92 static void do_child(void);
93 void do_child_uclinux(void);
94 static void sigterm_handler(void);
95 
96 static int Devflag;
97 static char *devname;
98 
99 static option_t options[] = {
100 	{"D:", &Devflag, &devname},
101 	{NULL, NULL, NULL}
102 };
103 
main(int ac,char ** av)104 int main(int ac, char **av)
105 {
106 	int lc;
107 	int rval;
108 
109 	tst_parse_opts(ac, av, options, &help);
110 
111 #ifdef UCLINUX
112 	maybe_run_child(&do_child_uclinux, "dS", &parentpid, &childtty);
113 #endif
114 
115 	if (!Devflag)
116 		tst_brkm(TBROK, NULL, "You must specify a tty device with "
117 			 "the -D option.");
118 
119 	tst_require_root();
120 
121 	setup();
122 
123 	for (lc = 0; TEST_LOOPING(lc); lc++) {
124 
125 		tst_count = 0;
126 
127 		parenttty = devname;
128 		childtty = devname;
129 
130 		parentpid = getpid();
131 
132 		childpid = FORK_OR_VFORK();
133 		if (childpid < 0)
134 			tst_brkm(TBROK, cleanup, "fork failed");
135 
136 		if (childpid == 0) {	/* child */
137 #ifdef UCLINUX
138 			if (self_exec(av[0], "dS", parentpid, childtty) < 0)
139 				tst_brkm(TBROK, cleanup, "self_exec failed");
140 #else
141 			do_child();
142 #endif
143 		}
144 
145 		while (!sigusr1)
146 			sleep(1);
147 
148 		sigusr1 = 0;
149 
150 		parentfd = do_parent_setup();
151 		if (parentfd < 0) {
152 			kill(childpid, SIGTERM);
153 			waitpid(childpid, NULL, 0);
154 			cleanup();
155 		}
156 
157 		/* run the parent test */
158 		rval = run_ptest();
159 		if (rval == -1) {
160 			/*
161 			 * Parent cannot set/get ioctl parameters.
162 			 * SIGTERM the child and cleanup.
163 			 */
164 			kill(childpid, SIGTERM);
165 			waitpid(childpid, NULL, 0);
166 			cleanup();
167 		}
168 
169 		if (rval != 0)
170 			tst_resm(TFAIL, "TCGETA/TCSETA tests FAILED with "
171 				 "%d %s", rval, rval > 1 ? "errors" : "error");
172 		else
173 			tst_resm(TPASS, "TCGETA/TCSETA tests SUCCEEDED");
174 
175 		/* FIXME: check return codes. */
176 		(void)kill(childpid, SIGTERM);
177 		(void)waitpid(childpid, NULL, 0);
178 
179 		/*
180 		 * Clean up things from the parent by restoring the
181 		 * tty device information that was saved in setup()
182 		 * and closing the tty file descriptor.
183 		 */
184 		if (ioctl(parentfd, TCSETA, &save_io) == -1)
185 			tst_resm(TINFO, "ioctl restore failed in main");
186 		SAFE_CLOSE(cleanup, parentfd);
187 
188 		closed = 1;
189 	}
190 	cleanup();
191 
192 	tst_exit();
193 }
194 
do_child(void)195 static void do_child(void)
196 {
197 	childfd = do_child_setup();
198 	if (childfd < 0)
199 		_exit(1);
200 	run_ctest();
201 	_exit(0);
202 }
203 
do_child_uclinux(void)204 void do_child_uclinux(void)
205 {
206 	struct sigaction act;
207 
208 	/* Set up the signal handlers again */
209 	act.sa_handler = (void *)sigterm_handler;
210 	act.sa_flags = 0;
211 	sigemptyset(&act.sa_mask);
212 	(void)sigaction(SIGTERM, &act, 0);
213 
214 	/* Run the normal child */
215 	do_child();
216 }
217 
218 /*
219  * run_ptest() - setup the various termio structure values and issue
220  *		 the TCSETA ioctl call with the TEST macro.
221  */
run_ptest(void)222 static int run_ptest(void)
223 {
224 	int i, rval;
225 
226 	/* Use "old" line discipline */
227 	termio.c_line = 0;
228 
229 	/* Set control modes */
230 	termio.c_cflag = B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL;
231 
232 	/* Set control chars. */
233 	for (i = 0; i < NCC; i++) {
234 		if (i == VEOL2)
235 			continue;
236 		termio.c_cc[i] = CSTART;
237 	}
238 
239 	/* Set local modes. */
240 	termio.c_lflag =
241 	    ((unsigned short)(ISIG | ICANON | XCASE | ECHO | ECHOE | NOFLSH));
242 
243 	/* Set input modes. */
244 	termio.c_iflag =
245 	    BRKINT | IGNPAR | INPCK | ISTRIP | ICRNL | IUCLC | IXON | IXANY |
246 	    IXOFF;
247 
248 	/* Set output modes. */
249 	termio.c_oflag = OPOST | OLCUC | ONLCR | ONOCR;
250 
251 	TEST(ioctl(parentfd, TCSETA, &termio));
252 
253 	if (TEST_RETURN < 0) {
254 		tst_resm(TFAIL, "ioctl TCSETA failed : "
255 			 "errno = %d", TEST_ERRNO);
256 		return -1;
257 	}
258 
259 	/* Get termio and see if all parameters actually got set */
260 	rval = ioctl(parentfd, TCGETA, &termio);
261 	if (rval < 0) {
262 		tst_resm(TFAIL, "ioctl TCGETA failed.  Ending test.");
263 		return -1;
264 	}
265 
266 	return chk_tty_parms();
267 }
268 
run_ctest(void)269 static int run_ctest(void)
270 {
271 	/*
272 	 * Wait till the parent has finished testing.
273 	 */
274 	while (!sigterm)
275 		sleep(1);
276 
277 	sigterm = 0;
278 
279 	tst_resm(TINFO, "child: Got SIGTERM from parent.");
280 
281 	if (close(childfd) == -1)
282 		tst_resm(TINFO, "close() in run_ctest() failed");
283 	return 0;
284 }
285 
chk_tty_parms(void)286 static int chk_tty_parms(void)
287 {
288 	int i, flag = 0;
289 
290 	if (termio.c_line != 0) {
291 		tst_resm(TINFO, "line discipline has incorrect value %o",
292 			 termio.c_line);
293 		flag++;
294 	}
295 	/*
296 	 * The following Code Sniffet is disabled to check the value of c_cflag
297 	 * as it seems that due to some changes from 2.6.24 onwards, this
298 	 * setting is not done properly for either of (B50|CS7|CREAD|PARENB|
299 	 * PARODD|CLOCAL|(CREAD|HUPCL|CLOCAL).
300 	 * However, it has been observed that other flags are properly set.
301 	 */
302 #if 0
303 	if (termio.c_cflag != (B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL)) {
304 		tst_resm(TINFO, "cflag has incorrect value. %o",
305 			 termio.c_cflag);
306 		flag++;
307 	}
308 #endif
309 
310 	for (i = 0; i < NCC; i++) {
311 		if (i == VEOL2) {
312 			if (termio.c_cc[VEOL2] == CNUL) {
313 				continue;
314 			} else {
315 				tst_resm(TINFO, "control char %d has "
316 					 "incorrect value %d %d", i,
317 					 termio.c_cc[i], CNUL);
318 				flag++;
319 				continue;
320 			}
321 		}
322 
323 		if (termio.c_cc[i] != CSTART) {
324 			tst_resm(TINFO, "control char %d has incorrect "
325 				 "value %d.", i, termio.c_cc[i]);
326 			flag++;
327 		}
328 	}
329 
330 	if (!
331 	    (termio.c_lflag
332 	     && (ISIG | ICANON | XCASE | ECHO | ECHOE | NOFLSH))) {
333 		tst_resm(TINFO, "lflag has incorrect value. %o",
334 			 termio.c_lflag);
335 		flag++;
336 	}
337 
338 	if (!
339 	    (termio.c_iflag
340 	     && (BRKINT | IGNPAR | INPCK | ISTRIP | ICRNL | IUCLC | IXON | IXANY
341 		 | IXOFF))) {
342 		tst_resm(TINFO, "iflag has incorrect value. %o",
343 			 termio.c_iflag);
344 		flag++;
345 	}
346 
347 	if (!(termio.c_oflag && (OPOST | OLCUC | ONLCR | ONOCR))) {
348 		tst_resm(TINFO, "oflag has incorrect value. %o",
349 			 termio.c_oflag);
350 		flag++;
351 	}
352 
353 	if (!flag)
354 		tst_resm(TINFO, "termio values are set as expected");
355 
356 	return flag;
357 }
358 
do_parent_setup(void)359 static int do_parent_setup(void)
360 {
361 	int pfd;
362 
363 	pfd = SAFE_OPEN(cleanup, parenttty, O_RDWR, 0777);
364 
365 	/* unset the closed flag */
366 	closed = 0;
367 
368 	/* flush tty queues to remove old output */
369 	SAFE_IOCTL(cleanup, pfd, TCFLSH, 2);
370 	return pfd;
371 }
372 
do_child_setup(void)373 static int do_child_setup(void)
374 {
375 	int cfd;
376 
377 	cfd = open(childtty, O_RDWR, 0777);
378 	if (cfd < 0) {
379 		tst_resm(TINFO, "Could not open %s in do_child_setup(), errno "
380 			 "= %d", childtty, errno);
381 		/* signal the parent so we don't hang the test */
382 		kill(parentpid, SIGUSR1);
383 		return -1;
384 	}
385 
386 	/* flush tty queues to remove old output */
387 	if (ioctl(cfd, TCFLSH, 2) < 0) {
388 		tst_resm(TINFO, "ioctl TCFLSH failed. : errno = %d", errno);
389 		/* signal the parent so we don't hang the test */
390 		kill(parentpid, SIGUSR1);
391 		return -1;
392 	}
393 
394 	/* tell the parent that we're done */
395 	kill(parentpid, SIGUSR1);
396 
397 	return cfd;
398 }
399 
400 /*
401  * Define the signals handlers here.
402  */
sigterm_handler(void)403 static void sigterm_handler(void)
404 {
405 	sigterm = 1;
406 }
407 
sigusr1_handler(void)408 static void sigusr1_handler(void)
409 {
410 	sigusr1 = 1;
411 }
412 
sigusr2_handler(void)413 static void sigusr2_handler(void)
414 {
415 	sigusr2 = 1;
416 }
417 
help(void)418 static void help(void)
419 {
420 	printf("  -D <tty device> : for example, /dev/tty[0-9]\n");
421 }
422 
setup(void)423 static void setup(void)
424 {
425 	int fd;
426 	struct sigaction act;
427 
428 	/* XXX: TERRNO required all over the place */
429 	fd = SAFE_OPEN(NULL, devname, O_RDWR, 0777);
430 
431 	/* Save the current device information - to be restored in cleanup() */
432 	SAFE_IOCTL(cleanup, fd, TCGETA, &save_io);
433 
434 	/* Close the device */
435 	SAFE_CLOSE(cleanup, fd);
436 
437 	/* Set up the signal handlers */
438 	act.sa_handler = (void *)sigterm_handler;
439 	act.sa_flags = 0;
440 	sigemptyset(&act.sa_mask);
441 	(void)sigaction(SIGTERM, &act, 0);
442 
443 	act.sa_handler = (void *)sigusr1_handler;
444 	act.sa_flags = 0;
445 	(void)sigaction(SIGUSR1, &act, 0);
446 
447 	act.sa_handler = (void *)sigusr2_handler;
448 	act.sa_flags = 0;
449 	(void)sigaction(SIGUSR2, &act, 0);
450 
451 	act.sa_handler = SIG_IGN;
452 	act.sa_flags = 0;
453 	(void)sigaction(SIGTTOU, &act, 0);
454 
455 	sigterm = sigusr1 = sigusr2 = 0;
456 
457 	TEST_PAUSE;
458 }
459 
cleanup(void)460 static void cleanup(void)
461 {
462 	if (!closed) {
463 		if (ioctl(parentfd, TCSETA, &save_io) == -1)
464 			tst_resm(TINFO, "ioctl restore failed in cleanup()");
465 		if (close(parentfd) == -1)
466 			tst_resm(TINFO, "close() failed in cleanup()");
467 	}
468 }
469