blob: 198ca52d520bb7b0168dc7f13fc93d0bec964c74 [file] [log] [blame]
Kenny Root8b9b1052010-07-27 09:20:02 -07001/*
2 * Copyright (c) 2010, The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google, Inc. nor the names of its contributors
15 * may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
Elliott Hughes703a3f12015-05-15 23:27:01 -070032#include <ctype.h>
Kenny Root8b9b1052010-07-27 09:20:02 -070033#include <dirent.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <libgen.h>
37#include <stdio.h>
38#include <stdlib.h>
Elliott Hughesa744b052015-01-28 11:37:57 -080039#include <string.h>
Kenny Root8b9b1052010-07-27 09:20:02 -070040#include <unistd.h>
41
Jeff Brownf96993e2011-06-29 15:00:39 -070042#include <pwd.h>
43#include <sys/stat.h>
44
Kenny Root8b9b1052010-07-27 09:20:02 -070045#define BUF_MAX 1024
Jeff Brownf96993e2011-06-29 15:00:39 -070046#define CMD_DISPLAY_MAX (9 + 1)
47#define USER_DISPLAY_MAX (10 + 1)
Kenny Root8b9b1052010-07-27 09:20:02 -070048
49struct pid_info_t {
50 pid_t pid;
Jeff Brownf96993e2011-06-29 15:00:39 -070051 char user[USER_DISPLAY_MAX];
Kenny Root8b9b1052010-07-27 09:20:02 -070052
53 char cmdline[CMD_DISPLAY_MAX];
54
55 char path[PATH_MAX];
56 ssize_t parent_length;
57};
58
Nick Kralevich960ac432013-06-03 12:10:30 -070059static void print_header()
Kenny Root8b9b1052010-07-27 09:20:02 -070060{
Elliott Hughes703a3f12015-05-15 23:27:01 -070061 printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n",
Kenny Root8b9b1052010-07-27 09:20:02 -070062 "COMMAND",
63 "PID",
64 "USER",
65 "FD",
66 "TYPE",
67 "DEVICE",
68 "SIZE/OFF",
69 "NODE",
70 "NAME");
71}
72
Elliott Hughes703a3f12015-05-15 23:27:01 -070073static void print_symlink(const char* name, const char* path, struct pid_info_t* info)
Kenny Root8b9b1052010-07-27 09:20:02 -070074{
75 static ssize_t link_dest_size;
76 static char link_dest[PATH_MAX];
77
Elliott Hughes703a3f12015-05-15 23:27:01 -070078 strlcat(info->path, path, sizeof(info->path));
Kenny Root8b9b1052010-07-27 09:20:02 -070079 if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) {
80 if (errno == ENOENT)
81 goto out;
82
83 snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno));
84 } else {
85 link_dest[link_dest_size] = '\0';
86 }
87
88 // Things that are just the root filesystem are uninteresting (we already know)
89 if (!strcmp(link_dest, "/"))
90 goto out;
91
Elliott Hughes703a3f12015-05-15 23:27:01 -070092 const char* fd = name;
93 char rw = ' ';
94 char locks = ' '; // TODO: read /proc/locks
95
96 const char* type = "unknown";
97 char device[32] = "?";
98 char size_off[32] = "?";
99 char node[32] = "?";
100
101 struct stat sb;
102 if (lstat(link_dest, &sb) != -1) {
103 switch ((sb.st_mode & S_IFMT)) {
104 case S_IFSOCK: type = "sock"; break; // TODO: what domain?
105 case S_IFLNK: type = "LINK"; break;
106 case S_IFREG: type = "REG"; break;
107 case S_IFBLK: type = "BLK"; break;
108 case S_IFDIR: type = "DIR"; break;
109 case S_IFCHR: type = "CHR"; break;
110 case S_IFIFO: type = "FIFO"; break;
111 }
112 snprintf(device, sizeof(device), "%d,%d", (int) sb.st_dev, (int) sb.st_rdev);
113 snprintf(node, sizeof(node), "%d", (int) sb.st_ino);
114 snprintf(size_off, sizeof(size_off), "%d", (int) sb.st_size);
115 }
116
117 if (!name) {
118 // We're looking at an fd, so read its flags.
119 fd = path;
120 char fdinfo_path[PATH_MAX];
121 snprintf(fdinfo_path, sizeof(fdinfo_path), "/proc/%d/fdinfo/%s", info->pid, path);
122 FILE* fp = fopen(fdinfo_path, "r");
123 if (fp != NULL) {
124 int pos;
125 unsigned flags;
126
127 if (fscanf(fp, "pos: %d flags: %o", &pos, &flags) == 2) {
128 flags &= O_ACCMODE;
129 if (flags == O_RDONLY) rw = 'r';
130 else if (flags == O_WRONLY) rw = 'w';
131 else rw = 'u';
132 }
133 fclose(fp);
134 }
135 }
136
137 printf("%-9s %5d %10s %4s%c%c %9s %18s %9s %10s %s\n",
138 info->cmdline, info->pid, info->user, fd, rw, locks, type, device, size_off, node, link_dest);
Kenny Root8b9b1052010-07-27 09:20:02 -0700139
140out:
141 info->path[info->parent_length] = '\0';
142}
143
144// Prints out all file that have been memory mapped
Nick Kralevich960ac432013-06-03 12:10:30 -0700145static void print_maps(struct pid_info_t* info)
Kenny Root8b9b1052010-07-27 09:20:02 -0700146{
147 FILE *maps;
Kenny Root8b9b1052010-07-27 09:20:02 -0700148 size_t offset;
Kenny Root8b9b1052010-07-27 09:20:02 -0700149 char device[10];
150 long int inode;
151 char file[PATH_MAX];
152
Nick Kralevich960ac432013-06-03 12:10:30 -0700153 strlcat(info->path, "maps", sizeof(info->path));
Kenny Root8b9b1052010-07-27 09:20:02 -0700154
155 maps = fopen(info->path, "r");
156 if (!maps)
157 goto out;
158
Elliott Hughes703a3f12015-05-15 23:27:01 -0700159 while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode, file) == 4) {
Kenny Root8b9b1052010-07-27 09:20:02 -0700160 // We don't care about non-file maps
161 if (inode == 0 || !strcmp(device, "00:00"))
162 continue;
163
Elliott Hughes703a3f12015-05-15 23:27:01 -0700164 printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n",
Jeff Brownf96993e2011-06-29 15:00:39 -0700165 info->cmdline, info->pid, info->user, "mem",
Elliott Hughes703a3f12015-05-15 23:27:01 -0700166 "REG", device, offset, inode, file);
Kenny Root8b9b1052010-07-27 09:20:02 -0700167 }
168
169 fclose(maps);
170
171out:
172 info->path[info->parent_length] = '\0';
173}
174
175// Prints out all open file descriptors
Nick Kralevich960ac432013-06-03 12:10:30 -0700176static void print_fds(struct pid_info_t* info)
Kenny Root8b9b1052010-07-27 09:20:02 -0700177{
178 static char* fd_path = "fd/";
Nick Kralevich960ac432013-06-03 12:10:30 -0700179 strlcat(info->path, fd_path, sizeof(info->path));
Kenny Root8b9b1052010-07-27 09:20:02 -0700180
181 int previous_length = info->parent_length;
182 info->parent_length += strlen(fd_path);
183
184 DIR *dir = opendir(info->path);
185 if (dir == NULL) {
186 char msg[BUF_MAX];
187 snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno));
Elliott Hughes703a3f12015-05-15 23:27:01 -0700188 printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
Jeff Brownf96993e2011-06-29 15:00:39 -0700189 info->cmdline, info->pid, info->user, "FDS",
Kenny Root8b9b1052010-07-27 09:20:02 -0700190 "", "", "", "", msg);
191 goto out;
192 }
193
194 struct dirent* de;
195 while ((de = readdir(dir))) {
196 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
197 continue;
198
Elliott Hughes703a3f12015-05-15 23:27:01 -0700199 print_symlink(NULL, de->d_name, info);
Kenny Root8b9b1052010-07-27 09:20:02 -0700200 }
201 closedir(dir);
202
203out:
204 info->parent_length = previous_length;
205 info->path[info->parent_length] = '\0';
206}
207
Nick Kralevich960ac432013-06-03 12:10:30 -0700208static void lsof_dumpinfo(pid_t pid)
Kenny Root8b9b1052010-07-27 09:20:02 -0700209{
210 int fd;
211 struct pid_info_t info;
Jeff Brownf96993e2011-06-29 15:00:39 -0700212 struct stat pidstat;
213 struct passwd *pw;
214
Kenny Root8b9b1052010-07-27 09:20:02 -0700215 info.pid = pid;
Kenny Root8b9b1052010-07-27 09:20:02 -0700216 snprintf(info.path, sizeof(info.path), "/proc/%d/", pid);
Kenny Root8b9b1052010-07-27 09:20:02 -0700217 info.parent_length = strlen(info.path);
218
Jeff Brownf96993e2011-06-29 15:00:39 -0700219 // Get the UID by calling stat on the proc/pid directory.
220 if (!stat(info.path, &pidstat)) {
221 pw = getpwuid(pidstat.st_uid);
222 if (pw) {
Kenny Rootb953fc22011-12-01 11:38:53 -0800223 strlcpy(info.user, pw->pw_name, sizeof(info.user));
Jeff Brownf96993e2011-06-29 15:00:39 -0700224 } else {
225 snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid);
226 }
227 } else {
228 strcpy(info.user, "???");
229 }
230
Kenny Root8b9b1052010-07-27 09:20:02 -0700231 // Read the command line information; each argument is terminated with NULL.
Nick Kralevich960ac432013-06-03 12:10:30 -0700232 strlcat(info.path, "cmdline", sizeof(info.path));
Kenny Root8b9b1052010-07-27 09:20:02 -0700233 fd = open(info.path, O_RDONLY);
234 if (fd < 0) {
235 fprintf(stderr, "Couldn't read %s\n", info.path);
236 return;
237 }
Kenny Rootb953fc22011-12-01 11:38:53 -0800238
Kenny Root8b9b1052010-07-27 09:20:02 -0700239 char cmdline[PATH_MAX];
Kenny Rootb953fc22011-12-01 11:38:53 -0800240 int numRead = read(fd, cmdline, sizeof(cmdline) - 1);
241 close(fd);
242
243 if (numRead < 0) {
Kenny Root8b9b1052010-07-27 09:20:02 -0700244 fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno));
Kenny Root8b9b1052010-07-27 09:20:02 -0700245 return;
246 }
Kenny Rootb953fc22011-12-01 11:38:53 -0800247
248 cmdline[numRead] = '\0';
Kenny Root8b9b1052010-07-27 09:20:02 -0700249
250 // We only want the basename of the cmdline
Kenny Rootb953fc22011-12-01 11:38:53 -0800251 strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline));
Kenny Root8b9b1052010-07-27 09:20:02 -0700252
253 // Read each of these symlinks
Elliott Hughes703a3f12015-05-15 23:27:01 -0700254 print_symlink("cwd", "cwd", &info);
255 print_symlink("txt", "exe", &info);
256 print_symlink("rtd", "root", &info);
Kenny Root8b9b1052010-07-27 09:20:02 -0700257 print_fds(&info);
258 print_maps(&info);
259}
260
261int lsof_main(int argc, char *argv[])
262{
Mike Lockwood794cc3f2010-12-22 16:37:36 -0500263 long int pid = 0;
264 char* endptr;
265 if (argc == 2) {
266 pid = strtol(argv[1], &endptr, 10);
Kenny Root8b9b1052010-07-27 09:20:02 -0700267 }
268
269 print_header();
270
Mike Lockwood794cc3f2010-12-22 16:37:36 -0500271 if (pid) {
Kenny Root8b9b1052010-07-27 09:20:02 -0700272 lsof_dumpinfo(pid);
Mike Lockwood794cc3f2010-12-22 16:37:36 -0500273 } else {
274 DIR *dir = opendir("/proc");
275 if (dir == NULL) {
276 fprintf(stderr, "Couldn't open /proc\n");
277 return -1;
278 }
279
280 struct dirent* de;
281 while ((de = readdir(dir))) {
282 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
283 continue;
284
285 // Only inspect directories that are PID numbers
Jeff Brownf96993e2011-06-29 15:00:39 -0700286 pid = strtol(de->d_name, &endptr, 10);
Mike Lockwood794cc3f2010-12-22 16:37:36 -0500287 if (*endptr != '\0')
288 continue;
289
290 lsof_dumpinfo(pid);
291 }
292 closedir(dir);
Kenny Root8b9b1052010-07-27 09:20:02 -0700293 }
Kenny Root8b9b1052010-07-27 09:20:02 -0700294
295 return 0;
296}