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