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@
27 #include <sys/fcntl.h>
28 #include <sys/errno.h>
29 #include <sys/param.h>
30 #include <sys/mount.h>
45 // open file descriptors
46 struct proc_fdinfo
*fds
;
50 // file/volume of interest
51 struct stat match_stat
;
56 } fdOpenInfo
, *fdOpenInfoRef
;
63 check_init(const char *path
, uint32_t flags
)
68 info
= malloc(sizeof(*info
));
84 status
= stat(path
, &info
->match_stat
);
104 check_free(fdOpenInfoRef info
)
106 if (info
->pids
!= NULL
) {
110 if (info
->threads
!= NULL
) {
114 if (info
->fds
!= NULL
) {
126 * check if a process vnode is of interest
134 check_file(fdOpenInfoRef info
, struct vinfo_stat
*sb
)
136 if (sb
->vst_dev
== 0) {
141 if (sb
->vst_dev
!= info
->match_stat
.st_dev
) {
142 // if not the requested filesystem
146 if (!(info
->flags
& PROC_LISTPIDSPATH_PATH_IS_VOLUME
) &&
147 (sb
->vst_ino
!= info
->match_stat
.st_ino
)) {
148 // if not the requested file
157 * check_process_vnodes
158 * check [process] current working directory
159 * check [process] root directory
167 check_process_vnodes(fdOpenInfoRef info
, int pid
)
171 struct proc_vnodepathinfo vpi
;
173 buf_used
= proc_pidinfo(pid
, PROC_PIDVNODEPATHINFO
, 0, &vpi
, sizeof(vpi
));
175 if (errno
== ESRCH
) {
176 // if the process is gone
180 } else if (buf_used
< sizeof(vpi
)) {
181 // if we didn't get enough information
185 // processing current working directory
186 status
= check_file(info
, &vpi
.pvi_cdir
.vip_vi
.vi_stat
);
192 // processing root directory
193 status
= check_file(info
, &vpi
.pvi_rdir
.vip_vi
.vi_stat
);
205 * check [process] text (memory)
213 check_process_text(fdOpenInfoRef info
, int pid
)
218 while (1) { // for all memory regions
220 struct proc_regionwithpathinfo rwpi
;
222 // processing next address
223 buf_used
= proc_pidinfo(pid
, PROC_PIDREGIONPATHINFO
, a
, &rwpi
, sizeof(rwpi
));
225 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
226 // if no more text information is available for this process.
230 } else if (buf_used
< sizeof(rwpi
)) {
231 // if we didn't get enough information
235 status
= check_file(info
, &rwpi
.prp_vip
.vip_vi
.vi_stat
);
241 a
= rwpi
.prp_prinfo
.pri_address
+ rwpi
.prp_prinfo
.pri_size
;
250 * check [process] open file descriptors
258 check_process_fds(fdOpenInfoRef info
, int pid
)
264 // get list of open file descriptors
265 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, NULL
, 0);
271 if (buf_used
> info
->fds_size
) {
272 // if we need to allocate [more] space
273 while (buf_used
> info
->fds_size
) {
274 info
->fds_size
+= (sizeof(struct proc_fdinfo
) * 32);
277 if (info
->fds
== NULL
) {
278 info
->fds
= malloc(info
->fds_size
);
280 info
->fds
= reallocf(info
->fds
, info
->fds_size
);
282 if (info
->fds
== NULL
) {
287 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, info
->fds
, info
->fds_size
);
292 if ((buf_used
+ sizeof(struct proc_fdinfo
)) >= info
->fds_size
) {
293 // if not enough room in the buffer for an extra fd
294 buf_used
= info
->fds_size
+ sizeof(struct proc_fdinfo
);
298 info
->fds_count
= buf_used
/ sizeof(struct proc_fdinfo
);
302 // iterate through each file descriptor
303 for (i
= 0; i
< info
->fds_count
; i
++) {
304 struct proc_fdinfo
*fdp
;
307 switch (fdp
->proc_fdtype
) {
308 case PROX_FDTYPE_VNODE
: {
310 struct vnode_fdinfo vi
;
312 buf_used
= proc_pidfdinfo(pid
, fdp
->proc_fd
, PROC_PIDFDVNODEINFO
, &vi
, sizeof(vi
));
314 if (errno
== ENOENT
) {
316 * The file descriptor's vnode may have been revoked. This is a
317 * bit of a hack, since an ENOENT error might not always mean the
318 * descriptor's vnode has been revoked. As the libproc API
319 * matures, this code may need to be revisited.
324 } else if (buf_used
< sizeof(vi
)) {
325 // if we didn't get enough information
329 if ((info
->flags
& PROC_LISTPIDSPATH_EXCLUDE_EVTONLY
) &&
330 (vi
.pfi
.fi_openflags
& O_EVTONLY
)) {
331 // if this file should be excluded
335 status
= check_file(info
, &vi
.pvi
.vi_stat
);
352 * check_process_threads
353 * check [process] thread working directories
361 check_process_threads(fdOpenInfoRef info
, int pid
)
365 struct proc_taskallinfo tai
;
367 buf_used
= proc_pidinfo(pid
, PROC_PIDTASKALLINFO
, 0, &tai
, sizeof(tai
));
369 if (errno
== ESRCH
) {
370 // if the process is gone
374 } else if (buf_used
< sizeof(tai
)) {
375 // if we didn't get enough information
380 if (tai
.pbsd
.pbi_flags
& PROC_FLAG_THCWD
) {
383 // get list of threads
384 buf_used
= tai
.ptinfo
.pti_threadnum
* sizeof(uint64_t);
387 if (buf_used
> info
->thr_size
) {
388 // if we need to allocate [more] space
389 while (buf_used
> info
->thr_size
) {
390 info
->thr_size
+= (sizeof(uint64_t) * 32);
393 if (info
->threads
== NULL
) {
394 info
->threads
= malloc(info
->thr_size
);
396 info
->threads
= reallocf(info
->threads
, info
->thr_size
);
398 if (info
->threads
== NULL
) {
403 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTTHREADS
, 0, info
->threads
, info
->thr_size
);
408 if ((buf_used
+ sizeof(uint64_t)) >= info
->thr_size
) {
409 // if not enough room in the buffer for an extra thread
410 buf_used
= info
->thr_size
+ sizeof(uint64_t);
414 info
->thr_count
= buf_used
/ sizeof(uint64_t);
418 // iterate through each thread
419 for (i
= 0; i
< info
->thr_count
; i
++) {
420 uint64_t thr
= info
->threads
[i
];
421 struct proc_threadwithpathinfo tpi
;
423 buf_used
= proc_pidinfo(pid
, PROC_PIDTHREADPATHINFO
, thr
, &tpi
, sizeof(tpi
));
425 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
426 // if the process or thread is gone
429 } else if (buf_used
< sizeof(tai
)) {
430 // if we didn't get enough information
434 status
= check_file(info
, &tpi
.pvip
.vip_vi
.vi_stat
);
448 * check [process] current working and root directories
449 * check [process] text (memory)
450 * check [process] open file descriptors
458 check_process(fdOpenInfoRef info
, int pid
)
462 // check root and current working directory
463 status
= check_process_vnodes(info
, pid
);
469 // check process text (memory)
470 status
= check_process_text(info
, pid
);
476 // check open file descriptors
477 status
= check_process_fds(info
, pid
);
483 // check per-thread working directories
484 status
= check_process_threads(info
, pid
);
503 * out : buffer filled with process IDs that have open file
504 * references that match the specified path or volume;
505 * return value is the bytes of the returned buffer
506 * that contains valid information.
509 proc_listpidspath(uint32_t type
,
517 int *buf_next
= (int *)buffer
;
522 if (buffer
== NULL
) {
523 // if this is a sizing request
524 return proc_listpids(type
, typeinfo
, NULL
, 0);
527 buffersize
-= (buffersize
% sizeof(int)); // make whole number of ints
528 if (buffersize
< sizeof(int)) {
529 // if we can't even return a single PID
535 info
= check_init(path
, pathflags
);
540 // get list of processes
541 buf_used
= proc_listpids(type
, typeinfo
, NULL
, 0);
547 if (buf_used
> info
->pids_size
) {
548 // if we need to allocate [more] space
549 while (buf_used
> info
->pids_size
) {
550 info
->pids_size
+= (sizeof(int) * 32);
553 if (info
->pids
== NULL
) {
554 info
->pids
= malloc(info
->pids_size
);
556 info
->pids
= reallocf(info
->pids
, info
->pids_size
);
558 if (info
->pids
== NULL
) {
563 buf_used
= proc_listpids(type
, typeinfo
, info
->pids
, info
->pids_size
);
568 if ((buf_used
+ sizeof(int)) >= info
->pids_size
) {
569 // if not enough room in the buffer for an extra pid
570 buf_used
= info
->pids_size
+ sizeof(int);
574 info
->pids_count
= buf_used
/ sizeof(int);
578 // iterate through each process
580 for (i
= info
->pids_count
- 1; i
>= 0; i
--) {
589 status
= check_process(info
, pid
);
596 buf_used
+= sizeof(int);
598 if (buf_used
>= buffersize
) {
599 // if we have filled the buffer