1/* vi: set sw=4 ts=4: */ 2/* 3 * tiny fuser implementation 4 * 5 * Copyright 2004 Tony J. White 6 * 7 * Licensed under GPLv2, see file LICENSE in this tarball for details. 8 */ 9 10#include "libbb.h" 11 12#define MAX_LINE 255 13 14#define OPTION_STRING "mks64" 15enum { 16 OPT_MOUNT = (1 << 0), 17 OPT_KILL = (1 << 1), 18 OPT_SILENT = (1 << 2), 19 OPT_IP6 = (1 << 3), 20 OPT_IP4 = (1 << 4), 21}; 22 23typedef struct inode_list { 24 struct inode_list *next; 25 ino_t inode; 26 dev_t dev; 27} inode_list; 28 29typedef struct pid_list { 30 struct pid_list *next; 31 pid_t pid; 32} pid_list; 33 34 35struct globals { 36 pid_list *pid_list_head; 37 inode_list *inode_list_head; 38}; 39#define G (*(struct globals*)&bb_common_bufsiz1) 40#define INIT_G() do { } while (0) 41 42 43static void add_pid(const pid_t pid) 44{ 45 pid_list **curr = &G.pid_list_head; 46 47 while (*curr) { 48 if ((*curr)->pid == pid) 49 return; 50 curr = &(*curr)->next; 51 } 52 53 *curr = xzalloc(sizeof(pid_list)); 54 (*curr)->pid = pid; 55} 56 57static void add_inode(const struct stat *st) 58{ 59 inode_list **curr = &G.inode_list_head; 60 61 while (*curr) { 62 if ((*curr)->dev == st->st_dev 63 && (*curr)->inode == st->st_ino 64 ) { 65 return; 66 } 67 curr = &(*curr)->next; 68 } 69 70 *curr = xzalloc(sizeof(inode_list)); 71 (*curr)->dev = st->st_dev; 72 (*curr)->inode = st->st_ino; 73} 74 75static void scan_proc_net(const char *path, unsigned port) 76{ 77 char line[MAX_LINE + 1]; 78 long long uint64_inode; 79 unsigned tmp_port; 80 FILE *f; 81 struct stat st; 82 int fd; 83 84 /* find socket dev */ 85 st.st_dev = 0; 86 fd = socket(AF_INET, SOCK_DGRAM, 0); 87 if (fd >= 0) { 88 fstat(fd, &st); 89 close(fd); 90 } 91 92 f = fopen_for_read(path); 93 if (!f) 94 return; 95 96 while (fgets(line, MAX_LINE, f)) { 97 char addr[68]; 98 if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x " 99 "%*x:%*x %*x %*d %*d %llu", 100 addr, &tmp_port, &uint64_inode) == 3 101 ) { 102 int len = strlen(addr); 103 if (len == 8 && (option_mask32 & OPT_IP6)) 104 continue; 105 if (len > 8 && (option_mask32 & OPT_IP4)) 106 continue; 107 if (tmp_port == port) { 108 st.st_ino = uint64_inode; 109 add_inode(&st); 110 } 111 } 112 } 113 fclose(f); 114} 115 116static int search_dev_inode(const struct stat *st) 117{ 118 inode_list *ilist = G.inode_list_head; 119 120 while (ilist) { 121 if (ilist->dev == st->st_dev) { 122 if (option_mask32 & OPT_MOUNT) 123 return 1; 124 if (ilist->inode == st->st_ino) 125 return 1; 126 } 127 ilist = ilist->next; 128 } 129 return 0; 130} 131 132static void scan_pid_maps(const char *fname, pid_t pid) 133{ 134 FILE *file; 135 char line[MAX_LINE + 1]; 136 int major, minor; 137 long long uint64_inode; 138 struct stat st; 139 140 file = fopen_for_read(fname); 141 if (!file) 142 return; 143 144 while (fgets(line, MAX_LINE, file)) { 145 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) 146 continue; 147 st.st_ino = uint64_inode; 148 if (major == 0 && minor == 0 && st.st_ino == 0) 149 continue; 150 st.st_dev = makedev(major, minor); 151 if (search_dev_inode(&st)) 152 add_pid(pid); 153 } 154 fclose(file); 155} 156 157static void scan_link(const char *lname, pid_t pid) 158{ 159 struct stat st; 160 161 if (stat(lname, &st) >= 0) { 162 if (search_dev_inode(&st)) 163 add_pid(pid); 164 } 165} 166 167static void scan_dir_links(const char *dname, pid_t pid) 168{ 169 DIR *d; 170 struct dirent *de; 171 char *lname; 172 173 d = opendir(dname); 174 if (!d) 175 return; 176 177 while ((de = readdir(d)) != NULL) { 178 lname = concat_subpath_file(dname, de->d_name); 179 if (lname == NULL) 180 continue; 181 scan_link(lname, pid); 182 free(lname); 183 } 184 closedir(d); 185} 186 187/* NB: does chdir internally */ 188static void scan_proc_pids(void) 189{ 190 DIR *d; 191 struct dirent *de; 192 pid_t pid; 193 194 xchdir("/proc"); 195 d = opendir("/proc"); 196 if (!d) 197 return; 198 199 while ((de = readdir(d)) != NULL) { 200 pid = (pid_t)bb_strtou(de->d_name, NULL, 10); 201 if (errno) 202 continue; 203 if (chdir(de->d_name) < 0) 204 continue; 205 scan_link("cwd", pid); 206 scan_link("exe", pid); 207 scan_link("root", pid); 208 209 scan_dir_links("fd", pid); 210 scan_dir_links("lib", pid); 211 scan_dir_links("mmap", pid); 212 213 scan_pid_maps("maps", pid); 214 xchdir("/proc"); 215 } 216 closedir(d); 217} 218 219int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 220int fuser_main(int argc UNUSED_PARAM, char **argv) 221{ 222 pid_list *plist; 223 pid_t mypid; 224 char **pp; 225 struct stat st; 226 unsigned port; 227 int opt; 228 int exitcode; 229 int killsig; 230/* 231fuser [OPTIONS] FILE or PORT/PROTO 232Find processes which use FILEs or PORTs 233 -m Find processes which use same fs as FILEs 234 -4 Search only IPv4 space 235 -6 Search only IPv6 space 236 -s Don't display PIDs 237 -k Kill found processes 238 -SIGNAL Signal to send (default: KILL) 239*/ 240 /* Handle -SIGNAL. Oh my... */ 241 killsig = SIGKILL; /* yes, the default is not SIGTERM */ 242 pp = argv; 243 while (*++pp) { 244 char *arg = *pp; 245 if (arg[0] != '-') 246 continue; 247 if (arg[1] == '-' && arg[2] == '\0') /* "--" */ 248 break; 249 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') 250 continue; /* it's "-4" or "-6" */ 251 opt = get_signum(&arg[1]); 252 if (opt < 0) 253 continue; 254 /* "-SIGNAL" option found. Remove it and bail out */ 255 killsig = opt; 256 do { 257 pp[0] = arg = pp[1]; 258 pp++; 259 } while (arg); 260 break; 261 } 262 263 opt_complementary = "-1"; /* at least one param */ 264 opt = getopt32(argv, OPTION_STRING); 265 argv += optind; 266 267 pp = argv; 268 while (*pp) { 269 /* parse net arg */ 270 char path[20], tproto[5]; 271 if (sscanf(*pp, "%u/%4s", &port, tproto) != 2) 272 goto file; 273 sprintf(path, "/proc/net/%s", tproto); 274 if (access(path, R_OK) != 0) { /* PORT/PROTO */ 275 scan_proc_net(path, port); 276 } else { /* FILE */ 277 file: 278 xstat(*pp, &st); 279 add_inode(&st); 280 } 281 pp++; 282 } 283 284 scan_proc_pids(); /* changes dir to "/proc" */ 285 286 mypid = getpid(); 287 plist = G.pid_list_head; 288 while (1) { 289 if (!plist) 290 return EXIT_FAILURE; 291 if (plist->pid != mypid) 292 break; 293 plist = plist->next; 294 } 295 296 exitcode = EXIT_SUCCESS; 297 do { 298 if (plist->pid != mypid) { 299 if (opt & OPT_KILL) { 300 if (kill(plist->pid, killsig) != 0) { 301 bb_perror_msg("kill pid %u", (unsigned)plist->pid); 302 exitcode = EXIT_FAILURE; 303 } 304 } 305 if (!(opt & OPT_SILENT)) { 306 printf("%u ", (unsigned)plist->pid); 307 } 308 } 309 plist = plist->next; 310 } while (plist); 311 312 if (!(opt & (OPT_SILENT))) { 313 bb_putchar('\n'); 314 } 315 316 return exitcode; 317} 318