xref: /illumos-gate/usr/src/cmd/ptools/pfiles/pfiles.c (revision 3385b4726f1b09aa9511340017fb23c7cde6aadd)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
25  */
26 /*
27  * Copyright (c) 2017 Joyent, Inc.  All Rights reserved.
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <dirent.h>
38 #include <limits.h>
39 #include <door.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <sys/mkdev.h>
44 #include <sys/stropts.h>
45 #include <sys/timod.h>
46 #include <sys/un.h>
47 #include <libproc.h>
48 #include <netinet/in.h>
49 #include <netinet/udp.h>
50 #include <arpa/inet.h>
51 #include <ucred.h>
52 #include <zone.h>
53 
54 #define	copyflock(dst, src) \
55 	(dst).l_type = (src).l_type;		\
56 	(dst).l_whence = (src).l_whence;	\
57 	(dst).l_start = (src).l_start;		\
58 	(dst).l_len = (src).l_len;		\
59 	(dst).l_sysid = (src).l_sysid;		\
60 	(dst).l_pid = (src).l_pid;
61 
62 static char *command;
63 static volatile int interrupt;
64 static int Fflag;
65 static boolean_t nflag = B_FALSE;
66 
67 static	void	intr(int);
68 static	void	dofcntl(struct ps_prochandle *, prfdinfo_t *, int, int);
69 static	void	dosocket(struct ps_prochandle *, int);
70 static	void	dofifo(struct ps_prochandle *, int);
71 static	void	dotli(struct ps_prochandle *, int);
72 static	void	show_files(struct ps_prochandle *);
73 static	void	show_fileflags(int);
74 static	void	show_door(struct ps_prochandle *, int);
75 static	int	getflock(struct ps_prochandle *, int, struct flock *);
76 
77 int
78 main(int argc, char **argv)
79 {
80 	int retc = 0;
81 	int opt;
82 	int errflg = 0;
83 	struct ps_prochandle *Pr;
84 
85 	if ((command = strrchr(argv[0], '/')) != NULL)
86 		command++;
87 	else
88 		command = argv[0];
89 
90 	/* options */
91 	while ((opt = getopt(argc, argv, "Fn")) != EOF) {
92 		switch (opt) {
93 		case 'F':		/* force grabbing (no O_EXCL) */
94 			Fflag = PGRAB_FORCE;
95 			break;
96 		case 'n':
97 			nflag = B_TRUE;
98 			break;
99 		default:
100 			errflg = 1;
101 			break;
102 		}
103 	}
104 
105 	argc -= optind;
106 	argv += optind;
107 
108 	if (errflg || argc <= 0) {
109 		(void) fprintf(stderr, "usage:\t%s [-F] { pid | core } ...\n",
110 		    command);
111 		(void) fprintf(stderr,
112 		    "  (report open files of each process)\n");
113 		(void) fprintf(stderr,
114 		    "  -F: force grabbing of the target process\n");
115 		exit(2);
116 	}
117 
118 	/* catch signals from terminal */
119 	if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
120 		(void) sigset(SIGHUP, intr);
121 	if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
122 		(void) sigset(SIGINT, intr);
123 	if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
124 		(void) sigset(SIGQUIT, intr);
125 	(void) sigset(SIGPIPE, intr);
126 	(void) sigset(SIGTERM, intr);
127 
128 	(void) proc_initstdio();
129 
130 
131 	while (--argc >= 0 && !interrupt) {
132 		char *arg;
133 		psinfo_t psinfo;
134 		pid_t pid;
135 		int gret;
136 
137 		(void) proc_flushstdio();
138 
139 		arg = *argv++;
140 
141 		/* get the specified pid and the psinfo struct */
142 		if ((pid = proc_arg_psinfo(arg, PR_ARG_PIDS,
143 		    &psinfo, &gret)) == -1) {
144 
145 			if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_CORES,
146 			    Fflag, &gret, NULL)) == NULL) {
147 				(void) fprintf(stderr,
148 				    "%s: cannot examine %s: %s\n",
149 				    command, arg, Pgrab_error(gret));
150 				retc++;
151 				continue;
152 			}
153 			if (proc_arg_psinfo(arg, PR_ARG_ANY, &psinfo,
154 			    &gret) < 0) {
155 				(void) fprintf(stderr,
156 				    "%s: cannot examine %s: %s\n",
157 				    command, arg, Pgrab_error(gret));
158 				retc++;
159 				Prelease(Pr, 0);
160 				continue;
161 			}
162 			(void) printf("core '%s' of %d:\t%.70s\n",
163 			    arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
164 
165 			show_files(Pr);
166 			Prelease(Pr, 0);
167 
168 		} else if ((Pr = Pgrab(pid, Fflag, &gret)) != NULL) {
169 			if (Pcreate_agent(Pr) == 0) {
170 				proc_unctrl_psinfo(&psinfo);
171 				(void) printf("%d:\t%.70s\n",
172 				    (int)pid, psinfo.pr_psargs);
173 				show_files(Pr);
174 				Pdestroy_agent(Pr);
175 			} else {
176 				(void) fprintf(stderr,
177 				    "%s: cannot control process %d\n",
178 				    command, (int)pid);
179 				retc++;
180 			}
181 			Prelease(Pr, 0);
182 			Pr = NULL;
183 		} else {
184 			switch (gret) {
185 			case G_SYS:
186 				proc_unctrl_psinfo(&psinfo);
187 				(void) printf("%d:\t%.70s\n", (int)pid,
188 				    psinfo.pr_psargs);
189 				(void) printf("  [system process]\n");
190 				break;
191 			default:
192 				(void) fprintf(stderr, "%s: %s: %d\n",
193 				    command, Pgrab_error(gret), (int)pid);
194 				retc++;
195 				break;
196 			}
197 		}
198 	}
199 
200 	(void) proc_finistdio();
201 
202 	if (interrupt && retc == 0)
203 		retc++;
204 	return (retc);
205 }
206 
207 /* ARGSUSED */
208 static void
209 intr(int sig)
210 {
211 	interrupt = 1;
212 }
213 
214 /* ------ begin specific code ------ */
215 
216 static int
217 show_file(void *data, prfdinfo_t *info)
218 {
219 	struct ps_prochandle *Pr = data;
220 	char unknown[12];
221 	char *s;
222 	mode_t mode;
223 
224 	if (interrupt)
225 		return (1);
226 
227 	mode = info->pr_mode;
228 
229 	switch (mode & S_IFMT) {
230 	case S_IFCHR: s = "S_IFCHR"; break;
231 	case S_IFBLK: s = "S_IFBLK"; break;
232 	case S_IFIFO: s = "S_IFIFO"; break;
233 	case S_IFDIR: s = "S_IFDIR"; break;
234 	case S_IFREG: s = "S_IFREG"; break;
235 	case S_IFLNK: s = "S_IFLNK"; break;
236 	case S_IFSOCK: s = "S_IFSOCK"; break;
237 	case S_IFDOOR: s = "S_IFDOOR"; break;
238 	case S_IFPORT: s = "S_IFPORT"; break;
239 	default:
240 		s = unknown;
241 		(void) sprintf(s, "0x%.4x ", (int)mode & S_IFMT);
242 		break;
243 	}
244 
245 	(void) printf("%4d: %s mode:0%.3o", info->pr_fd, s,
246 	    (int)mode & ~S_IFMT);
247 
248 	(void) printf(" dev:%u,%u",
249 	    (unsigned)info->pr_major, (unsigned)info->pr_minor);
250 
251 	if ((mode & S_IFMT) == S_IFPORT) {
252 		(void) printf(" uid:%d gid:%d",
253 		    (int)info->pr_uid, (int)info->pr_gid);
254 		(void) printf(" size:%lld\n", (longlong_t)info->pr_size);
255 		return (0);
256 	}
257 
258 	(void) printf(" ino:%llu uid:%d gid:%d",
259 	    (u_longlong_t)info->pr_ino, (int)info->pr_uid, (int)info->pr_gid);
260 
261 	if ((info->pr_rmajor == (major_t)NODEV) &&
262 	    (info->pr_rminor == (minor_t)NODEV))
263 		(void) printf(" size:%lld\n", (longlong_t)info->pr_size);
264 	else
265 		(void) printf(" rdev:%u,%u\n",
266 		    (unsigned)info->pr_rmajor, (unsigned)info->pr_rminor);
267 
268 	if (!nflag) {
269 		dofcntl(Pr, info,
270 		    (mode & (S_IFMT|S_ENFMT|S_IXGRP)) == (S_IFREG|S_ENFMT),
271 		    (mode & S_IFMT) == S_IFDOOR);
272 
273 		if (Pstate(Pr) != PS_DEAD) {
274 			char *dev = NULL;
275 
276 			if ((mode & S_IFMT) == S_IFSOCK)
277 				dosocket(Pr, info->pr_fd);
278 			else if ((mode & S_IFMT) == S_IFIFO)
279 				dofifo(Pr, info->pr_fd);
280 
281 			if ((mode & S_IFMT) == S_IFCHR) {
282 				/*
283 				 * There's no elegant way to determine
284 				 * if a character device supports TLI,
285 				 * so we lame out and just check a
286 				 * hardcoded list of known TLI devices.
287 				 */
288 				int i;
289 				const char *tlidevs[] = {
290 				    "tcp", "tcp6", "udp", "udp6", NULL
291 				};
292 
293 				/* global zone: /devices paths */
294 				dev = strrchr(info->pr_path, ':');
295 				/* also check the /dev path for zones */
296 				if (dev == NULL)
297 					dev = strrchr(info->pr_path, '/');
298 				if (dev != NULL) {
299 					dev++; /* skip past the `:' or '/' */
300 
301 					for (i = 0; tlidevs[i] != NULL; i++) {
302 						if (strcmp(dev, tlidevs[i]) ==
303 						    0) {
304 							dotli(Pr, info->pr_fd);
305 							break;
306 						}
307 					}
308 				}
309 			}
310 		}
311 
312 		if (info->pr_path[0] != '\0')
313 			(void) printf("      %s\n", info->pr_path);
314 
315 		if (info->pr_offset != -1) {
316 			(void) printf("      offset:%lld\n",
317 			    (long long)info->pr_offset);
318 		}
319 	}
320 	return (0);
321 }
322 
323 static void
324 show_files(struct ps_prochandle *Pr)
325 {
326 	struct rlimit rlim;
327 
328 	if (pr_getrlimit(Pr, RLIMIT_NOFILE, &rlim) == 0) {
329 		ulong_t nfd = rlim.rlim_cur;
330 		if (nfd == RLIM_INFINITY)
331 			(void) printf(
332 			    "  Current rlimit: unlimited file descriptors\n");
333 		else
334 			(void) printf(
335 			    "  Current rlimit: %lu file descriptors\n", nfd);
336 	}
337 
338 	(void) Pfdinfo_iter(Pr, show_file, Pr);
339 }
340 
341 
342 static int
343 getflock(struct ps_prochandle *Pr, int fd, struct flock *flock_native)
344 {
345 	int ret;
346 #ifdef _LP64
347 	struct flock64_32 flock_target;
348 
349 	if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) {
350 		copyflock(flock_target, *flock_native);
351 		ret = pr_fcntl(Pr, fd, F_GETLK, &flock_target);
352 		copyflock(*flock_native, flock_target);
353 		return (ret);
354 	}
355 #endif /* _LP64 */
356 	ret = pr_fcntl(Pr, fd, F_GETLK, flock_native);
357 	return (ret);
358 }
359 
360 /* examine open file with fcntl() */
361 static void
362 dofcntl(struct ps_prochandle *Pr, prfdinfo_t *info, int mandatory, int isdoor)
363 {
364 	struct flock flock;
365 	int fileflags;
366 	int fdflags;
367 	int fd;
368 
369 	fd = info->pr_fd;
370 
371 	fileflags = info->pr_fileflags;
372 	fdflags = info->pr_fdflags;
373 
374 	if (fileflags != -1 || fdflags != -1) {
375 		(void) printf("      ");
376 		if (fileflags != -1)
377 			show_fileflags(fileflags);
378 		if (fdflags != -1 && (fdflags & FD_CLOEXEC))
379 			(void) printf(" FD_CLOEXEC");
380 		if (isdoor && (Pstate(Pr) != PS_DEAD))
381 			show_door(Pr, fd);
382 		(void) fputc('\n', stdout);
383 	} else if (isdoor && (Pstate(Pr) != PS_DEAD)) {
384 		(void) printf("    ");
385 		show_door(Pr, fd);
386 		(void) fputc('\n', stdout);
387 	}
388 
389 	flock.l_type = F_WRLCK;
390 	flock.l_whence = 0;
391 	flock.l_start = 0;
392 	flock.l_len = 0;
393 	flock.l_sysid = 0;
394 	flock.l_pid = 0;
395 	if ((Pstate(Pr) != PS_DEAD) && (getflock(Pr, fd, &flock) != -1)) {
396 		if (flock.l_type != F_UNLCK && (flock.l_sysid || flock.l_pid)) {
397 			unsigned long sysid = flock.l_sysid;
398 
399 			(void) printf("      %s %s lock set by",
400 			    mandatory ? "mandatory" : "advisory",
401 			    flock.l_type == F_RDLCK? "read" : "write");
402 			if (sysid)
403 				(void) printf(" system 0x%lX", sysid);
404 			if (flock.l_pid)
405 				(void) printf(" process %d", (int)flock.l_pid);
406 			(void) fputc('\n', stdout);
407 		}
408 	}
409 }
410 
411 #define	ALL_O_FLAGS	O_ACCMODE | O_NDELAY | O_NONBLOCK | O_APPEND | \
412 			O_SYNC | O_DSYNC | O_RSYNC | O_XATTR | \
413 			O_CREAT | O_TRUNC | O_EXCL | O_NOCTTY | O_LARGEFILE
414 
415 static void
416 show_fileflags(int flags)
417 {
418 	char buffer[136];
419 	char *str = buffer;
420 
421 	switch (flags & O_ACCMODE) {
422 	case O_RDONLY:
423 		(void) strcpy(str, "O_RDONLY");
424 		break;
425 	case O_WRONLY:
426 		(void) strcpy(str, "O_WRONLY");
427 		break;
428 	case O_RDWR:
429 		(void) strcpy(str, "O_RDWR");
430 		break;
431 	case O_SEARCH:
432 		(void) strcpy(str, "O_SEARCH");
433 		break;
434 	case O_EXEC:
435 		(void) strcpy(str, "O_EXEC");
436 		break;
437 	default:
438 		(void) sprintf(str, "0x%x", flags & O_ACCMODE);
439 		break;
440 	}
441 
442 	if (flags & O_NDELAY)
443 		(void) strcat(str, "|O_NDELAY");
444 	if (flags & O_NONBLOCK)
445 		(void) strcat(str, "|O_NONBLOCK");
446 	if (flags & O_APPEND)
447 		(void) strcat(str, "|O_APPEND");
448 	if (flags & O_SYNC)
449 		(void) strcat(str, "|O_SYNC");
450 	if (flags & O_DSYNC)
451 		(void) strcat(str, "|O_DSYNC");
452 	if (flags & O_RSYNC)
453 		(void) strcat(str, "|O_RSYNC");
454 	if (flags & O_CREAT)
455 		(void) strcat(str, "|O_CREAT");
456 	if (flags & O_TRUNC)
457 		(void) strcat(str, "|O_TRUNC");
458 	if (flags & O_EXCL)
459 		(void) strcat(str, "|O_EXCL");
460 	if (flags & O_NOCTTY)
461 		(void) strcat(str, "|O_NOCTTY");
462 	if (flags & O_LARGEFILE)
463 		(void) strcat(str, "|O_LARGEFILE");
464 	if (flags & O_XATTR)
465 		(void) strcat(str, "|O_XATTR");
466 	if (flags & ~(ALL_O_FLAGS))
467 		(void) sprintf(str + strlen(str), "|0x%x",
468 		    flags & ~(ALL_O_FLAGS));
469 
470 	(void) printf("%s", str);
471 }
472 
473 /* show process on the other end of a door, socket or fifo */
474 static void
475 show_peer_process(pid_t ppid)
476 {
477 	psinfo_t psinfo;
478 
479 	if (proc_get_psinfo(ppid, &psinfo) == 0)
480 		(void) printf(" %s[%d]", psinfo.pr_fname, (int)ppid);
481 	else
482 		(void) printf(" pid %d", (int)ppid);
483 }
484 
485 /* show door info */
486 static void
487 show_door(struct ps_prochandle *Pr, int fd)
488 {
489 	door_info_t door_info;
490 
491 	if (pr_door_info(Pr, fd, &door_info) != 0)
492 		return;
493 
494 	(void) printf("  door to");
495 	show_peer_process(door_info.di_target);
496 }
497 
498 /*
499  * Print out the socket address pointed to by `sa'.  `len' is only
500  * needed for AF_UNIX sockets.
501  */
502 static void
503 show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len)
504 {
505 	struct sockaddr_in *so_in = (struct sockaddr_in *)(void *)sa;
506 	struct sockaddr_in6 *so_in6 = (struct sockaddr_in6 *)(void *)sa;
507 	struct sockaddr_un *so_un = (struct sockaddr_un *)sa;
508 	char  abuf[INET6_ADDRSTRLEN];
509 	const char *p;
510 
511 	if (len == 0)
512 		return;
513 
514 	switch (sa->sa_family) {
515 	default:
516 		return;
517 	case AF_INET:
518 		(void) printf("\t%s: AF_INET %s  port: %u\n", str,
519 		    inet_ntop(AF_INET, &so_in->sin_addr, abuf, sizeof (abuf)),
520 		    ntohs(so_in->sin_port));
521 		return;
522 	case AF_INET6:
523 		(void) printf("\t%s: AF_INET6 %s  port: %u\n", str,
524 		    inet_ntop(AF_INET6, &so_in6->sin6_addr,
525 		    abuf, sizeof (abuf)),
526 		    ntohs(so_in->sin_port));
527 		return;
528 	case AF_UNIX:
529 		if (len >= sizeof (so_un->sun_family)) {
530 			/* Null terminate */
531 			len -= sizeof (so_un->sun_family);
532 			so_un->sun_path[len] = '\0';
533 			(void) printf("\t%s: AF_UNIX %s\n",
534 			    str, so_un->sun_path);
535 		}
536 		return;
537 	case AF_IMPLINK:	p = "AF_IMPLINK";	break;
538 	case AF_PUP:		p = "AF_PUP";		break;
539 	case AF_CHAOS:		p = "AF_CHAOS";		break;
540 	case AF_NS:		p = "AF_NS";		break;
541 	case AF_NBS:		p = "AF_NBS";		break;
542 	case AF_ECMA:		p = "AF_ECMA";		break;
543 	case AF_DATAKIT:	p = "AF_DATAKIT";	break;
544 	case AF_CCITT:		p = "AF_CCITT";		break;
545 	case AF_SNA:		p = "AF_SNA";		break;
546 	case AF_DECnet:		p = "AF_DECnet";	break;
547 	case AF_DLI:		p = "AF_DLI";		break;
548 	case AF_LAT:		p = "AF_LAT";		break;
549 	case AF_HYLINK:		p = "AF_HYLINK";	break;
550 	case AF_APPLETALK:	p = "AF_APPLETALK";	break;
551 	case AF_NIT:		p = "AF_NIT";		break;
552 	case AF_802:		p = "AF_802";		break;
553 	case AF_OSI:		p = "AF_OSI";		break;
554 	case AF_X25:		p = "AF_X25";		break;
555 	case AF_OSINET:		p = "AF_OSINET";	break;
556 	case AF_GOSIP:		p = "AF_GOSIP";		break;
557 	case AF_IPX:		p = "AF_IPX";		break;
558 	case AF_ROUTE:		p = "AF_ROUTE";		break;
559 	case AF_KEY:		p = "AF_KEY";		break;
560 	case AF_POLICY:		p = "AF_POLICY";	break;
561 	case AF_LINK:		p = "AF_LINK";		break;
562 	}
563 
564 	(void) printf("\t%s: %s\n", str, p);
565 }
566 
567 /*
568  * Print out the process information for the other end of local sockets
569  * and fifos
570  */
571 static void
572 show_ucred(const char *str, ucred_t *cred)
573 {
574 	pid_t upid = ucred_getpid(cred);
575 	zoneid_t uzid = ucred_getzoneid(cred);
576 	char zonename[ZONENAME_MAX];
577 
578 	if ((upid != -1) || (uzid != -1)) {
579 		(void) printf("\t%s:", str);
580 		if (upid != -1) {
581 			show_peer_process(upid);
582 		}
583 		if (uzid != -1) {
584 			if (getzonenamebyid(uzid, zonename, sizeof (zonename))
585 			    != -1) {
586 				(void) printf(" zone: %s[%d]", zonename,
587 				    (int)uzid);
588 			} else {
589 				(void) printf(" zoneid: %d", (int)uzid);
590 			}
591 		}
592 		(void) printf("\n");
593 	}
594 }
595 
596 static void
597 show_socktype(uint_t type)
598 {
599 	static const char *types[] = {
600 		NULL, "DGRAM", "STREAM", NULL, "RAW", "RDM", "SEQPACKET"
601 	};
602 
603 	if (type < sizeof (types) / sizeof (*types) && types[type] != NULL)
604 		(void) printf("\tSOCK_%s\n", types[type]);
605 	else
606 		(void) printf("\tunknown socket type %u\n", type);
607 }
608 
609 #define	BUFSIZE	200
610 static void
611 show_sockopts(struct ps_prochandle *Pr, int fd)
612 {
613 	int val, vlen;
614 	char buf[BUFSIZE];
615 	char buf1[32];
616 	char ipaddr[INET_ADDRSTRLEN];
617 	int i;
618 	in_addr_t nexthop_val;
619 	struct boolopt {
620 		int		level;
621 		int		opt;
622 		const char	*name;
623 	};
624 	static struct boolopt boolopts[] = {
625 	    { SOL_SOCKET, SO_DEBUG,		"SO_DEBUG,"	},
626 	    { SOL_SOCKET, SO_REUSEADDR,		"SO_REUSEADDR,"	},
627 	    { SOL_SOCKET, SO_KEEPALIVE,		"SO_KEEPALIVE,"	},
628 	    { SOL_SOCKET, SO_DONTROUTE,		"SO_DONTROUTE,"	},
629 	    { SOL_SOCKET, SO_BROADCAST,		"SO_BROADCAST,"	},
630 	    { SOL_SOCKET, SO_OOBINLINE,		"SO_OOBINLINE,"	},
631 	    { SOL_SOCKET, SO_DGRAM_ERRIND,	"SO_DGRAM_ERRIND,"},
632 	    { SOL_SOCKET, SO_ALLZONES,		"SO_ALLZONES,"	},
633 	    { SOL_SOCKET, SO_MAC_EXEMPT,	"SO_MAC_EXEMPT," },
634 	    { SOL_SOCKET, SO_MAC_IMPLICIT,	"SO_MAC_IMPLICIT," },
635 	    { SOL_SOCKET, SO_EXCLBIND,		"SO_EXCLBIND," },
636 	    { SOL_SOCKET, SO_VRRP,		"SO_VRRP," },
637 	    { IPPROTO_UDP, UDP_NAT_T_ENDPOINT,	"UDP_NAT_T_ENDPOINT," },
638 	};
639 	struct linger l;
640 
641 	buf[0] = '!';		/* sentinel value, never printed */
642 	buf[1] = '\0';
643 
644 	for (i = 0; i < sizeof (boolopts) / sizeof (boolopts[0]); i++) {
645 		vlen = sizeof (val);
646 		if (pr_getsockopt(Pr, fd, boolopts[i].level, boolopts[i].opt,
647 		    &val, &vlen) == 0 && val != 0)
648 			(void) strlcat(buf, boolopts[i].name, sizeof (buf));
649 	}
650 
651 	vlen = sizeof (l);
652 	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_LINGER, &l, &vlen) == 0 &&
653 	    l.l_onoff != 0) {
654 		(void) snprintf(buf1, sizeof (buf1), "SO_LINGER(%d),",
655 		    l.l_linger);
656 		(void) strlcat(buf, buf1, sizeof (buf));
657 	}
658 
659 	vlen = sizeof (val);
660 	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_SNDBUF, &val, &vlen) == 0) {
661 		(void) snprintf(buf1, sizeof (buf1), "SO_SNDBUF(%d),", val);
662 		(void) strlcat(buf, buf1, sizeof (buf));
663 	}
664 	vlen = sizeof (val);
665 	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_RCVBUF, &val, &vlen) == 0) {
666 		(void) snprintf(buf1, sizeof (buf1), "SO_RCVBUF(%d),", val);
667 		(void) strlcat(buf, buf1, sizeof (buf));
668 	}
669 	vlen = sizeof (nexthop_val);
670 	if (pr_getsockopt(Pr, fd, IPPROTO_IP, IP_NEXTHOP, &nexthop_val,
671 	    &vlen) == 0) {
672 		if (vlen > 0) {
673 			(void) inet_ntop(AF_INET, (void *) &nexthop_val,
674 			    ipaddr, sizeof (ipaddr));
675 			(void) snprintf(buf1, sizeof (buf1), "IP_NEXTHOP(%s),",
676 			    ipaddr);
677 			(void) strlcat(buf, buf1, sizeof (buf));
678 		}
679 	}
680 
681 	buf[strlen(buf) - 1] = '\0'; /* overwrites sentinel if no options */
682 	if (buf[1] != '\0')
683 		(void) printf("\t%s\n", buf+1);
684 }
685 
686 #define	MAXNALLOC	32
687 static void
688 show_sockfilters(struct ps_prochandle *Pr, int fd)
689 {
690 	struct fil_info *fi;
691 	int i = 0, nalloc = 2, len = nalloc * sizeof (*fi);
692 	boolean_t printhdr = B_TRUE;
693 
694 	fi = calloc(nalloc, sizeof (*fi));
695 	if (fi == NULL) {
696 		perror("calloc");
697 		return;
698 	}
699 	/* CONSTCOND */
700 	while (1) {
701 		if (pr_getsockopt(Pr, fd, SOL_FILTER, FIL_LIST, fi, &len) != 0)
702 			break;
703 		/* No filters */
704 		if (len == 0)
705 			break;
706 		/* Make sure buffer was large enough */
707 		if (fi->fi_pos >= nalloc) {
708 			struct fil_info *new;
709 
710 			nalloc = fi->fi_pos + 1;
711 			if (nalloc > MAXNALLOC)
712 				break;
713 			len = nalloc * sizeof (*fi);
714 			new = realloc(fi, nalloc * sizeof (*fi));
715 			if (new == NULL) {
716 				perror("realloc");
717 				break;
718 			}
719 			fi = new;
720 			continue;
721 		}
722 
723 		for (i = 0; (i + 1) * sizeof (*fi) <= len; i++) {
724 			if (fi[i].fi_flags & FILF_BYPASS)
725 				continue;
726 			if (printhdr) {
727 				(void) printf("\tfilters: ");
728 				printhdr = B_FALSE;
729 			}
730 			(void) printf("%s", fi[i].fi_name);
731 			if (fi[i].fi_flags != 0) {
732 				(void) printf("(");
733 				if (fi[i].fi_flags & FILF_AUTO)
734 					(void) printf("auto,");
735 				if (fi[i].fi_flags & FILF_PROG)
736 					(void) printf("prog,");
737 				(void) printf("\b)");
738 			}
739 			if (fi[i].fi_pos == 0) /* last one */
740 				break;
741 			(void) printf(",");
742 		}
743 		if (!printhdr)
744 			(void) printf("\n");
745 		break;
746 	}
747 	free(fi);
748 }
749 
750 /* print peer credentials for sockets and named pipes */
751 static void
752 dopeerucred(struct ps_prochandle *Pr, int fd)
753 {
754 	ucred_t *peercred = NULL;	/* allocated by getpeerucred */
755 
756 	if (pr_getpeerucred(Pr, fd, &peercred) == 0) {
757 		show_ucred("peer", peercred);
758 		ucred_free(peercred);
759 	}
760 }
761 
762 /* the file is a socket */
763 static void
764 dosocket(struct ps_prochandle *Pr, int fd)
765 {
766 	/* A buffer large enough for PATH_MAX size AF_UNIX address */
767 	long buf[(sizeof (short) + PATH_MAX + sizeof (long) - 1)
768 	    / sizeof (long)];
769 	struct sockaddr *sa = (struct sockaddr *)buf;
770 	socklen_t len;
771 	int type, tlen;
772 
773 	tlen = sizeof (type);
774 	if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_TYPE, &type, &tlen) == 0)
775 		show_socktype((uint_t)type);
776 
777 	show_sockopts(Pr, fd);
778 	show_sockfilters(Pr, fd);
779 
780 	len = sizeof (buf);
781 	if (pr_getsockname(Pr, fd, sa, &len) == 0)
782 		show_sockaddr("sockname", sa, len);
783 
784 	len = sizeof (buf);
785 	if (pr_getpeername(Pr, fd, sa, &len) == 0)
786 		show_sockaddr("peername", sa, len);
787 
788 	dopeerucred(Pr, fd);
789 }
790 
791 /* the file is a fifo (aka "named pipe") */
792 static void
793 dofifo(struct ps_prochandle *Pr, int fd)
794 {
795 	dopeerucred(Pr, fd);
796 }
797 
798 /* the file is a TLI endpoint */
799 static void
800 dotli(struct ps_prochandle *Pr, int fd)
801 {
802 	struct strcmd strcmd;
803 
804 	strcmd.sc_len = STRCMDBUFSIZE;
805 	strcmd.sc_timeout = 5;
806 
807 	strcmd.sc_cmd = TI_GETMYNAME;
808 	if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0)
809 		show_sockaddr("sockname", (void *)&strcmd.sc_buf,
810 		    (size_t)strcmd.sc_len);
811 
812 	strcmd.sc_cmd = TI_GETPEERNAME;
813 	if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0)
814 		show_sockaddr("peername", (void *)&strcmd.sc_buf,
815 		    (size_t)strcmd.sc_len);
816 }
817