/* Broadcast a message to all the descriptors in fds. Returns -1 if any of the sends failed. */ int ncat_broadcast(fd_set *fds, const fd_list_t *fdlist, const char *msg, size_t size) { struct fdinfo *fdn; int i, ret; if (o.recvonly) return size; ret = 0; for (i = 0; i <= fdlist->fdmax; i++) { if (!FD_ISSET(i, fds)) continue; fdn = get_fdinfo(fdlist, i); ncat_assert(fdn != NULL); if (blocking_fdinfo_send(fdn, msg, size) <= 0) { if (o.debug > 1) logdebug("Error sending to fd %d: %s.\n", i, socket_strerror(socket_errno())); ret = -1; } } ncat_log_send(msg, size); return ret; }
static void shutdown_sockets(int how) { struct fdinfo *fdn; int i; for (i = 0; i <= broadcast_fdlist.fdmax; i++) { if (!FD_ISSET(i, &master_broadcastfds)) continue; fdn = get_fdinfo(&broadcast_fdlist, i); ncat_assert(fdn != NULL); shutdown(fdn->fd, how); } }
/* Read from a client socket and write to stdout. Return the number of bytes read from the socket, or -1 on error. */ int read_socket(int recv_fd) { char buf[DEFAULT_TCP_BUF_LEN]; struct fdinfo *fdn; int nbytes, pending; fdn = get_fdinfo(&client_fdlist, recv_fd); ncat_assert(fdn != NULL); nbytes = 0; do { int n; n = ncat_recv(fdn, buf, sizeof(buf), &pending); if (n <= 0) { if (o.debug) logdebug("Closing connection.\n"); #ifdef HAVE_OPENSSL if (o.ssl && fdn->ssl) { if (nbytes == 0) SSL_shutdown(fdn->ssl); SSL_free(fdn->ssl); } #endif close(recv_fd); FD_CLR(recv_fd, &master_readfds); rm_fd(&client_fdlist, recv_fd); FD_CLR(recv_fd, &master_broadcastfds); rm_fd(&broadcast_fdlist, recv_fd); conn_inc--; if (get_conn_count() == 0) FD_CLR(STDIN_FILENO, &master_readfds); return n; } Write(STDOUT_FILENO, buf, n); nbytes += n; } while (pending); return nbytes; }
int monitor_processes(int *nb_pid) { int pid_count, fd_count, result_count; int i,j; pidinfo_t pidinfo_list[MAX_PIDS]; fdinfo_t fdinfo; fdinfo_t biggest_fd; int fdnum_list[MAX_FD_PER_PID]; off_t max_size; char fsize[64]; char fpos[64]; char ftroughput[64]; float perc; result_t results[MAX_RESULTS]; signed char still_there; signed char search_all = 1; pid_count = 0; if (proc_specifiq_name_cnt) { for (i = 0; i < proc_specifiq_name_cnt; ++i) pid_count += find_pids_by_binary_name(proc_specifiq_name[i], pidinfo_list + pid_count, MAX_PIDS - pid_count); search_all = 0; } if (proc_specifiq_pid) { pid_count += find_pid_by_id(proc_specifiq_pid, pidinfo_list + pid_count); search_all = 0; } if (search_all) { for (i = 0 ; proc_names[i] ; i++) { pid_count += find_pids_by_binary_name(proc_names[i], pidinfo_list + pid_count, MAX_PIDS - pid_count); if(pid_count >= MAX_PIDS) { nfprintf(stderr, "Found too much procs (max = %d)\n",MAX_PIDS); break; } } } *nb_pid = pid_count; if (!pid_count) { if (flag_quiet) return 0; if (flag_monitor || flag_monitor_continous) { clear(); refresh(); } nfprintf(stderr,"No command currently running: "); for (i = 0 ; proc_names[i] ; i++) { nfprintf(stderr,"%s, ", proc_names[i]); } nfprintf(stderr,"exiting.\n"); return 0; } result_count = 0; for (i = 0 ; i < pid_count ; i++) { fd_count = find_fd_for_pid(pidinfo_list[i].pid, fdnum_list, MAX_FD_PER_PID); max_size = 0; // let's find the biggest opened file for (j = 0 ; j < fd_count ; j++) { get_fdinfo(pidinfo_list[i].pid, fdnum_list[j], &fdinfo); if (fdinfo.size > max_size) { biggest_fd = fdinfo; max_size = fdinfo.size; } } if (!max_size) { // nothing found nprintf("[%5d] %s inactive/flushing/streaming/...\n", pidinfo_list[i].pid, pidinfo_list[i].name); continue; } // We've our biggest_fd now, let's store the result results[result_count].pid = pidinfo_list[i]; results[result_count].fd = biggest_fd; results[result_count].hbegin = NULL; results[result_count].hend = NULL; results[result_count].hsize = 0; result_count++; } // wait a bit, so we can estimate the throughput if (flag_throughput) usleep(1000000 * throughput_wait_secs); if (flag_monitor || flag_monitor_continous) { clear(); refresh(); } copy_and_clean_results(results, result_count, 1); for (i = 0 ; i < result_count ; i++) { if (flag_throughput) { still_there = get_fdinfo(results[i].pid.pid, results[i].fd.num, &fdinfo); if (still_there && strcmp(results[i].fd.name, fdinfo.name)) still_there = 0; // still there, but it's not the same file ! } else still_there = 0; if (!still_there) { // pid is no more here (or no throughput was asked), use initial info format_size(results[i].fd.pos, fpos); format_size(results[i].fd.size, fsize); perc = ((double)100 / (double)results[i].fd.size) * (double)results[i].fd.pos; } else { // use the newest info format_size(fdinfo.pos, fpos); format_size(fdinfo.size, fsize); perc = ((double)100 / (double)fdinfo.size) * (double)fdinfo.pos; } nprintf("[%5d] %s %s %.1f%% (%s / %s)", results[i].pid.pid, results[i].pid.name, results[i].fd.name, perc, fpos, fsize); if (flag_throughput && still_there) { // results[i] vs fdinfo long long usec_diff; off_t byte_diff; off_t bytes_per_sec; usec_diff = (fdinfo.tv.tv_sec - results[i].fd.tv.tv_sec) * 1000000L + (fdinfo.tv.tv_usec - results[i].fd.tv.tv_usec); byte_diff = fdinfo.pos - results[i].fd.pos; results[i].hsize += add_to_hlist(&results[i].hbegin, &results[i].hend, results[i].hsize, byte_diff / (usec_diff / 1000000.0)); bytes_per_sec = get_hlist_average(results[i].hbegin, results[i].hsize); format_size(bytes_per_sec, ftroughput); nprintf(" %s/s", ftroughput); if (bytes_per_sec && fdinfo.size - fdinfo.pos > 0) { print_eta((fdinfo.size - fdinfo.pos) / bytes_per_sec); } } nprintf("\n"); // Need to work on window width when using screen/watch/... //~ printf(" ["); //~ print_bar(perc, ws.ws_col-6); //~ printf("]\n"); } copy_and_clean_results(results, result_count, 0); return 0; }
int main(int argc, char *argv[]) { int pid_count, fd_count, result_count; int i,j; pidinfo_t pidinfo_list[MAX_PIDS]; fdinfo_t fdinfo; fdinfo_t biggest_fd; int fdnum_list[MAX_FD_PER_PID]; off_t max_size; char fsize[64]; char fpos[64]; char ftroughput[64]; struct winsize ws; float perc; result_t results[MAX_RESULTS]; signed char still_there; parse_options(argc,argv); // ws.ws_row, ws.ws_col ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); pid_count = 0; if(!proc_specifiq) { for(i = 0 ; proc_names[i] ; i++) { pid_count += find_pids_by_binary_name(proc_names[i], pidinfo_list + pid_count, MAX_PIDS - pid_count); if(pid_count >= MAX_PIDS) { fprintf(stderr, "Found too much procs (max = %d)\n",MAX_PIDS); break; } } } else { pid_count += find_pids_by_binary_name(proc_specifiq, pidinfo_list + pid_count, MAX_PIDS - pid_count); } if(!pid_count) { if(flag_quiet) return 0; fprintf(stderr,"No command currently running: "); for(i = 0 ; proc_names[i] ; i++) { fprintf(stderr,"%s, ", proc_names[i]); } fprintf(stderr,"exiting.\n"); return 0; } result_count = 0; for(i = 0 ; i < pid_count ; i++) { fd_count = find_fd_for_pid(pidinfo_list[i].pid, fdnum_list, MAX_FD_PER_PID); max_size = 0; // let's find the biggest opened file for(j = 0 ; j < fd_count ; j++) { get_fdinfo(pidinfo_list[i].pid, fdnum_list[j], &fdinfo); if(fdinfo.size > max_size) { biggest_fd = fdinfo; max_size = fdinfo.size; } } if(!max_size) { // nothing found printf("[%5d] %s inactive/flushing/streaming/...\n", pidinfo_list[i].pid, pidinfo_list[i].name); continue; } // We've our biggest_fd now, let's store the result results[result_count].pid = pidinfo_list[i]; results[result_count].fd = biggest_fd; result_count++; } // wait a bit, so we can estimate the throughput if (flag_throughput) usleep(1000000 * throughput_wait_secs); for (i = 0 ; i < result_count ; i++) { if (flag_throughput) { still_there = get_fdinfo(results[i].pid.pid, results[i].fd.num, &fdinfo); if (still_there && strcmp(results[i].fd.name, fdinfo.name)) still_there = 0; // still there, but it's not the same file ! } else still_there = 0; if (!still_there) { // pid is no more here (or no throughput was asked), use initial info format_size(results[i].fd.pos, fpos); format_size(results[i].fd.size, fsize); perc = ((double)100 / (double)results[i].fd.size) * (double)results[i].fd.pos; } else { // use the newest info format_size(fdinfo.pos, fpos); format_size(fdinfo.size, fsize); perc = ((double)100 / (double)fdinfo.size) * (double)fdinfo.pos; } printf("[%5d] %s %s %.1f%% (%s / %s)", results[i].pid.pid, results[i].pid.name, results[i].fd.name, perc, fpos, fsize); if (flag_throughput && still_there) { // results[i] vs fdinfo long long usec_diff; off_t byte_diff; off_t bytes_per_sec; usec_diff = (fdinfo.tv.tv_sec - results[i].fd.tv.tv_sec) * 1000000L + (fdinfo.tv.tv_usec - results[i].fd.tv.tv_usec); byte_diff = fdinfo.pos - results[i].fd.pos; bytes_per_sec = byte_diff / (usec_diff / 1000000.0); format_size(bytes_per_sec, ftroughput); printf(" %s/s", ftroughput); if (bytes_per_sec && fdinfo.size - fdinfo.pos > 0) { print_eta((fdinfo.size - fdinfo.pos) / bytes_per_sec); } } printf("\n"); // Need to work on window width when using screen/watch/... //~ printf(" ["); //~ print_bar(perc, ws.ws_col-6); //~ printf("]\n"); } return 0; }
/* Read from recv_fd and broadcast whatever is read to all other descriptors in read_fds, with the exception of stdin, listen_socket, and recv_fd itself. Handles EOL translation and chat mode. On read error or end of stream, closes the socket and removes it from the read_fds list. */ static void read_and_broadcast(int recv_fd) { struct fdinfo *fdn; int pending; fdn = get_fdinfo(&client_fdlist, recv_fd); ncat_assert(fdn != NULL); /* Loop while ncat_recv indicates data is pending. */ do { char buf[DEFAULT_TCP_BUF_LEN]; char *chatbuf, *outbuf; char *tempbuf = NULL; fd_set broadcastfds; int n; /* Behavior differs depending on whether this is stdin or a socket. */ if (recv_fd == STDIN_FILENO) { n = read(recv_fd, buf, sizeof(buf)); if (n <= 0) { if (n < 0 && o.verbose) logdebug("Error reading from stdin: %s\n", strerror(errno)); if (n == 0 && o.debug) logdebug("EOF on stdin\n"); /* Don't close the file because that allows a socket to be fd 0. */ FD_CLR(recv_fd, &master_readfds); /* But mark that we've seen EOF so it doesn't get re-added to the select list. */ stdin_eof = 1; return; } if (o.crlf) fix_line_endings((char *) buf, &n, &tempbuf, &crlf_state); pending = 0; } else { /* From a connected socket, not stdin. */ n = ncat_recv(fdn, buf, sizeof(buf), &pending); if (n <= 0) { if (o.debug) logdebug("Closing connection.\n"); #ifdef HAVE_OPENSSL if (o.ssl && fdn->ssl) { if (n == 0) SSL_shutdown(fdn->ssl); SSL_free(fdn->ssl); } #endif close(recv_fd); FD_CLR(recv_fd, &master_readfds); rm_fd(&client_fdlist, recv_fd); FD_CLR(recv_fd, &master_broadcastfds); rm_fd(&broadcast_fdlist, recv_fd); conn_inc--; if (conn_inc == 0) FD_CLR(STDIN_FILENO, &master_readfds); if (o.chat) chat_announce_disconnect(recv_fd); return; } } if (o.debug > 1) logdebug("Handling data from client %d.\n", recv_fd); chatbuf = NULL; /* tempbuf is in use if we read from STDIN and fixed EOL */ if (tempbuf == NULL) outbuf = buf; else outbuf = tempbuf; if (o.chat) { chatbuf = chat_filter(outbuf, n, recv_fd, &n); if (chatbuf == NULL) { if (o.verbose) logdebug("Error formatting chat message from fd %d\n", recv_fd); } else { outbuf = chatbuf; } } /* Send to everyone except the one who sent this message. */ broadcastfds = master_broadcastfds; FD_CLR(recv_fd, &broadcastfds); ncat_broadcast(&broadcastfds, &broadcast_fdlist, outbuf, n); free(chatbuf); free(tempbuf); tempbuf = NULL; } while (pending); }
static int ncat_listen_stream(int proto) { int rc, i, fds_ready; fd_set listen_fds; struct timeval tv; struct timeval *tvp = NULL; unsigned int num_sockets; /* clear out structs */ FD_ZERO(&master_readfds); FD_ZERO(&master_writefds); FD_ZERO(&master_broadcastfds); FD_ZERO(&listen_fds); #ifdef HAVE_OPENSSL FD_ZERO(&sslpending_fds); #endif zmem(&client_fdlist, sizeof(client_fdlist)); zmem(&broadcast_fdlist, sizeof(broadcast_fdlist)); #ifdef WIN32 set_pseudo_sigchld_handler(decrease_conn_count); #else /* Reap on SIGCHLD */ Signal(SIGCHLD, sigchld_handler); /* Ignore the SIGPIPE that occurs when a client disconnects suddenly and we send data to it before noticing. */ Signal(SIGPIPE, SIG_IGN); #endif #ifdef HAVE_OPENSSL if (o.ssl) setup_ssl_listen(); #endif /* Not sure if this problem exists on Windows, but fcntl and /dev/null don't */ #ifndef WIN32 /* Check whether stdin is closed. Because we treat this fd specially, we * can't risk it being reopened for an incoming connection, so we'll hold * it open instead. */ if (fcntl(STDIN_FILENO, F_GETFD) == -1 && errno == EBADF) { logdebug("stdin is closed, attempting to reserve STDIN_FILENO\n"); rc = open("/dev/null", O_RDONLY); if (rc >= 0 && rc != STDIN_FILENO) { /* Oh well, we tried */ logdebug("Couldn't reserve STDIN_FILENO\n"); close(rc); } } #endif /* We need a list of fds to keep current fdmax. The second parameter is a number added to the supplied connection limit, that will compensate maxfds for the added by default listen and stdin sockets. */ init_fdlist(&client_fdlist, sadd(o.conn_limit, num_listenaddrs + 1)); for (i = 0; i < NUM_LISTEN_ADDRS; i++) listen_socket[i] = -1; num_sockets = 0; for (i = 0; i < num_listenaddrs; i++) { /* setup the main listening socket */ listen_socket[num_sockets] = do_listen(SOCK_STREAM, proto, &listenaddrs[i]); if (listen_socket[num_sockets] == -1) { if (o.debug > 0) logdebug("do_listen(\"%s\"): %s\n", inet_ntop_ez(&listenaddrs[i].storage, sizeof(listenaddrs[i].storage)), socket_strerror(socket_errno())); continue; } /* Make our listening socket non-blocking because there are timing issues * which could cause us to block on accept() even though select() says it's * readable. See UNPv1 2nd ed, p422 for more. */ unblock_socket(listen_socket[num_sockets]); /* setup select sets and max fd */ FD_SET(listen_socket[num_sockets], &master_readfds); add_fd(&client_fdlist, listen_socket[num_sockets]); FD_SET(listen_socket[num_sockets], &listen_fds); num_sockets++; } if (num_sockets == 0) { if (num_listenaddrs == 1) bye("Unable to open listening socket on %s: %s", inet_ntop_ez(&listenaddrs[0].storage, sizeof(listenaddrs[0].storage)), socket_strerror(socket_errno())); else bye("Unable to open any listening sockets."); } add_fd(&client_fdlist, STDIN_FILENO); init_fdlist(&broadcast_fdlist, o.conn_limit); if (o.idletimeout > 0) tvp = &tv; while (1) { /* We pass these temporary descriptor sets to fselect, since fselect modifies the sets it receives. */ fd_set readfds = master_readfds, writefds = master_writefds; struct fdinfo *fdi = NULL; if (o.debug > 1) logdebug("selecting, fdmax %d\n", client_fdlist.fdmax); if (o.debug > 1 && o.broker) logdebug("Broker connection count is %d\n", get_conn_count()); if (o.idletimeout > 0) ms_to_timeval(tvp, o.idletimeout); fds_ready = fselect(client_fdlist.fdmax + 1, &readfds, &writefds, NULL, tvp); if (o.debug > 1) logdebug("select returned %d fds ready\n", fds_ready); if (fds_ready == 0) bye("Idle timeout expired (%d ms).", o.idletimeout); /* * FIXME: optimize this loop to look only at the fds in the fd list, * doing it this way means that if you have one descriptor that is very * large, say 500, and none close to it, that you'll loop many times for * nothing. */ for (i = 0; i <= client_fdlist.fdmax && fds_ready > 0; i++) { /* Loop through descriptors until there's something to read */ if (!FD_ISSET(i, &readfds) && !FD_ISSET(i, &writefds)) continue; if (o.debug > 1) logdebug("fd %d is ready\n", i); #ifdef HAVE_OPENSSL /* Is this an ssl socket pending a handshake? If so handle it. */ if (o.ssl && FD_ISSET(i, &sslpending_fds)) { FD_CLR(i, &master_readfds); FD_CLR(i, &master_writefds); fdi = get_fdinfo(&client_fdlist, i); ncat_assert(fdi != NULL); switch (ssl_handshake(fdi)) { case NCAT_SSL_HANDSHAKE_COMPLETED: /* Clear from sslpending_fds once ssl is established */ FD_CLR(i, &sslpending_fds); post_handle_connection(*fdi); break; case NCAT_SSL_HANDSHAKE_PENDING_WRITE: FD_SET(i, &master_writefds); break; case NCAT_SSL_HANDSHAKE_PENDING_READ: FD_SET(i, &master_readfds); break; case NCAT_SSL_HANDSHAKE_FAILED: default: SSL_free(fdi->ssl); Close(fdi->fd); FD_CLR(i, &sslpending_fds); FD_CLR(i, &master_readfds); rm_fd(&client_fdlist, i); /* Are we in single listening mode(without -k)? If so then we should quit also. */ if (!o.keepopen && !o.broker) return 1; --conn_inc; break; } } else #endif if (FD_ISSET(i, &listen_fds)) { /* we have a new connection request */ handle_connection(i); } else if (i == STDIN_FILENO) { if (o.broker) { read_and_broadcast(i); } else { /* Read from stdin and write to all clients. */ rc = read_stdin(); if (rc == 0) { if (o.proto != IPPROTO_TCP || (o.proto == IPPROTO_TCP && o.sendonly)) { /* There will be nothing more to send. If we're not receiving anything, we can quit here. */ return 0; } if (!o.noshutdown) shutdown_sockets(SHUT_WR); } if (rc < 0) return 1; } } else if (!o.sendonly) { if (o.broker) { read_and_broadcast(i); } else { /* Read from a client and write to stdout. */ rc = read_socket(i); if (rc <= 0 && !o.keepopen) return rc == 0 ? 0 : 1; } } fds_ready--; } } return 0; }
static int ncat_listen_stream(int proto) { int rc, i, fds_ready; fd_set listen_fds; /* clear out structs */ FD_ZERO(&master_readfds); FD_ZERO(&master_writefds); FD_ZERO(&master_broadcastfds); FD_ZERO(&listen_fds); #ifdef HAVE_OPENSSL FD_ZERO(&sslpending_fds); #endif zmem(&client_fdlist, sizeof(client_fdlist)); zmem(&broadcast_fdlist, sizeof(broadcast_fdlist)); #ifdef WIN32 set_pseudo_sigchld_handler(decrease_conn_count); #else /* Reap on SIGCHLD */ Signal(SIGCHLD, sigchld_handler); /* Ignore the SIGPIPE that occurs when a client disconnects suddenly and we send data to it before noticing. */ Signal(SIGPIPE, SIG_IGN); #endif #ifdef HAVE_OPENSSL if (o.ssl) setup_ssl_listen(); #endif /* We need a list of fds to keep current fdmax. The second parameter is a number added to the supplied connection limit, that will compensate maxfds for the added by default listen and stdin sockets. */ init_fdlist(&client_fdlist, sadd(o.conn_limit, num_listenaddrs + 1)); for (i = 0; i < NUM_LISTEN_ADDRS; i++) listen_socket[i] = -1; for (i = 0; i < num_listenaddrs; i++) { /* setup the main listening socket */ listen_socket[i] = do_listen(SOCK_STREAM, proto, &listenaddrs[i]); /* Make our listening socket non-blocking because there are timing issues * which could cause us to block on accept() even though select() says it's * readable. See UNPv1 2nd ed, p422 for more. */ unblock_socket(listen_socket[i]); /* setup select sets and max fd */ FD_SET(listen_socket[i], &master_readfds); add_fd(&client_fdlist, listen_socket[i]); FD_SET(listen_socket[i], &listen_fds); } add_fd(&client_fdlist, STDIN_FILENO); init_fdlist(&broadcast_fdlist, o.conn_limit); while (1) { /* We pass these temporary descriptor sets to fselect, since fselect modifies the sets it receives. */ fd_set readfds = master_readfds, writefds = master_writefds; struct fdinfo *fdi = NULL; if (o.debug > 1) logdebug("selecting, fdmax %d\n", client_fdlist.fdmax); if (o.debug > 1 && o.broker) logdebug("Broker connection count is %d\n", get_conn_count()); fds_ready = fselect(client_fdlist.fdmax + 1, &readfds, &writefds, NULL, NULL); if (o.debug > 1) logdebug("select returned %d fds ready\n", fds_ready); /* * FIXME: optimize this loop to look only at the fds in the fd list, * doing it this way means that if you have one descriptor that is very * large, say 500, and none close to it, that you'll loop many times for * nothing. */ for (i = 0; i <= client_fdlist.fdmax && fds_ready > 0; i++) { /* Loop through descriptors until there's something to read */ if (!FD_ISSET(i, &readfds) && !FD_ISSET(i, &writefds)) continue; if (o.debug > 1) logdebug("fd %d is ready\n", i); #ifdef HAVE_OPENSSL /* Is this an ssl socket pending a handshake? If so handle it. */ if (o.ssl && FD_ISSET(i, &sslpending_fds)) { FD_CLR(i, &master_readfds); FD_CLR(i, &master_writefds); fdi = get_fdinfo(&client_fdlist, i); switch(ssl_handshake(fdi)){ case NCAT_SSL_HANDSHAKE_COMPLETED: /* Clear from sslpending_fds once ssl is established */ FD_CLR(i, &sslpending_fds); rm_fd(&client_fdlist, i); post_handle_connection(*fdi); break; case NCAT_SSL_HANDSHAKE_PENDING_WRITE: FD_SET(i, &master_writefds); break; case NCAT_SSL_HANDSHAKE_PENDING_READ: FD_SET(i, &master_readfds); break; case NCAT_SSL_HANDSHAKE_FAILED: default: SSL_free(fdi->ssl); Close(fdi->fd); FD_CLR(i, &sslpending_fds); FD_CLR(i, &master_readfds); rm_fd(&client_fdlist, i); /* Are we in single listening mode(without -k)? If so then we should quit also. */ if (!o.keepopen && !o.broker) return 1; --conn_inc; break; } } else #endif if (FD_ISSET(i, &listen_fds)) { /* we have a new connection request */ handle_connection(i); } else if (i == STDIN_FILENO) { if(o.broker) { read_and_broadcast(i); }else { /* Read from stdin and write to all clients. */ rc = read_stdin(); if (rc == 0 && o.sendonly) /* There will be nothing more to send. If we're not receiving anything, we can quit here. */ return 0; if (rc < 0) return 1; } } else if (!o.sendonly) { if(o.broker) { read_and_broadcast(i); }else { /* Read from a client and write to stdout. */ rc = read_socket(i); if (rc <= 0 && !o.keepopen) return rc == 0 ? 0 : 1; } } fds_ready--; } } return 0; }