Index: usr.bin/fstat/fstat.1 =================================================================== --- usr.bin/fstat/fstat.1 +++ usr.bin/fstat/fstat.1 @@ -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 @@ 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(s) information. .It Fl u Report all files open by the specified user. .It Fl v @@ -211,6 +213,14 @@ 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. +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 , Index: usr.bin/fstat/fstat.c =================================================================== --- usr.bin/fstat/fstat.c +++ usr.bin/fstat/fstat.c @@ -40,9 +40,12 @@ #include #include #include +#include #include +#include + #include #include #include @@ -61,6 +64,7 @@ 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 @@ 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 @@ what = KERN_PROC_PID; arg = atoi(optarg); break; + case 's': + sflg = 1; + break; case 'u': if (uflg++) usage(); @@ -314,6 +321,53 @@ 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; +#define IS_INADDR_ANY(x) ((x).s_addr == INADDR_ANY) + + 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; + snprintf(buffer, buflen, "%s:%d", + IS_INADDR_ANY(sin->sin_addr) ? + "*" : inet_ntoa(sin->sin_addr), + 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 +383,8 @@ 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 +424,11 @@ } 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 +446,25 @@ *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-nul 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 */