test_shutdown_select.c (10456B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2016 Karlson2k (Evgeny Grin) 4 5 libmicrohttpd is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libmicrohttpd is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libmicrohttpd; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 21 /** 22 * @file microhttpd/test_shutdown_select.c 23 * @brief Test whether shutdown socket triggers select()/poll() 24 * @details On some platforms shutting down the socket in one thread 25 * triggers select() or poll() waiting for this socket in 26 * other thread. libmicrohttpd depends on this behavior on 27 * these platforms. This program check whether select() 28 * and poll() (if available) work as expected. 29 * @author Karlson2k (Evgeny Grin) 30 */ 31 32 #include "MHD_config.h" 33 #include "platform.h" 34 #include "mhd_sockets.h" 35 #include <stdlib.h> 36 #include <stdio.h> 37 #include <errno.h> 38 39 #ifdef HAVE_UNISTD_H 40 #include <unistd.h> 41 #endif /* HAVE_UNISTD_H */ 42 43 #ifdef HAVE_TIME_H 44 #include <time.h> 45 #endif /* HAVE_TIME_H */ 46 47 #if defined(MHD_USE_POSIX_THREADS) 48 #include <pthread.h> 49 #endif /* MHD_USE_POSIX_THREADS */ 50 51 #if defined(MHD_WINSOCK_SOCKETS) 52 #include <winsock2.h> 53 #include <windows.h> 54 #define sock_errno (WSAGetLastError ()) 55 #elif defined(MHD_POSIX_SOCKETS) 56 #ifdef HAVE_SYS_TYPES_H 57 #include <sys/types.h> 58 #endif /* HAVE_SYS_TYPES_H */ 59 #ifdef HAVE_SYS_SOCKET_H 60 #include <sys/socket.h> 61 #endif /* HAVE_SYS_SOCKET_H */ 62 #ifdef HAVE_NETINET_IN_H 63 #include <netinet/in.h> 64 #endif /* HAVE_NETINET_IN_H */ 65 #ifdef HAVE_ARPA_INET_H 66 #include <arpa/inet.h> 67 #endif /* HAVE_ARPA_INET_H */ 68 #ifdef HAVE_SYS_SELECT_H 69 #include <sys/select.h> 70 #endif /* HAVE_SYS_SELECT_H */ 71 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) 72 #include <poll.h> 73 #endif /* HAVE_POLL && HAVE_POLL_H */ 74 #define sock_errno (errno) 75 #endif /* MHD_POSIX_SOCKETS */ 76 77 #ifdef HAVE_STDBOOL_H 78 #include <stdbool.h> 79 #endif /* HAVE_STDBOOL_H */ 80 81 #include "mhd_threads.h" 82 83 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 84 test into a marked, classifiable test error (TESTING.md, P5). */ 85 #include "mhd_panic_tripwire.h" 86 87 #ifndef SOMAXCONN 88 #define SOMAXCONN 511 89 #endif /* ! SOMAXCONN */ 90 91 #if ! defined(SHUT_RDWR) && defined(SD_BOTH) 92 #define SHUT_RDWR SD_BOTH 93 #endif 94 95 static bool check_err; 96 97 98 static bool 99 has_in_name (const char *prog_name, const char *marker) 100 { 101 size_t name_pos; 102 size_t pos; 103 104 if (! prog_name || ! marker) 105 return 0; 106 107 pos = 0; 108 name_pos = 0; 109 while (prog_name[pos]) 110 { 111 if ('/' == prog_name[pos]) 112 name_pos = pos + 1; 113 #if defined(_WIN32) || defined(__CYGWIN__) 114 else if ('\\' == prog_name[pos]) 115 name_pos = pos + 1; 116 #endif /* _WIN32 || __CYGWIN__ */ 117 pos++; 118 } 119 if (name_pos == pos) 120 return true; 121 return strstr (prog_name + name_pos, marker) != NULL; 122 } 123 124 125 static MHD_socket 126 start_socket_listen (int domain) 127 { 128 /* Create sockets similarly to daemon.c */ 129 MHD_socket fd; 130 int cloexec_set; 131 struct sockaddr_in sock_addr; 132 socklen_t addrlen; 133 134 #ifdef MHD_WINSOCK_SOCKETS 135 unsigned long flags = 1; 136 #else /* MHD_POSIX_SOCKETS */ 137 int flags; 138 #endif /* MHD_POSIX_SOCKETS */ 139 140 #if defined(MHD_POSIX_SOCKETS) && defined(SOCK_CLOEXEC) 141 fd = socket (domain, SOCK_STREAM | SOCK_CLOEXEC, 0); 142 cloexec_set = 1; 143 #elif defined(MHD_WINSOCK_SOCKETS) && defined(WSA_FLAG_NO_HANDLE_INHERIT) 144 fd = WSASocketW (domain, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT); 145 cloexec_set = 1; 146 #else /* !SOCK_CLOEXEC */ 147 fd = socket (domain, SOCK_STREAM, 0); 148 cloexec_set = 0; 149 #endif /* !SOCK_CLOEXEC */ 150 if ( (MHD_INVALID_SOCKET == fd) && (cloexec_set) ) 151 { 152 fd = socket (domain, SOCK_STREAM, 0); 153 cloexec_set = 0; 154 } 155 if (MHD_INVALID_SOCKET == fd) 156 { 157 fprintf (stderr, "Can't create socket: %u\n", 158 (unsigned) sock_errno); 159 return MHD_INVALID_SOCKET; 160 } 161 162 if (! cloexec_set) 163 { 164 #ifdef MHD_WINSOCK_SOCKETS 165 if (! SetHandleInformation ((HANDLE) fd, HANDLE_FLAG_INHERIT, 0)) 166 fprintf (stderr, "Failed to make socket non-inheritable: %u\n", 167 (unsigned int) GetLastError ()); 168 #else /* MHD_POSIX_SOCKETS */ 169 flags = fcntl (fd, F_GETFD); 170 if ( ( (-1 == flags) || 171 ( (flags != (flags | FD_CLOEXEC)) && 172 (0 != fcntl (fd, F_SETFD, flags | FD_CLOEXEC)) ) ) ) 173 fprintf (stderr, "Failed to make socket non-inheritable: %s\n", 174 MHD_socket_last_strerr_ ()); 175 #endif /* MHD_POSIX_SOCKETS */ 176 } 177 178 memset (&sock_addr, 0, sizeof (struct sockaddr_in)); 179 sock_addr.sin_family = AF_INET; 180 sock_addr.sin_port = htons (0); 181 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN 182 sock_addr.sin_len = sizeof (struct sockaddr_in); 183 #endif 184 addrlen = sizeof (struct sockaddr_in); 185 186 if (bind (fd, (const struct sockaddr *) &sock_addr, addrlen) < 0) 187 { 188 fprintf (stderr, "Failed to bind socket: %u\n", 189 (unsigned) sock_errno); 190 MHD_socket_close_chk_ (fd); 191 return MHD_INVALID_SOCKET; 192 } 193 194 #ifdef MHD_WINSOCK_SOCKETS 195 if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) 196 { 197 fprintf (stderr, "Failed to make socket non-blocking: %u\n", 198 (unsigned) sock_errno); 199 MHD_socket_close_chk_ (fd); 200 return MHD_INVALID_SOCKET; 201 } 202 #else /* MHD_POSIX_SOCKETS */ 203 flags = fcntl (fd, F_GETFL); 204 if ( ( (-1 == flags) || 205 ( (flags != (flags | O_NONBLOCK)) && 206 (0 != fcntl (fd, F_SETFL, flags | O_NONBLOCK)) ) ) ) 207 { 208 fprintf (stderr, "Failed to make socket non-blocking: %s\n", 209 MHD_socket_last_strerr_ ()); 210 MHD_socket_close_chk_ (fd); 211 return MHD_INVALID_SOCKET; 212 } 213 #endif /* MHD_POSIX_SOCKETS */ 214 215 if (listen (fd, SOMAXCONN) < 0) 216 { 217 fprintf (stderr, "Failed to listen on socket: %u\n", 218 (unsigned) sock_errno); 219 MHD_socket_close_chk_ (fd); 220 return MHD_INVALID_SOCKET; 221 } 222 223 return fd; 224 } 225 226 227 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ 228 select_thread (void *data) 229 { 230 /* use select() like in daemon.c */ 231 MHD_socket listen_sock = *((MHD_socket *) data); 232 fd_set rs, ws; 233 struct timeval timeout; 234 235 FD_ZERO (&rs); 236 FD_ZERO (&ws); 237 FD_SET (listen_sock, &rs); 238 timeout.tv_usec = 0; 239 timeout.tv_sec = 7; 240 241 check_err = (0 > MHD_SYS_select_ (listen_sock + 1, &rs, &ws, NULL, &timeout)); 242 243 return (MHD_THRD_RTRN_TYPE_) 0; 244 } 245 246 247 #ifdef HAVE_POLL 248 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ 249 poll_thread (void *data) 250 { 251 /* use poll() like in daemon.c */ 252 struct pollfd p[1]; 253 MHD_socket listen_sock = *((MHD_socket *) data); 254 255 p[0].fd = listen_sock; 256 p[0].events = POLLIN; 257 p[0].revents = 0; 258 259 check_err = (0 > MHD_sys_poll_ (p, 1, 7000)); 260 261 return (MHD_THRD_RTRN_TYPE_) 0; 262 } 263 264 265 #endif /* HAVE_POLL */ 266 267 268 static void 269 local_sleep (unsigned seconds) 270 { 271 #if defined(_WIN32) && ! defined(__CYGWIN__) 272 Sleep (seconds * 1000); 273 #else 274 unsigned seconds_left = seconds; 275 do 276 { 277 seconds_left = sleep (seconds_left); 278 } while (seconds_left > 0); 279 #endif 280 } 281 282 283 int 284 main (int argc, char *const *argv) 285 { 286 int i; 287 time_t start_t, end_t; 288 int result = 0; 289 MHD_THRD_RTRN_TYPE_ (MHD_THRD_CALL_SPEC_ * test_func)(void *data); 290 #ifdef MHD_WINSOCK_SOCKETS 291 WORD ver_req; 292 WSADATA wsa_data; 293 int err; 294 #endif /* MHD_WINSOCK_SOCKETS */ 295 bool test_poll; 296 bool must_ignore; 297 (void) argc; /* Unused. Silent compiler warning. */ 298 299 test_poll = has_in_name (argv[0], "_poll"); 300 must_ignore = has_in_name (argv[0], "_ignore"); 301 if (! test_poll) 302 test_func = &select_thread; 303 else 304 { 305 #ifndef HAVE_POLL 306 return 77; 307 #else /* ! HAVE_POLL */ 308 test_func = &poll_thread; 309 #endif /* ! HAVE_POLL */ 310 } 311 312 #ifdef MHD_WINSOCK_SOCKETS 313 ver_req = MAKEWORD (2, 2); 314 315 err = WSAStartup (ver_req, &wsa_data); 316 if ((err != 0) || (MAKEWORD (2, 2) != wsa_data.wVersion)) 317 { 318 printf ("WSAStartup() failed\n"); 319 WSACleanup (); 320 return 99; 321 } 322 #endif /* MHD_WINSOCK_SOCKETS */ 323 324 /* try several times to ensure that accidental incoming connection 325 * didn't interfere with test results 326 */ 327 for (i = 0; i < 5 && result == 0; i++) 328 { 329 MHD_thread_handle_native_ sel_thrd; 330 /* fprintf(stdout, "Creating, binding and listening socket...\n"); */ 331 MHD_socket listen_socket = start_socket_listen (AF_INET); 332 if (MHD_INVALID_SOCKET == listen_socket) 333 return 99; 334 335 check_err = true; 336 /* fprintf (stdout, "Starting select() thread...\n"); */ 337 #if defined(MHD_USE_POSIX_THREADS) 338 if (0 != pthread_create (&sel_thrd, NULL, test_func, &listen_socket)) 339 { 340 MHD_socket_close_chk_ (listen_socket); 341 fprintf (stderr, "Can't start thread\n"); 342 return 99; 343 } 344 #elif defined(MHD_USE_W32_THREADS) 345 sel_thrd = (HANDLE) (uintptr_t) 346 _beginthreadex (NULL, 0, test_func, &listen_socket, 0, NULL); 347 if (0 == (sel_thrd)) 348 { 349 MHD_socket_close_chk_ (listen_socket); 350 fprintf (stderr, "Can't start select() thread\n"); 351 return 99; 352 } 353 #else 354 #error No threading lib available 355 #endif 356 /* fprintf (stdout, "Waiting...\n"); */ 357 local_sleep (1); /* make sure that select() is started */ 358 359 /* fprintf (stdout, "Shutting down socket...\n"); */ 360 start_t = time (NULL); 361 shutdown (listen_socket, SHUT_RDWR); 362 363 /* fprintf (stdout, "Waiting for thread to finish...\n"); */ 364 if (! MHD_join_thread_ (sel_thrd)) 365 { 366 MHD_socket_close_chk_ (listen_socket); 367 fprintf (stderr, "Can't join select() thread\n"); 368 return 99; 369 } 370 if (check_err) 371 { 372 MHD_socket_close_chk_ (listen_socket); 373 fprintf (stderr, "Error in waiting thread\n"); 374 return 99; 375 } 376 end_t = time (NULL); 377 /* fprintf (stdout, "Thread finished.\n"); */ 378 MHD_socket_close_chk_ (listen_socket); 379 380 if ((start_t == (time_t) -1) || (end_t == (time_t) -1) ) 381 { 382 MHD_socket_close_chk_ (listen_socket); 383 fprintf (stderr, "Can't get current time\n"); 384 return 99; 385 } 386 if (end_t - start_t > 3) 387 result++; 388 } 389 390 #ifdef MHD_WINSOCK_SOCKETS 391 WSACleanup (); 392 #endif /* MHD_WINSOCK_SOCKETS */ 393 394 return must_ignore ? (! result) : (result); 395 }