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
)
216 struct proc_regionwithpathinfo rwpi
;
218 if (info
->flags
& PROC_LISTPIDSPATH_PATH_IS_VOLUME
) {
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
));
223 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
224 // if no more text information is available for this process.
228 } else if (buf_used
< sizeof(rwpi
)) {
229 // if we didn't get enough information
233 status
= check_file(info
, &rwpi
.prp_vip
.vip_vi
.vi_stat
);
241 while (1) { // for all memory regions
242 // processing next address
243 buf_used
= proc_pidinfo(pid
, PROC_PIDREGIONPATHINFO2
, a
, &rwpi
, sizeof(rwpi
));
245 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
246 // if no more text information is available for this process.
250 } else if (buf_used
< sizeof(rwpi
)) {
251 // if we didn't get enough information
255 status
= check_file(info
, &rwpi
.prp_vip
.vip_vi
.vi_stat
);
261 a
= rwpi
.prp_prinfo
.pri_address
+ rwpi
.prp_prinfo
.pri_size
;
271 * check [process] open file descriptors
279 check_process_fds(fdOpenInfoRef info
, int pid
)
285 // get list of open file descriptors
286 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, NULL
, 0);
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);
298 if (info
->fds
== NULL
) {
299 info
->fds
= malloc(info
->fds_size
);
301 info
->fds
= reallocf(info
->fds
, info
->fds_size
);
303 if (info
->fds
== NULL
) {
308 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, info
->fds
, (int)info
->fds_size
);
313 if ((buf_used
+ sizeof(struct proc_fdinfo
)) >= info
->fds_size
) {
314 // if not enough room in the buffer for an extra fd
315 buf_used
= (int)(info
->fds_size
+ sizeof(struct proc_fdinfo
));
319 info
->fds_count
= (int)(buf_used
/ sizeof(struct proc_fdinfo
));
323 // iterate through each file descriptor
324 for (i
= 0; i
< info
->fds_count
; i
++) {
325 struct proc_fdinfo
*fdp
;
328 switch (fdp
->proc_fdtype
) {
329 case PROX_FDTYPE_VNODE
: {
331 struct vnode_fdinfo vi
;
333 buf_used
= proc_pidfdinfo(pid
, fdp
->proc_fd
, PROC_PIDFDVNODEINFO
, &vi
, sizeof(vi
));
335 if (errno
== ENOENT
) {
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.
345 } else if (buf_used
< sizeof(vi
)) {
346 // if we didn't get enough information
350 if ((info
->flags
& PROC_LISTPIDSPATH_EXCLUDE_EVTONLY
) &&
351 (vi
.pfi
.fi_openflags
& O_EVTONLY
)) {
352 // if this file should be excluded
356 status
= check_file(info
, &vi
.pvi
.vi_stat
);
373 * check_process_threads
374 * check [process] thread working directories
382 check_process_threads(fdOpenInfoRef info
, int pid
)
386 struct proc_taskallinfo tai
;
388 buf_used
= proc_pidinfo(pid
, PROC_PIDTASKALLINFO
, 0, &tai
, sizeof(tai
));
390 if (errno
== ESRCH
) {
391 // if the process is gone
395 } else if (buf_used
< sizeof(tai
)) {
396 // if we didn't get enough information
401 if (tai
.pbsd
.pbi_flags
& PROC_FLAG_THCWD
) {
404 // get list of threads
405 buf_used
= tai
.ptinfo
.pti_threadnum
* sizeof(uint64_t);
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);
414 if (info
->threads
== NULL
) {
415 info
->threads
= malloc(info
->thr_size
);
417 info
->threads
= reallocf(info
->threads
, info
->thr_size
);
419 if (info
->threads
== NULL
) {
424 buf_used
= proc_pidinfo(pid
, PROC_PIDLISTTHREADS
, 0, info
->threads
, (int)info
->thr_size
);
429 if ((buf_used
+ sizeof(uint64_t)) >= info
->thr_size
) {
430 // if not enough room in the buffer for an extra thread
431 buf_used
= (int)(info
->thr_size
+ sizeof(uint64_t));
435 info
->thr_count
= buf_used
/ sizeof(uint64_t);
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
;
444 buf_used
= proc_pidinfo(pid
, PROC_PIDTHREADPATHINFO
, thr
, &tpi
, sizeof(tpi
));
446 if ((errno
== ESRCH
) || (errno
== EINVAL
)) {
447 // if the process or thread is gone
450 } else if (buf_used
< sizeof(tai
)) {
451 // if we didn't get enough information
455 status
= check_file(info
, &tpi
.pvip
.vip_vi
.vi_stat
);
468 * check_process_phase1
469 * check [process] process-wide current working and root directories
470 * check [process] open file descriptors
471 * check [process] per-thread current working and root directories
479 check_process_phase1(fdOpenInfoRef info
, int pid
)
483 // check root and current working directory
484 status
= check_process_vnodes(info
, pid
);
490 // check open file descriptors
491 status
= check_process_fds(info
, pid
);
497 // check per-thread working directories
498 status
= check_process_threads(info
, pid
);
508 * check_process_phase2
509 * check [process] text (memory)
517 check_process_phase2(fdOpenInfoRef info
, int pid
)
521 // check process text (memory)
522 status
= check_process_text(info
, pid
);
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.
546 proc_listpidspath(uint32_t type
,
554 int *buf_next
= (int *)buffer
;
559 if (buffer
== NULL
) {
560 // if this is a sizing request
561 return proc_listpids(type
, typeinfo
, NULL
, 0);
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
572 info
= check_init(path
, pathflags
);
577 // get list of processes
578 buf_used
= proc_listpids(type
, typeinfo
, NULL
, 0);
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);
590 if (info
->pids
== NULL
) {
591 info
->pids
= malloc(info
->pids_size
);
593 info
->pids
= reallocf(info
->pids
, info
->pids_size
);
595 if (info
->pids
== NULL
) {
600 buf_used
= proc_listpids(type
, typeinfo
, info
->pids
, (int)info
->pids_size
);
605 if ((buf_used
+ sizeof(int)) >= info
->pids_size
) {
606 // if not enough room in the buffer for an extra pid
607 buf_used
= (int)(info
->pids_size
+ sizeof(int));
611 info
->pids_count
= buf_used
/ sizeof(int);
615 // iterate through each process
617 for (i
= info
->pids_count
- 1; i
>= 0; i
--) {
626 pstatus
= check_process_phase1(info
, pid
);
633 buf_used
+= sizeof(int);
635 if (buf_used
>= buffersize
) {
636 // if we have filled the buffer
641 if (buf_used
>= buffersize
) {
642 // if we have filled the buffer
647 // do a more expensive search if we still have buffer space
648 for (i
= info
->pids_count
- 1; i
>= 0; i
--) {
657 pstatus
= check_process_phase2(info
, pid
);
664 buf_used
+= sizeof(int);
666 if (buf_used
>= buffersize
) {
667 // if we have filled the buffer