test_shutdown_select.c (10289B)
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 #ifndef SOMAXCONN 84 #define SOMAXCONN 511 85 #endif /* ! SOMAXCONN */ 86 87 #if ! defined(SHUT_RDWR) && defined(SD_BOTH) 88 #define SHUT_RDWR SD_BOTH 89 #endif 90 91 static bool check_err; 92 93 94 static bool 95 has_in_name (const char *prog_name, const char *marker) 96 { 97 size_t name_pos; 98 size_t pos; 99 100 if (! prog_name || ! marker) 101 return 0; 102 103 pos = 0; 104 name_pos = 0; 105 while (prog_name[pos]) 106 { 107 if ('/' == prog_name[pos]) 108 name_pos = pos + 1; 109 #if defined(_WIN32) || defined(__CYGWIN__) 110 else if ('\\' == prog_name[pos]) 111 name_pos = pos + 1; 112 #endif /* _WIN32 || __CYGWIN__ */ 113 pos++; 114 } 115 if (name_pos == pos) 116 return true; 117 return strstr (prog_name + name_pos, marker) != NULL; 118 } 119 120 121 static MHD_socket 122 start_socket_listen (int domain) 123 { 124 /* Create sockets similarly to daemon.c */ 125 MHD_socket fd; 126 int cloexec_set; 127 struct sockaddr_in sock_addr; 128 socklen_t addrlen; 129 130 #ifdef MHD_WINSOCK_SOCKETS 131 unsigned long flags = 1; 132 #else /* MHD_POSIX_SOCKETS */ 133 int flags; 134 #endif /* MHD_POSIX_SOCKETS */ 135 136 #if defined(MHD_POSIX_SOCKETS) && defined(SOCK_CLOEXEC) 137 fd = socket (domain, SOCK_STREAM | SOCK_CLOEXEC, 0); 138 cloexec_set = 1; 139 #elif defined(MHD_WINSOCK_SOCKETS) && defined(WSA_FLAG_NO_HANDLE_INHERIT) 140 fd = WSASocketW (domain, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT); 141 cloexec_set = 1; 142 #else /* !SOCK_CLOEXEC */ 143 fd = socket (domain, SOCK_STREAM, 0); 144 cloexec_set = 0; 145 #endif /* !SOCK_CLOEXEC */ 146 if ( (MHD_INVALID_SOCKET == fd) && (cloexec_set) ) 147 { 148 fd = socket (domain, SOCK_STREAM, 0); 149 cloexec_set = 0; 150 } 151 if (MHD_INVALID_SOCKET == fd) 152 { 153 fprintf (stderr, "Can't create socket: %u\n", 154 (unsigned) sock_errno); 155 return MHD_INVALID_SOCKET; 156 } 157 158 if (! cloexec_set) 159 { 160 #ifdef MHD_WINSOCK_SOCKETS 161 if (! SetHandleInformation ((HANDLE) fd, HANDLE_FLAG_INHERIT, 0)) 162 fprintf (stderr, "Failed to make socket non-inheritable: %u\n", 163 (unsigned int) GetLastError ()); 164 #else /* MHD_POSIX_SOCKETS */ 165 flags = fcntl (fd, F_GETFD); 166 if ( ( (-1 == flags) || 167 ( (flags != (flags | FD_CLOEXEC)) && 168 (0 != fcntl (fd, F_SETFD, flags | FD_CLOEXEC)) ) ) ) 169 fprintf (stderr, "Failed to make socket non-inheritable: %s\n", 170 MHD_socket_last_strerr_ ()); 171 #endif /* MHD_POSIX_SOCKETS */ 172 } 173 174 memset (&sock_addr, 0, sizeof (struct sockaddr_in)); 175 sock_addr.sin_family = AF_INET; 176 sock_addr.sin_port = htons (0); 177 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN 178 sock_addr.sin_len = sizeof (struct sockaddr_in); 179 #endif 180 addrlen = sizeof (struct sockaddr_in); 181 182 if (bind (fd, (const struct sockaddr *) &sock_addr, addrlen) < 0) 183 { 184 fprintf (stderr, "Failed to bind socket: %u\n", 185 (unsigned) sock_errno); 186 MHD_socket_close_chk_ (fd); 187 return MHD_INVALID_SOCKET; 188 } 189 190 #ifdef MHD_WINSOCK_SOCKETS 191 if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) 192 { 193 fprintf (stderr, "Failed to make socket non-blocking: %u\n", 194 (unsigned) sock_errno); 195 MHD_socket_close_chk_ (fd); 196 return MHD_INVALID_SOCKET; 197 } 198 #else /* MHD_POSIX_SOCKETS */ 199 flags = fcntl (fd, F_GETFL); 200 if ( ( (-1 == flags) || 201 ( (flags != (flags | O_NONBLOCK)) && 202 (0 != fcntl (fd, F_SETFL, flags | O_NONBLOCK)) ) ) ) 203 { 204 fprintf (stderr, "Failed to make socket non-blocking: %s\n", 205 MHD_socket_last_strerr_ ()); 206 MHD_socket_close_chk_ (fd); 207 return MHD_INVALID_SOCKET; 208 } 209 #endif /* MHD_POSIX_SOCKETS */ 210 211 if (listen (fd, SOMAXCONN) < 0) 212 { 213 fprintf (stderr, "Failed to listen on socket: %u\n", 214 (unsigned) sock_errno); 215 MHD_socket_close_chk_ (fd); 216 return MHD_INVALID_SOCKET; 217 } 218 219 return fd; 220 } 221 222 223 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ 224 select_thread (void *data) 225 { 226 /* use select() like in daemon.c */ 227 MHD_socket listen_sock = *((MHD_socket *) data); 228 fd_set rs, ws; 229 struct timeval timeout; 230 231 FD_ZERO (&rs); 232 FD_ZERO (&ws); 233 FD_SET (listen_sock, &rs); 234 timeout.tv_usec = 0; 235 timeout.tv_sec = 7; 236 237 check_err = (0 > MHD_SYS_select_ (listen_sock + 1, &rs, &ws, NULL, &timeout)); 238 239 return (MHD_THRD_RTRN_TYPE_) 0; 240 } 241 242 243 #ifdef HAVE_POLL 244 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ 245 poll_thread (void *data) 246 { 247 /* use poll() like in daemon.c */ 248 struct pollfd p[1]; 249 MHD_socket listen_sock = *((MHD_socket *) data); 250 251 p[0].fd = listen_sock; 252 p[0].events = POLLIN; 253 p[0].revents = 0; 254 255 check_err = (0 > MHD_sys_poll_ (p, 1, 7000)); 256 257 return (MHD_THRD_RTRN_TYPE_) 0; 258 } 259 260 261 #endif /* HAVE_POLL */ 262 263 264 static void 265 local_sleep (unsigned seconds) 266 { 267 #if defined(_WIN32) && ! defined(__CYGWIN__) 268 Sleep (seconds * 1000); 269 #else 270 unsigned seconds_left = seconds; 271 do 272 { 273 seconds_left = sleep (seconds_left); 274 } while (seconds_left > 0); 275 #endif 276 } 277 278 279 int 280 main (int argc, char *const *argv) 281 { 282 int i; 283 time_t start_t, end_t; 284 int result = 0; 285 MHD_THRD_RTRN_TYPE_ (MHD_THRD_CALL_SPEC_ * test_func)(void *data); 286 #ifdef MHD_WINSOCK_SOCKETS 287 WORD ver_req; 288 WSADATA wsa_data; 289 int err; 290 #endif /* MHD_WINSOCK_SOCKETS */ 291 bool test_poll; 292 bool must_ignore; 293 (void) argc; /* Unused. Silent compiler warning. */ 294 295 test_poll = has_in_name (argv[0], "_poll"); 296 must_ignore = has_in_name (argv[0], "_ignore"); 297 if (! test_poll) 298 test_func = &select_thread; 299 else 300 { 301 #ifndef HAVE_POLL 302 return 77; 303 #else /* ! HAVE_POLL */ 304 test_func = &poll_thread; 305 #endif /* ! HAVE_POLL */ 306 } 307 308 #ifdef MHD_WINSOCK_SOCKETS 309 ver_req = MAKEWORD (2, 2); 310 311 err = WSAStartup (ver_req, &wsa_data); 312 if ((err != 0) || (MAKEWORD (2, 2) != wsa_data.wVersion)) 313 { 314 printf ("WSAStartup() failed\n"); 315 WSACleanup (); 316 return 99; 317 } 318 #endif /* MHD_WINSOCK_SOCKETS */ 319 320 /* try several times to ensure that accidental incoming connection 321 * didn't interfere with test results 322 */ 323 for (i = 0; i < 5 && result == 0; i++) 324 { 325 MHD_thread_handle_native_ sel_thrd; 326 /* fprintf(stdout, "Creating, binding and listening socket...\n"); */ 327 MHD_socket listen_socket = start_socket_listen (AF_INET); 328 if (MHD_INVALID_SOCKET == listen_socket) 329 return 99; 330 331 check_err = true; 332 /* fprintf (stdout, "Starting select() thread...\n"); */ 333 #if defined(MHD_USE_POSIX_THREADS) 334 if (0 != pthread_create (&sel_thrd, NULL, test_func, &listen_socket)) 335 { 336 MHD_socket_close_chk_ (listen_socket); 337 fprintf (stderr, "Can't start thread\n"); 338 return 99; 339 } 340 #elif defined(MHD_USE_W32_THREADS) 341 sel_thrd = (HANDLE) (uintptr_t) 342 _beginthreadex (NULL, 0, test_func, &listen_socket, 0, NULL); 343 if (0 == (sel_thrd)) 344 { 345 MHD_socket_close_chk_ (listen_socket); 346 fprintf (stderr, "Can't start select() thread\n"); 347 return 99; 348 } 349 #else 350 #error No threading lib available 351 #endif 352 /* fprintf (stdout, "Waiting...\n"); */ 353 local_sleep (1); /* make sure that select() is started */ 354 355 /* fprintf (stdout, "Shutting down socket...\n"); */ 356 start_t = time (NULL); 357 shutdown (listen_socket, SHUT_RDWR); 358 359 /* fprintf (stdout, "Waiting for thread to finish...\n"); */ 360 if (! MHD_join_thread_ (sel_thrd)) 361 { 362 MHD_socket_close_chk_ (listen_socket); 363 fprintf (stderr, "Can't join select() thread\n"); 364 return 99; 365 } 366 if (check_err) 367 { 368 MHD_socket_close_chk_ (listen_socket); 369 fprintf (stderr, "Error in waiting thread\n"); 370 return 99; 371 } 372 end_t = time (NULL); 373 /* fprintf (stdout, "Thread finished.\n"); */ 374 MHD_socket_close_chk_ (listen_socket); 375 376 if ((start_t == (time_t) -1) || (end_t == (time_t) -1) ) 377 { 378 MHD_socket_close_chk_ (listen_socket); 379 fprintf (stderr, "Can't get current time\n"); 380 return 99; 381 } 382 if (end_t - start_t > 3) 383 result++; 384 } 385 386 #ifdef MHD_WINSOCK_SOCKETS 387 WSACleanup (); 388 #endif /* MHD_WINSOCK_SOCKETS */ 389 390 return must_ignore ? (! result) : (result); 391 }