busybox/procps/fuser.c
<<
>>
Prefs
   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
  34static dev_t find_socket_dev(void)
  35{
  36        int fd = socket(AF_INET, SOCK_DGRAM, 0);
  37        if (fd >= 0) {
  38                struct stat buf;
  39                int r = fstat(fd, &buf);
  40                close(fd);
  41                if (r == 0)
  42                        return buf.st_dev;
  43        }
  44        return 0;
  45}
  46
  47static int file_to_dev_inode(const char *filename, dev_t *dev, ino_t *inode)
  48{
  49        struct stat f_stat;
  50        if (stat(filename, &f_stat))
  51                return 0;
  52        *inode = f_stat.st_ino;
  53        *dev = f_stat.st_dev;
  54        return 1;
  55}
  56
  57static char *parse_net_arg(const char *arg, unsigned *port)
  58{
  59        char path[20], tproto[5];
  60
  61        if (sscanf(arg, "%u/%4s", port, tproto) != 2)
  62                return NULL;
  63        sprintf(path, "/proc/net/%s", tproto);
  64        if (access(path, R_OK) != 0)
  65                return NULL;
  66        return xstrdup(tproto);
  67}
  68
  69static pid_list *add_pid(pid_list *plist, pid_t pid)
  70{
  71        pid_list *curr = plist;
  72        while (curr != NULL) {
  73                if (curr->pid == pid)
  74                        return plist;
  75                curr = curr->next;
  76        }
  77        curr = xmalloc(sizeof(pid_list));
  78        curr->pid = pid;
  79        curr->next = plist;
  80        return curr;
  81}
  82
  83static inode_list *add_inode(inode_list *ilist, dev_t dev, ino_t inode)
  84{
  85        inode_list *curr = ilist;
  86        while (curr != NULL) {
  87                if (curr->inode == inode && curr->dev == dev)
  88                        return ilist;
  89                curr = curr->next;
  90        }
  91        curr = xmalloc(sizeof(inode_list));
  92        curr->dev = dev;
  93        curr->inode = inode;
  94        curr->next = ilist;
  95        return curr;
  96}
  97
  98static inode_list *scan_proc_net(const char *proto,
  99                                unsigned port, inode_list *ilist)
 100{
 101        char path[20], line[MAX_LINE + 1];
 102        ino_t tmp_inode;
 103        dev_t tmp_dev;
 104        long long uint64_inode;
 105        unsigned tmp_port;
 106        FILE *f;
 107
 108        tmp_dev = find_socket_dev();
 109
 110        sprintf(path, "/proc/net/%s", proto);
 111        f = fopen_for_read(path);
 112        if (!f)
 113                return ilist;
 114
 115        while (fgets(line, MAX_LINE, f)) {
 116                char addr[68];
 117                if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
 118                                "%*x:%*x %*x %*d %*d %llu",
 119                                addr, &tmp_port, &uint64_inode) == 3
 120                ) {
 121                        int len = strlen(addr);
 122                        if (len == 8 && (option_mask32 & OPT_IP6))
 123                                continue;
 124                        if (len > 8 && (option_mask32 & OPT_IP4))
 125                                continue;
 126                        if (tmp_port == port) {
 127                                tmp_inode = uint64_inode;
 128                                ilist = add_inode(ilist, tmp_dev, tmp_inode);
 129                        }
 130                }
 131        }
 132        fclose(f);
 133        return ilist;
 134}
 135
 136static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode)
 137{
 138        while (ilist) {
 139                if (ilist->dev == dev) {
 140                        if (option_mask32 & OPT_MOUNT)
 141                                return 1;
 142                        if (ilist->inode == inode)
 143                                return 1;
 144                }
 145                ilist = ilist->next;
 146        }
 147        return 0;
 148}
 149
 150static pid_list *scan_pid_maps(const char *fname, pid_t pid,
 151                                inode_list *ilist, pid_list *plist)
 152{
 153        FILE *file;
 154        char line[MAX_LINE + 1];
 155        int major, minor;
 156        ino_t inode;
 157        long long uint64_inode;
 158        dev_t dev;
 159
 160        file = fopen_for_read(fname);
 161        if (!file)
 162                return plist;
 163        while (fgets(line, MAX_LINE, file)) {
 164                if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
 165                        continue;
 166                inode = uint64_inode;
 167                if (major == 0 && minor == 0 && inode == 0)
 168                        continue;
 169                dev = makedev(major, minor);
 170                if (search_dev_inode(ilist, dev, inode))
 171                        plist = add_pid(plist, pid);
 172        }
 173        fclose(file);
 174        return plist;
 175}
 176
 177static pid_list *scan_link(const char *lname, pid_t pid,
 178                                inode_list *ilist, pid_list *plist)
 179{
 180        ino_t inode;
 181        dev_t dev;
 182
 183        if (!file_to_dev_inode(lname, &dev, &inode))
 184                return plist;
 185        if (search_dev_inode(ilist, dev, inode))
 186                plist = add_pid(plist, pid);
 187        return plist;
 188}
 189
 190static pid_list *scan_dir_links(const char *dname, pid_t pid,
 191                                inode_list *ilist, pid_list *plist)
 192{
 193        DIR *d;
 194        struct dirent *de;
 195        char *lname;
 196
 197        d = opendir(dname);
 198        if (!d)
 199                return plist;
 200        while ((de = readdir(d)) != NULL) {
 201                lname = concat_subpath_file(dname, de->d_name);
 202                if (lname == NULL)
 203                        continue;
 204                plist = scan_link(lname, pid, ilist, plist);
 205                free(lname);
 206        }
 207        closedir(d);
 208        return plist;
 209}
 210
 211/* NB: does chdir internally */
 212static pid_list *scan_proc_pids(inode_list *ilist)
 213{
 214        DIR *d;
 215        struct dirent *de;
 216        pid_t pid;
 217        pid_list *plist;
 218
 219        xchdir("/proc");
 220        d = opendir("/proc");
 221        if (!d)
 222                return NULL;
 223
 224        plist = NULL;
 225        while ((de = readdir(d)) != NULL) {
 226                pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
 227                if (errno)
 228                        continue;
 229                if (chdir(de->d_name) < 0)
 230                        continue;
 231                plist = scan_link("cwd", pid, ilist, plist);
 232                plist = scan_link("exe", pid, ilist, plist);
 233                plist = scan_link("root", pid, ilist, plist);
 234                plist = scan_dir_links("fd", pid, ilist, plist);
 235                plist = scan_dir_links("lib", pid, ilist, plist);
 236                plist = scan_dir_links("mmap", pid, ilist, plist);
 237                plist = scan_pid_maps("maps", pid, ilist, plist);
 238                xchdir("/proc");
 239        }
 240        closedir(d);
 241        return plist;
 242}
 243
 244static int print_pid_list(pid_list *plist)
 245{
 246        while (plist != NULL) {
 247                printf("%u ", (unsigned)plist->pid);
 248                plist = plist->next;
 249        }
 250        bb_putchar('\n');
 251        return 1;
 252}
 253
 254static int kill_pid_list(pid_list *plist, int sig)
 255{
 256        pid_t mypid = getpid();
 257        int success = 1;
 258
 259        while (plist != NULL) {
 260                if (plist->pid != mypid) {
 261                        if (kill(plist->pid, sig) != 0) {
 262                                bb_perror_msg("kill pid %u", (unsigned)plist->pid);
 263                                success = 0;
 264                        }
 265                }
 266                plist = plist->next;
 267        }
 268        return success;
 269}
 270
 271int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 272int fuser_main(int argc UNUSED_PARAM, char **argv)
 273{
 274        pid_list *plist;
 275        inode_list *ilist;
 276        char **pp;
 277        dev_t dev;
 278        ino_t inode;
 279        unsigned port;
 280        int opt;
 281        int success;
 282        int killsig;
 283/*
 284fuser [options] FILEs or PORT/PROTOs
 285Find processes which use FILEs or PORTs
 286        -m      Find processes which use same fs as FILEs
 287        -4      Search only IPv4 space
 288        -6      Search only IPv6 space
 289        -s      Silent: just exit with 0 if any processes are found
 290        -k      Kill found processes (otherwise display PIDs)
 291        -SIGNAL Signal to send (default: TERM)
 292*/
 293        /* Handle -SIGNAL. Oh my... */
 294        killsig = SIGTERM;
 295        pp = argv;
 296        while (*++pp) {
 297                char *arg = *pp;
 298                if (arg[0] != '-')
 299                        continue;
 300                if (arg[1] == '-' && arg[2] == '\0') /* "--" */
 301                        break;
 302                if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
 303                        continue; /* it's "-4" or "-6" */
 304                opt = get_signum(&arg[1]);
 305                if (opt < 0)
 306                        continue;
 307                /* "-SIGNAL" option found. Remove it and bail out */
 308                killsig = opt;
 309                do {
 310                        pp[0] = arg = pp[1];
 311                        pp++;
 312                } while (arg);
 313                break;
 314        }
 315
 316        opt = getopt32(argv, OPTION_STRING);
 317        argv += optind;
 318
 319        ilist = NULL;
 320        pp = argv;
 321        while (*pp) {
 322                char *proto = parse_net_arg(*pp, &port);
 323                if (proto) { /* PORT/PROTO */
 324                        ilist = scan_proc_net(proto, port, ilist);
 325                        free(proto);
 326                } else { /* FILE */
 327                        if (!file_to_dev_inode(*pp, &dev, &inode))
 328                                bb_perror_msg_and_die("can't open '%s'", *pp);
 329                        ilist = add_inode(ilist, dev, inode);
 330                }
 331                pp++;
 332        }
 333
 334        plist = scan_proc_pids(ilist); /* changes dir to "/proc" */
 335
 336        if (!plist)
 337                return EXIT_FAILURE;
 338        success = 1;
 339        if (opt & OPT_KILL) {
 340                success = kill_pid_list(plist, killsig);
 341        } else if (!(opt & OPT_SILENT)) {
 342                success = print_pid_list(plist);
 343        }
 344        return (success != 1); /* 0 == success */
 345}
 346