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 *
Pfd2info(struct ps_prochandle * P,int fd)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
load_fdinfo(struct ps_prochandle * P)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
Pfdinfo_iter(struct ps_prochandle * P,proc_fdinfo_f * func,void * cd)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