Updates to libprocstat(3) and procstat(1) to allow monitoring Capsicum
capability mode and capabilities.

Right now no attempt is made to unwrap capabilities when operating on
a crashdump, so further refinement is required.

Approved by:	re (xxx)
Sponsored by:	Google Inc

Index: lib/libprocstat/libprocstat.c
===================================================================
--- lib/libprocstat/libprocstat.c	(revision 224841)
+++ lib/libprocstat/libprocstat.c	(working copy)
@@ -282,7 +282,7 @@
 
 static struct filestat *
 filestat_new_entry(void *typedep, int type, int fd, int fflags, int uflags,
-    int refcount, off_t offset, char *path)
+    int refcount, off_t offset, char *path, cap_rights_t cap_rights)
 {
 	struct filestat *entry;
 
@@ -299,6 +299,7 @@
 	entry->fs_ref_count = refcount;
 	entry->fs_offset = offset;
 	entry->fs_path = path;
+	entry->fs_cap_rights = cap_rights;
 	return (entry);
 }
 
@@ -381,21 +382,21 @@
 	/* root directory vnode, if one. */
 	if (filed.fd_rdir) {
 		entry = filestat_new_entry(filed.fd_rdir, PS_FST_TYPE_VNODE, -1,
-		    PS_FST_FFLAG_READ, PS_FST_UFLAG_RDIR, 0, 0, NULL);
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_RDIR, 0, 0, NULL, 0);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
 	/* current working directory vnode. */
 	if (filed.fd_cdir) {
 		entry = filestat_new_entry(filed.fd_cdir, PS_FST_TYPE_VNODE, -1,
-		    PS_FST_FFLAG_READ, PS_FST_UFLAG_CDIR, 0, 0, NULL);
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_CDIR, 0, 0, NULL, 0);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
 	/* jail root, if any. */
 	if (filed.fd_jdir) {
 		entry = filestat_new_entry(filed.fd_jdir, PS_FST_TYPE_VNODE, -1,
-		    PS_FST_FFLAG_READ, PS_FST_UFLAG_JAIL, 0, 0, NULL);
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_JAIL, 0, 0, NULL, 0);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
@@ -403,14 +404,14 @@
 	if (kp->ki_tracep) {
 		entry = filestat_new_entry(kp->ki_tracep, PS_FST_TYPE_VNODE, -1,
 		    PS_FST_FFLAG_READ | PS_FST_FFLAG_WRITE,
-		    PS_FST_UFLAG_TRACE, 0, 0, NULL);
+		    PS_FST_UFLAG_TRACE, 0, 0, NULL, 0);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
 	/* text vnode, if one */
 	if (kp->ki_textvp) {
 		entry = filestat_new_entry(kp->ki_textvp, PS_FST_TYPE_VNODE, -1,
-		    PS_FST_FFLAG_READ, PS_FST_UFLAG_TEXT, 0, 0, NULL);
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_TEXT, 0, 0, NULL, 0);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
@@ -418,7 +419,7 @@
 	if ((vp = getctty(kd, kp)) != NULL) {
 		entry = filestat_new_entry(vp, PS_FST_TYPE_VNODE, -1,
 		    PS_FST_FFLAG_READ | PS_FST_FFLAG_WRITE,
-		    PS_FST_UFLAG_CTTY, 0, 0, NULL);
+		    PS_FST_UFLAG_CTTY, 0, 0, NULL, 0);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
@@ -471,8 +472,9 @@
 		default:
 			continue;
 		}
+		/* XXXRW: No capability rights support for kvm yet. */
 		entry = filestat_new_entry(data, type, i,
-		    to_filestat_flags(file.f_flag), 0, 0, 0, NULL);
+		    to_filestat_flags(file.f_flag), 0, 0, 0, NULL, 0);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
@@ -531,7 +533,7 @@
 			 */
 			entry = filestat_new_entry(object.handle,
 			    PS_FST_TYPE_VNODE, -1, fflags,
-			    PS_FST_UFLAG_MMAP, 0, 0, NULL);
+			    PS_FST_UFLAG_MMAP, 0, 0, NULL, 0);
 			if (entry != NULL)
 				STAILQ_INSERT_TAIL(head, entry, next);
 		}
@@ -586,6 +588,7 @@
 	} kfflags2fst[] = {
 		{ KF_FLAG_APPEND, PS_FST_FFLAG_APPEND },
 		{ KF_FLAG_ASYNC, PS_FST_FFLAG_ASYNC },
+		{ KF_FLAG_CAPABILITY, PS_FST_FFLAG_CAPABILITY },
 		{ KF_FLAG_CREAT, PS_FST_FFLAG_CREAT },
 		{ KF_FLAG_DIRECT, PS_FST_FFLAG_DIRECT },
 		{ KF_FLAG_EXCL, PS_FST_FFLAG_EXCL },
@@ -644,6 +647,7 @@
 	int cnt, fd, fflags;
 	int i, type, uflags;
 	int refcount;
+	cap_rights_t cap_rights;
 
 	assert(kp);
 	if (kp->ki_fd == NULL)
@@ -676,12 +680,13 @@
 			path = strdup(kif->kf_path);
 		else
 			path = NULL;
+		cap_rights = kif->kf_cap_rights;
 
 		/*
 		 * Create filestat entry.
 		 */
 		entry = filestat_new_entry(kif, type, fd, fflags, uflags,
-		    refcount, offset, path);
+		    refcount, offset, path, cap_rights);
 		if (entry != NULL)
 			STAILQ_INSERT_TAIL(head, entry, next);
 	}
@@ -707,7 +712,8 @@
 			else
 				path = NULL;
 			entry = filestat_new_entry(kve, PS_FST_TYPE_VNODE, -1,
-			    fflags, PS_FST_UFLAG_MMAP, refcount, offset, path);
+			    fflags, PS_FST_UFLAG_MMAP, refcount, offset, path,
+			    0);
 			if (entry != NULL)
 				STAILQ_INSERT_TAIL(head, entry, next);
 		}
Index: lib/libprocstat/libprocstat.h
===================================================================
--- lib/libprocstat/libprocstat.h	(revision 224841)
+++ lib/libprocstat/libprocstat.h	(working copy)
@@ -88,6 +88,7 @@
 #define	PS_FST_FFLAG_DIRECT	0x1000
 #define	PS_FST_FFLAG_EXEC	0x2000
 #define	PS_FST_FFLAG_HASLOCK	0x4000
+#define	PS_FST_FFLAG_CAPABILITY	0x8000
 
 struct procstat;
 struct filestat {
@@ -101,6 +102,7 @@
 	void	*fs_typedep;	/* Type dependent data. */
 	char	*fs_path;
 	STAILQ_ENTRY(filestat)	next;
+	cap_rights_t	fs_cap_rights;	/* Capability rights, if flag set. */
 };
 struct vnstat {
 	uint64_t	vn_fileid;
Index: usr.bin/procstat/procstat.c
===================================================================
--- usr.bin/procstat/procstat.c	(revision 224841)
+++ usr.bin/procstat/procstat.c	(working copy)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2007 Robert N. M. Watson
+ * Copyright (c) 2007, 2011 Robert N. M. Watson
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -40,13 +40,13 @@
 #include "procstat.h"
 
 static int aflag, bflag, cflag, fflag, iflag, jflag, kflag, sflag, tflag, vflag;
-int	hflag, nflag;
+int	hflag, nflag, Cflag;
 
 static void
 usage(void)
 {
 
-	fprintf(stderr, "usage: procstat [-h] [-M core] [-N system] "
+	fprintf(stderr, "usage: procstat [-h] [-C] [-M core] [-N system] "
 	    "[-w interval] [-b | -c | -f | -i | -j | -k | -s | -t | -v]\n");
 	fprintf(stderr, "                [-a | pid ...]\n");
 	exit(EX_USAGE);
@@ -117,8 +117,12 @@
 
 	interval = 0;
 	memf = nlistf = NULL;
-	while ((ch = getopt(argc, argv, "N:M:abcfijkhstvw:")) != -1) {
+	while ((ch = getopt(argc, argv, "CN:M:abcfijkhstvw:")) != -1) {
 		switch (ch) {
+		case 'C':
+			Cflag++;
+			break;
+
 		case 'M':
 			memf = optarg;
 			break;
@@ -204,6 +208,10 @@
 	if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0))
 		usage();
 
+	/* Only allow -C with -f. */
+	if (Cflag && !fflag)
+		usage();
+
 	if (memf != NULL)
 		prstat = procstat_open_kvm(nlistf, memf);
 	else
Index: usr.bin/procstat/procstat_files.c
===================================================================
--- usr.bin/procstat/procstat_files.c	(revision 224841)
+++ usr.bin/procstat/procstat_files.c	(working copy)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2007 Robert N. M. Watson
+ * Copyright (c) 2007-2011 Robert N. M. Watson
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,7 @@
  */
 
 #include <sys/param.h>
+#include <sys/capability.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <sys/un.h>
@@ -131,6 +132,133 @@
 	printf("%s", addr);
 }
 
+static struct cap_desc {
+	cap_rights_t	 cd_right;
+	const char	*cd_desc;
+} cap_desc[] = {
+	/* General file I/O. */
+	{ CAP_READ,		"rd" },
+	{ CAP_WRITE,		"wr" },
+	{ CAP_MMAP,		"mm" },
+	{ CAP_MAPEXEC,		"me" },
+	{ CAP_FEXECVE,		"fe" },
+	{ CAP_FSYNC,		"fy" },
+	{ CAP_FTRUNCATE,	"ft" },
+	{ CAP_SEEK,		"se" },
+
+	/* VFS methods. */
+	{ CAP_FCHFLAGS,		"cf" },
+	{ CAP_FCHDIR,		"cd" },
+	{ CAP_FCHMOD,		"cm" },
+	{ CAP_FCHOWN,		"cn" },
+	{ CAP_FCNTL,		"fc" },
+	{ CAP_FPATHCONF,	"fp" },
+	{ CAP_FLOCK,		"fl" },
+	{ CAP_FSCK,		"fk" },
+	{ CAP_FSTAT,		"fs" },
+	{ CAP_FSTATFS,		"sf" },
+	{ CAP_FUTIMES,		"fu" },
+	{ CAP_CREATE,		"cr" },
+	{ CAP_DELETE,		"de" },
+	{ CAP_MKDIR,		"md" },
+	{ CAP_RMDIR,		"rm" },
+	{ CAP_MKFIFO,		"mf" },
+
+	/* Lookups - used to constraint *at() calls. */
+	{ CAP_LOOKUP,		"lo" },
+
+	/* Extended attributes. */
+	{ CAP_EXTATTR_GET,	"eg" },
+	{ CAP_EXTATTR_SET,	"es" },
+	{ CAP_EXTATTR_DELETE,	"ed" },
+	{ CAP_EXTATTR_LIST,	"el" },
+
+	/* Access Control Lists. */
+	{ CAP_ACL_GET,		"ag" },
+	{ CAP_ACL_SET,		"as" },
+	{ CAP_ACL_DELETE,	"ad" },
+	{ CAP_ACL_CHECK,	"ac" },
+
+	/* Socket operations. */
+	{ CAP_ACCEPT,		"at" },
+	{ CAP_BIND,		"bd" },
+	{ CAP_CONNECT,		"co" },
+	{ CAP_GETPEERNAME,	"pn" },
+	{ CAP_GETSOCKNAME,	"sn" },
+	{ CAP_GETSOCKOPT,	"gs" },
+	{ CAP_LISTEN,		"ln" },
+	{ CAP_PEELOFF,		"pf" },
+	{ CAP_SETSOCKOPT,	"ss" },
+	{ CAP_SHUTDOWN,		"sh" },
+
+	/* Mandatory Access Control. */
+	{ CAP_MAC_GET,		"mg" },
+	{ CAP_MAC_SET,		"ms" },
+
+	/* Methods on semaphores. */
+	{ CAP_SEM_GETVALUE,	"sg" },
+	{ CAP_SEM_POST,		"sp" },
+	{ CAP_SEM_WAIT,		"sw" },
+
+	/* Event monitoring and posting. */
+	{ CAP_POLL_EVENT,	"po" },
+	{ CAP_POST_EVENT,	"ev" },
+
+	/* Strange and powerful rights that should not be given lightly. */
+	{ CAP_IOCTL,		"io" },
+	{ CAP_TTYHOOK,		"ty" },
+
+#ifdef NOTYET
+	{ CAP_PDGETPID,		"pg" },
+	{ CAP_PDWAIT4,		"pw" },
+	{ CAP_PDKILL,		"pk" },
+#endif
+};
+static const u_int	cap_desc_count = sizeof(cap_desc) /
+			    sizeof(cap_desc[0]);
+
+static u_int
+width_capability(cap_rights_t rights)
+{
+	u_int count, i, width;
+
+	count = 0;
+	width = 0;
+	for (i = 0; i < cap_desc_count; i++) {
+		if (rights & cap_desc[i].cd_right) {
+			width += strlen(cap_desc[i].cd_desc);
+			if (count)
+				width++;
+			count++;
+		}
+	}
+	return (width);
+}
+
+static void
+print_capability(cap_rights_t rights, u_int capwidth)
+{
+	u_int count, i, width;
+
+	count = 0;
+	width = 0;
+	for (i = width_capability(rights); i < capwidth; i++) {
+		if (rights || i != 0)
+			printf(" ");
+		else
+			printf("-");
+	}
+	for (i = 0; i < cap_desc_count; i++) {
+		if (rights & cap_desc[i].cd_right) {
+			printf("%s%s", count ? "," : "", cap_desc[i].cd_desc);
+			width += strlen(cap_desc[i].cd_desc);
+			if (count)
+				width++;
+			count++;
+		}
+	}
+}
+
 void
 procstat_files(struct procstat *procstat, struct kinfo_proc *kipp)
 { 
@@ -139,14 +267,39 @@
 	struct filestat *fst;
 	const char *str;
 	struct vnstat vn;
+	u_int capwidth, width;
 	int error;
 
-	if (!hflag)
-		printf("%5s %-16s %4s %1s %1s %-8s %3s %7s %-3s %-12s\n",
-		    "PID", "COMM", "FD", "T", "V", "FLAGS", "REF", "OFFSET",
-		    "PRO", "NAME");
+	/*
+	 * To print the header in capability mode, we need to know the width
+	 * of the widest capability string.  Even if we get no processes
+	 * back, we will print the header, so we defer aborting due to a lack
+	 * of processes until after the header logic.
+	 */
+	capwidth = 0;
+	head = procstat_getfiles(procstat, kipp, 0);
+	if (head != NULL && Cflag) {
+		STAILQ_FOREACH(fst, head, next) {
+			width = width_capability(fst->fs_cap_rights);
+			if (width > capwidth)
+				capwidth = width;
+		}
+		if (capwidth < strlen("CAPABILITIES"))
+			capwidth = strlen("CAPABILITIES");
+	}
 
-	head = procstat_getfiles(procstat, kipp, 0);
+	if (!hflag) {
+		if (Cflag)
+			printf("%5s %-16s %4s %1s %-9s %-*s "
+			    "%-3s %-12s\n", "PID", "COMM", "FD", "T",
+			    "FLAGS", capwidth, "CAPABILITIES", "PRO",
+			    "NAME");
+		else
+			printf("%5s %-16s %4s %1s %1s %-9s "
+			    "%3s %7s %-3s %-12s\n", "PID", "COMM", "FD", "T",
+			    "V", "FLAGS", "REF", "OFFSET", "PRO", "NAME");
+	}
+
 	if (head == NULL)
 		return;
 	STAILQ_FOREACH(fst, head, next) {
@@ -215,50 +368,53 @@
 			break;
 		}
 		printf("%1s ", str);
-		str = "-";
-		if (fst->fs_type == PS_FST_TYPE_VNODE) {
-			error = procstat_get_vnode_info(procstat, fst, &vn, NULL);
-			switch (vn.vn_type) {
-			case PS_FST_VTYPE_VREG:
-				str = "r";
-				break;
+		if (!Cflag) {
+			str = "-";
+			if (fst->fs_type == PS_FST_TYPE_VNODE) {
+				error = procstat_get_vnode_info(procstat, fst,
+				    &vn, NULL);
+				switch (vn.vn_type) {
+				case PS_FST_VTYPE_VREG:
+					str = "r";
+					break;
 
-			case PS_FST_VTYPE_VDIR:
-				str = "d";
-				break;
+				case PS_FST_VTYPE_VDIR:
+					str = "d";
+					break;
 
-			case PS_FST_VTYPE_VBLK:
-				str = "b";
-				break;
+				case PS_FST_VTYPE_VBLK:
+					str = "b";
+					break;
 
-			case PS_FST_VTYPE_VCHR:
-				str = "c";
-				break;
+				case PS_FST_VTYPE_VCHR:
+					str = "c";
+					break;
 
-			case PS_FST_VTYPE_VLNK:
-				str = "l";
-				break;
+				case PS_FST_VTYPE_VLNK:
+					str = "l";
+					break;
 
-			case PS_FST_VTYPE_VSOCK:
-				str = "s";
-				break;
+				case PS_FST_VTYPE_VSOCK:
+					str = "s";
+					break;
 
-			case PS_FST_VTYPE_VFIFO:
-				str = "f";
-				break;
+				case PS_FST_VTYPE_VFIFO:
+					str = "f";
+					break;
 
-			case PS_FST_VTYPE_VBAD:
-				str = "x";
-				break;
+				case PS_FST_VTYPE_VBAD:
+					str = "x";
+					break;
 
-			case PS_FST_VTYPE_VNON:
-			case PS_FST_VTYPE_UNKNOWN:
-			default:
-				str = "?";
-				break;
+				case PS_FST_VTYPE_VNON:
+				case PS_FST_VTYPE_UNKNOWN:
+				default:
+					str = "?";
+					break;
+				}
 			}
+			printf("%1s ", str);
 		}
-		printf("%1s ", str);
 		printf("%s", fst->fs_fflags & PS_FST_FFLAG_READ ? "r" : "-");
 		printf("%s", fst->fs_fflags & PS_FST_FFLAG_WRITE ? "w" : "-");
 		printf("%s", fst->fs_fflags & PS_FST_FFLAG_APPEND ? "a" : "-");
@@ -266,16 +422,23 @@
 		printf("%s", fst->fs_fflags & PS_FST_FFLAG_SYNC ? "f" : "-");
 		printf("%s", fst->fs_fflags & PS_FST_FFLAG_NONBLOCK ? "n" : "-");
 		printf("%s", fst->fs_fflags & PS_FST_FFLAG_DIRECT ? "d" : "-");
-		printf("%s ", fst->fs_fflags & PS_FST_FFLAG_HASLOCK ? "l" : "-");
-		if (fst->fs_ref_count > -1)
-			printf("%3d ", fst->fs_ref_count);
-		else
-			printf("%3c ", '-');
-		if (fst->fs_offset > -1)
-			printf("%7jd ", (intmax_t)fst->fs_offset);
-		else
-			printf("%7c ", '-');
-
+		printf("%s", fst->fs_fflags & PS_FST_FFLAG_HASLOCK ? "l" : "-");
+		printf("%s ", fst->fs_fflags & PS_FST_FFLAG_CAPABILITY ?
+		    "c" : "-");
+		if (!Cflag) {
+			if (fst->fs_ref_count > -1)
+				printf("%3d ", fst->fs_ref_count);
+			else
+				printf("%3c ", '-');
+			if (fst->fs_offset > -1)
+				printf("%7jd ", (intmax_t)fst->fs_offset);
+			else
+				printf("%7c ", '-');
+		}
+		if (Cflag) {
+			print_capability(fst->fs_cap_rights, capwidth);
+			printf(" ");
+		}
 		switch (fst->fs_type) {
 		case PS_FST_TYPE_VNODE:
 		case PS_FST_TYPE_FIFO:
@@ -314,7 +477,6 @@
 			break;
 
 		default:
-			printf("%-3s ", "-");
 			printf("%-18s", "-");
 		}
 
Index: usr.bin/procstat/procstat.h
===================================================================
--- usr.bin/procstat/procstat.h	(revision 224841)
+++ usr.bin/procstat/procstat.h	(working copy)
@@ -29,7 +29,7 @@
 #ifndef PROCSTAT_H
 #define	PROCSTAT_H
 
-extern int	hflag, nflag;
+extern int	hflag, nflag, Cflag;
 
 struct kinfo_proc;
 void	kinfo_proc_sort(struct kinfo_proc *kipp, int count);
Index: usr.bin/procstat/procstat_cred.c
===================================================================
--- usr.bin/procstat/procstat_cred.c	(revision 224841)
+++ usr.bin/procstat/procstat_cred.c	(working copy)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2007 Robert N. M. Watson
+ * Copyright (c) 2007-2008 Robert N. M. Watson
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -48,9 +48,9 @@
 	gid_t *groups = NULL;
 
 	if (!hflag)
-		printf("%5s %-16s %5s %5s %5s %5s %5s %5s %-20s\n", "PID",
+		printf("%5s %-16s %5s %5s %5s %5s %5s %5s %5s %-15s\n", "PID",
 		    "COMM", "EUID", "RUID", "SVUID", "EGID", "RGID", "SVGID",
-		    "GROUPS");
+		    "FLAGS", "GROUPS");
 
 	printf("%5d ", kipp->ki_pid);
 	printf("%-16s ", kipp->ki_comm);
@@ -60,6 +60,8 @@
 	printf("%5d ", kipp->ki_groups[0]);
 	printf("%5d ", kipp->ki_rgid);
 	printf("%5d ", kipp->ki_svgid);
+	printf("%s", kipp->ki_cr_flags & CRED_FLAG_CAPMODE ? "C" : "-");
+	printf("     ");
 
 	/*
 	 * We may have too many groups to fit in kinfo_proc's statically
Index: usr.bin/procstat/procstat.1
===================================================================
--- usr.bin/procstat/procstat.1	(revision 224841)
+++ usr.bin/procstat/procstat.1	(working copy)
@@ -1,5 +1,5 @@
 .\"-
-.\" Copyright (c) 2007-2008 Robert N. M. Watson
+.\" Copyright (c) 2007-2009 Robert N. M. Watson
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,7 @@
 .Nm
 .Op Fl h
 .Op Fl n
+.Op Fl C
 .Op Fl w Ar interval
 .Op Fl b | c | f | i | j | k | s | t | v
 .Op Fl a | Ar pid ...
@@ -88,6 +89,11 @@
 .Fl w
 flag is not specified, the output will not repeat.
 .Pp
+The
+.Fl C
+flag requests the printing of additional capability information in the file
+descriptor view.
+.Pp
 Some information, such as VM and file descriptor information, is available
 only to the owner of a process or the superuser.
 .Ss Binary Information
@@ -116,7 +122,8 @@
 Display detailed information about each file descriptor referenced by a
 process, including the process ID, command, file descriptor number, and
 per-file descriptor object information, such as object type and file system
-path:
+path.
+By default, the following information will be printed:
 .Pp
 .Bl -tag -width indent -compact
 .It PID
@@ -208,7 +215,17 @@
 direct I/O
 .It l
 lock held
+.It c
+descriptor is a capability
 .El
+.Pp
+If the
+.Fl C
+flag is specified, the vnode type, reference count, and offset fields will be
+omitted, and a new capabilities field will be included listing capabilities,
+as described in
+.Xr cap_new 2 ,
+present for each capability descriptor.
 .Ss Signal Disposition Information
 Display signal pending and disposition for a process:
 .Pp
@@ -306,9 +323,18 @@
 real group ID
 .It SVGID
 saved group ID
+.It FLAGS
+credential flags
 .It GROUPS
 group set
 .El
+.Pp
+The following credential flags may be displayed:
+.Pp
+.Bl -tag -width X -compact
+.It C
+capability mode
+.El
 .Ss Thread Information
 Display per-thread information, including process ID, per-thread ID, name,
 CPU, and execution state:
@@ -402,6 +428,8 @@
 .Xr fstat 1 ,
 .Xr ps 1 ,
 .Xr sockstat 1 ,
+.Xr cap_enter 2 ,
+.Xr cap_new 2 ,
 .Xr ddb 4 ,
 .Xr stack 9
 .Sh AUTHORS
