// Function called by network thread, used to separate the TCP listener from the console thread void *tcp_listen() { // Set up p2p_t struct, to pass variables into the thread function p2p_t params; // Create output buffer, in case the base server must communicate directly to the client char out[512] = { '\0' }; // Loop infinitely until Ctrl+C SIGINT is caught by the signal handler while(1) { // Attempt to accept incoming connections using the incoming socket if((inc_fd = accept(loc_fd, (struct sockaddr *)&inc_addr, &inc_len)) == -1) { // Print an error message and quit if server cannot accept connections fprintf(stderr, "%s: %s failed to accept incoming connections\n", SERVER_NAME, ERROR_MSG); return (void *)-1; } else { // If a connection is accepted, continue routines. // Capture client's IP address for logging. inet_ntop(inc_addr.ss_family, get_in_addr((struct sockaddr *)&inc_addr), clientaddr, sizeof(clientaddr)); // Print message when connection is received, and increment client counter fprintf(stdout, "%s: %s client connected from %s [fd: %d] [users: %d/%d]\n", SERVER_NAME, OK_MSG, clientaddr, inc_fd, client_count(1), num_threads); // If client count reaches the utilization threshold, print a warning if(((double)client_count(0) >= ((double)num_threads * TP_UTIL)) && (client_count(0) <= num_threads)) { // Print warning to server console, alter wording slightly if utilization is maxed out if(client_count(0) == num_threads) fprintf(stdout, "%s: %s thread pool exhausted [users: %d/%d]\n", SERVER_NAME, WARN_MSG, client_count(0), num_threads); else fprintf(stdout, "%s: %s thread pool nearing exhaustion [users: %d/%d]\n", SERVER_NAME, WARN_MSG, client_count(0), num_threads); } // If client count exceeds the number of threads in the pool, print an error else if((client_count(0)) > num_threads) { // Print error to console fprintf(stderr, "%s: %s thread pool over-exhausted [users: %d/%d]\n", SERVER_NAME, ERROR_MSG, client_count(0), num_threads); // Generate message to send to client sprintf(out, "%s: %s server has currently reached maximum user capacity, please wait\n", SERVER_NAME, USER_MSG); send_msg(inc_fd, out); } // Store user's file descriptor and IP address in the params struct params.fd = inc_fd; strcpy(params.ipaddr, clientaddr); // On client connection, add work to the threadpool, pass in params struct thpool_add_work(threadpool, &p2p, (void*)¶ms); } } }
// this handler fires, and it collates the stats collected, and possibly outputs to the log. // If we have migrating stats, then we need to migrate to the next slot. static void stats_handler(int fd, short int flags, void *arg) { int changed = 0; int new_nodes; int new_clients; assert(fd == -1); assert(flags & EV_TIMEOUT); assert(arg == NULL); new_nodes = node_active_count(); if (_stats.last.active_nodes != new_nodes) { _stats.last.active_nodes = new_nodes; changed++; } new_clients = client_count(); if (_stats.last.clients != new_clients) { _stats.last.clients = new_clients; changed++; } if (_stats.bytes_in > 0 || _stats.bytes_out > 0) { changed ++; } if (changed > 0) { logger(LOG_STATS, "Stats. Nodes:%d, Clients:%d, Bytes IN:%d, Bytes OUT:%d", new_nodes, new_clients, _stats.bytes_in, _stats.bytes_out); } _stats.bytes_in = 0; _stats.bytes_out = 0; evtimer_add(_stats_event, &_timeout_stats); }
// Simple dummy application which runs on the thread pool, receiving input and echoing it back void *echo(void *arg) { // Create and clear input and output buffers char in[512], out[512] = { '\0' }; // Keep track of user's file descriptor as passed in by thread arguments int user_fd = *(int *)arg; // Send user initial welcome message sprintf(out, "%s: %s type a message, or /quit to quit\n", APP_NAME, USER_MSG); send_msg(user_fd, out); // Loop until the user sends in the quit command while (strncmp(in, "/quit", 5) != 0) { // Receive user's message, remove newlines recv_msg(user_fd, (char *)&in); if (in[strlen(in) - 1] == '\n' || in[strlen(in) - 1] == '\r') { in[strlen(in) - 1] = '\0'; } // Echo it back to the user sprintf(out, "%s: %s received: %s\n", APP_NAME, USER_MSG, in); send_msg(user_fd, out); } // Send user goodbye message sprintf(out, "%s: %s goodbye!\n", APP_NAME, USER_MSG); send_msg(user_fd, out); // Clear I/O buffers memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); // Decrement client count client_count(-1); // Print user disconnect message to console fprintf(stdout, "%s: %s client disconnected [fd: %d]\n", APP_NAME, OK_MSG, user_fd); // Attempt to close user socket if (close(user_fd) == -1) { // On failure, print error to console fprintf(stderr, "%s: %s failed to close user socket\n", APP_NAME, ERROR_MSG); return (void *)-1; } // If all routines succeded, return success, so that thread may rejoin the pool return (void *)0; }
static void start_server (void) { int client_sd; int daemon_sd; int connected_sd; struct sockaddr_in client_sa; struct sockaddr_in daemon_sa; struct sockaddr_in connected_sa; socklen_t size; fd_set socket_set; int nfds; struct ifreq if_info; struct sockaddr_in *if_addr; char addr[INET_ADDRSTRLEN]; struct client *c; struct daemon *d; struct parsed_cmd *pcmd = NULL; char *ident_msg; int port; char *colon; /* Prepare all the threads */ slow_pool = NULL; fast_pool = NULL; clients_pool = NULL; daemons_pool = NULL; ABORT_IF (!(slow_pool = pool_create (prefs->nb_proc)), "Unable to create slow_pool") ABORT_IF (!(fast_pool = pool_create (prefs->nb_proc)), "Unable to create fast_pool") ABORT_IF (!(clients_pool = pool_create (prefs->max_clients)), "Unable to create clients_pool") ABORT_IF (!(daemons_pool = pool_create (prefs->max_daemons)), "Unable to create daemons_pool") /* Create the shared directory if it does not exist already */ ABORT_IF (create_dir (prefs->shared_folder, (mode_t)0755) < 0, "Unable to create shared directory") /* Initialize global pointers and their semaphores */ clients = NULL; ABORT_IF (sem_init (&clients_lock, 0, 1) < 0, "Unable to sem_init clients_lock") daemons = NULL; ABORT_IF (sem_init (&daemons_lock, 0, 1) < 0, "Unable to sem_init daemons_lock") file_cache = NULL; ABORT_IF (sem_init (&file_cache_lock, 0, 1) < 0, "Unable to sem_init file_cache_lock") list_client = NULL; ABORT_IF (sem_init (&list_lock, 0, 1) < 0, "Unable to sem_init list_lock") downloads = NULL; ABORT_IF (sem_init (&downloads_lock, 0, 1) < 0, "Unable to sem_init download_queue_lock") client_sa.sin_family = AF_INET; client_sa.sin_addr.s_addr = INADDR_ANY; client_sa.sin_port = htons (prefs->client_port); client_sd = socket_init (&client_sa); ABORT_IF (client_sd < 0, "Unable to socket_init client_sd") daemon_sa.sin_family = AF_INET; daemon_sa.sin_addr.s_addr = INADDR_ANY; daemon_sa.sin_port = htons (prefs->daemon_port); daemon_sd = socket_init (&daemon_sa); ABORT_IF (daemon_sd < 0, "Unable to socket_init daemon_sd") #if 1 /* We get our ip */ memcpy (if_info.ifr_name, prefs->interface, strlen (prefs->interface) + 1); if (ioctl (daemon_sd, SIOCGIFADDR, &if_info) == -1) { log_failure (log_file, "Can't get my ip from interface"); log_failure (log_file, "LOL ERRNO : %s\n", strerror (errno)); goto abort; } if_addr = (struct sockaddr_in *)&if_info.ifr_addr; inet_ntop (AF_INET, &if_addr->sin_addr, my_ip, INET_ADDRSTRLEN); log_success (log_file, "Found my IP : %s", my_ip); #endif /* socket_set contains both client_sd and daemon_sd */ FD_ZERO (&socket_set); size = sizeof (connected_sa); nfds = NFDS (client_sd, daemon_sd); for (;;) { /* * It is VERY important to FD_SET at each loop, because select * will FD_UNSET the socket descriptors */ FD_SET (client_sd, &socket_set); FD_SET (daemon_sd, &socket_set); /* Block until a socket is ready to accept */ if (select (nfds, &socket_set, NULL, NULL, NULL) < 0) { log_failure (log_file, "main () : select failed"); } if (FD_ISSET (client_sd, &socket_set)) { if ((connected_sd = (accept (client_sd, (struct sockaddr *) &connected_sa, &size))) < 0) { log_failure (log_file, "Failed to accept incoming connection."); break; } /* Can we handle this client? */ if (client_count () > prefs->max_clients) { socket_sendline (connected_sd, " < Too many clients\n"); goto close_socket; } /* Then, let's handle him */ if (!inet_ntop (AF_INET, &connected_sa.sin_addr, addr, INET_ADDRSTRLEN)) { socket_sendline (connected_sd, " < Oops\n"); goto close_socket; } if (!(c = client_new (connected_sd, addr))) { socket_sendline (connected_sd, " < Sorry pal :(\n"); } pool_queue (clients_pool, handle_client, c); } else if (FD_ISSET (daemon_sd, &socket_set)) { if ((connected_sd = (accept (daemon_sd, (struct sockaddr *) &connected_sa, &size))) < 0) { log_failure (log_file, "Failed to accept incoming connection."); break; } /* Can we handle this daemon? */ if (daemon_count () > prefs->max_daemons) { socket_sendline (connected_sd, " < Too many daemons\n"); goto close_socket; } /* Let's identify him first */ ident_msg = socket_try_getline (connected_sd, IDENTIFICATION_TIMEOUT); if (!ident_msg) { socket_sendline (connected_sd, "error: identification timed out\n"); goto close_socket; } if (cmd_parse_failed ((pcmd = cmd_parse (ident_msg, NULL)))) { pcmd = NULL; goto close_socket; } if (pcmd->argc < 2) goto close_socket; if (strcmp (pcmd->argv[0], "neighbour") != 0) goto close_socket; if (!(colon = strchr (pcmd->argv[1], ':'))) goto close_socket; port = atoi (colon + 1); free (ident_msg); cmd_parse_free (pcmd); pcmd = NULL; if (!inet_ntop (AF_INET, &connected_sa.sin_addr, addr, INET_ADDRSTRLEN)) { socket_sendline (connected_sd, " < Oops\n"); goto close_socket; } /* Now we've got his port, let him go in */ if (!(d = daemon_new (connected_sd, addr, port))) { socket_sendline (connected_sd, " < Sorry pal :(\n"); goto close_socket; } pool_queue (daemons_pool, handle_daemon, d); } else { /* This should never happen : neither client nor daemon!? */ log_failure (log_file, "Unknown connection"); } continue; close_socket: if (pcmd) { cmd_parse_free (pcmd); pcmd = NULL; } close (connected_sd); } abort: if (slow_pool) pool_destroy (slow_pool); if (fast_pool) pool_destroy (fast_pool); if (clients_pool) pool_destroy (clients_pool); if (daemons_pool) pool_destroy (daemons_pool); conf_free (prefs); exit (EXIT_FAILURE); }
int sound::outputcallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData){ sound * psound = (sound*)userData; short * voiceBuffer = (short *)output; memset(voiceBuffer, 0, psound->BUFF_LONG * sizeof(short) * psound->outputStreamParameters.channelCount); int empty = 0; iterator_client_set([&empty, psound, frameCount, &voiceBuffer](std::map<int, client*> & set){ for (auto var : set){ char * outbuf = 0; short channelcount = 0; int len = 0; if (!var.second->read_buff(outbuf, channelcount, len)){ empty++; continue; } short voicebuf[4096]; memset(voicebuf, 0, 4096 * sizeof(short)); psound->_encode.decoded(outbuf, len, (char*)voicebuf, psound->BUFF_LONG * psound->outputStreamParameters.channelCount); for (unsigned int i = 0; i < frameCount; i++) { int src = frameCount - i - 1; int dst = src * psound->outputStreamParameters.channelCount; if (voiceBuffer[dst] > 0 && voicebuf[src] > CLAMP_MAX - voiceBuffer[dst]){ voiceBuffer[dst] = CLAMP_MAX; } else if (voiceBuffer[i] <= 0 && voicebuf[src] < CLAMP_MIN - voiceBuffer[dst]){ voiceBuffer[dst] = CLAMP_MIN; } else{ voiceBuffer[dst] += voicebuf[src]; } } for (int channel = 1; channel < psound->outputStreamParameters.channelCount; channel++) { for (unsigned int i = 0; i < frameCount; i++) { int src = frameCount - i - 1;// (frameCount - i - 1) * psound->outputStreamParameters.channelCount; int dst = src + channel; if (voiceBuffer[dst] > 0 && voicebuf[src] > CLAMP_MAX - voiceBuffer[dst]){ voiceBuffer[dst] = CLAMP_MAX; } else if (voiceBuffer[i] <= 0 && voicebuf[src] < CLAMP_MIN - voiceBuffer[dst]){ voiceBuffer[dst] = CLAMP_MIN; } else{ voiceBuffer[dst] += voicebuf[src]; } } } } }); if (empty == client_count()){ return paContinue; } if (psound->isopenecho == true){ while (1){ int _begin = psound->begin.load(); int _new = _begin + 1; if (_new == psound->end.load()){ break; } if (_new == 16){ _new = 0; } if (psound->begin.compare_exchange_strong(_begin, _new)){ memcpy(psound->inputbuff[_begin].buf, voiceBuffer, frameCount*psound->outputStreamParameters.channelCount); psound->inputbuff[_begin].len = frameCount*psound->outputStreamParameters.channelCount; break; } } } return paContinue; }
// Statistics function, which can be called via console or signal void print_stats() { // Create variables to calculate elapsed time int hours, minutes, seconds; // Create a buffer to store total elapsed time char runtime[32] = { '\0' }; // Create a buffer to store thread pool usage calculations char tpusage[32] = { '\0' }; //------------------ CALCULATE RUNTIME --------------------- // Calculate total number of seconds since program start seconds = (int)difftime(time(NULL), start_time); // Calculate minutes as the number of seconds divided by 60 minutes = seconds / 60; // Calculate hours as the number of minutes divided by 60 hours = minutes / 60; // Recalculate minutes as the remainder after hours are taken out minutes = minutes % 60; // Recalculate seconds as the remainder after minutes are taken out seconds = seconds % 60; // Combine all time information into a single string sprintf(runtime, "%02d:%02d:%02d", hours, minutes, seconds); //----------------- CALCULATE THREAD POOL USAGE ------------- // Begin calculating thread pool usage, setting message type accordingly // If thread pool is under-capacity... if(client_count(0) < (num_threads * TP_UTIL)) { // Set message type to OK fprintf(stdout, "%s: %s ", SERVER_NAME, OK_MSG); // Format users string as normal sprintf(tpusage, "[users: %d/%d]", client_count(0), num_threads); } // If thread pool is near or at-capacity... else if(((double)client_count(0) >= ((double)num_threads * TP_UTIL)) && client_count(0) <= num_threads) { // Set message type to WARN fprintf(stdout, "%s: %s ", SERVER_NAME, WARN_MSG); // Format users string with warning color sprintf(tpusage, "\033[1;33m[users: %d/%d]\033[0m", client_count(0), num_threads); } // Finally, if thread pool is over-capacity... else { // Set message type to ERROR fprintf(stdout, "%s: %s ", SERVER_NAME, ERROR_MSG); // Format users string with error color sprintf(tpusage, "\033[1;31m[users: %d/%d]\033[0m", client_count(0), num_threads); } //----------------- PRINT STATISTICS ------------------------ // Print out server statistics, differ slightly if daemonized if(daemonized == 1) fprintf(stdout, "daemon running [PID: %d] [time: %s] [lock: %s] [port: %s] [queue: %d] %s\n", getpid(), runtime, lock_location, port, queue_length, tpusage); else fprintf(stdout, "server running [PID: %d] [time: %s] [port: %s] [queue: %d] %s\n", getpid(), runtime, port, queue_length, tpusage); }
// Shutdown handler, which catches signals and performs shutdown routines to cleanly terminate the server void shutdown_handler() { // Cancel the network handling thread, stopping all incoming connections pthread_cancel(net_thread); // If in daemon mode, unlock, close, and remove the lockfile if(daemonized == 1) { // Attempt to unlock lockfile if(lockf(pidfile, F_ULOCK, 0) == -1) { fprintf(stderr, "%s: %s failed to unlock lockfile\n", SERVER_NAME, ERROR_MSG); exit(-1); } // Attempt to close lockfile if(close(pidfile) == -1) { fprintf(stderr, "%s: %s failed to close daemon lockfile\n", SERVER_NAME, ERROR_MSG); exit(-1); } // Attempt to remove lockfile if(remove(lock_location) == -1) { fprintf(stderr, "%s: %s could not remove lockfile %s\n", SERVER_NAME, ERROR_MSG, lock_location); exit(-1); } } // Print newline to clean up output fprintf(stdout, "\n"); // Close SQLite database if(sqlite3_close(db) != SQLITE_OK) { // On failure, print error to console and exit fprintf(stderr, "%s: %s sqlite: failed to close database\n", SERVER_NAME, ERROR_MSG); exit(-1); } // Attempt to shutdown the local socket if(shutdown(loc_fd, 2) == -1) { // Print error and exit if socket fails to shutdown fprintf(stderr, "%s: %s failed to shutdown local socket\n", SERVER_NAME, ERROR_MSG); exit(-1); } // Attempt to close local socket to end program if(close(loc_fd) == -1) { // Print error and exit if socket fails to close fprintf(stderr, "%s: %s failed to close local socket\n", SERVER_NAME, ERROR_MSG); exit(-1); } // Destroy created threadpool. If 0 clients are connected, rejoin the threads. Else, cancel the threads (force destroy). if(client_count(0) == 0) thpool_destroy(threadpool, 0); else thpool_destroy(threadpool, 1); // Print final termination message fprintf(stdout, "%s: %s kicked %d client(s), server terminated\n", SERVER_NAME, OK_MSG, client_count(0)); // Exit, returning success exit(0); }