1 /* lsof.c - list open files.
3 * Copyright 2015 The Android Open Source Project
5 USE_LSOF(NEWTOY(lsof, "lp:t", TOYFLAG_USR|TOYFLAG_BIN))
11 usage: lsof [-lt] [-p PID1,PID2,...] [NAME]...
13 Lists open files. If names are given on the command line, only
14 those files will be shown.
16 -l list uids numerically
17 -p for given comma-separated pids only (default all pids)
18 -t terse (pid only) output
27 struct stat *sought_files;
29 struct double_list *files;
59 static int filter_matches(struct file_info *fi)
61 struct stat *sb = TT.sought_files;
63 for (; sb != &(TT.sought_files[toys.optc]); ++sb) {
64 if (sb->st_dev == fi->st_dev && sb->st_ino == fi->st_ino) return 1;
69 static void print_header()
71 // TODO: llist_traverse to measure the columns first.
73 "COMMAND", "PID", "USER", "FD", "TYPE", "DEVICE", "SIZE/OFF", "NODE", "NAME"
75 printf("%-9s %5s %10.10s %4s %7s %18s %9s %10s %s\n", names[0], names[1],
76 names[2], names[3], names[4], names[5], names[6], names[7], names[8]);
80 static void print_info(void *data)
82 struct file_info *fi = data;
84 if (toys.optc && !filter_matches(fi)) return;
86 if (toys.optflags&FLAG_t) {
87 if (fi->pi.pid != TT.last_shown_pid)
88 printf("%d\n", (TT.last_shown_pid = fi->pi.pid));
90 if (!TT.shown_header) print_header();
91 printf("%-9s %5d %10.10s %4s%c%c %7s %18s %9s %10s %s\n",
92 fi->pi.cmd, fi->pi.pid, fi->pi.user,
93 fi->fd, fi->rw, fi->locks, fi->type, fi->device, fi->size_off,
98 free(((struct file_info *)data)->name);
103 static void fill_flags(struct file_info *fi)
109 snprintf(toybuf, sizeof(toybuf), "/proc/%d/fdinfo/%s", fi->pi.pid, fi->fd);
110 fp = fopen(toybuf, "r");
113 if (fscanf(fp, "pos: %lld flags: %o", &pos, &flags) == 2) {
115 if (flags == O_RDONLY) fi->rw = 'r';
116 else if (flags == O_WRONLY) fi->rw = 'w';
119 snprintf(fi->size_off, sizeof(fi->size_off), "0t%lld", pos);
124 static int scan_proc_net_file(char *path, int family, char type,
125 void (*fn)(char *, int, char, struct file_info *, long),
126 struct file_info *fi, long sought_inode)
128 FILE *fp = fopen(path, "r");
130 size_t line_length = 0;
134 if (!getline(&line, &line_length, fp)) return 0; // Skip header.
136 while (getline(&line, &line_length, fp) > 0) {
137 fn(line, family, type, fi, sought_inode);
138 if (fi->name != 0) break;
144 return fi->name != 0;
147 static void match_unix(char *line, int af, char type, struct file_info *fi,
153 if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", &inode, &path_pos) >= 1 &&
154 inode == sought_inode) {
155 char *name = chomp(line + path_pos);
157 strcpy(fi->type, "unix");
158 fi->name = strdup(*name ? name : "socket");
162 static void match_netlink(char *line, int af, char type, struct file_info *fi,
167 char *netlink_states[] = {
168 "ROUTE", "UNUSED", "USERSOCK", "FIREWALL", "SOCK_DIAG", "NFLOG", "XFRM",
169 "SELINUX", "ISCSI", "AUDIT", "FIB_LOOKUP", "CONNECTOR", "NETFILTER",
170 "IP6_FW", "DNRTMSG", "KOBJECT_UEVENT", "GENERIC", "DM", "SCSITRANSPORT",
171 "ENCRYPTFS", "RDMA", "CRYPTO"
174 if (sscanf(line, "%*p %u %*u %*x %*u %*u %*u %*u %*u %lu",
175 &state, &inode) < 2 || inode != sought_inode) {
179 strcpy(fi->type, "netlink");
181 strdup(state < ARRAY_LEN(netlink_states) ? netlink_states[state] : "?");
184 static void match_ip(char *line, int af, char type, struct file_info *fi,
187 char *tcp_states[] = {
188 "UNKNOWN", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
189 "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING"
191 char local_ip[INET6_ADDRSTRLEN] = {0};
192 char remote_ip[INET6_ADDRSTRLEN] = {0};
193 struct in6_addr local, remote;
194 int local_port, remote_port, state;
199 ok = sscanf(line, " %*d: %x:%x %x:%x %x %*x:%*x %*X:%*X %*X %*d %*d %ld",
200 &(local.s6_addr32[0]), &local_port,
201 &(remote.s6_addr32[0]), &remote_port,
202 &state, &inode) == 6;
204 ok = sscanf(line, " %*d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x "
205 "%*x:%*x %*X:%*X %*X %*d %*d %ld",
206 &(local.s6_addr32[0]), &(local.s6_addr32[1]),
207 &(local.s6_addr32[2]), &(local.s6_addr32[3]),
209 &(remote.s6_addr32[0]), &(remote.s6_addr32[1]),
210 &(remote.s6_addr32[2]), &(remote.s6_addr32[3]),
211 &remote_port, &state, &inode) == 12;
213 if (!ok || inode != sought_inode) return;
215 strcpy(fi->type, af == 4 ? "IPv4" : "IPv6");
216 inet_ntop(af, &local, local_ip, sizeof(local_ip));
217 inet_ntop(af, &remote, remote_ip, sizeof(remote_ip));
219 if (state < 0 || state > TCP_CLOSING) state = 0;
220 fi->name = xmprintf(af == 4 ?
221 "TCP %s:%d->%s:%d (%s)" :
222 "TCP [%s]:%d->[%s]:%d (%s)",
223 local_ip, local_port, remote_ip, remote_port,
226 fi->name = xmprintf(af == 4 ? "%s %s:%d->%s:%d" : "%s [%s]:%d->[%s]:%d",
227 type == 'u' ? "UDP" : "RAW",
228 local_ip, local_port, remote_ip, remote_port);
232 static int find_socket(struct file_info *fi, long inode)
234 // TODO: other protocols (packet).
235 return scan_proc_net_file("/proc/net/tcp", 4, 't', match_ip, fi, inode) ||
236 scan_proc_net_file("/proc/net/tcp6", 6, 't', match_ip, fi, inode) ||
237 scan_proc_net_file("/proc/net/udp", 4, 'u', match_ip, fi, inode) ||
238 scan_proc_net_file("/proc/net/udp6", 6, 'u', match_ip, fi, inode) ||
239 scan_proc_net_file("/proc/net/raw", 4, 'r', match_ip, fi, inode) ||
240 scan_proc_net_file("/proc/net/raw6", 6, 'r', match_ip, fi, inode) ||
241 scan_proc_net_file("/proc/net/unix", 0, 0, match_unix, fi, inode) ||
242 scan_proc_net_file("/proc/net/netlink", 0, 0, match_netlink, fi, inode);
245 static void fill_stat(struct file_info *fi, const char* path)
250 if (stat(path, &sb)) return;
253 switch ((sb.st_mode & S_IFMT)) {
254 case S_IFBLK: strcpy(fi->type, "BLK"); break;
255 case S_IFCHR: strcpy(fi->type, "CHR"); break;
256 case S_IFDIR: strcpy(fi->type, "DIR"); break;
257 case S_IFIFO: strcpy(fi->type, "FIFO"); break;
258 case S_IFLNK: strcpy(fi->type, "LINK"); break;
259 case S_IFREG: strcpy(fi->type, "REG"); break;
260 case S_IFSOCK: strcpy(fi->type, "sock"); break;
262 snprintf(fi->type, sizeof(fi->type), "0%03o", sb.st_mode & S_IFMT);
266 if (S_ISSOCK(sb.st_mode)) find_socket(fi, sb.st_ino);
269 dev = (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) ? sb.st_rdev : sb.st_dev;
270 if (!S_ISSOCK(sb.st_mode))
271 snprintf(fi->device, sizeof(fi->device), "%ld,%ld",
272 (long)major(dev), (long)minor(dev));
275 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
276 snprintf(fi->size_off, sizeof(fi->size_off), "%lld",
277 (long long)sb.st_size);
280 snprintf(fi->node, sizeof(fi->node), "%ld", (long)sb.st_ino);
282 // Stash st_dev and st_ino for filtering.
283 fi->st_dev = sb.st_dev;
284 fi->st_ino = sb.st_ino;
287 struct file_info *new_file_info(struct proc_info *pi, const char* fd)
289 struct file_info *fi = xzalloc(sizeof(struct file_info));
291 dlist_add_nomalloc(&TT.files, (struct double_list *)fi);
297 strcpy(fi->type, "unknown");
298 fi->rw = fi->locks = ' ';
303 static void visit_symlink(struct proc_info *pi, char* name, char* path)
305 struct file_info *fi = new_file_info(pi, "");
308 if (name) { // "/proc/pid/[cwd]".
309 snprintf(fi->fd, sizeof(fi->fd), "%s", name);
310 snprintf(toybuf, sizeof(toybuf), "/proc/%d/%s", pi->pid, path);
311 } else { // "/proc/pid/fd/[3]"
312 snprintf(fi->fd, sizeof(fi->fd), "%s", path);
313 fill_flags(fi); // Clobbers toybuf.
314 snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd/%s", pi->pid, path);
316 // TODO: code called by fill_stat would be easier to write if we didn't
317 // rely on toybuf being preserved here.
318 fill_stat(fi, toybuf);
319 if (!fi->name) { // We already have a name for things like sockets.
320 fi->name = xreadlink(toybuf);
322 fi->name = xmprintf("%s (readlink: %s)", toybuf, strerror(errno));
327 static void visit_maps(struct proc_info *pi)
330 unsigned long long offset;
334 size_t line_length = 0;
336 snprintf(toybuf, sizeof(toybuf), "/proc/%d/maps", pi->pid);
337 fp = fopen(toybuf, "r");
340 while (getline(&line, &line_length, fp) > 0) {
343 if (sscanf(line, "%*x-%*x %*s %llx %s %ld %n",
344 &offset, device, &inode, &name_pos) >= 3) {
345 struct file_info *fi;
347 // Ignore non-file maps.
348 if (inode == 0 || !strcmp(device, "00:00")) continue;
349 // TODO: show unique maps even if they have a non-zero offset?
350 if (offset != 0) continue;
352 fi = new_file_info(pi, "mem");
353 fi->name = strdup(chomp(line + name_pos));
354 fill_stat(fi, fi->name);
361 static void visit_fds(struct proc_info *pi)
366 snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd", pi->pid);
367 if (!(dir = opendir(toybuf))) {
368 struct file_info *fi = new_file_info(pi, "NOFD");
370 fi->name = xmprintf("%s (opendir: %s)", toybuf, strerror(errno));
374 while ((de = readdir(dir))) {
375 if (*de->d_name == '.') continue;
376 visit_symlink(pi, NULL, de->d_name);
382 static void lsof_pid(int pid)
389 // Does this process even exist?
390 snprintf(toybuf, sizeof(toybuf), "/proc/%d/stat", pid);
391 fp = fopen(toybuf, "r");
396 line = fgets(toybuf, sizeof(toybuf), fp);
399 char *open_paren = strchr(toybuf, '(');
400 char *close_paren = strrchr(toybuf, ')');
402 if (open_paren && close_paren) {
404 snprintf(pi.cmd, sizeof(pi.cmd), "%s", open_paren + 1);
408 // We already know PID.
412 snprintf(toybuf, sizeof(toybuf), "/proc/%d", pid);
413 if (!stat(toybuf, &sb)) {
416 if (!(toys.optflags&FLAG_l) && (pw = getpwuid(sb.st_uid))) {
417 snprintf(pi.user, sizeof(pi.user), "%s", pw->pw_name);
418 } else snprintf(pi.user, sizeof(pi.user), "%u", (unsigned)sb.st_uid);
421 visit_symlink(&pi, "cwd", "cwd");
422 visit_symlink(&pi, "rtd", "root");
423 visit_symlink(&pi, "txt", "exe");
428 static int scan_slash_proc(struct dirtree *node)
432 if (!node->parent) return DIRTREE_RECURSE;
433 if ((pid = atol(node->name))) lsof_pid(pid);
441 // lsof will only filter on paths it can stat (because it filters by inode).
442 TT.sought_files = xmalloc(toys.optc*sizeof(struct stat));
443 for (i = 0; i < toys.optc; ++i) {
444 xstat(toys.optargs[i], &(TT.sought_files[i]));
447 if (toys.optflags&FLAG_p) {
451 while ((pid_str = comma_iterate(&TT.pids, &length))) {
453 if (!(pid = atoi(pid_str))) error_exit("bad pid '%s'", pid_str);
456 } else dirtree_read("/proc", scan_slash_proc);
458 llist_traverse(TT.files, print_info);