/* * Given a filename, walk the process tree querying all open files in the * system, and display an indication of the file, which processes have it * open, what they're doing with it, and who the user is (the typical use * of this utility is prior to unmounting a filesystem). */ int showopen(const char *filename, int mode, int username, int (*print)(FILE *, const char *, ...)) { DIR *dir; struct dirent *dp; const struct passwd *pw; match_t match; pid_t pid, usage; uid_t uid; int busy, error; busy = -1; (*print)(stderr, "%s: ", filename); if ((error = buildmatch(filename, mode, &match)) != EOK) { (*print)(stderr, "%s", strerror(error)); } else if ((dir = opendir("/proc")) != NULL) { busy = 0; while ((dp = readdir(dir)) != NULL) { if ((pid = atoi(dp->d_name)) != 0) { if ((usage = howused(pid, &match)) != 0) { (*print)(stdout, "%8d", pid); if (usage & USED_CWD) (*print)(stderr, "c"); if (usage & USED_OPEN) (*print)(stderr, "o"); if (usage & USED_ROOT) (*print)(stderr, "r"); if (usage & USED_CTTY) (*print)(stderr, "y"); if (username && (uid = whois(pid)) != (uid_t)-1) { if ((pw = getpwuid(uid)) != NULL) (*print)(stderr, "(%s)", pw->pw_name); else (*print)(stderr, "(%d)", uid); } ++busy; } } } closedir(dir); } (*print)(stderr, "\n"); return(busy); }
int main(int argc, char **argv) { int c, todo; u_int interval; /* milliseconds */ int reps; char *memf, *nlistf; char errbuf[_POSIX2_LINE_MAX]; memf = nlistf = NULL; interval = reps = todo = 0; maxshowdevs = 2; while ((c = getopt(argc, argv, "c:fiM:mN:n:p:stvw:z")) != -1) { switch (c) { case 'c': reps = atoi(optarg); break; case 'f': #ifdef notyet todo |= FORKSTAT; #else errx(EX_USAGE, "sorry, -f is not (re)implemented yet"); #endif break; case 'i': todo |= INTRSTAT; break; case 'M': memf = optarg; break; case 'm': todo |= MEMSTAT; break; case 'N': nlistf = optarg; break; case 'n': nflag = 1; maxshowdevs = atoi(optarg); if (maxshowdevs < 0) errx(1, "number of devices %d is < 0", maxshowdevs); break; case 'p': if (buildmatch(optarg, &matches, &num_matches) != 0) errx(1, "%s", devstat_errbuf); break; case 's': todo |= SUMSTAT; break; case 't': #ifdef notyet todo |= TIMESTAT; #else errx(EX_USAGE, "sorry, -t is not (re)implemented yet"); #endif break; case 'v': ++verbose; break; case 'w': interval = (u_int)(strtod(optarg, NULL) * 1000.0); break; case 'z': todo |= ZMEMSTAT; break; default: usage(); } } argc -= optind; argv += optind; if (todo == 0) todo = VMSTAT; /* * Discard setgid privileges if not the running kernel so that bad * guys can't print interesting stuff from kernel memory. */ if (nlistf != NULL || memf != NULL) setgid(getgid()); kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); if (kd == NULL) errx(1, "kvm_openfiles: %s", errbuf); if ((c = kvm_nlist(kd, namelist)) != 0) { if (c > 0) { warnx("undefined symbols:"); for (c = 0; c < (int)__arysize(namelist); c++) if (namelist[c].n_type == 0) fprintf(stderr, " %s", namelist[c].n_name); fputc('\n', stderr); } else warnx("kvm_nlist: %s", kvm_geterr(kd)); exit(1); } if (todo & VMSTAT) { struct winsize winsize; /* * Make sure that the userland devstat version matches the * kernel devstat version. If not, exit and print a * message informing the user of his mistake. */ if (checkversion() < 0) errx(1, "%s", devstat_errbuf); argv = getdrivedata(argv); winsize.ws_row = 0; ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize); if (winsize.ws_row > 0) winlines = winsize.ws_row; } #define BACKWARD_COMPATIBILITY #ifdef BACKWARD_COMPATIBILITY if (*argv) { interval = (u_int)(strtod(*argv, NULL) * 1000.0); if (*++argv) reps = atoi(*argv); } #endif if (interval) { if (!reps) reps = -1; } else if (reps) { interval = 1000; } #ifdef notyet if (todo & FORKSTAT) doforkst(); #endif if (todo & MEMSTAT) domem(); if (todo & ZMEMSTAT) dozmem(); if (todo & SUMSTAT) dosum(); #ifdef notyet if (todo & TIMESTAT) dotimes(); #endif if (todo & INTRSTAT) dointr(); if (todo & VMSTAT) dovmstat(interval, reps); exit(0); }