PageRenderTime 74ms CodeModel.GetById 13ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/usr.bin/fstat/fstat.c

https://bitbucket.org/freebsd/freebsd-head/
C | 538 lines | 434 code | 46 blank | 58 comment | 124 complexity | 6a25a7fca79bc07e937cb164c7ae5054 MD5 | raw file
  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_shm_info(struct procstat *procstat,
 88    struct filestat *fst);
 89static void	print_socket_info(struct procstat *procstat,
 90    struct filestat *fst);
 91static void	print_vnode_info(struct procstat *procstat,
 92    struct filestat *fst);
 93static void	usage(void) __dead2;
 94
 95int
 96do_fstat(int argc, char **argv)
 97{
 98	struct kinfo_proc *p;
 99	struct passwd *passwd;
100	struct procstat *procstat;
101	int arg, ch, what;
102	int cnt, i;
103
104	arg = 0;
105	what = KERN_PROC_PROC;
106	nlistf = memf = NULL;
107	while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1)
108		switch((char)ch) {
109		case 'f':
110			fsflg = 1;
111			break;
112		case 'M':
113			memf = optarg;
114			break;
115		case 'N':
116			nlistf = optarg;
117			break;
118		case 'm':
119			mflg = 1;
120			break;
121		case 'n':
122			nflg = 1;
123			break;
124		case 'p':
125			if (pflg++)
126				usage();
127			if (!isdigit(*optarg)) {
128				warnx("-p requires a process id");
129				usage();
130			}
131			what = KERN_PROC_PID;
132			arg = atoi(optarg);
133			break;
134		case 'u':
135			if (uflg++)
136				usage();
137			if (!(passwd = getpwnam(optarg)))
138				errx(1, "%s: unknown uid", optarg);
139			what = KERN_PROC_UID;
140			arg = passwd->pw_uid;
141			break;
142		case 'v':
143			vflg = 1;
144			break;
145		case '?':
146		default:
147			usage();
148		}
149
150	if (*(argv += optind)) {
151		for (; *argv; ++argv) {
152			if (getfname(*argv))
153				checkfile = 1;
154		}
155		if (!checkfile)	/* file(s) specified, but none accessible */
156			exit(1);
157	}
158
159	if (fsflg && !checkfile) {
160		/* -f with no files means use wd */
161		if (getfname(".") == 0)
162			exit(1);
163		checkfile = 1;
164	}
165
166	if (memf != NULL)
167		procstat = procstat_open_kvm(nlistf, memf);
168	else
169		procstat = procstat_open_sysctl();
170	if (procstat == NULL)
171		errx(1, "procstat_open()");
172	p = procstat_getprocs(procstat, what, arg, &cnt);
173	if (p == NULL)
174		errx(1, "procstat_getprocs()");
175
176	/*
177	 * Print header.
178	 */
179	if (nflg)
180		printf("%s",
181"USER     CMD          PID   FD  DEV    INUM       MODE SZ|DV R/W");
182	else
183		printf("%s",
184"USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W");
185	if (checkfile && fsflg == 0)
186		printf(" NAME\n");
187	else
188		putchar('\n');
189
190	/*
191	 * Go through the process list.
192	 */
193	for (i = 0; i < cnt; i++) {
194		if (p[i].ki_stat == SZOMB)
195			continue;
196		dofiles(procstat, &p[i]);
197	}
198	procstat_freeprocs(procstat, p);
199	procstat_close(procstat);
200	return (0);
201}
202
203static void
204dofiles(struct procstat *procstat, struct kinfo_proc *kp)
205{
206	const char *cmd;
207	const char *uname;
208	struct filestat *fst;
209	struct filestat_list *head;
210	int pid;
211
212	uname = user_from_uid(kp->ki_uid, 0);
213	pid = kp->ki_pid;
214	cmd = kp->ki_comm;
215
216	head = procstat_getfiles(procstat, kp, mflg);
217	if (head == NULL)
218		return;
219	STAILQ_FOREACH(fst, head, next)
220		print_file_info(procstat, fst, uname, cmd, pid);
221	procstat_freefiles(procstat, head);
222}
223
224
225static void
226print_file_info(struct procstat *procstat, struct filestat *fst,
227    const char *uname, const char *cmd, int pid)
228{
229	struct vnstat vn;
230	DEVS *d;
231	const char *filename;
232	int error, fsmatch = 0;
233	char errbuf[_POSIX2_LINE_MAX];
234
235	filename = NULL;
236	if (checkfile != 0) {
237		if (fst->fs_type != PS_FST_TYPE_VNODE &&
238		    fst->fs_type != PS_FST_TYPE_FIFO)
239			return;
240		error = procstat_get_vnode_info(procstat, fst, &vn, errbuf);
241		if (error != 0)
242			return;
243
244		for (d = devs; d != NULL; d = d->next)
245			if (d->fsid == vn.vn_fsid) {
246				fsmatch = 1;
247				if (d->ino == vn.vn_fileid) {
248					filename = d->name;
249					break;
250				}
251			}
252		if (fsmatch == 0 || (filename == NULL && fsflg == 0))
253			return;
254	}
255
256	/*
257	 * Print entry prefix.
258	 */
259	printf("%-8.8s %-10s %5d", uname, cmd, pid);
260	if (fst->fs_uflags & PS_FST_UFLAG_TEXT)
261		printf(" text");
262	else if (fst->fs_uflags & PS_FST_UFLAG_CDIR)
263		printf("   wd");
264	else if (fst->fs_uflags & PS_FST_UFLAG_RDIR)
265		printf(" root");
266	else if (fst->fs_uflags & PS_FST_UFLAG_TRACE)
267		printf("   tr");
268	else if (fst->fs_uflags & PS_FST_UFLAG_MMAP)
269		printf(" mmap");
270	else if (fst->fs_uflags & PS_FST_UFLAG_JAIL)
271		printf(" jail");
272	else if (fst->fs_uflags & PS_FST_UFLAG_CTTY)
273		printf(" ctty");
274	else
275		printf(" %4d", fst->fs_fd);
276
277	/*
278	 * Print type-specific data.
279	 */
280	switch (fst->fs_type) {
281	case PS_FST_TYPE_FIFO:
282	case PS_FST_TYPE_VNODE:
283		print_vnode_info(procstat, fst);
284		break;
285	case PS_FST_TYPE_SOCKET:
286		print_socket_info(procstat, fst);
287		break;
288	case PS_FST_TYPE_PIPE:
289		print_pipe_info(procstat, fst);
290		break;
291	case PS_FST_TYPE_PTS:
292		print_pts_info(procstat, fst);
293		break;
294	case PS_FST_TYPE_SHM:
295		print_shm_info(procstat, fst);
296		break;
297	default:	
298		if (vflg)
299			fprintf(stderr,
300			    "unknown file type %d for file %d of pid %d\n",
301			    fst->fs_type, fst->fs_fd, pid);
302	}
303	if (filename && !fsflg)
304		printf("  %s", filename);
305	putchar('\n');
306}
307
308static void
309print_socket_info(struct procstat *procstat, struct filestat *fst)
310{
311	static const char *stypename[] = {
312		"unused",	/* 0 */
313		"stream",	/* 1 */
314		"dgram",	/* 2 */
315		"raw",		/* 3 */
316		"rdm",		/* 4 */
317		"seqpak"	/* 5 */
318	};
319#define STYPEMAX 5
320	struct sockstat sock;
321	struct protoent *pe;
322	char errbuf[_POSIX2_LINE_MAX];
323	int error;
324	static int isopen;
325
326	error = procstat_get_socket_info(procstat, fst, &sock, errbuf);
327	if (error != 0) {
328		printf("* error");
329		return;
330	}
331	if (sock.type > STYPEMAX)
332		printf("* %s ?%d", sock.dname, sock.type);
333	else
334		printf("* %s %s", sock.dname, stypename[sock.type]);
335
336	/*
337	 * protocol specific formatting
338	 *
339	 * Try to find interesting things to print.  For tcp, the interesting
340	 * thing is the address of the tcpcb, for udp and others, just the
341	 * inpcb (socket pcb).  For unix domain, its the address of the socket
342	 * pcb and the address of the connected pcb (if connected).  Otherwise
343	 * just print the protocol number and address of the socket itself.
344	 * The idea is not to duplicate netstat, but to make available enough
345	 * information for further analysis.
346	 */
347	switch (sock.dom_family) {
348	case AF_INET:
349	case AF_INET6:
350		if (!isopen)
351			setprotoent(++isopen);
352		if ((pe = getprotobynumber(sock.proto)) != NULL)
353			printf(" %s", pe->p_name);
354		else
355			printf(" %d", sock.proto);
356		if (sock.proto == IPPROTO_TCP ) {
357			if (sock.inp_ppcb != 0)
358				printf(" %lx", (u_long)sock.inp_ppcb);
359		}
360		else if (sock.so_pcb != 0)
361			printf(" %lx", (u_long)sock.so_pcb);
362		break;
363	case AF_UNIX:
364		/* print address of pcb and connected pcb */
365		if (sock.so_pcb != 0) {
366			printf(" %lx", (u_long)sock.so_pcb);
367			if (sock.unp_conn) {
368				char shoconn[4], *cp;
369
370				cp = shoconn;
371				if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE))
372					*cp++ = '<';
373				*cp++ = '-';
374				if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE))
375					*cp++ = '>';
376				*cp = '\0';
377				printf(" %s %lx", shoconn,
378				    (u_long)sock.unp_conn);
379                        }
380		}
381		break;
382	default:
383		/* print protocol number and socket address */
384		printf(" %d %lx", sock.proto, (u_long)sock.so_addr);
385	}
386}
387
388static void
389print_pipe_info(struct procstat *procstat, struct filestat *fst)
390{
391	struct pipestat ps;
392	char errbuf[_POSIX2_LINE_MAX];
393	int error;
394
395	error = procstat_get_pipe_info(procstat, fst, &ps, errbuf);
396	if (error != 0) {
397		printf("* error");
398		return;
399	}
400	printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer);
401	printf(" %6zd", ps.buffer_cnt);
402	print_access_flags(fst->fs_fflags);
403}
404
405static void
406print_pts_info(struct procstat *procstat, struct filestat *fst)
407{
408	struct ptsstat pts;
409	char errbuf[_POSIX2_LINE_MAX];
410	int error;
411
412	error = procstat_get_pts_info(procstat, fst, &pts, errbuf);
413	if (error != 0) {
414		printf("* error");
415		return;
416	}
417	printf("* pseudo-terminal master ");
418	if (nflg || !*pts.devname) {
419		printf("%#10jx", (uintmax_t)pts.dev);
420	} else {
421		printf("%10s", pts.devname);
422	}
423	print_access_flags(fst->fs_fflags);
424}
425
426static void
427print_shm_info(struct procstat *procstat, struct filestat *fst)
428{
429	struct shmstat shm;
430	char errbuf[_POSIX2_LINE_MAX];
431	char mode[15];
432	int error;
433
434	error = procstat_get_shm_info(procstat, fst, &shm, errbuf);
435	if (error != 0) {
436		printf("* error");
437		return;
438	}
439	if (nflg) {
440		printf("             ");
441		(void)snprintf(mode, sizeof(mode), "%o", shm.mode);
442	} else {
443		printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-");
444		strmode(shm.mode, mode);
445	}
446	printf(" %10s %6ju", mode, shm.size);
447	print_access_flags(fst->fs_fflags);
448}
449
450static void
451print_vnode_info(struct procstat *procstat, struct filestat *fst)
452{
453	struct vnstat vn;
454	char errbuf[_POSIX2_LINE_MAX];
455	char mode[15];
456	const char *badtype;
457	int error;
458
459	badtype = NULL;
460	error = procstat_get_vnode_info(procstat, fst, &vn, errbuf);
461	if (error != 0)
462		badtype = errbuf;
463	else if (vn.vn_type == PS_FST_VTYPE_VBAD)
464		badtype = "bad";
465	else if (vn.vn_type == PS_FST_VTYPE_VNON)
466		badtype = "none";
467	if (badtype != NULL) {
468		printf(" -         -  %10s    -", badtype);
469		return;
470	}
471
472	if (nflg)
473		printf(" %#5jx", (uintmax_t)vn.vn_fsid);
474	else if (vn.vn_mntdir != NULL)
475		(void)printf(" %-8s", vn.vn_mntdir);
476
477	/*
478	 * Print access mode.
479	 */
480	if (nflg)
481		(void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode);
482	else {
483		strmode(vn.vn_mode, mode);
484	}
485	(void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode);
486
487	if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) {
488		if (nflg || !*vn.vn_devname)
489			printf(" %#6jx", (uintmax_t)vn.vn_dev);
490		else {
491			printf(" %6s", vn.vn_devname);
492		}
493	} else
494		printf(" %6ju", (uintmax_t)vn.vn_size);
495	print_access_flags(fst->fs_fflags);
496}
497
498static void
499print_access_flags(int flags)
500{
501	char rw[3];
502
503	rw[0] = '\0';
504	if (flags & PS_FST_FFLAG_READ)
505		strcat(rw, "r");
506	if (flags & PS_FST_FFLAG_WRITE)
507		strcat(rw, "w");
508	printf(" %2s", rw);
509}
510
511int
512getfname(const char *filename)
513{
514	struct stat statbuf;
515	DEVS *cur;
516
517	if (stat(filename, &statbuf)) {
518		warn("%s", filename);
519		return (0);
520	}
521	if ((cur = malloc(sizeof(DEVS))) == NULL)
522		err(1, NULL);
523	cur->next = devs;
524	devs = cur;
525
526	cur->ino = statbuf.st_ino;
527	cur->fsid = statbuf.st_dev;
528	cur->name = filename;
529	return (1);
530}
531
532static void
533usage(void)
534{
535	(void)fprintf(stderr,
536 "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n");
537	exit(1);
538}