diff options
-rw-r--r-- | procps/fuser.c | 386 |
1 files changed, 190 insertions, 196 deletions
diff --git a/procps/fuser.c b/procps/fuser.c index 7837ff8..2a7c310 100644 --- a/procps/fuser.c +++ b/procps/fuser.c @@ -37,33 +37,18 @@ typedef struct inode_list { dev_t dev; } inode_list; -typedef struct pid_list { - struct pid_list *next; - pid_t pid; -} pid_list; - - struct globals { - pid_list *pid_list_head; + 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 { } while (0) - - -static void add_pid(const pid_t pid) -{ - pid_list **curr = &G.pid_list_head; - - while (*curr) { - if ((*curr)->pid == pid) - return; - curr = &(*curr)->next; - } - - *curr = xzalloc(sizeof(pid_list)); - (*curr)->pid = pid; -} +#define INIT_G() do { \ + G.mypid = getpid(); \ + G.killsig = SIGKILL; \ +} while (0) static void add_inode(const struct stat *st) { @@ -83,48 +68,7 @@ static void add_inode(const struct stat *st) (*curr)->inode = st->st_ino; } -static void scan_proc_net(const char *path, unsigned port) -{ - char line[MAX_LINE + 1]; - long long uint64_inode; - unsigned tmp_port; - FILE *f; - struct stat st; - int fd; - - /* find socket dev */ - st.st_dev = 0; - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd >= 0) { - fstat(fd, &st); - close(fd); - } - - f = fopen_for_read(path); - if (!f) - return; - - while (fgets(line, MAX_LINE, f)) { - char addr[68]; - if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x " - "%*x:%*x %*x %*d %*d %llu", - addr, &tmp_port, &uint64_inode) == 3 - ) { - int len = strlen(addr); - if (len == 8 && (option_mask32 & OPT_IP6)) - continue; - if (len > 8 && (option_mask32 & OPT_IP4)) - continue; - if (tmp_port == port) { - st.st_ino = uint64_inode; - add_inode(&st); - } - } - } - fclose(f); -} - -static int search_dev_inode(const struct stat *st) +static smallint search_dev_inode(const struct stat *st) { inode_list *ilist = G.inode_list_head; @@ -140,130 +84,202 @@ static int search_dev_inode(const struct stat *st) return 0; } -static void scan_pid_maps(const char *fname, pid_t pid) +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 *file; - char line[MAX_LINE + 1]; - int major, minor; + FILE *f; + char line[MAX_LINE + 1], addr[68]; + int major, minor, r; long long uint64_inode; - struct stat st; + unsigned tmp_port; + smallint retval; + struct stat statbuf; + const char *fmt; + void *fag, *sag; - file = fopen_for_read(fname); - if (!file) - return; + f = fopen_for_read(path); + if (!f) + return 0; - while (fgets(line, MAX_LINE, file)) { - if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) - continue; - st.st_ino = uint64_inode; - if (major == 0 && minor == 0 && st.st_ino == 0) - continue; - st.st_dev = makedev(major, minor); - if (search_dev_inode(&st)) - add_pid(pid); - } - fclose(file); -} + if (G.recursion_depth == PROC_NET) { + int fd; -static void scan_link(const char *lname, pid_t pid) -{ - struct stat st; + /* find socket dev */ + statbuf.st_dev = 0; + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd >= 0) { + fstat(fd, &statbuf); + close(fd); + } - if (stat(lname, &st) >= 0) { - if (search_dev_inode(&st)) - add_pid(pid); + 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; } -} - -static void scan_dir_links(const char *dname, pid_t pid) -{ - DIR *d; - struct dirent *de; - char *lname; - - d = opendir(dname); - if (!d) - return; - while ((de = readdir(d)) != NULL) { - lname = concat_subpath_file(dname, de->d_name); - if (lname == NULL) + retval = 0; + while (fgets(line, MAX_LINE, f)) { + r = sscanf(line, fmt, fag, sag, &uint64_inode); + if (r != 3) continue; - scan_link(lname, pid); - free(lname); + + 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; + } + } } - closedir(d); + fclose(f); + + return retval; } -/* NB: does chdir internally */ -static void scan_proc_pids(void) +static smallint scan_recursive(const char *path) { DIR *d; - struct dirent *de; - pid_t pid; - - xchdir("/proc"); - d = opendir("/proc"); - if (!d) - return; - - while ((de = readdir(d)) != NULL) { - pid = (pid_t)bb_strtou(de->d_name, NULL, 10); - if (errno) - continue; - if (chdir(de->d_name) < 0) - continue; - scan_link("cwd", pid); - scan_link("exe", pid); - scan_link("root", pid); - - scan_dir_links("fd", pid); - scan_dir_links("lib", pid); - scan_dir_links("mmap", pid); + 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; - scan_pid_maps("maps", pid); - xchdir("/proc"); + 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) { - pid_list *plist; - pid_t mypid; char **pp; - struct stat st; - unsigned port; - int opt; - int exitcode; - int killsig; -/* -fuser [OPTIONS] FILE or PORT/PROTO -Find processes which use FILEs or PORTs - -m Find processes which use same fs as FILEs - -4 Search only IPv4 space - -6 Search only IPv6 space - -s Don't display PIDs - -k Kill found processes - -SIGNAL Signal to send (default: KILL) -*/ + + INIT_G(); + /* Handle -SIGNAL. Oh my... */ - killsig = SIGKILL; /* yes, the default is not SIGTERM */ 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" */ - opt = get_signum(&arg[1]); - if (opt < 0) + sig = get_signum(&arg[1]); + if (sig < 0) continue; /* "-SIGNAL" option found. Remove it and bail out */ - killsig = opt; + G.killsig = sig; do { pp[0] = arg = pp[1]; pp++; @@ -272,57 +288,35 @@ Find processes which use FILEs or PORTs } opt_complementary = "-1"; /* at least one param */ - opt = getopt32(argv, OPTION_STRING); + getopt32(argv, OPTION_STRING); argv += optind; pp = argv; while (*pp) { /* parse net arg */ - char path[20], tproto[5]; - if (sscanf(*pp, "%u/%4s", &port, tproto) != 2) - goto file; - sprintf(path, "/proc/net/%s", tproto); - if (access(path, R_OK) != 0) { /* PORT/PROTO */ - scan_proc_net(path, port); - } else { /* FILE */ - file: - xstat(*pp, &st); - add_inode(&st); + 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++; } - scan_proc_pids(); /* changes dir to "/proc" */ - - mypid = getpid(); - plist = G.pid_list_head; - while (1) { - if (!plist) - return EXIT_FAILURE; - if (plist->pid != mypid) - break; - plist = plist->next; - } - - exitcode = EXIT_SUCCESS; - do { - if (plist->pid != mypid) { - if (opt & OPT_KILL) { - if (kill(plist->pid, killsig) != 0) { - bb_perror_msg("kill pid %u", (unsigned)plist->pid); - exitcode = EXIT_FAILURE; - } - } - if (!(opt & OPT_SILENT)) { - printf("%u ", (unsigned)plist->pid); - } - } - plist = plist->next; - } while (plist); - - if (!(opt & (OPT_SILENT))) { - bb_putchar('\n'); + if (scan_recursive("/proc")) { + if (!(option_mask32 & OPT_SILENT)) + bb_putchar('\n'); + return G.kill_failed; } - return exitcode; + return EXIT_FAILURE; } |