1 /**
2 * Fd-enum is a simple tool to list open file descriptors of a pid by type
3 * Copyright (C) 2021 Aaron Ball <nullspoon@oper.io>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <dirent.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <sys/stat.h>
24
25 struct config {
26 int pid;
27 char dead;
28 char live;
29 char sockets;
30 char inodes;
31 char pipes;
32 char showstats;
33 };
34
35 int fexists(char* path) {
36 FILE *fd = fopen(path, "r");
37 if(!fd)
38 return 0;
39 fclose(fd);
40 return 1;
41 }
42
43 void get_help() {
44 puts("Usage: \n\
45 -d, --dead Print list of dead file descriptors (deleted but still open)\n\
46 -i, --inodes Print list of inode file descriptors\n\
47 -l, --live Print list of live file descriptors (open and existing)\n\
48 -p, --pipes Print list of pipe file descriptors\n\
49 -s, --sockets Print list of socket file descriptors\n\
50 \n\
51 -n, --no-stats Hide file descriptor statistics\n\
52 -h, --help Print this help text\n\
53 ");
54 }
55
56 int parse_args(int argc, char* argv[], struct config* c) {
57 // Initialize config struct
58 c->pid = -1;
59 c->dead = 0;
60 c->live = 0;
61 c->sockets = 0;
62 c->pipes = 0;
63 c->inodes = 0;
64 c->showstats = 1;
65 int i = 1;
66
67 while(i < argc) {
68 if(strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dead") == 0) {
69 c->dead = 1;
70 } else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--live") == 0) {
71 c->live = 1;
72 } else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--sockets") == 0) {
73 c->sockets = 1;
74 } else if(strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--inodes") == 0) {
75 c->inodes = 1;
76 } else if(strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--pipes") == 0) {
77 c->pipes = 1;
78 } else if(strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--no-stats") == 0) {
79 c->showstats = 0;
80 } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
81 get_help();
82 exit(1);
83 } else {
84 c->pid = strtol(argv[i], NULL, 10);
85 }
86
87 i++;
88 }
89
90 if(c->pid == -1) {
91 fprintf(stderr, "Must specify valid pid\n");
92 return 2;
93 }
94 return 0;
95 }
96
97 int main(int argc, char* argv[]) {
98 struct config c;
99
100 char pidpath_fd[32] = {'\0'};
101 char fdpath[289] = {'\0'};
102 char lnpath[512] = {'\0'};
103 int retval = 0;
104 int len = 0;
105 unsigned long unknown = 0;
106 unsigned long dead = 0;
107 unsigned long live = 0;
108 unsigned long sockets = 0;
109 unsigned long inodes = 0;
110 unsigned long pipes = 0;
111
112 struct stat sb;
113
114 DIR *dp = NULL;
115 struct dirent *de = NULL;
116
117 if(parse_args(argc, argv, &c) != 0)
118 return 1;
119
120 // Construct the pid path
121 sprintf(pidpath_fd, "/proc/%d/fd", c.pid);
122
123 dp = opendir(pidpath_fd);
124 while((de = readdir(dp)) != NULL) {
125 sprintf(fdpath, "%s/%s%c", pidpath_fd, de->d_name, '\0');
126
127 retval = stat(fdpath, &sb);
128 len = readlink(fdpath, lnpath, 512);
129
130 if(len <= 0)
131 continue;
132 lnpath[len] = '\0';
133
134 // Sockets
135 if(S_ISSOCK(sb.st_mode)) {
136 sockets++;
137 if(c.sockets)
138 printf("%s -> %s\n", fdpath, lnpath);
139
140 // Pipes
141 } else if(S_ISFIFO(sb.st_mode)) {
142 pipes++;
143 if(c.pipes)
144 printf("%s -> %s\n", fdpath, lnpath);
145
146 // Char device links
147 } else if(S_ISCHR(sb.st_mode)) {
148 live++;
149 if(c.live)
150 printf("%s -> %s\n", fdpath, lnpath);
151
152 // File desccriptors
153 } else if(S_ISREG(sb.st_mode)
154 || S_ISDIR(sb.st_mode)) {
155 // Exists
156 if(retval == 0 && fexists(lnpath)) {
157 live++;
158 if(c.live)
159 printf("%s -> %s\n", fdpath, lnpath);
160
161 // Does not exist
162 } else {
163 dead++;
164 if(c.dead)
165 printf("%s -> %s\n", fdpath, lnpath);
166 }
167
168 // Inodes
169 } else if(sb.st_mode == 384) {
170 inodes++;
171 if(c.inodes)
172 printf("%s -> %s\n", fdpath, lnpath);
173
174 } else {
175 unknown++;
176 }
177 }
178 closedir(dp);
179
180 if(c.showstats) {
181 printf("dead: %ld\n", dead);
182 printf("live: %ld\n", live);
183 printf("sockets: %ld\n", sockets);
184 printf("anon_inodes: %ld\n", inodes);
185 printf("pipes: %ld\n", pipes);
186 printf("unknown: %ld\n", unknown);
187 }
188
189 return 0;
190 }
|