]> git.saurik.com Git - apple/libc.git/blob - darwin/proc_listpidspath.c
028a67408675a4114f93c7afb20be0c8bb9edc7f
[apple/libc.git] / darwin / proc_listpidspath.c
1 /*
2 * Copyright (c) 2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/fcntl.h>
28 #include <sys/errno.h>
29 #include <sys/param.h>
30 #include <sys/mount.h>
31 #include <libproc.h>
32
33
34 typedef struct {
35 // process IDs
36 int *pids;
37 int pids_count;
38 size_t pids_size;
39
40 // open file descriptors
41 struct proc_fdinfo *fds;
42 int fds_count;
43 size_t fds_size;
44
45 // file/volume of interest
46 struct stat match_stat;
47
48 // flags
49 uint32_t flags;
50
51 } fdOpenInfo, *fdOpenInfoRef;
52
53
54 /*
55 * check_init
56 */
57 static fdOpenInfoRef
58 check_init(const char *path, uint32_t flags)
59 {
60 fdOpenInfoRef info;
61 int status;
62
63 info = malloc(sizeof(*info));
64 if (!info)
65 return NULL;
66
67 info->pids = NULL;
68 info->pids_count = 0;
69 info->pids_size = 0;
70
71 info->fds = NULL;
72 info->fds_count = 0;
73 info->fds_size = 0;
74
75 status = stat(path, &info->match_stat);
76 if (status == -1) {
77 goto fail;
78 }
79
80 info->flags = flags;
81
82 return info;
83
84 fail :
85
86 free(info);
87 return NULL;
88 }
89
90
91 /*
92 * check_free
93 */
94 static void
95 check_free(fdOpenInfoRef info)
96 {
97 if (info->pids != NULL) {
98 free(info->pids);
99 }
100
101 if (info->fds != NULL) {
102 free(info->fds);
103 }
104
105 free(info);
106
107 return;
108 }
109
110
111 /*
112 * check_file
113 * check if a process vnode is of interest
114 *
115 * in : vnode stat(2)
116 * out : -1 if error
117 * 0 if no match
118 * 1 if match
119 */
120 static int
121 check_file(fdOpenInfoRef info, struct vinfo_stat *sb)
122 {
123 if (sb->vst_dev == 0) {
124 // if no info
125 return 0;
126 }
127
128 if (sb->vst_dev != info->match_stat.st_dev) {
129 // if not the requested filesystem
130 return 0;
131 }
132
133 if (!(info->flags & PROC_LISTPIDSPATH_PATH_IS_VOLUME) &&
134 (sb->vst_ino != info->match_stat.st_ino)) {
135 // if not the requested file
136 return 0;
137 }
138
139 return 1;
140 }
141
142
143 /*
144 * check_process_vnodes
145 * check [process] current working directory
146 * check [process] root directory
147 *
148 * in : pid
149 * out : -1 if error
150 * 0 if no match
151 * 1 if match
152 */
153 static int
154 check_process_vnodes(fdOpenInfoRef info, int pid)
155 {
156 int buf_used;
157 int status;
158 struct proc_vnodepathinfo vpi;
159
160 buf_used = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
161 if (buf_used <= 0) {
162 if (errno == ESRCH) {
163 // if the process is gone
164 return 0;
165 }
166 return -1;
167 } else if (buf_used < sizeof(vpi)) {
168 // if we didn't get enough information
169 return -1;
170 }
171
172 // processing current working directory
173 status = check_file(info, &vpi.pvi_cdir.vip_vi.vi_stat);
174 if (status != 0) {
175 // if error or match
176 return status;
177 }
178
179 // processing root directory
180 status = check_file(info, &vpi.pvi_rdir.vip_vi.vi_stat);
181 if (status != 0) {
182 // if error or match
183 return status;
184 }
185
186 return 0;
187 }
188
189
190 /*
191 * check_process_text
192 * check [process] text (memory)
193 *
194 * in : pid
195 * out : -1 if error
196 * 0 if no match
197 * 1 if match
198 */
199 static int
200 check_process_text(fdOpenInfoRef info, int pid)
201 {
202 uint64_t a = 0;
203 int status;
204
205 while (1) { // for all memory regions
206 int buf_used;
207 struct proc_regionwithpathinfo rwpi;
208
209 // processing next address
210 buf_used = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, a, &rwpi, sizeof(rwpi));
211 if (buf_used <= 0) {
212 if ((errno == ESRCH) || (errno == EINVAL)) {
213 // if no more text information is available for this process.
214 break;
215 }
216 return -1;
217 } else if (buf_used < sizeof(rwpi)) {
218 // if we didn't get enough information
219 return -1;
220 }
221
222 //if (rwpi.prp_vip.vip_path[0])
223 status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
224 if (status != 0) {
225 // if error or match
226 return status;
227 }
228
229 a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
230 }
231
232 return 0;
233 }
234
235
236 /*
237 * check_process_fds
238 * check [process] open file descriptors
239 *
240 * in : pid
241 * out : -1 if error
242 * 0 if no match
243 * 1 if match
244 */
245 static int
246 check_process_fds(fdOpenInfoRef info, int pid)
247 {
248 int buf_used;
249 int i;
250 int status;
251
252 // get list of open file descriptors
253 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
254 if (buf_used <= 0) {
255 return -1;
256 }
257
258 while (1) {
259 if (buf_used > info->fds_size) {
260 // if we need to allocate [more] space
261 while (buf_used > info->fds_size) {
262 info->fds_size += (sizeof(struct proc_fdinfo) * 32);
263 }
264
265 if (info->fds == NULL) {
266 info->fds = malloc(info->fds_size);
267 } else {
268 info->fds = reallocf(info->fds, info->fds_size);
269 }
270 if (info->fds == NULL) {
271 return -1;
272 }
273 }
274
275 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info->fds, info->fds_size);
276 if (buf_used <= 0) {
277 return -1;
278 }
279
280 if ((buf_used + sizeof(struct proc_fdinfo)) >= info->fds_size) {
281 // if not enough room in the buffer for an extra fd
282 buf_used = info->fds_size;
283 continue;
284 }
285
286 info->fds_count = buf_used / sizeof(struct proc_fdinfo);
287 break;
288 }
289
290 // iterate through each file descriptor
291 for (i = 0; i < info->fds_count; i++) {
292 struct proc_fdinfo *fdp;
293
294 fdp = &info->fds[i];
295 switch (fdp->proc_fdtype) {
296 case PROX_FDTYPE_VNODE : {
297 int buf_used;
298 struct vnode_fdinfo vi;
299
300 buf_used = proc_pidfdinfo(pid, fdp->proc_fd, PROC_PIDFDVNODEINFO, &vi, sizeof(vi));
301 if (buf_used <= 0) {
302 if (errno == ENOENT) {
303 /*
304 * The file descriptor's vnode may have been revoked. This is a
305 * bit of a hack, since an ENOENT error might not always mean the
306 * descriptor's vnode has been revoked. As the libproc API
307 * matures, this code may need to be revisited.
308 */
309 continue;
310 }
311 return -1;
312 } else if (buf_used < sizeof(vi)) {
313 // if we didn't get enough information
314 return -1;
315 }
316
317 if ((info->flags & PROC_LISTPIDSPATH_EXCLUDE_EVTONLY) &&
318 (vi.pfi.fi_openflags & O_EVTONLY)) {
319 // if this file should be excluded
320 continue;
321 }
322
323 status = check_file(info, &vi.pvi.vi_stat);
324 if (status != 0) {
325 // if error or match
326 return status;
327 }
328 break;
329 }
330 default :
331 break;
332 }
333 }
334
335 return 0;
336 }
337
338
339 /*
340 * check_process
341 * check [process] current working and root directories
342 * check [process] text (memory)
343 * check [process] open file descriptors
344 *
345 * in : pid
346 * out : -1 if error
347 * 0 if no match
348 * 1 if match
349 */
350 static int
351 check_process(fdOpenInfoRef info, int pid)
352 {
353 int status;
354
355 // check root and current working directory
356 status = check_process_vnodes(info, pid);
357 if (status != 0) {
358 // if error or match
359 return status;
360 }
361
362 // check process text (memory)
363 status = check_process_text(info, pid);
364 if (status != 0) {
365 // if error or match
366 return status;
367 }
368
369 // check open file descriptors
370 status = check_process_fds(info, pid);
371 if (status != 0) {
372 // if error or match
373 return status;
374 }
375
376 return 0;
377 }
378
379
380 /*
381 * proc_listpidspath
382 *
383 * in : type
384 * : typeinfo
385 * : path
386 * : pathflags
387 * : buffer
388 * : buffersize
389 * out : buffer filled with process IDs that have open file
390 * references that match the specified path or volume;
391 * return value is the bytes of the returned buffer
392 * that contains valid information.
393 */
394 int
395 proc_listpidspath(uint32_t type,
396 uint32_t typeinfo,
397 const char *path,
398 uint32_t pathflags,
399 void *buffer,
400 int buffersize)
401 {
402 int buf_used;
403 int *buf_next = (int *)buffer;
404 int i;
405 fdOpenInfoRef info;
406 int status = -1;
407
408 if (buffer == NULL) {
409 // if this is a sizing request
410 return proc_listpids(type, typeinfo, NULL, 0);
411 }
412
413 buffersize -= (buffersize % sizeof(int)); // make whole number of ints
414 if (buffersize < sizeof(int)) {
415 // if we can't even return a single PID
416 errno = ENOMEM;
417 return -1;
418 }
419
420 // init
421 info = check_init(path, pathflags);
422 if (info == NULL) {
423 return -1;
424 }
425
426 // get list of processes
427 buf_used = proc_listpids(type, typeinfo, NULL, 0);
428 if (buf_used <= 0) {
429 goto done;
430 }
431
432 while (1) {
433 if (buf_used > info->pids_size) {
434 // if we need to allocate [more] space
435 while (buf_used > info->pids_size) {
436 info->pids_size += (sizeof(int) * 32);
437 }
438
439 if (info->pids == NULL) {
440 info->pids = malloc(info->pids_size);
441 } else {
442 info->pids = reallocf(info->pids, info->pids_size);
443 }
444 if (info->pids == NULL) {
445 goto done;
446 }
447 }
448
449 buf_used = proc_listpids(type, typeinfo, info->pids, info->pids_size);
450 if (buf_used <= 0) {
451 goto done;
452 }
453
454 if ((buf_used + sizeof(int)) >= info->pids_size) {
455 // if not enough room in the buffer for an extra pid
456 buf_used = info->pids_size;
457 continue;
458 }
459
460 info->pids_count = buf_used / sizeof(int);
461 break;
462 }
463
464 // iterate through each process
465 buf_used = 0;
466 for (i = info->pids_count - 1; i >= 0; i--) {
467 int pid;
468 int status;
469
470 pid = info->pids[i];
471 if (pid == 0) {
472 continue;
473 }
474
475 status = check_process(info, pid);
476 if (status != 1) {
477 // if not a match
478 continue;
479 }
480
481 *buf_next++ = pid;
482 buf_used += sizeof(int);
483
484 if (buf_used >= buffersize) {
485 // if we have filled the buffer
486 break;
487 }
488 }
489
490 status = buf_used;
491
492 done :
493
494 // cleanup
495 check_free(info);
496
497 return status;
498 }