diff options
author | Elliott Hughes <enh@google.com> | 2015-10-06 07:34:58 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2015-10-06 07:34:58 -0500 |
commit | 8b94351f089f8e6ef0a4a12092bfe2f04ffd960a (patch) | |
tree | 1ed86dcd9e26b5377370cfb9f4a4bfb3e6c7eb08 /toys | |
parent | 661540a99f2c346d7fb2a4b83db2e31a7d204490 (diff) | |
download | toybox-8b94351f089f8e6ef0a4a12092bfe2f04ffd960a.tar.gz |
Decode netlink sockets in lsof.
Refactor the /proc/net parsing so this only adds 7 lines overall.
Also clear the DEVICE field for sockets and fix alignment for long
usernames (until someone implements the two-pass output that measures
columns).
Diffstat (limited to 'toys')
-rw-r--r-- | toys/pending/lsof.c | 177 |
1 files changed, 92 insertions, 85 deletions
diff --git a/toys/pending/lsof.c b/toys/pending/lsof.c index ac38fddb..18013f77 100644 --- a/toys/pending/lsof.c +++ b/toys/pending/lsof.c @@ -72,7 +72,7 @@ static void print_header() char* names[] = { "COMMAND", "PID", "USER", "FD", "TYPE", "DEVICE", "SIZE/OFF", "NODE", "NAME" }; - printf("%-9s %5s %10s %4s %7s %18s %9s %10s %s\n", names[0], names[1], + printf("%-9s %5s %10.10s %4s %7s %18s %9s %10s %s\n", names[0], names[1], names[2], names[3], names[4], names[5], names[6], names[7], names[8]); TT.shown_header = 1; } @@ -88,7 +88,7 @@ static void print_info(void *data) printf("%d\n", (TT.last_shown_pid = fi->pi.pid)); } else { if (!TT.shown_header) print_header(); - printf("%-9s %5d %10s %4s%c%c %7s %18s %9s %10s %s\n", + printf("%-9s %5d %10.10s %4s%c%c %7s %18s %9s %10s %s\n", fi->pi.cmd, fi->pi.pid, fi->pi.user, fi->fd, fi->rw, fi->locks, fi->type, fi->device, fi->size_off, fi->node, fi->name); @@ -129,9 +129,11 @@ static char *chomp(char *s) return s; } -static int find_unix_socket(struct file_info *fi, long sought_inode) +static int scan_proc_net_file(char *path, int family, char type, + void (*fn)(char *, int, char, struct file_info *, long), + struct file_info *fi, long sought_inode) { - FILE *fp = fopen("/proc/net/unix", "r"); + FILE *fp = fopen(path, "r"); char *line = NULL; size_t line_length = 0; @@ -140,19 +142,8 @@ static int find_unix_socket(struct file_info *fi, long sought_inode) if (!getline(&line, &line_length, fp)) return 0; // Skip header. while (getline(&line, &line_length, fp) > 0) { - long inode; - int path_pos; - - if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", - &inode, &path_pos) >= 1) { - if (inode == sought_inode) { - char *name = chomp(line + path_pos); - - strcpy(fi->type, "unix"); - fi->name = strdup(*name ? name : "socket"); - break; - } - } + fn(line, family, type, fi, sought_inode); + if (fi->name != 0) break; } free(line); @@ -161,87 +152,102 @@ static int find_unix_socket(struct file_info *fi, long sought_inode) return fi->name != 0; } -// Matches lines in either /proc/net/tcp or /proc/net/tcp6, depending on 'af'. -static int ip_match(int af, char* line, struct in6_addr* l, int* l_port, - struct in6_addr* r, int* r_port, int* state, long* inode) +static void match_unix(char *line, int af, char type, struct file_info *fi, + long sought_inode) { - if (af == AF_INET) { - return sscanf(line, " %*d: %x:%x %x:%x %x %*x:%*x %*X:%*X %*X %*d %*d %ld", - &(l->s6_addr32[0]), l_port, &(r->s6_addr32[0]), r_port, - state, inode) == 6; - } else { - return sscanf(line, " %*d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x " - "%*x:%*x %*X:%*X %*X %*d %*d %ld", - &(l->s6_addr32[0]), &(l->s6_addr32[1]), &(l->s6_addr32[2]), - &(l->s6_addr32[3]), l_port, &(r->s6_addr32[0]), - &(r->s6_addr32[1]), &(r->s6_addr32[2]), &(r->s6_addr32[3]), - r_port, state, inode) == 12; + long inode; + int path_pos; + + if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", &inode, &path_pos) >= 1 && + inode == sought_inode) { + char *name = chomp(line + path_pos); + + strcpy(fi->type, "unix"); + fi->name = strdup(*name ? name : "socket"); } } -static int find_ip_socket(struct file_info *fi, const char *path, - int af, int type, long sought_inode) +static void match_netlink(char *line, int af, char type, struct file_info *fi, + long sought_inode) +{ + unsigned state; + long inode; + char *netlink_states[] = { + "ROUTE", "UNUSED", "USERSOCK", "FIREWALL", "SOCK_DIAG", "NFLOG", "XFRM", + "SELINUX", "ISCSI", "AUDIT", "FIB_LOOKUP", "CONNECTOR", "NETFILTER", + "IP6_FW", "DNRTMSG", "KOBJECT_UEVENT", "GENERIC", "DM", "SCSITRANSPORT", + "ENCRYPTFS", "RDMA", "CRYPTO" + }; + + if (sscanf(line, "%*p %u %*u %*x %*u %*u %*u %*u %*u %lu", + &state, &inode) < 2 || inode != sought_inode) { + return; + } + + strcpy(fi->type, "netlink"); + fi->name = + strdup(state < ARRAY_LEN(netlink_states) ? netlink_states[state] : "?"); +} + +static void match_ip(char *line, int af, char type, struct file_info *fi, + long sought_inode) { - FILE *fp = fopen(path, "r"); - char *line = NULL; - size_t line_length = 0; char *tcp_states[] = { "UNKNOWN", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING" }; + char local_ip[INET6_ADDRSTRLEN] = {0}; + char remote_ip[INET6_ADDRSTRLEN] = {0}; + struct in6_addr local, remote; + int local_port, remote_port, state; + long inode; + int ok; - if (!fp) return 0; - - if (!getline(&line, &line_length, fp)) return 0; // Skip header. - - while (getline(&line, &line_length, fp) > 0) { - struct in6_addr local, remote; - int local_port, remote_port, state; - long inode; - - if (ip_match(af, line, &local, &local_port, &remote, &remote_port, - &state, &inode)) { - if (inode == sought_inode) { - char local_ip[INET6_ADDRSTRLEN] = {0}; - char remote_ip[INET6_ADDRSTRLEN] = {0}; - - strcpy(fi->type, af == AF_INET ? "IPv4" : "IPv6"); - inet_ntop(af, &local, local_ip, sizeof(local_ip)); - inet_ntop(af, &remote, remote_ip, sizeof(remote_ip)); - if (type == SOCK_STREAM) { - if (state < 0 || state > TCP_CLOSING) state = 0; - fi->name = xmprintf(af == AF_INET ? - "TCP %s:%d->%s:%d (%s)" : - "TCP [%s]:%d->[%s]:%d (%s)", - local_ip, local_port, remote_ip, remote_port, - tcp_states[state]); - } else { - fi->name = xmprintf(af == AF_INET ? - "%s %s:%d->%s:%d" : "%s [%s]:%d->[%s]:%d", - type == SOCK_DGRAM ? "UDP" : "RAW", - local_ip, local_port, remote_ip, remote_port); - } - break; - } - } + if (af == 4) { + ok = sscanf(line, " %*d: %x:%x %x:%x %x %*x:%*x %*X:%*X %*X %*d %*d %ld", + &(local.s6_addr32[0]), &local_port, + &(remote.s6_addr32[0]), &remote_port, + &state, &inode) == 6; + } else { + ok = sscanf(line, " %*d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x " + "%*x:%*x %*X:%*X %*X %*d %*d %ld", + &(local.s6_addr32[0]), &(local.s6_addr32[1]), + &(local.s6_addr32[2]), &(local.s6_addr32[3]), + &local_port, + &(remote.s6_addr32[0]), &(remote.s6_addr32[1]), + &(remote.s6_addr32[2]), &(remote.s6_addr32[3]), + &remote_port, &state, &inode) == 12; + } + if (!ok || inode != sought_inode) return; + + strcpy(fi->type, af == 4 ? "IPv4" : "IPv6"); + inet_ntop(af, &local, local_ip, sizeof(local_ip)); + inet_ntop(af, &remote, remote_ip, sizeof(remote_ip)); + if (type == 't') { + if (state < 0 || state > TCP_CLOSING) state = 0; + fi->name = xmprintf(af == 4 ? + "TCP %s:%d->%s:%d (%s)" : + "TCP [%s]:%d->[%s]:%d (%s)", + local_ip, local_port, remote_ip, remote_port, + tcp_states[state]); + } else { + fi->name = xmprintf(af == 4 ? "%s %s:%d->%s:%d" : "%s [%s]:%d->[%s]:%d", + type == 'u' ? "UDP" : "RAW", + local_ip, local_port, remote_ip, remote_port); } - - free(line); - fclose(fp); - - return fi->name != 0; } static int find_socket(struct file_info *fi, long inode) { - // TODO: other protocols (netlink). - return find_unix_socket(fi, inode) || - find_ip_socket(fi, "/proc/net/tcp", AF_INET, SOCK_STREAM, inode) || - find_ip_socket(fi, "/proc/net/tcp6", AF_INET6, SOCK_STREAM, inode) || - find_ip_socket(fi, "/proc/net/udp", AF_INET, SOCK_DGRAM, inode) || - find_ip_socket(fi, "/proc/net/udp6", AF_INET6, SOCK_DGRAM, inode) || - find_ip_socket(fi, "/proc/net/raw", AF_INET, SOCK_RAW, inode) || - find_ip_socket(fi, "/proc/net/raw6", AF_INET6, SOCK_RAW, inode); + // TODO: other protocols (packet). + return scan_proc_net_file("/proc/net/tcp", 4, 't', match_ip, fi, inode) || + scan_proc_net_file("/proc/net/tcp6", 6, 't', match_ip, fi, inode) || + scan_proc_net_file("/proc/net/udp", 4, 'u', match_ip, fi, inode) || + scan_proc_net_file("/proc/net/udp6", 6, 'u', match_ip, fi, inode) || + scan_proc_net_file("/proc/net/raw", 4, 'r', match_ip, fi, inode) || + scan_proc_net_file("/proc/net/raw6", 6, 'r', match_ip, fi, inode) || + scan_proc_net_file("/proc/net/unix", 0, 0, match_unix, fi, inode) || + scan_proc_net_file("/proc/net/netlink", 0, 0, match_netlink, fi, inode); } static void fill_stat(struct file_info *fi, const char* path) @@ -269,8 +275,9 @@ static void fill_stat(struct file_info *fi, const char* path) // Fill DEVICE. dev = (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) ? sb.st_rdev : sb.st_dev; - snprintf(fi->device, sizeof(fi->device), "%ld,%ld", - (long)major(dev), (long)minor(dev)); + if (!S_ISSOCK(sb.st_mode)) + snprintf(fi->device, sizeof(fi->device), "%ld,%ld", + (long)major(dev), (long)minor(dev)); // Fill SIZE/OFF. if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) |