/* vi: set sw=4 ts=4: */ /* * tiny fuser implementation * * Copyright 2004 Tony J. White * * Licensed under GPLv2, see file LICENSE in this source tree. */ //usage:#define fuser_trivial_usage //usage: "[OPTIONS] FILE or PORT/PROTO" //usage:#define fuser_full_usage "\n\n" //usage: "Find processes which use FILEs or PORTs\n" //usage: "\n -m Find processes which use same fs as FILEs" //usage: "\n -4,-6 Search only IPv4/IPv6 space" //usage: "\n -s Don't display PIDs" //usage: "\n -k Kill found processes" //usage: "\n -SIGNAL Signal to send (default: KILL)" #include "libbb.h" #define MAX_LINE 255 #define OPTION_STRING "mks64" enum { OPT_MOUNT = (1 << 0), OPT_KILL = (1 << 1), OPT_SILENT = (1 << 2), OPT_IP6 = (1 << 3), OPT_IP4 = (1 << 4), }; typedef struct inode_list { struct inode_list *next; ino_t inode; dev_t dev; } inode_list; struct globals { int recursion_depth; pid_t mypid; inode_list *inode_list_head; smallint kill_failed; int killsig; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ G.mypid = getpid(); \ G.killsig = SIGKILL; \ } while (0) static void add_inode(const struct stat *st) { inode_list **curr = &G.inode_list_head; while (*curr) { if ((*curr)->dev == st->st_dev && (*curr)->inode == st->st_ino ) { return; } curr = &(*curr)->next; } *curr = xzalloc(sizeof(inode_list)); (*curr)->dev = st->st_dev; (*curr)->inode = st->st_ino; } static smallint search_dev_inode(const struct stat *st) { inode_list *ilist = G.inode_list_head; while (ilist) { if (ilist->dev == st->st_dev) { if (option_mask32 & OPT_MOUNT) return 1; if (ilist->inode == st->st_ino) return 1; } ilist = ilist->next; } return 0; } enum { PROC_NET = 0, PROC_DIR, PROC_DIR_LINKS, PROC_SUBDIR_LINKS, }; static smallint scan_proc_net_or_maps(const char *path, unsigned port) { FILE *f; char line[MAX_LINE + 1], addr[68]; int major, minor, r; long long uint64_inode; unsigned tmp_port; smallint retval; struct stat statbuf; const char *fmt; void *fag, *sag; f = fopen_for_read(path); if (!f) return 0; if (G.recursion_depth == PROC_NET) { int fd; /* find socket dev */ statbuf.st_dev = 0; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd >= 0) { fstat(fd, &statbuf); close(fd); } fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x " "%*x:%*x %*x:%*x %*x %*d %*d %llu"; fag = addr; sag = &tmp_port; } else { fmt = "%*s %*s %*s %x:%x %llu"; fag = &major; sag = &minor; } retval = 0; while (fgets(line, MAX_LINE, f)) { r = sscanf(line, fmt, fag, sag, &uint64_inode); if (r != 3) continue; statbuf.st_ino = uint64_inode; if (G.recursion_depth == PROC_NET) { r = strlen(addr); if (r == 8 && (option_mask32 & OPT_IP6)) continue; if (r > 8 && (option_mask32 & OPT_IP4)) continue; if (tmp_port == port) add_inode(&statbuf); } else { if (major != 0 && minor != 0 && statbuf.st_ino != 0) { statbuf.st_dev = makedev(major, minor); retval = search_dev_inode(&statbuf); if (retval) break; } } } fclose(f); return retval; } static smallint scan_recursive(const char *path) { DIR *d; struct dirent *d_ent; smallint stop_scan; smallint retval; d = opendir(path); if (d == NULL) return 0; G.recursion_depth++; retval = 0; stop_scan = 0; while (!stop_scan && (d_ent = readdir(d)) != NULL) { struct stat statbuf; pid_t pid; char *subpath; subpath = concat_subpath_file(path, d_ent->d_name); if (subpath == NULL) continue; /* . or .. */ switch (G.recursion_depth) { case PROC_DIR: pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10); if (errno != 0 || pid == G.mypid /* "this PID doesn't use specified FILEs or PORT/PROTO": */ || scan_recursive(subpath) == 0 ) { break; } if (option_mask32 & OPT_KILL) { if (kill(pid, G.killsig) != 0) { bb_perror_msg("kill pid %s", d_ent->d_name); G.kill_failed = 1; } } if (!(option_mask32 & OPT_SILENT)) printf("%s ", d_ent->d_name); retval = 1; break; case PROC_DIR_LINKS: switch ( index_in_substrings( "cwd" "\0" "exe" "\0" "root" "\0" "fd" "\0" "lib" "\0" "mmap" "\0" "maps" "\0", d_ent->d_name ) ) { enum { CWD_LINK, EXE_LINK, ROOT_LINK, FD_DIR_LINKS, LIB_DIR_LINKS, MMAP_DIR_LINKS, MAPS, }; case CWD_LINK: case EXE_LINK: case ROOT_LINK: goto scan_link; case FD_DIR_LINKS: case LIB_DIR_LINKS: case MMAP_DIR_LINKS: stop_scan = scan_recursive(subpath); if (stop_scan) retval = stop_scan; break; case MAPS: stop_scan = scan_proc_net_or_maps(subpath, 0); if (stop_scan) retval = stop_scan; default: break; } break; case PROC_SUBDIR_LINKS: scan_link: if (stat(subpath, &statbuf) < 0) break; stop_scan = search_dev_inode(&statbuf); if (stop_scan) retval = stop_scan; default: break; } free(subpath); } closedir(d); G.recursion_depth--; return retval; } int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fuser_main(int argc UNUSED_PARAM, char **argv) { char **pp; INIT_G(); /* Handle -SIGNAL. Oh my... */ pp = argv; while (*++pp) { int sig; char *arg = *pp; if (arg[0] != '-') continue; if (arg[1] == '-' && arg[2] == '\0') /* "--" */ break; if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') continue; /* it's "-4" or "-6" */ sig = get_signum(&arg[1]); if (sig < 0) continue; /* "-SIGNAL" option found. Remove it and bail out */ G.killsig = sig; do { pp[0] = arg = pp[1]; pp++; } while (arg); break; } opt_complementary = "-1"; /* at least one param */ getopt32(argv, OPTION_STRING); argv += optind; pp = argv; while (*pp) { /* parse net arg */ unsigned port; char path[sizeof("/proc/net/TCP6")]; strcpy(path, "/proc/net/"); if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2 && access(path, R_OK) == 0 ) { /* PORT/PROTO */ scan_proc_net_or_maps(path, port); } else { /* FILE */ struct stat statbuf; xstat(*pp, &statbuf); add_inode(&statbuf); } pp++; } if (scan_recursive("/proc")) { if (!(option_mask32 & OPT_SILENT)) bb_putchar('\n'); return G.kill_failed; } return EXIT_FAILURE; }