1 /* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */ 2 /* This test needs to be run as root; otherwise, openpty() won't work. */ 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <signal.h> 7 #include <termios.h> 8 #include <sys/wait.h> 9 #include <sys/syslimits.h> 10 #include <paths.h> 11 #include <fcntl.h> 12 #include <util.h> 13 14 #define ITERATIONS 10 15 16 #include "common.h" 17 18 static int sighups; /* number of SIGHUP signals received */ 19 20 /* 21 * Signal handler for SIGHUP and SIGUSR1. 22 */ 23 void 24 signal_handler(int sig) 25 { 26 if (sig == SIGHUP) 27 sighups++; 28 } 29 30 /* 31 * Set the slave side of the pseudo terminal to raw mode. This simplifies 32 * testing communication. 33 */ 34 static void 35 make_raw(int slavefd) 36 { 37 struct termios tios; 38 39 if (tcgetattr(slavefd, &tios) < 0) e(100); 40 41 cfmakeraw(&tios); 42 43 if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(101); 44 } 45 46 /* 47 * See if the given pseudo terminal can successfully perform basic 48 * communication between master and slave. 49 */ 50 static void 51 test_comm(int masterfd, int slavefd) 52 { 53 char c; 54 55 make_raw(slavefd); 56 57 c = 'A'; 58 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(200); 59 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(201); 60 if (c != 'A') e(202); 61 62 c = 'B'; 63 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(203); 64 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(204); 65 if (c != 'B') e(205); 66 67 c = 'C'; 68 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(206); 69 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(207); 70 if (c != 'C') e(208); 71 72 c = 'D'; 73 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(209); 74 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(210); 75 if (c != 'D') e(211); 76 } 77 78 /* 79 * Get device node names for the master and slave end of a free pseudo 80 * terminal. We don't want to replicate the entire openpty(3) logic here, so 81 * start by letting openpty(3) do the work for us. We make the assumption that 82 * nobody snatches the pair while we are running. 83 */ 84 static void 85 get_names(char pname[PATH_MAX], char tname[PATH_MAX]) 86 { 87 int len, masterfd, slavefd; 88 89 if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(300); 90 91 /* 92 * openpty(3) gives us only the slave name, but we also need the master 93 * name. 94 */ 95 strlcpy(pname, tname, PATH_MAX); 96 len = strlen(_PATH_DEV); 97 98 if (strncmp(pname, _PATH_DEV, len)) e(301); 99 100 /* If this fails, this test needs to be updated. */ 101 if (pname[len] != 't') e(302); 102 103 pname[len] = 'p'; 104 105 test_comm(masterfd, slavefd); 106 107 if (close(masterfd) < 0) e(303); 108 if (close(slavefd) < 0) e(304); 109 } 110 111 /* 112 * Test various orders of opening and closing the master and slave sides of a 113 * pseudo terminal, as well as opening/closing one side without ever opening 114 * the other. 115 */ 116 static void 117 test77a(void) 118 { 119 struct sigaction act, oact; 120 char pname[PATH_MAX], tname[PATH_MAX]; 121 int masterfd, slavefd; 122 123 subtest = 1; 124 125 /* We do not want to get SIGHUP signals in this test. */ 126 memset(&act, 0, sizeof(act)); 127 act.sa_handler = SIG_IGN; 128 if (sigaction(SIGHUP, &act, &oact) < 0) e(1); 129 130 /* Get master and slave device names for a free pseudo terminal. */ 131 get_names(pname, tname); 132 133 /* Try opening and then closing the master. */ 134 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2); 135 136 if (close(masterfd) < 0) e(3); 137 138 /* Now see if we can reopen the master as well as the slave. */ 139 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4); 140 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(5); 141 142 test_comm(masterfd, slavefd); 143 144 /* In the meantime, test different closing orders. This is order A. */ 145 if (close(slavefd) < 0) e(6); 146 if (close(masterfd) < 0) e(7); 147 148 /* Now try opening the pair again. */ 149 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(8); 150 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9); 151 152 test_comm(masterfd, slavefd); 153 154 if (close(slavefd) < 0) e(10); 155 156 /* 157 * Try reopening the slave after closing it. It is not very important 158 * that this works, but the TTY driver should currently support it. 159 */ 160 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(11); 161 162 test_comm(masterfd, slavefd); 163 164 /* This is closing order B. This may or may not cause a SIGHUP. */ 165 if (close(masterfd) < 0) e(12); 166 if (close(slavefd) < 0) e(13); 167 168 /* Try the normal open procedure. */ 169 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14); 170 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(15); 171 172 test_comm(masterfd, slavefd); 173 174 if (close(slavefd) < 0) e(16); 175 if (close(masterfd) < 0) e(17); 176 177 /* Try reopening and closing the slave, without opening the master. */ 178 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(18); 179 180 if (close(slavefd) < 0) e(19); 181 182 /* Again, try the normal open procedure. */ 183 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(20); 184 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(21); 185 186 test_comm(masterfd, slavefd); 187 188 if (close(slavefd) < 0) e(22); 189 if (close(masterfd) < 0) e(23); 190 191 /* Finally, try opening the slave first. */ 192 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(24); 193 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(25); 194 195 test_comm(masterfd, slavefd); 196 197 if (close(slavefd) < 0) e(26); 198 if (close(masterfd) < 0) e(27); 199 200 if (sigaction(SIGHUP, &oact, NULL) < 0) e(28); 201 } 202 203 /* 204 * Test opening a single side multiple times. 205 */ 206 static void 207 test77b(void) 208 { 209 char pname[PATH_MAX], tname[PATH_MAX]; 210 int masterfd, slavefd, extrafd; 211 212 subtest = 2; 213 214 /* Get master and slave device names for a free pseudo terminal. */ 215 get_names(pname, tname); 216 217 /* It must not be possible to open the master multiple times. */ 218 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1); 219 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(2); 220 221 test_comm(masterfd, slavefd); 222 223 if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(3); 224 if (errno != EIO) e(4); 225 226 test_comm(masterfd, slavefd); 227 228 if (close(slavefd) < 0) e(5); 229 if (close(masterfd) < 0) e(6); 230 231 /* The slave can be opened multiple times, though. */ 232 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(7); 233 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(8); 234 235 test_comm(masterfd, slavefd); 236 237 if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9); 238 239 test_comm(masterfd, extrafd); 240 test_comm(masterfd, slavefd); 241 242 if (close(slavefd) < 0) e(10); 243 if (close(extrafd) < 0) e(11); 244 if (close(masterfd) < 0) e(12); 245 } 246 247 /* 248 * Test communication on half-open pseudo terminals. 249 */ 250 static void 251 test77c(void) 252 { 253 struct sigaction act, oact; 254 char pname[PATH_MAX], tname[PATH_MAX]; 255 int masterfd, slavefd; 256 char c; 257 258 subtest = 3; 259 260 /* We do not want to get SIGHUP signals in this test. */ 261 memset(&act, 0, sizeof(act)); 262 act.sa_handler = SIG_IGN; 263 if (sigaction(SIGHUP, &act, &oact) < 0) e(1); 264 265 /* Get master and slave device names for a free pseudo terminal. */ 266 get_names(pname, tname); 267 268 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2); 269 270 /* Writes to the master should be buffered until there is a slave. */ 271 c = 'E'; 272 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(3); 273 274 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(4); 275 276 make_raw(slavefd); 277 278 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(5); 279 if (c != 'E') e(6); 280 281 /* Discard the echo on the master. */ 282 if (tcflush(slavefd, TCOFLUSH) != 0) e(7); 283 284 test_comm(masterfd, slavefd); 285 286 if (close(slavefd) < 0) e(8); 287 288 /* Writes to the master after the slave has been closed should fail. */ 289 if (write(masterfd, &c, sizeof(c)) >= 0) e(9); 290 if (errno != EIO) e(10); 291 292 if (close(masterfd) < 0) e(11); 293 294 /* Writes to the slave should be buffered until there is a master. */ 295 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(12); 296 297 make_raw(slavefd); 298 299 c = 'F'; 300 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(13); 301 302 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14); 303 304 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(15); 305 if (c != 'F') e(16); 306 307 test_comm(masterfd, slavefd); 308 309 if (close(masterfd) < 0) e(17); 310 311 if (write(slavefd, &c, sizeof(c)) >= 0) e(18); 312 if (errno != EIO) e(19); 313 314 if (close(slavefd) < 0) e(20); 315 316 if (sigaction(SIGHUP, &oact, NULL) < 0) e(21); 317 } 318 319 /* 320 * Test opening the slave side with and without the O_NOCTTY flag. 321 */ 322 static void 323 test77d(void) 324 { 325 char pname[PATH_MAX], tname[PATH_MAX]; 326 int masterfd, slavefd; 327 328 subtest = 4; 329 330 /* Get master and slave device names for a free pseudo terminal. */ 331 get_names(pname, tname); 332 333 /* Make ourselves process group leader if we aren't already. */ 334 (void) setsid(); 335 336 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1); 337 338 /* 339 * Opening the slave with O_NOCTTY should not change its controlling 340 * terminal. 341 */ 342 switch (fork()) { 343 case 0: 344 if (setsid() < 0) e(2); 345 346 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(3); 347 348 if (open("/dev/tty", O_RDWR) >= 0) e(4); 349 if (errno != ENXIO) e(5); 350 351 exit(0); 352 case -1: 353 e(6); 354 default: 355 break; 356 } 357 358 if (wait(NULL) <= 0) e(7); 359 360 if (close(masterfd) < 0) e(8); 361 362 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(9); 363 364 /* 365 * Opening the slave without O_NOCTTY should change its controlling 366 * terminal, though. 367 */ 368 switch (fork()) { 369 case 0: 370 if (setsid() < 0) e(10); 371 372 if ((slavefd = open(tname, O_RDWR)) < 0) e(11); 373 374 if (open("/dev/tty", O_RDWR) < 0) e(12); 375 376 exit(0); 377 case -1: 378 e(13); 379 default: 380 break; 381 } 382 383 if (wait(NULL) <= 0) e(14); 384 385 if (close(masterfd) < 0) e(15); 386 } 387 388 /* 389 * Test receiving of SIGHUP on master hang-up. All of the tests so far have 390 * ignored SIGHUP, and probably would not have received one anyway, since the 391 * process was not its own session leader. Time to test this aspect. 392 */ 393 static void 394 test77e(void) 395 { 396 struct sigaction act, hup_oact, usr_oact; 397 sigset_t set, oset; 398 char pname[PATH_MAX], tname[PATH_MAX]; 399 int masterfd, slavefd; 400 401 subtest = 5; 402 403 /* Get master and slave device names for a free pseudo terminal. */ 404 get_names(pname, tname); 405 406 memset(&act, 0, sizeof(act)); 407 act.sa_handler = signal_handler; 408 if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(1); 409 410 memset(&act, 0, sizeof(act)); 411 act.sa_handler = signal_handler; 412 if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(2); 413 414 sigemptyset(&set); 415 sigaddset(&set, SIGHUP); 416 sigaddset(&set, SIGUSR1); 417 if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(3); 418 419 sighups = 0; 420 421 /* Make ourselves process group leader if we aren't already. */ 422 (void) setsid(); 423 424 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4); 425 426 switch (fork()) { 427 case 0: 428 if (close(masterfd) < 0) e(5); 429 430 /* Become session leader. */ 431 if (setsid() < 0) e(6); 432 433 if ((slavefd = open(tname, O_RDWR)) < 0) e(7); 434 435 /* Tell the parent we are ready. */ 436 kill(getppid(), SIGUSR1); 437 438 /* We should now get a SIGHUP. */ 439 set = oset; 440 if (sigsuspend(&set) >= 0) e(8); 441 442 if (sighups != 1) e(9); 443 444 exit(0); 445 case -1: 446 e(10); 447 default: 448 break; 449 } 450 451 /* Wait for SIGUSR1 from the child. */ 452 set = oset; 453 if (sigsuspend(&set) >= 0) e(11); 454 455 /* Closing the master should now raise a SIGHUP signal in the child. */ 456 if (close(masterfd) < 0) e(12); 457 458 if (wait(NULL) <= 0) e(13); 459 460 if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(14); 461 462 if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(15); 463 if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(16); 464 } 465 466 int 467 main(int argc, char **argv) 468 { 469 int i, m; 470 471 start(77); 472 473 if (argc == 2) 474 m = atoi(argv[1]); 475 else 476 m = 0xFF; 477 478 for (i = 0; i < ITERATIONS; i++) { 479 if (m & 0x01) test77a(); 480 if (m & 0x02) test77b(); 481 if (m & 0x04) test77c(); 482 if (m & 0x08) test77d(); 483 if (m & 0x10) test77e(); 484 } 485 486 quit(); 487 } 488