1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org>
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/user.h>
35#include <sys/stat.h>
36#include <sys/socket.h>
37#include <sys/socketvar.h>
38#include <sys/sysctl.h>
39#include <sys/queue.h>
40#include <sys/un.h>
41
42#include <netinet/in.h>
43
44#include <arpa/inet.h>
45
46#include <assert.h>
47#include <ctype.h>
48#include <err.h>
49#include <libprocstat.h>
50#include <limits.h>
51#include <pwd.h>
52#include <stdint.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <stddef.h>
56#include <string.h>
57#include <unistd.h>
58#include <netdb.h>
59
60#include "functions.h"
61
62static int 	fsflg,	/* show files on same filesystem as file(s) argument */
63		pflg,	/* show files open by a particular pid */
64		sflg,	/* show socket details */
65		uflg;	/* show files open by a particular (effective) user */
66static int 	checkfile; /* restrict to particular files or filesystems */
67static int	nflg;	/* (numerical) display f.s. and rdev as dev_t */
68static int	mflg;	/* include memory-mapped files */
69static int	vflg;	/* be verbose */
70
71typedef struct devs {
72	struct devs	*next;
73	uint64_t	fsid;
74	uint64_t	ino;
75	const char	*name;
76} DEVS;
77
78static DEVS *devs;
79static char *memf, *nlistf;
80
81static int	getfname(const char *filename);
82static void	dofiles(struct procstat *procstat, struct kinfo_proc *p);
83static void	print_access_flags(int flags);
84static void	print_file_info(struct procstat *procstat,
85    struct filestat *fst, const char *uname, const char *cmd, int pid);
86static void	print_pipe_info(struct procstat *procstat,
87    struct filestat *fst);
88static void	print_pts_info(struct procstat *procstat,
89    struct filestat *fst);
90static void	print_sem_info(struct procstat *procstat,
91    struct filestat *fst);
92static void	print_shm_info(struct procstat *procstat,
93    struct filestat *fst);
94static void	print_socket_info(struct procstat *procstat,
95    struct filestat *fst);
96static void	print_vnode_info(struct procstat *procstat,
97    struct filestat *fst);
98static void	usage(void) __dead2;
99
100int
101do_fstat(int argc, char **argv)
102{
103	struct kinfo_proc *p;
104	struct passwd *passwd;
105	struct procstat *procstat;
106	int arg, ch, what;
107	int cnt, i;
108
109	arg = 0;
110	what = KERN_PROC_PROC;
111	nlistf = memf = NULL;
112	while ((ch = getopt(argc, argv, "fmnp:su:vN:M:")) != -1)
113		switch((char)ch) {
114		case 'f':
115			fsflg = 1;
116			break;
117		case 'M':
118			memf = optarg;
119			break;
120		case 'N':
121			nlistf = optarg;
122			break;
123		case 'm':
124			mflg = 1;
125			break;
126		case 'n':
127			nflg = 1;
128			break;
129		case 'p':
130			if (pflg++)
131				usage();
132			if (!isdigit(*optarg)) {
133				warnx("-p requires a process id");
134				usage();
135			}
136			what = KERN_PROC_PID;
137			arg = atoi(optarg);
138			break;
139		case 's':
140			sflg = 1;
141			break;
142		case 'u':
143			if (uflg++)
144				usage();
145			if (!(passwd = getpwnam(optarg)))
146				errx(1, "%s: unknown uid", optarg);
147			what = KERN_PROC_UID;
148			arg = passwd->pw_uid;
149			break;
150		case 'v':
151			vflg = 1;
152			break;
153		case '?':
154		default:
155			usage();
156		}
157
158	if (*(argv += optind)) {
159		for (; *argv; ++argv) {
160			if (getfname(*argv))
161				checkfile = 1;
162		}
163		if (!checkfile)	/* file(s) specified, but none accessible */
164			exit(1);
165	}
166
167	if (fsflg && !checkfile) {
168		/* -f with no files means use wd */
169		if (getfname(".") == 0)
170			exit(1);
171		checkfile = 1;
172	}
173
174	if (memf != NULL)
175		procstat = procstat_open_kvm(nlistf, memf);
176	else
177		procstat = procstat_open_sysctl();
178	if (procstat == NULL)
179		errx(1, "procstat_open()");
180	p = procstat_getprocs(procstat, what, arg, &cnt);
181	if (p == NULL)
182		errx(1, "procstat_getprocs()");
183
184	/*
185	 * Print header.
186	 */
187	if (nflg)
188		printf("%s",
189"USER     CMD          PID   FD  DEV    INUM       MODE SZ|DV R/W");
190	else
191		printf("%s",
192"USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W");
193	if (checkfile && fsflg == 0)
194		printf(" NAME\n");
195	else
196		putchar('\n');
197
198	/*
199	 * Go through the process list.
200	 */
201	for (i = 0; i < cnt; i++) {
202		if (p[i].ki_stat == SZOMB)
203			continue;
204		dofiles(procstat, &p[i]);
205	}
206	procstat_freeprocs(procstat, p);
207	procstat_close(procstat);
208	return (0);
209}
210
211static void
212dofiles(struct procstat *procstat, struct kinfo_proc *kp)
213{
214	const char *cmd;
215	const char *uname;
216	struct filestat *fst;
217	struct filestat_list *head;
218	int pid;
219
220	uname = user_from_uid(kp->ki_uid, 0);
221	pid = kp->ki_pid;
222	cmd = kp->ki_comm;
223
224	head = procstat_getfiles(procstat, kp, mflg);
225	if (head == NULL)
226		return;
227	STAILQ_FOREACH(fst, head, next)
228		print_file_info(procstat, fst, uname, cmd, pid);
229	procstat_freefiles(procstat, head);
230}
231
232
233static void
234print_file_info(struct procstat *procstat, struct filestat *fst,
235    const char *uname, const char *cmd, int pid)
236{
237	struct vnstat vn;
238	DEVS *d;
239	const char *filename;
240	int error, fsmatch = 0;
241	char errbuf[_POSIX2_LINE_MAX];
242
243	filename = NULL;
244	if (checkfile != 0) {
245		if (fst->fs_type != PS_FST_TYPE_VNODE &&
246		    fst->fs_type != PS_FST_TYPE_FIFO)
247			return;
248		error = procstat_get_vnode_info(procstat, fst, &vn, errbuf);
249		if (error != 0)
250			return;
251
252		for (d = devs; d != NULL; d = d->next)
253			if (d->fsid == vn.vn_fsid) {
254				fsmatch = 1;
255				if (d->ino == vn.vn_fileid) {
256					filename = d->name;
257					break;
258				}
259			}
260		if (fsmatch == 0 || (filename == NULL && fsflg == 0))
261			return;
262	}
263
264	/*
265	 * Print entry prefix.
266	 */
267	printf("%-8.8s %-10s %5d", uname, cmd, pid);
268	if (fst->fs_uflags & PS_FST_UFLAG_TEXT)
269		printf(" text");
270	else if (fst->fs_uflags & PS_FST_UFLAG_CDIR)
271		printf("   wd");
272	else if (fst->fs_uflags & PS_FST_UFLAG_RDIR)
273		printf(" root");
274	else if (fst->fs_uflags & PS_FST_UFLAG_TRACE)
275		printf("   tr");
276	else if (fst->fs_uflags & PS_FST_UFLAG_MMAP)
277		printf(" mmap");
278	else if (fst->fs_uflags & PS_FST_UFLAG_JAIL)
279		printf(" jail");
280	else if (fst->fs_uflags & PS_FST_UFLAG_CTTY)
281		printf(" ctty");
282	else
283		printf(" %4d", fst->fs_fd);
284
285	/*
286	 * Print type-specific data.
287	 */
288	switch (fst->fs_type) {
289	case PS_FST_TYPE_FIFO:
290	case PS_FST_TYPE_VNODE:
291		print_vnode_info(procstat, fst);
292		break;
293	case PS_FST_TYPE_SOCKET:
294		print_socket_info(procstat, fst);
295		break;
296	case PS_FST_TYPE_PIPE:
297		print_pipe_info(procstat, fst);
298		break;
299	case PS_FST_TYPE_PTS:
300		print_pts_info(procstat, fst);
301		break;
302	case PS_FST_TYPE_SHM:
303		print_shm_info(procstat, fst);
304		break;
305	case PS_FST_TYPE_SEM:
306		print_sem_info(procstat, fst);
307		break;
308	case PS_FST_TYPE_DEV:
309		break;
310	default:
311		if (vflg)
312			fprintf(stderr,
313			    "unknown file type %d for file %d of pid %d\n",
314			    fst->fs_type, fst->fs_fd, pid);
315	}
316	if (filename && !fsflg)
317		printf("  %s", filename);
318	putchar('\n');
319}
320
321static char *
322addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen)
323{
324	char buffer2[INET6_ADDRSTRLEN];
325	struct sockaddr_in6 *sin6;
326	struct sockaddr_in *sin;
327	struct sockaddr_un *sun;
328
329	switch (ss->ss_family) {
330	case AF_LOCAL:
331		sun = (struct sockaddr_un *)ss;
332		if (strlen(sun->sun_path) == 0)
333			strlcpy(buffer, "-", buflen);
334		else
335			strlcpy(buffer, sun->sun_path, buflen);
336		break;
337
338	case AF_INET:
339		sin = (struct sockaddr_in *)ss;
340		if (sin->sin_addr.s_addr == INADDR_ANY)
341			snprintf(buffer, buflen, "%s:%d", "*",
342			    ntohs(sin->sin_port));
343		else if (inet_ntop(AF_INET, &sin->sin_addr, buffer2,
344		    sizeof(buffer2)) != NULL)
345			snprintf(buffer, buflen, "%s:%d", buffer2,
346		            ntohs(sin->sin_port));
347		break;
348
349	case AF_INET6:
350		sin6 = (struct sockaddr_in6 *)ss;
351		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
352			snprintf(buffer, buflen, "%s.%d", "*",
353			    ntohs(sin6->sin6_port));
354		else if (inet_ntop(AF_INET6, &sin6->sin6_addr, buffer2,
355		    sizeof(buffer2)) != NULL)
356			snprintf(buffer, buflen, "%s.%d", buffer2,
357			    ntohs(sin6->sin6_port));
358		else
359			strlcpy(buffer, "-", buflen);
360		break;
361
362	default:
363		strlcpy(buffer, "", buflen);
364		break;
365	}
366	return buffer;
367}
368
369
370static void
371print_socket_info(struct procstat *procstat, struct filestat *fst)
372{
373	static const char *stypename[] = {
374		"unused",	/* 0 */
375		"stream",	/* 1 */
376		"dgram",	/* 2 */
377		"raw",		/* 3 */
378		"rdm",		/* 4 */
379		"seqpak"	/* 5 */
380	};
381#define STYPEMAX 5
382	struct sockstat sock;
383	struct protoent *pe;
384	char errbuf[_POSIX2_LINE_MAX];
385	char src_addr[PATH_MAX], dst_addr[PATH_MAX];
386	struct sockaddr_un *sun;
387	int error;
388	static int isopen;
389
390	error = procstat_get_socket_info(procstat, fst, &sock, errbuf);
391	if (error != 0) {
392		printf("* error");
393		return;
394	}
395	if (sock.type > STYPEMAX)
396		printf("* %s ?%d", sock.dname, sock.type);
397	else
398		printf("* %s %s", sock.dname, stypename[sock.type]);
399
400	/*
401	 * protocol specific formatting
402	 *
403	 * Try to find interesting things to print.  For internet and unix
404	 * sockets, its the address of the socket pcb.  For unix it is also the
405	 * address of the connected pcb (if connected).  Otherwise just print
406	 * the protocol number and address of the socket itself.
407	 * The idea is not to duplicate netstat, but to make available enough
408	 * information for further analysis.
409	 */
410	switch (sock.dom_family) {
411	case AF_INET:
412	case AF_INET6:
413		if (!isopen)
414			setprotoent(++isopen);
415		if ((pe = getprotobynumber(sock.proto)) != NULL)
416			printf(" %s", pe->p_name);
417		else
418			printf(" %d", sock.proto);
419		if (sock.so_pcb != 0)
420			printf(" %lx", (u_long)sock.so_pcb);
421		if (!sflg)
422			break;
423		printf(" %s <-> %s",
424		    addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)),
425		    addr_to_string(&sock.sa_peer, dst_addr, sizeof(dst_addr)));
426		break;
427	case AF_UNIX:
428		/* print address of pcb and connected pcb */
429		if (sock.so_pcb != 0) {
430			printf(" %lx", (u_long)sock.so_pcb);
431			if (sock.unp_conn) {
432				char shoconn[4], *cp;
433
434				cp = shoconn;
435				if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE))
436					*cp++ = '<';
437				*cp++ = '-';
438				if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE))
439					*cp++ = '>';
440				*cp = '\0';
441				printf(" %s %lx", shoconn,
442				    (u_long)sock.unp_conn);
443			}
444		}
445		if (!sflg)
446			break;
447		sun = (struct sockaddr_un *)&sock.sa_local;
448		/*
449		 * While generally we like to print two addresses,
450		 * local and peer, for sockets, it turns out to be
451		 * more useful to print the first non-null address for
452		 * local sockets, as typically they aren't bound and
453		 *  connected, and the path strings can get long.
454		 */
455		if (sun->sun_path[0] != 0)
456			addr_to_string(&sock.sa_local,
457			    src_addr, sizeof(src_addr));
458		else
459			addr_to_string(&sock.sa_peer,
460			    src_addr, sizeof(src_addr));
461		printf(" %s", src_addr);
462		break;
463	default:
464		/* print protocol number and socket address */
465		printf(" %d %lx", sock.proto, (u_long)sock.so_addr);
466	}
467}
468
469static void
470print_pipe_info(struct procstat *procstat, struct filestat *fst)
471{
472	struct pipestat ps;
473	char errbuf[_POSIX2_LINE_MAX];
474	int error;
475
476	error = procstat_get_pipe_info(procstat, fst, &ps, errbuf);
477	if (error != 0) {
478		printf("* error");
479		return;
480	}
481	printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer);
482	printf(" %6zd", ps.buffer_cnt);
483	print_access_flags(fst->fs_fflags);
484}
485
486static void
487print_pts_info(struct procstat *procstat, struct filestat *fst)
488{
489	struct ptsstat pts;
490	char errbuf[_POSIX2_LINE_MAX];
491	int error;
492
493	error = procstat_get_pts_info(procstat, fst, &pts, errbuf);
494	if (error != 0) {
495		printf("* error");
496		return;
497	}
498	printf("* pseudo-terminal master ");
499	if (nflg || !*pts.devname) {
500		printf("%#10jx", (uintmax_t)pts.dev);
501	} else {
502		printf("%10s", pts.devname);
503	}
504	print_access_flags(fst->fs_fflags);
505}
506
507static void
508print_sem_info(struct procstat *procstat, struct filestat *fst)
509{
510	struct semstat sem;
511	char errbuf[_POSIX2_LINE_MAX];
512	char mode[15];
513	int error;
514
515	error = procstat_get_sem_info(procstat, fst, &sem, errbuf);
516	if (error != 0) {
517		printf("* error");
518		return;
519	}
520	if (nflg) {
521		printf("             ");
522		(void)snprintf(mode, sizeof(mode), "%o", sem.mode);
523	} else {
524		printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-");
525		strmode(sem.mode, mode);
526	}
527	printf(" %10s %6u", mode, sem.value);
528	print_access_flags(fst->fs_fflags);
529}
530
531static void
532print_shm_info(struct procstat *procstat, struct filestat *fst)
533{
534	struct shmstat shm;
535	char errbuf[_POSIX2_LINE_MAX];
536	char mode[15];
537	int error;
538
539	error = procstat_get_shm_info(procstat, fst, &shm, errbuf);
540	if (error != 0) {
541		printf("* error");
542		return;
543	}
544	if (nflg) {
545		printf("             ");
546		(void)snprintf(mode, sizeof(mode), "%o", shm.mode);
547	} else {
548		printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-");
549		strmode(shm.mode, mode);
550	}
551	printf(" %10s %6ju", mode, shm.size);
552	print_access_flags(fst->fs_fflags);
553}
554
555static void
556print_vnode_info(struct procstat *procstat, struct filestat *fst)
557{
558	struct vnstat vn;
559	char errbuf[_POSIX2_LINE_MAX];
560	char mode[15];
561	const char *badtype;
562	int error;
563
564	badtype = NULL;
565	error = procstat_get_vnode_info(procstat, fst, &vn, errbuf);
566	if (error != 0)
567		badtype = errbuf;
568	else if (vn.vn_type == PS_FST_VTYPE_VBAD)
569		badtype = "bad";
570	else if (vn.vn_type == PS_FST_VTYPE_VNON)
571		badtype = "none";
572	if (badtype != NULL) {
573		printf(" -         -  %10s    -", badtype);
574		return;
575	}
576
577	if (nflg)
578		printf(" %#5jx", (uintmax_t)vn.vn_fsid);
579	else if (vn.vn_mntdir != NULL)
580		(void)printf(" %-8s", vn.vn_mntdir);
581
582	/*
583	 * Print access mode.
584	 */
585	if (nflg)
586		(void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode);
587	else {
588		strmode(vn.vn_mode, mode);
589	}
590	(void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode);
591
592	if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) {
593		if (nflg || !*vn.vn_devname)
594			printf(" %#6jx", (uintmax_t)vn.vn_dev);
595		else {
596			printf(" %6s", vn.vn_devname);
597		}
598	} else
599		printf(" %6ju", (uintmax_t)vn.vn_size);
600	print_access_flags(fst->fs_fflags);
601}
602
603static void
604print_access_flags(int flags)
605{
606	char rw[3];
607
608	rw[0] = '\0';
609	if (flags & PS_FST_FFLAG_READ)
610		strcat(rw, "r");
611	if (flags & PS_FST_FFLAG_WRITE)
612		strcat(rw, "w");
613	printf(" %2s", rw);
614}
615
616int
617getfname(const char *filename)
618{
619	struct stat statbuf;
620	DEVS *cur;
621
622	if (stat(filename, &statbuf)) {
623		warn("%s", filename);
624		return (0);
625	}
626	if ((cur = malloc(sizeof(DEVS))) == NULL)
627		err(1, NULL);
628	cur->next = devs;
629	devs = cur;
630
631	cur->ino = statbuf.st_ino;
632	cur->fsid = statbuf.st_dev;
633	cur->name = filename;
634	return (1);
635}
636
637static void
638usage(void)
639{
640	(void)fprintf(stderr,
641 "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n");
642	exit(1);
643}
644