#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libc/limits.h" /** * @fileoverview SO_RCVTIMEO + SA_RESTART interaction test * * This code tests that setting a read timeout on a socket will cause * read() to change its signal handling behavior from @restartable to * @norestart. This is currently the case on GNU/Systemd and Windows. */ struct sockaddr_in serv_addr; atomic_bool g_ready_for_conn; atomic_bool g_ready_for_data; atomic_bool g_ready_for_more; atomic_bool g_ready_for_exit; atomic_bool got_sigusr1; atomic_bool got_sigusr2; void on_sigusr1(int sig) { got_sigusr1 = true; } void on_sigusr2(int sig) { got_sigusr2 = true; } void *server_thread(void *arg) { int server, client; struct timeval timeout; socklen_t len; struct sockaddr_in cli_addr; // create listening socket server = socket(AF_INET, SOCK_STREAM, 0); if (server == -1) { perror("socket"); exit(31); } // initialize server address memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); serv_addr.sin_port = htons(0); // bind socket if (bind(server, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) { perror("bind"); exit(32); } // get assigned port len = sizeof(serv_addr); if (getsockname(server, (struct sockaddr *)&serv_addr, &len)) { perror("getsockname"); exit(30); } // listen on the socket if (listen(server, SOMAXCONN)) { perror("listen"); exit(33); } // wake main thread g_ready_for_conn = true; // accept connection len = sizeof(cli_addr); client = accept(server, (struct sockaddr *)&cli_addr, &len); if (client == -1) { perror("accept"); exit(35); } // wake main thread g_ready_for_data = true; // check read() has @restartable behavior char buf[1]; int rc = read(client, buf, 1); if (rc != -1) exit(35); if (errno != EINTR) exit(36); if (!got_sigusr1) exit(37); if (!got_sigusr2) exit(38); got_sigusr1 = false; got_sigusr2 = false; // install a socket receive timeout timeout.tv_sec = 5000000; timeout.tv_usec = 0; if (setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout) + !IsNetbsd())) { perror("setsockopt"); exit(34); } // wake main thread g_ready_for_more = true; // check read() has @norestart behavior rc = read(client, buf, 1); if (rc != -1) exit(35); if (errno != EINTR) exit(36); if (!got_sigusr1) exit(37); // here's the whammy if (IsLinux() || IsWindows()) { if (got_sigusr2) exit(38); } else { if (!got_sigusr2) exit(38); } // wait for main thread for (;;) if (g_ready_for_exit) break; // close listening socket if (close(server)) exit(40); if (close(client)) exit(39); return 0; } int main() { // handle signals struct sigaction sa = {0}; sa.sa_handler = on_sigusr1; sa.sa_flags = SA_RESTART; sigaction(SIGUSR1, &sa, 0); sa.sa_handler = on_sigusr2; sa.sa_flags = 0; sigaction(SIGUSR2, &sa, 0); // create server thread pthread_t th; if (pthread_create(&th, 0, server_thread, 0)) return 1; // wait for thread for (;;) if (g_ready_for_conn) break; // create socket int client = socket(AF_INET, SOCK_STREAM, 0); if (client == -1) { perror("socket"); return 2; } // connect to server if (connect(client, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { perror("connect"); return 3; } // wait for thread for (;;) if (g_ready_for_data) break; usleep(100e3); if (pthread_kill(th, SIGUSR1)) return 4; usleep(100e3); if (pthread_kill(th, SIGUSR2)) return 5; // wait for thread for (;;) if (g_ready_for_more) break; usleep(100e3); if (pthread_kill(th, SIGUSR1)) return 4; usleep(400e3); if (pthread_kill(th, SIGUSR2)) return 5; g_ready_for_exit = true; if (pthread_join(th, 0)) return 20; }