fstat.c revision 232233
1248619Sdes/*-
260573Skris * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org>
360573Skris * Copyright (c) 1988, 1993
460573Skris *	The Regents of the University of California.  All rights reserved.
560573Skris *
660573Skris * Redistribution and use in source and binary forms, with or without
760573Skris * modification, are permitted provided that the following conditions
860573Skris * are met:
960573Skris * 1. Redistributions of source code must retain the above copyright
1060573Skris *    notice, this list of conditions and the following disclaimer.
1160573Skris * 2. Redistributions in binary form must reproduce the above copyright
1260573Skris *    notice, this list of conditions and the following disclaimer in the
1360573Skris *    documentation and/or other materials provided with the distribution.
1460573Skris * 4. Neither the name of the University nor the names of its contributors
1560573Skris *    may be used to endorse or promote products derived from this software
1660573Skris *    without specific prior written permission.
1760573Skris *
1860573Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1960573Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2060573Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2160573Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2260573Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2360573Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2460573Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2565674Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2660573Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27162856Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2860573Skris * SUCH DAMAGE.
29162856Sdes */
30181111Sdes
31181111Sdes#include <sys/cdefs.h>
32162856Sdes__FBSDID("$FreeBSD: head/usr.bin/fstat/fstat.c 232233 2012-02-27 17:39:34Z pluknet $");
33181111Sdes
34162856Sdes#include <sys/param.h>
35162856Sdes#include <sys/user.h>
36162856Sdes#include <sys/stat.h>
37181111Sdes#include <sys/socket.h>
38162856Sdes#include <sys/socketvar.h>
39197679Sdes#include <sys/sysctl.h>
40162856Sdes#include <sys/queue.h>
4176262Sgreen
4260573Skris#include <netinet/in.h>
4376262Sgreen
44162856Sdes#include <assert.h>
4560573Skris#include <ctype.h>
4660573Skris#include <err.h>
47162856Sdes#include <libprocstat.h>
48162856Sdes#include <limits.h>
4960573Skris#include <pwd.h>
5060573Skris#include <stdint.h>
5176262Sgreen#include <stdio.h>
52147005Sdes#include <stdlib.h>
53162856Sdes#include <stddef.h>
5460573Skris#include <string.h>
55124211Sdes#include <unistd.h>
56124211Sdes#include <netdb.h>
57124211Sdes
58162856Sdes#include "functions.h"
59124211Sdes
6060573Skrisstatic int 	fsflg,	/* show files on same filesystem as file(s) argument */
6160573Skris		pflg,	/* show files open by a particular pid */
6276262Sgreen		uflg;	/* show files open by a particular (effective) user */
63124211Sdesstatic int 	checkfile; /* restrict to particular files or filesystems */
64147005Sdesstatic int	nflg;	/* (numerical) display f.s. and rdev as dev_t */
6560573Skrisstatic int	mflg;	/* include memory-mapped files */
6698684Sdesstatic int	vflg;	/* be verbose */
6798684Sdes
6898684Sdestypedef struct devs {
6998684Sdes	struct devs	*next;
7098684Sdes	uint32_t	fsid;
7198684Sdes	uint64_t	ino;
7298684Sdes	const char	*name;
73124211Sdes} DEVS;
74124211Sdes
75124211Sdesstatic DEVS *devs;
76192595Sdesstatic char *memf, *nlistf;
77192595Sdes
78192595Sdesstatic int	getfname(const char *filename);
7998684Sdesstatic void	dofiles(struct procstat *procstat, struct kinfo_proc *p);
8098684Sdesstatic void	print_access_flags(int flags);
8198684Sdesstatic void	print_file_info(struct procstat *procstat,
8298684Sdes    struct filestat *fst, const char *uname, const char *cmd, int pid);
83124211Sdesstatic void	print_pipe_info(struct procstat *procstat,
84124211Sdes    struct filestat *fst);
85124211Sdesstatic void	print_pts_info(struct procstat *procstat,
86192595Sdes    struct filestat *fst);
87192595Sdesstatic void	print_socket_info(struct procstat *procstat,
88192595Sdes    struct filestat *fst);
8998684Sdesstatic void	print_vnode_info(struct procstat *procstat,
9098684Sdes    struct filestat *fst);
9198684Sdesstatic void	usage(void) __dead2;
9298684Sdes
9369591Sgreenint
9469591Sgreendo_fstat(int argc, char **argv)
9560573Skris{
9660573Skris	struct kinfo_proc *p;
9792559Sdes	struct passwd *passwd;
9892559Sdes	struct procstat *procstat;
9960573Skris	int arg, ch, what;
10060573Skris	int cnt, i;
101248619Sdes
102248619Sdes	arg = 0;
103248619Sdes	what = KERN_PROC_PROC;
104248619Sdes	nlistf = memf = NULL;
10560573Skris	while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1)
106181111Sdes		switch((char)ch) {
107181111Sdes		case 'f':
108181111Sdes			fsflg = 1;
109181111Sdes			break;
110181111Sdes		case 'M':
111181111Sdes			memf = optarg;
112181111Sdes			break;
113181111Sdes		case 'N':
114181111Sdes			nlistf = optarg;
115181111Sdes			break;
116181111Sdes		case 'm':
117181111Sdes			mflg = 1;
118181111Sdes			break;
119181111Sdes		case 'n':
120240075Sdes			nflg = 1;
121181111Sdes			break;
122181111Sdes		case 'p':
123181111Sdes			if (pflg++)
124181111Sdes				usage();
125181111Sdes			if (!isdigit(*optarg)) {
126181111Sdes				warnx("-p requires a process id");
127181111Sdes				usage();
128181111Sdes			}
129181111Sdes			what = KERN_PROC_PID;
130181111Sdes			arg = atoi(optarg);
131181111Sdes			break;
132181111Sdes		case 'u':
133181111Sdes			if (uflg++)
134181111Sdes				usage();
135181111Sdes			if (!(passwd = getpwnam(optarg)))
136181111Sdes				errx(1, "%s: unknown uid", optarg);
137181111Sdes			what = KERN_PROC_UID;
138181111Sdes			arg = passwd->pw_uid;
139181111Sdes			break;
140181111Sdes		case 'v':
141181111Sdes			vflg = 1;
142181111Sdes			break;
143181111Sdes		case '?':
144181111Sdes		default:
145181111Sdes			usage();
146181111Sdes		}
147181111Sdes
148181111Sdes	if (*(argv += optind)) {
149181111Sdes		for (; *argv; ++argv) {
150181111Sdes			if (getfname(*argv))
151181111Sdes				checkfile = 1;
152181111Sdes		}
153181111Sdes		if (!checkfile)	/* file(s) specified, but none accessible */
154181111Sdes			exit(1);
155181111Sdes	}
156181111Sdes
157181111Sdes	if (fsflg && !checkfile) {
158181111Sdes		/* -f with no files means use wd */
159181111Sdes		if (getfname(".") == 0)
160181111Sdes			exit(1);
161181111Sdes		checkfile = 1;
162181111Sdes	}
163181111Sdes
164181111Sdes	if (memf != NULL)
165181111Sdes		procstat = procstat_open_kvm(nlistf, memf);
166181111Sdes	else
167181111Sdes		procstat = procstat_open_sysctl();
168181111Sdes	if (procstat == NULL)
169181111Sdes		errx(1, "procstat_open()");
170181111Sdes	p = procstat_getprocs(procstat, what, arg, &cnt);
17160573Skris	if (p == NULL)
17269591Sgreen		errx(1, "procstat_getprocs()");
17360573Skris
174126277Sdes	/*
175126277Sdes	 * Print header.
17660573Skris	 */
17792559Sdes	if (nflg)
17860573Skris		printf("%s",
17969591Sgreen"USER     CMD          PID   FD  DEV    INUM       MODE SZ|DV R/W");
18060573Skris	else
18160573Skris		printf("%s",
182162856Sdes"USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W");
18392559Sdes	if (checkfile && fsflg == 0)
18492559Sdes		printf(" NAME\n");
18560573Skris	else
18669591Sgreen		putchar('\n');
18776262Sgreen
188106130Sdes	/*
189221420Sdes	 * Go through the process list.
19092559Sdes	 */
19160573Skris	for (i = 0; i < cnt; i++) {
19269591Sgreen		if (p[i].ki_stat == SZOMB)
19369591Sgreen			continue;
19469591Sgreen		dofiles(procstat, &p[i]);
19560573Skris	}
19669591Sgreen	procstat_freeprocs(procstat, p);
197106130Sdes	procstat_close(procstat);
19860573Skris	return (0);
19960573Skris}
20060573Skris
20160573Skrisstatic void
20260573Skrisdofiles(struct procstat *procstat, struct kinfo_proc *kp)
20360573Skris{
204106130Sdes	const char *cmd;
20560573Skris	const char *uname;
20660573Skris	struct filestat *fst;
20760573Skris	struct filestat_list *head;
20860573Skris	int pid;
20960573Skris
21060573Skris	uname = user_from_uid(kp->ki_uid, 0);
21160573Skris	pid = kp->ki_pid;
21260573Skris	cmd = kp->ki_comm;
21360573Skris
21460573Skris	head = procstat_getfiles(procstat, kp, mflg);
21560573Skris	if (head == NULL)
216162856Sdes		return;
21792559Sdes	STAILQ_FOREACH(fst, head, next)
21892559Sdes		print_file_info(procstat, fst, uname, cmd, pid);
21960573Skris	procstat_freefiles(procstat, head);
22069591Sgreen}
22169591Sgreen
22276262Sgreen
22360573Skrisstatic void
22499053Sdesprint_file_info(struct procstat *procstat, struct filestat *fst,
22599053Sdes    const char *uname, const char *cmd, int pid)
22699053Sdes{
22760573Skris	struct vnstat vn;
228231584Sed	DEVS *d;
229231584Sed	const char *filename;
23099053Sdes	int error, fsmatch = 0;
23199053Sdes	char errbuf[_POSIX2_LINE_MAX];
23269591Sgreen
23369591Sgreen	filename = NULL;
23460573Skris	if (checkfile != 0) {
235221420Sdes		if (fst->fs_type != PS_FST_TYPE_VNODE &&
236221420Sdes		    fst->fs_type != PS_FST_TYPE_FIFO)
237221420Sdes			return;
23860573Skris		error = procstat_get_vnode_info(procstat, fst, &vn, errbuf);
23976262Sgreen		if (error != 0)
24060573Skris			return;
24176262Sgreen
24276262Sgreen		for (d = devs; d != NULL; d = d->next)
24376262Sgreen			if (d->fsid == vn.vn_fsid) {
24476262Sgreen				fsmatch = 1;
24569591Sgreen				if ((unsigned)d->ino == vn.vn_fileid) {
24698684Sdes					filename = d->name;
247128460Sdes					break;
24898684Sdes				}
24969591Sgreen			}
25069591Sgreen		if (fsmatch == 0 || (filename == NULL && fsflg == 0))
25169591Sgreen			return;
252137019Sdes	}
253124211Sdes
254147005Sdes	/*
255147005Sdes	 * Print entry prefix.
256147005Sdes	 */
25760573Skris	printf("%-8.8s %-10s %5d", uname, cmd, pid);
258157019Sdes	if (fst->fs_uflags & PS_FST_UFLAG_TEXT)
259157019Sdes		printf(" text");
260157019Sdes	else if (fst->fs_uflags & PS_FST_UFLAG_CDIR)
261157019Sdes		printf("   wd");
262137019Sdes	else if (fst->fs_uflags & PS_FST_UFLAG_RDIR)
26398684Sdes		printf(" root");
26469591Sgreen	else if (fst->fs_uflags & PS_FST_UFLAG_TRACE)
26592559Sdes		printf("   tr");
26698684Sdes	else if (fst->fs_uflags & PS_FST_UFLAG_MMAP)
26798684Sdes		printf(" mmap");
268181111Sdes	else if (fst->fs_uflags & PS_FST_UFLAG_JAIL)
269248619Sdes		printf(" jail");
270248619Sdes	else if (fst->fs_uflags & PS_FST_UFLAG_CTTY)
27192559Sdes		printf(" ctty");
27292559Sdes	else
27392559Sdes		printf(" %4d", fst->fs_fd);
27492559Sdes
27592559Sdes	/*
27660573Skris	 * Print type-specific data.
27799053Sdes	 */
27899053Sdes	switch (fst->fs_type) {
279231584Sed	case PS_FST_TYPE_FIFO:
280231584Sed	case PS_FST_TYPE_VNODE:
281231584Sed		print_vnode_info(procstat, fst);
282231584Sed		break;
283231584Sed	case PS_FST_TYPE_SOCKET:
284231584Sed		print_socket_info(procstat, fst);
285231584Sed		break;
286231584Sed	case PS_FST_TYPE_PIPE:
287231584Sed		print_pipe_info(procstat, fst);
288231584Sed		break;
289231584Sed	case PS_FST_TYPE_PTS:
290231584Sed		print_pts_info(procstat, fst);
291231584Sed		break;
292231584Sed	default:
293231584Sed		if (vflg)
294231584Sed			fprintf(stderr,
295231584Sed			    "unknown file type %d for file %d of pid %d\n",
29699053Sdes			    fst->fs_type, fst->fs_fd, pid);
29799053Sdes	}
29876262Sgreen	if (filename && !fsflg)
29992559Sdes		printf("  %s", filename);
300192595Sdes	putchar('\n');
301192595Sdes}
302192595Sdes
303124211Sdesstatic void
304124211Sdesprint_socket_info(struct procstat *procstat, struct filestat *fst)
305192595Sdes{
306124211Sdes	static const char *stypename[] = {
307124211Sdes		"unused",	/* 0 */
308124211Sdes		"stream",	/* 1 */
309124211Sdes		"dgram",	/* 2 */
31076262Sgreen		"raw",		/* 3 */
311226046Sdes		"rdm",		/* 4 */
31268704Sgreen		"seqpak"	/* 5 */
31376262Sgreen	};
314248619Sdes#define STYPEMAX 5
315181111Sdes	struct sockstat sock;
31669591Sgreen	struct protoent *pe;
31769591Sgreen	char errbuf[_POSIX2_LINE_MAX];
31869591Sgreen	int error;
319248619Sdes	static int isopen;
32069591Sgreen
32169591Sgreen	error = procstat_get_socket_info(procstat, fst, &sock, errbuf);
32269591Sgreen	if (error != 0) {
32369591Sgreen		printf("* error");
32469591Sgreen		return;
32569591Sgreen	}
32669591Sgreen	if (sock.type > STYPEMAX)
327248619Sdes		printf("* %s ?%d", sock.dname, sock.type);
328248619Sdes	else
32969591Sgreen		printf("* %s %s", sock.dname, stypename[sock.type]);
33092559Sdes
331248619Sdes	/*
33292559Sdes	 * protocol specific formatting
33376262Sgreen	 *
33476262Sgreen	 * Try to find interesting things to print.  For tcp, the interesting
33576262Sgreen	 * thing is the address of the tcpcb, for udp and others, just the
336248619Sdes	 * inpcb (socket pcb).  For unix domain, its the address of the socket
337248619Sdes	 * pcb and the address of the connected pcb (if connected).  Otherwise
33869591Sgreen	 * just print the protocol number and address of the socket itself.
33976262Sgreen	 * The idea is not to duplicate netstat, but to make available enough
340113911Sdes	 * information for further analysis.
341147005Sdes	 */
34276262Sgreen	switch (sock.dom_family) {
343147005Sdes	case AF_INET:
344147005Sdes	case AF_INET6:
345147005Sdes		if (!isopen)
346147005Sdes			setprotoent(++isopen);
34760573Skris		if ((pe = getprotobynumber(sock.proto)) != NULL)
348248619Sdes			printf(" %s", pe->p_name);
349248619Sdes		else
350248619Sdes			printf(" %d", sock.proto);
351248619Sdes		if (sock.proto == IPPROTO_TCP ) {
352248619Sdes			if (sock.inp_ppcb != 0)
353248619Sdes				printf(" %lx", (u_long)sock.inp_ppcb);
354248619Sdes		}
355248619Sdes		else if (sock.so_pcb != 0)
356248619Sdes			printf(" %lx", (u_long)sock.so_pcb);
357248619Sdes		break;
358248619Sdes	case AF_UNIX:
359248619Sdes		/* print address of pcb and connected pcb */
360248619Sdes		if (sock.so_pcb != 0) {
36198941Sdes			printf(" %lx", (u_long)sock.so_pcb);
362147005Sdes			if (sock.unp_conn) {
363147005Sdes				char shoconn[4], *cp;
364147005Sdes
365147005Sdes				cp = shoconn;
366147005Sdes				if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE))
367147005Sdes					*cp++ = '<';
368147005Sdes				*cp++ = '-';
369147005Sdes				if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE))
370147005Sdes					*cp++ = '>';
371149753Sdes				*cp = '\0';
372147005Sdes				printf(" %s %lx", shoconn,
373147005Sdes				    (u_long)sock.unp_conn);
374124211Sdes                        }
37598941Sdes		}
376106130Sdes		break;
377106130Sdes	default:
378106130Sdes		/* print protocol number and socket address */
379248619Sdes		printf(" %d %lx", sock.proto, (u_long)sock.so_addr);
380106130Sdes	}
381106130Sdes}
382106130Sdes
38392559Sdesstatic void
38492559Sdesprint_pipe_info(struct procstat *procstat, struct filestat *fst)
38592559Sdes{
38692559Sdes	struct pipestat ps;
38792559Sdes	char errbuf[_POSIX2_LINE_MAX];
38892559Sdes	int error;
38992559Sdes
39092559Sdes	error = procstat_get_pipe_info(procstat, fst, &ps, errbuf);
39192559Sdes	if (error != 0) {
392181111Sdes		printf("* error");
393181111Sdes		return;
394226046Sdes	}
395226046Sdes	printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer);
396181111Sdes	printf(" %6zd", ps.buffer_cnt);
397181111Sdes	print_access_flags(fst->fs_fflags);
398147005Sdes}
399147005Sdes
400147005Sdesstatic void
40192559Sdesprint_pts_info(struct procstat *procstat, struct filestat *fst)
402147005Sdes{
403248619Sdes	struct ptsstat pts;
404248619Sdes	char errbuf[_POSIX2_LINE_MAX];
405248619Sdes	int error;
40692559Sdes
40792559Sdes	error = procstat_get_pts_info(procstat, fst, &pts, errbuf);
408248619Sdes	if (error != 0) {
40992559Sdes		printf("* error");
41092559Sdes		return;
41192559Sdes	}
41292559Sdes	printf("* pseudo-terminal master ");
41376262Sgreen	if (nflg || !*pts.devname) {
41469591Sgreen		printf("%#10jx", (uintmax_t)pts.dev);
415248619Sdes	} else {
416248619Sdes		printf("%10s", pts.devname);
417248619Sdes	}
418248619Sdes	print_access_flags(fst->fs_fflags);
419248619Sdes}
420248619Sdes
421248619Sdesstatic void
422248619Sdesprint_vnode_info(struct procstat *procstat, struct filestat *fst)
423248619Sdes{
424248619Sdes	struct vnstat vn;
425248619Sdes	char errbuf[_POSIX2_LINE_MAX];
426248619Sdes	char mode[15];
427248619Sdes	const char *badtype;
428248619Sdes	int error;
429248619Sdes
430248619Sdes	badtype = NULL;
431248619Sdes	error = procstat_get_vnode_info(procstat, fst, &vn, errbuf);
432248619Sdes	if (error != 0)
433248619Sdes		badtype = errbuf;
434248619Sdes	else if (vn.vn_type == PS_FST_VTYPE_VBAD)
435248619Sdes		badtype = "bad";
436248619Sdes	else if (vn.vn_type == PS_FST_VTYPE_VNON)
437248619Sdes		badtype = "none";
43892559Sdes	if (badtype != NULL) {
439248619Sdes		printf(" -         -  %10s    -", badtype);
44060573Skris		return;
44192559Sdes	}
44269591Sgreen
443248619Sdes	if (nflg)
44460573Skris		printf(" %#5jx", (uintmax_t)vn.vn_fsid);
44592559Sdes	else if (vn.vn_mntdir != NULL)
44698684Sdes		(void)printf(" %-8s", vn.vn_mntdir);
44798684Sdes
44869591Sgreen	/*
449248619Sdes	 * Print access mode.
450248619Sdes	 */
451248619Sdes	if (nflg)
452248619Sdes		(void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode);
453248619Sdes	else {
454248619Sdes		strmode(vn.vn_mode, mode);
455248619Sdes	}
456248619Sdes	(void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode);
457248619Sdes
45869591Sgreen	if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) {
45992559Sdes		if (nflg || !*vn.vn_devname)
46092559Sdes			printf(" %#6jx", (uintmax_t)vn.vn_dev);
46192559Sdes		else {
46269591Sgreen			printf(" %6s", vn.vn_devname);
46360573Skris		}
46460573Skris	} else
46592559Sdes		printf(" %6ju", (uintmax_t)vn.vn_size);
466248619Sdes	print_access_flags(fst->fs_fflags);
46769591Sgreen}
46898684Sdes
46998684Sdesstatic void
47069591Sgreenprint_access_flags(int flags)
47198684Sdes{
47298684Sdes	char rw[3];
47398684Sdes
474248619Sdes	rw[0] = '\0';
475248619Sdes	if (flags & PS_FST_FFLAG_READ)
47698684Sdes		strcat(rw, "r");
47798684Sdes	if (flags & PS_FST_FFLAG_WRITE)
47898684Sdes		strcat(rw, "w");
47969591Sgreen	printf(" %2s", rw);
48069591Sgreen}
481181111Sdes
482248619Sdesint
483248619Sdesgetfname(const char *filename)
484248619Sdes{
485248619Sdes	struct stat statbuf;
486248619Sdes	DEVS *cur;
487248619Sdes
488248619Sdes	if (stat(filename, &statbuf)) {
489248619Sdes		warn("%s", filename);
490248619Sdes		return (0);
491248619Sdes	}
492248619Sdes	if ((cur = malloc(sizeof(DEVS))) == NULL)
493248619Sdes		err(1, NULL);
494248619Sdes	cur->next = devs;
495248619Sdes	devs = cur;
496248619Sdes
497248619Sdes	cur->ino = statbuf.st_ino;
498248619Sdes	cur->fsid = statbuf.st_dev;
499248619Sdes	cur->name = filename;
500248619Sdes	return (1);
501248619Sdes}
502248619Sdes
503248619Sdesstatic void
504248619Sdesusage(void)
505248619Sdes{
506248619Sdes	(void)fprintf(stderr,
507248619Sdes "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n");
508248619Sdes	exit(1);
509248619Sdes}
510248619Sdes