changeset 19407:479b173cd63f

12046 Provide /proc/<PID>/fdinfo/ Reviewed by: John Levon <john.levon@joyent.com> Reviewed by: Robert Mustacchi <rm@fingolfin.org> Approved by: Dan McDonald <danmcd@joyent.com>
author Andy Fiddaman <omnios@citrus-it.co.uk>
date Tue, 19 Nov 2019 11:04:46 +0000
parents 4e101f375b58
children 8b9ac5d11dc3
files usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c usr/src/cmd/ptools/pfiles/pfiles.c usr/src/cmd/sgs/elfdump/common/elfdump.msg usr/src/cmd/sgs/elfdump/common/gen_layout_obj.c usr/src/cmd/sgs/elfdump/common/gen_struct_layout.c usr/src/cmd/sgs/elfdump/common/struct_layout.h usr/src/cmd/sgs/elfdump/common/struct_layout_amd64.c usr/src/cmd/sgs/elfdump/common/struct_layout_i386.c usr/src/cmd/sgs/elfdump/common/struct_layout_sparc.c usr/src/cmd/sgs/elfdump/common/struct_layout_sparcv9.c usr/src/lib/libproc/Makefile usr/src/lib/libproc/Makefile.com usr/src/lib/libproc/common/Pcontrol.c usr/src/lib/libproc/common/Pcontrol.h usr/src/lib/libproc/common/Pcore.c usr/src/lib/libproc/common/Pfdinfo.c usr/src/lib/libproc/common/Pgcore.c usr/src/lib/libproc/common/libproc.h usr/src/lib/libproc/common/mapfile-vers usr/src/lib/libproc/common/proc_fd.c usr/src/lib/libproc/common/proc_fd.h usr/src/man/man3lib/libproc.3lib usr/src/man/man3proc/Makefile usr/src/man/man3proc/proc_fdinfo_misc.3proc usr/src/man/man3proc/proc_fdinfowalk.3proc usr/src/man/man3proc/proc_fdwalk.3proc usr/src/man/man3proc/proc_get_fdinfo.3proc usr/src/man/man4/core.4 usr/src/man/man4/proc.4 usr/src/pkg/manifests/system-library.man3proc.inc usr/src/uts/common/exec/elf/elf_notes.c usr/src/uts/common/fs/proc/prdata.h usr/src/uts/common/fs/proc/prsubr.c usr/src/uts/common/fs/proc/prvnops.c usr/src/uts/common/sys/procfs.h usr/src/uts/common/sys/prsystm.h
diffstat 36 files changed, 2274 insertions(+), 499 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c	Tue Nov 19 11:04:46 2019 +0000
@@ -24,7 +24,7 @@
  * netstat.c 2.2, last change 9/9/91
  * MROUTING Revision 3.5
  * Copyright 2018, Joyent, Inc.
- * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -988,7 +988,7 @@
 }
 
 static int
-process_hash_iter(const psinfo_t *psinfo, const prfdinfo_t *pr,
+process_hash_iter(const psinfo_t *psinfo, const prfdinfo_core_t *pr,
     struct ps_prochandle *Pr)
 {
 	proc_fdinfo_t *ph;
@@ -1089,7 +1089,7 @@
 	while ((ent = readdir(dirp)) != NULL) {
 		char path[PATH_MAX];
 		struct stat st;
-		prfdinfo_t info;
+		prfdinfo_core_t info;
 		int fd, len;
 
 		if (!isdigit(ent->d_name[0]))
--- a/usr/src/cmd/ptools/pfiles/pfiles.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/ptools/pfiles/pfiles.c	Tue Nov 19 11:04:46 2019 +0000
@@ -25,6 +25,7 @@
  */
 /*
  * Copyright (c) 2017 Joyent, Inc.  All Rights reserved.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <stdio.h>
@@ -51,28 +52,19 @@
 #include <ucred.h>
 #include <zone.h>
 
-#define	copyflock(dst, src) \
-	(dst).l_type = (src).l_type;		\
-	(dst).l_whence = (src).l_whence;	\
-	(dst).l_start = (src).l_start;		\
-	(dst).l_len = (src).l_len;		\
-	(dst).l_sysid = (src).l_sysid;		\
-	(dst).l_pid = (src).l_pid;
-
 static char *command;
 static volatile int interrupt;
 static int Fflag;
 static boolean_t nflag = B_FALSE;
 
 static	void	intr(int);
-static	void	dofcntl(struct ps_prochandle *, prfdinfo_t *, int, int);
-static	void	dosocket(struct ps_prochandle *, int);
-static	void	dofifo(struct ps_prochandle *, int);
-static	void	dotli(struct ps_prochandle *, int);
+static	void	dofcntl(struct ps_prochandle *, const prfdinfo_t *, int, int);
+static	void	dosocket(struct ps_prochandle *, const prfdinfo_t *);
+static	void	dosocknames(struct ps_prochandle *, const prfdinfo_t *);
+static	void	dofifo(struct ps_prochandle *, const prfdinfo_t *);
 static	void	show_files(struct ps_prochandle *);
 static	void	show_fileflags(int);
-static	void	show_door(struct ps_prochandle *, int);
-static	int	getflock(struct ps_prochandle *, int, struct flock *);
+static	void	show_door(struct ps_prochandle *, const prfdinfo_t *);
 
 int
 main(int argc, char **argv)
@@ -213,11 +205,13 @@
 
 /* ------ begin specific code ------ */
 
+
 static int
-show_file(void *data, prfdinfo_t *info)
+show_file(void *data, const prfdinfo_t *info)
 {
 	struct ps_prochandle *Pr = data;
 	char unknown[12];
+	const char *path;
 	char *s;
 	mode_t mode;
 
@@ -265,58 +259,41 @@
 		(void) printf(" rdev:%u,%u\n",
 		    (unsigned)info->pr_rmajor, (unsigned)info->pr_rminor);
 
+	path = proc_fdinfo_misc(info, PR_PATHNAME, NULL);
+
 	if (!nflag) {
 		dofcntl(Pr, info,
 		    (mode & (S_IFMT|S_ENFMT|S_IXGRP)) == (S_IFREG|S_ENFMT),
 		    (mode & S_IFMT) == S_IFDOOR);
 
 		if (Pstate(Pr) != PS_DEAD) {
-			char *dev = NULL;
-
-			if ((mode & S_IFMT) == S_IFSOCK)
-				dosocket(Pr, info->pr_fd);
-			else if ((mode & S_IFMT) == S_IFIFO)
-				dofifo(Pr, info->pr_fd);
-
-			if ((mode & S_IFMT) == S_IFCHR) {
+			switch (mode & S_IFMT) {
+			case S_IFSOCK:
+				dosocket(Pr, info);
+				break;
+			case S_IFIFO:
+				dofifo(Pr, info);
+				break;
+			case S_IFCHR:
 				/*
-				 * There's no elegant way to determine
-				 * if a character device supports TLI,
-				 * so we lame out and just check a
-				 * hardcoded list of known TLI devices.
+				 * This may be a TLI endpoint. If so, it will
+				 * have socket names in the fdinfo and this
+				 * will print them.
 				 */
-				int i;
-				const char *tlidevs[] = {
-				    "tcp", "tcp6", "udp", "udp6", NULL
-				};
-
-				/* global zone: /devices paths */
-				dev = strrchr(info->pr_path, ':');
-				/* also check the /dev path for zones */
-				if (dev == NULL)
-					dev = strrchr(info->pr_path, '/');
-				if (dev != NULL) {
-					dev++; /* skip past the `:' or '/' */
-
-					for (i = 0; tlidevs[i] != NULL; i++) {
-						if (strcmp(dev, tlidevs[i]) ==
-						    0) {
-							dotli(Pr, info->pr_fd);
-							break;
-						}
-					}
-				}
+				dosocknames(Pr, info);
+				break;
 			}
 		}
 
-		if (info->pr_path[0] != '\0')
-			(void) printf("      %s\n", info->pr_path);
+		if (path != NULL)
+			(void) printf("      %s\n", path);
 
 		if (info->pr_offset != -1) {
 			(void) printf("      offset:%lld\n",
 			    (long long)info->pr_offset);
 		}
 	}
+
 	return (0);
 }
 
@@ -338,35 +315,13 @@
 	(void) Pfdinfo_iter(Pr, show_file, Pr);
 }
 
-
-static int
-getflock(struct ps_prochandle *Pr, int fd, struct flock *flock_native)
-{
-	int ret;
-#ifdef _LP64
-	struct flock64_32 flock_target;
-
-	if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) {
-		copyflock(flock_target, *flock_native);
-		ret = pr_fcntl(Pr, fd, F_GETLK, &flock_target);
-		copyflock(*flock_native, flock_target);
-		return (ret);
-	}
-#endif /* _LP64 */
-	ret = pr_fcntl(Pr, fd, F_GETLK, flock_native);
-	return (ret);
-}
-
 /* examine open file with fcntl() */
 static void
-dofcntl(struct ps_prochandle *Pr, prfdinfo_t *info, int mandatory, int isdoor)
+dofcntl(struct ps_prochandle *Pr, const prfdinfo_t *info, int mandatory,
+    int isdoor)
 {
-	struct flock flock;
 	int fileflags;
 	int fdflags;
-	int fd;
-
-	fd = info->pr_fd;
 
 	fileflags = info->pr_fileflags;
 	fdflags = info->pr_fdflags;
@@ -378,31 +333,27 @@
 		if (fdflags != -1 && (fdflags & FD_CLOEXEC))
 			(void) printf(" FD_CLOEXEC");
 		if (isdoor && (Pstate(Pr) != PS_DEAD))
-			show_door(Pr, fd);
+			show_door(Pr, info);
 		(void) fputc('\n', stdout);
 	} else if (isdoor && (Pstate(Pr) != PS_DEAD)) {
 		(void) printf("    ");
-		show_door(Pr, fd);
+		show_door(Pr, info);
 		(void) fputc('\n', stdout);
 	}
 
-	flock.l_type = F_WRLCK;
-	flock.l_whence = 0;
-	flock.l_start = 0;
-	flock.l_len = 0;
-	flock.l_sysid = 0;
-	flock.l_pid = 0;
-	if ((Pstate(Pr) != PS_DEAD) && (getflock(Pr, fd, &flock) != -1)) {
-		if (flock.l_type != F_UNLCK && (flock.l_sysid || flock.l_pid)) {
-			unsigned long sysid = flock.l_sysid;
+	if (Pstate(Pr) != PS_DEAD) {
+		if (info->pr_locktype != F_UNLCK &&
+		    (info->pr_locksysid != -1 || info->pr_lockpid != -1)) {
+			unsigned long sysid = info->pr_locksysid;
 
-			(void) printf("      %s %s lock set by",
+			(void) printf("      %s %s lock set",
 			    mandatory ? "mandatory" : "advisory",
-			    flock.l_type == F_RDLCK? "read" : "write");
+			    info->pr_locktype == F_RDLCK? "read" : "write");
 			if (sysid)
-				(void) printf(" system 0x%lX", sysid);
-			if (flock.l_pid)
-				(void) printf(" process %d", (int)flock.l_pid);
+				(void) printf(" by system 0x%lX", sysid);
+			if (info->pr_lockpid != -1)
+				(void) printf(" by process %d",
+				    (int)info->pr_lockpid);
 			(void) fputc('\n', stdout);
 		}
 	}
@@ -484,11 +435,11 @@
 
 /* show door info */
 static void
-show_door(struct ps_prochandle *Pr, int fd)
+show_door(struct ps_prochandle *Pr, const prfdinfo_t *info)
 {
 	door_info_t door_info;
 
-	if (pr_door_info(Pr, fd, &door_info) != 0)
+	if (pr_door_info(Pr, info->pr_fd, &door_info) != 0)
 		return;
 
 	(void) printf("  door to");
@@ -500,7 +451,7 @@
  * needed for AF_UNIX sockets.
  */
 static void
-show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len)
+show_sockaddr(const char *str, const struct sockaddr *sa, socklen_t len)
 {
 	struct sockaddr_in *so_in = (struct sockaddr_in *)(void *)sa;
 	struct sockaddr_in6 *so_in6 = (struct sockaddr_in6 *)(void *)sa;
@@ -527,11 +478,9 @@
 		return;
 	case AF_UNIX:
 		if (len >= sizeof (so_un->sun_family)) {
-			/* Null terminate */
-			len -= sizeof (so_un->sun_family);
-			so_un->sun_path[len] = '\0';
-			(void) printf("\t%s: AF_UNIX %s\n",
-			    str, so_un->sun_path);
+			(void) printf("\t%s: AF_UNIX %.*s\n",
+			    str, len - sizeof (so_un->sun_family),
+			    so_un->sun_path);
 		}
 		return;
 	case AF_IMPLINK:	p = "AF_IMPLINK";	break;
@@ -608,74 +557,74 @@
 
 #define	BUFSIZE	200
 static void
-show_sockopts(struct ps_prochandle *Pr, int fd)
+show_sockopts(struct ps_prochandle *Pr, const prfdinfo_t *info)
 {
-	int val, vlen;
+	const int *val;
+	size_t vlen;
 	char buf[BUFSIZE];
 	char buf1[32];
 	char ipaddr[INET_ADDRSTRLEN];
 	int i;
-	in_addr_t nexthop_val;
+	const in_addr_t *nexthop_val;
+	const prsockopts_bool_opts_t *opts;
 	struct boolopt {
-		int		level;
 		int		opt;
 		const char	*name;
 	};
 	static struct boolopt boolopts[] = {
-	    { SOL_SOCKET, SO_DEBUG,		"SO_DEBUG,"	},
-	    { SOL_SOCKET, SO_REUSEADDR,		"SO_REUSEADDR,"	},
-	    { SOL_SOCKET, SO_KEEPALIVE,		"SO_KEEPALIVE,"	},
-	    { SOL_SOCKET, SO_DONTROUTE,		"SO_DONTROUTE,"	},
-	    { SOL_SOCKET, SO_BROADCAST,		"SO_BROADCAST,"	},
-	    { SOL_SOCKET, SO_OOBINLINE,		"SO_OOBINLINE,"	},
-	    { SOL_SOCKET, SO_DGRAM_ERRIND,	"SO_DGRAM_ERRIND,"},
-	    { SOL_SOCKET, SO_ALLZONES,		"SO_ALLZONES,"	},
-	    { SOL_SOCKET, SO_MAC_EXEMPT,	"SO_MAC_EXEMPT," },
-	    { SOL_SOCKET, SO_MAC_IMPLICIT,	"SO_MAC_IMPLICIT," },
-	    { SOL_SOCKET, SO_EXCLBIND,		"SO_EXCLBIND," },
-	    { SOL_SOCKET, SO_VRRP,		"SO_VRRP," },
-	    { IPPROTO_UDP, UDP_NAT_T_ENDPOINT,	"UDP_NAT_T_ENDPOINT," },
+	    { PR_SO_DEBUG,		"SO_DEBUG,"	},
+	    { PR_SO_REUSEADDR,		"SO_REUSEADDR,"	},
+	    { PR_SO_KEEPALIVE,		"SO_KEEPALIVE,"	},
+	    { PR_SO_DONTROUTE,		"SO_DONTROUTE,"	},
+	    { PR_SO_BROADCAST,		"SO_BROADCAST,"	},
+	    { PR_SO_OOBINLINE,		"SO_OOBINLINE,"	},
+	    { PR_SO_DGRAM_ERRIND,	"SO_DGRAM_ERRIND,"},
+	    { PR_SO_ALLZONES,		"SO_ALLZONES,"	},
+	    { PR_SO_MAC_EXEMPT,		"SO_MAC_EXEMPT," },
+	    { PR_SO_MAC_IMPLICIT,	"SO_MAC_IMPLICIT," },
+	    { PR_SO_EXCLBIND,		"SO_EXCLBIND," },
+	    { PR_SO_VRRP,		"SO_VRRP," },
+	    { PR_UDP_NAT_T_ENDPOINT,	"UDP_NAT_T_ENDPOINT," },
 	};
-	struct linger l;
+	const struct linger *l;
+
+	opts = proc_fdinfo_misc(info, PR_SOCKOPTS_BOOL_OPTS, NULL);
 
 	buf[0] = '!';		/* sentinel value, never printed */
 	buf[1] = '\0';
 
 	for (i = 0; i < sizeof (boolopts) / sizeof (boolopts[0]); i++) {
-		vlen = sizeof (val);
-		if (pr_getsockopt(Pr, fd, boolopts[i].level, boolopts[i].opt,
-		    &val, &vlen) == 0 && val != 0)
+		if (opts != NULL && opts->prsock_bool_opts & boolopts[i].opt)
 			(void) strlcat(buf, boolopts[i].name, sizeof (buf));
 	}
 
-	vlen = sizeof (l);
-	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_LINGER, &l, &vlen) == 0 &&
-	    l.l_onoff != 0) {
+	l = proc_fdinfo_misc(info, PR_SOCKOPT_LINGER, NULL);
+	if (l != NULL && l->l_onoff != 0) {
 		(void) snprintf(buf1, sizeof (buf1), "SO_LINGER(%d),",
-		    l.l_linger);
+		    l->l_linger);
 		(void) strlcat(buf, buf1, sizeof (buf));
 	}
 
-	vlen = sizeof (val);
-	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_SNDBUF, &val, &vlen) == 0) {
-		(void) snprintf(buf1, sizeof (buf1), "SO_SNDBUF(%d),", val);
-		(void) strlcat(buf, buf1, sizeof (buf));
-	}
-	vlen = sizeof (val);
-	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_RCVBUF, &val, &vlen) == 0) {
-		(void) snprintf(buf1, sizeof (buf1), "SO_RCVBUF(%d),", val);
+	val = proc_fdinfo_misc(info, PR_SOCKOPT_SNDBUF, NULL);
+	if (val != NULL) {
+		(void) snprintf(buf1, sizeof (buf1), "SO_SNDBUF(%d),", *val);
 		(void) strlcat(buf, buf1, sizeof (buf));
 	}
-	vlen = sizeof (nexthop_val);
-	if (pr_getsockopt(Pr, fd, IPPROTO_IP, IP_NEXTHOP, &nexthop_val,
-	    &vlen) == 0) {
-		if (vlen > 0) {
-			(void) inet_ntop(AF_INET, (void *) &nexthop_val,
-			    ipaddr, sizeof (ipaddr));
-			(void) snprintf(buf1, sizeof (buf1), "IP_NEXTHOP(%s),",
-			    ipaddr);
-			(void) strlcat(buf, buf1, sizeof (buf));
-		}
+
+	val = proc_fdinfo_misc(info, PR_SOCKOPT_RCVBUF, NULL);
+	if (val != NULL) {
+		(void) snprintf(buf1, sizeof (buf1), "SO_RCVBUF(%d),", *val);
+		(void) strlcat(buf, buf1, sizeof (buf));
+	}
+
+
+	nexthop_val = proc_fdinfo_misc(info, PR_SOCKOPT_IP_NEXTHOP, &vlen);
+	if (nexthop_val != NULL && vlen > 0) {
+		(void) inet_ntop(AF_INET, (void *) nexthop_val,
+		    ipaddr, sizeof (ipaddr));
+		(void) snprintf(buf1, sizeof (buf1), "IP_NEXTHOP(%s),",
+		    ipaddr);
+		(void) strlcat(buf, buf1, sizeof (buf));
 	}
 
 	buf[strlen(buf) - 1] = '\0'; /* overwrites sentinel if no options */
@@ -685,11 +634,12 @@
 
 #define	MAXNALLOC	32
 static void
-show_sockfilters(struct ps_prochandle *Pr, int fd)
+show_sockfilters(struct ps_prochandle *Pr, const prfdinfo_t *info)
 {
 	struct fil_info *fi;
 	int i = 0, nalloc = 2, len = nalloc * sizeof (*fi);
 	boolean_t printhdr = B_TRUE;
+	int fd = info->pr_fd;
 
 	fi = calloc(nalloc, sizeof (*fi));
 	if (fi == NULL) {
@@ -749,68 +699,50 @@
 
 /* print peer credentials for sockets and named pipes */
 static void
-dopeerucred(struct ps_prochandle *Pr, int fd)
+dopeerucred(struct ps_prochandle *Pr, const prfdinfo_t *info)
 {
 	ucred_t *peercred = NULL;	/* allocated by getpeerucred */
 
-	if (pr_getpeerucred(Pr, fd, &peercred) == 0) {
+	if (pr_getpeerucred(Pr, info->pr_fd, &peercred) == 0) {
 		show_ucred("peer", peercred);
 		ucred_free(peercred);
 	}
 }
 
+static void
+dosocknames(struct ps_prochandle *Pr, const prfdinfo_t *info)
+{
+	const struct sockaddr *sa;
+	size_t vlen;
+
+	sa = proc_fdinfo_misc(info, PR_SOCKETNAME, &vlen);
+	if (sa != NULL)
+		show_sockaddr("sockname", sa, vlen);
+
+	sa = proc_fdinfo_misc(info, PR_PEERSOCKNAME, &vlen);
+	if (sa != NULL)
+		show_sockaddr("peername", sa, vlen);
+}
+
 /* the file is a socket */
 static void
-dosocket(struct ps_prochandle *Pr, int fd)
+dosocket(struct ps_prochandle *Pr, const prfdinfo_t *info)
 {
-	/* A buffer large enough for PATH_MAX size AF_UNIX address */
-	long buf[(sizeof (short) + PATH_MAX + sizeof (long) - 1)
-	    / sizeof (long)];
-	struct sockaddr *sa = (struct sockaddr *)buf;
-	socklen_t len;
-	int type, tlen;
-
-	tlen = sizeof (type);
-	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_TYPE, &type, &tlen) == 0)
-		show_socktype((uint_t)type);
+	const int *type;
 
-	show_sockopts(Pr, fd);
-	show_sockfilters(Pr, fd);
+	type = proc_fdinfo_misc(info, PR_SOCKOPT_TYPE, NULL);
+	if (type != NULL)
+		show_socktype((uint_t)*type);
 
-	len = sizeof (buf);
-	if (pr_getsockname(Pr, fd, sa, &len) == 0)
-		show_sockaddr("sockname", sa, len);
-
-	len = sizeof (buf);
-	if (pr_getpeername(Pr, fd, sa, &len) == 0)
-		show_sockaddr("peername", sa, len);
-
-	dopeerucred(Pr, fd);
+	show_sockopts(Pr, info);
+	show_sockfilters(Pr, info);
+	dosocknames(Pr, info);
+	dopeerucred(Pr, info);
 }
 
 /* the file is a fifo (aka "named pipe") */
 static void
-dofifo(struct ps_prochandle *Pr, int fd)
-{
-	dopeerucred(Pr, fd);
-}
-
-/* the file is a TLI endpoint */
-static void
-dotli(struct ps_prochandle *Pr, int fd)
+dofifo(struct ps_prochandle *Pr, const prfdinfo_t *info)
 {
-	struct strcmd strcmd;
-
-	strcmd.sc_len = STRCMDBUFSIZE;
-	strcmd.sc_timeout = 5;
-
-	strcmd.sc_cmd = TI_GETMYNAME;
-	if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0)
-		show_sockaddr("sockname", (void *)&strcmd.sc_buf,
-		    (size_t)strcmd.sc_len);
-
-	strcmd.sc_cmd = TI_GETPEERNAME;
-	if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0)
-		show_sockaddr("peername", (void *)&strcmd.sc_buf,
-		    (size_t)strcmd.sc_len);
+	dopeerucred(Pr, info);
 }
--- a/usr/src/cmd/sgs/elfdump/common/elfdump.msg	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/elfdump.msg	Tue Nov 19 11:04:46 2019 +0000
@@ -23,6 +23,7 @@
 # Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
 # Copyright 2018 Joyent, Inc.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 #
 
 @ _START_
@@ -468,7 +469,7 @@
 @ MSG_CNOTE_DESC_PSINFO_T		"desc: (psinfo_t)"
 @ MSG_CNOTE_DESC_PSTATUS_T		"desc: (pstatus_t)"
 @ MSG_CNOTE_DESC_STRUCT_UTSNAME		"desc: (struct utsname)"
-@ MSG_CNOTE_DESC_PRFDINFO_T		"desc: (prfdinfo_t)"
+@ MSG_CNOTE_DESC_PRFDINFO_T		"desc: (prfdinfo_core_t)"
 @ MSG_CNOTE_DESC_PRSECFLAGS_T		"desc: (prsecflags_t)"
 @ MSG_CNOTE_DESC_PRLWPNAME_T		"desc: (prlwpname_t)"
 
--- a/usr/src/cmd/sgs/elfdump/common/gen_layout_obj.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/gen_layout_obj.c	Tue Nov 19 11:04:46 2019 +0000
@@ -12,6 +12,7 @@
 /*
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -57,6 +58,6 @@
 sysset_t sysset;
 timestruc_t ts;
 struct utsname uts;
-prfdinfo_t ptfd;
+prfdinfo_core_t ptfd;
 prsecflags_t psf;
 prlwpname_t psn;
--- a/usr/src/cmd/sgs/elfdump/common/gen_struct_layout.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/gen_struct_layout.c	Tue Nov 19 11:04:46 2019 +0000
@@ -25,6 +25,7 @@
  *
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -568,22 +569,22 @@
 static void
 gen_prfdinfo(void)
 {
-	START(prfdinfo, prfdinfo_t);
+	START(prfdinfo, prfdinfo_core_t);
 
-	SCALAR_FIELD(prfdinfo_t,	pr_fd,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_mode,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_uid,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_gid,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_major,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_minor,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_rmajor,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_rminor,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_ino,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_offset,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_size,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_fileflags,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_fdflags,	0);
-	ARRAY_FIELD(prfdinfo_t,		pr_path,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_fd,		0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_mode,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_uid,		0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_gid,		0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_major,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_minor,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_rmajor,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_rminor,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_ino,		0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_offset,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_size,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_fileflags,	0);
+	SCALAR_FIELD(prfdinfo_core_t,	pr_fdflags,	0);
+	ARRAY_FIELD(prfdinfo_core_t,	pr_path,	0);
 
 	END;
 }
--- a/usr/src/cmd/sgs/elfdump/common/struct_layout.h	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/struct_layout.h	Tue Nov 19 11:04:46 2019 +0000
@@ -27,6 +27,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef	_STRUCT_LAYOUT_H
@@ -507,7 +508,7 @@
 } sl_utsname_layout_t;
 
 /*
- * Layout description of prdinfo_t, from <sys/procfs.h>.
+ * Layout description of prfdinfo_core_t, from <sys/procfs.h>.
  */
 typedef struct {
 	sl_field_t		sizeof_struct;
--- a/usr/src/cmd/sgs/elfdump/common/struct_layout_amd64.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/struct_layout_amd64.c	Tue Nov 19 11:04:46 2019 +0000
@@ -26,6 +26,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <struct_layout.h>
@@ -360,7 +361,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_core_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- a/usr/src/cmd/sgs/elfdump/common/struct_layout_i386.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/struct_layout_i386.c	Tue Nov 19 11:04:46 2019 +0000
@@ -26,6 +26,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <struct_layout.h>
@@ -360,7 +361,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_core_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- a/usr/src/cmd/sgs/elfdump/common/struct_layout_sparc.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/struct_layout_sparc.c	Tue Nov 19 11:04:46 2019 +0000
@@ -26,6 +26,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <struct_layout.h>
@@ -360,7 +361,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_core_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- a/usr/src/cmd/sgs/elfdump/common/struct_layout_sparcv9.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/cmd/sgs/elfdump/common/struct_layout_sparcv9.c	Tue Nov 19 11:04:46 2019 +0000
@@ -26,6 +26,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <struct_layout.h>
@@ -360,7 +361,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_core_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- a/usr/src/lib/libproc/Makefile	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/Makefile	Tue Nov 19 11:04:46 2019 +0000
@@ -22,6 +22,7 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 #
 
 include ../Makefile.lib
@@ -34,11 +35,10 @@
 clobber :=	TARGET= clobber
 delete :=	TARGET= delete
 install :=	TARGET= install
-lint :=		TARGET= lint
 _msg :=		TARGET= _msg
 package :=	TARGET= package
 
-LIBRARY= 	libproc.a
+LIBRARY=	libproc.a
 TEXT_DOMAIN=	SUNW_OST_OSLIB
 XGETFLAGS=	-a
 POFILE=		$(LIBRARY:.a=.po)
@@ -61,7 +61,7 @@
 
 .KEEP_STATE:
 
-all clean clobber delete install lint package: $(SUBDIRS)
+all clean clobber delete install package: $(SUBDIRS)
 
 install_h: $(ROOTHDRS)
 
--- a/usr/src/lib/libproc/Makefile.com	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/Makefile.com	Tue Nov 19 11:04:46 2019 +0000
@@ -24,6 +24,7 @@
 # Copyright (c) 2013 by Delphix. All rights reserved.
 #
 # Copyright (c) 2018, Joyent, Inc.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 
 LIBRARY = libproc.a
 VERS = .1
@@ -70,6 +71,7 @@
 	proc_get_info.o	\
 	proc_names.o	\
 	proc_arg.o	\
+	proc_fd.o	\
 	proc_set.o	\
 	proc_stdio.o
 
@@ -84,13 +86,12 @@
 
 SRCS =		$(CMNOBJS:%.o=../common/%.c) $(ISAOBJS:%.o=%.c)
 
-LIBS =		$(DYNLIB) $(LINTLIB)
+LIBS =		$(DYNLIB)
 LDLIBS +=	-lrtld_db -lelf -lctf -lc
 CSTD =	$(CSTD_GNU99)
 CPPFLAGS +=	$($(MACH64)_CPPFLAGS)
 
 SRCDIR =	../common
-$(LINTLIB) :=	SRCS = $(SRCDIR)/$(LINTSRC)
 
 CFLAGS +=	$(CCVERBOSE)
 CPPFLAGS +=	-I$(SRCDIR)
@@ -114,8 +115,6 @@
 
 all: $(LIBS)
 
-lint: lintcheck
-
 # include library targets
 include ../../Makefile.targ
 
--- a/usr/src/lib/libproc/common/Pcontrol.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/common/Pcontrol.c	Tue Nov 19 11:04:46 2019 +0000
@@ -27,6 +27,7 @@
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright 2015, Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <assert.h>
@@ -1201,6 +1202,7 @@
 	while (P->num_fd > 0) {
 		fd_info_t *fip = list_next(&P->fd_head);
 		list_unlink(fip);
+		proc_fdinfo_free(fip->fd_info);
 		free(fip);
 		P->num_fd--;
 	}
--- a/usr/src/lib/libproc/common/Pcontrol.h	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/common/Pcontrol.h	Tue Nov 19 11:04:46 2019 +0000
@@ -26,6 +26,7 @@
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef	_PCONTROL_H
@@ -147,7 +148,7 @@
 
 typedef struct fd_info {
 	plist_t	fd_list;	/* linked list */
-	prfdinfo_t fd_info;	/* fd info */
+	prfdinfo_t *fd_info;	/* fd info */
 } fd_info_t;
 
 typedef struct core_info {	/* information specific to core files */
--- a/usr/src/lib/libproc/common/Pcore.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/common/Pcore.c	Tue Nov 19 11:04:46 2019 +0000
@@ -27,6 +27,7 @@
  * Copyright (c) 2018, Joyent, Inc. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright 2015 Gary Mills
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <sys/types.h>
@@ -50,6 +51,7 @@
 #include "Pcontrol.h"
 #include "P32ton.h"
 #include "Putil.h"
+#include "proc_fd.h"
 #ifdef __x86
 #include "Pcore_linux.h"
 #endif
@@ -754,7 +756,7 @@
 static int
 note_fdinfo(struct ps_prochandle *P, size_t nbytes)
 {
-	prfdinfo_t prfd;
+	prfdinfo_core_t prfd;
 	fd_info_t *fip;
 
 	if ((nbytes < sizeof (prfd)) ||
@@ -767,7 +769,13 @@
 		dprintf("Pgrab_core: failed to add NT_FDINFO\n");
 		return (-1);
 	}
-	(void) memcpy(&fip->fd_info, &prfd, sizeof (prfd));
+	if (fip->fd_info == NULL) {
+		if (proc_fdinfo_from_core(&prfd, &fip->fd_info) != 0) {
+			dprintf("Pgrab_core: failed to convert NT_FDINFO\n");
+			return (-1);
+		}
+	}
+
 	return (0);
 }
 
--- a/usr/src/lib/libproc/common/Pfdinfo.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/common/Pfdinfo.c	Tue Nov 19 11:04:46 2019 +0000
@@ -14,6 +14,7 @@
  */
 /*
  * Copyright (c) 2013 Joyent, Inc.  All Rights reserved.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <limits.h>
@@ -27,6 +28,7 @@
 
 #include "libproc.h"
 #include "Pcontrol.h"
+#include "proc_fd.h"
 
 /*
  * Pfdinfo.c - obtain open file information.
@@ -51,10 +53,13 @@
 	}
 
 	for (i = 0; i < P->num_fd; i++, fip = list_next(fip)) {
-		if (fip->fd_info.pr_fd == fd) {
+		if (fip->fd_info == NULL)
+			continue;
+
+		if (fip->fd_info->pr_fd == fd) {
 			return (fip);
 		}
-		if (fip->fd_info.pr_fd < fd) {
+		if (fip->fd_info->pr_fd < fd) {
 			break;
 		}
 	}
@@ -63,12 +68,34 @@
 	if ((fip = calloc(1, sizeof (*fip))) == NULL)
 		return (NULL);
 
-	fip->fd_info.pr_fd = fd;
 	list_link(fip, next ? next : (void *)&(P->fd_head));
 	P->num_fd++;
 	return (fip);
 }
 
+static int
+fdwalk_cb(const prfdinfo_t *info, void *arg)
+{
+	struct ps_prochandle *P = arg;
+	fd_info_t *fip;
+
+	fip = Pfd2info(P, info->pr_fd);
+	if (fip == NULL) {
+		errno = ENOMEM;
+		return (-1);
+	}
+
+	if (fip->fd_info == NULL)
+		fip->fd_info = proc_fdinfo_dup(info);
+
+	if (fip->fd_info == NULL) {
+		errno = ENOMEM;
+		return (-1);
+	}
+
+	return (0);
+}
+
 /*
  * Attempt to load the open file information from a live process.
  */
@@ -81,83 +108,13 @@
 	 * This is an edge case it isn't worth adding additional state to
 	 * to eliminate.
 	 */
-	if (P->num_fd > 0) {
+	if (P->num_fd > 0)
 		return;
-	}
-
-	if (P->state != PS_DEAD && P->state != PS_IDLE) {
-		char dir_name[PATH_MAX];
-		char path[PATH_MAX];
-		struct dirent *ent;
-		DIR	*dirp;
-		int	fd;
-
-		/*
-		 * Try to get the path information first.
-		 */
-		(void) snprintf(dir_name, sizeof (dir_name),
-		    "%s/%d/path", procfs_path, (int)P->pid);
-		dirp = opendir(dir_name);
-		if (dirp == NULL) {
-			return;
-		}
-		ent = NULL;
-		while ((ent = readdir(dirp)) != NULL) {
-			fd_info_t	*fip;
-			prfdinfo_t	*info;
-			int		len;
-			struct stat64	stat;
-
-			if (!isdigit(ent->d_name[0]))
-				continue;
-
-			fd = atoi(ent->d_name);
-
-			fip = Pfd2info(P, fd);
-			info = &fip->fd_info;
-			info->pr_fd = fd;
 
-			if (pr_fstat64(P, fd, &stat) == 0) {
-				info->pr_mode = stat.st_mode;
-				info->pr_uid = stat.st_uid;
-				info->pr_gid = stat.st_gid;
-				info->pr_major = major(stat.st_dev);
-				info->pr_minor = minor(stat.st_dev);
-				info->pr_rmajor = major(stat.st_rdev);
-				info->pr_rminor = minor(stat.st_rdev);
-				info->pr_size = stat.st_size;
-				info->pr_ino = stat.st_ino;
-			}
-
-			info->pr_fileflags = pr_fcntl(P, fd, F_GETXFL, 0);
-			info->pr_fdflags = pr_fcntl(P, fd, F_GETFD, 0);
-			info->pr_offset = pr_llseek(P, fd, 0, SEEK_CUR);
+	if (P->state == PS_DEAD || P->state == PS_IDLE)
+		return;
 
-			/* attempt to determine the path to it */
-			switch (info->pr_mode & S_IFMT) {
-			case S_IFDOOR:
-			case S_IFSOCK:
-				/* not applicable */
-				len = -1;
-				break;
-			default:
-				(void) snprintf(path, sizeof (path),
-				    "%s/%d/path/%d", procfs_path, (int)P->pid,
-				    fd);
-				len = readlink(path, info->pr_path,
-				    sizeof (info->pr_path) - 1);
-				break;
-			}
-
-			if (len < 0) {
-				info->pr_path[0] = 0;
-			} else {
-				info->pr_path[len] = 0;
-			}
-		}
-		(void) closedir(dirp);
-
-	}
+	proc_fdwalk(P->pid, fdwalk_cb, P);
 }
 
 int
@@ -174,7 +131,7 @@
 	for (fip = list_prev(&P->fd_head);
 	    fip != (void *)&P->fd_head && fip != NULL;
 	    fip = list_prev(fip)) {
-		if ((rv = func(cd, &fip->fd_info)) != 0)
+		if ((rv = func(cd, fip->fd_info)) != 0)
 			return (rv);
 	}
 	return (0);
--- a/usr/src/lib/libproc/common/Pgcore.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/common/Pgcore.c	Tue Nov 19 11:04:46 2019 +0000
@@ -27,6 +27,7 @@
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #define	_STRUCTURED_PROC	1
@@ -49,6 +50,7 @@
 
 #include "Pcontrol.h"
 #include "P32ton.h"
+#include "proc_fd.h"
 
 typedef enum {
 	STR_NONE,
@@ -611,12 +613,19 @@
 }
 
 static int
-iter_fd(void *data, prfdinfo_t *fdinfo)
+iter_fd(void *data, const prfdinfo_t *fdinfo)
 {
 	fditer_t *iter = data;
+	prfdinfo_core_t core;
+	int ret = 0;
 
-	if (write_note(iter->fd_fd, NT_FDINFO, fdinfo,
-	    sizeof (*fdinfo), iter->fd_doff) != 0)
+	if (proc_fdinfo_to_core(fdinfo, &core) != 0)
+		return (1);
+
+	ret = write_note(iter->fd_fd, NT_FDINFO, &core,
+	    sizeof (core), iter->fd_doff);
+
+	if (ret != 0)
 		return (1);
 	return (0);
 }
--- a/usr/src/lib/libproc/common/libproc.h	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/common/libproc.h	Tue Nov 19 11:04:46 2019 +0000
@@ -28,7 +28,7 @@
  * Copyright 2019 Joyent, Inc.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright 2019, Carlos Neira <cneirabustos@gmail.com>
- * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -469,6 +469,18 @@
 #define	PR_WALK_INCLUDE_SYS	0x80000000	/* include SSYS processes */
 
 /*
+ * File descriptor iteration.
+ */
+typedef int proc_fdwalk_f(const prfdinfo_t *, void *);
+extern int proc_fdwalk(pid_t, proc_fdwalk_f *, void *);
+
+/*
+ * fdinfo iteration.
+ */
+typedef int proc_fdinfowalk_f(uint_t, const void *, size_t, void *);
+extern int proc_fdinfowalk(const prfdinfo_t *, proc_fdinfowalk_f *, void *);
+
+/*
  * Determine if an lwp is in a set as returned from proc_arg_xgrab().
  */
 extern int proc_lwp_in_set(const char *, lwpid_t);
@@ -706,6 +718,9 @@
 extern int proc_get_psinfo(pid_t, psinfo_t *);
 extern int proc_get_status(pid_t, pstatus_t *);
 extern int proc_get_secflags(pid_t, prsecflags_t **);
+extern prfdinfo_t *proc_get_fdinfo(pid_t, int);
+extern const void *proc_fdinfo_misc(const prfdinfo_t *, uint_t, size_t *);
+extern void proc_fdinfo_free(prfdinfo_t *);
 
 /*
  * Utility functions for debugging tools to convert numeric fault,
@@ -774,7 +789,7 @@
 /*
  * Iterate over all open files.
  */
-typedef int proc_fdinfo_f(void *, prfdinfo_t *);
+typedef int proc_fdinfo_f(void *, const prfdinfo_t *);
 extern int Pfdinfo_iter(struct ps_prochandle *, proc_fdinfo_f *, void *);
 
 #ifdef	__cplusplus
--- a/usr/src/lib/libproc/common/mapfile-vers	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/lib/libproc/common/mapfile-vers	Tue Nov 19 11:04:46 2019 +0000
@@ -24,7 +24,7 @@
 # Copyright 2018 Joyent, Inc.
 # Copyright (c) 2013 by Delphix. All rights reserved.
 # Copyright (c) 2019 Carlos Neira <cneirabustos@gmail.com>
-# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 #
 
 #
@@ -207,6 +207,10 @@
 	proc_arg_xpsinfo;
 	proc_content2str;
 	proc_dmodelname;
+	proc_fdinfo_free;
+	proc_fdinfo_misc;
+	proc_fdinfowalk;
+	proc_fdwalk;
 	proc_finistdio;
 	proc_fltname;
 	proc_fltset2str;
@@ -214,6 +218,7 @@
 	proc_free_priv;
 	proc_get_auxv;
 	proc_get_cred;
+	proc_get_fdinfo;
 	proc_get_priv;
 	proc_get_psinfo;
         proc_get_secflags;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libproc/common/proc_fd.c	Tue Nov 19 11:04:46 2019 +0000
@@ -0,0 +1,350 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/sysmacros.h>
+
+#include <libgen.h>
+#include <limits.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include "Pcontrol.h"
+
+/*
+ * Walk all file descriptors open for a process and call func() for each.
+ */
+int
+proc_fdwalk(pid_t pid, proc_fdwalk_f *func, void *arg)
+{
+	struct dirent *dirent;
+	DIR *fddir;
+	char *dir;
+	int ret = 0;
+
+	if (asprintf(&dir, "%s/%d/fd", procfs_path, (int)pid) == -1)
+		return (-1);
+
+	if ((fddir = opendir(dir)) == NULL) {
+		free(dir);
+		return (-1);
+	}
+
+	free(dir);
+
+	while ((dirent = readdir(fddir)) != NULL) {
+		prfdinfo_t *info;
+		char *errptr;
+		int fd;
+
+		if (!isdigit(dirent->d_name[0]))
+			continue;
+
+		fd = (int)strtol(dirent->d_name, &errptr, 10);
+		if (errptr != NULL && *errptr != '\0')
+			continue;
+
+		if ((info = proc_get_fdinfo(pid, fd)) == NULL)
+			continue;
+
+		ret = func(info, arg);
+
+		free(info);
+
+		if (ret != 0)
+			break;
+	}
+
+	(void) closedir(fddir);
+	return (ret);
+}
+
+int
+proc_fdinfowalk(const prfdinfo_t *info, proc_fdinfowalk_f *func, void *arg)
+{
+	off_t off = offsetof(prfdinfo_t, pr_misc);
+	int ret = 0;
+
+	for (;;) {
+		const pr_misc_header_t *misc;
+		uint_t type;
+		size_t size;
+
+		misc = (pr_misc_header_t *)((uint8_t *)info + off);
+
+		/* Found terminating record */
+		if (misc->pr_misc_size == 0)
+			break;
+
+		off += misc->pr_misc_size;
+
+		type = misc->pr_misc_type;
+		size = misc->pr_misc_size - sizeof (pr_misc_header_t);
+		misc++;
+
+		ret = func(type, misc, size, arg);
+
+		if (ret != 0)
+			break;
+	}
+
+	return (ret);
+}
+
+prfdinfo_t *
+proc_get_fdinfo(pid_t pid, int fd)
+{
+	prfdinfo_t *info = NULL;
+	char *fname;
+	uint_t retries;
+	int ifd, err = EIO;
+
+	if (asprintf(&fname, "%s/%d/fdinfo/%d",
+	    procfs_path, (int)pid, fd) == -1) {
+		return (NULL);
+	}
+
+	if ((ifd = open(fname, O_RDONLY)) == -1) {
+		free(fname);
+		return (NULL);
+	}
+
+	free(fname);
+
+	/*
+	 * There is a race between stat()-ing the file and reading from
+	 * it where the size may change. To protect against that, we
+	 * walk the returned data to ensure that it is properly
+	 * terminated. If not, increase the buffer size and try again.
+	 */
+
+	for (retries = 1; retries < 5; retries++) {
+		struct stat st;
+		off_t off;
+		size_t l;
+
+		if (fstat(ifd, &st) == -1) {
+			err = errno;
+			break;
+		}
+
+		st.st_size *= retries;
+
+		if ((info = reallocf(info, st.st_size)) == NULL) {
+			err = errno;
+			break;
+		}
+
+		if ((l = read(ifd, info, st.st_size)) == -1) {
+			err = errno;
+			break;
+		}
+
+		/* Walk the data to check that is properly terminated. */
+
+		off = offsetof(prfdinfo_t, pr_misc);
+
+		while (off <= l - sizeof (pr_misc_header_t)) {
+			pr_misc_header_t *misc;
+
+			misc = (pr_misc_header_t *)((uint8_t *)info + off);
+
+			if (misc->pr_misc_size == 0) {
+				/* Found terminator record */
+				(void) close(ifd);
+				return (info);
+			}
+
+			/* Next record */
+			off += misc->pr_misc_size;
+		}
+	}
+
+	(void) close(ifd);
+	free(info);
+
+	errno = err;
+
+	return (NULL);
+}
+
+typedef struct proc_fdinfo_misc_cbdata {
+	uint_t type;
+	const void *data;
+	size_t len;
+} pfm_data_t;
+
+static int
+proc_fdinfo_misc_cb(uint_t type, const void *data, size_t len, void *datap)
+{
+	pfm_data_t *cb = (pfm_data_t *)datap;
+
+	if (type == cb->type) {
+		cb->data = data;
+		cb->len = len;
+		return (1);
+	}
+	return (0);
+}
+
+const void *
+proc_fdinfo_misc(const prfdinfo_t *info, uint_t type, size_t *buflen)
+{
+	pfm_data_t cb;
+
+	cb.data = NULL;
+	cb.type = type;
+
+	(void) proc_fdinfowalk(info, proc_fdinfo_misc_cb, (void *)&cb);
+
+	if (cb.data != NULL) {
+		if (buflen != NULL)
+			*buflen = cb.len;
+
+		return (cb.data);
+	}
+
+	return (NULL);
+}
+
+static int
+proc_fdinfo_dup_cb(uint_t type, const void *data, size_t len, void *datap)
+{
+	size_t *sz = (size_t *)datap;
+
+	*sz += len + sizeof (pr_misc_header_t);
+	return (0);
+}
+
+
+prfdinfo_t *
+proc_fdinfo_dup(const prfdinfo_t *old)
+{
+	prfdinfo_t *new;
+	size_t sz = offsetof(prfdinfo_t, pr_misc);
+
+	/* Determine the size of the miscellaneous items */
+	(void) proc_fdinfowalk(old, proc_fdinfo_dup_cb, (void *)&sz);
+
+	/* Add the size of the terminator record */
+	sz += sizeof (pr_misc_header_t);
+
+	if ((new = calloc(1, sz)) == NULL)
+		return (NULL);
+
+	bcopy(old, new, sz);
+
+	return (new);
+}
+
+void
+proc_fdinfo_free(prfdinfo_t *info)
+{
+	free(info);
+}
+
+/*
+ * Convert a prfdinfo_core_t to prfdinfo_t
+ */
+int
+proc_fdinfo_from_core(const prfdinfo_core_t *core, prfdinfo_t **infop)
+{
+	prfdinfo_t *info;
+	size_t len, slen = 0;
+
+	len = offsetof(prfdinfo_t, pr_misc) + sizeof (pr_misc_header_t);
+	if (*core->pr_path != '\0') {
+		slen = strlen(core->pr_path) + 1;
+		len += PRFDINFO_ROUNDUP(slen) + sizeof (pr_misc_header_t);
+	}
+
+	if ((info = calloc(1, len)) == NULL)
+		return (-1);
+
+	*infop = info;
+
+	info->pr_fd = core->pr_fd;
+	info->pr_mode = core->pr_mode;
+	info->pr_uid = core->pr_uid;
+	info->pr_gid = core->pr_gid;
+	info->pr_major = core->pr_major;
+	info->pr_minor = core->pr_minor;
+	info->pr_rmajor = core->pr_rmajor;
+	info->pr_rminor = core->pr_rminor;
+	info->pr_size = core->pr_size;
+	info->pr_ino = core->pr_ino;
+	info->pr_fileflags = core->pr_fileflags;
+	info->pr_fdflags = core->pr_fdflags;
+	info->pr_offset = core->pr_offset;
+
+	if (slen != 0) {
+		pr_misc_header_t *misc;
+
+		misc = (pr_misc_header_t *)&info->pr_misc;
+
+		misc->pr_misc_size = sizeof (*misc) + PRFDINFO_ROUNDUP(slen);
+		misc->pr_misc_type = PR_PATHNAME;
+		misc++;
+		bcopy(core->pr_path, misc, slen);
+	}
+
+	return (0);
+}
+
+/*
+ * Convert a prfdinfo_t to prfdinfo_core_t
+ */
+int
+proc_fdinfo_to_core(const prfdinfo_t *info, prfdinfo_core_t *core)
+{
+	const char *path;
+	size_t pathl;
+
+	bzero(core, sizeof (*core));
+
+	core->pr_fd = info->pr_fd;
+	core->pr_mode = info->pr_mode;
+	core->pr_uid = info->pr_uid;
+	core->pr_gid = info->pr_gid;
+	core->pr_major = info->pr_major;
+	core->pr_minor = info->pr_minor;
+	core->pr_rmajor = info->pr_rmajor;
+	core->pr_rminor = info->pr_rminor;
+	core->pr_size = info->pr_size;
+	core->pr_ino = info->pr_ino;
+	core->pr_fileflags = info->pr_fileflags;
+	core->pr_fdflags = info->pr_fdflags;
+	core->pr_offset = info->pr_offset;
+
+	path = proc_fdinfo_misc(info, PR_PATHNAME, &pathl);
+	if (path != NULL) {
+		/*
+		 * Rather than provide a truncated path in the pr_path field
+		 * just leave it empty if the path will not fit.
+		 */
+		if (pathl <= sizeof (core->pr_path) - 1)
+			bcopy(path, core->pr_path, pathl + 1);
+	}
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libproc/common/proc_fd.h	Tue Nov 19 11:04:46 2019 +0000
@@ -0,0 +1,32 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#ifndef	_PROC_FD_H
+#define	_PROC_FD_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* Private functions */
+extern int proc_fdinfo_from_core(const prfdinfo_core_t *, prfdinfo_t **);
+extern int proc_fdinfo_to_core(const prfdinfo_t *, prfdinfo_core_t *);
+extern prfdinfo_t *proc_fdinfo_dup(const prfdinfo_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _PROC_FD_H */
--- a/usr/src/man/man3lib/libproc.3lib	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/man/man3lib/libproc.3lib	Tue Nov 19 11:04:46 2019 +0000
@@ -11,7 +11,7 @@
 .\"
 .\" Copyright 2018 Joyent, Inc.
 .\" Copyright (c) 2019 Carlos Neira <cneirabustos@gmail.com>
-.\" Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 .\"
 .Dd February 22, 2019
 .Dt LIBPROC 3LIB
@@ -311,7 +311,8 @@
 .It Sy proc_dmodelname Ta Sy proc_finistdio
 .It Sy proc_fltname Ta Sy proc_fltset2str
 .It Sy proc_flushstdio Ta Sy proc_proc_get_auxv
-.It Sy proc_get_cred Ta Sy proc_get_priv
+.It Sy proc_fdinfo_misc Ta Sy proc_get_cred
+.It Sy proc_get_fdinfo Ta Sy proc_get_priv
 .It Sy proc_get_psinfo Ta Sy proc_get_status
 .It Sy proc_get_initstdio Ta Sy proc_lwp_in_set
 .It Sy proc_lwp_range_valid Ta Sy proc_signame
@@ -562,25 +563,39 @@
 The
 .Vt prfdinfo_t
 structure is used with the
-.Fn Pfdinfo_iter
-function which describes information about a file descriptor.
+.Fn Pfdinfo_iter ,
+.Fn proc_fdwalk ,
+.Fn proc_fdinfowalk
+and
+.Fn proc_get_fdinfo
+functions and describes information about a file descriptor.
 The structure is defined as follows:
 .Bd -literal
 typedef struct prfdinfo {
-	int		pr_fd;
-	mode_t		pr_mode;
-	uid_t		pr_uid;
-	gid_t		pr_gid;
-	major_t		pr_major;	/* think stat.st_dev */
-	minor_t		pr_minor;
-	major_t		pr_rmajor;	/* think stat.st_rdev */
-	minor_t		pr_rminor;
-	ino64_t		pr_ino;
-	off64_t		pr_offset;
-	off64_t		pr_size;
-	int		pr_fileflags;	/* fcntl(F_GETXFL), etc */
-	int		pr_fdflags;	/* fcntl(F_GETFD), etc. */
-	char		pr_path[MAXPATHLEN];
+    int		pr_fd;		/* file descriptor number */
+    mode_t	pr_mode;	/* (see st_mode in stat(2)) */
+    ino64_t	pr_ino;		/* inode number */
+    off64_t	pr_size;	/* file size */
+    off64_t	pr_offset;	/* current offset */
+    uid_t	pr_uid;		/* owner's user id */
+    gid_t	pr_gid;		/* owner's group id */
+    major_t	pr_major;	/* major number of device */
+    minor_t	pr_minor;	/* minor number of device */
+    major_t	pr_rmajor;	/* major number (if special file) */
+    minor_t	pr_rminor;	/* minor number (if special file) */
+    int		pr_fileflags;	/* (see F_GETXFL in fcntl(2)) */
+    int		pr_fdflags;	/* (see F_GETFD in fcntl(2)) */
+    short	pr_locktype;	/* (see F_GETLK in fcntl(2)) */
+    pid_t	pr_lockpid;	/* process holding file lock */
+    int		pr_locksysid;	/* sysid of locking process */
+    pid_t	pr_peerpid;	/* peer process (socket, door) */
+    int		pr_filler[25];	/* reserved for future use */
+    char	pr_peername[PRFNSZ];	/* peer process name */
+#if    __STDC_VERSION__ >= 199901L
+    uint8_t	pr_misc[];	/* self describing structures */
+else
+    uint8_t	pr_misc[1];	/* self describing structures */
+#endif
 } prfdinfo_t;
 .Ed
 .Pp
@@ -650,6 +665,31 @@
 .Dv F_GETFD
 respectively.
 .Pp
+The
+.Fa pr_locktype ,
+.Fa pr_lockpid ,
+and
+.Fa pr_locksysid
+contain the information that would have been returned by a call to
+.Xr fcntl 2
+with an argument of
+.Dv F_GETLK .
+.Pp
+The
+.Fa pr_peerpid
+and
+.Fa pr_peername
+members contain the process ID and name of any peer endpoint of a
+connection-oriented socket or stream fd.
+This information is the same as that which would be returned by a call to
+.Xr getpeerucred 3C
+.Pp
+The
+.Fa pr_misc
+member contains miscellaneous additional data relating to the file descriptor.
+The format of these data is described in
+.Xr proc 4 .
+.Pp
 .Vt prsyminfo_t
 .Pp
 The
@@ -747,6 +787,54 @@
 For additional information on using this type, see
 .Xr Plwp_iter_all 3PROC .
 .Pp
+.Vt proc_fdinfowalk_f
+.Pp
+The
+.Vt proc_fdinfowalk_f
+is a function pointer type that is used with the
+.Fn proc_fdinfowalk
+function to walk the miscellaneous data items contained within a
+.Vt prfdinfo_t
+structure.
+It is defined as
+.Sy typedef
+.Ft int
+.Fo proc_fdinfowalk_f
+.Fa "uint_t"
+.Fa "const void *"
+.Fa "size_t"
+.Fa "void *"
+.Fc .
+The first argument contains the type of the miscellaneous information being
+presented, the second and third provide a pointer to the associated data and
+the length of that data.
+The final argument is a pointer to an argument that the user specifies.
+For more information on using this, see
+.Xr proc_fdinfowalk 3PROC .
+.Pp
+.Vt proc_fdwalk_f
+.Pp
+The
+.Vt proc_fdwalk_f
+is a function pointer type that is used with the
+.Fn proc_fdwalk
+function.
+It is defined as
+.Sy typedef
+.Ft int
+.Fo proc_fdwalk_f
+.Fa "const prfdinfo_t *"
+.Fa "void *"
+.Fc .
+The first argument contains the file descriptor information.
+The
+.Sy prfdinfo_t
+structure is defined in
+.Xr proc 4 .
+The final argument is a pointer to an argument that the user specifies.
+For more information on using this, see
+.Xr proc_fdwalk 3PROC .
+.Pp
 .Vt proc_walk_f
 .Pp
 The
@@ -1227,6 +1315,7 @@
 .Xr proc_flushstdio 3PROC ,
 .Xr proc_get_auxv 3PROC ,
 .Xr proc_get_cred 3PROC ,
+.Xr proc_get_fdinfo 3PROC ,
 .Xr proc_get_priv 3PROC ,
 .Xr proc_get_psinfo 3PROC ,
 .Xr proc_get_status 3PROC ,
@@ -1245,6 +1334,8 @@
 .Xr proc_sysname 3PROC ,
 .Xr proc_sysset2str 3PROC ,
 .Xr proc_unctrl_psinfo 3PROC ,
+.Xr proc_fdinfowalk 3PROC ,
+.Xr proc_fdwalk 3PROC ,
 .Xr proc_walk 3PROC
 .Pp
 .Xr Pldt 3PROC ,
--- a/usr/src/man/man3proc/Makefile	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/man/man3proc/Makefile	Tue Nov 19 11:04:46 2019 +0000
@@ -14,7 +14,7 @@
 # Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
 # Copyright 2018 Joyent, Inc.
 # Copyright 2019, Carlos Neira <cneirabustos@gmail.com>
-# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 
 include		$(SRC)/Makefile.master
 
@@ -123,10 +123,14 @@
 		proc_arg_grab.3proc		\
 		proc_arg_psinfo.3proc		\
 		proc_content2str.3proc		\
+		proc_fdinfo_misc.3proc		\
+		proc_fdinfowalk.3proc		\
+		proc_fdwalk.3proc		\
 		proc_fltname.3proc		\
 		proc_fltset2str.3proc		\
 		proc_get_auxv.3proc		\
 		proc_get_cred.3proc		\
+		proc_get_fdinfo.3proc		\
 		proc_get_priv.3proc		\
 		proc_get_psinfo.3proc		\
 		proc_get_status.3proc		\
@@ -222,6 +226,7 @@
 		proc_arg_xgrab.3proc		\
 		proc_arg_xpsinfo.3proc		\
 		proc_dmodelname.3proc		\
+		proc_fdinfo_free.3proc		\
 		proc_finistdio.3proc		\
 		proc_flushstdio.3proc		\
 		proc_free_priv.3proc		\
@@ -394,6 +399,8 @@
 
 proc_str2content.3proc		:= LINKSRC = proc_content2str.3proc
 
+proc_fdinfo_free.3proc		:= LINKSRC = proc_get_fdinfo.3proc
+
 proc_flushstdio.3proc		:= LINKSRC = proc_initstdio.3proc
 proc_finistdio.3proc		:= LINKSRC = proc_initstdio.3proc
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/man/man3proc/proc_fdinfo_misc.3proc	Tue Nov 19 11:04:46 2019 +0000
@@ -0,0 +1,71 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+.\"
+.Dd January 6, 2020
+.Dt PROC_FDINFO_MISC 3PROC
+.Os
+.Sh NAME
+.Nm proc_fdinfo_misc
+.Nd retrieve a miscellaneous information item from a prfdinfo_t structure
+.Sh SYNOPSIS
+.Lb libproc
+.In libproc.h
+.Ft const void *
+.Fo proc_fdinfo_misc
+.Fa "const prfdinfo_t *info"
+.Fa "uint_t type"
+.Fa "size_t *len"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn proc_fdinfo_misc
+function is a convenient way to retrieve a miscellaneous information item from
+a
+.Vt prfdinfo_t
+structure.
+.Pp
+If a miscellaneous item of type
+.Fa type
+is found, then this function returns a pointer to the data for that item and
+updates
+.Fa len
+with the item's size.
+.Pp
+In the case that there are multiple instances of the requested type in the
+structure, only the first is returned.
+To see all instances, use the
+.Xr proc_fdinfowalk 3PROC
+function.
+.Pp
+The definition of the
+.Vt prfdinfo_t
+structure may be found in the
+.Sx fdinfo
+section of
+.Xr proc 4 .
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn proc_fdinfo_misc
+function returns a pointer to the first instance of data with the requested
+type.
+Otherwise,
+.Sy NULL
+is returned to indicate that the item was not found.
+.Sh INTERFACE STABILITY
+.Sy Uncommitted
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr libproc 3LIB ,
+.Xr proc_fdinfowalk 3PROC ,
+.Xr proc 4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/man/man3proc/proc_fdinfowalk.3proc	Tue Nov 19 11:04:46 2019 +0000
@@ -0,0 +1,95 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+.\"
+.Dd January 6, 2020
+.Dt PROC_FDINFOWALK 3PROC
+.Os
+.Sh NAME
+.Nm proc_fdinfowalk
+.Nd walk the additional miscellaneous information in a prfdinfo_t structure
+.Sh SYNOPSIS
+.Lb libproc
+.In libproc.h
+.Ft int
+.Fo proc_fdinfowalk
+.Fa "const prfdinfo_t *info"
+.Fa "proc_fdinfowalk_f *func"
+.Fa "void *arg"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn proc_fdinfowalk
+function walks a
+.Vt prfdinfo_t
+structure and calls the callback function
+.Fa func
+once for each miscellaneous item of information present,
+along with the user-specified
+.Fa arg .
+The definition of
+.Sy proc_fdinfowalk_f
+is available in
+.Xr libproc 3LIB .
+The miscellaneous data types and structures are found in the
+.Sx fdinfo
+section of
+.Xr proc 4 .
+.Pp
+.Fa func
+will be called once for each miscellaneous item, providing the item's
+.Fa type ,
+.Fa size
+and
+.Fa address.
+The
+.Fa size
+may include trailing padding bytes which will be set to zero.
+.Pp
+The return value of the caller's
+.Fa func
+function determines whether or not iteration will continue.
+If
+.Fa func
+returns a non-zero value, then iteration will terminate and that
+return value will be returned to the caller.
+To distinguish between system errors and caller errors, it is recommended that
+the function only return positive integers in the event of an error.
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn proc_fdinfowalk
+function returns
+.Sy 0 .
+Otherwise,
+.Sy -1
+is returned and
+.Sy errno
+is updated to reflect the error that occurred.
+.Sh ERRORS
+In addition to the errors listed below, the
+.Fn proc_fdinfowalk
+function may fail for the same reasons as the
+.Xr opendir 3C ,
+.Xr readdir 3C ,
+and
+.Xr malloc 3C
+functions.
+.Sh INTERFACE STABILITY
+.Sy Uncommitted
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr malloc 3C ,
+.Xr opendir 3C ,
+.Xr readdir 3C ,
+.Xr libproc 3LIB ,
+.Xr proc 4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/man/man3proc/proc_fdwalk.3proc	Tue Nov 19 11:04:46 2019 +0000
@@ -0,0 +1,87 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+.\"
+.Dd January 6, 2020
+.Dt PROC_FDWALK 3PROC
+.Os
+.Sh NAME
+.Nm proc_fdwalk
+.Nd walk the open file descriptors for a process
+.Sh SYNOPSIS
+.Lb libproc
+.In libproc.h
+.Ft int
+.Fo proc_fdwalk
+.Fa "pid_t pid"
+.Fa "proc_fdwalk_f *func"
+.Fa "void *arg"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn proc_fdwalk
+function walks all file descriptors currently open in the process with ID
+.Fa pid
+and calls the callback function
+.Fa func
+once for each file descriptor with the user-specified
+.Fa arg .
+The definition of
+.Vt proc_fdwalk_f
+is available in
+.Xr libproc 3LIB .
+.Pp
+.Fa func
+will be called once for each file descriptor and will have its first
+argument filled in with the contents of the corresponding
+.Pa /proc
+.Sy fdinfo
+file for the file descriptor.
+.Pp
+The return value of the caller's
+.Fa func
+function determines whether or not iteration will continue.
+If
+.Fa func
+returns a non-zero value, then iteration will terminate and that
+return value will be returned to the caller.
+To distinguish between system errors and caller errors, it is recommended that
+the function only return positive integers in the event of an error.
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn proc_fdwalk
+function returns
+.Sy 0 .
+Otherwise,
+.Sy -1
+is returned and
+.Sy errno
+is updated to reflect the error that occurred.
+.Sh ERRORS
+In addition to the errors listed below, the
+.Fn proc_fdwalk
+function may fail for the same reasons as the
+.Xr opendir 3C ,
+.Xr readdir 3C ,
+and
+.Xr malloc 3C
+functions.
+.Sh INTERFACE STABILITY
+.Sy Uncommitted
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr malloc 3C ,
+.Xr opendir 3C ,
+.Xr readdir 3C ,
+.Xr libproc 3LIB ,
+.Xr proc 4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/man/man3proc/proc_get_fdinfo.3proc	Tue Nov 19 11:04:46 2019 +0000
@@ -0,0 +1,99 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+.\"
+.Dd January 6, 2020
+.Dt PROC_GET_FDINFO 3PROC
+.Os
+.Sh NAME
+.Nm proc_get_fdinfo ,
+.Nm proc_fdinfo_free
+.Nd get process file descriptor information
+.Sh LIBRARY
+.Lb libproc
+.Sh SYNOPSIS
+.In libproc.h
+.Ft prfdinfo_t *
+.Fo proc_get_fdinfo
+.Fa "pid_t pid"
+.Fa "int fd"
+.Fc
+.Ft "void"
+.Fo proc_fdinfo_free
+.Fa "prfdinfo_t *info"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn proc_get_fdinfo
+function is a convenient way to read the
+.Pa /proc/pid/fdinfo/fd
+file for the process
+.Fa pid
+and file descriptor
+.Fa fd .
+On success, the return value of the function is a pointer to a properly
+terminated
+.Sy prfdinfo_t
+structure.
+.Pp
+The definition of the
+.Vt prfdinfo_t
+structure may be found in
+.Xr proc 4 .
+.Pp
+The caller must free the returned memory by calling the
+.Fn proc_fdinfo_free
+function.
+.Pp
+The
+.Fn proc_fdinfo_free
+frees all memory associated with the
+.Vt prfdinfo_t
+structure passed as
+.Fa info .
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn proc_get_fdinfo
+function returns a pointer to an allocated
+.Vt prfdinfo_t
+structure.
+Otherwise,
+.Dv NULL
+is returned to indicate an error occurred and
+.Vt errno
+is set to indicate the error.
+.Sh ERRORS
+In addition to the errors listed below, the
+.Fn proc_get_fdinfo
+function may fail for the same reasons as the
+.Xr fstat 2 ,
+.Xr malloc 3C ,
+.Xr open 2 ,
+and
+.Xr read 2
+functions.
+The
+.Fn proc_get_fdinfo
+function will fail if:
+.Bl -tag -width Er
+.It Er EIO
+The data read from the file under
+.Pa /proc
+is not properly terminated.
+.El
+.Sh INTERFACE STABILITY
+.Sy Uncommitted
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr libproc 3LIB ,
+.Xr proc 4
--- a/usr/src/man/man4/core.4	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/man/man4/core.4	Tue Nov 19 11:04:46 2019 +0000
@@ -2,15 +2,15 @@
 .\" Copyright (C) 2008, Sun Microsystems, Inc. All Rights Reserved.
 .\" Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
 .\" Copyright (c) 2013, Joyent, Inc. All rights reserved.
+.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 .\" Copyright 1989 AT&T
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH CORE 4 "Jun 6, 2016"
+.TH CORE 4 "Jan 6, 2020"
 .SH NAME
 core \- process core file
 .SH DESCRIPTION
-.LP
 The operating system writes out a core file for a process when the process is
 terminated due to receiving certain signals. A core file is a disk copy of the
 contents of the process address space at the time the process received the
@@ -335,12 +335,12 @@
 .sp
 .ne 2
 .na
-\fB\fBprfdinfo_t\fR\fR
+\fB\fBprfdinfo_core_t\fR\fR
 .ad
 .RS 20n
 \fBn_type\fR: \fBNT_FDINFO\fR. This structure contains information about
 any open file descriptors, including the path, flags, and
-\fBstat\fR(2) information.  The \fBprfdinfo_t\fR structure is defined in
+\fBstat\fR(2) information.  The \fBprfdinfo_core_t\fR structure is defined in
 <\fBsys/procfs.h\fR>.
 .RE
 
@@ -472,7 +472,6 @@
 The size of the core file created by a process can be controlled by the user
 (see \fBgetrlimit\fR(2)).
 .SH SEE ALSO
-.LP
 \fBelfdump\fR(1), \fBgcore\fR(1), \fBmdb\fR(1), \fBproc\fR(1), \fBps\fR(1),
 \fBcoreadm\fR(1M), \fBgetrlimit\fR(2), \fBsetrlimit\fR(2), \fBsetuid\fR(2),
 \fBsysinfo\fR(2), \fBuname\fR(2), \fBgetzonenamebyid\fR(3C),
--- a/usr/src/man/man4/proc.4	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/man/man4/proc.4	Tue Nov 19 11:04:46 2019 +0000
@@ -1,6 +1,7 @@
 .\" Copyright 1989 AT&T
 .\" Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved.
 .\" Copyright 2019, Joyent, Inc.
+.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 .\"
 .\" The contents of this file are subject to the terms of the
 .\" Common Development and Distribution License (the "License").
@@ -17,7 +18,7 @@
 .\" fields enclosed by brackets "[]" replaced with your own identifying
 .\" information: Portions Copyright [yyyy] [name of copyright owner]
 .\"
-.Dd January 11, 2019
+.Dd January 6, 2020
 .Dt PROC 4
 .Os
 .Sh NAME
@@ -1382,6 +1383,140 @@
 .Pa /proc/ Ns Em pid Ns Pa /cwd .
 An attempt to open any other type of entry fails with
 .Er EACCES .
+.Ss fdinfo
+A directory containing information about each of the process's open files.
+Each entry is a decimal number corresponding to an open file descriptor in the
+process.
+Each file contains a
+.Sy prfdinfo_t
+structure defined as follows:
+.Bd -literal -offset 2
+typedef struct prfdinfo {
+    int     pr_fd;          /* file descriptor number */
+    mode_t  pr_mode;        /* (see st_mode in stat(2)) */
+    uint64_t pr_ino;        /* inode number */
+    uint64_t pr_size;       /* file size */
+    int64_t pr_offset;      /* current offset of file descriptor */
+    uid_t   pr_uid;         /* owner's user id */
+    gid_t   pr_gid;         /* owner's group id */
+    major_t pr_major;       /* major number of device containing file */
+    minor_t pr_minor;       /* minor number of device containing file */
+    major_t pr_rmajor;      /* major number (if special file) */
+    minor_t pr_rminor;      /* minor number (if special file) */
+    int     pr_fileflags;   /* (see F_GETXFL in fcntl(2)) */
+    int     pr_fdflags;     /* (see F_GETFD in fcntl(2)) */
+    short   pr_locktype;    /* (see F_GETLK in fcntl(2)) */
+    pid_t   pr_lockpid;     /* process holding file lock (see F_GETLK) */
+    int     pr_locksysid;   /* sysid of locking process (see F_GETLK) */
+    pid_t   pr_peerpid;     /* peer process (socket, door) */
+    int     pr_filler[25];  /* reserved for future use */
+    char    pr_peername[PRFNSZ]; /* peer process name */
+#if __STDC_VERSION__ >= 199901L
+    char    pr_misc[];      /* self describing structures */
+#else
+    char    pr_misc[1];
+#endif
+} prfdinfo_t;
+.Ed
+.Pp
+The
+.Sy pr_misc
+element points to a list of additional miscellaneous data items, each of which
+has a header of type
+.Sy pr_misc_header_t
+specifying the size and type, and some data which immediately follow
+the header.
+.Bd -literal -offset 2
+typedef struct pr_misc_header {
+    uint_t          pr_misc_size;
+    uint_t          pr_misc_type;
+} pr_misc_header_t;
+.Ed
+.Pp
+The
+.Sy pr_misc_size
+field is the sum of the sizes of the header and the associated data and any
+trailing padding bytes which will be set to zero.
+The end of the list is indicated by a header with a zero size and a type with
+all bits set.
+.Pp
+The following miscellaneous data types can be present:
+.Bl -tag -width "PR_SOCKOPT_TCP_CONGESTION" -offset left
+.It Sy PR_PATHNAME
+The file descriptor's path in the filesystem.
+This is a NUL-terminated sequence of characters.
+.It Sy PR_SOCKETNAME
+A
+.Sy sockaddr
+structure representing the local socket name for this file descriptor, as
+would be returned by calling
+.Fn getsockname
+within the process.
+.It Sy PR_PEERSOCKNAME
+A
+.Sy sockaddr
+structure representing the peer socket name for this file descriptor, as
+would be returned by calling
+.Fn getpeername
+within the process.
+.It Sy PR_SOCKOPTS_BOOL_OPTS
+An unsigned integer which has bits set corresponding to options which are
+set on the underlying socket.
+The following bits may be set:
+.Bl -tag -width "PR_SO_PASSIVE_CONNECT"
+.It Sy PR_SO_DEBUG
+.It Sy PR_SO_REUSEADDR
+.It Sy PR_SO_REUSEPORT
+.It Sy PR_SO_KEEPALIVE
+.It Sy PR_SO_DONTROUTE
+.It Sy PR_SO_BROADCAST
+.It Sy PR_SO_OOBINLINE
+.It Sy PR_SO_DGRAM_ERRIND
+.It Sy PR_SO_ALLZONES
+.It Sy PR_SO_MAC_EXEMPT
+.It Sy PR_SO_EXCLBIND
+.It Sy PR_SO_PASSIVE_CONNECT
+.It Sy PR_SO_ACCEPTCONN
+.It Sy PR_UDP_NAT_T_ENDPOINT
+.It Sy PR_SO_VRRP
+.It Sy PR_SO_MAC_IMPLICIT
+.El
+.It Sy PR_SOCKOPT_LINGER
+A
+.Sy struct linger
+as would be returned by calling
+.Fn getsockopt SO_LINGER
+within the process.
+.It Sy PR_SOCKOPT_SNDBUF
+The data that would be returned by calling
+.Fn getsockopt SO_SNDBUF
+within the process.
+.It Sy PR_SOCKOPT_RCVBUF
+The data that would be returned by calling
+.Fn getsockopt SO_RCVBUF
+within the process.
+.It Sy PR_SOCKOPT_IP_NEXTHOP
+The data that would be returned by calling
+.Fn getsockopt IPPROTO_IP IP_NEXTHOP
+within the process.
+.It Sy PR_SOCKOPT_IPV6_NEXTHOP
+The data that would be returned by calling
+.Fn getsockopt IPPROTO_IPV6 IPV6_NEXTHOP
+within the process.
+.It Sy PR_SOCKOPT_TYPE
+The data that would be returned by calling
+.Fn getsockopt SO_TYPE
+within the process.
+.It Sy PR_SOCKOPT_TCP_CONGESTION
+For TCP sockets, the data that would be returned by calling
+.Fn getsockopt IPPROTO_TCP TCP_CONGESTION
+within the process.
+This is a NUL-terminated character array containing the name of the congestion
+algorithm in use for the socket.
+.It Sy PR_SOCKFILTERS_PRIV
+Private data relating to up to the first 32 socket filters pushed on this
+descriptor.
+.El
 .Ss object
 A directory containing read-only files with names corresponding to the
 .Sy pr_mapname
--- a/usr/src/pkg/manifests/system-library.man3proc.inc	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/pkg/manifests/system-library.man3proc.inc	Tue Nov 19 11:04:46 2019 +0000
@@ -12,37 +12,13 @@
 #
 # Copyright 2018 Joyent, Inc.
 # Copyright (c) 2019 Carlos Neira <cneirabustos@gmail.com>
-# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 #
 
-file path=usr/share/man/man3proc/proc_service.3proc
-file path=usr/share/man/man3proc/ps_lgetregs.3proc
-file path=usr/share/man/man3proc/ps_pglobal_lookup.3proc
-file path=usr/share/man/man3proc/ps_pread.3proc
-file path=usr/share/man/man3proc/ps_pstop.3proc
-link path=usr/share/man/man3proc/ps_kill.3proc target=ps_pstop.3proc
-link path=usr/share/man/man3proc/ps_lcontinue.3proc target=ps_pstop.3proc
-link path=usr/share/man/man3proc/ps_lgetfpregs.3proc target=ps_lgetregs.3proc
-link path=usr/share/man/man3proc/ps_lgetxregs.3proc target=ps_lgetregs.3proc
-link path=usr/share/man/man3proc/ps_lgetxregsize.3proc \
-    target=ps_lgetregs.3proc
-link path=usr/share/man/man3proc/ps_lrolltoaddr.3proc target=ps_pstop.3proc
-link path=usr/share/man/man3proc/ps_lsetfpregs.3proc target=ps_lgetregs.3proc
-link path=usr/share/man/man3proc/ps_lsetregs.3proc target=ps_lgetregs.3proc
-link path=usr/share/man/man3proc/ps_lsetxregs.3proc target=ps_lgetregs.3proc
-link path=usr/share/man/man3proc/ps_lstop.3proc target=ps_pstop.3proc
-link path=usr/share/man/man3proc/ps_pcontinue.3proc target=ps_pstop.3proc
-link path=usr/share/man/man3proc/ps_pdread.3proc target=ps_pread.3proc
-link path=usr/share/man/man3proc/ps_pdwrite.3proc target=ps_pread.3proc
-link path=usr/share/man/man3proc/ps_pglobal_sym.3proc \
-    target=ps_pglobal_lookup.3proc
-link path=usr/share/man/man3proc/ps_ptread.3proc target=ps_pread.3proc
-link path=usr/share/man/man3proc/ps_ptwrite.3proc target=ps_pread.3proc
-link path=usr/share/man/man3proc/ps_pwrite.3proc target=ps_pread.3proc
 file path=usr/share/man/man3proc/Lctlfd.3proc
 file path=usr/share/man/man3proc/Lfree.3proc
+file path=usr/share/man/man3proc/Lgrab.3proc
 file path=usr/share/man/man3proc/Lgrab_error.3proc
-file path=usr/share/man/man3proc/Lgrab.3proc
 file path=usr/share/man/man3proc/Lprochandle.3proc
 file path=usr/share/man/man3proc/Lpsinfo.3proc
 file path=usr/share/man/man3proc/Lstate.3proc
@@ -54,9 +30,9 @@
 file path=usr/share/man/man3proc/Pclearfault.3proc
 file path=usr/share/man/man3proc/Pclearsig.3proc
 file path=usr/share/man/man3proc/Pcontent.3proc
+file path=usr/share/man/man3proc/Pcreate.3proc
 file path=usr/share/man/man3proc/Pcreate_agent.3proc
 file path=usr/share/man/man3proc/Pcreate_error.3proc
-file path=usr/share/man/man3proc/Pcreate.3proc
 file path=usr/share/man/man3proc/Pcred.3proc
 file path=usr/share/man/man3proc/Pctlfd.3proc
 file path=usr/share/man/man3proc/Pdelbkpt.3proc
@@ -72,10 +48,10 @@
 file path=usr/share/man/man3proc/Pgetauxval.3proc
 file path=usr/share/man/man3proc/Pgetauxvec.3proc
 file path=usr/share/man/man3proc/Pgetenv.3proc
+file path=usr/share/man/man3proc/Pgrab.3proc
 file path=usr/share/man/man3proc/Pgrab_core.3proc
 file path=usr/share/man/man3proc/Pgrab_error.3proc
 file path=usr/share/man/man3proc/Pgrab_file.3proc
-file path=usr/share/man/man3proc/Pgrab.3proc
 file path=usr/share/man/man3proc/Pisprocdir.3proc
 file path=usr/share/man/man3proc/Pissyscall.3proc
 file path=usr/share/man/man3proc/Pldt.3proc
@@ -96,6 +72,36 @@
 file path=usr/share/man/man3proc/Ppltdest.3proc
 file path=usr/share/man/man3proc/Ppriv.3proc
 file path=usr/share/man/man3proc/Ppsinfo.3proc
+file path=usr/share/man/man3proc/Prd_agent.3proc
+file path=usr/share/man/man3proc/Pread.3proc
+file path=usr/share/man/man3proc/Prelease.3proc
+file path=usr/share/man/man3proc/Preopen.3proc
+file path=usr/share/man/man3proc/Preset_maps.3proc
+file path=usr/share/man/man3proc/Psecflags.3proc
+file path=usr/share/man/man3proc/Psetbkpt.3proc
+file path=usr/share/man/man3proc/Psetcred.3proc
+file path=usr/share/man/man3proc/Psetfault.3proc
+file path=usr/share/man/man3proc/Psetflags.3proc
+file path=usr/share/man/man3proc/Psetpriv.3proc
+file path=usr/share/man/man3proc/Psetrun.3proc
+file path=usr/share/man/man3proc/Psetsignal.3proc
+file path=usr/share/man/man3proc/Psetsysentry.3proc
+file path=usr/share/man/man3proc/Psetwapt.3proc
+file path=usr/share/man/man3proc/Psetzoneid.3proc
+file path=usr/share/man/man3proc/Psignal.3proc
+file path=usr/share/man/man3proc/Pstack_iter.3proc
+file path=usr/share/man/man3proc/Pstate.3proc
+file path=usr/share/man/man3proc/Pstatus.3proc
+file path=usr/share/man/man3proc/Pstopstatus.3proc
+file path=usr/share/man/man3proc/Psymbol_iter.3proc
+file path=usr/share/man/man3proc/Psync.3proc
+file path=usr/share/man/man3proc/Psysentry.3proc
+file path=usr/share/man/man3proc/Puname.3proc
+file path=usr/share/man/man3proc/Pupdate_maps.3proc
+file path=usr/share/man/man3proc/Pupdate_syms.3proc
+file path=usr/share/man/man3proc/Pwrite.3proc
+file path=usr/share/man/man3proc/Pxecbkpt.3proc
+file path=usr/share/man/man3proc/Pzonename.3proc
 file path=usr/share/man/man3proc/pr_access.3proc
 file path=usr/share/man/man3proc/pr_close.3proc
 file path=usr/share/man/man3proc/pr_creat.3proc
@@ -133,52 +139,31 @@
 file path=usr/share/man/man3proc/pr_statvfs.3proc
 file path=usr/share/man/man3proc/pr_unlink.3proc
 file path=usr/share/man/man3proc/pr_waitid.3proc
-file path=usr/share/man/man3proc/Prd_agent.3proc
-file path=usr/share/man/man3proc/Pread.3proc
-file path=usr/share/man/man3proc/Prelease.3proc
-file path=usr/share/man/man3proc/Preopen.3proc
-file path=usr/share/man/man3proc/Preset_maps.3proc
 file path=usr/share/man/man3proc/proc_arg_grab.3proc
 file path=usr/share/man/man3proc/proc_arg_psinfo.3proc
 file path=usr/share/man/man3proc/proc_content2str.3proc
+file path=usr/share/man/man3proc/proc_fdinfo_misc.3proc
+file path=usr/share/man/man3proc/proc_fdinfowalk.3proc
+file path=usr/share/man/man3proc/proc_fdwalk.3proc
 file path=usr/share/man/man3proc/proc_fltname.3proc
 file path=usr/share/man/man3proc/proc_fltset2str.3proc
 file path=usr/share/man/man3proc/proc_get_auxv.3proc
 file path=usr/share/man/man3proc/proc_get_cred.3proc
+file path=usr/share/man/man3proc/proc_get_fdinfo.3proc
 file path=usr/share/man/man3proc/proc_get_priv.3proc
 file path=usr/share/man/man3proc/proc_get_psinfo.3proc
 file path=usr/share/man/man3proc/proc_get_status.3proc
 file path=usr/share/man/man3proc/proc_initstdio.3proc
 file path=usr/share/man/man3proc/proc_lwp_in_set.3proc
+file path=usr/share/man/man3proc/proc_service.3proc
 file path=usr/share/man/man3proc/proc_str2flt.3proc
 file path=usr/share/man/man3proc/proc_str2fltset.3proc
 file path=usr/share/man/man3proc/proc_unctrl_psinfo.3proc
 file path=usr/share/man/man3proc/proc_walk.3proc
-file path=usr/share/man/man3proc/Psecflags.3proc
-file path=usr/share/man/man3proc/Psetbkpt.3proc
-file path=usr/share/man/man3proc/Psetcred.3proc
-file path=usr/share/man/man3proc/Psetfault.3proc
-file path=usr/share/man/man3proc/Psetflags.3proc
-file path=usr/share/man/man3proc/Psetpriv.3proc
-file path=usr/share/man/man3proc/Psetrun.3proc
-file path=usr/share/man/man3proc/Psetsignal.3proc
-file path=usr/share/man/man3proc/Psetsysentry.3proc
-file path=usr/share/man/man3proc/Psetwapt.3proc
-file path=usr/share/man/man3proc/Psetzoneid.3proc
-file path=usr/share/man/man3proc/Psignal.3proc
-file path=usr/share/man/man3proc/Pstack_iter.3proc
-file path=usr/share/man/man3proc/Pstate.3proc
-file path=usr/share/man/man3proc/Pstatus.3proc
-file path=usr/share/man/man3proc/Pstopstatus.3proc
-file path=usr/share/man/man3proc/Psymbol_iter.3proc
-file path=usr/share/man/man3proc/Psync.3proc
-file path=usr/share/man/man3proc/Psysentry.3proc
-file path=usr/share/man/man3proc/Puname.3proc
-file path=usr/share/man/man3proc/Pupdate_maps.3proc
-file path=usr/share/man/man3proc/Pupdate_syms.3proc
-file path=usr/share/man/man3proc/Pwrite.3proc
-file path=usr/share/man/man3proc/Pxecbkpt.3proc
-file path=usr/share/man/man3proc/Pzonename.3proc
+file path=usr/share/man/man3proc/ps_lgetregs.3proc
+file path=usr/share/man/man3proc/ps_pglobal_lookup.3proc
+file path=usr/share/man/man3proc/ps_pread.3proc
+file path=usr/share/man/man3proc/ps_pstop.3proc
 link path=usr/share/man/man3proc/Lalt_stack.3proc target=Plwp_stack.3proc
 link path=usr/share/man/man3proc/Lclearfault.3proc target=Pclearfault.3proc
 link path=usr/share/man/man3proc/Lclearsig.3proc target=Pclearsig.3proc
@@ -193,7 +178,8 @@
 link path=usr/share/man/man3proc/Lwait.3proc target=Pstopstatus.3proc
 link path=usr/share/man/man3proc/Lxecbkpt.3proc target=Pxecbkpt.3proc
 link path=usr/share/man/man3proc/Lxecwapt.3proc target=Pxecbkpt.3proc
-link path=usr/share/man/man3proc/Paddr_to_text_map.3proc target=Paddr_to_map.3proc
+link path=usr/share/man/man3proc/Paddr_to_text_map.3proc \
+    target=Paddr_to_map.3proc
 link path=usr/share/man/man3proc/Pcreate_callback.3proc target=Pcreate.3proc
 link path=usr/share/man/man3proc/Pdstop.3proc target=Pstopstatus.3proc
 link path=usr/share/man/man3proc/Pfgcore.3proc target=Pgcore.3proc
@@ -201,64 +187,113 @@
 link path=usr/share/man/man3proc/Pfree.3proc target=Prelease.3proc
 link path=usr/share/man/man3proc/Pissyscall_prev.3proc target=Pissyscall.3proc
 link path=usr/share/man/man3proc/Plmid_to_ctf.3proc target=Paddr_to_ctf.3proc
-link path=usr/share/man/man3proc/Plmid_to_loadobj.3proc target=Paddr_to_loadobj.3proc
+link path=usr/share/man/man3proc/Plmid_to_loadobj.3proc \
+    target=Paddr_to_loadobj.3proc
 link path=usr/share/man/man3proc/Plmid_to_map.3proc target=Paddr_to_map.3proc
-link path=usr/share/man/man3proc/Plookup_by_name.3proc target=Plookup_by_addr.3proc
+link path=usr/share/man/man3proc/Plookup_by_name.3proc \
+    target=Plookup_by_addr.3proc
 link path=usr/share/man/man3proc/Plwp_alt_stack.3proc target=Plwp_stack.3proc
-link path=usr/share/man/man3proc/Plwp_getfpregs.3proc target=Plwp_getregs.3proc
+link path=usr/share/man/man3proc/Plwp_getfpregs.3proc \
+    target=Plwp_getregs.3proc
 link path=usr/share/man/man3proc/Plwp_iter_all.3proc target=Plwp_iter.3proc
 link path=usr/share/man/man3proc/Plwp_main_stack.3proc target=Plwp_stack.3proc
 link path=usr/share/man/man3proc/Plwp_setasrs.3proc target=Plwp_getasrs.3proc
-link path=usr/share/man/man3proc/Plwp_setfpregs.3proc target=Plwp_getregs.3proc
+link path=usr/share/man/man3proc/Plwp_setfpregs.3proc \
+    target=Plwp_getregs.3proc
 link path=usr/share/man/man3proc/Plwp_setregs.3proc target=Plwp_getregs.3proc
-link path=usr/share/man/man3proc/Plwp_setxregs.3proc target=Plwp_getxregs.3proc
-link path=usr/share/man/man3proc/Pmapping_iter_resolved.3proc target=Pmapping_iter.3proc
+link path=usr/share/man/man3proc/Plwp_setxregs.3proc \
+    target=Plwp_getxregs.3proc
+link path=usr/share/man/man3proc/Pmapping_iter_resolved.3proc \
+    target=Pmapping_iter.3proc
 link path=usr/share/man/man3proc/Pname_to_ctf.3proc target=Paddr_to_ctf.3proc
-link path=usr/share/man/man3proc/Pname_to_loadobj.3proc target=Paddr_to_loadobj.3proc
+link path=usr/share/man/man3proc/Pname_to_loadobj.3proc \
+    target=Paddr_to_loadobj.3proc
 link path=usr/share/man/man3proc/Pname_to_map.3proc target=Paddr_to_map.3proc
-link path=usr/share/man/man3proc/Pobject_iter_resolved.3proc target=Pmapping_iter.3proc
 link path=usr/share/man/man3proc/Pobject_iter.3proc target=Pmapping_iter.3proc
+link path=usr/share/man/man3proc/Pobject_iter_resolved.3proc \
+    target=Pmapping_iter.3proc
 link path=usr/share/man/man3proc/Pobjname_resolved.3proc target=Pobjname.3proc
 link path=usr/share/man/man3proc/Ppriv_free.3proc target=Ppriv.3proc
 link path=usr/share/man/man3proc/Pputareg.3proc target=Pgetareg.3proc
-link path=usr/share/man/man3proc/pr_fstat.3proc target=pr_stat.3proc
-link path=usr/share/man/man3proc/pr_fstat64.3proc target=pr_stat.3proc
-link path=usr/share/man/man3proc/pr_getrlimit64.3proc target=pr_getrlimit.3proc
-link path=usr/share/man/man3proc/pr_lstat.3proc target=pr_stat.3proc
-link path=usr/share/man/man3proc/pr_lstat64.3proc target=pr_stat.3proc
-link path=usr/share/man/man3proc/pr_setrlimit64.3proc target=pr_setrlimit.3proc
-link path=usr/share/man/man3proc/pr_stat64.3proc target=pr_stat.3proc
 link path=usr/share/man/man3proc/Pread_string.3proc target=Pread.3proc
-link path=usr/share/man/man3proc/proc_arg_xgrab.3proc target=proc_arg_grab.3proc
-link path=usr/share/man/man3proc/proc_arg_xpsinfo.3proc target=proc_arg_psinfo.3proc
-link path=usr/share/man/man3proc/proc_dmodelname.3proc target=proc_fltname.3proc
-link path=usr/share/man/man3proc/proc_finistdio.3proc target=proc_initstdio.3proc
-link path=usr/share/man/man3proc/proc_flushstdio.3proc target=proc_initstdio.3proc
-link path=usr/share/man/man3proc/proc_free_priv.3proc target=proc_get_priv.3proc
-link path=usr/share/man/man3proc/proc_get_ldt.3proc target=Pldt.3proc
-link path=usr/share/man/man3proc/proc_lwp_range_valid.3proc target=proc_lwp_in_set.3proc
-link path=usr/share/man/man3proc/proc_signame.3proc target=proc_fltname.3proc
-link path=usr/share/man/man3proc/proc_sigset2str.3proc target=proc_fltset2str.3proc
-link path=usr/share/man/man3proc/proc_str2content.3proc target=proc_content2str.3proc
-link path=usr/share/man/man3proc/proc_str2sig.3proc target=proc_str2flt.3proc
-link path=usr/share/man/man3proc/proc_str2sigset.3proc target=proc_str2fltset.3proc
-link path=usr/share/man/man3proc/proc_str2sys.3proc target=proc_str2flt.3proc
-link path=usr/share/man/man3proc/proc_str2sysset.3proc target=proc_str2fltset.3proc
-link path=usr/share/man/man3proc/proc_sysname.3proc target=proc_fltname.3proc
-link path=usr/share/man/man3proc/proc_sysset2str.3proc target=proc_fltset2str.3proc
 link path=usr/share/man/man3proc/Psetsysexit.3proc target=Psetsysentry.3proc
 link path=usr/share/man/man3proc/Pstop.3proc target=Pstopstatus.3proc
-link path=usr/share/man/man3proc/Psymbol_iter_by_addr.3proc target=Psymbol_iter.3proc
-link path=usr/share/man/man3proc/Psymbol_iter_by_lmid.3proc target=Psymbol_iter.3proc
-link path=usr/share/man/man3proc/Psymbol_iter_by_name.3proc target=Psymbol_iter.3proc
+link path=usr/share/man/man3proc/Psymbol_iter_by_addr.3proc \
+    target=Psymbol_iter.3proc
+link path=usr/share/man/man3proc/Psymbol_iter_by_lmid.3proc \
+    target=Psymbol_iter.3proc
+link path=usr/share/man/man3proc/Psymbol_iter_by_name.3proc \
+    target=Psymbol_iter.3proc
 link path=usr/share/man/man3proc/Psysexit.3proc target=Psysentry.3proc
 link path=usr/share/man/man3proc/Punsetflags.3proc target=Psetflags.3proc
 link path=usr/share/man/man3proc/Pwait.3proc target=Pstopstatus.3proc
 link path=usr/share/man/man3proc/Pxcreate.3proc target=Pcreate.3proc
 link path=usr/share/man/man3proc/Pxecwapt.3proc target=Pxecbkpt.3proc
-link path=usr/share/man/man3proc/Pxlookup_by_addr_resolved.3proc target=Plookup_by_addr.3proc
-link path=usr/share/man/man3proc/Pxlookup_by_addr.3proc target=Plookup_by_addr.3proc
-link path=usr/share/man/man3proc/Pxlookup_by_name.3proc target=Plookup_by_addr.3proc
+link path=usr/share/man/man3proc/Pxlookup_by_addr.3proc \
+    target=Plookup_by_addr.3proc
+link path=usr/share/man/man3proc/Pxlookup_by_addr_resolved.3proc \
+    target=Plookup_by_addr.3proc
+link path=usr/share/man/man3proc/Pxlookup_by_name.3proc \
+    target=Plookup_by_addr.3proc
 link path=usr/share/man/man3proc/Pxsymbol_iter.3proc target=Psymbol_iter.3proc
 link path=usr/share/man/man3proc/Pzonepath.3proc target=Pzonename.3proc
 link path=usr/share/man/man3proc/Pzoneroot.3proc target=Pzonename.3proc
+link path=usr/share/man/man3proc/pr_fstat.3proc target=pr_stat.3proc
+link path=usr/share/man/man3proc/pr_fstat64.3proc target=pr_stat.3proc
+link path=usr/share/man/man3proc/pr_getrlimit64.3proc \
+    target=pr_getrlimit.3proc
+link path=usr/share/man/man3proc/pr_lstat.3proc target=pr_stat.3proc
+link path=usr/share/man/man3proc/pr_lstat64.3proc target=pr_stat.3proc
+link path=usr/share/man/man3proc/pr_setrlimit64.3proc \
+    target=pr_setrlimit.3proc
+link path=usr/share/man/man3proc/pr_stat64.3proc target=pr_stat.3proc
+link path=usr/share/man/man3proc/proc_arg_xgrab.3proc \
+    target=proc_arg_grab.3proc
+link path=usr/share/man/man3proc/proc_arg_xpsinfo.3proc \
+    target=proc_arg_psinfo.3proc
+link path=usr/share/man/man3proc/proc_dmodelname.3proc \
+    target=proc_fltname.3proc
+link path=usr/share/man/man3proc/proc_fdinfo_free.3proc \
+    target=proc_get_fdinfo.3proc
+link path=usr/share/man/man3proc/proc_finistdio.3proc \
+    target=proc_initstdio.3proc
+link path=usr/share/man/man3proc/proc_flushstdio.3proc \
+    target=proc_initstdio.3proc
+link path=usr/share/man/man3proc/proc_free_priv.3proc \
+    target=proc_get_priv.3proc
+link path=usr/share/man/man3proc/proc_get_ldt.3proc target=Pldt.3proc
+link path=usr/share/man/man3proc/proc_lwp_range_valid.3proc \
+    target=proc_lwp_in_set.3proc
+link path=usr/share/man/man3proc/proc_signame.3proc target=proc_fltname.3proc
+link path=usr/share/man/man3proc/proc_sigset2str.3proc \
+    target=proc_fltset2str.3proc
+link path=usr/share/man/man3proc/proc_str2content.3proc \
+    target=proc_content2str.3proc
+link path=usr/share/man/man3proc/proc_str2sig.3proc target=proc_str2flt.3proc
+link path=usr/share/man/man3proc/proc_str2sigset.3proc \
+    target=proc_str2fltset.3proc
+link path=usr/share/man/man3proc/proc_str2sys.3proc target=proc_str2flt.3proc
+link path=usr/share/man/man3proc/proc_str2sysset.3proc \
+    target=proc_str2fltset.3proc
+link path=usr/share/man/man3proc/proc_sysname.3proc target=proc_fltname.3proc
+link path=usr/share/man/man3proc/proc_sysset2str.3proc \
+    target=proc_fltset2str.3proc
+link path=usr/share/man/man3proc/ps_kill.3proc target=ps_pstop.3proc
+link path=usr/share/man/man3proc/ps_lcontinue.3proc target=ps_pstop.3proc
+link path=usr/share/man/man3proc/ps_lgetfpregs.3proc target=ps_lgetregs.3proc
+link path=usr/share/man/man3proc/ps_lgetxregs.3proc target=ps_lgetregs.3proc
+link path=usr/share/man/man3proc/ps_lgetxregsize.3proc \
+    target=ps_lgetregs.3proc
+link path=usr/share/man/man3proc/ps_lrolltoaddr.3proc target=ps_pstop.3proc
+link path=usr/share/man/man3proc/ps_lsetfpregs.3proc target=ps_lgetregs.3proc
+link path=usr/share/man/man3proc/ps_lsetregs.3proc target=ps_lgetregs.3proc
+link path=usr/share/man/man3proc/ps_lsetxregs.3proc target=ps_lgetregs.3proc
+link path=usr/share/man/man3proc/ps_lstop.3proc target=ps_pstop.3proc
+link path=usr/share/man/man3proc/ps_pcontinue.3proc target=ps_pstop.3proc
+link path=usr/share/man/man3proc/ps_pdread.3proc target=ps_pread.3proc
+link path=usr/share/man/man3proc/ps_pdwrite.3proc target=ps_pread.3proc
+link path=usr/share/man/man3proc/ps_pglobal_sym.3proc \
+    target=ps_pglobal_lookup.3proc
+link path=usr/share/man/man3proc/ps_ptread.3proc target=ps_pread.3proc
+link path=usr/share/man/man3proc/ps_ptwrite.3proc target=ps_pread.3proc
+link path=usr/share/man/man3proc/ps_pwrite.3proc target=ps_pread.3proc
--- a/usr/src/uts/common/exec/elf/elf_notes.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/uts/common/exec/elf/elf_notes.c	Tue Nov 19 11:04:46 2019 +0000
@@ -27,6 +27,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <sys/types.h>
@@ -108,7 +109,7 @@
 	    + (nlwp + nzomb) * roundup(sizeof (lwpsinfo_t), sizeof (Word))
 	    + nlwp * roundup(sizeof (lwpstatus_t), sizeof (Word))
 	    + nlwp * roundup(sizeof (prlwpname_t), sizeof (Word))
-	    + nfd * roundup(sizeof (prfdinfo_t), sizeof (Word));
+	    + nfd * roundup(sizeof (prfdinfo_core_t), sizeof (Word));
 
 	if (curproc->p_agenttp != NULL) {
 		v[0].p_filesz += sizeof (Note) +
@@ -350,7 +351,7 @@
 		vnode_t *fvp;
 		struct file *fp;
 		vattr_t vattr;
-		prfdinfo_t fdinfo;
+		prfdinfo_core_t fdinfo;
 
 		bzero(&fdinfo, sizeof (fdinfo));
 
--- a/usr/src/uts/common/fs/proc/prdata.h	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/uts/common/fs/proc/prdata.h	Tue Nov 19 11:04:46 2019 +0000
@@ -28,6 +28,7 @@
 
 /*
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef _SYS_PROC_PRDATA_H
@@ -131,6 +132,8 @@
 	PR_ROOTDIR,		/* /proc/<pid>/root			*/
 	PR_FDDIR,		/* /proc/<pid>/fd			*/
 	PR_FD,			/* /proc/<pid>/fd/nn			*/
+	PR_FDINFODIR,		/* /proc/<pid>/fdinfo			*/
+	PR_FDINFO,		/* /proc/<pid>/fdinfo/nn		*/
 	PR_OBJECTDIR,		/* /proc/<pid>/object			*/
 	PR_OBJECT,		/* /proc/<pid>/object/xxx		*/
 	PR_LWPDIR,		/* /proc/<pid>/lwp			*/
@@ -288,7 +291,7 @@
  *
  *	pr_iol_initlist(&listhead, sizeof (*mp), n);
  *	while (whatever) {
- *		mp = pr_iol_newbuf(&listhead, sizeof (*mp);
+ *		mp = pr_iol_newbuf(&listhead, sizeof (*mp));
  *		...
  *		error = ...
  *	}
@@ -313,6 +316,7 @@
 extern	void *	pr_iol_newbuf(list_t *head, size_t itemsize);
 extern	int	pr_iol_copyout_and_free(list_t *head, caddr_t *tgt, int errin);
 extern	int	pr_iol_uiomove_and_free(list_t *head, uio_t *uiop, int errin);
+extern	void	pr_iol_freelist(list_t *);
 
 #if defined(_SYSCALL32_IMPL)
 
@@ -347,6 +351,8 @@
 extern	int	pr_set(proc_t *, long);
 extern	int	pr_unset(proc_t *, long);
 extern	void	pr_sethold(prnode_t *, sigset_t *);
+extern	file_t	*pr_getf(proc_t *, uint_t, short *);
+extern	void	pr_releasef(proc_t *, uint_t);
 extern	void	pr_setfault(proc_t *, fltset_t *);
 extern	int	prusrio(proc_t *, enum uio_rw, struct uio *, int);
 extern	int	prwritectl(vnode_t *, struct uio *, cred_t *);
--- a/usr/src/uts/common/fs/proc/prsubr.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/uts/common/fs/proc/prsubr.c	Tue Nov 19 11:04:46 2019 +0000
@@ -22,10 +22,11 @@
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
-/*	  All Rights Reserved  	*/
+/*	  All Rights Reserved	*/
 
 #include <sys/types.h>
 #include <sys/t_lock.h>
@@ -65,6 +66,18 @@
 #include <sys/copyops.h>
 #include <sys/time.h>
 #include <sys/msacct.h>
+#include <sys/flock_impl.h>
+#include <sys/stropts.h>
+#include <sys/strsubr.h>
+#include <sys/pathname.h>
+#include <sys/mode.h>
+#include <sys/socketvar.h>
+#include <sys/autoconf.h>
+#include <sys/dtrace.h>
+#include <sys/timod.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <inet/cc.h>
 #include <vm/as.h>
 #include <vm/rm.h>
 #include <vm/seg.h>
@@ -1454,6 +1467,57 @@
 	return (len);
 }
 
+file_t *
+pr_getf(proc_t *p, uint_t fd, short *flag)
+{
+	uf_entry_t *ufp;
+	uf_info_t *fip;
+	file_t *fp;
+
+	ASSERT(MUTEX_HELD(&p->p_lock) && (p->p_proc_flag & P_PR_LOCK));
+
+	fip = P_FINFO(p);
+
+	if (fd >= fip->fi_nfiles)
+		return (NULL);
+
+	mutex_exit(&p->p_lock);
+	mutex_enter(&fip->fi_lock);
+	UF_ENTER(ufp, fip, fd);
+	if ((fp = ufp->uf_file) != NULL && fp->f_count > 0) {
+		if (flag != NULL)
+			*flag = ufp->uf_flag;
+		ufp->uf_refcnt++;
+	} else {
+		fp = NULL;
+	}
+	UF_EXIT(ufp);
+	mutex_exit(&fip->fi_lock);
+	mutex_enter(&p->p_lock);
+
+	return (fp);
+}
+
+void
+pr_releasef(proc_t *p, uint_t fd)
+{
+	uf_entry_t *ufp;
+	uf_info_t *fip;
+
+	ASSERT(MUTEX_HELD(&p->p_lock) && (p->p_proc_flag & P_PR_LOCK));
+
+	fip = P_FINFO(p);
+
+	mutex_exit(&p->p_lock);
+	mutex_enter(&fip->fi_lock);
+	UF_ENTER(ufp, fip, fd);
+	ASSERT3U(ufp->uf_refcnt, >, 0);
+	ufp->uf_refcnt--;
+	UF_EXIT(ufp);
+	mutex_exit(&fip->fi_lock);
+	mutex_enter(&p->p_lock);
+}
+
 void
 pr_object_name(char *name, vnode_t *vp, struct vattr *vattr)
 {
@@ -1563,6 +1627,18 @@
 	return (new);
 }
 
+void
+pr_iol_freelist(list_t *iolhead)
+{
+	piol_t	*iol;
+
+	while ((iol = list_head(iolhead)) != NULL) {
+		list_remove(iolhead, iol);
+		kmem_free(iol, iol->piol_size);
+	}
+	list_destroy(iolhead);
+}
+
 int
 pr_iol_copyout_and_free(list_t *iolhead, caddr_t *tgt, int errin)
 {
@@ -2375,6 +2451,481 @@
 	}
 }
 
+static size_t
+prfdinfomisc(list_t *data, uint_t type, const void *val, size_t vlen)
+{
+	pr_misc_header_t *misc;
+	size_t len;
+
+	len = PRFDINFO_ROUNDUP(sizeof (*misc) + vlen);
+
+	if (data != NULL) {
+		misc = pr_iol_newbuf(data, len);
+		misc->pr_misc_type = type;
+		misc->pr_misc_size = len;
+		misc++;
+		bcopy((char *)val, (char *)misc, vlen);
+	}
+
+	return (len);
+}
+
+/*
+ * There's no elegant way to determine if a character device
+ * supports TLI, so just check a hardcoded list of known TLI
+ * devices.
+ */
+
+static boolean_t
+pristli(vnode_t *vp)
+{
+	static const char *tlidevs[] = {
+	    "udp", "udp6", "tcp", "tcp6"
+	};
+	char *devname;
+	uint_t i;
+
+	ASSERT(vp != NULL);
+
+	if (vp->v_type != VCHR || vp->v_stream == NULL || vp->v_rdev == 0)
+		return (B_FALSE);
+
+	if ((devname = mod_major_to_name(getmajor(vp->v_rdev))) == NULL)
+		return (B_FALSE);
+
+	for (i = 0; i < ARRAY_SIZE(tlidevs); i++) {
+		if (strcmp(devname, tlidevs[i]) == 0)
+			return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
+static size_t
+prfdinfopath(proc_t *p, vnode_t *vp, list_t *data, cred_t *cred)
+{
+	char *pathname;
+	vnode_t *vrootp;
+	size_t pathlen;
+	size_t sz = 0;
+
+	pathlen = MAXPATHLEN + 1;
+	pathname = kmem_alloc(pathlen, KM_SLEEP);
+
+	mutex_enter(&p->p_lock);
+	if ((vrootp = PTOU(p)->u_rdir) == NULL)
+		vrootp = rootdir;
+	VN_HOLD(vrootp);
+	mutex_exit(&p->p_lock);
+
+	if (vnodetopath(vrootp, vp, pathname, pathlen, cred) == 0) {
+		sz += prfdinfomisc(data, PR_PATHNAME,
+		    pathname, strlen(pathname) + 1);
+	}
+	VN_RELE(vrootp);
+
+	kmem_free(pathname, pathlen);
+	return (sz);
+}
+
+static size_t
+prfdinfotlisockopt(vnode_t *vp, list_t *data, cred_t *cred)
+{
+	strcmd_t strcmd;
+	int32_t rval;
+	size_t sz = 0;
+
+	strcmd.sc_cmd = TI_GETMYNAME;
+	strcmd.sc_timeout = 1;
+	strcmd.sc_len = STRCMDBUFSIZE;
+
+	if (VOP_IOCTL(vp, _I_CMD, (intptr_t)&strcmd, FKIOCTL, cred,
+	    &rval, NULL) == 0 && strcmd.sc_len > 0) {
+		sz += prfdinfomisc(data, PR_SOCKETNAME, strcmd.sc_buf,
+		    strcmd.sc_len);
+	}
+
+	strcmd.sc_cmd = TI_GETPEERNAME;
+	strcmd.sc_timeout = 1;
+	strcmd.sc_len = STRCMDBUFSIZE;
+
+	if (VOP_IOCTL(vp, _I_CMD, (intptr_t)&strcmd, FKIOCTL, cred,
+	    &rval, NULL) == 0 && strcmd.sc_len > 0) {
+		sz += prfdinfomisc(data, PR_PEERSOCKNAME, strcmd.sc_buf,
+		    strcmd.sc_len);
+	}
+
+	return (sz);
+}
+
+static size_t
+prfdinfosockopt(vnode_t *vp, list_t *data, cred_t *cred)
+{
+	sonode_t *so;
+	socklen_t vlen;
+	size_t sz = 0;
+	uint_t i;
+
+	if (vp->v_stream != NULL) {
+		so = VTOSO(vp->v_stream->sd_vnode);
+
+		if (so->so_version == SOV_STREAM)
+			so = NULL;
+	} else {
+		so = VTOSO(vp);
+	}
+
+	if (so == NULL)
+		return (0);
+
+	DTRACE_PROBE1(sonode, sonode_t *, so);
+
+	/* prmisc - PR_SOCKETNAME */
+
+	struct sockaddr_storage buf;
+	struct sockaddr *name = (struct sockaddr *)&buf;
+
+	vlen = sizeof (buf);
+	if (SOP_GETSOCKNAME(so, name, &vlen, cred) == 0 && vlen > 0)
+		sz += prfdinfomisc(data, PR_SOCKETNAME, name, vlen);
+
+	/* prmisc - PR_PEERSOCKNAME */
+
+	vlen = sizeof (buf);
+	if (SOP_GETPEERNAME(so, name, &vlen, B_FALSE, cred) == 0 && vlen > 0)
+		sz += prfdinfomisc(data, PR_PEERSOCKNAME, name, vlen);
+
+	/* prmisc - PR_SOCKOPTS_BOOL_OPTS */
+
+	static struct boolopt {
+		int		level;
+		int		opt;
+		int		bopt;
+	} boolopts[] = {
+		{ SOL_SOCKET, SO_DEBUG,		PR_SO_DEBUG },
+		{ SOL_SOCKET, SO_REUSEADDR,	PR_SO_REUSEADDR },
+#ifdef SO_REUSEPORT
+		/* SmartOS and OmniOS have SO_REUSEPORT */
+		{ SOL_SOCKET, SO_REUSEPORT,	PR_SO_REUSEPORT },
+#endif
+		{ SOL_SOCKET, SO_KEEPALIVE,	PR_SO_KEEPALIVE },
+		{ SOL_SOCKET, SO_DONTROUTE,	PR_SO_DONTROUTE },
+		{ SOL_SOCKET, SO_BROADCAST,	PR_SO_BROADCAST },
+		{ SOL_SOCKET, SO_OOBINLINE,	PR_SO_OOBINLINE },
+		{ SOL_SOCKET, SO_DGRAM_ERRIND,	PR_SO_DGRAM_ERRIND },
+		{ SOL_SOCKET, SO_ALLZONES,	PR_SO_ALLZONES },
+		{ SOL_SOCKET, SO_MAC_EXEMPT,	PR_SO_MAC_EXEMPT },
+		{ SOL_SOCKET, SO_MAC_IMPLICIT,	PR_SO_MAC_IMPLICIT },
+		{ SOL_SOCKET, SO_EXCLBIND,	PR_SO_EXCLBIND },
+		{ SOL_SOCKET, SO_VRRP,		PR_SO_VRRP },
+		{ IPPROTO_UDP, UDP_NAT_T_ENDPOINT,
+		    PR_UDP_NAT_T_ENDPOINT }
+	};
+	prsockopts_bool_opts_t opts;
+	int val;
+
+	if (data != NULL) {
+		opts.prsock_bool_opts = 0;
+
+		for (i = 0; i < ARRAY_SIZE(boolopts); i++) {
+			vlen = sizeof (val);
+			if (SOP_GETSOCKOPT(so, boolopts[i].level,
+			    boolopts[i].opt, &val, &vlen, 0, cred) == 0 &&
+			    val != 0) {
+				opts.prsock_bool_opts |= boolopts[i].bopt;
+			}
+		}
+	}
+
+	sz += prfdinfomisc(data, PR_SOCKOPTS_BOOL_OPTS, &opts, sizeof (opts));
+
+	/* prmisc - PR_SOCKOPT_LINGER */
+
+	struct linger l;
+
+	vlen = sizeof (l);
+	if (SOP_GETSOCKOPT(so, SOL_SOCKET, SO_LINGER, &l, &vlen,
+	    0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_LINGER, &l, vlen);
+	}
+
+	/* prmisc - PR_SOCKOPT_* int types */
+
+	static struct sopt {
+		int		level;
+		int		opt;
+		int		bopt;
+	} sopts[] = {
+		{ SOL_SOCKET, SO_TYPE,		PR_SOCKOPT_TYPE },
+		{ SOL_SOCKET, SO_SNDBUF,	PR_SOCKOPT_SNDBUF },
+		{ SOL_SOCKET, SO_RCVBUF,	PR_SOCKOPT_RCVBUF }
+	};
+
+	for (i = 0; i < ARRAY_SIZE(sopts); i++) {
+		vlen = sizeof (val);
+		if (SOP_GETSOCKOPT(so, sopts[i].level, sopts[i].opt,
+		    &val, &vlen, 0, cred) == 0 && vlen > 0) {
+			sz += prfdinfomisc(data, sopts[i].bopt, &val, vlen);
+		}
+	}
+
+	/* prmisc - PR_SOCKOPT_IP_NEXTHOP */
+
+	in_addr_t nexthop_val;
+
+	vlen = sizeof (nexthop_val);
+	if (SOP_GETSOCKOPT(so, IPPROTO_IP, IP_NEXTHOP,
+	    &nexthop_val, &vlen, 0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_IP_NEXTHOP,
+		    &nexthop_val, vlen);
+	}
+
+	/* prmisc - PR_SOCKOPT_IPV6_NEXTHOP */
+
+	struct sockaddr_in6 nexthop6_val;
+
+	vlen = sizeof (nexthop6_val);
+	if (SOP_GETSOCKOPT(so, IPPROTO_IPV6, IPV6_NEXTHOP,
+	    &nexthop6_val, &vlen, 0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_IPV6_NEXTHOP,
+		    &nexthop6_val, vlen);
+	}
+
+	/* prmisc - PR_SOCKOPT_TCP_CONGESTION */
+
+	char cong[CC_ALGO_NAME_MAX];
+
+	vlen = sizeof (cong);
+	if (SOP_GETSOCKOPT(so, IPPROTO_TCP, TCP_CONGESTION,
+	    &cong, &vlen, 0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_TCP_CONGESTION, cong, vlen);
+	}
+
+	/* prmisc - PR_SOCKFILTERS_PRIV */
+
+	struct fil_info fi;
+
+	vlen = sizeof (fi);
+	if (SOP_GETSOCKOPT(so, SOL_FILTER, FIL_LIST,
+	    &fi, &vlen, 0, cred) == 0 && vlen != 0) {
+		pr_misc_header_t *misc;
+		size_t len;
+
+		/*
+		 * We limit the number of returned filters to 32.
+		 * This is the maximum number that pfiles will print
+		 * anyway.
+		 */
+		vlen = MIN(32, fi.fi_pos + 1);
+		vlen *= sizeof (fi);
+
+		len = PRFDINFO_ROUNDUP(sizeof (*misc) + vlen);
+		sz += len;
+
+		if (data != NULL) {
+			/*
+			 * So that the filter list can be built incrementally,
+			 * prfdinfomisc() is not used here. Instead we
+			 * allocate a buffer directly on the copyout list using
+			 * pr_iol_newbuf()
+			 */
+			misc = pr_iol_newbuf(data, len);
+			misc->pr_misc_type = PR_SOCKFILTERS_PRIV;
+			misc->pr_misc_size = len;
+			misc++;
+			len = vlen;
+			if (SOP_GETSOCKOPT(so, SOL_FILTER, FIL_LIST,
+			    misc, &vlen, 0, cred) == 0) {
+				/*
+				 * In case the number of filters has reduced
+				 * since the first call, explicitly zero out
+				 * any unpopulated space.
+				 */
+				if (vlen < len)
+					bzero(misc + vlen, len - vlen);
+			} else {
+				/* Something went wrong, zero out the result */
+				bzero(misc, vlen);
+			}
+		}
+	}
+
+	return (sz);
+}
+
+u_offset_t
+prgetfdinfosize(proc_t *p, vnode_t *vp, cred_t *cred)
+{
+	u_offset_t sz;
+
+	/*
+	 * All fdinfo files will be at least this big -
+	 * sizeof fdinfo struct + zero length trailer
+	 */
+	sz = offsetof(prfdinfo_t, pr_misc) + sizeof (pr_misc_header_t);
+
+	/* Pathname */
+	if (vp->v_type != VSOCK && vp->v_type != VDOOR)
+		sz += prfdinfopath(p, vp, NULL, cred);
+
+	/* Socket options */
+	if (vp->v_type == VSOCK)
+		sz += prfdinfosockopt(vp, NULL, cred);
+
+	/* TLI/XTI sockets */
+	if (pristli(vp))
+		sz += prfdinfotlisockopt(vp, NULL, cred);
+
+	return (sz);
+}
+
+int
+prgetfdinfo(proc_t *p, vnode_t *vp, prfdinfo_t *fdinfo, cred_t *cred,
+    list_t *data)
+{
+	vattr_t vattr;
+	int error;
+
+	/*
+	 * The buffer has been initialised to zero by pr_iol_newbuf().
+	 * Initialise defaults for any values that should not default to zero.
+	 */
+	fdinfo->pr_uid = (uid_t)-1;
+	fdinfo->pr_gid = (gid_t)-1;
+	fdinfo->pr_size = -1;
+	fdinfo->pr_locktype = F_UNLCK;
+	fdinfo->pr_lockpid = -1;
+	fdinfo->pr_locksysid = -1;
+	fdinfo->pr_peerpid = -1;
+
+	/* Offset */
+
+	/*
+	 * pr_offset has already been set from the underlying file_t.
+	 * Check if it is plausible and reset to -1 if not.
+	 */
+	if (fdinfo->pr_offset != -1 &&
+	    VOP_SEEK(vp, 0, (offset_t *)&fdinfo->pr_offset, NULL) != 0)
+		fdinfo->pr_offset = -1;
+
+	/* Attributes */
+	vattr.va_mask = AT_STAT;
+	if (VOP_GETATTR(vp, &vattr, 0, cred, NULL) == 0) {
+		fdinfo->pr_major = getmajor(vattr.va_fsid);
+		fdinfo->pr_minor = getminor(vattr.va_fsid);
+		fdinfo->pr_rmajor = getmajor(vattr.va_rdev);
+		fdinfo->pr_rminor = getminor(vattr.va_rdev);
+		fdinfo->pr_ino = (ino64_t)vattr.va_nodeid;
+		fdinfo->pr_size = (off64_t)vattr.va_size;
+		fdinfo->pr_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
+		fdinfo->pr_uid = vattr.va_uid;
+		fdinfo->pr_gid = vattr.va_gid;
+		if (vp->v_type == VSOCK)
+			fdinfo->pr_fileflags |= sock_getfasync(vp);
+	}
+
+	/* locks */
+
+	flock64_t bf;
+
+	bzero(&bf, sizeof (bf));
+	bf.l_type = F_WRLCK;
+
+	if (VOP_FRLOCK(vp, F_GETLK, &bf,
+	    (uint16_t)(fdinfo->pr_fileflags & 0xffff), 0, NULL,
+	    cred, NULL) == 0 && bf.l_type != F_UNLCK) {
+		fdinfo->pr_locktype = bf.l_type;
+		fdinfo->pr_lockpid = bf.l_pid;
+		fdinfo->pr_locksysid = bf.l_sysid;
+	}
+
+	/* peer cred */
+
+	k_peercred_t kpc;
+
+	switch (vp->v_type) {
+	case VFIFO:
+	case VSOCK: {
+		int32_t rval;
+
+		error = VOP_IOCTL(vp, _I_GETPEERCRED, (intptr_t)&kpc,
+		    FKIOCTL, cred, &rval, NULL);
+		break;
+	}
+	case VCHR: {
+		struct strioctl strioc;
+		int32_t rval;
+
+		if (vp->v_stream == NULL) {
+			error = ENOTSUP;
+			break;
+		}
+		strioc.ic_cmd = _I_GETPEERCRED;
+		strioc.ic_timout = INFTIM;
+		strioc.ic_len = (int)sizeof (k_peercred_t);
+		strioc.ic_dp = (char *)&kpc;
+
+		error = strdoioctl(vp->v_stream, &strioc, FNATIVE | FKIOCTL,
+		    STR_NOSIG | K_TO_K, cred, &rval);
+		break;
+	}
+	default:
+		error = ENOTSUP;
+		break;
+	}
+
+	if (error == 0 && kpc.pc_cr != NULL) {
+		proc_t *peerp;
+
+		fdinfo->pr_peerpid = kpc.pc_cpid;
+
+		crfree(kpc.pc_cr);
+
+		mutex_enter(&pidlock);
+		if ((peerp = prfind(fdinfo->pr_peerpid)) != NULL) {
+			user_t *up;
+
+			mutex_enter(&peerp->p_lock);
+			mutex_exit(&pidlock);
+
+			up = PTOU(peerp);
+			bcopy(up->u_comm, fdinfo->pr_peername,
+			    MIN(sizeof (up->u_comm),
+			    sizeof (fdinfo->pr_peername) - 1));
+
+			mutex_exit(&peerp->p_lock);
+		} else {
+			mutex_exit(&pidlock);
+		}
+	}
+
+	/*
+	 * Don't attempt to determine the vnode path for a socket or a door
+	 * as it will cause a linear scan of the dnlc table given there is no
+	 * v_path associated with the vnode.
+	 */
+	if (vp->v_type != VSOCK && vp->v_type != VDOOR)
+		(void) prfdinfopath(p, vp, data, cred);
+
+	if (vp->v_type == VSOCK)
+		(void) prfdinfosockopt(vp, data, cred);
+
+	/* TLI/XTI stream sockets */
+	if (pristli(vp))
+		(void) prfdinfotlisockopt(vp, data, cred);
+
+	/*
+	 * Add a terminating header with a zero size.
+	 */
+	pr_misc_header_t *misc;
+
+	misc = pr_iol_newbuf(data, sizeof (*misc));
+	misc->pr_misc_size = 0;
+	misc->pr_misc_type = (uint_t)-1;
+
+	return (0);
+}
+
 #ifdef _SYSCALL32_IMPL
 void
 prgetpsinfo32(proc_t *p, psinfo32_t *psp)
@@ -2670,7 +3221,7 @@
 #define	PR_COPY_TIMESPEC(s, d, field)				\
 	TIMESPEC_TO_TIMESPEC32(&d->field, &s->field);
 
-#define	PR_COPY_BUF(s, d, field)	 			\
+#define	PR_COPY_BUF(s, d, field)				\
 	bcopy(s->field, d->field, sizeof (d->field));
 
 #define	PR_IGNORE_FIELD(s, d, field)
--- a/usr/src/uts/common/fs/proc/prvnops.c	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/uts/common/fs/proc/prvnops.c	Tue Nov 19 11:04:46 2019 +0000
@@ -23,6 +23,7 @@
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018, Joyent, Inc.
  * Copyright (c) 2017 by Delphix. All rights reserved.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*	Copyright (c) 1984,	 1986, 1987, 1988, 1989 AT&T	*/
@@ -154,20 +155,22 @@
 		"root" },
 	{ PR_FDDIR,	21 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"fd" },
-	{ PR_OBJECTDIR,	22 * sizeof (prdirent_t), sizeof (prdirent_t),
+	{ PR_FDINFODIR,	22 * sizeof (prdirent_t), sizeof (prdirent_t),
+		"fdinfo" },
+	{ PR_OBJECTDIR,	23 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"object" },
-	{ PR_LWPDIR,	23 * sizeof (prdirent_t), sizeof (prdirent_t),
+	{ PR_LWPDIR,	24 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"lwp" },
-	{ PR_PRIV,	24 * sizeof (prdirent_t), sizeof (prdirent_t),
+	{ PR_PRIV,	25 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"priv" },
-	{ PR_PATHDIR,	25 * sizeof (prdirent_t), sizeof (prdirent_t),
+	{ PR_PATHDIR,	26 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"path" },
-	{ PR_CTDIR,	26 * sizeof (prdirent_t), sizeof (prdirent_t),
+	{ PR_CTDIR,	27 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"contracts" },
-	{ PR_SECFLAGS,	27 * sizeof (prdirent_t), sizeof (prdirent_t),
+	{ PR_SECFLAGS,	28 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"secflags" },
 #if defined(__x86)
-	{ PR_LDT,	28 * sizeof (prdirent_t), sizeof (prdirent_t),
+	{ PR_LDT,	29 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"ldt" },
 #endif
 };
@@ -596,7 +599,8 @@
 #if defined(__sparc)
 	pr_read_gwindows(), pr_read_asrs(),
 #endif
-	pr_read_piddir(), pr_read_pidfile(), pr_read_opagedata();
+	pr_read_piddir(), pr_read_pidfile(), pr_read_opagedata(),
+	pr_read_fdinfo();
 
 static int (*pr_read_function[PR_NFILES])() = {
 	pr_read_inval,		/* /proc				*/
@@ -625,6 +629,8 @@
 	pr_read_inval,		/* /proc/<pid>/root			*/
 	pr_read_inval,		/* /proc/<pid>/fd			*/
 	pr_read_inval,		/* /proc/<pid>/fd/nn			*/
+	pr_read_inval,		/* /proc/<pid>/fdinfo			*/
+	pr_read_fdinfo,		/* /proc/<pid>/fdinfo/nn		*/
 	pr_read_inval,		/* /proc/<pid>/object			*/
 	pr_read_inval,		/* /proc/<pid>/object/xxx		*/
 	pr_read_inval,		/* /proc/<pid>/lwp			*/
@@ -813,6 +819,95 @@
 }
 
 static int
+pr_read_fdinfo(prnode_t *pnp, uio_t *uiop)
+{
+	prfdinfo_t *fdinfo;
+	list_t data;
+	proc_t *p;
+	vnode_t *vp;
+	uint_t fd;
+	file_t *fp;
+	cred_t *cred;
+	short ufp_flag;
+	int error = 0;
+
+	ASSERT(pnp->pr_type == PR_FDINFO);
+
+	/*
+	 * This is a guess at the size of the structure that needs to
+	 * be returned. It's a balance between not allocating too much more
+	 * space than is required and not requiring too many subsequent
+	 * reallocations. Allocate it before acquiring the process lock.
+	 */
+	pr_iol_initlist(&data, sizeof (prfdinfo_t) + MAXPATHLEN + 2, 1);
+
+	if ((error = prlock(pnp, ZNO)) != 0) {
+		pr_iol_freelist(&data);
+		return (error);
+	}
+
+	p = pnp->pr_common->prc_proc;
+
+	if ((p->p_flag & SSYS) || p->p_as == &kas) {
+		prunlock(pnp);
+		pr_iol_freelist(&data);
+		return (0);
+	}
+
+	fd = pnp->pr_index;
+
+	/* Fetch and lock the file_t for this descriptor */
+	fp = pr_getf(p, fd, &ufp_flag);
+
+	if (fp == NULL) {
+		error = ENOENT;
+		prunlock(pnp);
+		goto out;
+	}
+
+	vp = fp->f_vnode;
+	VN_HOLD(vp);
+
+	/*
+	 * For fdinfo, we don't want to include the placeholder pr_misc at the
+	 * end of the struct. We'll terminate the data with an empty pr_misc
+	 * header before returning.
+	 */
+
+	fdinfo = pr_iol_newbuf(&data, offsetof(prfdinfo_t, pr_misc));
+	fdinfo->pr_fd = fd;
+	fdinfo->pr_fdflags = ufp_flag;
+	/* FEPOLLED on f_flag2 should never be user-visible */
+	fdinfo->pr_fileflags = (fp->f_flag2 & ~FEPOLLED) << 16 | fp->f_flag;
+	if ((fdinfo->pr_fileflags & (FSEARCH | FEXEC)) == 0)
+		fdinfo->pr_fileflags += FOPEN;
+	fdinfo->pr_offset = fp->f_offset;
+	cred = fp->f_cred;
+	crhold(cred);
+	/*
+	 * Information from the vnode (rather than the file_t) is retrieved
+	 * later, in prgetfdinfo() - for example sock_getfasync()
+	 */
+	pr_releasef(p, fd);
+
+	prunlock(pnp);
+
+	error = prgetfdinfo(p, vp, fdinfo, cred, &data);
+
+	crfree(cred);
+
+	VN_RELE(vp);
+
+out:
+	if (error == 0)
+		error = pr_iol_uiomove_and_free(&data, uiop, error);
+	else
+		pr_iol_freelist(&data);
+
+	return (error);
+}
+
+static int
 pr_read_lpsinfo(prnode_t *pnp, uio_t *uiop)
 {
 	proc_t *p;
@@ -1835,6 +1930,8 @@
 	pr_read_inval,		/* /proc/<pid>/root			*/
 	pr_read_inval,		/* /proc/<pid>/fd			*/
 	pr_read_inval,		/* /proc/<pid>/fd/nn			*/
+	pr_read_inval,		/* /proc/<pid>/fdinfo			*/
+	pr_read_fdinfo,		/* /proc/<pid>/fdinfo/nn		*/
 	pr_read_inval,		/* /proc/<pid>/object			*/
 	pr_read_inval,		/* /proc/<pid>/object/xxx		*/
 	pr_read_inval,		/* /proc/<pid>/lwp			*/
@@ -3080,9 +3177,29 @@
 		vap->va_size = 0;
 		break;
 	case PR_FDDIR:
+	case PR_FDINFODIR:
 		vap->va_nlink = 2;
 		vap->va_size = (P_FINFO(p)->fi_nfiles + 2) * PRSDSIZE;
 		break;
+	case PR_FDINFO: {
+		file_t *fp;
+		vnode_t *vp;
+		int fd = pnp->pr_index;
+
+		fp = pr_getf(p, fd, NULL);
+		if (fp == NULL) {
+			prunlock(pnp);
+			return (ENOENT);
+		}
+		vp = fp->f_vnode;
+		VN_HOLD(vp);
+		pr_releasef(p, fd);
+		prunlock(pnp);
+		vap->va_size = prgetfdinfosize(p, vp, cr);
+		VN_RELE(vp);
+		vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+		return (0);
+	}
 	case PR_LWPDIR:
 		/*
 		 * va_nlink: count each lwp as a directory link.
@@ -3407,8 +3524,8 @@
  */
 static vnode_t *pr_lookup_notdir(), *pr_lookup_procdir(), *pr_lookup_piddir(),
 	*pr_lookup_objectdir(), *pr_lookup_lwpdir(), *pr_lookup_lwpiddir(),
-	*pr_lookup_fddir(), *pr_lookup_pathdir(), *pr_lookup_tmpldir(),
-	*pr_lookup_ctdir();
+	*pr_lookup_fddir(), *pr_lookup_fdinfodir(), *pr_lookup_pathdir(),
+	*pr_lookup_tmpldir(), *pr_lookup_ctdir();
 
 static vnode_t *(*pr_lookup_function[PR_NFILES])() = {
 	pr_lookup_procdir,	/* /proc				*/
@@ -3437,6 +3554,8 @@
 	pr_lookup_notdir,	/* /proc/<pid>/root			*/
 	pr_lookup_fddir,	/* /proc/<pid>/fd			*/
 	pr_lookup_notdir,	/* /proc/<pid>/fd/nn			*/
+	pr_lookup_fdinfodir,	/* /proc/<pid>/fdinfo			*/
+	pr_lookup_notdir,	/* /proc/<pid>/fdinfo/nn		*/
 	pr_lookup_objectdir,	/* /proc/<pid>/object			*/
 	pr_lookup_notdir,	/* /proc/<pid>/object/xxx		*/
 	pr_lookup_lwpdir,	/* /proc/<pid>/lwp			*/
@@ -3524,7 +3643,8 @@
 		break;
 	}
 
-	if ((type == PR_OBJECTDIR || type == PR_FDDIR || type == PR_PATHDIR) &&
+	if ((type == PR_OBJECTDIR || type == PR_FDDIR ||
+	    type == PR_FDINFODIR || type == PR_PATHDIR) &&
 	    (error = praccess(dp, VEXEC, 0, cr, ct)) != 0)
 		return (error);
 
@@ -4138,8 +4258,6 @@
 	file_t *fp;
 	uint_t fd;
 	int c;
-	uf_entry_t *ufp;
-	uf_info_t *fip;
 
 	ASSERT(dpnp->pr_type == PR_FDDIR);
 
@@ -4149,8 +4267,8 @@
 		if (c < '0' || c > '9')
 			return (NULL);
 		ofd = fd;
-		fd = 10*fd + c - '0';
-		if (fd/10 != ofd)	/* integer overflow */
+		fd = 10 * fd + c - '0';
+		if (fd / 10 != ofd)	/* integer overflow */
 			return (NULL);
 	}
 
@@ -4167,44 +4285,93 @@
 		return (NULL);
 	}
 
-	fip = P_FINFO(p);
-	mutex_exit(&p->p_lock);
-	mutex_enter(&fip->fi_lock);
-	if (fd < fip->fi_nfiles) {
-		UF_ENTER(ufp, fip, fd);
-		if ((fp = ufp->uf_file) != NULL) {
-			pnp->pr_mode = 07111;
-			if (fp->f_flag & FREAD)
-				pnp->pr_mode |= 0444;
-			if (fp->f_flag & FWRITE)
-				pnp->pr_mode |= 0222;
-			vp = fp->f_vnode;
-			VN_HOLD(vp);
-		}
-		UF_EXIT(ufp);
-	}
-	mutex_exit(&fip->fi_lock);
-	mutex_enter(&p->p_lock);
+	if ((fp = pr_getf(p, fd, NULL)) != NULL) {
+		pnp->pr_mode = 07111;
+		if (fp->f_flag & FREAD)
+			pnp->pr_mode |= 0444;
+		if (fp->f_flag & FWRITE)
+			pnp->pr_mode |= 0222;
+		vp = fp->f_vnode;
+		VN_HOLD(vp);
+		pr_releasef(p, fd);
+	}
+
 	prunlock(dpnp);
 
-	if (vp == NULL)
+	if (vp == NULL) {
 		prfreenode(pnp);
-	else {
-		/*
-		 * Fill in the prnode so future references will
-		 * be able to find the underlying object's vnode.
-		 * Don't link this prnode into the list of all
-		 * prnodes for the process; this is a one-use node.
-		 */
-		pnp->pr_realvp = vp;
-		pnp->pr_parent = dp;		/* needed for prlookup */
-		VN_HOLD(dp);
-		vp = PTOV(pnp);
-		if (pnp->pr_realvp->v_type == VDIR) {
-			vp->v_type = VDIR;
-			vp->v_flag |= VTRAVERSE;
-		}
-	}
+		return (NULL);
+	}
+
+	/*
+	 * Fill in the prnode so future references will
+	 * be able to find the underlying object's vnode.
+	 * Don't link this prnode into the list of all
+	 * prnodes for the process; this is a one-use node.
+	 */
+	pnp->pr_realvp = vp;
+	pnp->pr_parent = dp;		/* needed for prlookup */
+	VN_HOLD(dp);
+	vp = PTOV(pnp);
+	if (pnp->pr_realvp->v_type == VDIR) {
+		vp->v_type = VDIR;
+		vp->v_flag |= VTRAVERSE;
+	}
+
+	return (vp);
+}
+
+static vnode_t *
+pr_lookup_fdinfodir(vnode_t *dp, char *comp)
+{
+	prnode_t *dpnp = VTOP(dp);
+	prnode_t *pnp;
+	vnode_t *vp = NULL;
+	proc_t *p;
+	uint_t fd;
+	int c;
+
+	ASSERT(dpnp->pr_type == PR_FDINFODIR);
+
+	fd = 0;
+	while ((c = *comp++) != '\0') {
+		int ofd;
+		if (c < '0' || c > '9')
+			return (NULL);
+		ofd = fd;
+		fd = 10 * fd + c - '0';
+		if (fd / 10 != ofd)	/* integer overflow */
+			return (NULL);
+	}
+
+	pnp = prgetnode(dp, PR_FDINFO);
+
+	if (prlock(dpnp, ZNO) != 0) {
+		prfreenode(pnp);
+		return (NULL);
+	}
+	p = dpnp->pr_common->prc_proc;
+	if ((p->p_flag & SSYS) || p->p_as == &kas) {
+		prunlock(dpnp);
+		prfreenode(pnp);
+		return (NULL);
+	}
+
+	/*
+	 * Don't link this prnode into the list of all
+	 * prnodes for the process; this is a one-use node.
+	 * Unlike the FDDIR case, the underlying vnode is not stored in
+	 * pnp->pr_realvp. Instead, the fd number is stored in pnp->pr_index
+	 * and used by pr_read_fdinfo() to return information for the right
+	 * file descriptor.
+	 */
+	pnp->pr_common = dpnp->pr_common;
+	pnp->pr_pcommon = dpnp->pr_pcommon;
+	pnp->pr_parent = dp;
+	pnp->pr_index = fd;
+	VN_HOLD(dp);
+	prunlock(dpnp);
+	vp = PTOV(pnp);
 
 	return (vp);
 }
@@ -4667,6 +4834,7 @@
 	case PR_CURDIR:
 	case PR_ROOTDIR:
 	case PR_FDDIR:
+	case PR_FDINFODIR:
 	case PR_OBJECTDIR:
 	case PR_PATHDIR:
 	case PR_CTDIR:
@@ -4799,8 +4967,8 @@
  */
 static int pr_readdir_notdir(), pr_readdir_procdir(), pr_readdir_piddir(),
 	pr_readdir_objectdir(), pr_readdir_lwpdir(), pr_readdir_lwpiddir(),
-	pr_readdir_fddir(), pr_readdir_pathdir(), pr_readdir_tmpldir(),
-	pr_readdir_ctdir();
+	pr_readdir_fddir(), pr_readdir_fdinfodir(), pr_readdir_pathdir(),
+	pr_readdir_tmpldir(), pr_readdir_ctdir();
 
 static int (*pr_readdir_function[PR_NFILES])() = {
 	pr_readdir_procdir,	/* /proc				*/
@@ -4829,6 +4997,8 @@
 	pr_readdir_notdir,	/* /proc/<pid>/root			*/
 	pr_readdir_fddir,	/* /proc/<pid>/fd			*/
 	pr_readdir_notdir,	/* /proc/<pid>/fd/nn			*/
+	pr_readdir_fdinfodir,	/* /proc/<pid>/fdinfo			*/
+	pr_readdir_notdir,	/* /proc/<pid>/fdinfo/nn		*/
 	pr_readdir_objectdir,	/* /proc/<pid>/object			*/
 	pr_readdir_notdir,	/* /proc/<pid>/object/xxx		*/
 	pr_readdir_lwpdir,	/* /proc/<pid>/lwp			*/
@@ -5361,9 +5531,12 @@
 	return (0);
 }
 
-/* ARGSUSED */
+/*
+ * Helper function for reading a directory which lists open file desciptors
+ */
 static int
-pr_readdir_fddir(prnode_t *pnp, uio_t *uiop, int *eofp)
+pr_readdir_fdlist(prnode_t *pnp, uio_t *uiop, int *eofp,
+    prnodetype_t dirtype, prnodetype_t entrytype)
 {
 	gfs_readdir_state_t gstate;
 	int error, eof = 0;
@@ -5373,8 +5546,6 @@
 	int fddirsize;
 	uf_info_t *fip;
 
-	ASSERT(pnp->pr_type == PR_FDDIR);
-
 	if ((error = prlock(pnp, ZNO)) != 0)
 		return (error);
 	p = pnp->pr_common->prc_proc;
@@ -5383,7 +5554,7 @@
 	mutex_exit(&p->p_lock);
 
 	if ((error = gfs_readdir_init(&gstate, PLNSIZ, PRSDSIZE, uiop,
-	    pmkino(0, pslot, PR_PIDDIR), pmkino(0, pslot, PR_FDDIR), 0)) != 0) {
+	    pmkino(0, pslot, PR_PIDDIR), pmkino(0, pslot, dirtype), 0)) != 0) {
 		mutex_enter(&p->p_lock);
 		prunlock(pnp);
 		return (error);
@@ -5414,7 +5585,7 @@
 		}
 
 		error = gfs_readdir_emitn(&gstate, uiop, n,
-		    pmkino(n, pslot, PR_FD), n);
+		    pmkino(n, pslot, entrytype), n);
 		if (error)
 			break;
 	}
@@ -5426,6 +5597,24 @@
 	return (gfs_readdir_fini(&gstate, error, eofp, eof));
 }
 
+static int
+pr_readdir_fddir(prnode_t *pnp, uio_t *uiop, int *eofp)
+{
+
+	ASSERT(pnp->pr_type == PR_FDDIR);
+
+	return (pr_readdir_fdlist(pnp, uiop, eofp, pnp->pr_type, PR_FD));
+}
+
+static int
+pr_readdir_fdinfodir(prnode_t *pnp, uio_t *uiop, int *eofp)
+{
+
+	ASSERT(pnp->pr_type == PR_FDINFODIR);
+
+	return (pr_readdir_fdlist(pnp, uiop, eofp, pnp->pr_type, PR_FDINFO));
+}
+
 /* ARGSUSED */
 static int
 pr_readdir_pathdir(prnode_t *pnp, uio_t *uiop, int *eofp)
@@ -5735,6 +5924,7 @@
 	switch (type) {
 	case PR_OBJECT:
 	case PR_FD:
+	case PR_FDINFO:
 	case PR_SELF:
 	case PR_PATH:
 		/* These are not linked into the usual lists */
--- a/usr/src/uts/common/sys/procfs.h	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/uts/common/sys/procfs.h	Tue Nov 19 11:04:46 2019 +0000
@@ -26,6 +26,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef _SYS_PROCFS_H
@@ -505,7 +506,7 @@
 #define	PG_HWMAPPED	0x04		/* page is present and mapped */
 
 /*
- * Open files.  Only in core files (for now).  Note that we'd like to use
+ * Open file information; used in core files.  Note that we'd like to use
  * the stat or stat64 structure, but both of these structures are unfortunately
  * not consistent between 32 and 64 bit modes.  To keep our lives simpler, we
  * just define our own structure with types that are not sensitive to this
@@ -513,7 +514,7 @@
  * struct stat (e.g. times, device sizes, etc.) so we don't bother adding those
  * here.
  */
-typedef struct prfdinfo {
+typedef struct prfdinfo_core {
 	int		pr_fd;
 	mode_t		pr_mode;
 
@@ -534,8 +535,97 @@
 	int		pr_fdflags;	/* fcntl(F_GETFD), etc. */
 
 	char		pr_path[MAXPATHLEN];
+} prfdinfo_core_t;
+
+/*
+ * Open file information; used in /proc/<pid>/fdinfo/nn files.
+ * This differs from prfdinfo_core_t above as it exposes more information
+ * and is variable length, with the last element, pr_misc, pointing to an
+ * array of miscellaneous data items.
+ */
+
+typedef struct prfdinfo {
+	int		pr_fd;		/* file descriptor number */
+	mode_t		pr_mode;	/* (see st_mode in stat(2)) */
+	ino64_t		pr_ino;		/* inode number */
+	off64_t		pr_size;	/* file size */
+	off64_t		pr_offset;	/* current offset of file descriptor */
+	uid_t		pr_uid;		/* owner's user id */
+	gid_t		pr_gid;		/* owner's group id */
+	major_t		pr_major;	/* major number of device */
+	minor_t		pr_minor;	/* minor number of device */
+	major_t		pr_rmajor;	/* major number (if special file) */
+	minor_t		pr_rminor;	/* minor number (if special file) */
+	int		pr_fileflags;	/* (see F_GETXFL in fcntl(2)) */
+	int		pr_fdflags;	/* (see F_GETFD in fcntl(2)) */
+	short		pr_locktype;	/* (see F_GETLK in fcntl(2)) */
+	pid_t		pr_lockpid;	/* process holding file lock */
+					/* (see F_GETLK) */
+	int		pr_locksysid;	/* sysid of locking process */
+					/* (see F_GETLK) */
+	pid_t		pr_peerpid;	/* peer process (socket, door) */
+	int		pr_filler[25];	/* reserved for future use */
+	char		pr_peername[PRFNSZ];	/* peer process name */
+#if	__STDC_VERSION__ >= 199901L
+	uint8_t		pr_misc[];	/* self describing structures */
+#else
+	uint8_t		pr_misc[1];	/* self describing structures */
+#endif
 } prfdinfo_t;
 
+/* pr_misc item size is rounded up to maintain alignment */
+#define	PRFDINFO_ROUNDUP(num) P2ROUNDUP((num), 4)
+
+typedef struct pr_misc_header {
+	uint_t		pr_misc_size;
+	uint_t		pr_misc_type;
+} pr_misc_header_t;
+
+/*
+ * The gaps in this enumeration are present to maintain compatibility with
+ * the values used in Solaris. Any future illumos-specific additions to this
+ * list should use the PR_ILLUMOS_ prefix, be placed after PR_MISC_TYPES_MAX
+ * and start from a number sufficiently large enough to leave space for any
+ * future additions in Solaris.
+ */
+
+enum PR_MISC_TYPES {
+	PR_PATHNAME,
+	PR_SOCKETNAME,
+	PR_PEERSOCKNAME,
+	PR_SOCKOPTS_BOOL_OPTS,
+	PR_SOCKOPT_LINGER,
+	PR_SOCKOPT_SNDBUF,
+	PR_SOCKOPT_RCVBUF,
+	PR_SOCKOPT_IP_NEXTHOP,
+	PR_SOCKOPT_IPV6_NEXTHOP,
+	PR_SOCKOPT_TYPE,
+	PR_SOCKOPT_TCP_CONGESTION = 11,
+	PR_SOCKFILTERS_PRIV = 14,
+	PR_MISC_TYPES_MAX
+};
+
+typedef struct prsockopts_bool_opts {
+	unsigned int prsock_bool_opts;
+} prsockopts_bool_opts_t;
+
+#define	PR_SO_DEBUG		(1 << 0)
+#define	PR_SO_REUSEADDR		(1 << 1)
+#define	PR_SO_REUSEPORT		(1 << 2)
+#define	PR_SO_KEEPALIVE		(1 << 3)
+#define	PR_SO_DONTROUTE		(1 << 4)
+#define	PR_SO_BROADCAST		(1 << 5)
+#define	PR_SO_OOBINLINE		(1 << 7)
+#define	PR_SO_DGRAM_ERRIND	(1 << 8)
+#define	PR_SO_ALLZONES		(1 << 9)
+#define	PR_SO_MAC_EXEMPT	(1 << 10)
+#define	PR_SO_EXCLBIND		(1 << 11)
+#define	PR_SO_PASSIVE_CONNECT	(1 << 12)
+#define	PR_SO_ACCEPTCONN	(1 << 13)
+#define	PR_UDP_NAT_T_ENDPOINT	(1 << 14)
+#define	PR_SO_VRRP		(1 << 15)
+#define	PR_SO_MAC_IMPLICIT	(1 << 16)
+
 /*
  * Representation of LWP name in core files.  In /proc, we use a simple char
  * array, but in core files we need to make it easy to correlate the note back
--- a/usr/src/uts/common/sys/prsystm.h	Tue Jan 14 16:34:53 2020 +0000
+++ b/usr/src/uts/common/sys/prsystm.h	Tue Nov 19 11:04:46 2019 +0000
@@ -29,6 +29,7 @@
 
 /*
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef _SYS_PRSYSTM_H
@@ -54,6 +55,7 @@
 struct prcred;
 struct prpriv;
 struct prsecflags;
+struct prfdinfo;
 
 struct seg;
 struct regs;
@@ -77,16 +79,14 @@
 extern void prgetprfpregs(klwp_t *, struct prfpregset *);
 extern void prgetprxregs(klwp_t *, caddr_t);
 extern int  prgetprxregsize(proc_t *);
-#if defined(__lint)
-/* Work around lint confusion between old and new prcred definitions */
-extern void prgetcred();
-#else
 extern void prgetcred(proc_t *, struct prcred *);
-#endif
 extern void prgetpriv(proc_t *, struct prpriv *);
 extern size_t prgetprivsize(void);
 extern void prgetsecflags(proc_t *, struct prsecflags *);
 extern int  prnsegs(struct as *, int);
+extern u_offset_t prgetfdinfosize(proc_t *, vnode_t *, cred_t *);
+extern int prgetfdinfo(proc_t *, vnode_t *, struct prfdinfo *, cred_t *,
+    list_t *);
 extern void prexit(proc_t *);
 extern void prfree(proc_t *);
 extern void prlwpexit(kthread_t *);