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
;
55 } fdOpenInfo
, *fdOpenInfoRef
;
62 check_init(const char *path
, uint32_t flags
)
67 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
)
217 while (1) { // for all memory regions
219 struct proc_regionwithpathinfo rwpi
;
221 // processing next address
222 buf_used
= proc_pidinfo(pid
, PROC_PIDREGIONPATHINFO
, a
, &rwpi
, sizeof(rwpi
));
224 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
225 // if no more text information is available for this process.
229 } else if (buf_used
< sizeof(rwpi
)) {
230 // if we didn't get enough information
234 status
= check_file(info
, &rwpi
.prp_vip
.vip_vi
.vi_stat
);
240 a
= rwpi
.prp_prinfo
.pri_address
+ rwpi
.prp_prinfo
.pri_size
;
249 * check [process] open file descriptors
257 check_process_fds(fdOpenInfoRef info
, int pid
)
263 // get list of open file descriptors
264 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, NULL
, 0);
270 if (buf_used
> info
->fds_size
) {
271 // if we need to allocate [more] space
272 while (buf_used
> info
->fds_size
) {
273 info
->fds_size
+= (sizeof(struct proc_fdinfo
) * 32);
276 if (info
->fds
== NULL
) {
277 info
->fds
= malloc(info
->fds_size
);
279 info
->fds
= reallocf(info
->fds
, info
->fds_size
);
281 if (info
->fds
== NULL
) {
286 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, info
->fds
, info
->fds_size
);
291 if ((buf_used
+ sizeof(struct proc_fdinfo
)) >= info
->fds_size
) {
292 // if not enough room in the buffer for an extra fd
293 buf_used
= info
->fds_size
+ sizeof(struct proc_fdinfo
);
297 info
->fds_count
= buf_used
/ sizeof(struct proc_fdinfo
);
301 // iterate through each file descriptor
302 for (i
= 0; i
< info
->fds_count
; i
++) {
303 struct proc_fdinfo
*fdp
;
306 switch (fdp
->proc_fdtype
) {
307 case PROX_FDTYPE_VNODE
: {
309 struct vnode_fdinfo vi
;
311 buf_used
= proc_pidfdinfo(pid
, fdp
->proc_fd
, PROC_PIDFDVNODEINFO
, &vi
, sizeof(vi
));
313 if (errno
== ENOENT
) {
315 * The file descriptor's vnode may have been revoked. This is a
316 * bit of a hack, since an ENOENT error might not always mean the
317 * descriptor's vnode has been revoked. As the libproc API
318 * matures, this code may need to be revisited.
323 } else if (buf_used
< sizeof(vi
)) {
324 // if we didn't get enough information
328 if ((info
->flags
& PROC_LISTPIDSPATH_EXCLUDE_EVTONLY
) &&
329 (vi
.pfi
.fi_openflags
& O_EVTONLY
)) {
330 // if this file should be excluded
334 status
= check_file(info
, &vi
.pvi
.vi_stat
);
351 * check_process_threads
352 * check [process] thread working directories
360 check_process_threads(fdOpenInfoRef info
, int pid
)
364 struct proc_taskallinfo tai
;
366 buf_used
= proc_pidinfo(pid
, PROC_PIDTASKALLINFO
, 0, &tai
, sizeof(tai
));
368 if (errno
== ESRCH
) {
369 // if the process is gone
373 } else if (buf_used
< sizeof(tai
)) {
374 // if we didn't get enough information
379 if (tai
.pbsd
.pbi_flags
& PROC_FLAG_THCWD
) {
382 // get list of threads
383 buf_used
= tai
.ptinfo
.pti_threadnum
* sizeof(uint64_t);
386 if (buf_used
> info
->thr_size
) {
387 // if we need to allocate [more] space
388 while (buf_used
> info
->thr_size
) {
389 info
->thr_size
+= (sizeof(uint64_t) * 32);
392 if (info
->threads
== NULL
) {
393 info
->threads
= malloc(info
->thr_size
);
395 info
->threads
= reallocf(info
->threads
, info
->thr_size
);
397 if (info
->threads
== NULL
) {
402 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTTHREADS
, 0, info
->threads
, info
->thr_size
);
407 if ((buf_used
+ sizeof(uint64_t)) >= info
->thr_size
) {
408 // if not enough room in the buffer for an extra thread
409 buf_used
= info
->thr_size
+ sizeof(uint64_t);
413 info
->thr_count
= buf_used
/ sizeof(uint64_t);
417 // iterate through each thread
418 for (i
= 0; i
< info
->thr_count
; i
++) {
419 uint64_t thr
= info
->threads
[i
];
420 struct proc_threadwithpathinfo tpi
;
422 buf_used
= proc_pidinfo(pid
, PROC_PIDTHREADPATHINFO
, thr
, &tpi
, sizeof(tpi
));
424 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
425 // if the process or thread is gone
428 } else if (buf_used
< sizeof(tai
)) {
429 // if we didn't get enough information
433 status
= check_file(info
, &tpi
.pvip
.vip_vi
.vi_stat
);
447 * check [process] current working and root directories
448 * check [process] text (memory)
449 * check [process] open file descriptors
457 check_process(fdOpenInfoRef info
, int pid
)
461 // check root and current working directory
462 status
= check_process_vnodes(info
, pid
);
468 // check process text (memory)
469 status
= check_process_text(info
, pid
);
475 // check open file descriptors
476 status
= check_process_fds(info
, pid
);
482 // check per-thread working directories
483 status
= check_process_threads(info
, pid
);
502 * out : buffer filled with process IDs that have open file
503 * references that match the specified path or volume;
504 * return value is the bytes of the returned buffer
505 * that contains valid information.
508 proc_listpidspath(uint32_t type
,
516 int *buf_next
= (int *)buffer
;
521 if (buffer
== NULL
) {
522 // if this is a sizing request
523 return proc_listpids(type
, typeinfo
, NULL
, 0);
526 buffersize
-= (buffersize
% sizeof(int)); // make whole number of ints
527 if (buffersize
< sizeof(int)) {
528 // if we can't even return a single PID
534 info
= check_init(path
, pathflags
);
539 // get list of processes
540 buf_used
= proc_listpids(type
, typeinfo
, NULL
, 0);
546 if (buf_used
> info
->pids_size
) {
547 // if we need to allocate [more] space
548 while (buf_used
> info
->pids_size
) {
549 info
->pids_size
+= (sizeof(int) * 32);
552 if (info
->pids
== NULL
) {
553 info
->pids
= malloc(info
->pids_size
);
555 info
->pids
= reallocf(info
->pids
, info
->pids_size
);
557 if (info
->pids
== NULL
) {
562 buf_used
= proc_listpids(type
, typeinfo
, info
->pids
, info
->pids_size
);
567 if ((buf_used
+ sizeof(int)) >= info
->pids_size
) {
568 // if not enough room in the buffer for an extra pid
569 buf_used
= info
->pids_size
+ sizeof(int);
573 info
->pids_count
= buf_used
/ sizeof(int);
577 // iterate through each process
579 for (i
= info
->pids_count
- 1; i
>= 0; i
--) {
588 status
= check_process(info
, pid
);
595 buf_used
+= sizeof(int);
597 if (buf_used
>= buffersize
) {
598 // if we have filled the buffer