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 #include <limits.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include <unistd.h>
20 #include <dirent.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <sys/mkdev.h>
24 
25 #include "libproc.h"
26 #include "Pcontrol.h"
27 
28 /*
29  * Pfdinfo.c - obtain open file information.
30  */
31 
32 /*
33  * Allocate an fd_info structure and stick it on the list.
34  * (Unless one already exists.)  The list is sorted in
35  * reverse order.  We will traverse it in that order later.
36  * This makes the usual ordered insert *fast*.
37  */
38 fd_info_t *
39 Pfd2info(struct ps_prochandle *P, int fd)
40 {
41 	fd_info_t	*fip = list_next(&P->fd_head);
42 	fd_info_t	*next;
43 	int i;
44 
45 	if (fip == NULL) {
46 		list_link(&P->fd_head, NULL);
47 		fip = list_next(&P->fd_head);
48 	}
49 
50 	for (i = 0; i < P->num_fd; i++, fip = list_next(fip)) {
51 		if (fip->fd_info.pr_fd == fd) {
52 			return (fip);
53 		}
54 		if (fip->fd_info.pr_fd < fd) {
55 			break;
56 		}
57 	}
58 
59 	next = fip;
60 	if ((fip = calloc(1, sizeof (*fip))) == NULL)
61 		return (NULL);
62 
63 	fip->fd_info.pr_fd = fd;
64 	list_link(fip, next ? next : (void *)&(P->fd_head));
65 	P->num_fd++;
66 	return (fip);
67 }
68 
69 /*
70  * Attempt to load the open file information from a live process.
71  */
72 static void
73 load_fdinfo(struct ps_prochandle *P)
74 {
75 	/*
76 	 * In the unlikely case there are *no* file descriptors open,
77 	 * we will keep rescanning the proc directory, which will be empty.
78 	 * This is an edge case it isn't worth adding additional state to
79 	 * to eliminate.
80 	 */
81 	if (P->num_fd > 0) {
82 		return;
83 	}
84 
85 	if (P->state != PS_DEAD && P->state != PS_IDLE) {
86 		char dir_name[PATH_MAX];
87 		char path[PATH_MAX];
88 		struct dirent *ent;
89 		DIR	*dirp;
90 		int	fd;
91 
92 		/*
93 		 * Try to get the path information first.
94 		 */
95 		(void) snprintf(dir_name, sizeof (dir_name),
96 		    "%s/%d/path", procfs_path, (int)P->pid);
97 		dirp = opendir(dir_name);
98 		if (dirp == NULL) {
99 			return;
100 		}
101 		ent = NULL;
102 		while ((ent = readdir(dirp)) != NULL) {
103 			fd_info_t	*fip;
104 			prfdinfo_t	*info;
105 			int		len;
106 			struct stat64	stat;
107 
108 			if (!isdigit(ent->d_name[0]))
109 				continue;
110 
111 			fd = atoi(ent->d_name);
112 
113 			fip = Pfd2info(P, fd);
114 			info = &fip->fd_info;
115 			info->pr_fd = fd;
116 
117 			if (pr_fstat64(P, fd, &stat) == 0) {
118 				info->pr_mode = stat.st_mode;
119 				info->pr_uid = stat.st_uid;
120 				info->pr_gid = stat.st_gid;
121 				info->pr_major = major(stat.st_dev);
122 				info->pr_minor = minor(stat.st_dev);
123 				info->pr_rmajor = major(stat.st_rdev);
124 				info->pr_rminor = minor(stat.st_rdev);
125 				info->pr_size = stat.st_size;
126 				info->pr_ino = stat.st_ino;
127 			}
128 
129 			info->pr_fileflags = pr_fcntl(P, fd, F_GETXFL, 0);
130 			info->pr_fdflags = pr_fcntl(P, fd, F_GETFD, 0);
131 			info->pr_offset = pr_llseek(P, fd, 0, SEEK_CUR);
132 
133 			/* attempt to determine the path to it */
134 			(void) snprintf(path, sizeof (path),
135 			    "%s/%d/path/%d", procfs_path, (int)P->pid, fd);
136 			len = readlink(path, info->pr_path,
137 			    sizeof (info->pr_path) - 1);
138 
139 			if (len < 0) {
140 				info->pr_path[0] = 0;
141 			} else {
142 				info->pr_path[len] = 0;
143 			}
144 		}
145 		(void) closedir(dirp);
146 
147 	}
148 }
149 
150 int
151 Pfdinfo_iter(struct ps_prochandle *P, proc_fdinfo_f *func, void *cd)
152 {
153 	fd_info_t *fip;
154 	int rv;
155 
156 	/* Make sure we have live data, if appropriate */
157 	load_fdinfo(P);
158 
159 	/* NB: We walk the list backwards. */
160 
161 	for (fip = list_prev(&P->fd_head);
162 	    fip != (void *)&P->fd_head;
163 	    fip = list_prev(fip)) {
164 		if ((rv = func(cd, &fip->fd_info)) != 0)
165 			return (rv);
166 	}
167 	return (0);
168 }
169