2 * Copyright (c) 2007, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/fcntl.h>
27 #include <sys/errno.h>
28 #include <sys/param.h>
29 #include <sys/mount.h>
44 // open file descriptors
45 struct proc_fdinfo
*fds
;
49 // file/volume of interest
50 struct stat match_stat
;
54 } fdOpenInfo
, *fdOpenInfoRef
;
61 check_init(const char *path
, uint32_t flags
)
66 info
= malloc(sizeof(*info
));
83 status
= stat(path
, &info
->match_stat
);
103 check_free(fdOpenInfoRef info
)
105 if (info
->pids
!= NULL
) {
109 if (info
->threads
!= NULL
) {
113 if (info
->fds
!= NULL
) {
125 * check if a process vnode is of interest
133 check_file(fdOpenInfoRef info
, struct vinfo_stat
*sb
)
135 if (sb
->vst_dev
== 0) {
140 if (sb
->vst_dev
!= info
->match_stat
.st_dev
) {
141 // if not the requested filesystem
145 if (!(info
->flags
& PROC_LISTPIDSPATH_PATH_IS_VOLUME
) &&
146 (sb
->vst_ino
!= info
->match_stat
.st_ino
)) {
147 // if not the requested file
156 * check_process_vnodes
157 * check [process] current working directory
158 * check [process] root directory
166 check_process_vnodes(fdOpenInfoRef info
, int pid
)
170 struct proc_vnodepathinfo vpi
;
172 buf_used
= proc_pidinfo(pid
, PROC_PIDVNODEPATHINFO
, 0, &vpi
, sizeof(vpi
));
174 if (errno
== ESRCH
) {
175 // if the process is gone
179 } else if (buf_used
< sizeof(vpi
)) {
180 // if we didn't get enough information
184 // processing current working directory
185 status
= check_file(info
, &vpi
.pvi_cdir
.vip_vi
.vi_stat
);
191 // processing root directory
192 status
= check_file(info
, &vpi
.pvi_rdir
.vip_vi
.vi_stat
);
204 * check [process] text (memory)
212 check_process_text(fdOpenInfoRef info
, int pid
)
216 struct proc_regionwithpathinfo rwpi
;
218 if (info
->flags
& PROC_LISTPIDSPATH_PATH_IS_VOLUME
) {
219 // ask for first memory region that matches mountpoint
220 buf_used
= proc_pidinfo(pid
, PROC_PIDREGIONPATHINFO3
, info
->match_stat
.st_dev
, &rwpi
, sizeof(rwpi
));
222 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
223 // if no more text information is available for this process.
227 } else if (buf_used
< sizeof(rwpi
)) {
228 // if we didn't get enough information
232 status
= check_file(info
, &rwpi
.prp_vip
.vip_vi
.vi_stat
);
240 while (1) { // for all memory regions
241 // processing next address
242 buf_used
= proc_pidinfo(pid
, PROC_PIDREGIONPATHINFO2
, a
, &rwpi
, sizeof(rwpi
));
244 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
245 // if no more text information is available for this process.
249 } else if (buf_used
< sizeof(rwpi
)) {
250 // if we didn't get enough information
254 status
= check_file(info
, &rwpi
.prp_vip
.vip_vi
.vi_stat
);
260 a
= rwpi
.prp_prinfo
.pri_address
+ rwpi
.prp_prinfo
.pri_size
;
270 * check [process] open file descriptors
278 check_process_fds(fdOpenInfoRef info
, int pid
)
284 // get list of open file descriptors
285 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, NULL
, 0);
291 if (buf_used
> info
->fds_size
) {
292 // if we need to allocate [more] space
293 while (buf_used
> info
->fds_size
) {
294 info
->fds_size
+= (sizeof(struct proc_fdinfo
) * 32);
297 if (info
->fds
== NULL
) {
298 info
->fds
= malloc(info
->fds_size
);
300 info
->fds
= reallocf(info
->fds
, info
->fds_size
);
302 if (info
->fds
== NULL
) {
307 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, info
->fds
, (int)info
->fds_size
);
312 if ((buf_used
+ sizeof(struct proc_fdinfo
)) >= info
->fds_size
) {
313 // if not enough room in the buffer for an extra fd
314 buf_used
= (int)(info
->fds_size
+ sizeof(struct proc_fdinfo
));
318 info
->fds_count
= (int)(buf_used
/ sizeof(struct proc_fdinfo
));
322 // iterate through each file descriptor
323 for (i
= 0; i
< info
->fds_count
; i
++) {
324 struct proc_fdinfo
*fdp
;
327 switch (fdp
->proc_fdtype
) {
328 case PROX_FDTYPE_VNODE
: {
330 struct vnode_fdinfo vi
;
332 buf_used
= proc_pidfdinfo(pid
, fdp
->proc_fd
, PROC_PIDFDVNODEINFO
, &vi
, sizeof(vi
));
334 if (errno
== ENOENT
) {
336 * The file descriptor's vnode may have been revoked. This is a
337 * bit of a hack, since an ENOENT error might not always mean the
338 * descriptor's vnode has been revoked. As the libproc API
339 * matures, this code may need to be revisited.
344 } else if (buf_used
< sizeof(vi
)) {
345 // if we didn't get enough information
349 if ((info
->flags
& PROC_LISTPIDSPATH_EXCLUDE_EVTONLY
) &&
350 (vi
.pfi
.fi_openflags
& O_EVTONLY
)) {
351 // if this file should be excluded
355 status
= check_file(info
, &vi
.pvi
.vi_stat
);
372 * check_process_threads
373 * check [process] thread working directories
381 check_process_threads(fdOpenInfoRef info
, int pid
)
385 struct proc_taskallinfo tai
;
387 buf_used
= proc_pidinfo(pid
, PROC_PIDTASKALLINFO
, 0, &tai
, sizeof(tai
));
389 if (errno
== ESRCH
) {
390 // if the process is gone
394 } else if (buf_used
< sizeof(tai
)) {
395 // if we didn't get enough information
400 if (tai
.pbsd
.pbi_flags
& PROC_FLAG_THCWD
) {
403 // get list of threads
404 buf_used
= tai
.ptinfo
.pti_threadnum
* sizeof(uint64_t);
407 if (buf_used
> info
->thr_size
) {
408 // if we need to allocate [more] space
409 while (buf_used
> info
->thr_size
) {
410 info
->thr_size
+= (sizeof(uint64_t) * 32);
413 if (info
->threads
== NULL
) {
414 info
->threads
= malloc(info
->thr_size
);
416 info
->threads
= reallocf(info
->threads
, info
->thr_size
);
418 if (info
->threads
== NULL
) {
423 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTTHREADS
, 0, info
->threads
, (int)info
->thr_size
);
428 if ((buf_used
+ sizeof(uint64_t)) >= info
->thr_size
) {
429 // if not enough room in the buffer for an extra thread
430 buf_used
= (int)(info
->thr_size
+ sizeof(uint64_t));
434 info
->thr_count
= buf_used
/ sizeof(uint64_t);
438 // iterate through each thread
439 for (i
= 0; i
< info
->thr_count
; i
++) {
440 uint64_t thr
= info
->threads
[i
];
441 struct proc_threadwithpathinfo tpi
;
443 buf_used
= proc_pidinfo(pid
, PROC_PIDTHREADPATHINFO
, thr
, &tpi
, sizeof(tpi
));
445 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
446 // if the process or thread is gone
449 } else if (buf_used
< sizeof(tai
)) {
450 // if we didn't get enough information
454 status
= check_file(info
, &tpi
.pvip
.vip_vi
.vi_stat
);
467 * check_process_phase1
468 * check [process] process-wide current working and root directories
469 * check [process] open file descriptors
470 * check [process] per-thread current working and root directories
478 check_process_phase1(fdOpenInfoRef info
, int pid
)
482 // check root and current working directory
483 status
= check_process_vnodes(info
, pid
);
489 // check open file descriptors
490 status
= check_process_fds(info
, pid
);
496 // check per-thread working directories
497 status
= check_process_threads(info
, pid
);
507 * check_process_phase2
508 * check [process] text (memory)
516 check_process_phase2(fdOpenInfoRef info
, int pid
)
520 // check process text (memory)
521 status
= check_process_text(info
, pid
);
539 * out : buffer filled with process IDs that have open file
540 * references that match the specified path or volume;
541 * return value is the bytes of the returned buffer
542 * that contains valid information.
545 proc_listpidspath(uint32_t type
,
553 int *buf_next
= (int *)buffer
;
558 if (buffer
== NULL
) {
559 // if this is a sizing request
560 return proc_listpids(type
, typeinfo
, NULL
, 0);
563 buffersize
-= (buffersize
% sizeof(int)); // make whole number of ints
564 if (buffersize
< sizeof(int)) {
565 // if we can't even return a single PID
571 info
= check_init(path
, pathflags
);
576 // get list of processes
577 buf_used
= proc_listpids(type
, typeinfo
, NULL
, 0);
583 if (buf_used
> info
->pids_size
) {
584 // if we need to allocate [more] space
585 while (buf_used
> info
->pids_size
) {
586 info
->pids_size
+= (sizeof(int) * 32);
589 if (info
->pids
== NULL
) {
590 info
->pids
= malloc(info
->pids_size
);
592 info
->pids
= reallocf(info
->pids
, info
->pids_size
);
594 if (info
->pids
== NULL
) {
599 buf_used
= proc_listpids(type
, typeinfo
, info
->pids
, (int)info
->pids_size
);
604 if ((buf_used
+ sizeof(int)) >= info
->pids_size
) {
605 // if not enough room in the buffer for an extra pid
606 buf_used
= (int)(info
->pids_size
+ sizeof(int));
610 info
->pids_count
= buf_used
/ sizeof(int);
614 // iterate through each process
616 for (i
= info
->pids_count
- 1; i
>= 0; i
--) {
625 pstatus
= check_process_phase1(info
, pid
);
632 buf_used
+= sizeof(int);
634 if (buf_used
>= buffersize
) {
635 // if we have filled the buffer
640 if (buf_used
>= buffersize
) {
641 // if we have filled the buffer
646 // do a more expensive search if we still have buffer space
647 for (i
= info
->pids_count
- 1; i
>= 0; i
--) {
656 pstatus
= check_process_phase2(info
, pid
);
663 buf_used
+= sizeof(int);
665 if (buf_used
>= buffersize
) {
666 // if we have filled the buffer