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