PageRenderTime 21ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/test/t40e.c

http://www.minix3.org/
C | 432 lines | 248 code | 62 blank | 122 comment | 71 complexity | 18b6f3b3f3f9780c50cca61dca748cc1 MD5 | raw file
Possible License(s): MIT, WTFPL, AGPL-1.0, BSD-3-Clause, GPL-3.0, LGPL-2.0, JSON, 0BSD
  1. /* t40e.c
  2. *
  3. * Test sockets
  4. *
  5. * Select works on regular files, (pseudo) terminal devices, streams-based
  6. * files, FIFOs, pipes, and sockets. This test verifies selecting for sockets.
  7. *
  8. * This test is part of a bigger select test. It expects as argument which sub-
  9. * test it is.
  10. *
  11. * Specific rules for sockets:
  12. * If a socket has a pending error, it shall be considered to have an
  13. * exceptional condition pending. Otherwise, what constitutes an exceptional
  14. * condition is file type-specific. For a file descriptor for use with a
  15. * socket, it is protocol-specific except as noted below. For other file types
  16. * it is implementation-defined. If the operation is meaningless for a
  17. * particular file type, pselect() or select() shall indicate that the
  18. * descriptor is ready for read or write operations, and shall indicate that
  19. * the descriptor has no exceptional condition pending.
  20. *
  21. * [1] If a descriptor refers to a socket, the implied input function is the
  22. * recvmsg()function with parameters requesting normal and ancillary data, such
  23. * that the presence of either type shall cause the socket to be marked as
  24. * readable. The presence of out-of-band data shall be checked if the socket
  25. * option SO_OOBINLINE has been enabled, as out-of-band data is enqueued with
  26. * normal data. If the socket is currently listening, then it shall be marked
  27. * as readable if an incoming connection request has been received, and a call
  28. * to the accept() function shall complete without blocking.
  29. *
  30. * [2] If a descriptor refers to a socket, the implied output function is the
  31. * sendmsg() function supplying an amount of normal data equal to the current
  32. * value of the SO_SNDLOWAT option for the socket. If a non-blocking call to
  33. * the connect() function has been made for a socket, and the connection
  34. * attempt has either succeeded or failed leaving a pending error, the socket
  35. * shall be marked as writable.
  36. *
  37. * [3] A socket shall be considered to have an exceptional condition pending if
  38. * a receive operation with O_NONBLOCK clear for the open file description and
  39. * with the MSG_OOB flag set would return out-of-band data without blocking.
  40. * (It is protocol-specific whether the MSG_OOB flag would be used to read
  41. * out-of-band data.) A socket shall also be considered to have an exceptional
  42. * condition pending if an out-of-band data mark is present in the receive
  43. * queue. Other circumstances under which a socket may be considered to have an
  44. * exceptional condition pending are protocol-specific and
  45. * implementation-defined.
  46. */
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <unistd.h>
  50. #include <sys/types.h>
  51. #include <sys/stat.h>
  52. #include <sys/wait.h>
  53. #include <sys/select.h>
  54. #include <sys/socket.h>
  55. #include <netinet/in.h>
  56. #include <arpa/inet.h>
  57. #include <fcntl.h>
  58. #include <errno.h>
  59. #include <string.h>
  60. #include <time.h>
  61. #include <assert.h>
  62. #include <netdb.h>
  63. #define DO_HANDLEDATA 1
  64. #define DO_PAUSE 3
  65. #define DO_TIMEOUT 7
  66. #define MYPORT 3490
  67. #define NUMCHILDREN 5
  68. #define MAX_ERROR 10
  69. int errct = 0, subtest = -1;
  70. char errbuf[1000];
  71. void e(int n, char *s) {
  72. printf("Subtest %d, error %d, %s\n", subtest, n, s);
  73. if (errct++ > MAX_ERROR) {
  74. printf("Too many errors; test aborted\n");
  75. exit(errct);
  76. }
  77. }
  78. /* All *_fds routines are helping routines. They intentionally use FD_* macros
  79. in order to prevent making assumptions on how the macros are implemented.*/
  80. int count_fds(int nfds, fd_set *fds) {
  81. /* Return number of bits set in fds */
  82. int i, result = 0;
  83. assert(fds != NULL && nfds > 0);
  84. for(i = 0; i < nfds; i++) {
  85. if(FD_ISSET(i, fds)) result++;
  86. }
  87. return result;
  88. }
  89. int empty_fds(int nfds, fd_set *fds) {
  90. /* Returns nonzero if the first bits up to nfds in fds are not set */
  91. int i;
  92. assert(fds != NULL && nfds > 0);
  93. for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
  94. return 1;
  95. }
  96. int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
  97. /* Returns nonzero if lh equals rh up to nfds bits */
  98. int i;
  99. assert(lh != NULL && rh != NULL && nfds > 0);
  100. for(i = 0; i < nfds; i++) {
  101. if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
  102. (!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
  103. return 0;
  104. }
  105. }
  106. return 1;
  107. }
  108. void dump_fds(int nfds, fd_set *fds) {
  109. /* Print a graphical representation of bits in fds */
  110. int i;
  111. if(fds != NULL && nfds > 0) {
  112. for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
  113. printf("\n");
  114. }
  115. }
  116. void do_child(int childno) {
  117. int fd_sock, port;
  118. int retval;
  119. fd_set fds_read, fds_write, fds_error;
  120. fd_set fds_compare_write;
  121. struct hostent *he;
  122. struct sockaddr_in server;
  123. struct timeval tv;
  124. if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
  125. perror("Error getting socket\n");
  126. exit(-1);
  127. }
  128. if((he = gethostbyname("127.0.0.1")) == NULL){/*"localhost" might be unknown*/
  129. perror("Error resolving");
  130. exit(-1);
  131. }
  132. /* Child 4 connects to the wrong port. See Actual testing description below.*/
  133. port = (childno == 3 ? MYPORT + 1 : MYPORT);
  134. memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
  135. server.sin_family = AF_INET;
  136. server.sin_port = htons(port);
  137. #if 0
  138. printf("Going to connect to: %s:%d\n", inet_ntoa(server.sin_addr),
  139. ntohs(server.sin_port));
  140. #endif
  141. /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
  142. #ifndef _MINIX
  143. memset(server.sin_zero, '\0', sizeof server.sin_zero);
  144. #endif
  145. /* Wait for parent to set up connection */
  146. tv.tv_sec = (childno <= 1 ? DO_PAUSE : DO_TIMEOUT);
  147. tv.tv_usec = 0;
  148. retval = select(0, NULL, NULL, NULL, &tv);
  149. /* All set, let's do some testing */
  150. /* Children 3 and 4 do a non-blocking connect */
  151. if(childno == 2 || childno == 3)
  152. fcntl(fd_sock, F_SETFL, fcntl(fd_sock, F_GETFL, 0) | O_NONBLOCK);
  153. if(connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
  154. /* Well, we don't actually care. The connect is non-blocking and is
  155. supposed to "in progress" at this point. */
  156. }
  157. if(childno == 2 || childno == 3) { /* Children 3 and 4 */
  158. /* Open Group: "If a non-blocking call to the connect() function has been
  159. made for a socket, and the connection attempt has either succeeded or
  160. failed leaving a pending error, the socket shall be marked as writable.
  161. ...
  162. A socket shall be considered to have an exceptional condition pending if
  163. a receive operation with O_NONBLOCK clear for the open file description
  164. and with the MSG_OOB flag set would return out-of-band data without
  165. blocking. (It is protocol-specific whether the MSG_OOB flag would be used
  166. to read out-of-band data.) A socket shall also be considered to have an
  167. exceptional condition pending if an out-of-band data mark is present in
  168. the receive queue. Other circumstances under which a socket may be
  169. considered to have an exceptional condition pending are protocol-specific
  170. and implementation-defined."
  171. In other words, it only makes sense for us to check the write set as the
  172. read set is not expected to be set, but is allowed to be set (i.e.,
  173. unspecified) and whether the error set is set is implementation-defined.
  174. */
  175. FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
  176. FD_SET(fd_sock, &fds_write);
  177. tv.tv_sec = DO_TIMEOUT;
  178. tv.tv_usec = 0;
  179. retval = select(fd_sock+1, NULL, &fds_write, NULL, &tv);
  180. if(retval <= 0) e(6, "expected one fd to be ready");
  181. FD_ZERO(&fds_compare_write); FD_SET(fd_sock, &fds_compare_write);
  182. if(!compare_fds(fd_sock+1, &fds_compare_write, &fds_compare_write))
  183. e(7, "write should be set");
  184. }
  185. if(close(fd_sock) < 0) {
  186. perror("Error disconnecting");
  187. exit(-1);
  188. }
  189. exit(errct);
  190. }
  191. void do_parent(void) {
  192. #ifndef _MINIX
  193. int yes = 1;
  194. #endif
  195. int fd_sock, fd_new, exitstatus;
  196. int sockets[NUMCHILDREN], i;
  197. fd_set fds_read, fds_write, fds_error;
  198. fd_set fds_compare_read, fds_compare_write;
  199. struct timeval tv;
  200. int retval, childresults = 0;
  201. struct sockaddr_in my_addr;
  202. struct sockaddr_in other_addr;
  203. socklen_t other_size;
  204. if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
  205. perror("Error getting socket\n");
  206. exit(-1);
  207. }
  208. my_addr.sin_family = AF_INET;
  209. my_addr.sin_port = htons(MYPORT); /* Short, network byte order */
  210. my_addr.sin_addr.s_addr = INADDR_ANY;
  211. /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
  212. #ifndef _MINIX
  213. memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
  214. #endif
  215. /* Reuse port number. Not implemented in Minix. */
  216. #ifndef _MINIX
  217. if(setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
  218. perror("Error setting port reuse option");
  219. exit(-1);
  220. }
  221. #endif
  222. /* Bind to port */
  223. if(bind(fd_sock, (struct sockaddr *) &my_addr, sizeof my_addr) < 0) {
  224. perror("Error binding to port");
  225. exit(-1);
  226. }
  227. /* Mark socket to be used for incoming connections */
  228. if(listen(fd_sock, 20) < 0) {
  229. perror("Listen");
  230. exit(-1);
  231. }
  232. /* Actual testing */
  233. /* While sockets resemble file descriptors, they are not the same at all.
  234. We can read/write from/to and close file descriptors, but we cannot open
  235. them O_RDONLY or O_WRONLY; they are always O_RDWR (other flags do not make
  236. sense regarding sockets). As such, we cannot provide wrong file descriptors
  237. to select, except for descriptors that are not in use.
  238. We will test standard behavior and what is described in [2]. [1] and [3]
  239. are not possible to test on Minix, as Minix does not support OOB data. That
  240. is, the TCP layer can handle it, but there is no socket interface for it.
  241. Our test consists of waiting for input from the first two children and
  242. waiting to write output [standard usage]. Then the first child closes its
  243. connection we select for reading. This should fail with error set. Then we
  244. close child number two on our side and select for reading. This should fail
  245. with EBADF. Child number three shall then do a non-blocking connect (after
  246. waiting for DO_PAUSE seconds) and do a select, resulting in being marked
  247. ready for writing. Subsequently child number four also does a non-blocking
  248. connect to loclhost on MYPORT+1 (causing the connect to fail) and then does
  249. a select. This should result in write and error being set (error because of
  250. pending error).
  251. */
  252. /* Accept and store connections from the first two children */
  253. other_size = sizeof(other_addr);
  254. for(i = 0; i < 2; i++) {
  255. fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
  256. if(fd_new < 0) break;
  257. sockets[i] = fd_new;
  258. }
  259. /* If we break out of the for loop, we ran across an error and want to exit.
  260. Check whether we broke out. */
  261. if(fd_new < 0) {
  262. perror("Error accepting connection");
  263. exit(-1);
  264. }
  265. /* Select error condition checking */
  266. for(childresults = 0; childresults < 2; childresults++) {
  267. FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
  268. FD_SET(sockets[childresults], &fds_read);
  269. FD_SET(sockets[childresults], &fds_write);
  270. FD_SET(sockets[childresults], &fds_error);
  271. tv.tv_sec = DO_TIMEOUT;
  272. tv.tv_usec = 0;
  273. retval = select(sockets[childresults]+1, &fds_read, &fds_write, &fds_error,
  274. &tv);
  275. if(retval <= 0) {
  276. snprintf(errbuf, sizeof(errbuf),
  277. "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
  278. e(1, errbuf);
  279. }
  280. FD_ZERO(&fds_compare_read); FD_ZERO(&fds_compare_write);
  281. FD_SET(sockets[childresults], &fds_compare_write);
  282. /* We can't say much about being ready for reading at this point or not. It
  283. is not specified and the other side might have data ready for us to read
  284. */
  285. if(!compare_fds(sockets[childresults]+1, &fds_compare_write, &fds_write))
  286. e(2, "write should be set");
  287. if(!empty_fds(sockets[childresults]+1, &fds_error))
  288. e(3, "no error should be set");
  289. }
  290. /* We continue by accepting a connection of child 3 */
  291. fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
  292. if(fd_new < 0) {
  293. perror("Error accepting connection\n");
  294. exit(-1);
  295. }
  296. sockets[2] = fd_new;
  297. /* Child 4 will never connect */
  298. /* Child 5 is still pending to be accepted. Open Group: "If the socket is
  299. currently listening, then it shall be marked as readable if an incoming
  300. connection request has been received, and a call to the accept() function
  301. shall complete without blocking."*/
  302. FD_ZERO(&fds_read);
  303. FD_SET(fd_sock, &fds_read);
  304. tv.tv_sec = DO_TIMEOUT;
  305. tv.tv_usec = 0;
  306. retval = select(fd_sock+1, &fds_read, NULL, NULL, &tv);
  307. if(retval <= 0) {
  308. snprintf(errbuf, sizeof(errbuf),
  309. "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
  310. e(4, errbuf);
  311. }
  312. /* Check read bit is set */
  313. FD_ZERO(&fds_compare_read); FD_SET(fd_sock, &fds_compare_read);
  314. if(!compare_fds(fd_sock+1, &fds_compare_read, &fds_read))
  315. e(5, "read should be set");
  316. /* Accept incoming connection to unblock child 5 */
  317. fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
  318. if(fd_new < 0) {
  319. perror("Error accepting connection\n");
  320. exit(-1);
  321. }
  322. sockets[4] = fd_new;
  323. /* We're done, let's wait a second to synchronize children and parent. */
  324. tv.tv_sec = DO_HANDLEDATA;
  325. tv.tv_usec = 0;
  326. select(0, NULL, NULL, NULL, &tv);
  327. /* Close connection with children. */
  328. for(i = 0; i < NUMCHILDREN; i++) {
  329. if(i == 3) /* No need to disconnect child 4 that failed to connect. */
  330. continue;
  331. if(close(sockets[i]) < 0) {
  332. perror(NULL);
  333. }
  334. }
  335. /* Close listening socket */
  336. if(close(fd_sock) < 0) {
  337. perror("Closing listening socket");
  338. errct++;
  339. }
  340. for(i = 0; i < NUMCHILDREN; i++) {
  341. wait(&exitstatus); /* Wait for children */
  342. if(exitstatus > 0)
  343. errct += WEXITSTATUS(exitstatus); /* and count their errors, too. */
  344. }
  345. exit(errct);
  346. }
  347. int main(int argc, char **argv) {
  348. int forkres, i;
  349. /* Get subtest number */
  350. if(argc != 2) {
  351. printf("Usage: %s subtest_no\n", argv[0]);
  352. exit(-2);
  353. } else if(sscanf(argv[1], "%d", &subtest) != 1) {
  354. printf("Usage: %s subtest_no\n", argv[0]);
  355. exit(-2);
  356. }
  357. /* Fork off a bunch of children */
  358. for(i = 0; i < NUMCHILDREN; i++) {
  359. forkres = fork();
  360. if(forkres == 0) do_child(i);
  361. else if(forkres < 0) {
  362. perror("Unable to fork");
  363. exit(-1);
  364. }
  365. }
  366. /* do_child always calls exit(), so when we end up here, we're the parent. */
  367. do_parent();
  368. exit(-2); /* We're not supposed to get here. Both do_* routines should exit.*/
  369. }