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