xref: /illumos-gate/usr/src/lib/libproc/common/Pfdinfo.c (revision 28b6fd27d5ff75fe6fdeb119a21575b0652a7e70)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
14  */
15 /*
16  * Copyright (c) 2013 Joyent, Inc.  All Rights reserved.
17  */
18 
19 #include <limits.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <dirent.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <sys/mkdev.h>
27 
28 #include "libproc.h"
29 #include "Pcontrol.h"
30 
31 /*
32  * Pfdinfo.c - obtain open file information.
33  */
34 
35 /*
36  * Allocate an fd_info structure and stick it on the list.
37  * (Unless one already exists.)  The list is sorted in
38  * reverse order.  We will traverse it in that order later.
39  * This makes the usual ordered insert *fast*.
40  */
41 fd_info_t *
42 Pfd2info(struct ps_prochandle *P, int fd)
43 {
44 	fd_info_t	*fip = list_next(&P->fd_head);
45 	fd_info_t	*next;
46 	int i;
47 
48 	if (fip == NULL) {
49 		list_link(&P->fd_head, NULL);
50 		fip = list_next(&P->fd_head);
51 	}
52 
53 	for (i = 0; i < P->num_fd; i++, fip = list_next(fip)) {
54 		if (fip->fd_info.pr_fd == fd) {
55 			return (fip);
56 		}
57 		if (fip->fd_info.pr_fd < fd) {
58 			break;
59 		}
60 	}
61 
62 	next = fip;
63 	if ((fip = calloc(1, sizeof (*fip))) == NULL)
64 		return (NULL);
65 
66 	fip->fd_info.pr_fd = fd;
67 	list_link(fip, next ? next : (void *)&(P->fd_head));
68 	P->num_fd++;
69 	return (fip);
70 }
71 
72 /*
73  * Attempt to load the open file information from a live process.
74  */
75 static void
76 load_fdinfo(struct ps_prochandle *P)
77 {
78 	/*
79 	 * In the unlikely case there are *no* file descriptors open,
80 	 * we will keep rescanning the proc directory, which will be empty.
81 	 * This is an edge case it isn't worth adding additional state to
82 	 * to eliminate.
83 	 */
84 	if (P->num_fd > 0) {
85 		return;
86 	}
87 
88 	if (P->state != PS_DEAD && P->state != PS_IDLE) {
89 		char dir_name[PATH_MAX];
90 		char path[PATH_MAX];
91 		struct dirent *ent;
92 		DIR	*dirp;
93 		int	fd;
94 
95 		/*
96 		 * Try to get the path information first.
97 		 */
98 		(void) snprintf(dir_name, sizeof (dir_name),
99 		    "%s/%d/path", procfs_path, (int)P->pid);
100 		dirp = opendir(dir_name);
101 		if (dirp == NULL) {
102 			return;
103 		}
104 		ent = NULL;
105 		while ((ent = readdir(dirp)) != NULL) {
106 			fd_info_t	*fip;
107 			prfdinfo_t	*info;
108 			int		len;
109 			struct stat64	stat;
110 
111 			if (!isdigit(ent->d_name[0]))
112 				continue;
113 
114 			fd = atoi(ent->d_name);
115 
116 			fip = Pfd2info(P, fd);
117 			info = &fip->fd_info;
118 			info->pr_fd = fd;
119 
120 			if (pr_fstat64(P, fd, &stat) == 0) {
121 				info->pr_mode = stat.st_mode;
122 				info->pr_uid = stat.st_uid;
123 				info->pr_gid = stat.st_gid;
124 				info->pr_major = major(stat.st_dev);
125 				info->pr_minor = minor(stat.st_dev);
126 				info->pr_rmajor = major(stat.st_rdev);
127 				info->pr_rminor = minor(stat.st_rdev);
128 				info->pr_size = stat.st_size;
129 				info->pr_ino = stat.st_ino;
130 			}
131 
132 			info->pr_fileflags = pr_fcntl(P, fd, F_GETXFL, 0);
133 			info->pr_fdflags = pr_fcntl(P, fd, F_GETFD, 0);
134 			info->pr_offset = pr_llseek(P, fd, 0, SEEK_CUR);
135 
136 			/* attempt to determine the path to it */
137 			switch (info->pr_mode & S_IFMT) {
138 			case S_IFDOOR:
139 			case S_IFSOCK:
140 				/* not applicable */
141 				len = -1;
142 				break;
143 			default:
144 				(void) snprintf(path, sizeof (path),
145 				    "%s/%d/path/%d", procfs_path, (int)P->pid,
146 				    fd);
147 				len = readlink(path, info->pr_path,
148 				    sizeof (info->pr_path) - 1);
149 				break;
150 			}
151 
152 			if (len < 0) {
153 				info->pr_path[0] = 0;
154 			} else {
155 				info->pr_path[len] = 0;
156 			}
157 		}
158 		(void) closedir(dirp);
159 
160 	}
161 }
162 
163 int
164 Pfdinfo_iter(struct ps_prochandle *P, proc_fdinfo_f *func, void *cd)
165 {
166 	fd_info_t *fip;
167 	int rv;
168 
169 	/* Make sure we have live data, if appropriate */
170 	load_fdinfo(P);
171 
172 	/* NB: We walk the list backwards. */
173 
174 	for (fip = list_prev(&P->fd_head);
175 	    fip != (void *)&P->fd_head && fip != NULL;
176 	    fip = list_prev(fip)) {
177 		if ((rv = func(cd, &fip->fd_info)) != 0)
178 			return (rv);
179 	}
180 	return (0);
181 }
182