From 6e69e4237d8f43232afd0dc207275f27c08039ed Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sun, 27 Jul 2008 12:10:07 +0000 Subject: [PATCH] netstat: optional -p support by L. Gabriel Somlo Without FEATURE_NETSTAT_PRG: function old new delta recursive_action 416 425 +9 tcp_do_one 420 428 +8 udp_do_one 492 499 +7 raw_do_one 472 479 +7 expand 1697 1701 +4 netstat_main 489 492 +3 unix_do_one 486 488 +2 flags 1 - -1 qgravechar 109 106 -3 net_conn_line 4 - -4 bbunpack 391 383 -8 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 7/2 up/down: 40/-16) Total: 24 bytes With FEATURE_NETSTAT_PRG: file_act - 213 +213 dir_act - 192 +192 netstat_main 489 601 +112 prg_cache_get - 50 +50 tcp_do_one 420 462 +42 udp_do_one 492 533 +41 raw_do_one 472 513 +41 unix_do_one 486 519 +33 recursive_action 416 425 +9 expand 1697 1701 +4 flags 1 - -1 qgravechar 109 106 -3 net_conn_line 4 - -4 bbunpack 391 383 -8 packed_usage 24586 24572 -14 ------------------------------------------------------------------------------ (add/remove: 3/2 grow/shrink: 7/3 up/down: 737/-30) Total: 707 bytes --- include/libbb.h | 1 + include/usage.h | 5 +- libbb/recursive_action.c | 3 +- networking/Config.in | 8 ++ networking/netstat.c | 282 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 276 insertions(+), 23 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 48cbd90c0..2dcdeacb1 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -278,6 +278,7 @@ enum { ACTION_FOLLOWLINKS_L0 = (1 << 2), ACTION_DEPTHFIRST = (1 << 3), /*ACTION_REVERSE = (1 << 4), - unused */ + ACTION_QUIET = (1 << 5), }; extern int recursive_action(const char *fileName, unsigned flags, int FAST_FUNC (*fileAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), diff --git a/include/usage.h b/include/usage.h index 9a73d0890..110fbf641 100644 --- a/include/usage.h +++ b/include/usage.h @@ -2820,7 +2820,7 @@ #endif #define netstat_trivial_usage \ - "[-laentuwxr"USE_FEATURE_NETSTAT_WIDE("W")"]" + "[-laentuwxr"USE_FEATURE_NETSTAT_WIDE("W")USE_FEATURE_NETSTAT_PRG("p")"]" #define netstat_full_usage "\n\n" \ "Display networking information\n" \ "\nOptions:" \ @@ -2835,6 +2835,9 @@ "\n -r Display routing table" \ USE_FEATURE_NETSTAT_WIDE( \ "\n -W Display with no column truncation" \ + ) \ + USE_FEATURE_NETSTAT_PRG( \ + "\n -p Display PID/Program name for sockets" \ ) #define nice_trivial_usage \ diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c index 07bd0abfd..3ec596a35 100644 --- a/libbb/recursive_action.c +++ b/libbb/recursive_action.c @@ -141,6 +141,7 @@ int FAST_FUNC recursive_action(const char *fileName, return status; done_nak_warn: - bb_simple_perror_msg(fileName); + if (!(flags & ACTION_QUIET)) + bb_simple_perror_msg(fileName); return FALSE; } diff --git a/networking/Config.in b/networking/Config.in index 5f38062c6..91f172997 100644 --- a/networking/Config.in +++ b/networking/Config.in @@ -633,6 +633,14 @@ config FEATURE_NETSTAT_WIDE Add support for wide columns. Useful when displaying IPv6 addresses (-W option). +config FEATURE_NETSTAT_PRG + bool "Enable PID/Program name output" + default n + depends on NETSTAT + help + Add support for -p flag to print out PID and program name. + +700 bytes of code. + config NSLOOKUP bool "nslookup" default n diff --git a/networking/netstat.c b/networking/netstat.c index 46510acc1..0c0b69afe 100644 --- a/networking/netstat.c +++ b/networking/netstat.c @@ -8,18 +8,37 @@ * 2002-04-20 * IPV6 support added by Bart Visscher * + * 2008-07-10 + * optional '-p' flag support ported from net-tools by G. Somlo + * * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ #include "libbb.h" #include "inet_common.h" +#define NETSTAT_OPTS "laentuwx" \ + USE_ROUTE( "r") \ + USE_FEATURE_NETSTAT_WIDE("W") \ + USE_FEATURE_NETSTAT_PRG( "p") + enum { - OPT_extended = 0x4, - OPT_showroute = 0x100, - OPT_widedisplay = 0x200 * ENABLE_FEATURE_NETSTAT_WIDE, + OPTBIT_KEEP_OLD = 7, + USE_ROUTE( OPTBIT_ROUTE,) + USE_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,) + USE_FEATURE_NETSTAT_PRG( OPTBIT_PRG ,) + OPT_sock_listen = 1 << 0, // l + OPT_sock_all = 1 << 1, // a + OPT_extended = 1 << 2, // e + OPT_noresolve = 1 << 3, // n + OPT_sock_tcp = 1 << 4, // t + OPT_sock_udp = 1 << 5, // u + OPT_sock_raw = 1 << 6, // w + OPT_sock_unix = 1 << 7, // x + OPT_route = USE_ROUTE( (1 << OPTBIT_ROUTE)) + 0, // r + OPT_wide = USE_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W + OPT_prg = USE_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG )) + 0, // p }; -# define NETSTAT_OPTS "laentuwxr"USE_FEATURE_NETSTAT_WIDE("W") #define NETSTAT_CONNECTED 0x01 #define NETSTAT_LISTENING 0x02 @@ -31,7 +50,6 @@ enum { #define NETSTAT_UNIX 0x80 #define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX) -static smallint flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; enum { TCP_ESTABLISHED = 1, @@ -44,7 +62,7 @@ enum { TCP_CLOSE_WAIT, TCP_LAST_ACK, TCP_LISTEN, - TCP_CLOSING /* now a valid state */ + TCP_CLOSING, /* now a valid state */ }; static const char *const tcp_state[] = { @@ -76,8 +94,8 @@ typedef enum { /* Standard printout size */ #define PRINT_IP_MAX_SIZE 23 -#define PRINT_NET_CONN "%s %6ld %6ld %-23s %-23s %-12s\n" -#define PRINT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State\n" +#define PRINT_NET_CONN "%s %6ld %6ld %-23s %-23s %-12s" +#define PRINT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State " /* When there are IPv6 connections the IPv6 addresses will be * truncated to none-recognition. The '-W' option makes the @@ -86,10 +104,201 @@ typedef enum { * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd */ #define PRINT_IP_MAX_SIZE_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */ -#define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s\n" -#define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State\n" +#define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s" +#define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State " -static const char *net_conn_line = PRINT_NET_CONN; + +#define PROGNAME_WIDTH 20 +#define PROGNAME_WIDTH_STR "20" +/* PROGNAME_WIDTH chars: 12345678901234567890 */ +#define PROGNAME_BANNER "PID/Program name " + +struct prg_node { + struct prg_node *next; + long inode; + char name[PROGNAME_WIDTH]; +}; + +#define PRG_HASH_SIZE 211 + + +struct globals { + const char *net_conn_line; + smallint flags; +#if ENABLE_FEATURE_NETSTAT_PRG + smallint prg_cache_loaded; + struct prg_node *prg_hash[PRG_HASH_SIZE]; +#endif +}; +#define G (*ptr_to_globals) +#define flags (G.flags ) +#define net_conn_line (G.net_conn_line ) +#define prg_hash (G.prg_hash ) +#define prg_cache_loaded (G.prg_cache_loaded) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \ + net_conn_line = PRINT_NET_CONN; \ +} while (0) + + +#if ENABLE_FEATURE_NETSTAT_PRG + +/* Deliberately truncating long to unsigned *int* */ +#define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE) + +#define print_progname_banner() do { \ + if (option_mask32 & OPT_prg) printf(PROGNAME_BANNER); \ +} while (0) + +static void prg_cache_add(long inode, char *name) +{ + unsigned hi = PRG_HASHIT(inode); + struct prg_node **pnp, *pn; + + prg_cache_loaded = 2; + for (pnp = prg_hash + hi; (pn = *pnp) != NULL; pnp = &pn->next) { + if (pn->inode == inode) { + /* Some warning should be appropriate here + as we got multiple processes for one i-node */ + return; + } + } + *pnp = xzalloc(sizeof(struct prg_node)); + pn = *pnp; + pn->inode = inode; + safe_strncpy(pn->name, name, PROGNAME_WIDTH); +} + +static const char *prg_cache_get(long inode) +{ + unsigned hi = PRG_HASHIT(inode); + struct prg_node *pn; + + for (pn = prg_hash[hi]; pn; pn = pn->next) + if (pn->inode == inode) + return pn->name; + return "-"; +} + +#if ENABLE_FEATURE_CLEAN_UP +static void prg_cache_clear(void) +{ + struct prg_node **pnp, *pn; + + for (pnp = prg_hash; pnp < prg_hash + PRG_HASH_SIZE; pnp++) { + while ((pn = *pnp) != NULL) { + *pnp = pn->next; + free(pn); + } + } +} +#else +#define prg_cache_clear() ((void)0) +#endif + +static long extract_socket_inode(const char *lname) { + + long inode = -1; + + if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) { + /* "socket:[12345]", extract the "12345" as inode */ + inode = bb_strtol(lname + sizeof("socket:[")-1, (char**)&lname, 0); + if (*lname != ']') + inode = -1; + } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) { + /* "[0000]:12345", extract the "12345" as inode */ + inode = bb_strtol(lname + sizeof("[0000]:")-1, NULL, 0); + if (errno) /* not NUL terminated? */ + inode = -1; + } + +#if 0 /* bb_strtol returns all-ones bit pattern on ERANGE anyway */ + if (errno == ERANGE) + inode = -1; +#endif + return inode; +} + +static int FAST_FUNC file_act(const char *fileName, + struct stat *statbuf UNUSED_PARAM, + void *userData, + int depth UNUSED_PARAM) +{ + char *linkname; + long inode; + + linkname = xmalloc_readlink(fileName); + if (linkname != NULL) { + inode = extract_socket_inode(linkname); + free(linkname); + if (inode >= 0) + prg_cache_add(inode, (char *)userData); + } + return TRUE; +} + +static int FAST_FUNC dir_act(const char *fileName, + struct stat *statbuf UNUSED_PARAM, + void *userData UNUSED_PARAM, + int depth) +{ + const char *shortName; + char *p, *q; + char cmdline_buf[512]; + int i; + + if (depth == 0) /* "/proc" itself */ + return TRUE; /* continue looking one level below /proc */ + + shortName = fileName + sizeof("/proc/")-1; /* point after "/proc/" */ + if (!isdigit(shortName[0])) /* skip /proc entries whic aren't processes */ + return SKIP; + + p = concat_path_file(fileName, "cmdline"); /* "/proc/PID/cmdline" */ + i = open_read_close(p, cmdline_buf, sizeof(cmdline_buf) - 1); + free(p); + if (i < 0) + return FALSE; + cmdline_buf[i] = '\0'; + q = concat_path_file(shortName, bb_basename(cmdline_buf)); /* "PID/argv0" */ + + /* go through all files in /proc/PID/fd */ + p = concat_path_file(fileName, "fd"); + i = recursive_action(p, ACTION_RECURSE | ACTION_QUIET, + file_act, NULL, (void *)q, 0); + + free(p); + free(q); + + if (!i) + return FALSE; /* signal permissions error to caller */ + + return SKIP; /* caller should not recurse further into this dir. */ +} + +static void prg_cache_load(void) +{ + int load_ok; + + prg_cache_loaded = 1; + load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET, + NULL, dir_act, NULL, 0); + if (load_ok) + return; + + if (prg_cache_loaded == 1) + bb_error_msg("can't scan /proc - are you root?"); + else + bb_error_msg("showing only processes with your user ID"); +} + +#else + +#define prg_cache_clear() ((void)0) +#define print_progname_banner() ((void)0) + +#endif //ENABLE_FEATURE_NETSTAT_PRG #if ENABLE_FEATURE_IPV6 @@ -195,6 +404,11 @@ static int tcp_do_one(int lnr, char *line) "tcp", flags & NETSTAT_NUMERIC); printf(net_conn_line, "tcp", rxq, txq, l, r, tcp_state[state]); +#if ENABLE_FEATURE_NETSTAT_PRG + if (option_mask32 & OPT_prg) + printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(inode)); +#endif + bb_putchar('\n'); free(l); free(r); } @@ -276,6 +490,11 @@ static int udp_do_one(int lnr, char *line) "udp", flags & NETSTAT_NUMERIC); printf(net_conn_line, "udp", rxq, txq, l, r, state_str); +#if ENABLE_FEATURE_NETSTAT_PRG + if (option_mask32 & OPT_prg) + printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(inode)); +#endif + bb_putchar('\n'); free(l); free(r); } @@ -332,6 +551,11 @@ static int raw_do_one(int lnr, char *line) "raw", flags & NETSTAT_NUMERIC); printf(net_conn_line, "raw", rxq, txq, l, r, itoa(state)); +#if ENABLE_FEATURE_NETSTAT_PRG + if (option_mask32 & OPT_prg) + printf("%-"PROGNAME_WIDTH_STR"s", prg_cache_get(inode)); +#endif + bb_putchar('\n'); free(l); free(r); } @@ -443,6 +667,11 @@ static int unix_do_one(int nr, char *line) ss_proto, refcnt, ss_flags, ss_type, ss_state, inode ); +#if ENABLE_FEATURE_NETSTAT_PRG + if (option_mask32 & OPT_prg) + printf("%-"PROGNAME_WIDTH_STR"s", prg_cache_get(inode)); +#endif + /* TODO: currently we stop at first NUL byte. Is it a problem? */ line += path_ofs; *strchrnul(line, '\n') = '\0'; @@ -497,6 +726,8 @@ int netstat_main(int argc UNUSED_PARAM, char **argv) enum { inet = 1, inet6 = 0 }; #endif + INIT_G(); + /* Option string must match NETSTAT_xxx constants */ opt = getopt32(argv, NETSTAT_OPTS); if (opt & 0x1) { // -l @@ -510,7 +741,7 @@ int netstat_main(int argc UNUSED_PARAM, char **argv) //if (opt & 0x20) // -u: NETSTAT_UDP //if (opt & 0x40) // -w: NETSTAT_RAW //if (opt & 0x80) // -x: NETSTAT_UNIX - if (opt & OPT_showroute) { // -r + if (opt & OPT_route) { // -r #if ENABLE_ROUTE bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended)); return 0; @@ -518,11 +749,15 @@ int netstat_main(int argc UNUSED_PARAM, char **argv) bb_show_usage(); #endif } - - if (opt & OPT_widedisplay) { // -W + if (opt & OPT_wide) { // -W net_conn_line = PRINT_NET_CONN_WIDE; net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE; } +#if ENABLE_FEATURE_NETSTAT_PRG + if (opt & OPT_prg) { // -p + prg_cache_load(); + } +#endif opt &= NETSTAT_ALLPROTO; if (opt) { @@ -539,23 +774,25 @@ int netstat_main(int argc UNUSED_PARAM, char **argv) else printf("(w/o servers)"); printf(net_conn_line_header, "Local Address", "Foreign Address"); + print_progname_banner(); + bb_putchar('\n'); } - if (inet && flags & NETSTAT_TCP) + if (inet && (flags & NETSTAT_TCP)) do_info(_PATH_PROCNET_TCP, "AF INET (tcp)", tcp_do_one); #if ENABLE_FEATURE_IPV6 - if (inet6 && flags & NETSTAT_TCP) + if (inet6 && (flags & NETSTAT_TCP)) do_info(_PATH_PROCNET_TCP6, "AF INET6 (tcp)", tcp_do_one); #endif - if (inet && flags & NETSTAT_UDP) + if (inet && (flags & NETSTAT_UDP)) do_info(_PATH_PROCNET_UDP, "AF INET (udp)", udp_do_one); #if ENABLE_FEATURE_IPV6 - if (inet6 && flags & NETSTAT_UDP) + if (inet6 && (flags & NETSTAT_UDP)) do_info(_PATH_PROCNET_UDP6, "AF INET6 (udp)", udp_do_one); #endif - if (inet && flags & NETSTAT_RAW) + if (inet && (flags & NETSTAT_RAW)) do_info(_PATH_PROCNET_RAW, "AF INET (raw)", raw_do_one); #if ENABLE_FEATURE_IPV6 - if (inet6 && flags & NETSTAT_RAW) + if (inet6 && (flags & NETSTAT_RAW)) do_info(_PATH_PROCNET_RAW6, "AF INET6 (raw)", raw_do_one); #endif if (flags & NETSTAT_UNIX) { @@ -566,8 +803,11 @@ int netstat_main(int argc UNUSED_PARAM, char **argv) printf("(only servers)"); else printf("(w/o servers)"); - printf("\nProto RefCnt Flags Type State I-Node Path\n"); + printf("\nProto RefCnt Flags Type State I-Node "); + print_progname_banner(); + printf("Path\n"); do_info(_PATH_PROCNET_UNIX, "AF UNIX", unix_do_one); } + prg_cache_clear(); return 0; }