ref: d394e8d5f0e2f20479dc3e0a9a5631712720ec8e
parent: 42897a39aaa6e9448d000b7ee81faf71a731a585
author: Jacob Moody <moody@posixcafe.org>
date: Mon Mar 17 21:32:40 EDT 2025
netstat: rewrite in awk
--- /dev/null
+++ b/rc/bin/netstat
@@ -1,0 +1,97 @@
+#!/bin/rc
+rfork e
+flagfmt='i, n, t:time, p:proto proto'
+args='[netmtpt]'
+netmtpt=/net
+if(! ifs=() eval `{aux/getflags $*}){
+ aux/usage
+ exit usage
+}
+if(! ~ $1 '')
+ netmtpt=$1
+cd $netmtpt || exit 'invalid netmtpt'
+
+if(~ $#flagi 1){
+ cat ipifc/*/status | awk '
+ /^device/{
+ device=$2
+ mtu=$4
+ pktin=$28
+ pktout=$30
+ errin=$32
+ errout=$34
+ }
+ /^\t/{
+ if(length(device) > dlen)
+ dlen = length(device)
+ if(length($1) > iplen)
+ iplen = length($1)
+ if(length($3) > iplen)
+ iplen = length($3)
+ output[i++] = device" "mtu" "$1" "$2" "$3" "pktin" "pktout" "errin" "errout
+ }
+ END{
+ for(i=0; i < length(output); i++){
+ split(output[i], a)
+ printf("%-*s %5s %-*s %5s %-*s", dlen, a[1], a[2], iplen, a[3], a[4], iplen, a[5])
+ printf("%8s %8s %8s %8s\n", pktin, pktout, errin, errout)
+ }
+ }
+ '
+ exit
+}
+
+if(~ $#proto 0){
+ # use ls to avoid sorting, compatability with old netstat
+ for(i in `{ls -n})
+ if(! ~ $i 'ipifc' && ls $i/0/local >/dev/null >[2=1])
+ proto=($proto $i)
+}
+now=''
+if(~ $#time 1)
+ now=`{date -n}
+{ for(i in $proto)@{
+ cd $i && walk -n0,1 -e nUm | awk -v 'now='^$now -v 'flagn='$#flagn -v 'proto='$i '
+ function cat(f, v){
+ getline v < f
+ close(f)
+ return v
+ }
+ function q(s){
+ gsub("''", "''''", s)
+ return "''"s"''"
+ }
+ function csquery(p, inkey, outkey, cache, v, n, a){
+ if(flagn == 1)
+ return p
+ if(cache[p] != "")
+ return cache[p]
+ system("ndb/query -c "inkey" "q(p)" "outkey" >/env/output >[2]/dev/null")
+ v = cat("/env/output")
+ if(v == "")
+ v = p
+ cache[p] = v
+ return v
+ }
+ function getport(p){
+ return csquery(p, "port", proto, portcache)
+ }
+ function getdns(p){
+ return csquery(p, "ip", "dom", dnscache)
+ }
+ /^[0-9]/{
+ split(cat($1"/status"), state, " ")
+ split(cat($1"/local"), local, "!")
+ split(cat($1"/remote"), remote, "!")
+ if(now != "")
+ printf("%-7s ", now-$3)
+ printf("%-6s %-4s %-10s %-12s ", proto, $1, $2, state[1])
+ printf("%-10s %-10s %-10s\n", getport(local[2]), getport(remote[2]), getdns(remote[1]))
+ }
+ '
+}} | {
+ if(~ $#time 1)
+ sort -n
+ if not
+ cat
+}
--- a/sys/man/1/netstat
+++ b/sys/man/1/netstat
@@ -4,7 +4,7 @@
.SH SYNOPSIS
.B netstat
[
-.B -in
+.B -int
] [
.B -p
.I proto
@@ -42,6 +42,10 @@
.TP
.B -p
Show only connections with the given protocol.
+.TP
+-B -t
+Prefix connections with how many seconds they've been alive.
+The output is sorted with the youngest connections first.
.PD
.SH FILES
.B /net/*/*
--- a/sys/src/cmd/netstat.c
+++ /dev/null
@@ -1,238 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <ip.h>
-#include <ndb.h>
-
-void pip(char*, Dir*);
-void nstat(char*, void (*)(char*, Dir*));
-void pipifc(void);
-
-Biobuf out;
-char *netroot;
-char *proto[20];
-int nproto;
-int notrans;
-
-void
-usage(void)
-{
- fprint(2, "usage: %s [-in] [-p proto] [network-dir]\n", argv0);
- exits("usage");
-}
-
-void
-main(int argc, char *argv[])
-{
- int justinterfaces = 0;
- int i, tot, fd;
- Dir *d;
- char buf[128];
-
- ARGBEGIN{
- case 'i':
- justinterfaces = 1;
- break;
- case 'n':
- notrans = 1;
- break;
- case 'p':
- if(nproto >= nelem(proto))
- sysfatal("too many protos");
- proto[nproto++] = EARGF(usage());
- break;
- default:
- usage();
- }ARGEND;
-
- netroot = "/net";
- switch(argc){
- case 0:
- break;
- case 1:
- netroot = argv[0];
- break;
- default:
- usage();
- }
-
- Binit(&out, 1, OWRITE);
-
- if(justinterfaces){
- pipifc();
- exits(0);
- }
-
- if(nproto){
- for(i=0; i<nproto; i++)
- nstat(proto[i], pip);
- }else{
- fd = open(netroot, OREAD);
- if(fd < 0)
- sysfatal("open %s: %r", netroot);
-
- tot = dirreadall(fd, &d);
- for(i=0; i<tot; i++){
- if(strcmp(d[i].name, "ipifc") == 0)
- continue;
- snprint(buf, sizeof buf, "%s/%s/0/local", netroot, d[i].name);
- if(access(buf, 0) >= 0)
- nstat(d[i].name, pip);
- }
- }
- exits(0);
-}
-
-void
-nstat(char *net, void (*f)(char*, Dir*))
-{
- int fdir, i, tot;
- Dir *dir;
- char buf[128];
-
- snprint(buf, sizeof buf, "%s/%s", netroot, net);
- fdir = open(buf, OREAD);
- if(fdir < 0)
- return;
-
- tot = dirreadall(fdir, &dir);
- for(i = 0; i < tot; i++) {
- (*f)(net, &dir[i]);
- Bflush(&out);
- }
- free(dir);
- close(fdir);
-}
-
-char*
-getport(char *net, char *p)
-{
- static char port[10];
-
- strncpy(port, p, sizeof(port)-1);
- port[sizeof(port)-1] = 0;
- if(notrans || (p = csgetvalue(netroot, "port", p, net, nil)) == nil)
- return port;
- strncpy(port, p, sizeof(port)-1);
- port[sizeof(port)-1] = 0;
- free(p);
- return port;
-}
-
-void
-pip(char *net, Dir *db)
-{
- int n, fd;
- char buf[128], *p;
- char *dname;
-
- if(strcmp(db->name, "clone") == 0)
- return;
- if(strcmp(db->name, "stats") == 0)
- return;
-
- snprint(buf, sizeof buf, "%s/%s/%s/status", netroot, net, db->name);
- fd = open(buf, OREAD);
- if(fd < 0)
- return;
- n = read(fd, buf, sizeof(buf));
- close(fd);
- if(n < 0)
- return;
- buf[n] = 0;
-
- p = strchr(buf, ' ');
- if(p != 0)
- *p = 0;
- p = strrchr(buf, '\n');
- if(p != 0)
- *p = 0;
- Bprint(&out, "%-4s %-4s %-10s %-12s ", net, db->name, db->uid, buf);
-
- snprint(buf, sizeof buf, "%s/%s/%s/local", netroot, net, db->name);
- fd = open(buf, OREAD);
- if(fd < 0) {
- Bprint(&out, "\n");
- return;
- }
- n = read(fd, buf, sizeof(buf));
- close(fd);
- if(n < 0) {
- Bprint(&out, "\n");
- return;
- }
- buf[n-1] = 0;
- p = strchr(buf, '!');
- if(p == 0) {
- Bprint(&out, "\n");
- return;
- }
- *p = '\0';
- Bprint(&out, "%-10s ", getport(net, p+1));
-
- snprint(buf, sizeof buf, "%s/%s/%s/remote", netroot, net, db->name);
- fd = open(buf, OREAD);
- if(fd < 0) {
- print("\n");
- return;
- }
- n = read(fd, buf, sizeof(buf));
- close(fd);
- if(n < 0) {
- print("\n");
- return;
- }
- buf[n-1] = 0;
- p = strchr(buf, '!');
- if(p != nil)
- *p++ = '\0';
-
- if(notrans){
- Bprint(&out, "%-10s %s\n", getport(net, p), buf);
- return;
- }
- dname = csgetvalue(netroot, "ip", buf, "dom", nil);
- if(dname == nil) {
- Bprint(&out, "%-10s %s\n", getport(net, p), buf);
- return;
- }
- Bprint(&out, "%-10s %s\n", getport(net, p), dname);
- Bflush(&out);
- free(dname);
-}
-
-void
-pipifc(void)
-{
- Ipifc *ip, *nip;
- Iplifc *lifc;
- char buf[100];
- int l, i;
-
- fmtinstall('I', eipfmt);
- fmtinstall('M', eipfmt);
-
- ip = readipifc(netroot, nil, -1);
-
- l = 7;
- for(nip = ip; nip; nip = nip->next){
- for(lifc = nip->lifc; lifc; lifc = lifc->next){
- i = snprint(buf, sizeof buf, "%I", lifc->ip);
- if(i > l)
- l = i;
- i = snprint(buf, sizeof buf, "%I", lifc->net);
- if(i > l)
- l = i;
- }
- }
-
- for(nip = ip; nip; nip = nip->next){
- for(lifc = nip->lifc; lifc; lifc = lifc->next)
- Bprint(&out, "%-12s %5d %-*I %5M %-*I %8lud %8lud %8lud %8lud\n",
- nip->dev, nip->mtu,
- l, lifc->ip, lifc->mask, l, lifc->net,
- nip->pktin, nip->pktout,
- nip->errin, nip->errout);
- }
- Bflush(&out);
-}
--
⑨