int manageConnections( SocketData *socket_data ) { int client_socket; struct sockaddr_in adr_clnt; struct timeval socket_timeout; int res; int c; socket_error = 0; prepare_for_select( socket_data ); socket_timeout.tv_sec = socket_data->timeout_secs; socket_timeout.tv_usec = socket_data->timeout_usecs; res = select( socket_data->max_fd, &socket_data->rd_set, NULL, NULL, &socket_timeout ); if ( res < 0 ) { socket_error = errno; if ( socket_error == EINTR ) { client_socket = 0; } else { printf( "Select failed: %d\n", errno ); client_socket = -1; } } else if ( res == 0 ) { // zero is a timeout client_socket = 0; } else if ( FD_ISSET( socket_data->socket_listener, &socket_data->rd_set ) ) { // New client connecting - what priority should this be? retry: client_socket = accept( socket_data->socket_listener, (struct sockaddr *)&adr_clnt, &socket_data->len_inet ); if ( client_socket < 0 ) { socket_error = errno; if ( socket_error == EINTR ) { goto retry; } else if ( socket_error == EAGAIN ) { // EAGAIN only occurs on non-blocking sockets to indicate no data yet socket_error = 0; } else { printf( "Accept failed: %d\n", errno ); client_socket = -1; } } else if ( client_socket > FD_SETSIZE ) { printf( "Socket closed: at capacity - FD_SETSIZE exceeded\n" ); close( client_socket ); client_socket = -1; } else { if ( client_socket + 1 > socket_data->max_fd ) { socket_data->max_fd = client_socket + 1; } FD_SET( client_socket, &socket_data->so_set ); // client_socket = 0; // nothing to do } } else { //printf( "Existing socket\n" ); // existing client with new data // not very efficient here since accept may report multiple // clients but I'm only servicing one at a time. client_socket = 0; for ( c = 0 ; c < socket_data->max_fd ; ++c ) { if ( c == socket_data->socket_listener ) // should never occur continue; // but ignore listener if ( FD_ISSET( c, &socket_data->rd_set ) ) { client_socket = c; break; } } } return client_socket; } // get_client_connection
int main(int argc, char **argv) { TSS_RESULT result; int newsd, c, rv, option_index = 0; int i; socklen_t client_len; char *hostname = NULL; fd_set rdfd_set; int num_fds = 0; int nfds = 0; int stor_errno; sigset_t sigmask, termmask, oldsigmask; struct sockaddr_storage client_addr; struct srv_sock_info socks_info[MAX_IP_PROTO]; struct passwd *pwd; struct option long_options[] = { {"help", 0, NULL, 'h'}, {"foreground", 0, NULL, 'f'}, {"config", 1, NULL, 'c'}, {0, 0, 0, 0} }; unsetenv("TCSD_USE_TCP_DEVICE"); while ((c = getopt_long(argc, argv, "fhec:", long_options, &option_index)) != -1) { switch (c) { case 'f': setenv("TCSD_FOREGROUND", "1", 1); break; case 'c': tcsd_config_file = optarg; break; case 'e': setenv("TCSD_USE_TCP_DEVICE", "1", 1); break; case 'h': /* fall through */ default: usage(); return -1; break; } } if (!tcsd_config_file) tcsd_config_file = TCSD_DEFAULT_CONFIG_FILE; if ((result = tcsd_startup())) return (int)result; #ifdef NOUSERCHECK LogWarn("will not switch user or check for file permissions. " "(Compiled with --disable-usercheck)"); #else #ifndef SOLARIS pwd = getpwnam(TSS_USER_NAME); if (pwd == NULL) { if (errno == 0) { LogError("User \"%s\" not found, please add this user" " manually.", TSS_USER_NAME); } else { LogError("getpwnam(%s): %s", TSS_USER_NAME, strerror(errno)); } return TCSERR(TSS_E_INTERNAL_ERROR); } setuid(pwd->pw_uid); #endif #endif if (setup_server_sockets(socks_info) == -1) { LogError("Could not create sockets to listen to connections. Aborting..."); return -1; } if (getenv("TCSD_FOREGROUND") == NULL) { if (daemon(0, 0) == -1) { perror("daemon"); tcsd_shutdown(socks_info); return -1; } } LogInfo("%s: TCSD up and running.", PACKAGE_STRING); sigemptyset(&sigmask); sigaddset(&sigmask, SIGTERM); sigaddset(&sigmask, SIGHUP); sigemptyset(&termmask); sigaddset(&termmask, SIGTERM); do { prepare_for_select(socks_info, &num_fds, &rdfd_set, &nfds); // Sanity check if (num_fds == 0) { LogError("No server sockets available to listen connections. Aborting..."); return -1; } // Block TERM and HUP signals to prevent race condition if (sigprocmask(SIG_BLOCK, &sigmask, &oldsigmask) == -1) { LogError("Error setting interrupt mask before accept"); } // TERM and HUP are blocked here, so its safe to test flags. if (hup) { // Config reading can be slow, so unmask SIGTERM. if (sigprocmask(SIG_UNBLOCK, &termmask, NULL) == -1) { LogError("Error unblocking SIGTERM before config reload"); } if (reload_config() != TSS_SUCCESS) LogError("Failed reloading config"); if (sigprocmask(SIG_BLOCK, &termmask, NULL) == -1) { LogError("Error blocking SIGTERM after config reload"); } } if (term) break; // Select IPv4 and IPv6 socket descriptors with appropriate sigmask. LogDebug("Waiting for connections"); rv = pselect(nfds+1, &rdfd_set, NULL, NULL, NULL, &oldsigmask); stor_errno = errno; // original mask must be set ASAP, so store errno. if (sigprocmask(SIG_SETMASK, &oldsigmask, NULL) == -1) { LogError("Error reseting signal mask to the original configuration."); } if (rv == -1) { if (stor_errno != EINTR) { LogError("Error monitoring server socket descriptors."); return -1; } continue; } for (i=0; i < num_fds; i++) { // accept connections from all IP versions (with valid sd) if (!FD_ISSET(socks_info[i].sd, &rdfd_set)) { continue; } client_len = socks_info[i].addr_len; newsd = accept(socks_info[i].sd, (struct sockaddr *) &client_addr, &client_len); if (newsd < 0) { if (errno != EINTR) LogError("Failed accept: %s", strerror(errno)); continue; } LogDebug("accepted socket %i", newsd); hostname = fetch_hostname(&client_addr, client_len); if (hostname == NULL) hostname=INVALID_ADDR_STR; tcsd_thread_create(newsd, hostname); hostname = NULL; } // for (i=0; i < MAX_IP_PROTO; i++) } while (term ==0); /* To close correctly, we must receive a SIGTERM */ tcsd_shutdown(socks_info); return 0; }