--- old/usr/src/uts/common/fs/proc/prsubr.c	Thu Dec  5 11:53:01 2019
+++ new/usr/src/uts/common/fs/proc/prsubr.c	Thu Dec  5 11:53:01 2019
@@ -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 2019 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,7 +1467,58 @@
 	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));
+
+	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));
+
+	fip = P_FINFO(p);
+
+	mutex_exit(&p->p_lock);
+	mutex_enter(&fip->fi_lock);
+	UF_ENTER(ufp, fip, fd);
+	ASSERT(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)
 {
 	char *s = name;
@@ -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,475 @@
 	}
 }
 
+static size_t
+prfdinfomisc(list_t *data, uint_t type, void *val, off_t vlen)
+{
+	pr_misc_header_t *misc;
+	size_t len;
+
+	len = 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", NULL
+	};
+	struct devnames *dnp;
+	major_t major;
+	uint_t i;
+
+	ASSERT(vp != NULL);
+	ASSERT(vp->v_type == VCHR);
+
+	if (vp->v_rdev == 0)
+		return (B_FALSE);
+
+	major = getmajor(vp->v_rdev);
+	if (major == DDI_MAJOR_T_NONE || major > devcnt)
+		return (B_FALSE);
+
+	dnp = &devnamesp[major];
+
+	for (i = 0; tlidevs[i] != NULL; i++) {
+		if (strcmp(dnp->dn_name, 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[MAXPATHLEN];
+	vnode_t *vrootp;
+	size_t sz = 0;
+
+	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, sizeof (pathname), cred) == 0) {
+		sz += prfdinfomisc(data, PR_PATHNAME,
+		    pathname, strlen(pathname));
+	}
+	VN_RELE(vrootp);
+
+	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) {
+		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 < sizeof (boolopts) / sizeof (boolopts[0]);
+		    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 < sizeof (sopts) / sizeof (sopts[0]); 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 = sizeof (*misc) + vlen;
+		sz += len;
+
+		if (data != NULL) {
+			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(prfdinfov2_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 (vp->v_type == VCHR && pristli(vp) && vp->v_stream != NULL)
+		sz += prfdinfotlisockopt(vp, NULL, cred);
+
+	return (sz);
+}
+
+int
+prgetfdinfo(proc_t *p, vnode_t *vp, prfdinfov2_t *fdinfo, cred_t *cred,
+    list_t *data)
+{
+	vattr_t vattr;
+	int error;
+
+	if (vp == NULL)
+		return (ENOENT);
+
+	/*
+	 * Initialise defaults for values that do 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 */
+
+	switch (vp->v_type) {
+	case VFIFO:
+	case VDOOR:
+	case VSOCK:
+		/* Do not provide an offset for these file types */
+		fdinfo->pr_offset = -1;
+		break;
+	}
+
+	/* 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;
+	}
+
+	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 (vp->v_type == VCHR && pristli(vp) && vp->v_stream != NULL)
+		(void) prfdinfotlisockopt(vp, data, cred);
+
+	/*
+	 * Add a terminating record with a zero size (pr_iol_newbuf will
+	 * bzero the memory)
+	 */
+	(void) pr_iol_newbuf(data, sizeof (pr_misc_header_t));
+
+	return (0);
+}
+
 #ifdef _SYSCALL32_IMPL
 void
 prgetpsinfo32(proc_t *p, psinfo32_t *psp)
@@ -2670,7 +3215,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)
