The branch main has been updated by asomers: URL: https://cgit.FreeBSD.org/src/commit/?id=7b35b4d196309baf579571e1c1a433a4000d74c9
commit 7b35b4d196309baf579571e1c1a433a4000d74c9 Author: Damin Rido <r...@freebsd.org> AuthorDate: 2025-07-16 17:20:15 +0000 Commit: Alan Somers <asom...@freebsd.org> CommitDate: 2025-07-30 20:27:14 +0000 sockstat: add libxo support Sponsored by: Google, LLC (GSoC 2025) MFC after: 2 weeks Reviewed by: asomers Pull Request: https://github.com/freebsd/freebsd-src/pull/1770 Relnotes: yes --- usr.bin/sockstat/Makefile | 2 +- usr.bin/sockstat/sockstat.1 | 17 +- usr.bin/sockstat/sockstat.c | 467 +++++++++++++++++++++++++++----------------- 3 files changed, 306 insertions(+), 180 deletions(-) diff --git a/usr.bin/sockstat/Makefile b/usr.bin/sockstat/Makefile index 188432dfc27e..7254511f21c6 100644 --- a/usr.bin/sockstat/Makefile +++ b/usr.bin/sockstat/Makefile @@ -2,7 +2,7 @@ PROG= sockstat -LIBADD= jail +LIBADD= jail xo .if ${MK_CASPER} != "no" LIBADD+= casper diff --git a/usr.bin/sockstat/sockstat.1 b/usr.bin/sockstat/sockstat.1 index 4832a09764fd..091911cd0879 100644 --- a/usr.bin/sockstat/sockstat.1 +++ b/usr.bin/sockstat/sockstat.1 @@ -25,7 +25,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd June 30, 2025 +.Dd July 17, 2025 .Dt SOCKSTAT 1 .Os .Sh NAME @@ -33,6 +33,7 @@ .Nd list open sockets .Sh SYNOPSIS .Nm +.Op Fl -libxo .Op Fl 46ACcfIiLlnqSsUuvw .Op Fl j Ar jail .Op Fl p Ar ports @@ -46,6 +47,13 @@ domain sockets. .Pp The following options are available: .Bl -tag -width Fl +.It Fl -libxo +Generate output via +.Xr libxo 3 +in a selection of different human and machine readable formats. +See +.Xr xo_options 7 +for details on command line arguments. .It Fl 4 Show .Dv AF_INET @@ -229,6 +237,11 @@ Show TCP IPv6 sockets which are listening and connected (default): .Bd -literal -offset indent $ sockstat -6 -P tcp .Ed +.Pp +Show all sockets in JSON format with neat alignment: +.Bd -literal -offset indent +$ sockstat --libxo json,pretty +.Ed .Sh SEE ALSO .Xr fstat 1 , .Xr netstat 1 , @@ -237,6 +250,8 @@ $ sockstat -6 -P tcp .Xr inet 4 , .Xr inet6 4 , .Xr protocols 5 +.Xr libxo 3 , +.Xr xo_options 7 .Sh HISTORY The .Nm diff --git a/usr.bin/sockstat/sockstat.c b/usr.bin/sockstat/sockstat.c index d0540c54a1aa..7355eaa272a0 100644 --- a/usr.bin/sockstat/sockstat.c +++ b/usr.bin/sockstat/sockstat.c @@ -55,7 +55,6 @@ #include <capsicum_helpers.h> #include <ctype.h> -#include <err.h> #include <errno.h> #include <inttypes.h> #include <jail.h> @@ -67,6 +66,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <libxo/xo.h> #include <libcasper.h> #include <casper/cap_net.h> @@ -74,6 +74,7 @@ #include <casper/cap_pwd.h> #include <casper/cap_sysctl.h> +#define SOCKSTAT_XO_VERSION "1" #define sstosin(ss) ((struct sockaddr_in *)(ss)) #define sstosin6(ss) ((struct sockaddr_in6 *)(ss)) #define sstosun(ss) ((struct sockaddr_un *)(ss)) @@ -197,7 +198,7 @@ static bool _check_ksize(size_t received_size, size_t expected_size, const char *struct_name) { if (received_size != expected_size) { - warnx("%s size mismatch: expected %zd, received %zd", + xo_warnx("%s size mismatch: expected %zd, received %zd", struct_name, expected_size, received_size); return false; } @@ -209,7 +210,7 @@ static void _enforce_ksize(size_t received_size, size_t expected_size, const char *struct_name) { if (received_size != expected_size) { - errx(1, "fatal: struct %s size mismatch: expected %zd, received %zd", + xo_errx(1, "fatal: struct %s size mismatch: expected %zd, received %zd", struct_name, expected_size, received_size); } } @@ -227,7 +228,7 @@ get_proto_type(const char *proto) else pent = getprotobyname(proto); if (pent == NULL) { - warn("cap_getprotobyname"); + xo_warn("cap_getprotobyname"); return (-1); } return (pent->p_proto); @@ -248,7 +249,7 @@ init_protos(int num) } if ((protos = malloc(sizeof(int) * proto_count)) == NULL) - err(1, "malloc"); + xo_err(1, "malloc"); numprotos = proto_count; } @@ -282,17 +283,17 @@ parse_ports(const char *portspec) if (ports == NULL) if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL) - err(1, "calloc()"); + xo_err(1, "calloc()"); p = portspec; while (*p != '\0') { if (!isdigit(*p)) - errx(1, "syntax error in port range"); + xo_errx(1, "syntax error in port range"); for (q = p; *q != '\0' && isdigit(*q); ++q) /* nothing */ ; for (port = 0; p < q; ++p) port = port * 10 + digittoint(*p); if (port < 0 || port > 65535) - errx(1, "invalid port number"); + xo_errx(1, "invalid port number"); SET_PORT(port); switch (*p) { case '-': @@ -310,7 +311,7 @@ parse_ports(const char *portspec) for (end = 0; p < q; ++p) end = end * 10 + digittoint(*p); if (end < port || end > 65535) - errx(1, "invalid port number"); + xo_errx(1, "invalid port number"); while (port++ < end) SET_PORT(port); if (*p == ',') @@ -395,15 +396,15 @@ gather_sctp(void) varname = "net.inet.sctp.assoclist"; if (cap_sysctlbyname(capsysctl, varname, 0, &len, 0, 0) < 0) { if (errno != ENOENT) - err(1, "cap_sysctlbyname()"); + xo_err(1, "cap_sysctlbyname()"); return; } if ((buf = (char *)malloc(len)) == NULL) { - err(1, "malloc()"); + xo_err(1, "malloc()"); return; } if (cap_sysctlbyname(capsysctl, varname, buf, &len, 0, 0) < 0) { - err(1, "cap_sysctlbyname()"); + xo_err(1, "cap_sysctlbyname()"); free(buf); return; } @@ -411,7 +412,7 @@ gather_sctp(void) offset = sizeof(struct xsctp_inpcb); while ((offset < len) && (xinpcb->last == 0)) { if ((sock = calloc(1, sizeof *sock)) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; @@ -439,7 +440,7 @@ gather_sctp(void) if (xladdr->last == 1) break; if ((laddr = calloc(1, sizeof(struct addr))) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ @@ -461,7 +462,7 @@ gather_sctp(void) htons(xinpcb->local_port)); break; default: - errx(1, "address family %d not supported", + xo_errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } laddr->next = NULL; @@ -474,7 +475,7 @@ gather_sctp(void) if (sock->laddr == NULL) { if ((sock->laddr = calloc(1, sizeof(struct addr))) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); sock->laddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->laddr->address.ss_len = @@ -485,7 +486,7 @@ gather_sctp(void) local_all_loopback = 0; } if ((sock->faddr = calloc(1, sizeof(struct addr))) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); sock->faddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->faddr->address.ss_len = @@ -512,7 +513,7 @@ gather_sctp(void) no_stcb = 0; if (opt_c) { if ((sock = calloc(1, sizeof *sock)) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; @@ -542,7 +543,7 @@ gather_sctp(void) continue; laddr = calloc(1, sizeof(struct addr)); if (laddr == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ @@ -564,7 +565,7 @@ gather_sctp(void) htons(xstcb->local_port)); break; default: - errx(1, + xo_errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } @@ -587,7 +588,7 @@ gather_sctp(void) continue; faddr = calloc(1, sizeof(struct addr)); if (faddr == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); switch (xraddr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ @@ -609,7 +610,7 @@ gather_sctp(void) htons(xstcb->remote_port)); break; default: - errx(1, + xo_errx(1, "address family %d not supported", xraddr->address.sa.sa_family); } @@ -673,7 +674,7 @@ gather_inet(int proto) protoname = "div"; break; default: - errx(1, "protocol %d not supported", proto); + xo_errx(1, "protocol %d not supported", proto); } buf = NULL; @@ -682,7 +683,7 @@ gather_inet(int proto) do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) - err(1, "realloc()"); + xo_err(1, "realloc()"); len = bufsize; if (cap_sysctlbyname(capsysctl, varname, buf, &len, NULL, 0) == 0) @@ -690,7 +691,7 @@ gather_inet(int proto) if (errno == ENOENT) goto out; if (errno != ENOMEM || len != bufsize) - err(1, "cap_sysctlbyname()"); + xo_err(1, "cap_sysctlbyname()"); bufsize *= 2; } xig = (struct xinpgen *)buf; @@ -701,7 +702,7 @@ gather_inet(int proto) } while (xig->xig_gen != exig->xig_gen && retry--); if (xig->xig_gen != exig->xig_gen && opt_v) - warnx("warning: data may be inconsistent"); + xo_warnx("warning: data may be inconsistent"); for (;;) { xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); @@ -722,7 +723,7 @@ gather_inet(int proto) goto out; break; default: - errx(1, "protocol %d not supported", proto); + xo_errx(1, "protocol %d not supported", proto); } so = &xip->xi_socket; if ((xip->inp_vflag & vflag) == 0) @@ -748,15 +749,15 @@ gather_inet(int proto) continue; } else { if (opt_v) - warnx("invalid vflag 0x%x", xip->inp_vflag); + xo_warnx("invalid vflag 0x%x", xip->inp_vflag); continue; } if ((sock = calloc(1, sizeof(*sock))) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); sock->socket = so->xso_so; sock->pcb = so->so_pcb; sock->splice_socket = so->so_splice_so; @@ -822,7 +823,9 @@ gather_unix(int proto) break; case SOCK_SEQPACKET: varname = "net.local.seqpacket.pcblist"; - protoname = "seqpac"; + protoname = (xo_get_style(NULL) == XO_STYLE_TEXT) + ? "seqpac" + : "seqpacket"; break; default: abort(); @@ -833,13 +836,13 @@ gather_unix(int proto) do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) - err(1, "realloc()"); + xo_err(1, "realloc()"); len = bufsize; if (cap_sysctlbyname(capsysctl, varname, buf, &len, NULL, 0) == 0) break; if (errno != ENOMEM || len != bufsize) - err(1, "cap_sysctlbyname()"); + xo_err(1, "cap_sysctlbyname()"); bufsize *= 2; } xug = (struct xunpgen *)buf; @@ -851,7 +854,7 @@ gather_unix(int proto) } while (xug->xug_gen != exug->xug_gen && retry--); if (xug->xug_gen != exug->xug_gen && opt_v) - warnx("warning: data may be inconsistent"); + xo_warnx("warning: data may be inconsistent"); for (;;) { xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len); @@ -864,11 +867,11 @@ gather_unix(int proto) (xup->unp_conn != 0 && !opt_c)) continue; if ((sock = calloc(1, sizeof(*sock))) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); sock->socket = xup->xu_socket.xso_so; sock->pcb = xup->xu_unpp; sock->proto = proto; @@ -899,21 +902,21 @@ getfiles(void) olen = len = sizeof(*xfiles); if ((xfiles = malloc(len)) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); while (cap_sysctlbyname(capsysctl, "kern.file", xfiles, &len, 0, 0) == -1) { if (errno != ENOMEM || len != olen) - err(1, "cap_sysctlbyname()"); + xo_err(1, "cap_sysctlbyname()"); olen = len *= 2; if ((xfiles = realloc(xfiles, len)) == NULL) - err(1, "realloc()"); + xo_err(1, "realloc()"); } if (len > 0) enforce_ksize(xfiles->xf_size, struct xfile); nfiles = len / sizeof(*xfiles); if ((files = malloc(nfiles * sizeof(struct file))) == NULL) - err(1, "malloc()"); + xo_err(1, "malloc()"); for (int i = 0; i < nfiles; i++) { files[i].xf_data = xfiles[i].xf_data; @@ -932,6 +935,7 @@ formataddr(struct sockaddr_storage *ss, char *buf, size_t bufsize) struct sockaddr_un *sun; char addrstr[NI_MAXHOST] = { '\0', '\0' }; int error, off, port = 0; + const bool is_text_style = (xo_get_style(NULL) == XO_STYLE_TEXT); switch (ss->ss_family) { case AF_INET: @@ -947,6 +951,11 @@ formataddr(struct sockaddr_storage *ss, char *buf, size_t bufsize) case AF_UNIX: sun = sstosun(ss); off = (int)((char *)&sun->sun_path - (char *)sun); + if (!is_text_style) { + xo_emit("{:path/%.*s}", sun->sun_len - off, + sun->sun_path); + return 0; + } return snprintf(buf, bufsize, "%.*s", sun->sun_len - off, sun->sun_path); } @@ -954,7 +963,12 @@ formataddr(struct sockaddr_storage *ss, char *buf, size_t bufsize) error = cap_getnameinfo(capnet, sstosa(ss), ss->ss_len, addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST); if (error) - errx(1, "cap_getnameinfo()"); + xo_errx(1, "cap_getnameinfo()"); + } + if (!is_text_style) { + xo_emit("{:address/%s}", addrstr); + xo_emit("{:port/%d}", port); + return 0; } if (port == 0) return snprintf(buf, bufsize, "%s:*", addrstr); @@ -977,7 +991,7 @@ getprocname(pid_t pid) == -1) { /* Do not warn if the process exits before we get its name. */ if (errno != ESRCH) - warn("cap_sysctl()"); + xo_warn("cap_sysctl()"); return ("??"); } return (proc.ki_comm); @@ -999,7 +1013,7 @@ getprocjid(pid_t pid) == -1) { /* Do not warn if the process exits before we get its jid. */ if (errno != ESRCH) - warn("cap_sysctl()"); + xo_warn("cap_sysctl()"); return (-1); } return (proc.ki_jid); @@ -1099,13 +1113,15 @@ format_unix_faddr(struct addr *faddr, char *buf, size_t bufsize) { #define SAFESIZE (buf == NULL ? 0 : bufsize - pos) size_t pos = 0; - /* Remote peer we connect(2) to, if any. */ + const bool is_text_style = (xo_get_style(NULL) == XO_STYLE_TEXT); if (faddr->conn != 0) { + /* Remote peer we connect(2) to, if any. */ struct sock *p; - pos += strlcpy(SAFEBUF, "-> ", SAFESIZE); + if (is_text_style) + pos += strlcpy(SAFEBUF, "-> ", SAFESIZE); p = RB_FIND(pcbs_t, &pcbs, &(struct sock){ .pcb = faddr->conn }); - if (__predict_false(p == NULL)) { + if (__predict_false(p == NULL) && is_text_style) { /* XXGL: can this happen at all? */ pos += snprintf(SAFEBUF, SAFESIZE, "??"); } else if (p->laddr->address.ss_len == 0) { @@ -1114,34 +1130,52 @@ format_unix_faddr(struct addr *faddr, char *buf, size_t bufsize) { &(struct file){ .xf_data = p->socket }); if (f != NULL) { - pos += snprintf(SAFEBUF, SAFESIZE, "[%lu %d]", - (u_long)f->xf_pid, f->xf_fd); + if (is_text_style) { + pos += snprintf(SAFEBUF, SAFESIZE, + "[%lu %d]", (u_long)f->xf_pid, + f->xf_fd); + } else { + xo_open_list("connections"); + xo_open_instance("connections"); + xo_emit("{:pid/%lu}", (u_long)f->xf_pid); + xo_emit("{:fd/%d}", f->xf_fd); + xo_close_instance("connections"); + xo_close_list("connections"); + } } } else pos += formataddr(&p->laddr->address, SAFEBUF, SAFESIZE); - } - /* Remote peer(s) connect(2)ed to us, if any. */ - if (faddr->firstref != 0) { + } else if (faddr->firstref != 0) { + /* Remote peer(s) connect(2)ed to us, if any. */ struct sock *p; struct file *f; kvaddr_t ref = faddr->firstref; bool fref = true; - pos += snprintf(SAFEBUF, SAFESIZE, " <- "); - + if (is_text_style) + pos += snprintf(SAFEBUF, SAFESIZE, " <- "); + xo_open_list("connections"); while ((p = RB_FIND(pcbs_t, &pcbs, &(struct sock){ .pcb = ref })) != 0) { f = RB_FIND(files_t, &ftree, &(struct file){ .xf_data = p->socket }); if (f != NULL) { - pos += snprintf(SAFEBUF, SAFESIZE, - "%s[%lu %d]", fref ? "" : ",", - (u_long)f->xf_pid, f->xf_fd); + if (is_text_style) { + pos += snprintf(SAFEBUF, SAFESIZE, + "%s[%lu %d]", fref ? "" : ",", + (u_long)f->xf_pid, f->xf_fd); + } else { + xo_open_instance("connections"); + xo_emit("{:pid/%lu}", (u_long)f->xf_pid); + xo_emit("{:fd/%d}", f->xf_fd); + xo_close_instance("connections"); + } } ref = p->faddr->nextref; fref = false; } + xo_close_list("connections"); } return pos; } @@ -1183,7 +1217,7 @@ calculate_sock_column_widths(struct col_widths *cw, struct sock *s) while (laddr != NULL || faddr != NULL) { if (opt_w && s->family == AF_UNIX) { if ((laddr == NULL) || (faddr == NULL)) - errx(1, "laddr = %p or faddr = %p is NULL", + xo_errx(1, "laddr = %p or faddr = %p is NULL", (void *)laddr, (void *)faddr); if (laddr->address.ss_len > 0) len = formataddr(&laddr->address, NULL, 0); @@ -1298,6 +1332,7 @@ calculate_column_widths(struct col_widths *cw) struct sock *s; struct passwd *pwd; + cap_setpassent(cappwd, 1); for (xf = files, n = 0; n < nfiles; ++n, ++xf) { if (xf->xf_data == 0) continue; @@ -1345,65 +1380,104 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize) laddr = s->laddr; faddr = s->faddr; first = true; + const bool is_text_style = (xo_get_style(NULL) == XO_STYLE_TEXT); snprintf(buf, bufsize, "%s%s%s", s->protoname, s->vflag & INP_IPV4 ? "4" : "", s->vflag & INP_IPV6 ? "6" : ""); - printf(" %-*s", cw->proto, buf); + xo_emit(" {:proto/%-*s}", cw->proto, buf); while (laddr != NULL || faddr != NULL) { if (s->family == AF_UNIX) { if ((laddr == NULL) || (faddr == NULL)) - errx(1, "laddr = %p or faddr = %p is NULL", + xo_errx(1, "laddr = %p or faddr = %p is NULL", (void *)laddr, (void *)faddr); - if (laddr->address.ss_len > 0) + if (laddr->address.ss_len > 0) { + xo_open_container("local"); formataddr(&laddr->address, buf, bufsize); - else if (laddr->address.ss_len == 0 && faddr->conn == 0) - strlcpy(buf, "(not connected)", bufsize); - else - strlcpy(buf, "??", bufsize); - printf(" %-*.*s", cw->local_addr, cw->local_addr, buf); - if (format_unix_faddr(faddr, buf, bufsize) == 0) - strlcpy(buf, "??", bufsize); - printf(" %-*.*s", cw->foreign_addr, - cw->foreign_addr, buf); + if (is_text_style) { + xo_emit(" {:/%-*.*s}", cw->local_addr, + cw->local_addr, buf); + } + xo_close_container("local"); + } else if (laddr->address.ss_len == 0 && + faddr->conn == 0 && is_text_style) { + xo_emit(" {:/%-*.*s}", cw->local_addr, + cw->local_addr, "(not connected)"); + } else if (is_text_style) { + xo_emit(" {:/%-*.*s}", cw->local_addr, + cw->local_addr, "??"); + } + if (faddr->conn != 0 || faddr->firstref != 0) { + xo_open_container("foreign"); + int len = format_unix_faddr(faddr, buf, + bufsize); + if (len == 0 && is_text_style) + xo_emit(" {:/%-*s}", + cw->foreign_addr, "??"); + else if (is_text_style) + xo_emit(" {:/%-*.*s}", cw->foreign_addr, + cw->foreign_addr, buf); + xo_close_container("foreign"); + } else if (is_text_style) + xo_emit(" {:/%-*s}", cw->foreign_addr, "??"); } else { - if (laddr != NULL) + if (laddr != NULL) { + xo_open_container("local"); formataddr(&laddr->address, buf, bufsize); - else - strlcpy(buf, "??", bufsize); - printf(" %-*.*s", cw->local_addr, cw->local_addr, buf); - if (faddr != NULL) + if (is_text_style) { + xo_emit(" {:/%-*.*s}", cw->local_addr, + cw->local_addr, buf); + } + xo_close_container("local"); + } else if (is_text_style) + xo_emit(" {:/%-*.*s}", cw->local_addr, + cw->local_addr, "??"); + if (faddr != NULL) { + xo_open_container("foreign"); formataddr(&faddr->address, buf, bufsize); - else - strlcpy(buf, "??", bufsize); - printf(" %-*.*s", cw->foreign_addr, - cw->foreign_addr, buf); + if (is_text_style) { + xo_emit(" {:/%-*.*s}", cw->foreign_addr, + cw->foreign_addr, buf); + } + xo_close_container("foreign"); + } else if (is_text_style) { + xo_emit(" {:/%-*.*s}", cw->foreign_addr, + cw->foreign_addr, "??"); + } + } + if (opt_A) { + snprintf(buf, bufsize, "%#*" PRIx64, + cw->pcb_kva, s->pcb); + xo_emit(" {:pcb-kva/%s}", buf); } - if (opt_A) - printf(" %#*" PRIx64, cw->pcb_kva, s->pcb); if (opt_f) - printf(" %*d", cw->fib, s->fibnum); + xo_emit(" {:fib/%*d}", cw->fib, s->fibnum); if (opt_I) { if (s->splice_socket != 0) { struct sock *sp; sp = RB_FIND(socks_t, &socks, &(struct sock) { .socket = s->splice_socket }); - if (sp != NULL) + if (sp != NULL) { + xo_open_container("splice"); formataddr(&sp->laddr->address, buf, bufsize); - else + xo_close_container("splice"); + } else if (is_text_style) strlcpy(buf, "??", bufsize); - } else + } else if (is_text_style) strlcpy(buf, "??", bufsize); - printf(" %-*s", cw->splice_address, buf); + if (is_text_style) + xo_emit(" {:/%-*s}", cw->splice_address, buf); } if (opt_i) { if (s->proto == IPPROTO_TCP || s->proto == IPPROTO_UDP) - printf(" %*" PRIu64, cw->inp_gencnt, + { + snprintf(buf, bufsize, "%" PRIu64, s->inp_gencnt); - else - printf(" %*s", cw->inp_gencnt, "??"); + xo_emit(" {:id/%*s}", cw->inp_gencnt, buf); + } else if (is_text_style) + xo_emit(" {:/%*s}", cw->inp_gencnt, "??"); } if (opt_U) { if (faddr != NULL && @@ -1414,10 +1488,10 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize) (s->proto == IPPROTO_TCP && s->state != TCPS_CLOSED && s->state != TCPS_LISTEN))) { - printf(" %*u", cw->encaps, + xo_emit(" {:encaps/%*u}", cw->encaps, ntohs(faddr->encaps_port)); - } else - printf(" %*s", cw->encaps, "??"); + } else if (is_text_style) + xo_emit(" {:/%*s}", cw->encaps, "??"); } if (opt_s) { if (faddr != NULL && @@ -1425,10 +1499,10 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize) s->state != SCTP_CLOSED && s->state != SCTP_BOUND && s->state != SCTP_LISTEN) { - printf(" %-*s", cw->path_state, + xo_emit(" {:path-state/%-*s}", cw->path_state, sctp_path_state(faddr->state)); - } else - printf(" %-*s", cw->path_state, "??"); + } else if (is_text_style) + xo_emit(" {:/%-*s}", cw->path_state, "??"); } if (first) { if (opt_s) { @@ -1436,47 +1510,52 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize) s->proto == IPPROTO_TCP) { switch (s->proto) { case IPPROTO_SCTP: - printf(" %-*s", cw->conn_state, - sctp_conn_state(s->state)); + xo_emit(" {:path-state/%-*s}", + cw->path_state, + sctp_path_state( + faddr->state)); break; case IPPROTO_TCP: if (s->state >= 0 && s->state < TCP_NSTATES) - printf(" %-*s", - cw->conn_state, - tcpstates[s->state]); - else - printf(" %-*s", - cw->conn_state, "??"); + xo_emit(" {:conn-state/%-*s}", + cw->conn_state, + tcpstates[s->state]); + else if (is_text_style) + xo_emit(" {:/%-*s}", + cw->conn_state, "??"); break; } - } else - printf(" %-*s", cw->conn_state, "??"); + } else if (is_text_style) + xo_emit(" {:/%-*s}", + cw->conn_state, "??"); } if (opt_S) { if (s->proto == IPPROTO_TCP) - printf(" %-*s", cw->stack, s->stack); - else - printf(" %-*s", cw->stack, "??"); + xo_emit(" {:stack/%-*s}", + cw->stack, s->stack); + else if (is_text_style) + xo_emit(" {:/%-*s}", + cw->stack, "??"); } if (opt_C) { if (s->proto == IPPROTO_TCP) - printf(" %-*s", cw->cc, s->cc); - else - printf(" %-*s", cw->cc, "??"); + xo_emit(" {:cc/%-*s}", cw->cc, s->cc); + else if (is_text_style) + xo_emit(" {:/%-*s}", cw->cc, "??"); } } if (laddr != NULL) laddr = laddr->next; if (faddr != NULL) faddr = faddr->next; - if (laddr != NULL || faddr != NULL) - printf("%-*s %-*s %-*s %-*s %-*s", cw->user, "", - cw->command, "", cw->pid, "", cw->fd, "", - cw->proto, ""); + if (is_text_style && (laddr != NULL || faddr != NULL)) + xo_emit("{:/%-*s} {:/%-*s} {:/%*s} {:/%*s}", + cw->user, "??", cw->command, "??", + cw->pid, "??", cw->fd, "??"); first = false; } - printf("\n"); + xo_emit("\n"); } static void @@ -1490,56 +1569,63 @@ display(void) const size_t bufsize = 512; void *buf; if ((buf = (char *)malloc(bufsize)) == NULL) { - err(1, "malloc()"); + xo_err(1, "malloc()"); return; } - cw = (struct col_widths) { - .user = strlen("USER"), - .command = 10, - .pid = strlen("PID"), - .fd = strlen("FD"), - .proto = strlen("PROTO"), - .local_addr = opt_w ? strlen("LOCAL ADDRESS") : 21, - .foreign_addr = opt_w ? strlen("FOREIGN ADDRESS") : 21, - .pcb_kva = 18, - .fib = strlen("FIB"), - .splice_address = strlen("SPLICE ADDRESS"), - .inp_gencnt = strlen("ID"), - .encaps = strlen("ENCAPS"), - .path_state = strlen("PATH STATE"), - .conn_state = strlen("CONN STATE"), - .stack = strlen("STACK"), - .cc = strlen("CC"), - }; - calculate_column_widths(&cw); + if (xo_get_style(NULL) == XO_STYLE_TEXT) { + cw = (struct col_widths) { + .user = strlen("USER"), + .command = 10, + .pid = strlen("PID"), + .fd = strlen("FD"), + .proto = strlen("PROTO"), + .local_addr = opt_w ? strlen("LOCAL ADDRESS") : 21, + .foreign_addr = opt_w ? strlen("FOREIGN ADDRESS") : 21, + .pcb_kva = 18, + .fib = strlen("FIB"), + .splice_address = strlen("SPLICE ADDRESS"), + .inp_gencnt = strlen("ID"), + .encaps = strlen("ENCAPS"), + .path_state = strlen("PATH STATE"), + .conn_state = strlen("CONN STATE"), + .stack = strlen("STACK"), + .cc = strlen("CC"), + }; + calculate_column_widths(&cw); + } else + memset(&cw, 0, sizeof(cw)); + xo_set_version(SOCKSTAT_XO_VERSION); + xo_open_container("sockstat"); + xo_open_list("socket"); if (!opt_q) { - printf("%-*s %-*s %*s %*s %-*s %-*s %-*s", - cw.user, "USER", cw.command, "COMMAND", - cw.pid, "PID", cw.fd, "FD", cw.proto, "PROTO", - cw.local_addr, "LOCAL ADDRESS", - cw.foreign_addr,"FOREIGN ADDRESS"); + xo_emit("{T:/%-*s} {T:/%-*s} {T:/%*s} {T:/%*s} {T:/%-*s} " + "{T:/%-*s} {T:/%-*s}", cw.user, "USER", cw.command, + "COMMAND", cw.pid, "PID", cw.fd, "FD", cw.proto, + "PROTO", cw.local_addr, "LOCAL ADDRESS", + cw.foreign_addr, "FOREIGN ADDRESS"); if (opt_A) - printf(" %-*s", cw.pcb_kva, "PCB KVA"); + xo_emit(" {T:/%-*s}", cw.pcb_kva, "PCB KVA"); if (opt_f) /* RT_MAXFIBS is 65535. */ - printf(" %*s", cw.fib, "FIB"); + xo_emit(" {T:/%*s}", cw.fib, "FIB"); if (opt_I) - printf(" %-*s", cw.splice_address, "SPLICE ADDRESS"); + xo_emit(" {T:/%-*s}", cw.splice_address, + "SPLICE ADDRESS"); if (opt_i) - printf(" %*s", cw.inp_gencnt, "ID"); + xo_emit(" {T:/%*s}", cw.inp_gencnt, "ID"); if (opt_U) - printf(" %*s", cw.encaps, "ENCAPS"); + xo_emit(" {T:/%*s}", cw.encaps, "ENCAPS"); if (opt_s) { - printf(" %-*s", cw.path_state, "PATH STATE"); - printf(" %-*s", cw.conn_state, "CONN STATE"); + xo_emit(" {T:/%-*s}", cw.path_state, "PATH STATE"); + xo_emit(" {T:/%-*s}", cw.conn_state, "CONN STATE"); } if (opt_S) - printf(" %-*s", cw.stack, "STACK"); + xo_emit(" {T:/%-*s}", cw.stack, "STACK"); if (opt_C) - printf(" %-*s", cw.cc, "CC"); - printf("\n"); + xo_emit(" {T:/%-*s}", cw.cc, "CC"); + xo_emit("\n"); } cap_setpassent(cappwd, 1); for (xf = files, n = 0; n < nfiles; ++n, ++xf) { @@ -1550,17 +1636,24 @@ display(void) s = RB_FIND(socks_t, &socks, &(struct sock){ .socket = xf->xf_data}); if (s != NULL && check_ports(s)) { + xo_open_instance("socket"); s->shown = 1; if (opt_n || (pwd = cap_getpwuid(cappwd, xf->xf_uid)) == NULL) - printf("%-*lu", cw.user, (u_long)xf->xf_uid); + xo_emit("{:user/%-*lu}", cw.user, + (u_long)xf->xf_uid); + else + xo_emit("{:user/%-*s}", cw.user, pwd->pw_name); + if (xo_get_style(NULL) == XO_STYLE_TEXT) + xo_emit(" {:/%-*.10s}", cw.command, + getprocname(xf->xf_pid)); else - printf("%-*s", cw.user, pwd->pw_name); - printf(" %-*.*s", cw.command, cw.command, - getprocname(xf->xf_pid)); - printf(" %*lu", cw.pid, (u_long)xf->xf_pid); - printf(" %*d", cw.fd, xf->xf_fd); + xo_emit(" {:command/%-*s}", cw.command, + getprocname(xf->xf_pid)); + xo_emit(" {:pid/%*lu}", cw.pid, (u_long)xf->xf_pid); + xo_emit(" {:fd/%*d}", cw.fd, xf->xf_fd); display_sock(s, &cw, buf, bufsize); + xo_close_instance("socket"); } } if (opt_j >= 0) @@ -1568,20 +1661,33 @@ display(void) SLIST_FOREACH(s, &nosocks, socket_list) { if (!check_ports(s)) continue; - printf("%-*s %-*s %*s %*s", cw.user, "??", cw.command, "??", - cw.pid, "??", cw.fd, "??"); + xo_open_instance("socket"); + if (xo_get_style(NULL) == XO_STYLE_TEXT) + xo_emit("{:/%-*s} {:/%-*s} {:/%*s} {:/%*s}", + cw.user, "??", cw.command, "??", + cw.pid, "??", cw.fd, "??"); display_sock(s, &cw, buf, bufsize); + xo_close_instance("socket"); } RB_FOREACH(s, socks_t, &socks) { if (s->shown) continue; if (!check_ports(s)) continue; - printf("%-*s %-*s %*s %*s", cw.user, "??", cw.command, "??", - cw.pid, "??", cw.fd, "??"); + xo_open_instance("socket"); + if (xo_get_style(NULL) == XO_STYLE_TEXT) + xo_emit("{:/%-*s} {:/%-*s} {:/%*s} {:/%*s}", + cw.user, "??", cw.command, "??", + cw.pid, "??", cw.fd, "??"); display_sock(s, &cw, buf, bufsize); + xo_close_instance("socket"); } + xo_close_list("socket"); + xo_close_container("sockstat"); *** 103 LINES SKIPPED ***