diff options
author | Aaron Ball <nullspoon@oper.io> | 2021-07-22 21:35:30 -0600 |
---|---|---|
committer | Aaron Ball <nullspoon@oper.io> | 2021-07-22 21:35:30 -0600 |
commit | a8058f8aa87ac470894459245146cb15300bde5d (patch) | |
tree | 370c993f66be12225e5064779708ea13777d272c /main.c | |
download | fd-enum-a8058f8aa87ac470894459245146cb15300bde5d.tar.gz fd-enum-a8058f8aa87ac470894459245146cb15300bde5d.tar.xz |
Initial commit
This includes support to display statistics on a given pid's open file
descriptors. It also can print open file descriptor paths and resolved
paths by type. The current version of this tool has been tested on over
550,000 open file descriptors and performs well (runtime was about 6
seconds).
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 190 |
1 files changed, 190 insertions, 0 deletions
@@ -0,0 +1,190 @@ +/** + * Fd-enum is a simple tool to list open file descriptors of a pid by type + * Copyright (C) 2021 Aaron Ball <nullspoon@oper.io> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> + +struct config { + int pid; + char dead; + char live; + char sockets; + char inodes; + char pipes; + char showstats; +}; + +int fexists(char* path) { + FILE *fd = fopen(path, "r"); + if(!fd) + return 0; + fclose(fd); + return 1; +} + +void get_help() { + puts("Usage: \n\ + -d, --dead Print list of dead file descriptors (deleted but still open)\n\ + -i, --inodes Print list of inode file descriptors\n\ + -l, --live Print list of live file descriptors (open and existing)\n\ + -p, --pipes Print list of pipe file descriptors\n\ + -s, --sockets Print list of socket file descriptors\n\ +\n\ + -n, --no-stats Hide file descriptor statistics\n\ + -h, --help Print this help text\n\ +"); +} + +int parse_args(int argc, char* argv[], struct config* c) { + // Initialize config struct + c->pid = -1; + c->dead = 0; + c->live = 0; + c->sockets = 0; + c->pipes = 0; + c->inodes = 0; + c->showstats = 1; + int i = 1; + + while(i < argc) { + if(strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dead") == 0) { + c->dead = 1; + } else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--live") == 0) { + c->live = 1; + } else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--sockets") == 0) { + c->sockets = 1; + } else if(strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--inodes") == 0) { + c->inodes = 1; + } else if(strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--pipes") == 0) { + c->pipes = 1; + } else if(strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--no-stats") == 0) { + c->showstats = 0; + } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + get_help(); + exit(1); + } else { + c->pid = strtol(argv[i], NULL, 10); + } + + i++; + } + + if(c->pid == -1) { + fprintf(stderr, "Must specify valid pid\n"); + return 2; + } + return 0; +} + +int main(int argc, char* argv[]) { + struct config c; + + char pidpath_fd[32] = {'\0'}; + char fdpath[289] = {'\0'}; + char lnpath[512] = {'\0'}; + int retval = 0; + int len = 0; + unsigned long unknown = 0; + unsigned long dead = 0; + unsigned long live = 0; + unsigned long sockets = 0; + unsigned long inodes = 0; + unsigned long pipes = 0; + + struct stat sb; + + DIR *dp = NULL; + struct dirent *de = NULL; + + if(parse_args(argc, argv, &c) != 0) + return 1; + + // Construct the pid path + sprintf(pidpath_fd, "/proc/%d/fd", c.pid); + + dp = opendir(pidpath_fd); + while((de = readdir(dp)) != NULL) { + sprintf(fdpath, "%s/%s%c", pidpath_fd, de->d_name, '\0'); + + retval = stat(fdpath, &sb); + len = readlink(fdpath, lnpath, 512); + + if(len <= 0) + continue; + lnpath[len] = '\0'; + + // Sockets + if(S_ISSOCK(sb.st_mode)) { + sockets++; + if(c.sockets) + printf("%s -> %s\n", fdpath, lnpath); + + // Pipes + } else if(S_ISFIFO(sb.st_mode)) { + pipes++; + if(c.pipes) + printf("%s -> %s\n", fdpath, lnpath); + + // Char device links + } else if(S_ISCHR(sb.st_mode)) { + live++; + if(c.live) + printf("%s -> %s\n", fdpath, lnpath); + + // File desccriptors + } else if(S_ISREG(sb.st_mode) + || S_ISDIR(sb.st_mode)) { + // Exists + if(retval == 0 && fexists(lnpath)) { + live++; + if(c.live) + printf("%s -> %s\n", fdpath, lnpath); + + // Does not exist + } else { + dead++; + if(c.dead) + printf("%s -> %s\n", fdpath, lnpath); + } + + // Inodes + } else if(sb.st_mode == 384) { + inodes++; + if(c.inodes) + printf("%s -> %s\n", fdpath, lnpath); + + } else { + unknown++; + } + } + closedir(dp); + + if(c.showstats) { + printf("dead: %ld\n", dead); + printf("live: %ld\n", live); + printf("sockets: %ld\n", sockets); + printf("anon_inodes: %ld\n", inodes); + printf("pipes: %ld\n", pipes); + printf("unknown: %ld\n", unknown); + } + + return 0; +} |