Add the fstat -s option to display socket information.

Reviewed by:	jilles
MFC after:	1 week
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D21880
This commit is contained in:
jlh 2019-10-19 19:52:19 +00:00
parent 0baf65d25a
commit 3073a2ee26
2 changed files with 95 additions and 7 deletions

View File

@ -28,7 +28,7 @@
.\" @(#)fstat.1 8.3 (Berkeley) 2/25/94
.\" $FreeBSD$
.\"
.Dd September 28, 2011
.Dd October 19, 2019
.Dt FSTAT 1
.Os
.Sh NAME
@ -36,7 +36,7 @@
.Nd identify active files
.Sh SYNOPSIS
.Nm
.Op Fl fmnv
.Op Fl fmnsv
.Op Fl M Ar core
.Op Fl N Ar system
.Op Fl p Ar pid
@ -85,6 +85,8 @@ in
and print the mode of the file in octal instead of symbolic form.
.It Fl p
Report all files open by the specified process.
.It Fl s
Print socket endpoint information.
.It Fl u
Report all files open by the specified user.
.It Fl v
@ -199,9 +201,6 @@ For tcp, it is the address of the tcpcb, and for udp, the inpcb (socket pcb).
For unix domain sockets, its the address of the socket pcb and the address
of the connected pcb (if connected).
Otherwise the protocol number and address of the socket itself are printed.
The attempt is to make enough information available to
permit further analysis without duplicating
.Xr netstat 1 .
.Pp
For example, the addresses mentioned above are the addresses which the
.Dq Li netstat -A
@ -211,6 +210,15 @@ connected unix domain stream socket.
A unidirectional unix domain socket indicates the direction of flow with
an arrow (``<-'' or ``->''), and a full duplex socket shows a double arrow
(``<->'').
.Pp
When the
.Fl s
flag is used, socket endpoint information is shown after the address of the
socket.
For internet sockets the local and remote address are shown, separated with
a double arrow (``<->'').
For unix/local sockets either the local or remote address is shown, depending
on which one is available.
.Sh SEE ALSO
.Xr netstat 1 ,
.Xr nfsstat 1 ,

View File

@ -40,9 +40,12 @@ __FBSDID("$FreeBSD$");
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
@ -61,6 +64,7 @@ __FBSDID("$FreeBSD$");
static int fsflg, /* show files on same filesystem as file(s) argument */
pflg, /* show files open by a particular pid */
sflg, /* show socket details */
uflg; /* show files open by a particular (effective) user */
static int checkfile; /* restrict to particular files or filesystems */
static int nflg; /* (numerical) display f.s. and rdev as dev_t */
@ -108,7 +112,7 @@ do_fstat(int argc, char **argv)
arg = 0;
what = KERN_PROC_PROC;
nlistf = memf = NULL;
while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1)
while ((ch = getopt(argc, argv, "fmnp:su:vN:M:")) != -1)
switch((char)ch) {
case 'f':
fsflg = 1;
@ -135,6 +139,9 @@ do_fstat(int argc, char **argv)
what = KERN_PROC_PID;
arg = atoi(optarg);
break;
case 's':
sflg = 1;
break;
case 'u':
if (uflg++)
usage();
@ -314,6 +321,55 @@ print_file_info(struct procstat *procstat, struct filestat *fst,
putchar('\n');
}
static char *
addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen)
{
char buffer2[INET6_ADDRSTRLEN];
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
struct sockaddr_un *sun;
switch (ss->ss_family) {
case AF_LOCAL:
sun = (struct sockaddr_un *)ss;
if (strlen(sun->sun_path) == 0)
strlcpy(buffer, "-", buflen);
else
strlcpy(buffer, sun->sun_path, buflen);
break;
case AF_INET:
sin = (struct sockaddr_in *)ss;
if (sin->sin_addr.s_addr == INADDR_ANY)
snprintf(buffer, buflen, "%s:%d", "*",
ntohs(sin->sin_port));
else if (inet_ntop(AF_INET, &sin->sin_addr, buffer2,
sizeof(buffer2)) != NULL)
snprintf(buffer, buflen, "%s:%d", buffer2,
ntohs(sin->sin_port));
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)ss;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
snprintf(buffer, buflen, "%s.%d", "*",
ntohs(sin6->sin6_port));
else if (inet_ntop(AF_INET6, &sin6->sin6_addr, buffer2,
sizeof(buffer2)) != NULL)
snprintf(buffer, buflen, "%s.%d", buffer2,
ntohs(sin6->sin6_port));
else
strlcpy(buffer, "-", buflen);
break;
default:
strlcpy(buffer, "", buflen);
break;
}
return buffer;
}
static void
print_socket_info(struct procstat *procstat, struct filestat *fst)
{
@ -329,6 +385,8 @@ print_socket_info(struct procstat *procstat, struct filestat *fst)
struct sockstat sock;
struct protoent *pe;
char errbuf[_POSIX2_LINE_MAX];
char src_addr[PATH_MAX], dst_addr[PATH_MAX];
struct sockaddr_un *sun;
int error;
static int isopen;
@ -368,6 +426,11 @@ print_socket_info(struct procstat *procstat, struct filestat *fst)
}
else if (sock.so_pcb != 0)
printf(" %lx", (u_long)sock.so_pcb);
if (!sflg)
break;
printf(" %s <-> %s",
addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)),
addr_to_string(&sock.sa_peer, dst_addr, sizeof(dst_addr)));
break;
case AF_UNIX:
/* print address of pcb and connected pcb */
@ -385,8 +448,25 @@ print_socket_info(struct procstat *procstat, struct filestat *fst)
*cp = '\0';
printf(" %s %lx", shoconn,
(u_long)sock.unp_conn);
}
}
}
if (!sflg)
break;
sun = (struct sockaddr_un *)&sock.sa_local;
/*
* While generally we like to print two addresses,
* local and peer, for sockets, it turns out to be
* more useful to print the first non-null address for
* local sockets, as typically they aren't bound and
* connected, and the path strings can get long.
*/
if (sun->sun_path[0] != 0)
addr_to_string(&sock.sa_local,
src_addr, sizeof(src_addr));
else
addr_to_string(&sock.sa_peer,
src_addr, sizeof(src_addr));
printf(" %s", src_addr);
break;
default:
/* print protocol number and socket address */