[3320] | 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 source tree.
|
---|
| 8 | */
|
---|
| 9 |
|
---|
| 10 | #include "libbb.h"
|
---|
| 11 |
|
---|
| 12 | #define MAX_LINE 255
|
---|
| 13 |
|
---|
| 14 | #define OPTION_STRING "mks64"
|
---|
| 15 | enum {
|
---|
| 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 |
|
---|
| 23 | typedef struct inode_list {
|
---|
| 24 | struct inode_list *next;
|
---|
| 25 | ino_t inode;
|
---|
| 26 | dev_t dev;
|
---|
| 27 | } inode_list;
|
---|
| 28 |
|
---|
| 29 | typedef struct pid_list {
|
---|
| 30 | struct pid_list *next;
|
---|
| 31 | pid_t pid;
|
---|
| 32 | } pid_list;
|
---|
| 33 |
|
---|
| 34 |
|
---|
| 35 | struct 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 |
|
---|
| 43 | static 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 |
|
---|
| 57 | static 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 |
|
---|
| 75 | static 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 |
|
---|
| 116 | static 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 |
|
---|
| 132 | static 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 |
|
---|
| 157 | static 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 |
|
---|
| 167 | static 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 */
|
---|
| 188 | static 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 |
|
---|
| 219 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
---|
| 220 | int 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 | /*
|
---|
| 231 | fuser [OPTIONS] FILE or PORT/PROTO
|
---|
| 232 | Find 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 | }
|
---|