diff options
author | Paul Warren <pdw@ex-parrot.com> | 2022-10-09 22:07:55 +0100 |
---|---|---|
committer | Paul Warren <pdw@ex-parrot.com> | 2022-10-09 22:07:55 +0100 |
commit | aee8e05f48f0ffee4a724c6bd582f51ca65b27f1 (patch) | |
tree | 76dab521c580e74095b55ba4c8ecf3c0561a9d2f | |
parent | 8d76ef06d225c4d72db544b54ea1ef1a0e077967 (diff) |
Show local process names.origin/proc-display
Patch from Mark Benjamin
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | addr_hash.c | 1 | ||||
-rw-r--r-- | cfgfile.c | 47 | ||||
-rw-r--r-- | counter_hash.c | 1 | ||||
-rw-r--r-- | hash.c | 7 | ||||
-rw-r--r-- | hash.h | 1 | ||||
-rw-r--r-- | ns_hash.c | 1 | ||||
-rw-r--r-- | options.c | 15 | ||||
-rw-r--r-- | options.h | 2 | ||||
-rw-r--r-- | proc_hash.c | 198 | ||||
-rw-r--r-- | proc_hash.h | 22 | ||||
-rw-r--r-- | serv_hash.c | 1 | ||||
-rw-r--r-- | tui.c | 30 | ||||
-rw-r--r-- | ui.c | 23 | ||||
-rw-r--r-- | ui_common.c | 40 | ||||
-rw-r--r-- | ui_common.h | 15 |
16 files changed, 338 insertions, 68 deletions
diff --git a/Makefile.am b/Makefile.am index 4a5ec95..e3e10a9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,7 +17,7 @@ iftop_SOURCES = addr_hash.c edline.c hash.c iftop.c ns_hash.c \ options.c resolver.c screenfilter.c serv_hash.c \ sorted_list.c threadprof.c ui_common.c ui.c tui.c util.c \ addrs_ioctl.c addrs_dlpi.c dlcommon.c \ - stringmap.c cfgfile.c vector.c + stringmap.c cfgfile.c vector.c proc_hash.c #iftop_dump_SOURCES = counter_hash.c hash.c iftop-dump.c \ # options.c \ diff --git a/addr_hash.c b/addr_hash.c index f5c002c..f2e73fd 100644 --- a/addr_hash.c +++ b/addr_hash.c @@ -94,6 +94,7 @@ hash_type* addr_hash_create() { hash_table->hash = &hash; hash_table->delete_key = &delete_key; hash_table->copy_key = ©_key; + hash_table->not_found_callback = NULL; hash_initialise(hash_table); return hash_table; } @@ -22,29 +22,30 @@ #define MAX_CONFIG_LINE 2048 char * config_directives[] = { - "interface", - "dns-resolution", - "port-resolution", - "filter-code", - "show-bars", - "promiscuous", - "hide-source", - "hide-destination", - "use-bytes", - "bandwidth-unit", - "sort", - "line-display", - "show-totals", - "log-scale", - "max-bandwidth", - "net-filter", - "net-filter6", - "link-local", - "port-display", - "timed-output", - "no-curses", - "num-lines", - NULL + "interface", + "dns-resolution", + "port-resolution", + "filter-code", + "show-bars", + "promiscuous", + "hide-source", + "hide-destination", + "use-bytes", + "bandwidth-unit", + "sort", + "line-display", + "show-totals", + "log-scale", + "max-bandwidth", + "net-filter", + "net-filter6", + "link-local", + "port-display", + "timed-output", + "no-curses", + "num-lines", + "proc-names", + NULL }; stringmap config; diff --git a/counter_hash.c b/counter_hash.c index d0a275b..f97c326 100644 --- a/counter_hash.c +++ b/counter_hash.c @@ -50,6 +50,7 @@ hash_type* counter_hash_create() { hash_table->hash = &counter_hash_hash; hash_table->delete_key = &counter_hash_delete_key; hash_table->copy_key = &counter_hash_copy_key; + hash_table->not_found_callback = NULL; hash_initialise(hash_table); return hash_table; } @@ -69,7 +69,12 @@ hash_status_enum hash_find(hash_type* hash_table, void* key, void **rec) { while (p && !hash_table->compare(p->key, key)) { p = p->next; } - if (!p) return HASH_STATUS_KEY_NOT_FOUND; + if (!p) { + if (hash_table->not_found_callback != NULL) { + hash_table->not_found_callback((void *)hash_table); + } + return HASH_STATUS_KEY_NOT_FOUND; + } *rec = p->rec; return HASH_STATUS_OK; } @@ -24,6 +24,7 @@ typedef struct { int (*hash) (void*); void* (*copy_key) (void*); void (*delete_key) (void*); + void (*not_found_callback)(void*); hash_node_type** table; int size; } hash_type; @@ -63,6 +63,7 @@ hash_type* ns_hash_create() { hash_table->hash = &ns_hash_hash; hash_table->delete_key = &ns_hash_delete_key; hash_table->copy_key = &ns_hash_copy_key; + hash_table->not_found_callback = NULL; hash_initialise(hash_table); return hash_table; } @@ -30,7 +30,7 @@ options_t options; -char optstr[] = "+i:f:nNF:G:lhpbBu:Pm:c:s:tL:o:"; +char optstr[] = "+i:f:nNF:G:lhpbBu:Pm:c:s:tL:o:A"; /* Global options. */ @@ -165,6 +165,7 @@ void options_set_defaults() { options.timed_output = 0; options.no_curses = 0; options.num_lines = 10; + options.process_names = 0; /* Figure out the name for the config file */ s = getenv("HOME"); @@ -204,6 +205,7 @@ static void usage(FILE *fp) { " -G net6/mask6 show traffic flows in/out of IPv6 network\n" " -l display and count link-local IPv6 traffic (default: off)\n" " -P show ports as well as hosts\n" +" -A show local process names\n" " -m limit sets the upper limit for the bandwidth scale\n" " -c config file specifies an alternative configuration file\n" " -t use text interface without ncurses\n" @@ -282,9 +284,9 @@ void options_read_args(int argc, char **argv) { config_set_string("bandwidth-unit", "bytes"); break; - case 'u': - config_set_string("bandwidth-unit", optarg); - break; + case 'u': + config_set_string("bandwidth-unit", optarg); + break; case 's': config_set_string("timed-output", optarg); @@ -308,6 +310,10 @@ void options_read_args(int argc, char **argv) { options.config_file_specified = 1; break; + case 'A': + config_set_string("proc-names", "true"); + break; + case '?': fprintf(stderr, "iftop: unknown option -%c\n", optopt); usage(stderr); @@ -585,6 +591,7 @@ void options_make() { options_config_get_int("timed-output", &options.timed_output); options_config_get_bool("no-curses", &options.no_curses); options_config_get_int("num-lines", &options.num_lines); + options_config_get_bool("proc-names", &options.process_names); options_config_get_net_filter(); options_config_get_net_filter6(); }; @@ -97,6 +97,8 @@ typedef struct { char *config_file; int config_file_specified; + int process_names; + } options_t; diff --git a/proc_hash.c b/proc_hash.c new file mode 100644 index 0000000..8ff0eca --- /dev/null +++ b/proc_hash.c @@ -0,0 +1,198 @@ +/* hash table */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> +#include <unistd.h> +#include "proc_hash.h" +#include "hash.h" +#include "iftop.h" + +// Deliberately not a power of 2 or 10 +#define hash_table_size 123 + +typedef struct proc_list proc_list; + +struct proc_list { + ip_process* this; + proc_list* next; +}; + +static const char *const re_port_num = "^(udp|tcp)[[:space:]]*[[:alpha:]-]+[[:space:]]+[[:digit:]]+[[:space:]]+" + "[[:digit:]]+[[:space:]]+" + "(([[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+" + "(%[[:alnum:]]+)?)|(\\[([[:digit:]]*:)+[[:digit:]]*\\]))" + ":([[:digit:]]+)"; // \7 is port +static const char *const re_proc_name_pid = "users:\\(\\(\"([[:alpha:]-]+)\",pid=([[:digit:]]+)"; // \1 is procname, \2 is pid + +regex_t rege_port_num; +regex_t rege_proc_name_pid; + +int proc_hash_compare(void* a, void* b) { + ip_process* aa = (ip_process*)a; + ip_process* bb = (ip_process*)b; + return (aa->port == bb->port); +} + +int proc_hash_hash(void* key) { + ip_process* pkey = (ip_process*)key; + return pkey->port % hash_table_size; +} + +void* proc_hash_copy_key(void* orig) { + int* copy; + copy = xmalloc(sizeof *copy); + *copy = *(int*)orig; + return copy; +} + +void proc_hash_delete_key(void* key) { + free(key); +} + +void child_exec(int* pipe_fd, int option) { + close(pipe_fd[0]); // close reading end in the child + dup2(pipe_fd[1], 1); // send stdout to the pipe + dup2(pipe_fd[1], 2); // send stderr to the pipe + close(pipe_fd[1]); // this descriptor is no longer needed + switch (option) { + case 2: + execlp("ss", "-O", "-H", "-l", "-n", "-t", "-u", "-p", (char *)NULL); + break; + case 1: + execlp("ss", "-O", "-H", "-n", "-t", "-u", "-p", (char *)NULL); + break; + case 0: + default: + execlp("ss", "-O", "-H", "-a", "-n", "-t", "-u", "-p", (char *)NULL); + break; + } +} + +proc_list* proc_parse_parent(int *pipefd) { + proc_list* list = malloc(sizeof(proc_list)); + proc_list* ret = list; + list->this = malloc(sizeof(ip_process)); + list->this->name = strdup("myprog"); + list->this->port = 45678; + list->next = NULL; + + regmatch_t pmatch[10]; + regoff_t len, shift; + int buf_len = 65536; + char buffer[buf_len]; + uint16_t tmp_port; + char* tmp_sport; + char* tmp_name; + close(pipefd[1]); // close the write end of the pipe in the parent + while (read(pipefd[0], buffer, sizeof(buffer)) != 0) + { + // parse results from child to match process names with port numbers + const char *c = buffer; + while (1) { + if (regexec(®e_port_num, c, sizeof(pmatch) / sizeof(pmatch[0]), pmatch, 0)) { + break; + } + shift = pmatch[0].rm_eo; + len = pmatch[7].rm_eo - pmatch[7].rm_so; + tmp_sport = strndup(c + pmatch[7].rm_so, len); + tmp_port = atoi(tmp_sport); + if (regexec(®e_proc_name_pid, c, sizeof(pmatch) / sizeof(pmatch[0]), pmatch, 0)) { + c += shift; + continue; + } + len = pmatch[1].rm_eo - pmatch[1].rm_so; + tmp_name = strndup(c + pmatch[1].rm_so, len); + // no need to retrieve pid + list->this = malloc(sizeof(ip_process)); + list->this->port = tmp_port; + list->this->name = tmp_name; + list->next = malloc(sizeof(proc_list)); + list = list->next; + list->this = NULL; + list->next = NULL; + c += pmatch[0].rm_eo; + } + memset(buffer, 0, sizeof(buffer)); + } + return ret; +} + +proc_list* proc_parse_fork() { + int pipefd[2]; + pipe(pipefd); + if (fork() == 0) { // child + child_exec(pipefd, 0); // "-a" flag + } + return proc_parse_parent(pipefd); +} + + +// return value == true for 'more items in list' +bool proc_result_free(proc_list** procs) { + if ((*procs)->next == NULL) { + free(*procs); + return false; + } + proc_list* rem = *procs; + *procs = (*procs)->next; + free(rem); + return true; +} + +void proc_hash_init_refresh(hash_type* sh, bool refresh) { + proc_list* processes; + ip_process* process; + void* rec; + if (!refresh) { + regcomp(®e_port_num, re_port_num, REG_NEWLINE | REG_EXTENDED); + regcomp(®e_proc_name_pid, re_proc_name_pid, REG_NEWLINE | REG_EXTENDED); + } + processes = proc_parse_fork(); + while (processes != NULL && processes->this != NULL) { + process = processes->this; + if (refresh && hash_find(sh, process, &rec) == HASH_STATUS_OK) { + if (strcmp((char *)rec, process->name) == 0) { + if (!proc_result_free(&processes)) { + break; + } + continue; + } else { + hash_delete(sh, process); + } + } + hash_insert(sh, process, strdup(process->name)); + if (!proc_result_free(&processes)) { + break; + } + } +} + +void proc_hash_not_found(void* vp_process_hash) { + static bool recursive; + static int n_times; + hash_type* process_hash = (hash_type*)vp_process_hash; + if (!recursive && ++n_times >= 20) { + n_times = 0; + recursive = true; + proc_hash_init_refresh(process_hash, true); + recursive = false; + } +} + +/* + * Allocate and return a hash + */ +hash_type* proc_hash_create() { + hash_type* hash_table; + hash_table = xcalloc(hash_table_size, sizeof *hash_table); + hash_table->size = hash_table_size; + hash_table->compare = &proc_hash_compare; + hash_table->hash = &proc_hash_hash; + hash_table->delete_key = &proc_hash_delete_key; + hash_table->copy_key = &proc_hash_copy_key; + hash_table->not_found_callback = proc_hash_not_found; + hash_initialise(hash_table); + return hash_table; +} diff --git a/proc_hash.h b/proc_hash.h new file mode 100644 index 0000000..49724b4 --- /dev/null +++ b/proc_hash.h @@ -0,0 +1,22 @@ +/* + * proc_hash.h: + * + */ + +#ifndef __PROC_HASH_H_ /* include guard */ +#define __PROC_HASH_H_ + +#include <stdint.h> +#include <sys/socket.h> +#include <stdbool.h> +#include "hash.h" + +typedef struct { + uint16_t port; + char* name; +} ip_process; + +hash_type* proc_hash_create(void); +void proc_hash_init_refresh(hash_type* sh, bool refresh); + +#endif /* __PROC_HASH_H_ */ diff --git a/serv_hash.c b/serv_hash.c index 279032e..c9dbdbd 100644 --- a/serv_hash.c +++ b/serv_hash.c @@ -44,6 +44,7 @@ hash_type* serv_hash_create() { hash_table->hash = &serv_hash_hash; hash_table->delete_key = &serv_hash_delete_key; hash_table->copy_key = &serv_hash_copy_key; + hash_table->not_found_callback = NULL; hash_initialise(hash_table); return hash_table; } @@ -14,6 +14,7 @@ #include <stdio.h> #include <signal.h> #include <stdlib.h> +#include <stdbool.h> #include <unistd.h> #if defined(HAVE_TERMIOS_H) @@ -81,8 +82,10 @@ void tui_print() { host_pair_line* screen_line = (host_pair_line*)nn->data; /* Assemble host information */ - sprint_host(host1, screen_line->ap.af, &(screen_line->ap.src6), screen_line->ap.src_port, screen_line->ap.protocol, PRINT_WIDTH, options.aggregate_src); - sprint_host(host2, screen_line->ap.af, &(screen_line->ap.dst6), screen_line->ap.dst_port, screen_line->ap.protocol, PRINT_WIDTH, options.aggregate_dest); + sprint_host(host1, screen_line->ap.af, &(screen_line->ap.src6), screen_line->ap.src_port, + screen_line->ap.protocol, PRINT_WIDTH, options.aggregate_src, true); + sprint_host(host2, screen_line->ap.af, &(screen_line->ap.dst6), screen_line->ap.dst_port, + screen_line->ap.protocol, PRINT_WIDTH, options.aggregate_dest, false); /* Send rate per connection */ printf("%4d %s%s", l, host1, " =>"); @@ -169,6 +172,8 @@ void tui_init() { screen_hash = addr_hash_create(); service_hash = serv_hash_create(); serv_hash_initialise(service_hash); + process_hash = proc_hash_create(); + proc_hash_init_refresh(process_hash, false); printf("Listening on %s\n", options.interface); } @@ -235,6 +240,11 @@ void tui_loop() { printf("Port resolution is %s.\n\n", options.portresolution ? "ON" : "OFF"); tick(1); break; + case 'A': + options.process_names ^= 1; + printf("Process name display is %s.\n\n", options.process_names ? "ON" : "OFF"); + tick(1); + break; case 's': options.aggregate_src ^= 1; printf("%s source host\n\n", options.aggregate_src ? "Hide" : "Show"); @@ -302,20 +312,20 @@ void tui_loop() { if (options.paused) { printf("Pausing... press 'P' again to continue.\n"); } - else { - printf("Continuing.\n\n"); - tick(1); - } - break; + else { + printf("Continuing.\n\n"); + tick(1); + } + break; case 'o': options.freezeorder ^= 1; printf("Order %s.\n\n", options.freezeorder ? "frozen" : "unfrozen"); - tick(1); - break; + tick(1); + break; case '1': options.sort = OPTION_SORT_DIV1; printf("Sorting by column 1.\n\n"); - tick(1); + tick(1); break; case '2': options.sort = OPTION_SORT_DIV2; @@ -44,7 +44,8 @@ " S - toggle show source port l - set screen filter\n"\ " D - toggle show destination port L - lin/log scales\n"\ " p - toggle port display ! - shell command\n"\ -" q - quit\n"\ +" A - toggle show process names q - quit\n"\ +"\n"\ "Sorting:\n"\ " 1/2/3 - sort by 1st/2nd/3rd column\n"\ " < - sort by source name\n"\ @@ -315,11 +316,13 @@ void ui_print() { sprint_host(host1, screen_line->ap.af, &(screen_line->ap.src6), screen_line->ap.src_port, - screen_line->ap.protocol, L, options.aggregate_src); + screen_line->ap.protocol, L, + options.aggregate_src, true); sprint_host(host2, screen_line->ap.af, &(screen_line->ap.dst6), screen_line->ap.dst_port, - screen_line->ap.protocol, L, options.aggregate_dest); + screen_line->ap.protocol, L, + options.aggregate_dest, false); if(!screen_filter_match(host1) && !screen_filter_match(host2)) { continue; @@ -467,6 +470,9 @@ void ui_init() { service_hash = serv_hash_create(); serv_hash_initialise(service_hash); + process_hash = proc_hash_create(); + proc_hash_init_refresh(process_hash, false); + snprintf(msg,20,"Listening on %s",options.interface); showhelp(msg); @@ -530,6 +536,17 @@ void ui_loop() { tick(1); break; + case 'A': + if (options.process_names) { + options.process_names = 0; + showhelp("Process names off"); + } else { + options.process_names = 1; + showhelp("Process names on"); + } + tick(1); + break; + case 'h': case '?': options.showhelp = !options.showhelp; diff --git a/ui_common.c b/ui_common.c index dcf6646..757bbc4 100644 --- a/ui_common.c +++ b/ui_common.c @@ -27,6 +27,14 @@ char* unit_disp[][UNIT_DIVISIONS] = { [OPTION_BW_PKTS] = { "p", "Kp", "Mp", "GB"}, }; +sorted_list_type screen_list; +host_pair_line totals; +int peaksent, peakrecv, peaktotal; +hash_type* screen_hash; +hash_type* service_hash; +hash_type* process_hash; + + extern hash_type* history; extern int history_pos; extern int history_len; @@ -281,18 +289,6 @@ void analyse_data() { if(options.aggregate_dest) { memset(&ap.dst6, '\0', sizeof(ap.dst6)); } - - /* Aggregate ports, if required */ - if(options.showports == OPTION_PORTS_DEST || options.showports == OPTION_PORTS_OFF) { - ap.src_port = 0; - } - if(options.showports == OPTION_PORTS_SRC || options.showports == OPTION_PORTS_OFF) { - ap.dst_port = 0; - } - if(options.showports == OPTION_PORTS_OFF) { - ap.protocol = 0; - } - if(hash_find(screen_hash, &ap, u_screen_line.void_pp) == HASH_STATUS_KEY_NOT_FOUND) { screen_line = xcalloc(1, sizeof *screen_line); @@ -324,7 +320,8 @@ void analyse_data() { } -void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, unsigned int protocol, int L, int unspecified_as_star) { +void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, + unsigned int protocol, int L, int unspecified_as_star, bool is_local) { char hostname[HOSTNAME_LENGTH]; char service[HOSTNAME_LENGTH]; char* s_name; @@ -334,6 +331,7 @@ void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, } u_s_name = { &s_name }; ip_service skey; + ip_process pkey; int left; if(IN6_IS_ADDR_UNSPECIFIED(addr) && unspecified_as_star) { @@ -346,21 +344,23 @@ void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, inet_ntop(af, addr, hostname, sizeof(hostname)); } left = strlen(hostname); - - if(port != 0) { + service[0] = '\0'; + if ((options.showports == OPTION_PORTS_ON) || (options.showports == OPTION_PORTS_SRC && is_local) || + (options.showports == OPTION_PORTS_DEST && !is_local)) { skey.port = port; skey.protocol = protocol; if(options.portresolution && hash_find(service_hash, &skey, u_s_name.void_pp) == HASH_STATUS_OK) { snprintf(service, HOSTNAME_LENGTH, ":%s", s_name); - } - else { + } else { snprintf(service, HOSTNAME_LENGTH, ":%d", port); } } - else { - service[0] = '\0'; + pkey.port = (uint16_t)port; + if (options.process_names && is_local && + hash_find(process_hash, &pkey, (void **) &(pkey.name)) == HASH_STATUS_OK) { + snprintf(service + strlen(service), HOSTNAME_LENGTH - strlen(service), "(%s)", pkey.name); } - + /* If we're showing IPv6 addresses with a port number, put them in square * brackets. */ if(port == 0 || af == AF_INET || L < 2) { diff --git a/ui_common.h b/ui_common.h index 63ae5bb..44c04e6 100644 --- a/ui_common.h +++ b/ui_common.h @@ -11,6 +11,7 @@ #include <stdio.h> #include "addr_hash.h" +#include "proc_hash.h" #include "serv_hash.h" #include "iftop.h" #include "resolver.h" @@ -33,16 +34,18 @@ typedef struct host_pair_line_tag { extern options_t options; -sorted_list_type screen_list; -host_pair_line totals; -int peaksent, peakrecv, peaktotal; +extern sorted_list_type screen_list; +extern host_pair_line totals; +extern int peaksent, peakrecv, peaktotal; extern history_type history_totals; -hash_type* screen_hash; -hash_type* service_hash; +extern hash_type* screen_hash; +extern hash_type* service_hash; +extern hash_type* process_hash; void analyse_data(void); void screen_list_init(void); -void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, unsigned int protocol, int L, int unspecified_as_star); +void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, + unsigned int protocol, int L, int unspecified_as_star, bool is_local); void readable_size(float, char*, int, int, option_bw_unit_t); #endif /* __UI_COMMON_H_ */ |