diff options
Diffstat (limited to 'daemons/shaper/src/shaper_daemon.c')
-rw-r--r-- | daemons/shaper/src/shaper_daemon.c | 986 |
1 files changed, 986 insertions, 0 deletions
diff --git a/daemons/shaper/src/shaper_daemon.c b/daemons/shaper/src/shaper_daemon.c new file mode 100644 index 00000000..1ef6a69c --- /dev/null +++ b/daemons/shaper/src/shaper_daemon.c @@ -0,0 +1,986 @@ +/************************************************************************************************************* +Copyright (c) 2016-2017, Harman International Industries, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS LISTED BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************************************************/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <ctype.h> +#include <net/if.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> + +#define SHAPER_LOG_COMPONENT "Main" +#include "shaper_log.h" + +#define STREAMDA_LENGTH 18 +#define SHAPER_PORT 15365 /* Unassigned at https://www.iana.org/assignments/port-numbers */ +#define MAX_CLIENT_CONNECTIONS 10 +#define USER_COMMAND_PROMPT "\nEnter the command: " + +typedef struct cmd_ip +{ + int reserve_bw; + int unreserve_bw; + char interface[IFNAMSIZ]; + int class_a, class_b; + int measurement_interval;//usec + int max_frame_size; + int max_frame_interval; + char stream_da[STREAMDA_LENGTH]; + int delete_qdisc; + int quit; +} cmd_ip; + +typedef struct stream_da +{ + char dest_addr[STREAMDA_LENGTH]; + int bandwidth; + char class_id[5]; + int filter_handle; + struct stream_da *next; +} stream_da; + +stream_da *head = NULL; +int sr_classa=0, sr_classb=0; +int classa_48=0, classa_44=0, classb_48=0, classb_44=0; +int classa_bw_48=0, classa_bw_44=0, classb_bw_48=0, classb_bw_44=0; +int daemonize=0,c=0; +int filterhandle_classa=1,filterhandle_classb=20; +char classid_a_48[]="2:10"; +char classid_a_44[]="2:20"; +char classid_b_48[]="3:30"; +char classid_b_44[]="3:40"; +char interface[IFNAMSIZ] = {0}; +int bandwidth = 0; +int classa_parent = 2, classb_parent=3; +int exit_received = 0; + +static void signal_handler(int signal) +{ + if (signal == SIGINT || signal == SIGTERM) { + if (!exit_received) { + SHAPER_LOG_INFO("Shutdown signal received"); + exit_received = 1; + } + else { + // Force shutdown + exit(2); + } + } + else if (signal == SIGUSR1) { + SHAPER_LOG_DEBUG("Waking up streaming thread"); + } + else { + SHAPER_LOG_ERROR("Unexpected signal"); + } +} + +void log_client_error_message(int sockfd, const char *fmt, ...) +{ + static char error_msg[200], combined_error_msg[250]; + va_list args; + + if (SHAPER_LOG_LEVEL_ERROR > SHAPER_LOG_LEVEL) + { + return; + } + + /* Get the error message. */ + va_start(args, fmt); + vsprintf(error_msg, fmt, args); + va_end(args); + + if (sockfd < 0) + { + /* Received from stdin. Just log this error. */ + SHAPER_LOG_ERROR(error_msg); + } + else + { + /* Log this as a remote client error. */ + sprintf(combined_error_msg, "Client %d: %s", sockfd, error_msg); + SHAPER_LOG_ERROR(combined_error_msg); + + /* Send the error message to the client. */ + sprintf(combined_error_msg, "ERROR: %s\n", error_msg); + send(sockfd, combined_error_msg, strlen(combined_error_msg), 0); + } +} + +void log_client_debug_message(int sockfd, const char *fmt, ...) +{ + static char debug_msg[200], combined_debug_msg[250]; + va_list args; + + if (SHAPER_LOG_LEVEL_DEBUG > SHAPER_LOG_LEVEL) + { + return; + } + + /* Get the debug message. */ + va_start(args, fmt); + vsprintf(debug_msg, fmt, args); + va_end(args); + + if (sockfd < 0) + { + /* Received from stdin. Just log this message. */ + SHAPER_LOG_DEBUG(debug_msg); + } + else + { + /* Log this as a remote client message. */ + sprintf(combined_debug_msg, "Client %d: %s", sockfd, debug_msg); + SHAPER_LOG_DEBUG(combined_debug_msg); + + /* Send the message to the client. */ + sprintf(combined_debug_msg, "DEBUG: %s\n", debug_msg); + send(sockfd, combined_debug_msg, strlen(combined_debug_msg), 0); + } +} + +int is_empty() +{ + return head == NULL; +} + +void insert_stream_da(int sockfd, char dest_addr[], int bandwidth, char class_id[], int filter_handle) +{ + stream_da *node = (stream_da *)malloc(sizeof(stream_da)); + if (node == NULL) + { + log_client_error_message(sockfd, "Unable to allocate memory. Exiting program"); + shaperLogExit(); + exit(1); + } + strcpy(node->dest_addr,dest_addr); + node->bandwidth = bandwidth; + strcpy(node->class_id,class_id); + node->filter_handle=filter_handle; + node->next = NULL; + if (is_empty()) + { + head = node; + } + else + { + stream_da *current = head; + while (current->next != NULL) + { + current = current->next; + } + current->next = node; + } +} + +int check_stream_da(int sockfd, char dest_addr[]) +{ + stream_da *current = head; + while (current != NULL) + { + if (!strcmp(current->dest_addr, dest_addr)) + { + log_client_error_message(sockfd, "Stream DA already present"); + return 1; + } + current = current->next; + } + return 0; +} + +stream_da* get_stream_da(int sockfd, char dest_addr[]) +{ + stream_da *current = head; + while (current != NULL) + { + if (!strcmp(current->dest_addr, dest_addr)) + { + + return current; + } + current = current->next; + } + log_client_error_message(sockfd, "Unknown Stream DA"); + return NULL; +} + +void remove_stream_da(int sockfd, char dest_addr[]) +{ + stream_da *current = head; + (void) sockfd; + + if (current != NULL && !strcmp(current->dest_addr, dest_addr)) + { + head = current->next; + free(current); + return; + } + + while (current->next != NULL) + { + if (!strcmp(current->next->dest_addr, dest_addr)) + { + stream_da *temp = current->next; + current->next = current->next->next; + free(temp); + return; + } + current = current->next; + } +} + +void delete_streamda_list() +{ + stream_da *current = head; + stream_da *next = NULL; + while (current != NULL) + { + next = current->next; + free(current); + current = next; + } + head = NULL; +} + +void usage (int sockfd) +{ + const char *usage = "Usage:\n" + " -r Reserve Bandwidth\n" + " -u Unreserve Bandwidth\n" + " -i Interface\n" + " -c Class ('A' or 'B')\n" + " -s Measurement interval (in microseconds)\n" + " -b Maximum frame size (in bytes)\n" + " -f Maximum frame interval\n" + " -a Stream Destination Address\n" + " -d Delete qdisc\n" + " -q Quit Application\n" + "Reserving Bandwidth Example:\n" + " -ri eth2 -c A -s 125 -b 74 -f 1 -a ff:ff:ff:ff:ff:11\n" + "Unreserving Bandwidth Example:\n" + " -ua ff:ff:ff:ff:ff:11\n" + "Quit Example:\n" + " -q\n" + "Delete qdisc Example:\n" + " -d\n"; + if (sockfd < 0) + { + printf("%s",usage); + } + else + { + send(sockfd, usage, strlen(usage),0); + } + +} + +cmd_ip parse_cmd(char command[]) +{ + cmd_ip inputs={0}; + int i=0; + char temp[100]; + while(command[i++] != '\0') + { + + if (command[i] == 'r') + { + inputs.reserve_bw=1; + i++; + } + + if (command[i] == 'u') + { + inputs.unreserve_bw=1; + i++; + } + + if (command[i] == 'i') + { + int k=0; + i = i+2; + while (command[i] != ' ' && k < IFNAMSIZ-1) + { + inputs.interface[k] = command[i]; + k++;i++; + } + inputs.interface[k] = '\0'; + } + + if (command[i] == 'c') + { + i = i+2; + if (command[i] == 'A' || command[i] == 'a') + { + inputs.class_a = 1; + } + else if (command[i] == 'B' || command[i] == 'b') + { + inputs.class_b = 1; + } + i++; + } + + if (command[i] == 's') + { + int k=0; + i = i+2; + while (command[i] != ' ' && k < (int) sizeof(temp)-1) + { + temp[k] = command[i]; + k++;i++; + } + temp[k] = '\0'; + inputs.measurement_interval = atoi(temp); + } + + if (command[i] == 'b') + { + int k=0; + i = i+2; + while (command[i] != ' ' && k < (int) sizeof(temp)-1) + { + temp[k] = command[i]; + k++;i++; + } + temp[k] = '\0'; + inputs.max_frame_size = atoi(temp); + } + + if (command[i] == 'f') + { + int k=0; + i = i+2; + while (command[i] != ' ' && k < (int) sizeof(temp)-1) + { + temp[k] = command[i]; + k++;i++; + } + temp[k] = '\0'; + inputs.max_frame_interval = atoi(temp); + } + + if (command[i] == 'a') + { + int k=0; + i = i+2; + while (command[i] != ' ' && k < STREAMDA_LENGTH-1) + { + inputs.stream_da[k] = command[i]; + k++;i++; + } + inputs.stream_da[k] = '\0'; + } + + if (command[i] == 'd') + { + inputs.delete_qdisc=1; + i++; + } + + if (command[i] == 'q') + { + inputs.quit = 1; + i++; + } + } + return inputs; +} + +void tc_class_command(int sockfd, char command[], char class_id[], char interface[], int bandwidth, int cburst) +{ + char tc_command[1000]={0}; + sprintf(tc_command, "tc class %s dev %s classid %s htb rate %dbps cburst %d", + command, interface, class_id, bandwidth, cburst); + log_client_debug_message(sockfd, "tc command: \"%s\"", tc_command); + if (system(tc_command) < 0) + { + log_client_error_message(sockfd, "command(\"%s\") failed", tc_command); + } +} + +void add_filter(int sockfd, char interface[], int parent, int filter_handle, char class_id[], char dest_addr[]) +{ + char tc_command[1000]={0}; + sprintf(tc_command, "tc filter add dev %s prio 1 handle 800::%d parent %d: u32 classid %s match ether dst %s", + interface, filter_handle, parent, class_id, dest_addr); + log_client_debug_message(sockfd, "tc command: \"%s\"", tc_command); + if (system(tc_command) < 0) + { + log_client_error_message(sockfd, "command(\"%s\") failed", tc_command); + } +} + +// Returns 1 if successful, -1 on an error, or 0 if exit requested. +int process_command(int sockfd, char command[]) +{ + cmd_ip input = parse_cmd(command); + char tc_command[1000]={0}; + int maxburst = 0; + + if (input.reserve_bw && input.unreserve_bw) + { + log_client_error_message(sockfd, "Cannot reserve and unreserve bandwidth at the same time"); + usage(sockfd); + return -1; + } + + + if (input.reserve_bw) + { + if (input.max_frame_size == 0 || input.measurement_interval == 0 || input.max_frame_interval == 0 || + (!input.class_a && !input.class_b)) + { + log_client_error_message(sockfd, "class, max_frame_size, measurement_interval and max_frame_interval are mandatory inputs"); + usage(sockfd); + return -1; + } + + if (input.class_a && input.class_b) + { + log_client_error_message(sockfd, "Cannot reserve for both class A and class B"); + usage(sockfd); + return -1; + } + } + if (input.unreserve_bw) + { + if (input.stream_da == 0) + { + log_client_error_message(sockfd, "Stream Destination Address is required to unreserve bandwidth"); + usage(sockfd); + return -1; + } + } + if ((input.quit==1 || input.delete_qdisc==1) &&(input.reserve_bw==1 || input.unreserve_bw==1)) + { + log_client_error_message(sockfd, "Quit or Delete cannot be used with other commands"); + usage(sockfd); + return -1; + } + + if (input.quit == 1 || input.delete_qdisc == 1) + { + if (sr_classa != 0 || sr_classb != 0) + { + //delete all the Stream DAs in list + delete_streamda_list(); + if (strlen(interface) != 0) + { + //delete qdisc + sprintf(tc_command, "tc qdisc del dev %s root handle 1:", interface); + log_client_debug_message(sockfd, "tc command: \"%s\"", tc_command); + if (system(tc_command) < 0) + { + log_client_error_message(sockfd, "command(\"%s\") failed", tc_command); + } + } + sr_classa = sr_classb = 0; + classa_48 = classa_44 = classb_48 = classb_44 = 0; + classa_bw_48 = classa_bw_44 = classb_bw_48 = classb_bw_44 = 0; + } + + if (input.quit == 1) + { + return 0; + } + } + + if (sr_classa == 0 && sr_classb == 0) + { + //Initializing qdisc + if (strlen(input.interface)==0) + { + if (input.unreserve_bw || input.reserve_bw) + { + log_client_error_message(sockfd, "Interface is mandatory for the first command"); + } + usage(sockfd); + return -1; + } + sprintf(tc_command, "tc qdisc add dev %s root handle 1: mqprio num_tc 4 map 3 3 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 1@2 1@3 hw 0", input.interface); + log_client_debug_message(sockfd, "tc command: \"%s\"", tc_command); + if (system(tc_command) < 0) + { + log_client_error_message(sockfd, "command(\"%s\") failed", tc_command); + return -1; + } + strcpy(interface,input.interface); + } + + if (input.unreserve_bw || input.reserve_bw) + { + bandwidth = ceil(((1/(input.measurement_interval*pow(10,-6))) * (input.max_frame_size*8) * input.max_frame_interval) / 8); + maxburst = input.max_frame_size*input.max_frame_interval*2; + } + + if (input.reserve_bw == 1) + { + if (check_stream_da(sockfd, input.stream_da)) + { + return 1; + } + if (input.class_a) + { + if (sr_classa == 0) + { + sr_classa = 1; + //Create qdisc for Class A traffic + sprintf(tc_command, "tc qdisc add dev %s handle %d: parent 1:5 htb", interface, classa_parent); + log_client_debug_message(sockfd, "tc command: \"%s\"", tc_command); + if (system(tc_command) < 0) + { + log_client_error_message(sockfd, "command(\"%s\") failed", tc_command); + return -1; + } + } + + if (input.measurement_interval == 125) + { + classa_bw_48 = classa_bw_48 + bandwidth; + if (classa_48 == 0) + { + classa_48 = 1; + tc_class_command(sockfd, "add", classid_a_48, interface, classa_bw_48, maxburst); + } + else + { + tc_class_command(sockfd, "change", classid_a_48, interface, classa_bw_48, maxburst); + } + + add_filter(sockfd, interface, classa_parent, filterhandle_classa, classid_a_48, input.stream_da); + insert_stream_da(sockfd, input.stream_da, bandwidth, classid_a_48, filterhandle_classa ); + filterhandle_classa++; + } + else if (input.measurement_interval == 136) + { + classa_bw_44 = classa_bw_44 + bandwidth; + if (classa_44 == 0) + { + classa_44 = 1; + tc_class_command(sockfd, "add", classid_a_44, interface, classa_bw_44, maxburst); + } + else + { + tc_class_command(sockfd, "change", classid_a_44, interface, classa_bw_44, maxburst); + } + + add_filter(sockfd, interface, classa_parent, filterhandle_classa, classid_a_44, input.stream_da); + insert_stream_da(sockfd, input.stream_da, bandwidth, classid_a_44, filterhandle_classa); + filterhandle_classa++; + } + else + { + log_client_error_message(sockfd, "Measurement Interval (%d) doesn't match that of Class A (125 or 136) traffic. " + "Enter a valid measurement interval", + input.measurement_interval); + return -1; + } + } + else + { + if (sr_classb == 0) + { + sr_classb = 1; + //Create qdisc for Class B traffic + sprintf(tc_command, "tc qdisc add dev %s handle %d: parent 1:6 htb", interface, classb_parent); + log_client_debug_message(sockfd, "tc command: \"%s\"", tc_command); + if (system(tc_command) < 0) + { + log_client_error_message(sockfd, "command(\"%s\") failed", tc_command); + return -1; + } + } + + if (input.measurement_interval == 250) + { + classb_bw_48 = classb_bw_48 + bandwidth; + if (classb_48 == 0) + { + classb_48 = 1; + tc_class_command(sockfd, "add", classid_b_48, interface, classb_bw_48, maxburst); + } + else + { + tc_class_command(sockfd, "change", classid_b_48, interface, classb_bw_48, maxburst); + } + add_filter(sockfd, interface, classb_parent, filterhandle_classb, classid_b_48, input.stream_da); + insert_stream_da(sockfd, input.stream_da, bandwidth, classid_b_48, filterhandle_classb); + filterhandle_classb++; + } + else if (input.measurement_interval == 272) + { + classb_bw_44 = classb_bw_44 + bandwidth; + if (classb_44 == 0) + { + classb_44 = 1; + tc_class_command(sockfd, "add", classid_b_44, interface, classb_bw_44, maxburst); + } + else + { + tc_class_command(sockfd, "change", classid_b_44, interface, classb_bw_44, maxburst); + } + add_filter(sockfd, interface, classb_parent, filterhandle_classb, classid_b_44, input.stream_da); + insert_stream_da(sockfd, input.stream_da, bandwidth, classid_b_44, filterhandle_classb); + filterhandle_classb++; + } + else + { + log_client_error_message(sockfd, "Measurement Interval (%d) doesn't match that of Class B (250 or 272) traffic. " + "Enter a valid measurement interval", + input.measurement_interval); + return -1; + } + } + } + else if (input.unreserve_bw==1) + { + stream_da *remove_stream = get_stream_da(sockfd, input.stream_da); + if (remove_stream != NULL) + { + int class_bw = 0; + if (!strcmp(remove_stream->class_id, classid_a_48)) + { + classa_bw_48 = classa_bw_48 - remove_stream->bandwidth; + class_bw = classa_bw_48; + } + else if (!strcmp(remove_stream->class_id, classid_a_44)) + { + classa_bw_44 = classa_bw_44 - remove_stream->bandwidth; + class_bw = classa_bw_44; + } + else if (!strcmp(remove_stream->class_id, classid_b_48)) + { + classb_bw_48 = classb_bw_48 - remove_stream->bandwidth; + class_bw = classb_bw_48; + } + else if (!strcmp(remove_stream->class_id, classid_b_44)) + { + classb_bw_44 = classb_bw_44 - remove_stream->bandwidth; + class_bw = classb_bw_44; + } + if (class_bw == 0) + { + class_bw = 1; + } + tc_class_command(sockfd, "change", remove_stream->class_id, interface, class_bw, maxburst); + char parent[3] = {0}; + strncpy(parent,remove_stream->class_id,2); + sprintf(tc_command, "tc filter del dev %s parent %s handle 800::%d prio 1 protocol all u32", + interface, parent, remove_stream->filter_handle); + log_client_debug_message(sockfd, "tc command: \"%s\"", tc_command); + if (system(tc_command) < 0) + { + log_client_error_message(sockfd, "command(\"%s\") failed", tc_command); + return -1; + } + remove_stream_da(sockfd, remove_stream->dest_addr); + } + } + + return 1; +} + +int init_socket() +{ + int socketfd = 0; + struct sockaddr_in serv_addr; + int yes=1; + if ((socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) + { + SHAPER_LOGF_ERROR("Could not open socket %d. Error %d (%s)", socketfd, errno, strerror(errno)); + return -1; + } + if (fcntl(socketfd, F_SETFL, O_NONBLOCK) < 0) + { + SHAPER_LOGF_ERROR("Could not set the socket to non-blocking. Error %d (%s)", errno, strerror(errno)); + close(socketfd); + return -1; + } + + + 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(SHAPER_PORT); + setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + + if (bind(socketfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))<0) + { + SHAPER_LOGF_ERROR("bind() error %d (%s)", errno, strerror(errno)); + return -1; + } + if (listen(socketfd, 10)<0) + { + SHAPER_LOGF_ERROR("listen() error %d (%s)", errno, strerror(errno)); + return -1; + } + + return socketfd; + +} + +int main (int argc, char *argv[]) +{ + char command[100]; + int socketfd = 0,newfd = 0; + int clientfd[MAX_CLIENT_CONNECTIONS]; + int i, nextclientindex; + fd_set read_fds; + int fdmax; + int recvbytes; + + shaperLogInit(); + + while((c = getopt(argc,argv,"d"))>=0) + { + switch(c) + { + case 'd': + daemonize = 1; + break; + } + } + + if (daemonize == 1 && daemon(1,0) < 0) + { + SHAPER_LOGF_ERROR("Error %d (%s) starting the daemon", errno, strerror(errno)); + shaperLogExit(); + return 1; + } + + if ((socketfd = init_socket())<0) + { + shaperLogExit(); + return 1; + } + + // Setup signal handler + // We catch SIGINT and shutdown cleanly + int err; + struct sigaction sa; + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + err = sigaction(SIGINT, &sa, NULL); + if (err) + { + SHAPER_LOG_ERROR("Failed to setup SIGINT handler"); + shaperLogExit(); + return 1; + } + err = sigaction(SIGTERM, &sa, NULL); + if (err) + { + SHAPER_LOG_ERROR("Failed to setup SIGTERM handler"); + shaperLogExit(); + return 1; + } + err = sigaction(SIGUSR1, &sa, NULL); + if (err) + { + SHAPER_LOG_ERROR("Failed to setup SIGUSR1 handler"); + shaperLogExit(); + return 1; + } + + // Ignore SIGPIPE signals. + signal(SIGPIPE, SIG_IGN); + + for (i = 0; i < MAX_CLIENT_CONNECTIONS; ++i) + { + clientfd[i] = -1; + } + nextclientindex = 0; + + fputs(USER_COMMAND_PROMPT, stdout); + fflush(stdout); + + while (!exit_received) + { + FD_ZERO(&read_fds); + if (!daemonize) + { + FD_SET(STDIN_FILENO, &read_fds); + } + FD_SET(socketfd, &read_fds); + fdmax = socketfd; + for (i = 0; i < MAX_CLIENT_CONNECTIONS; ++i) + { + if (clientfd[i] > 0) + { + FD_SET(clientfd[i], &read_fds); + if (clientfd[i] > fdmax) + { + fdmax = clientfd[i]; + } + } + } + int n = select(fdmax+1, &read_fds, NULL, NULL, NULL); + if(n == -1) + { + if (exit_received) + { + // Assume the app received a signal to quit. + // Process the quit command. + process_command(-1, "-q"); + } + else + { + SHAPER_LOGF_ERROR("select() error %d (%s)", errno, strerror(errno)); + break; + } + } + else + { + /* Handle any commands received via stdin. */ + if (FD_ISSET(STDIN_FILENO, &read_fds)) + { + recvbytes = read(STDIN_FILENO, command, sizeof(command) - 1); + if (recvbytes <= 0) + { + SHAPER_LOGF_ERROR("Error %d reading from stdin (%s)", errno, strerror(errno)); + } + else + { + command[recvbytes] = '\0'; + + /* Process the command data. */ + int ret = process_command(-1, command); + if (!ret) + { + /* Received a command to exit. */ + exit_received = 1; + } + else + { + /* Prompt the user again. */ + shaperLogDisplayAll(); + fputs(USER_COMMAND_PROMPT, stdout); + fflush(stdout); + } + } + } + + if (FD_ISSET(socketfd, &read_fds)) + { + newfd = accept(socketfd, (struct sockaddr*)NULL, NULL); + if (clientfd[nextclientindex] != -1) + { + /* Find the next available index. */ + for (i = (nextclientindex + 1) % MAX_CLIENT_CONNECTIONS; i != nextclientindex; i = (i + 1) % MAX_CLIENT_CONNECTIONS) + { + if (clientfd[nextclientindex] == -1) + { + /* Found an empty array slot. */ + break; + } + } + if (i == nextclientindex) + { + /* No more client slots available. Connection rejected. */ + SHAPER_LOG_WARNING("Out of client connection slots. Connection rejected."); + close(newfd); + newfd = -1; + } + } + + + if (newfd != -1) + { + clientfd[nextclientindex] = newfd; + nextclientindex = (nextclientindex + 1) % MAX_CLIENT_CONNECTIONS; /* Next slot used for the next try. */ + + /* Send a prompt to the user. */ + send(newfd, USER_COMMAND_PROMPT, strlen(USER_COMMAND_PROMPT), 0); + } + } + + for (i = 0; i < MAX_CLIENT_CONNECTIONS; ++i) + { + if (clientfd[i] != -1 && FD_ISSET(clientfd[i], &read_fds)) + { + recvbytes = recv(clientfd[i], command, sizeof(command) - 1, 0); + if (recvbytes < 0) + { + SHAPER_LOGF_ERROR("Error %d reading from socket %d (%s). Connection closed.", errno, clientfd[i], strerror(errno)); + close(clientfd[i]); + clientfd[i] = -1; + nextclientindex = i; /* We know this slot will be empty. */ + continue; + } + if (recvbytes == 0) + { + SHAPER_LOGF_INFO("Socket %d closed", clientfd[i]); + close(clientfd[i]); + clientfd[i] = -1; + nextclientindex = i; /* We know this slot will be empty. */ + continue; + } + + command[recvbytes] = '\0'; + while (recvbytes > 0 && isspace(command[recvbytes - 1])) + { + /* Remove trailing whitespace. */ + command[--recvbytes] = '\0'; + } + SHAPER_LOGF_INFO("The received command is \"%s\"",command); + int ret = process_command(clientfd[i], command); + if (!ret) + { + /* Received a command to exit. */ + exit_received = 1; + } + else + { + /* Send another prompt to the user. */ + send(clientfd[i], USER_COMMAND_PROMPT, strlen(USER_COMMAND_PROMPT), 0); + } + } + } + } + }//main while loop + + close(socketfd); + + /* Close any connected sockets. */ + for (i = 0; i < MAX_CLIENT_CONNECTIONS; ++i) { + if (clientfd[i] != -1) { + SHAPER_LOGF_INFO("Socket %d closed", clientfd[i]); + close(clientfd[i]); + clientfd[i] = -1; + } + } + + shaperLogExit(); + + return 0; +} |