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