1/* 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 32#include <dirent.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <libgen.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <unistd.h> 40 41#include <pwd.h> 42#include <sys/stat.h> 43 44#define BUF_MAX 1024 45#define CMD_DISPLAY_MAX (9 + 1) 46#define USER_DISPLAY_MAX (10 + 1) 47 48struct pid_info_t { 49 pid_t pid; 50 char user[USER_DISPLAY_MAX]; 51 52 char cmdline[CMD_DISPLAY_MAX]; 53 54 char path[PATH_MAX]; 55 ssize_t parent_length; 56}; 57 58static void print_header() 59{ 60 printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n", 61 "COMMAND", 62 "PID", 63 "USER", 64 "FD", 65 "TYPE", 66 "DEVICE", 67 "SIZE/OFF", 68 "NODE", 69 "NAME"); 70} 71 72static void print_type(char *type, struct pid_info_t* info) 73{ 74 static ssize_t link_dest_size; 75 static char link_dest[PATH_MAX]; 76 77 strlcat(info->path, type, sizeof(info->path)); 78 if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) { 79 if (errno == ENOENT) 80 goto out; 81 82 snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno)); 83 } else { 84 link_dest[link_dest_size] = '\0'; 85 } 86 87 // Things that are just the root filesystem are uninteresting (we already know) 88 if (!strcmp(link_dest, "/")) 89 goto out; 90 91 printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", 92 info->cmdline, info->pid, info->user, type, 93 "???", "???", "???", "???", link_dest); 94 95out: 96 info->path[info->parent_length] = '\0'; 97} 98 99// Prints out all file that have been memory mapped 100static void print_maps(struct pid_info_t* info) 101{ 102 FILE *maps; 103 size_t offset; 104 char device[10]; 105 long int inode; 106 char file[PATH_MAX]; 107 108 strlcat(info->path, "maps", sizeof(info->path)); 109 110 maps = fopen(info->path, "r"); 111 if (!maps) 112 goto out; 113 114 while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode, 115 file) == 4) { 116 // We don't care about non-file maps 117 if (inode == 0 || !strcmp(device, "00:00")) 118 continue; 119 120 printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n", 121 info->cmdline, info->pid, info->user, "mem", 122 "???", device, offset, inode, file); 123 } 124 125 fclose(maps); 126 127out: 128 info->path[info->parent_length] = '\0'; 129} 130 131// Prints out all open file descriptors 132static void print_fds(struct pid_info_t* info) 133{ 134 static char* fd_path = "fd/"; 135 strlcat(info->path, fd_path, sizeof(info->path)); 136 137 int previous_length = info->parent_length; 138 info->parent_length += strlen(fd_path); 139 140 DIR *dir = opendir(info->path); 141 if (dir == NULL) { 142 char msg[BUF_MAX]; 143 snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno)); 144 printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", 145 info->cmdline, info->pid, info->user, "FDS", 146 "", "", "", "", msg); 147 goto out; 148 } 149 150 struct dirent* de; 151 while ((de = readdir(dir))) { 152 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 153 continue; 154 155 print_type(de->d_name, info); 156 } 157 closedir(dir); 158 159out: 160 info->parent_length = previous_length; 161 info->path[info->parent_length] = '\0'; 162} 163 164static void lsof_dumpinfo(pid_t pid) 165{ 166 int fd; 167 struct pid_info_t info; 168 struct stat pidstat; 169 struct passwd *pw; 170 171 info.pid = pid; 172 snprintf(info.path, sizeof(info.path), "/proc/%d/", pid); 173 info.parent_length = strlen(info.path); 174 175 // Get the UID by calling stat on the proc/pid directory. 176 if (!stat(info.path, &pidstat)) { 177 pw = getpwuid(pidstat.st_uid); 178 if (pw) { 179 strlcpy(info.user, pw->pw_name, sizeof(info.user)); 180 } else { 181 snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid); 182 } 183 } else { 184 strcpy(info.user, "???"); 185 } 186 187 // Read the command line information; each argument is terminated with NULL. 188 strlcat(info.path, "cmdline", sizeof(info.path)); 189 fd = open(info.path, O_RDONLY); 190 if (fd < 0) { 191 fprintf(stderr, "Couldn't read %s\n", info.path); 192 return; 193 } 194 195 char cmdline[PATH_MAX]; 196 int numRead = read(fd, cmdline, sizeof(cmdline) - 1); 197 close(fd); 198 199 if (numRead < 0) { 200 fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno)); 201 return; 202 } 203 204 cmdline[numRead] = '\0'; 205 206 // We only want the basename of the cmdline 207 strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline)); 208 209 // Read each of these symlinks 210 print_type("cwd", &info); 211 print_type("exe", &info); 212 print_type("root", &info); 213 214 print_fds(&info); 215 print_maps(&info); 216} 217 218int lsof_main(int argc, char *argv[]) 219{ 220 long int pid = 0; 221 char* endptr; 222 if (argc == 2) { 223 pid = strtol(argv[1], &endptr, 10); 224 } 225 226 print_header(); 227 228 if (pid) { 229 lsof_dumpinfo(pid); 230 } else { 231 DIR *dir = opendir("/proc"); 232 if (dir == NULL) { 233 fprintf(stderr, "Couldn't open /proc\n"); 234 return -1; 235 } 236 237 struct dirent* de; 238 while ((de = readdir(dir))) { 239 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 240 continue; 241 242 // Only inspect directories that are PID numbers 243 pid = strtol(de->d_name, &endptr, 10); 244 if (*endptr != '\0') 245 continue; 246 247 lsof_dumpinfo(pid); 248 } 249 closedir(dir); 250 } 251 252 return 0; 253} 254