GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: fstat.c,v 1.90 2017/01/21 12:21:57 deraadt Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com> |
||
5 |
* |
||
6 |
* Permission to use, copy, modify, and distribute this software for any |
||
7 |
* purpose with or without fee is hereby granted, provided that the above |
||
8 |
* copyright notice and this permission notice appear in all copies. |
||
9 |
* |
||
10 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||
11 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||
12 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||
13 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||
14 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||
15 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||
16 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||
17 |
*/ |
||
18 |
|||
19 |
/*- |
||
20 |
* Copyright (c) 1988, 1993 |
||
21 |
* The Regents of the University of California. All rights reserved. |
||
22 |
* |
||
23 |
* Redistribution and use in source and binary forms, with or without |
||
24 |
* modification, are permitted provided that the following conditions |
||
25 |
* are met: |
||
26 |
* 1. Redistributions of source code must retain the above copyright |
||
27 |
* notice, this list of conditions and the following disclaimer. |
||
28 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
29 |
* notice, this list of conditions and the following disclaimer in the |
||
30 |
* documentation and/or other materials provided with the distribution. |
||
31 |
* 3. Neither the name of the University nor the names of its contributors |
||
32 |
* may be used to endorse or promote products derived from this software |
||
33 |
* without specific prior written permission. |
||
34 |
* |
||
35 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
36 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
37 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
38 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
39 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
40 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
41 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
42 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
43 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
44 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
45 |
* SUCH DAMAGE. |
||
46 |
*/ |
||
47 |
|||
48 |
#include <sys/types.h> |
||
49 |
#include <sys/queue.h> |
||
50 |
#include <sys/mount.h> |
||
51 |
#include <sys/stat.h> |
||
52 |
#include <sys/vnode.h> |
||
53 |
#include <sys/socket.h> |
||
54 |
#include <sys/socketvar.h> |
||
55 |
#include <sys/eventvar.h> |
||
56 |
#include <sys/sysctl.h> |
||
57 |
#include <sys/filedesc.h> |
||
58 |
#define _KERNEL /* for DTYPE_* */ |
||
59 |
#include <sys/file.h> |
||
60 |
#undef _KERNEL |
||
61 |
|||
62 |
#include <net/route.h> |
||
63 |
#include <netinet/in.h> |
||
64 |
|||
65 |
#include <netdb.h> |
||
66 |
#include <arpa/inet.h> |
||
67 |
|||
68 |
#include <sys/pipe.h> |
||
69 |
|||
70 |
#include <ctype.h> |
||
71 |
#include <errno.h> |
||
72 |
#include <fcntl.h> |
||
73 |
#include <kvm.h> |
||
74 |
#include <limits.h> |
||
75 |
#include <nlist.h> |
||
76 |
#include <pwd.h> |
||
77 |
#include <search.h> |
||
78 |
#include <signal.h> |
||
79 |
#include <stdio.h> |
||
80 |
#include <stdint.h> |
||
81 |
#include <stdlib.h> |
||
82 |
#include <string.h> |
||
83 |
#include <unistd.h> |
||
84 |
#include <err.h> |
||
85 |
|||
86 |
#include "fstat.h" |
||
87 |
|||
88 |
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
||
89 |
|||
90 |
struct fileargs fileargs = SLIST_HEAD_INITIALIZER(fileargs); |
||
91 |
|||
92 |
int fsflg; /* show files on same filesystem as file(s) argument */ |
||
93 |
int pflg; /* show files open by a particular pid */ |
||
94 |
int uflg; /* show files open by a particular (effective) user */ |
||
95 |
int checkfile; /* true if restricting to particular files or filesystems */ |
||
96 |
int nflg; /* (numerical) display f.s. and rdev as dev_t */ |
||
97 |
int oflg; /* display file offset */ |
||
98 |
int sflg; /* display file xfer/bytes counters */ |
||
99 |
int vflg; /* display errors in locating kernel data objects etc... */ |
||
100 |
int cflg; /* fuser only */ |
||
101 |
|||
102 |
int fuser; /* 1 if we are fuser, 0 if we are fstat */ |
||
103 |
int signo; /* signal to send (fuser only) */ |
||
104 |
|||
105 |
kvm_t *kd; |
||
106 |
uid_t uid; |
||
107 |
|||
108 |
void fstat_dofile(struct kinfo_file *); |
||
109 |
void fstat_header(void); |
||
110 |
void getinetproto(int); |
||
111 |
__dead void usage(void); |
||
112 |
int getfname(char *); |
||
113 |
void kqueuetrans(struct kinfo_file *); |
||
114 |
void pipetrans(struct kinfo_file *); |
||
115 |
struct kinfo_file *splice_find(char, u_int64_t); |
||
116 |
void splice_insert(char, u_int64_t, struct kinfo_file *); |
||
117 |
void find_splices(struct kinfo_file *, int); |
||
118 |
void print_inet_details(struct kinfo_file *); |
||
119 |
void print_inet6_details(struct kinfo_file *); |
||
120 |
void print_sock_details(struct kinfo_file *); |
||
121 |
void socktrans(struct kinfo_file *); |
||
122 |
void vtrans(struct kinfo_file *); |
||
123 |
const char *inet6_addrstr(struct in6_addr *); |
||
124 |
int signame_to_signum(char *); |
||
125 |
void hide(void *p); |
||
126 |
|||
127 |
int hideroot; |
||
128 |
|||
129 |
void |
||
130 |
hide(void *p) |
||
131 |
{ |
||
132 |
107476 |
printf("%p", hideroot ? NULL : p); |
|
133 |
53738 |
} |
|
134 |
|||
135 |
int |
||
136 |
main(int argc, char *argv[]) |
||
137 |
{ |
||
138 |
struct passwd *passwd; |
||
139 |
struct kinfo_file *kf, *kflast; |
||
140 |
int arg, ch, what; |
||
141 |
char *memf, *nlistf, *optstr; |
||
142 |
642 |
char buf[_POSIX2_LINE_MAX]; |
|
143 |
321 |
const char *errstr; |
|
144 |
321 |
int cnt, flags; |
|
145 |
|||
146 |
321 |
hideroot = getuid(); |
|
147 |
|||
148 |
arg = -1; |
||
149 |
what = KERN_FILE_BYPID; |
||
150 |
nlistf = memf = NULL; |
||
151 |
321 |
oflg = 0; |
|
152 |
|||
153 |
/* are we fstat(1) or fuser(1)? */ |
||
154 |
✗✓ | 321 |
if (strcmp(__progname, "fuser") == 0) { |
155 |
fuser = 1; |
||
156 |
optstr = "cfks:uM:N:"; |
||
157 |
} else { |
||
158 |
321 |
fuser = 0; |
|
159 |
optstr = "fnop:su:vN:M:"; |
||
160 |
} |
||
161 |
|||
162 |
/* |
||
163 |
* fuser and fstat share three flags: -f, -s and -u. In both cases |
||
164 |
* -f is a boolean, but for -u fstat wants an argument while fuser |
||
165 |
* does not and for -s fuser wants an argument whereas fstat does not. |
||
166 |
*/ |
||
167 |
✗✓ | 321 |
while ((ch = getopt(argc, argv, optstr)) != -1) |
168 |
switch ((char)ch) { |
||
169 |
case 'c': |
||
170 |
if (fsflg) |
||
171 |
usage(); |
||
172 |
cflg = 1; |
||
173 |
break; |
||
174 |
case 'f': |
||
175 |
if (cflg) |
||
176 |
usage(); |
||
177 |
fsflg = 1; |
||
178 |
break; |
||
179 |
case 'k': |
||
180 |
sflg = 1; |
||
181 |
signo = SIGKILL; |
||
182 |
break; |
||
183 |
case 'M': |
||
184 |
memf = optarg; |
||
185 |
break; |
||
186 |
case 'N': |
||
187 |
nlistf = optarg; |
||
188 |
break; |
||
189 |
case 'n': |
||
190 |
nflg = 1; |
||
191 |
break; |
||
192 |
case 'o': |
||
193 |
oflg = 1; |
||
194 |
break; |
||
195 |
case 'p': |
||
196 |
if (pflg++) |
||
197 |
usage(); |
||
198 |
arg = strtonum(optarg, 0, INT_MAX, &errstr); |
||
199 |
if (errstr != NULL) { |
||
200 |
warnx("-p requires a process id, %s: %s", |
||
201 |
errstr, optarg); |
||
202 |
usage(); |
||
203 |
} |
||
204 |
what = KERN_FILE_BYPID; |
||
205 |
break; |
||
206 |
case 's': |
||
207 |
sflg = 1; |
||
208 |
if (fuser) { |
||
209 |
signo = signame_to_signum(optarg); |
||
210 |
if (signo == -1) { |
||
211 |
warnx("invalid signal %s", optarg); |
||
212 |
usage(); |
||
213 |
} |
||
214 |
} |
||
215 |
break; |
||
216 |
case 'u': |
||
217 |
if (uflg++) |
||
218 |
usage(); |
||
219 |
if (!fuser) { |
||
220 |
if (!(passwd = getpwnam(optarg))) { |
||
221 |
arg = strtonum(optarg, 0, UID_MAX, |
||
222 |
&errstr); |
||
223 |
if (errstr != NULL) { |
||
224 |
errx(1, "%s: unknown uid", |
||
225 |
optarg); |
||
226 |
} |
||
227 |
} else |
||
228 |
arg = passwd->pw_uid; |
||
229 |
what = KERN_FILE_BYUID; |
||
230 |
} |
||
231 |
break; |
||
232 |
case 'v': |
||
233 |
vflg = 1; |
||
234 |
break; |
||
235 |
default: |
||
236 |
usage(); |
||
237 |
} |
||
238 |
|||
239 |
/* |
||
240 |
* get the uid, for oflg and sflg |
||
241 |
*/ |
||
242 |
321 |
uid = getuid(); |
|
243 |
|||
244 |
/* |
||
245 |
* Use sysctl unless inspecting an alternate kernel. |
||
246 |
*/ |
||
247 |
✓✗ | 321 |
if (nlistf == NULL || memf == NULL) |
248 |
321 |
flags = KVM_NO_FILES; |
|
249 |
else |
||
250 |
flags = O_RDONLY; |
||
251 |
|||
252 |
✗✓ | 321 |
if ((kd = kvm_openfiles(nlistf, memf, NULL, flags, buf)) == NULL) |
253 |
errx(1, "%s", buf); |
||
254 |
|||
255 |
✗✓ | 321 |
if (*(argv += optind)) { |
256 |
for (; *argv; ++argv) { |
||
257 |
if (getfname(*argv)) |
||
258 |
checkfile = 1; |
||
259 |
} |
||
260 |
/* file(s) specified, but none accessible */ |
||
261 |
if (!checkfile) |
||
262 |
exit(1); |
||
263 |
✗✓ | 321 |
} else if (fuser) |
264 |
usage(); |
||
265 |
|||
266 |
✗✓ | 321 |
if (!fuser && fsflg && !checkfile) { |
267 |
/* fstat -f with no files means use wd */ |
||
268 |
if (getfname(".") == 0) |
||
269 |
exit(1); |
||
270 |
checkfile = 1; |
||
271 |
} |
||
272 |
|||
273 |
✗✓ | 321 |
if ((kf = kvm_getfiles(kd, what, arg, sizeof(*kf), &cnt)) == NULL) |
274 |
errx(1, "%s", kvm_geterr(kd)); |
||
275 |
|||
276 |
✗✓ | 321 |
if (fuser) { |
277 |
/* |
||
278 |
* fuser |
||
279 |
* uflg: need "getpw" |
||
280 |
* sflg: need "proc" (might call kill(2)) |
||
281 |
*/ |
||
282 |
if (uflg && sflg) { |
||
283 |
if (pledge("stdio rpath getpw proc flock cpath wpath", NULL) == -1) |
||
284 |
err(1, "pledge"); |
||
285 |
} else if (uflg) { |
||
286 |
if (pledge("stdio rpath getpw flock cpath wpath", NULL) == -1) |
||
287 |
err(1, "pledge"); |
||
288 |
} else if (sflg) { |
||
289 |
if (pledge("stdio rpath proc flock cpath wpath", NULL) == -1) |
||
290 |
err(1, "pledge"); |
||
291 |
} else { |
||
292 |
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
||
293 |
err(1, "pledge"); |
||
294 |
} |
||
295 |
} else { |
||
296 |
/* fstat */ |
||
297 |
✗✓ | 321 |
if (pledge("stdio rpath getpw flock cpath wpath", NULL) == -1) |
298 |
err(1, "pledge"); |
||
299 |
} |
||
300 |
|||
301 |
321 |
find_splices(kf, cnt); |
|
302 |
✓✗ | 321 |
if (!fuser) |
303 |
321 |
fstat_header(); |
|
304 |
✓✓ | 256582 |
for (kflast = &kf[cnt]; kf < kflast; ++kf) { |
305 |
✗✓ | 127970 |
if (fuser) |
306 |
fuser_check(kf); |
||
307 |
else |
||
308 |
127970 |
fstat_dofile(kf); |
|
309 |
} |
||
310 |
✗✓ | 321 |
if (fuser) |
311 |
fuser_run(); |
||
312 |
|||
313 |
exit(0); |
||
314 |
} |
||
315 |
|||
316 |
void |
||
317 |
fstat_header(void) |
||
318 |
{ |
||
319 |
✗✓ | 642 |
if (nflg) |
320 |
printf("%s", |
||
321 |
"USER CMD PID FD DEV INUM MODE R/W SZ|DV"); |
||
322 |
else |
||
323 |
321 |
printf("%s", |
|
324 |
"USER CMD PID FD MOUNT INUM MODE R/W SZ|DV"); |
||
325 |
✗✓ | 321 |
if (oflg) |
326 |
printf("%s", ":OFFSET "); |
||
327 |
✗✓ | 321 |
if (checkfile && fsflg == 0) |
328 |
printf(" NAME"); |
||
329 |
✗✓ | 321 |
if (sflg) |
330 |
printf(" XFERS KBYTES"); |
||
331 |
✓✗ | 642 |
putchar('\n'); |
332 |
321 |
} |
|
333 |
|||
334 |
char *Uname, *Comm; |
||
335 |
uid_t *procuid; |
||
336 |
pid_t Pid; |
||
337 |
|||
338 |
#define PREFIX(i) do { \ |
||
339 |
printf("%-8.8s %-10s %5ld", Uname, Comm, (long)Pid); \ |
||
340 |
switch (i) { \ |
||
341 |
case KERN_FILE_TEXT: \ |
||
342 |
printf(" text"); \ |
||
343 |
break; \ |
||
344 |
case KERN_FILE_CDIR: \ |
||
345 |
printf(" wd"); \ |
||
346 |
break; \ |
||
347 |
case KERN_FILE_RDIR: \ |
||
348 |
printf(" root"); \ |
||
349 |
break; \ |
||
350 |
case KERN_FILE_TRACE: \ |
||
351 |
printf(" tr"); \ |
||
352 |
break; \ |
||
353 |
default: \ |
||
354 |
printf(" %4d", i); \ |
||
355 |
break; \ |
||
356 |
} \ |
||
357 |
} while (0) |
||
358 |
|||
359 |
/* |
||
360 |
* print open files attributed to this process |
||
361 |
*/ |
||
362 |
void |
||
363 |
fstat_dofile(struct kinfo_file *kf) |
||
364 |
{ |
||
365 |
|||
366 |
255940 |
Uname = user_from_uid(kf->p_uid, 0); |
|
367 |
127970 |
procuid = &kf->p_uid; |
|
368 |
127970 |
Pid = kf->p_pid; |
|
369 |
127970 |
Comm = kf->p_comm; |
|
370 |
|||
371 |
✓✓✓✓ ✗ |
127970 |
switch (kf->f_type) { |
372 |
case DTYPE_VNODE: |
||
373 |
91162 |
vtrans(kf); |
|
374 |
91162 |
break; |
|
375 |
case DTYPE_SOCKET: |
||
376 |
✓✗ | 27016 |
if (checkfile == 0) |
377 |
27016 |
socktrans(kf); |
|
378 |
break; |
||
379 |
case DTYPE_PIPE: |
||
380 |
✓✗ | 5619 |
if (checkfile == 0) |
381 |
5619 |
pipetrans(kf); |
|
382 |
break; |
||
383 |
case DTYPE_KQUEUE: |
||
384 |
✓✗ | 4173 |
if (checkfile == 0) |
385 |
4173 |
kqueuetrans(kf); |
|
386 |
break; |
||
387 |
default: |
||
388 |
if (vflg) { |
||
389 |
warnx("unknown file type %d for file %d of pid %ld", |
||
390 |
kf->f_type, kf->fd_fd, (long)Pid); |
||
391 |
} |
||
392 |
break; |
||
393 |
} |
||
394 |
127970 |
} |
|
395 |
|||
396 |
void |
||
397 |
vtrans(struct kinfo_file *kf) |
||
398 |
{ |
||
399 |
const char *badtype = NULL; |
||
400 |
182324 |
char rwep[5], mode[12]; |
|
401 |
char *filename = NULL; |
||
402 |
|||
403 |
✗✓ | 91162 |
if (kf->v_type == VNON) |
404 |
badtype = "none"; |
||
405 |
✗✓ | 91162 |
else if (kf->v_type == VBAD) |
406 |
badtype = "bad"; |
||
407 |
✓✓✗✓ |
92125 |
else if (kf->v_tag == VT_NON && !(kf->v_flag & VCLONE)) |
408 |
badtype = "none"; /* not a clone */ |
||
409 |
|||
410 |
✗✓ | 91162 |
if (checkfile) { |
411 |
int fsmatch = 0; |
||
412 |
struct filearg *fa; |
||
413 |
|||
414 |
if (badtype) |
||
415 |
return; |
||
416 |
SLIST_FOREACH(fa, &fileargs, next) { |
||
417 |
if (fa->dev == kf->va_fsid) { |
||
418 |
fsmatch = 1; |
||
419 |
if (fa->ino == kf->va_fileid) { |
||
420 |
filename = fa->name; |
||
421 |
break; |
||
422 |
} |
||
423 |
} |
||
424 |
} |
||
425 |
if (fsmatch == 0 || (filename == NULL && fsflg == 0)) |
||
426 |
return; |
||
427 |
} |
||
428 |
✓✓✓✓ ✓ |
182324 |
PREFIX(kf->fd_fd); |
429 |
✗✓ | 91162 |
if (badtype) { |
430 |
(void)printf(" - - %10s -\n", badtype); |
||
431 |
return; |
||
432 |
} |
||
433 |
|||
434 |
✗✓ | 91162 |
if (nflg) |
435 |
(void)printf(" %2ld,%-2ld", (long)major(kf->va_fsid), |
||
436 |
(long)minor(kf->va_fsid)); |
||
437 |
✓✓ | 91162 |
else if (!(kf->v_flag & VCLONE)) |
438 |
90199 |
(void)printf(" %-8s", kf->f_mntonname); |
|
439 |
else |
||
440 |
963 |
(void)printf(" clone "); |
|
441 |
✗✓ | 91162 |
if (nflg) |
442 |
(void)snprintf(mode, sizeof(mode), "%o", kf->va_mode); |
||
443 |
else |
||
444 |
91162 |
strmode(kf->va_mode, mode); |
|
445 |
182324 |
printf(" %8llu%s %11s", kf->va_fileid, |
|
446 |
91162 |
kf->va_nlink == 0 ? "*" : " ", |
|
447 |
91162 |
mode); |
|
448 |
91162 |
rwep[0] = '\0'; |
|
449 |
✓✓ | 91162 |
if (kf->f_flag & FREAD) |
450 |
84052 |
strlcat(rwep, "r", sizeof rwep); |
|
451 |
✓✓ | 91162 |
if (kf->f_flag & FWRITE) |
452 |
55391 |
strlcat(rwep, "w", sizeof rwep); |
|
453 |
✓✓ | 91162 |
if (kf->fd_ofileflags & UF_EXCLOSE) |
454 |
4756 |
strlcat(rwep, "e", sizeof rwep); |
|
455 |
91162 |
printf(" %4s", rwep); |
|
456 |
✓✓ | 91162 |
switch (kf->v_type) { |
457 |
case VBLK: |
||
458 |
case VCHR: { |
||
459 |
char *name; |
||
460 |
|||
461 |
✓✗✗✓ |
192220 |
if (nflg || ((name = devname(kf->va_rdev, |
462 |
96110 |
kf->v_type == VCHR ? S_IFCHR : S_IFBLK)) == NULL)) |
|
463 |
printf(" %2d,%-3d", major(kf->va_rdev), minor(kf->va_rdev)); |
||
464 |
else |
||
465 |
48055 |
printf(" %7s", name); |
|
466 |
✗✓ | 48055 |
if (oflg) |
467 |
printf(" "); |
||
468 |
break; |
||
469 |
} |
||
470 |
default: |
||
471 |
43107 |
printf(" %8llu", kf->va_size); |
|
472 |
✗✓ | 43107 |
if (oflg) { |
473 |
if (uid == 0 || uid == *procuid) |
||
474 |
printf(":%-8llu", kf->f_offset); |
||
475 |
else |
||
476 |
printf(":%-8s", "*"); |
||
477 |
} |
||
478 |
} |
||
479 |
✗✓ | 91162 |
if (sflg) { |
480 |
if (uid == 0 || uid == *procuid) { |
||
481 |
printf(" %8llu %8llu", |
||
482 |
(kf->f_rxfer + kf->f_rwfer), |
||
483 |
(kf->f_rbytes + kf->f_wbytes) / 1024); |
||
484 |
} else { |
||
485 |
printf(" %8s %8s", "*", "*"); |
||
486 |
} |
||
487 |
} |
||
488 |
✗✓ | 91162 |
if (filename && !fsflg) |
489 |
printf(" %s", filename); |
||
490 |
✓✗ | 182324 |
putchar('\n'); |
491 |
182324 |
} |
|
492 |
|||
493 |
void |
||
494 |
pipetrans(struct kinfo_file *kf) |
||
495 |
{ |
||
496 |
void *maxaddr; |
||
497 |
|||
498 |
✗✗✗✗ ✓ |
16857 |
PREFIX(kf->fd_fd); |
499 |
|||
500 |
5619 |
printf(" "); |
|
501 |
|||
502 |
/* |
||
503 |
* We don't have enough space to fit both peer and own address, so |
||
504 |
* we select the higher address so both ends of the pipe have the |
||
505 |
* same visible addr. (it's the higher address because when the other |
||
506 |
* end closes, it becomes 0) |
||
507 |
*/ |
||
508 |
5619 |
maxaddr = (void *)(uintptr_t)MAXIMUM(kf->f_data, kf->pipe_peer); |
|
509 |
|||
510 |
5619 |
printf("pipe "); |
|
511 |
5619 |
hide(maxaddr); |
|
512 |
5619 |
printf(" state: %s%s%s", |
|
513 |
5619 |
(kf->pipe_state & PIPE_WANTR) ? "R" : "", |
|
514 |
5619 |
(kf->pipe_state & PIPE_WANTW) ? "W" : "", |
|
515 |
5619 |
(kf->pipe_state & PIPE_EOF) ? "E" : ""); |
|
516 |
✗✓ | 5619 |
if (sflg) |
517 |
printf("\t%8llu %8llu", |
||
518 |
(kf->f_rxfer + kf->f_rwfer), |
||
519 |
(kf->f_rbytes + kf->f_wbytes) / 1024); |
||
520 |
5619 |
printf("\n"); |
|
521 |
return; |
||
522 |
5619 |
} |
|
523 |
|||
524 |
void |
||
525 |
kqueuetrans(struct kinfo_file *kf) |
||
526 |
{ |
||
527 |
✗✗✗✗ ✓ |
12519 |
PREFIX(kf->fd_fd); |
528 |
|||
529 |
4173 |
printf(" "); |
|
530 |
|||
531 |
4173 |
printf("kqueue "); |
|
532 |
4173 |
hide((void *)(uintptr_t)kf->f_data); |
|
533 |
4173 |
printf(" %d state: %s%s\n", |
|
534 |
4173 |
kf->kq_count, |
|
535 |
4173 |
(kf->kq_state & KQ_SEL) ? "S" : "", |
|
536 |
4173 |
(kf->kq_state & KQ_SLEEP) ? "W" : ""); |
|
537 |
4173 |
return; |
|
538 |
} |
||
539 |
|||
540 |
const char * |
||
541 |
inet6_addrstr(struct in6_addr *p) |
||
542 |
{ |
||
543 |
3662 |
struct sockaddr_in6 sin6; |
|
544 |
static char hbuf[NI_MAXHOST]; |
||
545 |
const int niflags = NI_NUMERICHOST; |
||
546 |
|||
547 |
1831 |
memset(&sin6, 0, sizeof(sin6)); |
|
548 |
1831 |
sin6.sin6_family = AF_INET6; |
|
549 |
1831 |
sin6.sin6_len = sizeof(struct sockaddr_in6); |
|
550 |
1831 |
sin6.sin6_addr = *p; |
|
551 |
✓✓✓✗ ✓✗ |
2473 |
if (IN6_IS_ADDR_LINKLOCAL(p) && |
552 |
321 |
*(u_int16_t *)&sin6.sin6_addr.s6_addr[2] != 0) { |
|
553 |
321 |
sin6.sin6_scope_id = |
|
554 |
321 |
ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); |
|
555 |
321 |
sin6.sin6_addr.s6_addr[2] = sin6.sin6_addr.s6_addr[3] = 0; |
|
556 |
321 |
} |
|
557 |
|||
558 |
✗✓ | 1831 |
if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, |
559 |
hbuf, sizeof(hbuf), NULL, 0, niflags)) |
||
560 |
return "invalid"; |
||
561 |
|||
562 |
1831 |
return hbuf; |
|
563 |
1831 |
} |
|
564 |
|||
565 |
void |
||
566 |
splice_insert(char type, u_int64_t ptr, struct kinfo_file *data) |
||
567 |
{ |
||
568 |
ENTRY entry, *found; |
||
569 |
|||
570 |
if (asprintf(&entry.key, "%c%llx", type, hideroot ? 0 : ptr) == -1) |
||
571 |
err(1, NULL); |
||
572 |
entry.data = data; |
||
573 |
if ((found = hsearch(entry, ENTER)) == NULL) |
||
574 |
err(1, "hsearch"); |
||
575 |
/* if it's ambiguous, set the data to NULL */ |
||
576 |
if (found->data != data) |
||
577 |
found->data = NULL; |
||
578 |
} |
||
579 |
|||
580 |
struct kinfo_file * |
||
581 |
splice_find(char type, u_int64_t ptr) |
||
582 |
{ |
||
583 |
ENTRY entry, *found; |
||
584 |
char buf[20]; |
||
585 |
|||
586 |
snprintf(buf, sizeof(buf), "%c%llx", type, hideroot ? 0 : ptr); |
||
587 |
entry.key = buf; |
||
588 |
found = hsearch(entry, FIND); |
||
589 |
return (found != NULL ? found->data : NULL); |
||
590 |
} |
||
591 |
|||
592 |
void |
||
593 |
find_splices(struct kinfo_file *kf, int cnt) |
||
594 |
{ |
||
595 |
int i, created; |
||
596 |
|||
597 |
created = 0; |
||
598 |
✓✓ | 256903 |
for (i = 0; i < cnt; i++) { |
599 |
✓✓✗✓ |
154986 |
if (kf[i].f_type != DTYPE_SOCKET || |
600 |
✓✗ | 54032 |
(kf[i].so_splice == 0 && kf[i].so_splicelen != -1)) |
601 |
continue; |
||
602 |
if (created++ == 0) { |
||
603 |
if (hcreate(1000) == 0) |
||
604 |
err(1, "hcreate"); |
||
605 |
} |
||
606 |
splice_insert('>', kf[i].f_data, &kf[i]); |
||
607 |
if (kf[i].so_splice != 0) |
||
608 |
splice_insert('<', kf[i].so_splice, &kf[i]); |
||
609 |
} |
||
610 |
321 |
} |
|
611 |
|||
612 |
void |
||
613 |
print_inet_details(struct kinfo_file *kf) |
||
614 |
{ |
||
615 |
struct in_addr laddr, faddr; |
||
616 |
|||
617 |
5250 |
memcpy(&laddr, kf->inp_laddru, sizeof(laddr)); |
|
618 |
2625 |
memcpy(&faddr, kf->inp_faddru, sizeof(faddr)); |
|
619 |
✓✓ | 2625 |
if (kf->so_protocol == IPPROTO_TCP) { |
620 |
1267 |
printf(" "); |
|
621 |
1267 |
hide((void *)(uintptr_t)kf->inp_ppcb); |
|
622 |
✓✓ | 4702 |
printf(" %s:%d", laddr.s_addr == INADDR_ANY ? "*" : |
623 |
2168 |
inet_ntoa(laddr), ntohs(kf->inp_lport)); |
|
624 |
✓✓ | 1267 |
if (kf->inp_fport) { |
625 |
✓✓ | 461 |
if (kf->so_state & SS_CONNECTOUT) |
626 |
70 |
printf(" --> "); |
|
627 |
else |
||
628 |
391 |
printf(" <-- "); |
|
629 |
461 |
printf("%s:%d", |
|
630 |
✓✗ | 1383 |
faddr.s_addr == INADDR_ANY ? "*" : |
631 |
922 |
inet_ntoa(faddr), ntohs(kf->inp_fport)); |
|
632 |
461 |
} |
|
633 |
✓✓ | 1358 |
} else if (kf->so_protocol == IPPROTO_UDP) { |
634 |
✓✓ | 3329 |
printf(" %s:%d", laddr.s_addr == INADDR_ANY ? "*" : |
635 |
1255 |
inet_ntoa(laddr), ntohs(kf->inp_lport)); |
|
636 |
✗✓ | 1037 |
if (kf->inp_fport) { |
637 |
printf(" <-> %s:%d", |
||
638 |
faddr.s_addr == INADDR_ANY ? "*" : |
||
639 |
inet_ntoa(faddr), ntohs(kf->inp_fport)); |
||
640 |
} |
||
641 |
✓✗ | 321 |
} else if (kf->so_pcb) { |
642 |
321 |
printf(" "); |
|
643 |
321 |
hide((void *)(uintptr_t)kf->so_pcb); |
|
644 |
321 |
} |
|
645 |
2625 |
} |
|
646 |
|||
647 |
void |
||
648 |
print_inet6_details(struct kinfo_file *kf) |
||
649 |
{ |
||
650 |
4248 |
char xaddrbuf[NI_MAXHOST + 2]; |
|
651 |
2124 |
struct in6_addr laddr6, faddr6; |
|
652 |
|||
653 |
2124 |
memcpy(&laddr6, kf->inp_laddru, sizeof(laddr6)); |
|
654 |
2124 |
memcpy(&faddr6, kf->inp_faddru, sizeof(faddr6)); |
|
655 |
✓✓ | 2124 |
if (kf->so_protocol == IPPROTO_TCP) { |
656 |
1040 |
printf(" "); |
|
657 |
1040 |
hide((void *)(uintptr_t)kf->inp_ppcb); |
|
658 |
2080 |
snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]", |
|
659 |
1040 |
inet6_addrstr(&laddr6)); |
|
660 |
1040 |
printf(" %s:%d", |
|
661 |
✓✓✓✗ ✓✗✓✓ |
4956 |
IN6_IS_ADDR_UNSPECIFIED(&laddr6) ? "*" : |
662 |
1040 |
xaddrbuf, ntohs(kf->inp_lport)); |
|
663 |
✓✓ | 1040 |
if (kf->inp_fport) { |
664 |
✓✓ | 28 |
if (kf->so_state & SS_CONNECTOUT) |
665 |
14 |
printf(" --> "); |
|
666 |
else |
||
667 |
14 |
printf(" <-- "); |
|
668 |
28 |
snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]", |
|
669 |
28 |
inet6_addrstr(&faddr6)); |
|
670 |
28 |
printf("%s:%d", |
|
671 |
✓✗✓✗ ✓✗✓✗ |
168 |
IN6_IS_ADDR_UNSPECIFIED(&faddr6) ? "*" : |
672 |
28 |
xaddrbuf, ntohs(kf->inp_fport)); |
|
673 |
28 |
} |
|
674 |
✓✓ | 1084 |
} else if (kf->so_protocol == IPPROTO_UDP) { |
675 |
1526 |
snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]", |
|
676 |
763 |
inet6_addrstr(&laddr6)); |
|
677 |
763 |
printf(" %s:%d", |
|
678 |
✓✗✓✗ ✓✗✓✓ |
3857 |
IN6_IS_ADDR_UNSPECIFIED(&laddr6) ? "*" : |
679 |
763 |
xaddrbuf, ntohs(kf->inp_lport)); |
|
680 |
✗✓ | 763 |
if (kf->inp_fport) { |
681 |
snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]", |
||
682 |
inet6_addrstr(&faddr6)); |
||
683 |
printf(" <-> %s:%d", |
||
684 |
IN6_IS_ADDR_UNSPECIFIED(&faddr6) ? "*" : |
||
685 |
xaddrbuf, ntohs(kf->inp_fport)); |
||
686 |
} |
||
687 |
✓✗ | 321 |
} else if (kf->so_pcb) { |
688 |
321 |
printf(" "); |
|
689 |
321 |
hide((void *)(uintptr_t)kf->so_pcb); |
|
690 |
321 |
} |
|
691 |
2124 |
} |
|
692 |
|||
693 |
void |
||
694 |
print_sock_details(struct kinfo_file *kf) |
||
695 |
{ |
||
696 |
if (kf->so_family == AF_INET) |
||
697 |
print_inet_details(kf); |
||
698 |
else if (kf->so_family == AF_INET6) |
||
699 |
print_inet6_details(kf); |
||
700 |
} |
||
701 |
|||
702 |
void |
||
703 |
socktrans(struct kinfo_file *kf) |
||
704 |
{ |
||
705 |
static char *stypename[] = { |
||
706 |
"unused", /* 0 */ |
||
707 |
"stream", /* 1 */ |
||
708 |
"dgram", /* 2 */ |
||
709 |
"raw", /* 3 */ |
||
710 |
"rdm", /* 4 */ |
||
711 |
"seqpak" /* 5 */ |
||
712 |
}; |
||
713 |
#define STYPEMAX 5 |
||
714 |
54032 |
char *stype, stypebuf[24]; |
|
715 |
|||
716 |
✗✗✗✗ ✓ |
54032 |
PREFIX(kf->fd_fd); |
717 |
|||
718 |
✗✓ | 27016 |
if (kf->so_type > STYPEMAX) { |
719 |
snprintf(stypebuf, sizeof(stypebuf), "?%d", kf->so_type); |
||
720 |
stype = stypebuf; |
||
721 |
} else { |
||
722 |
27016 |
stype = stypename[kf->so_type]; |
|
723 |
} |
||
724 |
|||
725 |
/* |
||
726 |
* protocol specific formatting |
||
727 |
* |
||
728 |
* Try to find interesting things to print. For tcp, the interesting |
||
729 |
* thing is the address of the tcpcb, for udp and others, just the |
||
730 |
* inpcb (socket pcb). For unix domain, its the address of the socket |
||
731 |
* pcb and the address of the connected pcb (if connected). Otherwise |
||
732 |
* just print the protocol number and address of the socket itself. |
||
733 |
* The idea is not to duplicate netstat, but to make available enough |
||
734 |
* information for further analysis. |
||
735 |
*/ |
||
736 |
✓✓✓✗ ✓✗ |
27016 |
switch (kf->so_family) { |
737 |
case AF_INET: |
||
738 |
2625 |
printf("* internet %s", stype); |
|
739 |
2625 |
getinetproto(kf->so_protocol); |
|
740 |
2625 |
print_inet_details(kf); |
|
741 |
2625 |
break; |
|
742 |
case AF_INET6: |
||
743 |
2124 |
printf("* internet6 %s", stype); |
|
744 |
2124 |
getinetproto(kf->so_protocol); |
|
745 |
2124 |
print_inet6_details(kf); |
|
746 |
2124 |
break; |
|
747 |
case AF_UNIX: |
||
748 |
/* print address of pcb and connected pcb */ |
||
749 |
20983 |
printf("* unix %s", stype); |
|
750 |
✓✗ | 20983 |
if (kf->so_pcb) { |
751 |
20983 |
printf(" "); |
|
752 |
20983 |
hide((void *)(uintptr_t)kf->so_pcb); |
|
753 |
✓✓ | 20983 |
if (kf->unp_conn) { |
754 |
18730 |
char shoconn[4], *cp; |
|
755 |
|||
756 |
18730 |
cp = shoconn; |
|
757 |
✓✗ | 18730 |
if (!(kf->so_state & SS_CANTRCVMORE)) |
758 |
18730 |
*cp++ = '<'; |
|
759 |
18730 |
*cp++ = '-'; |
|
760 |
✓✗ | 18730 |
if (!(kf->so_state & SS_CANTSENDMORE)) |
761 |
18730 |
*cp++ = '>'; |
|
762 |
18730 |
*cp = '\0'; |
|
763 |
18730 |
printf(" %s ", shoconn); |
|
764 |
18730 |
hide((void *)(uintptr_t)kf->unp_conn); |
|
765 |
18730 |
} |
|
766 |
} |
||
767 |
break; |
||
768 |
case AF_MPLS: |
||
769 |
/* print protocol number and socket address */ |
||
770 |
printf("* mpls %s", stype); |
||
771 |
printf(" %d ", kf->so_protocol); |
||
772 |
hide((void *)(uintptr_t)kf->f_data); |
||
773 |
break; |
||
774 |
case AF_ROUTE: |
||
775 |
/* print protocol number and socket address */ |
||
776 |
1284 |
printf("* route %s", stype); |
|
777 |
1284 |
printf(" %d ", kf->so_protocol); |
|
778 |
1284 |
hide((void *)(uintptr_t)kf->f_data); |
|
779 |
1284 |
break; |
|
780 |
default: |
||
781 |
/* print protocol number and socket address */ |
||
782 |
printf("* %d %s", kf->so_family, stype); |
||
783 |
printf(" %d ", kf->so_protocol); |
||
784 |
hide((void *)(uintptr_t)kf->f_data); |
||
785 |
} |
||
786 |
✓✗✗✓ |
54032 |
if (kf->so_splice != 0 || kf->so_splicelen == -1) { |
787 |
struct kinfo_file *from, *to; |
||
788 |
|||
789 |
from = splice_find('<', kf->f_data); |
||
790 |
to = NULL; |
||
791 |
if (kf->so_splice != 0) |
||
792 |
to = splice_find('>', kf->so_splice); |
||
793 |
|||
794 |
if (to != NULL && from == to) { |
||
795 |
printf(" <==>"); |
||
796 |
print_sock_details(to); |
||
797 |
} else if (kf->so_splice != 0) { |
||
798 |
printf(" ==>"); |
||
799 |
if (to != NULL) |
||
800 |
print_sock_details(to); |
||
801 |
} else if (kf->so_splicelen == -1) { |
||
802 |
printf(" <=="); |
||
803 |
if (from != NULL) |
||
804 |
print_sock_details(from); |
||
805 |
} |
||
806 |
} |
||
807 |
✗✓ | 27016 |
if (sflg) |
808 |
printf("\t%8llu %8llu", |
||
809 |
(kf->f_rxfer + kf->f_rwfer), |
||
810 |
(kf->f_rbytes + kf->f_wbytes) / 1024); |
||
811 |
27016 |
printf("\n"); |
|
812 |
27016 |
} |
|
813 |
|||
814 |
/* |
||
815 |
* getinetproto -- |
||
816 |
* print name of protocol number |
||
817 |
*/ |
||
818 |
void |
||
819 |
getinetproto(int number) |
||
820 |
{ |
||
821 |
static int isopen; |
||
822 |
struct protoent *pe; |
||
823 |
|||
824 |
✓✓ | 9498 |
if (!isopen) |
825 |
321 |
setprotoent(++isopen); |
|
826 |
✓✗ | 4749 |
if ((pe = getprotobynumber(number)) != NULL) |
827 |
4749 |
printf(" %s", pe->p_name); |
|
828 |
else |
||
829 |
printf(" %d", number); |
||
830 |
4749 |
} |
|
831 |
|||
832 |
int |
||
833 |
getfname(char *filename) |
||
834 |
{ |
||
835 |
static struct statfs *mntbuf; |
||
836 |
static int nmounts; |
||
837 |
int i; |
||
838 |
struct stat sb; |
||
839 |
struct filearg *cur; |
||
840 |
|||
841 |
if (stat(filename, &sb)) { |
||
842 |
warn("%s", filename); |
||
843 |
return (0); |
||
844 |
} |
||
845 |
|||
846 |
/* |
||
847 |
* POSIX specifies "For block special devices, all processes using any |
||
848 |
* file on that device are listed". However the -f flag description |
||
849 |
* states "The report shall be only for the named files", so we only |
||
850 |
* look up a block device if the -f flag has not be specified. |
||
851 |
*/ |
||
852 |
if (fuser && !fsflg && S_ISBLK(sb.st_mode)) { |
||
853 |
if (mntbuf == NULL) { |
||
854 |
nmounts = getmntinfo(&mntbuf, MNT_NOWAIT); |
||
855 |
if (nmounts == -1) |
||
856 |
err(1, "getmntinfo"); |
||
857 |
} |
||
858 |
for (i = 0; i < nmounts; i++) { |
||
859 |
if (!strcmp(mntbuf[i].f_mntfromname, filename)) { |
||
860 |
if (stat(mntbuf[i].f_mntonname, &sb) == -1) { |
||
861 |
warn("%s", filename); |
||
862 |
return (0); |
||
863 |
} |
||
864 |
cflg = 1; |
||
865 |
break; |
||
866 |
} |
||
867 |
} |
||
868 |
} |
||
869 |
|||
870 |
if ((cur = malloc(sizeof(*cur))) == NULL) |
||
871 |
err(1, NULL); |
||
872 |
|||
873 |
cur->ino = sb.st_ino; |
||
874 |
cur->dev = sb.st_dev & 0xffff; |
||
875 |
cur->name = filename; |
||
876 |
TAILQ_INIT(&cur->fusers); |
||
877 |
SLIST_INSERT_HEAD(&fileargs, cur, next); |
||
878 |
return (1); |
||
879 |
} |
||
880 |
|||
881 |
int |
||
882 |
signame_to_signum(char *sig) |
||
883 |
{ |
||
884 |
int n; |
||
885 |
const char *errstr = NULL; |
||
886 |
|||
887 |
if (isdigit((unsigned char)*sig)) { |
||
888 |
n = strtonum(sig, 0, NSIG - 1, &errstr); |
||
889 |
return (errstr ? -1 : n); |
||
890 |
} |
||
891 |
if (!strncasecmp(sig, "sig", 3)) |
||
892 |
sig += 3; |
||
893 |
for (n = 1; n < NSIG; n++) { |
||
894 |
if (!strcasecmp(sys_signame[n], sig)) |
||
895 |
return (n); |
||
896 |
} |
||
897 |
return (-1); |
||
898 |
} |
||
899 |
|||
900 |
void |
||
901 |
usage(void) |
||
902 |
{ |
||
903 |
if (fuser) { |
||
904 |
fprintf(stderr, "usage: fuser [-cfku] [-M core] " |
||
905 |
"[-N system] [-s signal] file ...\n"); |
||
906 |
} else { |
||
907 |
fprintf(stderr, "usage: fstat [-fnosv] [-M core] [-N system] " |
||
908 |
"[-p pid] [-u user] [file ...]\n"); |
||
909 |
} |
||
910 |
exit(1); |
||
911 |
} |
Generated by: GCOVR (Version 3.3) |