// thread_functions.cpp #include "thread_functions.h" void *thread_driver_func(void *arg) { ThreadData *tData = (ThreadData *) arg; INT result = 0; time_t current_time = time(NULL); ClientSocket *client; struct timeval tv; fd_set fdset; // set a timeout? tv.tv_sec = 0; tv.tv_usec = 500000; while(*tData->shutdown == false) { tData->driver->copy_fdset(&fdset); result = select(tData->driver->get_fdmax(), &fdset, NULL, NULL, &tv); // some os's (like linux) will modify tv to reflect leftover time, so we repair it tv.tv_sec = 0; tv.tv_usec = 500000; if(result == -1) { sil_log::log("thread_select(): select() returned error"); // close down the driver *tData->shutdown = true; } else if(result == 0) { // timeout if( (time(NULL) - current_time) >= 1) { // this is run about every second current_time = time(NULL); cmd::update(tData->driver); } } else { // did we get a new connection? if(FD_ISSET(tData->driver->get_socket_fd(), &fdset)) { sil_log::log("New incoming connection"); tData->driver->new_connection(); } // poll currently opened sockets for(INT i = 0; i < tData->driver->get_number_of_connections(); i++) { if( (client = tData->driver->get_client_ptr(i)) != NULL) { sil_log::log("polling open clients"); // if this socket is closed, skip it if(client->get_conn_state() == Closed) continue; // if it's in our descriptor list as open but we can't read from it, close it if(client->get_conn_state() > Closed) { if(FD_ISSET(client->get_fd(), &fdset) && !client->read_socket()) { sil_log::log("no longer reading from descriptor %d", i); tData->driver->shutdown_connection(i); continue; } } // we have work to process, let the other thread know about it // // ************* IMPORTANT ************ // this is incremented once for _every_ socket waiting to be read sem_post(tData->semafor); } } } } // while() // we are being shut down pthread_exit(0); } void *thread_process_func(void *arg) { ThreadData *tData = (ThreadData *)arg; ClientSocket *client; STRING client_command; INT poll_size = 0; while(*tData->shutdown == false) { // wait for action sem_wait(tData->semafor); poll_size = tData->driver->get_number_of_connections(); for(INT i = 0; i < poll_size; i++) { client = tData->driver->get_client_ptr(i); // hopefully this never happens if(client == NULL) continue; // skip if closed if(client->get_conn_state() == Closed) continue; tData->driver->parse_in_buffer(i); client_command = client->get_next_command(); if(!client_command.empty()) { switch(client->get_conn_state()) { case LoginName: case LoginPassword: sil_log::log("passing %d to login daemon", i); tData->driver->process_login(i); break; case Playing: sil_log::log("%d in playing state", i); cmd::do_command(tData->driver, i); break; default: sil_log::log("Client conn_state out of sync"); } client->clear_next_command(); } // perhaps our client just quit.. if(client->get_conn_state() == Closed) continue; if(!tData->driver->flush_output(i)) tData->driver->shutdown_connection(i); } // for() } // while() pthread_exit(0); } void *thread_comm_func(void *arg) { ThreadData *tData = (ThreadData *)arg; fd_set fdset; struct timeval tv; INT result = 0; CommClient *client; // initialize our timeout to 2 seconds tv.tv_sec = 2; tv.tv_usec = 0; while(tData->shutdown == false) { tData->comm->copy_fdset(&fdset); result = select(tData->comm->get_fdmax(), &fdset, NULL, NULL, &tv); tv.tv_sec = 2; tv.tv_usec = 0; if(result == -1) { // select failed, exit gracefully sil_log::log("comm thread select failed, trying to exit nicely"); close(tData->comm->get_socket_fd()); pthread_exit(0); } if(result == 0) { // just give us a chance to exit if tData->shutdown becomes true } else { if(FD_ISSET(tData->comm->get_socket_fd(), &fdset)) { // new incoming connection tData->comm->new_connection(); } for(INT i = 0; i < tData->comm->get_number_of_connections(); i++) { if( (client = tData->comm->get_comm_client_ptr(i)) != NULL) { if(FD_ISSET(client->get_fd(), &fdset)) { if(!client->read_comm()) { tData->comm->shutdown_connection(i); continue; } // we read something from the client, evaluate it if(client->evaluate()) { INT recipient = tData->driver->find_client_by_name(client->get_recipient()); if(recipient != -1) tData->driver->write_to(recipient, client->get_message()); } } } } } } // while() pthread_exit(0); } void *lookup_address(void *arg) { if(arg == NULL) { sil_log::log("lookup thread received NULL argument"); pthread_exit(0); } ClientSocket *client = (ClientSocket *)arg; int error; // shouldn't be a long int (INT) for call to gethostbyaddr_r() CHAR buffer[2048] = {0}; // host database entries are stored here, 256 bytes is usually safe, 2k should be more than enough CHAR ip[32] = {0}; // should be 16 at most, but just in case... assert(client->get_hostname().length() < 32); strcpy(ip, client->get_hostname().c_str()); struct hostent fromHost, *from = &fromHost, ent; struct in_addr host; void *addr = &host; // initialize host structure memset(addr, 0, sizeof(struct in_addr)); // convert dotted quad ip to network address structure in AF_INET (IPv4) family inet_pton(AF_INET, ip, addr); /* do the lookup and store the result at &from */ gethostbyaddr_r(addr, sizeof(struct in_addr), AF_INET, &ent, buffer, sizeof(buffer), &from, &error); if(from != NULL && strlen(from->h_name) > 0) { client->set_hostname(from->h_name); } pthread_exit(0); }