PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

test/t40d.c

http://www.minix3.org/
C | 428 lines | 263 code | 69 blank | 96 comment | 73 complexity | 131ea45d3ae3755417013dfde93da38d MD5 | raw file
Possible License(s): MIT, WTFPL, AGPL-1.0, BSD-3-Clause, GPL-3.0, LGPL-2.0, JSON, 0BSD
  1. /* t40d.c
  2. *
  3. * Test FIFOs and pipes
  4. *
  5. * Select works on regular files, (pseudo) terminal devices, streams-based
  6. * files, FIFOs, pipes, and sockets. This test verifies selecting for FIFOs
  7. * (named pipes) and pipes (anonymous pipes). This test will not verify most
  8. * error file descriptors, as the setting of this fdset in the face of an error
  9. * condition is implementation-specific (except for regular files (alway set)
  10. * and sockets (protocol-specific or OOB data received), but those file types
  11. * are not being tested in this specific test).
  12. *
  13. * This test is part of a bigger select test. It expects as argument which sub-
  14. * test it is.
  15. *
  16. * [1] If a socket has a pending error, it shall be considered to have an
  17. * exceptional condition pending. Otherwise, what constitutes an exceptional
  18. * condition is file type-specific. For a file descriptor for use with a
  19. * socket, it is protocol-specific except as noted below. For other file types
  20. * it is implementation-defined. If the operation is meaningless for a
  21. * particular file type, pselect() or select() shall indicate that the
  22. * descriptor is ready for read or write operations, and shall indicate that
  23. * the descriptor has no exceptional condition pending.
  24. */
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <unistd.h>
  28. #include <sys/types.h>
  29. #include <sys/stat.h>
  30. #include <fcntl.h>
  31. #include <sys/select.h>
  32. #include <errno.h>
  33. #include <sys/wait.h>
  34. #include <string.h>
  35. #include <time.h>
  36. #include <assert.h>
  37. #define NAMEDPIPE1 "selecttestd-1"
  38. #define NAMEDPIPE2 "selecttestd-2"
  39. #define SENDSTRING "minixrocks"
  40. #define DO_HANDLEDATA 1
  41. #define DO_PAUSE 3
  42. #define DO_TIMEOUT 7
  43. #define MAX_ERROR 5
  44. int errct = 0, subtest = -1;
  45. char errbuf[1000];
  46. int fd_ap[2]; /* Anonymous pipe; read from fd_ap[0], write to fd_ap[1] */
  47. int fd_np1; /* Named pipe */
  48. int fd_np2; /* Named pipe */
  49. void e(int n, char *s) {
  50. printf("Subtest %d, error %d, %s\n", subtest, n, s);
  51. if (errct++ > MAX_ERROR) {
  52. printf("Too many errors; test aborted\n");
  53. exit(errct);
  54. }
  55. }
  56. void do_child(void) {
  57. struct timeval tv;
  58. int retval;
  59. /* Open named pipe for writing. This will block until a reader arrives. */
  60. if((fd_np1 = open(NAMEDPIPE1, O_WRONLY)) == -1) {
  61. printf("Error opening %s for writing, signalling parent to quit\n",
  62. NAMEDPIPE1);
  63. perror(NULL);
  64. printf("Please make sure that %s is not in use while running this test\n",
  65. NAMEDPIPE1);
  66. exit(-1);
  67. }
  68. /* Going to sleep for three seconds to allow the parent proc to get ready */
  69. tv.tv_sec = DO_HANDLEDATA;
  70. tv.tv_usec = 0;
  71. select(0, NULL, NULL, NULL, &tv);
  72. /* Try to write. Doesn't matter how many bytes we actually send. */
  73. retval = write(fd_np1, SENDSTRING, strlen(SENDSTRING));
  74. /* Wait for another second to allow the parent to process incoming data */
  75. tv.tv_sec = DO_HANDLEDATA;
  76. tv.tv_usec = 0;
  77. retval = select(0,NULL, NULL, NULL, &tv);
  78. close(fd_np1);
  79. /* Wait for another second to allow the parent to process incoming data */
  80. tv.tv_sec = DO_HANDLEDATA;
  81. tv.tv_usec = 0;
  82. retval = select(0,NULL, NULL, NULL, &tv);
  83. /* Open named pipe for reading. This will block until a writer arrives. */
  84. if((fd_np2 = open(NAMEDPIPE2, O_RDONLY)) == -1) {
  85. printf("Error opening %s for reading, signalling parent to quit\n",
  86. NAMEDPIPE2);
  87. perror(NULL);
  88. printf("Please make sure that %s is not in use while running this test\n",
  89. NAMEDPIPE2);
  90. exit(-1);
  91. }
  92. /* Wait for another second to allow the parent to run some tests. */
  93. tv.tv_sec = DO_HANDLEDATA;
  94. tv.tv_usec = 0;
  95. retval = select(0, NULL, NULL, NULL, &tv);
  96. close(fd_np2);
  97. /* Anonymous pipe */
  98. /* Let the parent do initial read and write tests from and to the pipe. */
  99. tv.tv_sec = DO_PAUSE;
  100. tv.tv_usec = 0;
  101. retval = select(0, NULL, NULL, NULL, &tv);
  102. /* Unblock blocking read select by writing data */
  103. if(write(fd_ap[1], SENDSTRING, strlen(SENDSTRING)) < 0) {
  104. perror("Could not write to anonymous pipe");
  105. exit(-1);
  106. }
  107. exit(0);
  108. }
  109. int count_fds(int nfds, fd_set *fds) {
  110. /* Return number of bits set in fds */
  111. int i, result = 0;
  112. assert(fds != NULL && nfds > 0);
  113. for(i = 0; i < nfds; i++) {
  114. if(FD_ISSET(i, fds)) result++;
  115. }
  116. return result;
  117. }
  118. int empty_fds(int nfds, fd_set *fds) {
  119. /* Returns nonzero if the first bits up to nfds in fds are not set */
  120. int i;
  121. assert(fds != NULL && nfds > 0);
  122. for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
  123. return 1;
  124. }
  125. int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
  126. /* Returns nonzero if lh equals rh up to nfds bits */
  127. int i;
  128. assert(lh != NULL && rh != NULL && nfds > 0);
  129. for(i = 0; i < nfds; i++) {
  130. if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
  131. (!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
  132. return 0;
  133. }
  134. }
  135. return 1;
  136. }
  137. void dump_fds(int nfds, fd_set *fds) {
  138. /* Print a graphical representation of bits in fds */
  139. int i;
  140. if(fds != NULL && nfds > 0) {
  141. for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
  142. printf("\n");
  143. }
  144. }
  145. void do_parent(int child) {
  146. fd_set fds_read, fds_write, fds_error;
  147. fd_set fds_compare_read, fds_compare_write;
  148. struct timeval tv;
  149. time_t start, end;
  150. int retval;
  151. char buf[20];
  152. /* Open named pipe for reading. This will block until a writer arrives. */
  153. if((fd_np1 = open(NAMEDPIPE1, O_RDONLY)) == -1) {
  154. printf("Error opening %s for reading\n", NAMEDPIPE1);
  155. perror(NULL);
  156. printf("Please make sure that %s is not in use while running this test.\n",
  157. NAMEDPIPE1);
  158. waitpid(child, &retval, 0);
  159. exit(-1);
  160. }
  161. /* Clear bit masks */
  162. FD_ZERO(&fds_read); FD_ZERO(&fds_write);
  163. /* Set read and write bits */
  164. FD_SET(fd_np1, &fds_read);
  165. FD_SET(fd_np1, &fds_write);
  166. tv.tv_sec = DO_TIMEOUT;
  167. tv.tv_usec = 0;
  168. /* Test if we can read or write from/to fd_np1. As fd_np1 is opened read only
  169. * we cannot actually write, so the select should return immediately [1] and
  170. * the offending bit set in the fd set. We read from a pipe that is opened
  171. * with O_NONBLOCKING cleared, so it is guaranteed we can read.
  172. * However, at this moment the writer is sleeping, so the pipe is empty and
  173. * read is supposed to block. Therefore, only 1 file descriptor should be
  174. * ready. A timeout value is still set in case an error occurs in a faulty
  175. * implementation. */
  176. retval = select(fd_np1+1, &fds_read, &fds_write, NULL, &tv);
  177. /* Did we receive an error? */
  178. if(retval <= 0) {
  179. snprintf(errbuf, sizeof(errbuf),
  180. "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
  181. e(1, errbuf);
  182. }
  183. if(!empty_fds(fd_np1+1,&fds_read)) e(2, "no read bits should be set");
  184. /* Make sure the write bit is set (and just 1 bit) */
  185. FD_ZERO(&fds_compare_write); FD_SET(fd_np1, &fds_compare_write);
  186. if(!compare_fds(fd_np1+1, &fds_compare_write, &fds_write))
  187. e(3, "write should be set");
  188. /* Clear sets and set up new bit masks */
  189. FD_ZERO(&fds_read); FD_ZERO(&fds_write);
  190. FD_SET(fd_np1, &fds_read);
  191. tv.tv_sec = DO_TIMEOUT; /* To make sure we get to see some error messages
  192. instead of blocking forever when the
  193. implementation is faulty. A timeout causes retval
  194. to be 0. */
  195. tv.tv_usec = 0;
  196. /* The sleeping writer is about to wake up and write data to the pipe. */
  197. retval = select(fd_np1+1, &fds_read, &fds_write, NULL, &tv);
  198. /* Correct amount of ready file descriptors? Just 1 read */
  199. if(retval != 1) {
  200. snprintf(errbuf, sizeof(errbuf),
  201. "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
  202. e(4, errbuf);
  203. }
  204. if(!FD_ISSET(fd_np1, &fds_read)) e(5, "read should be set");
  205. /* Note that we left the write set empty. This should be equivalent to
  206. * setting this parameter to NULL. */
  207. if(!empty_fds(fd_np1+1, &fds_write)) e(6, "write should NOT be set");
  208. /* In case something went wrong above, we might end up with a child process
  209. * blocking on a write call we close the file descriptor now. Synchronize on
  210. * a read. */
  211. if(read(fd_np1, buf, sizeof(SENDSTRING)) < 0) perror("Read error");
  212. /* Close file descriptor. We're going to reverse the test */
  213. close(fd_np1);
  214. /* Wait for a second to allow the child to close the pipe as well */
  215. tv.tv_sec = DO_HANDLEDATA;
  216. tv.tv_usec = 0;
  217. retval = select(0,NULL, NULL, NULL, &tv);
  218. /* Open named pipe for writing. This call blocks until a reader arrives. */
  219. if((fd_np2 = open(NAMEDPIPE2, O_WRONLY)) == -1) {
  220. printf("Error opening %s for writing\n",
  221. NAMEDPIPE2);
  222. perror(NULL);
  223. printf("Please make sure that %s is not in use while running this test\n",
  224. NAMEDPIPE2);
  225. exit(-1);
  226. }
  227. /* At this moment the child process has opened the named pipe for reading and
  228. * we have opened it for writing. We're now going to reverse some of the
  229. * tests we've done earlier. */
  230. /* Clear sets and set up bit masks */
  231. FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
  232. FD_SET(fd_np2, &fds_read);
  233. FD_SET(fd_np2, &fds_write);
  234. tv.tv_sec = DO_TIMEOUT;
  235. tv.tv_usec = 0;
  236. /* Select for reading from an fd opened O_WRONLY. This should return
  237. * immediately as it is not a meaningful operation [1] and is therefore not
  238. * blocking. The select should return two file descriptors are ready (the
  239. * failing read and valid write). */
  240. retval = select(fd_np2+1, &fds_read, &fds_write, &fds_error, &tv);
  241. /* Did we receive an error? */
  242. if(retval <= 0) {
  243. snprintf(errbuf, sizeof(errbuf),
  244. "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
  245. e(7, errbuf);
  246. }
  247. /* Make sure read bit is set (and just 1 bit) */
  248. FD_ZERO(&fds_compare_read); FD_SET(fd_np2, &fds_compare_read);
  249. if(!compare_fds(fd_np2+1, &fds_compare_read, &fds_read))
  250. e(8, "read should be set");
  251. /* Write bit should be set (and just 1 bit) */
  252. FD_ZERO(&fds_compare_write); FD_SET(fd_np2, &fds_compare_write);
  253. if(!compare_fds(fd_np2+1, &fds_compare_write, &fds_write))
  254. e(9, "write should be set");
  255. if(!empty_fds(fd_np2+1, &fds_error))
  256. e(10, "Error should NOT be set");
  257. FD_ZERO(&fds_read); FD_ZERO(&fds_write);
  258. FD_SET(fd_np2, &fds_write);
  259. tv.tv_sec = DO_TIMEOUT;
  260. tv.tv_usec = 0;
  261. retval = select(fd_np2+1, &fds_read, &fds_write, NULL, &tv);
  262. /* Correct amount of ready file descriptors? Just 1 write */
  263. if(retval != 1) {
  264. snprintf(errbuf, sizeof(errbuf),
  265. "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
  266. e(11, errbuf);
  267. }
  268. if(!empty_fds(fd_np2+1, &fds_read)) e(12, "read should NOT be set");
  269. /* Anonymous pipe */
  270. /* Check if we can write to the pipe */
  271. FD_ZERO(&fds_read); FD_ZERO(&fds_write);
  272. FD_SET(fd_ap[1], &fds_write);
  273. tv.tv_sec = DO_TIMEOUT;
  274. tv.tv_usec = 0;
  275. retval = select(fd_ap[1]+1, NULL, &fds_write, NULL, &tv);
  276. /* Correct amount of ready file descriptors? Just 1 write */
  277. if(retval != 1) {
  278. snprintf(errbuf, sizeof(errbuf),
  279. "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
  280. e(13, errbuf);
  281. }
  282. /* Make sure write bit is set (and just 1 bit) */
  283. FD_ZERO(&fds_compare_write); FD_SET(fd_ap[1], &fds_compare_write);
  284. if(!compare_fds(fd_ap[1]+1, &fds_compare_write, &fds_write))
  285. e(14, "write should be set");
  286. /* Intentionally test reading from pipe and letting it time out. */
  287. FD_SET(fd_ap[0], &fds_read);
  288. tv.tv_sec = 1;
  289. tv.tv_usec = 0;
  290. start = time(NULL);
  291. retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
  292. end = time(NULL);
  293. /* Did we time out? */
  294. if(retval != 0) e(15, "we should have timed out");
  295. /* Did it take us approximately 1 second? */
  296. if((int) (end - start) != 1) {
  297. snprintf(errbuf, sizeof(errbuf),
  298. "time out is not 1 second (instead, it is %ld)",
  299. (long int) (end - start));
  300. e(16, errbuf);
  301. }
  302. /* Do another read, but this time we expect incoming data from child. */
  303. FD_ZERO(&fds_read);
  304. FD_SET(fd_ap[0], &fds_read);
  305. tv.tv_sec = DO_TIMEOUT;
  306. tv.tv_usec = 0;
  307. retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
  308. /* Correct amount of ready file descriptors? Just 1 read. */
  309. if(retval != 1) e(17, "one fd should be set");
  310. /* Is the read bit set? And just 1 bit. */
  311. FD_ZERO(&fds_compare_read); FD_SET(fd_ap[0], &fds_compare_read);
  312. if(!compare_fds(fd_ap[0]+1, &fds_compare_read, &fds_read))
  313. e(18, "read should be set.");
  314. /* By convention fd_ap[0] is meant to be used for reading from the pipe and
  315. * fd_ap[1] is meant for writing, where fd_ap is a an anonymous pipe.
  316. * However, it is unspecified what happens when fd_ap[0] is used for writing
  317. * and fd_ap[1] for reading. (It is unsupported on Minix.) As such, it is not
  318. * necessary to make test cases for wrong pipe file descriptors using select.
  319. */
  320. waitpid(child, &retval, 0);
  321. unlink(NAMEDPIPE2);
  322. unlink(NAMEDPIPE1);
  323. exit(errct);
  324. }
  325. int main(int argc, char **argv) {
  326. int forkres;
  327. /* Get subtest number */
  328. if(argc != 2) {
  329. printf("Usage: %s subtest_no\n", argv[0]);
  330. exit(-2);
  331. } else if(sscanf(argv[1], "%d", &subtest) != 1) {
  332. printf("Usage: %s subtest_no\n", argv[0]);
  333. exit(-2);
  334. }
  335. /* Set up anonymous pipe */
  336. if(pipe(fd_ap) < 0) {
  337. perror("Could not create anonymous pipe");
  338. exit(-1);
  339. }
  340. /* Create named pipe2. It is unlinked by do_parent. */
  341. if(mkfifo(NAMEDPIPE1, 0600) < 0) {
  342. printf("Could not create named pipe %s", NAMEDPIPE1);
  343. perror(NULL);
  344. exit(-1);
  345. }
  346. if(mkfifo(NAMEDPIPE2, 0600) < 0) {
  347. printf("Could not create named pipe %s", NAMEDPIPE2);
  348. perror(NULL);
  349. exit(-1);
  350. }
  351. forkres = fork();
  352. if(forkres == 0) do_child();
  353. else if(forkres > 0) do_parent(forkres);
  354. else { /* Fork failed */
  355. perror("Unable to fork");
  356. exit(-1);
  357. }
  358. exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
  359. }