/usr.bin/fstat/fstat.c

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