1 /* lsof.c - list open files. 2 * 3 * Copyright 2015 The Android Open Source Project 4 5 USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN)) 6 7 config LSOF 8 bool "lsof" 9 default n 10 help 11 usage: lsof [-lt] [-p PID1,PID2,...] [NAME]... 12 13 Lists open files. If names are given on the command line, only 14 those files will be shown. 15 16 -l list uids numerically 17 -p for given comma-separated pids only (default all pids) 18 -t terse (pid only) output 19 */ 20 21 #define FOR_lsof 22 #include "toys.h" 23 24 GLOBALS( 25 struct arg_list *p; 26 27 struct stat *sought_files; 28 29 struct double_list *all_sockets; 30 struct double_list *files; 31 int last_shown_pid; 32 int shown_header; 33 ) 34 35 struct proc_info { 36 char cmd[10]; 37 int pid; 38 char user[12]; 39 }; 40 41 struct file_info { 42 char *next, *prev; 43 44 // For output. 45 struct proc_info pi; 46 char* name; 47 char fd[8], rw, locks, type[10], device[32], size_off[32], node[32]; 48 49 // For filtering. 50 dev_t st_dev; 51 ino_t st_ino; 52 }; 53 54 static void print_info(void *data) 55 { 56 struct file_info *fi = data; 57 58 // Filter matches 59 if (toys.optc) { 60 int i; 61 62 for (i = 0; i<toys.optc; i++) 63 if (TT.sought_files[i].st_dev==fi->st_dev) 64 if (TT.sought_files[i].st_ino==fi->st_ino) break; 65 66 if (i==toys.optc) return; 67 } 68 69 if (toys.optflags&FLAG_t) { 70 if (fi->pi.pid != TT.last_shown_pid) 71 printf("%d\n", TT.last_shown_pid = fi->pi.pid); 72 } else { 73 if (!TT.shown_header) { 74 // TODO: llist_traverse to measure the columns first. 75 printf("%-9s %5s %10.10s %4s %7s %18s %9s %10s %s\n", "COMMAND", "PID", 76 "USER", "FD", "TYPE", "DEVICE", "SIZE/OFF", "NODE", "NAME"); 77 TT.shown_header = 1; 78 } 79 80 printf("%-9s %5d %10.10s %4s%c%c %7s %18s %9s %10s %s\n", 81 fi->pi.cmd, fi->pi.pid, fi->pi.user, 82 fi->fd, fi->rw, fi->locks, fi->type, fi->device, fi->size_off, 83 fi->node, fi->name); 84 } 85 } 86 87 static void free_info(void *data) 88 { 89 free(((struct file_info *)data)->name); 90 free(data); 91 } 92 93 static void fill_flags(struct file_info *fi) 94 { 95 FILE* fp; 96 long long pos; 97 unsigned flags; 98 99 snprintf(toybuf, sizeof(toybuf), "/proc/%d/fdinfo/%s", fi->pi.pid, fi->fd); 100 fp = fopen(toybuf, "r"); 101 if (!fp) return; 102 103 if (fscanf(fp, "pos: %lld flags: %o", &pos, &flags) == 2) { 104 flags &= O_ACCMODE; 105 if (flags == O_RDONLY) fi->rw = 'r'; 106 else if (flags == O_WRONLY) fi->rw = 'w'; 107 else fi->rw = 'u'; 108 109 snprintf(fi->size_off, sizeof(fi->size_off), "0t%lld", pos); 110 } 111 fclose(fp); 112 } 113 114 static void scan_proc_net_file(char *path, int family, char type, 115 void (*fn)(char *, int, char)) 116 { 117 FILE *fp = fopen(path, "r"); 118 char *line = NULL; 119 size_t line_length = 0; 120 121 if (!fp) return; 122 123 if (!getline(&line, &line_length, fp)) return; // Skip header. 124 125 while (getline(&line, &line_length, fp) > 0) { 126 fn(line, family, type); 127 } 128 129 free(line); 130 fclose(fp); 131 } 132 133 static struct file_info *add_socket(ino_t inode, const char *type) 134 { 135 struct file_info *fi = xzalloc(sizeof(struct file_info)); 136 137 dlist_add_nomalloc(&TT.all_sockets, (struct double_list *)fi); 138 fi->st_ino = inode; 139 strcpy(fi->type, type); 140 return fi; 141 } 142 143 static void scan_unix(char *line, int af, char type) 144 { 145 long inode; 146 int path_pos; 147 148 if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", &inode, &path_pos) >= 1) { 149 struct file_info *fi = add_socket(inode, "unix"); 150 char *name = chomp(line + path_pos); 151 152 fi->name = strdup(*name ? name : "socket"); 153 } 154 } 155 156 static void scan_netlink(char *line, int af, char type) 157 { 158 unsigned state; 159 long inode; 160 char *netlink_states[] = { 161 "ROUTE", "UNUSED", "USERSOCK", "FIREWALL", "SOCK_DIAG", "NFLOG", "XFRM", 162 "SELINUX", "ISCSI", "AUDIT", "FIB_LOOKUP", "CONNECTOR", "NETFILTER", 163 "IP6_FW", "DNRTMSG", "KOBJECT_UEVENT", "GENERIC", "DM", "SCSITRANSPORT", 164 "ENCRYPTFS", "RDMA", "CRYPTO" 165 }; 166 167 if (sscanf(line, "%*p %u %*u %*x %*u %*u %*u %*u %*u %lu", &state, &inode) 168 < 2) { 169 return; 170 } 171 172 struct file_info *fi = add_socket(inode, "netlink"); 173 fi->name = 174 strdup(state < ARRAY_LEN(netlink_states) ? netlink_states[state] : "?"); 175 } 176 177 static void scan_ip(char *line, int af, char type) 178 { 179 char *tcp_states[] = { 180 "UNKNOWN", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2", 181 "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING" 182 }; 183 char local_ip[INET6_ADDRSTRLEN] = {0}; 184 char remote_ip[INET6_ADDRSTRLEN] = {0}; 185 struct in6_addr local, remote; 186 int local_port, remote_port, state; 187 long inode; 188 int ok; 189 190 if (af == 4) { 191 ok = sscanf(line, " %*d: %x:%x %x:%x %x %*x:%*x %*X:%*X %*X %*d %*d %ld", 192 &(local.s6_addr32[0]), &local_port, 193 &(remote.s6_addr32[0]), &remote_port, 194 &state, &inode) == 6; 195 } else { 196 ok = sscanf(line, " %*d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x " 197 "%*x:%*x %*X:%*X %*X %*d %*d %ld", 198 &(local.s6_addr32[0]), &(local.s6_addr32[1]), 199 &(local.s6_addr32[2]), &(local.s6_addr32[3]), 200 &local_port, 201 &(remote.s6_addr32[0]), &(remote.s6_addr32[1]), 202 &(remote.s6_addr32[2]), &(remote.s6_addr32[3]), 203 &remote_port, &state, &inode) == 12; 204 } 205 if (!ok) return; 206 207 struct file_info *fi = add_socket(inode, af == 4 ? "IPv4" : "IPv6"); 208 inet_ntop(af, &local, local_ip, sizeof(local_ip)); 209 inet_ntop(af, &remote, remote_ip, sizeof(remote_ip)); 210 if (type == 't') { 211 if (state < 0 || state > TCP_CLOSING) state = 0; 212 fi->name = xmprintf(af == 4 ? 213 "TCP %s:%d->%s:%d (%s)" : 214 "TCP [%s]:%d->[%s]:%d (%s)", 215 local_ip, local_port, remote_ip, remote_port, 216 tcp_states[state]); 217 } else { 218 fi->name = xmprintf(af == 4 ? "%s %s:%d->%s:%d" : "%s [%s]:%d->[%s]:%d", 219 type == 'u' ? "UDP" : "RAW", 220 local_ip, local_port, remote_ip, remote_port); 221 } 222 } 223 224 static int find_socket(struct file_info *fi, long inode) 225 { 226 static int cached; 227 if (!cached) { 228 scan_proc_net_file("/proc/net/tcp", 4, 't', scan_ip); 229 scan_proc_net_file("/proc/net/tcp6", 6, 't', scan_ip); 230 scan_proc_net_file("/proc/net/udp", 4, 'u', scan_ip); 231 scan_proc_net_file("/proc/net/udp6", 6, 'u', scan_ip); 232 scan_proc_net_file("/proc/net/raw", 4, 'r', scan_ip); 233 scan_proc_net_file("/proc/net/raw6", 6, 'r', scan_ip); 234 scan_proc_net_file("/proc/net/unix", 0, 0, scan_unix); 235 scan_proc_net_file("/proc/net/netlink", 0, 0, scan_netlink); 236 cached = 1; 237 } 238 void* list = TT.all_sockets; 239 240 while (list) { 241 struct file_info *s = (struct file_info*) llist_pop(&list); 242 243 if (s->st_ino == inode) { 244 fi->name = s->name ? strdup(s->name) : NULL; 245 strcpy(fi->type, s->type); 246 return 1; 247 } 248 if (list == TT.all_sockets) break; 249 } 250 251 return 0; 252 } 253 254 static void fill_stat(struct file_info *fi, const char *path) 255 { 256 struct stat sb; 257 long dev; 258 259 if (stat(path, &sb)) return; 260 261 // Fill TYPE. 262 switch ((sb.st_mode & S_IFMT)) { 263 case S_IFBLK: strcpy(fi->type, "BLK"); break; 264 case S_IFCHR: strcpy(fi->type, "CHR"); break; 265 case S_IFDIR: strcpy(fi->type, "DIR"); break; 266 case S_IFIFO: strcpy(fi->type, "FIFO"); break; 267 case S_IFLNK: strcpy(fi->type, "LINK"); break; 268 case S_IFREG: strcpy(fi->type, "REG"); break; 269 case S_IFSOCK: strcpy(fi->type, "sock"); break; 270 default: 271 snprintf(fi->type, sizeof(fi->type), "0%03o", sb.st_mode & S_IFMT); 272 break; 273 } 274 275 if (S_ISSOCK(sb.st_mode)) find_socket(fi, sb.st_ino); 276 277 // Fill DEVICE. 278 dev = (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) ? sb.st_rdev : sb.st_dev; 279 if (!S_ISSOCK(sb.st_mode)) 280 snprintf(fi->device, sizeof(fi->device), "%d,%d", 281 dev_major(dev), dev_minor(dev)); 282 283 // Fill SIZE/OFF. 284 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) 285 snprintf(fi->size_off, sizeof(fi->size_off), "%lld", 286 (long long)sb.st_size); 287 288 // Fill NODE. 289 snprintf(fi->node, sizeof(fi->node), "%ld", (long)sb.st_ino); 290 291 // Stash st_dev and st_ino for filtering. 292 fi->st_dev = sb.st_dev; 293 fi->st_ino = sb.st_ino; 294 } 295 296 struct file_info *new_file_info(struct proc_info *pi, const char *fd) 297 { 298 struct file_info *fi = xzalloc(sizeof(struct file_info)); 299 300 dlist_add_nomalloc(&TT.files, (struct double_list *)fi); 301 302 fi->pi = *pi; 303 304 // Defaults. 305 strcpy(fi->fd, fd); 306 strcpy(fi->type, "unknown"); 307 fi->rw = fi->locks = ' '; 308 309 return fi; 310 } 311 312 static void visit_symlink(struct proc_info *pi, char *name, char *path) 313 { 314 struct file_info *fi = new_file_info(pi, ""); 315 316 // Get NAME. 317 if (name) { // "/proc/pid/[cwd]". 318 snprintf(fi->fd, sizeof(fi->fd), "%s", name); 319 snprintf(toybuf, sizeof(toybuf), "/proc/%d/%s", pi->pid, path); 320 } else { // "/proc/pid/fd/[3]" 321 snprintf(fi->fd, sizeof(fi->fd), "%s", path); 322 fill_flags(fi); // Clobbers toybuf. 323 snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd/%s", pi->pid, path); 324 } 325 // TODO: code called by fill_stat would be easier to write if we didn't 326 // rely on toybuf being preserved here. 327 fill_stat(fi, toybuf); 328 if (!fi->name) { // We already have a name for things like sockets. 329 fi->name = xreadlink(toybuf); 330 if (!fi->name) { 331 fi->name = xmprintf("%s (readlink: %s)", toybuf, strerror(errno)); 332 } 333 } 334 } 335 336 static void visit_maps(struct proc_info *pi) 337 { 338 FILE *fp; 339 unsigned long long offset; 340 char device[10]; 341 long inode; 342 char *line = NULL; 343 size_t line_length = 0; 344 345 snprintf(toybuf, sizeof(toybuf), "/proc/%d/maps", pi->pid); 346 fp = fopen(toybuf, "r"); 347 if (!fp) return; 348 349 while (getline(&line, &line_length, fp) > 0) { 350 int name_pos; 351 352 if (sscanf(line, "%*x-%*x %*s %llx %s %ld %n", 353 &offset, device, &inode, &name_pos) >= 3) { 354 struct file_info *fi; 355 356 // Ignore non-file maps. 357 if (inode == 0 || !strcmp(device, "00:00")) continue; 358 // TODO: show unique maps even if they have a non-zero offset? 359 if (offset != 0) continue; 360 361 fi = new_file_info(pi, "mem"); 362 fi->name = strdup(chomp(line + name_pos)); 363 fill_stat(fi, fi->name); 364 } 365 } 366 free(line); 367 fclose(fp); 368 } 369 370 static void visit_fds(struct proc_info *pi) 371 { 372 DIR *dir; 373 struct dirent *de; 374 375 snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd", pi->pid); 376 if (!(dir = opendir(toybuf))) { 377 struct file_info *fi = new_file_info(pi, "NOFD"); 378 379 fi->name = xmprintf("%s (opendir: %s)", toybuf, strerror(errno)); 380 return; 381 } 382 383 while ((de = readdir(dir))) { 384 if (*de->d_name == '.') continue; 385 visit_symlink(pi, NULL, de->d_name); 386 } 387 388 closedir(dir); 389 } 390 391 static void lsof_pid(int pid) 392 { 393 struct proc_info pi; 394 char *line; 395 struct stat sb; 396 397 // Skip nonexistent pids 398 sprintf(toybuf, "/proc/%d/stat", pid); 399 if (!(line = readfile(toybuf, toybuf, sizeof(toybuf)))) return; 400 401 // Get COMMAND. 402 strcpy(pi.cmd, "?"); 403 if (line) { 404 char *open_paren = strchr(toybuf, '('); 405 char *close_paren = strrchr(toybuf, ')'); 406 407 if (open_paren && close_paren) { 408 *close_paren = 0; 409 snprintf(pi.cmd, sizeof(pi.cmd), "%s", open_paren + 1); 410 } 411 } 412 413 // We already know PID. 414 pi.pid = pid; 415 416 // Get USER. 417 snprintf(toybuf, sizeof(toybuf), "/proc/%d", pid); 418 if (!stat(toybuf, &sb)) { 419 struct passwd *pw; 420 421 if (!(toys.optflags&FLAG_l) && (pw = getpwuid(sb.st_uid))) { 422 snprintf(pi.user, sizeof(pi.user), "%s", pw->pw_name); 423 } else snprintf(pi.user, sizeof(pi.user), "%u", (unsigned)sb.st_uid); 424 } 425 426 visit_symlink(&pi, "cwd", "cwd"); 427 visit_symlink(&pi, "rtd", "root"); 428 visit_symlink(&pi, "txt", "exe"); 429 visit_maps(&pi); 430 visit_fds(&pi); 431 } 432 433 static int scan_proc(struct dirtree *node) 434 { 435 int pid; 436 437 if (!node->parent) return DIRTREE_RECURSE|DIRTREE_SHUTUP; 438 if ((pid = atol(node->name))) lsof_pid(pid); 439 440 return 0; 441 } 442 443 void lsof_main(void) 444 { 445 struct arg_list *pp; 446 int i; 447 448 // lsof will only filter on paths it can stat (because it filters by inode). 449 TT.sought_files = xmalloc(toys.optc*sizeof(struct stat)); 450 for (i = 0; i<toys.optc; ++i) xstat(toys.optargs[i], TT.sought_files+i); 451 452 if (!TT.p) dirtree_read("/proc", scan_proc); 453 else for (pp = TT.p; pp; pp = pp->next) { 454 char *start, *end, *next = pp->arg; 455 int length, pid; 456 457 while ((start = comma_iterate(&next, &length))) { 458 pid = strtol(start, &end, 10); 459 if (pid<1 || (*end && *end!=',')) 460 error_exit("bad -p '%.*s'", (int)(end-start), start); 461 lsof_pid(pid); 462 } 463 } 464 465 llist_traverse(TT.files, print_info); 466 467 if (CFG_TOYBOX_FREE) { 468 llist_traverse(TT.files, free_info); 469 llist_traverse(TT.all_sockets, free_info); 470 } 471 } 472