diff --git a/ChangeLog b/ChangeLog index dd24fdd..6151a19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +0.2.6: +- Better "!uptime" command formatting. +- Better "!stats"; can display peak and current bandwidth usage. +- Added "+myip" command. +- Ensure super users and hub owners also have the operator flag set. +- Bug #1: Better dynamic send queue handling for large hubs at the cost of more memory use. +- Bug #5: Always provide IP-address to all users, not just for active clients. + + 0.2.5-3487: - Fixed out of memory situations, used when uhub operates on a limited heap. - Code cleanups, reduced heap memory footprint. diff --git a/src/adcconst.h b/src/adcconst.h index 3c7e8f2..35d6564 100644 --- a/src/adcconst.h +++ b/src/adcconst.h @@ -134,8 +134,8 @@ typedef uint32_t fourcc_t; #define ADC_CLIENT_TYPE_BOT "1" #define ADC_CLIENT_TYPE_REGISTERED_USER "2" #define ADC_CLIENT_TYPE_OPERATOR "4" -#define ADC_CLIENT_TYPE_SUPER_USER "8" -#define ADC_CLIENT_TYPE_ADMIN "16" /* hub owner */ +#define ADC_CLIENT_TYPE_SUPER_USER "12" /* 8 + 4 */ +#define ADC_CLIENT_TYPE_ADMIN "20" /* 16 + 4 = hub owner */ #define ADC_CLIENT_TYPE_HUB "32" /* the hub itself */ diff --git a/src/auth.c b/src/auth.c index ab70c18..2b4b7c3 100644 --- a/src/auth.c +++ b/src/auth.c @@ -53,7 +53,7 @@ static int check_cmd_bool(const char* cmd, struct linked_list* list, char* line, data++; data = strip_white_space(data); - if (!strlen(data)) + if (!*data) { hub_log(log_fatal, "ACL parse error on line %d", line_count); return -1; @@ -80,7 +80,7 @@ static int check_cmd_user(const char* cmd, int status, struct linked_list* list, data++; data = strip_white_space(data); - if (!strlen(data)) + if (!*data) { hub_log(log_fatal, "ACL parse error on line %d", line_count); return -1; @@ -192,7 +192,7 @@ static int check_cmd_addr(const char* cmd, struct linked_list* list, char* line, data1++; data1 = strip_white_space(data1); - if (!strlen(data1)) + if (!*data1) { hub_log(log_fatal, "ACL parse error on line %d", line_count); return -1; @@ -266,7 +266,7 @@ static int acl_parse_line(char* line, int line_count, void* ptr_data) pos[0] = 0; } - if (strlen(line) == 0) + if (!*line) return 0; #ifdef ACL_DEBUG @@ -274,7 +274,7 @@ static int acl_parse_line(char* line, int line_count, void* ptr_data) #endif line = strip_white_space(line); - if (!strlen(line)) + if (!*line) { hub_log(log_fatal, "ACL parse error on line %d", line_count); return -1; @@ -328,7 +328,7 @@ int acl_initialize(struct hub_config* config, struct acl_handle* handle) if (config) { - if (strlen(config->file_acl) == 0) return 0; + if (!*config->file_acl) return 0; ret = file_read_lines(config->file_acl, handle, &acl_parse_line); if (ret == -1) diff --git a/src/commands.c b/src/commands.c index 3c21d74..2329139 100644 --- a/src/commands.c +++ b/src/commands.c @@ -35,12 +35,20 @@ static int command_access_denied(struct user* user) static int command_stats(struct user* user, const char* message) { struct adc_message* command; - + if (user->credentials < cred_super) return command_access_denied(user); - - char temp[64]; - snprintf(temp, 64, "*** Stats: %u users, peak %u", (unsigned int) user->hub->users->count, (unsigned int) user->hub->users->count_peak); + + char temp[128]; + + snprintf(temp, 128, "*** Stats: %zu users, peak: %zu. Network (up/down): %d/%d KB/s, peak: %d/%d KB/s", + user->hub->users->count, + user->hub->users->count_peak, + (int) user->hub->stats.net_tx / 1024, + (int) user->hub->stats.net_rx / 1024, + (int) user->hub->stats.net_tx_peak / 1024, + (int) user->hub->stats.net_rx_peak / 1024); + char* buffer = adc_msg_escape(temp); command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); adc_msg_add_argument(command, buffer); @@ -54,7 +62,7 @@ static int command_stats(struct user* user, const char* message) static int command_help(struct user* user, const char* message) { struct adc_message* command; - char* buffer = adc_msg_escape( + char* buffer = adc_msg_escape("\n" "*** Available commands:\n" "!help - Show this help message\n" "!stats - Show hub stats (super)\n" @@ -74,9 +82,36 @@ static int command_help(struct user* user, const char* message) static int command_uptime(struct user* user, const char* message) { struct adc_message* command; - char temp[64]; - snprintf(temp, 64, "*** Uptime: %s seconds", uhub_itoa((int) difftime(time(0), user->hub->tm_started))); - char* buffer = adc_msg_escape(temp); + char tmp[128]; + size_t d; + size_t h; + size_t m; + size_t D = (size_t) difftime(time(0), user->hub->tm_started); + + d = D / (24 * 3600); + D = D % (24 * 3600); + h = D / 3600; + D = D % 3600; + m = D / 60; + + tmp[0] = 0; + strcat(tmp, "*** Uptime: "); + + if (d) + { + strcat(tmp, uhub_itoa((int) d)); + strcat(tmp, " day"); + if (d != 1) strcat(tmp, "s"); + strcat(tmp, ", "); + } + + if (h < 10) strcat(tmp, "0"); + strcat(tmp, uhub_itoa((int) h)); + strcat(tmp, ":"); + if (m < 10) strcat(tmp, "0"); + strcat(tmp, uhub_itoa((int) m)); + + char* buffer = adc_msg_escape(tmp); command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); adc_msg_add_argument(command, buffer); route_to_user(user, command); @@ -113,6 +148,26 @@ static int command_version(struct user* user, const char* message) return 0; } +static int command_myip(struct user* user, const char* message) +{ + struct adc_message* command; + char tmp[128]; + char* buffer; + + tmp[0] = 0; + strcat(tmp, "*** Your IP: "); + strcat(tmp, ip_convert_to_string(&user->ipaddr)); + + buffer = adc_msg_escape(tmp); + command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); + adc_msg_add_argument(command, buffer); + route_to_user(user, command); + adc_msg_free(command); + hub_free(buffer); + return 0; +} + + int command_dipatcher(struct user* user, const char* message) { if (!strncmp(message, "!stats", 6)) command_stats(user, message); @@ -120,6 +175,7 @@ int command_dipatcher(struct user* user, const char* message) else if (!strncmp(message, "!kick", 5)) command_kick(user, message); else if (!strncmp(message, "!version", 8)) command_version(user, message); else if (!strncmp(message, "!uptime", 7)) command_uptime(user, message); + else if (!strncmp(message, "+myip", 5)) command_myip(user, message); else return 1; return 0; diff --git a/src/config.c b/src/config.c index e4be43a..8e61ff8 100644 --- a/src/config.c +++ b/src/config.c @@ -64,7 +64,7 @@ int val; \ errno = 0; \ val = strtol(data, &endptr, 10); \ - if (((errno == ERANGE && (val == INT_MAX || val == INT_MIN)) || (errno != 0 && val == 0)) || endptr == data /*|| endptr != &data[strlen(data)-1]*/) { \ + if (((errno == ERANGE && (val == INT_MAX || val == INT_MIN)) || (errno != 0 && val == 0)) || endptr == data) { \ hub_log(log_fatal, "Configuration error on line %d: '%s' must be a number", line_count, key); \ return -1; \ } \ @@ -458,7 +458,7 @@ static int config_parse_line(char* line, int line_count, void* ptr_data) pos[0] = 0; } - if (strlen(line) == 0) return 0; + if (!*line) return 0; #ifdef CONFIG_DUMP hub_log(log_trace, "config_parse_line(): '%s'", line); @@ -479,7 +479,7 @@ static int config_parse_line(char* line, int line_count, void* ptr_data) key = strip_white_space(key); data = strip_white_space(data); - if (!strlen(key) || !strlen(data)) + if (!*key || !*data) { hub_log(log_fatal, "Configuration parse error on line %d", line_count); return -1; diff --git a/src/config.h b/src/config.h index 1414711..d9a9659 100644 --- a/src/config.h +++ b/src/config.h @@ -35,8 +35,8 @@ struct hub_config char* hub_name; /**<<< "Name of hub (default: 'My uhub hub')" */ char* hub_description; /**<<< "Name of hub (default: 'no description')" */ int max_recv_buffer; /**<<< "Max read buffer before parse, per user (default: 4096)" */ - int max_send_buffer; /**<<< "Max send buffer before disconnect, per user (default: 16384)" */ - int max_send_buffer_soft; /**<<< "Max send buffer before message drops, per user (default: 8192)" */ + int max_send_buffer; /**<<< "Max send buffer before disconnect, per user (default: 128K)" */ + int max_send_buffer_soft; /**<<< "Max send buffer before message drops, per user (default: 96K)" */ int low_bandwidth_mode; /**<<< "If this is enabled, the hub will strip off elements from each user's info message to reduce bandwidth usage" */ /* Limits enforced on users */ diff --git a/src/hub.c b/src/hub.c index 7c2b07b..bb0805e 100644 --- a/src/hub.c +++ b/src/hub.c @@ -189,7 +189,7 @@ int hub_handle_chat_message(struct user* u, struct adc_message* cmd) int relay = 1; /* TODO: Check for hub-commands here. Set relay to 0 and the message will not be sent to other users. */ - if (message[0] == '!') + if (message[0] == '!' || message[0] == '+') { relay = command_dipatcher(u, message); } diff --git a/src/hub.h b/src/hub.h index 319572c..5ef99f8 100644 --- a/src/hub.h +++ b/src/hub.h @@ -66,6 +66,19 @@ enum hub_state hub_status_disabled = 5, /**<<<"Hub is disabled (Running, but not accepting users) */ }; +/** + * Always updated each minute. + */ +struct hub_stats +{ + size_t net_tx; + size_t net_rx; + size_t net_tx_peak; + size_t net_rx_peak; + size_t net_tx_total; + size_t net_rx_total; +}; + struct hub_info { int fd_tcp; @@ -77,6 +90,7 @@ struct hub_info #ifdef ADC_UDP_OPERATION struct event ev_datagram; #endif + struct hub_stats stats; struct event_queue* queue; struct hub_config* config; struct user_manager* users; diff --git a/src/inf.c b/src/inf.c index 23ae829..b680678 100644 --- a/src/inf.c +++ b/src/inf.c @@ -58,7 +58,7 @@ static int set_feature_cast_supports(struct user* u, struct adc_message* cmd) it = &it[5]; } - if (strlen(it) > 0) + if (*it) { user_set_feature_cast_support(u, it); } @@ -188,65 +188,37 @@ static int check_required_login_flags(struct user* user, struct adc_message* cmd */ int check_network(struct user* user, struct adc_message* cmd) { - int want_ipv4 = 0; - int want_ipv6 = 0; - int nat_override = 0; - const char* address = 0; + const char* address = ip_convert_to_string(&user->ipaddr); - if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR)) + /* Check for NAT override address */ + if (acl_is_ip_nat_override(user->hub->acl, address)) { - want_ipv6 = 1; - } - - if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR)) - { - want_ipv4 = 1; - } - - if (!want_ipv4 && !want_ipv6) - return 0; - - /* Add correct/verified IP addresses instead (if requested/stripped) */ - address = (char*) net_get_peer_address(user->sd); - if (address) - { - if (want_ipv4 && strchr(address, '.')) + char* client_given_ip = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); + if (strcmp(client_given_ip, "0.0.0.0") != 0) { - want_ipv6 = 0; - } - else if (want_ipv6) - { - want_ipv4 = 0; - } - - /* check if user can do nat override */ - if (want_ipv4 && acl_is_ip_nat_override(user->hub->acl, address)) - { - char* client_given_ip = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); - if (strcmp(client_given_ip, "0.0.0.0") != 0) - { - user_set_nat_override(user); - nat_override = 1; - } + user_set_nat_override(user); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT); hub_free(client_given_ip); + return 0; } + hub_free(client_given_ip); } - - if (!nat_override) + + if (strchr(address, '.')) + { + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); + adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address); + } + else if (strchr(address, ':')) { adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); - if (!want_ipv4) - adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_UDP_PORT); - else - adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address); - + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_UDP_PORT); adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR); - if (!want_ipv6) - adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT); - else - adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR, address); + adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR, address); } - return 0; } @@ -359,7 +331,7 @@ static int check_logged_in(struct user* user, struct adc_message* cmd) { if (lookup1 == lookup2) { - hub_log(log_debug, "check_logged_in: exact same user is logged in: %s", user->id.nick); + hub_log(log_error, "check_logged_in: exact same user is logged in: %s", user->id.nick); user_disconnect(lookup1, quit_timeout); return 0; } diff --git a/src/ipcalc.c b/src/ipcalc.c index f3070f0..dd89def 100644 --- a/src/ipcalc.c +++ b/src/ipcalc.c @@ -90,7 +90,7 @@ int ip_convert_to_binary(const char* taddr, struct ip_addr_encap* raw) } -char* ip_convert_to_string(struct ip_addr_encap* raw) +const char* ip_convert_to_string(struct ip_addr_encap* raw) { static char address[INET6_ADDRSTRLEN+1]; memset(address, 0, INET6_ADDRSTRLEN); diff --git a/src/ipcalc.h b/src/ipcalc.h index b5f35e5..3011b9f 100644 --- a/src/ipcalc.h +++ b/src/ipcalc.h @@ -36,7 +36,7 @@ struct ip_addr_encap { extern int ip_convert_to_binary(const char* text_addr, struct ip_addr_encap* raw); -extern char* ip_convert_to_string(struct ip_addr_encap* raw); +extern const char* ip_convert_to_string(struct ip_addr_encap* raw); /* diff --git a/src/main.c b/src/main.c index 505fdd6..1745efc 100644 --- a/src/main.c +++ b/src/main.c @@ -70,7 +70,7 @@ void hub_handle_signal(int fd, short events, void* arg) case SIGUSR2: hub_log(log_trace, "hub_handle_signal(): caught SIGUSR2"); { - user_manager_stats(hub); + user_manager_print_stats(hub); } break; diff --git a/src/message.c b/src/message.c index 231a910..18768cb 100644 --- a/src/message.c +++ b/src/message.c @@ -688,7 +688,7 @@ char* adc_msg_get_argument(struct adc_message* cmd, int offset) argument[strlen(argument)-1] = 0; } - if (strlen(argument)) + if (*argument) { adc_msg_terminate(cmd); return argument; diff --git a/src/misc.c b/src/misc.c index 3728c58..20242f9 100644 --- a/src/misc.c +++ b/src/misc.c @@ -38,8 +38,8 @@ char* strip_white_space(char* string) while (string[0] && is_white_space(string[0])) string++; - if (!strlen(string)) - return string; + if (!*string) + return 0; /* Strip appending whitespace */ pos = &string[strlen(string)-1]; @@ -187,7 +187,7 @@ int file_read_lines(const char* file, void* data, file_line_handler_t handler) while ((pos = strchr(start, '\n'))) { pos[0] = '\0'; - if (strlen(start) > 0) + if (*start) { hub_log(log_dump, "Line: %s", start); if (handler(start, line_count+1, data) < 0) @@ -197,7 +197,7 @@ int file_read_lines(const char* file, void* data, file_line_handler_t handler) line_count++; } - if (strlen(start) > 0) + if (*start) { buf[strlen(start)] = 0; hub_log(log_dump, "Line: %s", start); diff --git a/src/netevent.c b/src/netevent.c index c0d3a43..9b8756b 100644 --- a/src/netevent.c +++ b/src/netevent.c @@ -79,7 +79,7 @@ void net_on_read(int fd, short ev, void *arg) while ((pos = strchr(start, '\n'))) { pos[0] = '\0'; - if (strlen(start) > 0 && strlen(start) < user->hub->config->max_recv_buffer) + if (*start && strlen(start) < user->hub->config->max_recv_buffer) { if (hub_handle_message(user, start, &pos[0]-&start[0]) == -1) { @@ -176,6 +176,10 @@ void net_on_write(int fd, short ev, void *arg) { user->send_queue_size -= ret; user->send_queue_offset = 0; + + assert(user->send_queue_size >= 0); + assert(user->send_queue_offset >= 0); + list_remove(user->send_queue, msg); if (user_flag_get(user, flag_user_list) && (msg == user->info || user->send_queue_size == 0)) @@ -193,6 +197,9 @@ void net_on_write(int fd, short ev, void *arg) { user->send_queue_size -= ret; user->send_queue_offset += ret; + + assert(user->send_queue_size >= 0); + assert(user->send_queue_offset >= 0); break; } } diff --git a/src/network.c b/src/network.c index 064f371..469e773 100644 --- a/src/network.c +++ b/src/network.c @@ -591,19 +591,6 @@ void net_stats_get(struct net_statistics** intermediate, struct net_statistics** } -void net_stats_report() -{ - int factor = (time(NULL) - stats.timestamp); - if (!factor) factor++; - - hub_log(log_info, "Statistics NET: tx=%d KB/s, rx=%d KB/s, (acc=%d/cls=%d/err=%d)", - (int) ((stats.tx / factor) / 1024), - (int) ((stats.rx / factor) / 1024), - (int) stats.accept, - (int) stats.closed, - (int) stats.errors); -} - void net_stats_reset() { stats_total.tx += stats.tx; @@ -619,8 +606,7 @@ void net_stats_reset() int net_stats_timeout() { - /* FIXME: Configurable time for dumping network statistics */ - return (time(NULL) - stats.timestamp > 60) ? 1 : 0; + return (difftime(time(NULL), stats.timestamp) > TIMEOUT_STATS) ? 1 : 0; } diff --git a/src/route.c b/src/route.c index 92e0d1d..b0f34c1 100644 --- a/src/route.c +++ b/src/route.c @@ -80,6 +80,26 @@ static void queue_command(struct user* user, struct adc_message* msg__, int offs } // #define ALWAYS_QUEUE_MESSAGES +static size_t get_max_send_queue(struct hub_info* hub) +{ + return MAX(hub->config->max_send_buffer, (hub->config->max_recv_buffer * hub_get_user_count(hub))); +} + +/* + * @return 1 if send queue is OK. + * -1 if send queue is overflowed + * 0 if soft send queue is overflowed (not implemented at the moment) + */ +static int check_send_queue(struct user* user, struct adc_message* msg) +{ + if (user_flag_get(user, flag_user_list)) + return 1; + + if ((user->send_queue_size + msg->length) > get_max_send_queue(user->hub)) + return -1; + + return 1; +} int route_to_user(struct user* user, struct adc_message* msg) { @@ -118,24 +138,23 @@ int route_to_user(struct user* user, struct adc_message* msg) else #endif { - if (!user_flag_get(user, flag_user_list) && user->send_queue_size + msg->length > user->hub->config->max_send_buffer && msg->priority >= 0) + ret = check_send_queue(user, msg); + if (ret == -1) { /* User is not able to swallow the data, let's cut our losses and disconnect. */ user_disconnect(user, quit_send_queue); - return 0; + } + else if (ret == 1) + { + /* queue command */ + queue_command(user, msg, 0); + if (user->ev_write) + event_add(user->ev_write, NULL); + } else { - if (user->send_queue_size + msg->length > user->hub->config->max_send_buffer_soft && msg->priority >= 0) - { - /* Don't queue this message if it is low priority! */ - } - else - { - queue_command(user, msg, 0); - if (user->ev_write) - event_add(user->ev_write, NULL); - } + /* do not queue command as our soft-limits are exceeded */ } } diff --git a/src/uhub.h b/src/uhub.h index d8c429e..459c78c 100644 --- a/src/uhub.h +++ b/src/uhub.h @@ -113,7 +113,7 @@ #define TIMEOUT_HANDSHAKE 30 #define TIMEOUT_SENDQ 120 #define TIMEOUT_IDLE 7200 -#define TIMEOUT_STATS 3600 +#define TIMEOUT_STATS 60 #define MAX_CLIENTS 512 #define MAX_CID_LEN 39 diff --git a/src/usermanager.c b/src/usermanager.c index 8fbdd93..00ff896 100644 --- a/src/usermanager.c +++ b/src/usermanager.c @@ -39,30 +39,40 @@ static void clear_user_list_callback(void* ptr) } -void user_manager_stats(struct hub_info* hub) +void user_manager_update_stats(struct hub_info* hub) { - int factor = 0; + const int factor = TIMEOUT_STATS; struct net_statistics* total; struct net_statistics* intermediate; net_stats_get(&intermediate, &total); - - factor = (time(NULL) - intermediate->timestamp); - if (!factor) factor++; - hub_log(log_info, "Statistics users=%zu, net_tx=%d KB/s, net_rx=%d KB/s", - hub->users->count, - (int) ((intermediate->tx / factor) / 1024), - (int) ((intermediate->rx / factor) / 1024)); + hub->stats.net_tx = (intermediate->tx / factor); + hub->stats.net_rx = (intermediate->rx / factor); + hub->stats.net_tx_peak = MAX(hub->stats.net_tx, hub->stats.net_tx_peak); + hub->stats.net_rx_peak = MAX(hub->stats.net_rx, hub->stats.net_rx_peak); + hub->stats.net_tx_total = total->tx; + hub->stats.net_rx_total = total->rx; net_stats_reset(); } +void user_manager_print_stats(struct hub_info* hub) +{ + hub_log(log_info, "Statistics users=%zu (peak_users=%zu), net_tx=%d KB/s, net_rx=%d KB/s (peak_tx=%d KB/s, peak_rx=%d KB/s)", + hub->users->count, + hub->users->count_peak, + (int) hub->stats.net_tx / 1024, + (int) hub->stats.net_rx / 1024, + (int) hub->stats.net_tx_peak / 1024, + (int) hub->stats.net_rx_peak / 1024); +} + static void timer_statistics(int fd, short ev, void *arg) { struct hub_info* hub = (struct hub_info*) arg; struct timeval timeout = { TIMEOUT_STATS, 0 }; - user_manager_stats(hub); + user_manager_update_stats(hub); evtimer_set(&hub->ev_timer, timer_statistics, hub); evtimer_add(&hub->ev_timer, &timeout); } diff --git a/src/usermanager.h b/src/usermanager.h index 07207b6..348fc5e 100644 --- a/src/usermanager.h +++ b/src/usermanager.h @@ -45,7 +45,8 @@ extern void user_manager_shutdown(struct hub_info* hub); /** * Generate statistics for logfiles. */ -extern void user_manager_stats(struct hub_info* hub); +extern void user_manager_update_stats(struct hub_info* hub); +extern void user_manager_print_stats(struct hub_info* hub); /** * Add a new user to the user manager.