1/*
2 * Copyright 2006-2015, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 *		Axel D��rfler, axeld@pinc-software.de
7 */
8
9
10#include <OS.h>
11#include <fs_info.h>
12
13#include <syscalls.h>
14#include <vfs_defs.h>
15
16#include <ctype.h>
17#include <errno.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22
23enum info_mode {
24	kList,
25	kFilterDevice,
26	kFilterFile,
27};
28
29extern const char *__progname;
30
31
32const char *
33open_mode_to_string(int openMode)
34{
35	switch (openMode & O_RWMASK) {
36		case O_RDONLY:
37			return "R  ";
38		case O_WRONLY:
39			return "  W";
40		default:
41			return "R/W";
42	}
43}
44
45
46void
47print_fds(team_info &teamInfo)
48{
49	printf("Team: (%" B_PRId32 ") %s\n", teamInfo.team, teamInfo.args);
50
51	uint32 cookie = 0;
52	fd_info info;
53
54	while (_kern_get_next_fd_info(teamInfo.team, &cookie, &info, sizeof(fd_info)) == B_OK) {
55		printf("%5d  %s %" B_PRIdDEV ":%" B_PRIdINO "\n", info.number,
56			open_mode_to_string(info.open_mode), info.device, info.node);
57	}
58}
59
60
61void
62filter_device(team_info &teamInfo, dev_t device, bool brief)
63{
64	uint32 cookie = 0;
65	fd_info info;
66
67	while (_kern_get_next_fd_info(teamInfo.team, &cookie, &info, sizeof(fd_info)) == B_OK) {
68		if (info.device != device)
69			continue;
70
71		if (brief) {
72			printf("%5" B_PRId32 " %s\n", teamInfo.team, teamInfo.args);
73			break;
74		}
75
76		printf("%5" B_PRId32 " %3d  %3s  %" B_PRIdDEV ":%" B_PRIdINO " %s\n",
77			teamInfo.team, info.number, open_mode_to_string(info.open_mode),
78				info.device, info.node, teamInfo.args);
79	}
80}
81
82
83void
84filter_file(team_info &teamInfo, dev_t device, ino_t node, bool brief)
85{
86	uint32 cookie = 0;
87	fd_info info;
88
89	while (_kern_get_next_fd_info(teamInfo.team, &cookie, &info, sizeof(fd_info)) == B_OK) {
90		if (info.device != device || info.node != node)
91			continue;
92
93		if (brief) {
94			printf("%5" B_PRId32 " %s\n", teamInfo.team, teamInfo.args);
95			break;
96		}
97
98		printf("%5" B_PRId32 " %3d  %3s  %s\n", teamInfo.team, info.number,
99			open_mode_to_string(info.open_mode), teamInfo.args);
100	}
101}
102
103
104void
105usage(bool failure)
106{
107	printf("Usage: %s <id/pattern> or -[dD] <path-to-device> or -[fF] <file>\n"
108		"  Shows info about the used file descriptors in the system.\n\n"
109		"  -d\tOnly shows accesses to the given device\n"
110		"  -D\tLikewise, but only shows the teams that access it\n"
111		"  -f\tOnly shows accesses to the given file\n"
112		"  -F\tLikewise, but only shows the teams that access it\n",
113		__progname);
114
115	exit(failure ? 1 : 0);
116}
117
118
119int
120main(int argc, char **argv)
121{
122	const char *pattern = NULL;
123	dev_t device = -1;
124	ino_t node = -1;
125	int32 id = -1;
126	info_mode mode = kList;
127	bool brief = false;
128
129	// parse arguments
130
131	if (argc == 2) {
132		// filter output
133		if (isdigit(argv[1][0]))
134			id = atol(argv[1]);
135		else if (argv[1][0] == '-')
136			usage(!strcmp(argv[1], "--help"));
137		else
138			pattern = argv[1];
139	} else if (argc > 2) {
140		if (!strcmp(argv[1], "-d") || !strcmp(argv[1], "-D")) {
141			// filter by device usage
142			device = dev_for_path(argv[2]);
143			if (device < 0) {
144				fprintf(stderr, "%s: could not find device: %s\n", __progname,
145					strerror(errno));
146				return 1;
147			}
148			mode = kFilterDevice;
149			if (argv[1][1] == 'D')
150				brief = true;
151		} else if (!strcmp(argv[1], "-f") || !strcmp(argv[1], "-F")) {
152			// filter by file usage
153			struct stat stat;
154			if (::stat(argv[2], &stat) < 0) {
155				fprintf(stderr, "%s: could not open file: %s\n", __progname,
156					strerror(errno));
157				return 1;
158			}
159			device = stat.st_dev;
160			node = stat.st_ino;
161			mode = kFilterFile;
162			if (argv[1][1] == 'F')
163				brief = true;
164		} else
165			usage(true);
166	}
167
168	// do the job!
169
170	team_info info;
171	int32 cookie = 0;
172
173	while (get_next_team_info(&cookie, &info) == B_OK) {
174		switch (mode) {
175			case kList:
176				if ((id != -1 && id != info.team)
177					|| (pattern != NULL && !strstr(info.args, pattern)))
178					continue;
179				print_fds(info);
180				break;
181
182			case kFilterDevice:
183				filter_device(info, device, brief);
184				break;
185			case kFilterFile:
186				filter_file(info, device, node, brief);
187				break;
188		}
189	}
190
191	return 0;
192}
193
194