]> git.saurik.com Git - apple/xnu.git/blame - libsyscall/wrappers/libproc/proc_listpidspath.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / libsyscall / wrappers / libproc / proc_listpidspath.c
CommitLineData
39236c6e
A
1/*
2 * Copyright (c) 2007, 2008 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 <stdlib.h>
25#include <unistd.h>
26#include <sys/fcntl.h>
27#include <sys/errno.h>
28#include <sys/param.h>
29#include <sys/mount.h>
30#include <libproc.h>
31
32
33typedef struct {
34 // process IDs
35 int *pids;
36 int pids_count;
37 size_t pids_size;
38
39 // threads
40 uint64_t *threads;
41 int thr_count;
42 size_t thr_size;
43
44 // open file descriptors
45 struct proc_fdinfo *fds;
46 int fds_count;
47 size_t fds_size;
48
49 // file/volume of interest
50 struct stat match_stat;
51
52 // flags
53 uint32_t flags;
54
55} fdOpenInfo, *fdOpenInfoRef;
56
57
58/*
59 * check_init
60 */
61static fdOpenInfoRef
62check_init(const char *path, uint32_t flags)
63{
64 fdOpenInfoRef info;
65 int status;
66
67 info = malloc(sizeof(*info));
68 if (!info)
69 return NULL;
70
71 info->pids = NULL;
72 info->pids_count = 0;
73 info->pids_size = 0;
74
75 info->threads = NULL;
76 info->thr_count = 0;
77 info->thr_size = 0;
78
79 info->fds = NULL;
80 info->fds_count = 0;
81 info->fds_size = 0;
82
83 status = stat(path, &info->match_stat);
84 if (status == -1) {
85 goto fail;
86 }
87
88 info->flags = flags;
89
90 return info;
91
92 fail :
93
94 free(info);
95 return NULL;
96}
97
98
99/*
100 * check_free
101 */
102static void
103check_free(fdOpenInfoRef info)
104{
105 if (info->pids != NULL) {
106 free(info->pids);
107 }
108
109 if (info->threads != NULL) {
110 free(info->threads);
111 }
112
113 if (info->fds != NULL) {
114 free(info->fds);
115 }
116
117 free(info);
118
119 return;
120}
121
122
123/*
124 * check_file
125 * check if a process vnode is of interest
126 *
127 * in : vnode stat(2)
128 * out : -1 if error
129 * 0 if no match
130 * 1 if match
131 */
132static int
133check_file(fdOpenInfoRef info, struct vinfo_stat *sb)
134{
135 if (sb->vst_dev == 0) {
136 // if no info
137 return 0;
138 }
139
140 if (sb->vst_dev != info->match_stat.st_dev) {
141 // if not the requested filesystem
142 return 0;
143 }
144
145 if (!(info->flags & PROC_LISTPIDSPATH_PATH_IS_VOLUME) &&
146 (sb->vst_ino != info->match_stat.st_ino)) {
147 // if not the requested file
148 return 0;
149 }
150
151 return 1;
152}
153
154
155/*
156 * check_process_vnodes
157 * check [process] current working directory
158 * check [process] root directory
159 *
160 * in : pid
161 * out : -1 if error
162 * 0 if no match
163 * 1 if match
164 */
165static int
166check_process_vnodes(fdOpenInfoRef info, int pid)
167{
168 int buf_used;
169 int status;
170 struct proc_vnodepathinfo vpi;
171
172 buf_used = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
173 if (buf_used <= 0) {
174 if (errno == ESRCH) {
175 // if the process is gone
176 return 0;
177 }
178 return -1;
179 } else if (buf_used < sizeof(vpi)) {
180 // if we didn't get enough information
181 return -1;
182 }
183
184 // processing current working directory
185 status = check_file(info, &vpi.pvi_cdir.vip_vi.vi_stat);
186 if (status != 0) {
187 // if error or match
188 return status;
189 }
190
191 // processing root directory
192 status = check_file(info, &vpi.pvi_rdir.vip_vi.vi_stat);
193 if (status != 0) {
194 // if error or match
195 return status;
196 }
197
198 return 0;
199}
200
201
202/*
203 * check_process_text
204 * check [process] text (memory)
205 *
206 * in : pid
207 * out : -1 if error
208 * 0 if no match
209 * 1 if match
210 */
211static int
212check_process_text(fdOpenInfoRef info, int pid)
213{
39236c6e 214 int status;
fe8ab488
A
215 int buf_used;
216 struct proc_regionwithpathinfo rwpi;
39236c6e 217
fe8ab488 218 if (info->flags & PROC_LISTPIDSPATH_PATH_IS_VOLUME) {
39236c6e 219
fe8ab488
A
220 // ask for first memory region that matches mountpoint
221 buf_used = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO3, info->match_stat.st_dev, &rwpi, sizeof(rwpi));
39236c6e
A
222 if (buf_used <= 0) {
223 if ((errno == ESRCH) || (errno == EINVAL)) {
224 // if no more text information is available for this process.
fe8ab488 225 return 0;
39236c6e
A
226 }
227 return -1;
228 } else if (buf_used < sizeof(rwpi)) {
229 // if we didn't get enough information
230 return -1;
231 }
fe8ab488 232
39236c6e
A
233 status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
234 if (status != 0) {
235 // if error or match
236 return status;
237 }
fe8ab488
A
238 } else {
239 uint64_t a = 0;
240
241 while (1) { // for all memory regions
242 // processing next address
243 buf_used = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO2, a, &rwpi, sizeof(rwpi));
244 if (buf_used <= 0) {
245 if ((errno == ESRCH) || (errno == EINVAL)) {
246 // if no more text information is available for this process.
247 break;
248 }
249 return -1;
250 } else if (buf_used < sizeof(rwpi)) {
251 // if we didn't get enough information
252 return -1;
253 }
254
255 status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
256 if (status != 0) {
257 // if error or match
258 return status;
259 }
260
261 a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
262 }
39236c6e
A
263 }
264
265 return 0;
266}
267
268
269/*
270 * check_process_fds
271 * check [process] open file descriptors
272 *
273 * in : pid
274 * out : -1 if error
275 * 0 if no match
276 * 1 if match
277 */
278static int
279check_process_fds(fdOpenInfoRef info, int pid)
280{
281 int buf_used;
282 int i;
283 int status;
284
285 // get list of open file descriptors
286 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
287 if (buf_used <= 0) {
288 return -1;
289 }
290
291 while (1) {
292 if (buf_used > info->fds_size) {
293 // if we need to allocate [more] space
294 while (buf_used > info->fds_size) {
295 info->fds_size += (sizeof(struct proc_fdinfo) * 32);
296 }
297
298 if (info->fds == NULL) {
299 info->fds = malloc(info->fds_size);
300 } else {
301 info->fds = reallocf(info->fds, info->fds_size);
302 }
303 if (info->fds == NULL) {
304 return -1;
305 }
306 }
307
fe8ab488 308 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info->fds, (int)info->fds_size);
39236c6e
A
309 if (buf_used <= 0) {
310 return -1;
311 }
312
313 if ((buf_used + sizeof(struct proc_fdinfo)) >= info->fds_size) {
314 // if not enough room in the buffer for an extra fd
fe8ab488 315 buf_used = (int)(info->fds_size + sizeof(struct proc_fdinfo));
39236c6e
A
316 continue;
317 }
318
fe8ab488 319 info->fds_count = (int)(buf_used / sizeof(struct proc_fdinfo));
39236c6e
A
320 break;
321 }
322
323 // iterate through each file descriptor
324 for (i = 0; i < info->fds_count; i++) {
325 struct proc_fdinfo *fdp;
326
327 fdp = &info->fds[i];
328 switch (fdp->proc_fdtype) {
329 case PROX_FDTYPE_VNODE : {
330 int buf_used;
331 struct vnode_fdinfo vi;
332
333 buf_used = proc_pidfdinfo(pid, fdp->proc_fd, PROC_PIDFDVNODEINFO, &vi, sizeof(vi));
334 if (buf_used <= 0) {
335 if (errno == ENOENT) {
336 /*
337 * The file descriptor's vnode may have been revoked. This is a
338 * bit of a hack, since an ENOENT error might not always mean the
339 * descriptor's vnode has been revoked. As the libproc API
340 * matures, this code may need to be revisited.
341 */
342 continue;
343 }
344 return -1;
345 } else if (buf_used < sizeof(vi)) {
346 // if we didn't get enough information
347 return -1;
348 }
349
350 if ((info->flags & PROC_LISTPIDSPATH_EXCLUDE_EVTONLY) &&
351 (vi.pfi.fi_openflags & O_EVTONLY)) {
352 // if this file should be excluded
353 continue;
354 }
355
356 status = check_file(info, &vi.pvi.vi_stat);
357 if (status != 0) {
358 // if error or match
359 return status;
360 }
361 break;
362 }
363 default :
364 break;
365 }
366 }
367
368 return 0;
369}
370
371
372/*
373 * check_process_threads
374 * check [process] thread working directories
375 *
376 * in : pid
377 * out : -1 if error
378 * 0 if no match
379 * 1 if match
380 */
381static int
382check_process_threads(fdOpenInfoRef info, int pid)
383{
384 int buf_used;
385 int status;
386 struct proc_taskallinfo tai;
387
388 buf_used = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &tai, sizeof(tai));
389 if (buf_used <= 0) {
390 if (errno == ESRCH) {
391 // if the process is gone
392 return 0;
393 }
394 return -1;
395 } else if (buf_used < sizeof(tai)) {
396 // if we didn't get enough information
397 return -1;
398 }
399
400 // check thread info
401 if (tai.pbsd.pbi_flags & PROC_FLAG_THCWD) {
402 int i;
403
404 // get list of threads
405 buf_used = tai.ptinfo.pti_threadnum * sizeof(uint64_t);
406
407 while (1) {
408 if (buf_used > info->thr_size) {
409 // if we need to allocate [more] space
410 while (buf_used > info->thr_size) {
411 info->thr_size += (sizeof(uint64_t) * 32);
412 }
413
414 if (info->threads == NULL) {
415 info->threads = malloc(info->thr_size);
416 } else {
417 info->threads = reallocf(info->threads, info->thr_size);
418 }
419 if (info->threads == NULL) {
420 return -1;
421 }
422 }
423
fe8ab488 424 buf_used = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, info->threads, (int)info->thr_size);
39236c6e
A
425 if (buf_used <= 0) {
426 return -1;
427 }
428
429 if ((buf_used + sizeof(uint64_t)) >= info->thr_size) {
430 // if not enough room in the buffer for an extra thread
fe8ab488 431 buf_used = (int)(info->thr_size + sizeof(uint64_t));
39236c6e
A
432 continue;
433 }
434
435 info->thr_count = buf_used / sizeof(uint64_t);
436 break;
437 }
438
439 // iterate through each thread
440 for (i = 0; i < info->thr_count; i++) {
441 uint64_t thr = info->threads[i];
442 struct proc_threadwithpathinfo tpi;
443
444 buf_used = proc_pidinfo(pid, PROC_PIDTHREADPATHINFO, thr, &tpi, sizeof(tpi));
445 if (buf_used <= 0) {
446 if ((errno == ESRCH) || (errno == EINVAL)) {
447 // if the process or thread is gone
448 continue;
449 }
450 } else if (buf_used < sizeof(tai)) {
451 // if we didn't get enough information
452 return -1;
453 }
454
455 status = check_file(info, &tpi.pvip.vip_vi.vi_stat);
456 if (status != 0) {
457 // if error or match
458 return status;
459 }
460 }
461 }
462
463 return 0;
464}
465
466
467/*
fe8ab488
A
468 * check_process_phase1
469 * check [process] process-wide current working and root directories
39236c6e 470 * check [process] open file descriptors
fe8ab488 471 * check [process] per-thread current working and root directories
39236c6e
A
472 *
473 * in : pid
474 * out : -1 if error
475 * 0 if no match
476 * 1 if match
477 */
478static int
fe8ab488 479check_process_phase1(fdOpenInfoRef info, int pid)
39236c6e
A
480{
481 int status;
482
483 // check root and current working directory
484 status = check_process_vnodes(info, pid);
485 if (status != 0) {
486 // if error or match
487 return status;
488 }
489
39236c6e
A
490 // check open file descriptors
491 status = check_process_fds(info, pid);
492 if (status != 0) {
493 // if error or match
494 return status;
495 }
496
497 // check per-thread working directories
498 status = check_process_threads(info, pid);
499 if (status != 0) {
500 // if error or match
501 return status;
502 }
503
504 return 0;
505}
506
fe8ab488
A
507/*
508 * check_process_phase2
509 * check [process] text (memory)
510 *
511 * in : pid
512 * out : -1 if error
513 * 0 if no match
514 * 1 if match
515 */
516static int
517check_process_phase2(fdOpenInfoRef info, int pid)
518{
519 int status;
520
521 // check process text (memory)
522 status = check_process_text(info, pid);
523 if (status != 0) {
524 // if error or match
525 return status;
526 }
527
528 return 0;
529}
39236c6e
A
530
531/*
532 * proc_listpidspath
533 *
534 * in : type
535 * : typeinfo
536 * : path
537 * : pathflags
538 * : buffer
539 * : buffersize
540 * out : buffer filled with process IDs that have open file
541 * references that match the specified path or volume;
542 * return value is the bytes of the returned buffer
543 * that contains valid information.
544 */
545int
546proc_listpidspath(uint32_t type,
547 uint32_t typeinfo,
548 const char *path,
549 uint32_t pathflags,
550 void *buffer,
551 int buffersize)
552{
553 int buf_used;
554 int *buf_next = (int *)buffer;
555 int i;
556 fdOpenInfoRef info;
557 int status = -1;
558
559 if (buffer == NULL) {
560 // if this is a sizing request
561 return proc_listpids(type, typeinfo, NULL, 0);
562 }
563
564 buffersize -= (buffersize % sizeof(int)); // make whole number of ints
565 if (buffersize < sizeof(int)) {
566 // if we can't even return a single PID
567 errno = ENOMEM;
568 return -1;
569 }
570
571 // init
572 info = check_init(path, pathflags);
573 if (info == NULL) {
574 return -1;
575 }
576
577 // get list of processes
578 buf_used = proc_listpids(type, typeinfo, NULL, 0);
579 if (buf_used <= 0) {
580 goto done;
581 }
582
583 while (1) {
584 if (buf_used > info->pids_size) {
585 // if we need to allocate [more] space
586 while (buf_used > info->pids_size) {
587 info->pids_size += (sizeof(int) * 32);
588 }
589
590 if (info->pids == NULL) {
591 info->pids = malloc(info->pids_size);
592 } else {
593 info->pids = reallocf(info->pids, info->pids_size);
594 }
595 if (info->pids == NULL) {
596 goto done;
597 }
598 }
599
fe8ab488 600 buf_used = proc_listpids(type, typeinfo, info->pids, (int)info->pids_size);
39236c6e
A
601 if (buf_used <= 0) {
602 goto done;
603 }
604
605 if ((buf_used + sizeof(int)) >= info->pids_size) {
606 // if not enough room in the buffer for an extra pid
fe8ab488 607 buf_used = (int)(info->pids_size + sizeof(int));
39236c6e
A
608 continue;
609 }
610
611 info->pids_count = buf_used / sizeof(int);
612 break;
613 }
614
615 // iterate through each process
616 buf_used = 0;
617 for (i = info->pids_count - 1; i >= 0; i--) {
618 int pid;
fe8ab488
A
619 int pstatus;
620
621 pid = info->pids[i];
622 if (pid == 0) {
623 continue;
624 }
625
626 pstatus = check_process_phase1(info, pid);
627 if (pstatus != 1) {
628 // if not a match
629 continue;
630 }
631
632 *buf_next++ = pid;
633 buf_used += sizeof(int);
634
635 if (buf_used >= buffersize) {
636 // if we have filled the buffer
637 break;
638 }
639 }
640
641 if (buf_used >= buffersize) {
642 // if we have filled the buffer
643 status = buf_used;
644 goto done;
645 }
646
647 // do a more expensive search if we still have buffer space
648 for (i = info->pids_count - 1; i >= 0; i--) {
649 int pid;
650 int pstatus;
39236c6e
A
651
652 pid = info->pids[i];
653 if (pid == 0) {
654 continue;
655 }
656
fe8ab488
A
657 pstatus = check_process_phase2(info, pid);
658 if (pstatus != 1) {
39236c6e
A
659 // if not a match
660 continue;
661 }
662
663 *buf_next++ = pid;
664 buf_used += sizeof(int);
665
666 if (buf_used >= buffersize) {
667 // if we have filled the buffer
668 break;
669 }
670 }
671
672 status = buf_used;
673
674 done :
675
676 // cleanup
677 check_free(info);
678
679 return status;
680}