]> git.saurik.com Git - apple/xnu.git/blame - libsyscall/wrappers/libproc/proc_listpidspath.c
xnu-7195.81.3.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@
0a7de745 5 *
39236c6e
A
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.
0a7de745 12 *
39236c6e
A
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.
0a7de745 20 *
39236c6e
A
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
0a7de745
A
35 int *pids;
36 int pids_count;
37 size_t pids_size;
39236c6e
A
38
39 // threads
0a7de745
A
40 uint64_t *threads;
41 int thr_count;
42 size_t thr_size;
39236c6e
A
43
44 // open file descriptors
0a7de745
A
45 struct proc_fdinfo *fds;
46 int fds_count;
47 size_t fds_size;
39236c6e
A
48
49 // file/volume of interest
0a7de745 50 struct stat match_stat;
39236c6e
A
51
52 // flags
0a7de745 53 uint32_t flags;
39236c6e
A
54} fdOpenInfo, *fdOpenInfoRef;
55
56
57/*
58 * check_init
59 */
60static fdOpenInfoRef
61check_init(const char *path, uint32_t flags)
62{
0a7de745
A
63 fdOpenInfoRef info;
64 int status;
39236c6e
A
65
66 info = malloc(sizeof(*info));
0a7de745 67 if (!info) {
39236c6e 68 return NULL;
0a7de745 69 }
39236c6e 70
0a7de745
A
71 info->pids = NULL;
72 info->pids_count = 0;
73 info->pids_size = 0;
39236c6e 74
0a7de745
A
75 info->threads = NULL;
76 info->thr_count = 0;
77 info->thr_size = 0;
39236c6e 78
0a7de745
A
79 info->fds = NULL;
80 info->fds_count = 0;
81 info->fds_size = 0;
39236c6e
A
82
83 status = stat(path, &info->match_stat);
84 if (status == -1) {
85 goto fail;
86 }
87
0a7de745 88 info->flags = flags;
39236c6e
A
89
90 return info;
91
0a7de745 92fail:
39236c6e
A
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{
0a7de745
A
168 int buf_used;
169 int status;
170 struct proc_vnodepathinfo vpi;
39236c6e
A
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{
0a7de745
A
214 int status;
215 int buf_used;
216 struct proc_regionwithpathinfo rwpi;
39236c6e 217
fe8ab488 218 if (info->flags & PROC_LISTPIDSPATH_PATH_IS_VOLUME) {
fe8ab488
A
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));
39236c6e
A
221 if (buf_used <= 0) {
222 if ((errno == ESRCH) || (errno == EINVAL)) {
223 // if no more text information is available for this process.
fe8ab488 224 return 0;
39236c6e
A
225 }
226 return -1;
227 } else if (buf_used < sizeof(rwpi)) {
228 // if we didn't get enough information
229 return -1;
230 }
0a7de745 231
39236c6e
A
232 status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
233 if (status != 0) {
234 // if error or match
235 return status;
236 }
fe8ab488 237 } else {
0a7de745
A
238 uint64_t a = 0;
239
240 while (1) { // for all memory regions
fe8ab488
A
241 // processing next address
242 buf_used = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO2, a, &rwpi, sizeof(rwpi));
243 if (buf_used <= 0) {
244 if ((errno == ESRCH) || (errno == EINVAL)) {
245 // if no more text information is available for this process.
246 break;
247 }
248 return -1;
249 } else if (buf_used < sizeof(rwpi)) {
250 // if we didn't get enough information
251 return -1;
252 }
0a7de745 253
fe8ab488
A
254 status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
255 if (status != 0) {
256 // if error or match
257 return status;
258 }
0a7de745 259
fe8ab488
A
260 a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
261 }
39236c6e
A
262 }
263
264 return 0;
265}
266
267
268/*
269 * check_process_fds
270 * check [process] open file descriptors
271 *
272 * in : pid
273 * out : -1 if error
274 * 0 if no match
275 * 1 if match
276 */
277static int
278check_process_fds(fdOpenInfoRef info, int pid)
279{
0a7de745
A
280 int buf_used;
281 int i;
282 int status;
39236c6e
A
283
284 // get list of open file descriptors
285 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
286 if (buf_used <= 0) {
287 return -1;
288 }
289
290 while (1) {
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);
295 }
296
297 if (info->fds == NULL) {
298 info->fds = malloc(info->fds_size);
299 } else {
300 info->fds = reallocf(info->fds, info->fds_size);
301 }
302 if (info->fds == NULL) {
303 return -1;
304 }
305 }
306
fe8ab488 307 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info->fds, (int)info->fds_size);
39236c6e
A
308 if (buf_used <= 0) {
309 return -1;
310 }
311
312 if ((buf_used + sizeof(struct proc_fdinfo)) >= info->fds_size) {
313 // if not enough room in the buffer for an extra fd
fe8ab488 314 buf_used = (int)(info->fds_size + sizeof(struct proc_fdinfo));
39236c6e
A
315 continue;
316 }
317
fe8ab488 318 info->fds_count = (int)(buf_used / sizeof(struct proc_fdinfo));
39236c6e
A
319 break;
320 }
321
322 // iterate through each file descriptor
323 for (i = 0; i < info->fds_count; i++) {
0a7de745 324 struct proc_fdinfo *fdp;
39236c6e
A
325
326 fdp = &info->fds[i];
327 switch (fdp->proc_fdtype) {
0a7de745
A
328 case PROX_FDTYPE_VNODE: {
329 int buf_used;
330 struct vnode_fdinfo vi;
39236c6e 331
0a7de745
A
332 buf_used = proc_pidfdinfo(pid, fdp->proc_fd, PROC_PIDFDVNODEINFO, &vi, sizeof(vi));
333 if (buf_used <= 0) {
334 if (errno == ENOENT) {
335 /*
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.
340 */
39236c6e
A
341 continue;
342 }
0a7de745
A
343 return -1;
344 } else if (buf_used < sizeof(vi)) {
345 // if we didn't get enough information
346 return -1;
347 }
39236c6e 348
0a7de745
A
349 if ((info->flags & PROC_LISTPIDSPATH_EXCLUDE_EVTONLY) &&
350 (vi.pfi.fi_openflags & O_EVTONLY)) {
351 // if this file should be excluded
352 continue;
353 }
354
355 status = check_file(info, &vi.pvi.vi_stat);
356 if (status != 0) {
357 // if error or match
358 return status;
39236c6e 359 }
0a7de745
A
360 break;
361 }
362 default:
363 break;
39236c6e
A
364 }
365 }
366
367 return 0;
368}
369
370
371/*
372 * check_process_threads
373 * check [process] thread working directories
374 *
375 * in : pid
376 * out : -1 if error
377 * 0 if no match
378 * 1 if match
379 */
380static int
381check_process_threads(fdOpenInfoRef info, int pid)
382{
0a7de745
A
383 int buf_used;
384 int status;
385 struct proc_taskallinfo tai;
39236c6e
A
386
387 buf_used = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &tai, sizeof(tai));
388 if (buf_used <= 0) {
389 if (errno == ESRCH) {
390 // if the process is gone
391 return 0;
392 }
393 return -1;
394 } else if (buf_used < sizeof(tai)) {
395 // if we didn't get enough information
396 return -1;
397 }
398
399 // check thread info
400 if (tai.pbsd.pbi_flags & PROC_FLAG_THCWD) {
0a7de745 401 int i;
39236c6e
A
402
403 // get list of threads
404 buf_used = tai.ptinfo.pti_threadnum * sizeof(uint64_t);
405
406 while (1) {
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);
411 }
412
413 if (info->threads == NULL) {
414 info->threads = malloc(info->thr_size);
415 } else {
416 info->threads = reallocf(info->threads, info->thr_size);
417 }
418 if (info->threads == NULL) {
419 return -1;
420 }
421 }
422
fe8ab488 423 buf_used = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, info->threads, (int)info->thr_size);
39236c6e
A
424 if (buf_used <= 0) {
425 return -1;
426 }
427
428 if ((buf_used + sizeof(uint64_t)) >= info->thr_size) {
429 // if not enough room in the buffer for an extra thread
fe8ab488 430 buf_used = (int)(info->thr_size + sizeof(uint64_t));
39236c6e
A
431 continue;
432 }
433
434 info->thr_count = buf_used / sizeof(uint64_t);
435 break;
436 }
437
438 // iterate through each thread
439 for (i = 0; i < info->thr_count; i++) {
0a7de745
A
440 uint64_t thr = info->threads[i];
441 struct proc_threadwithpathinfo tpi;
39236c6e
A
442
443 buf_used = proc_pidinfo(pid, PROC_PIDTHREADPATHINFO, thr, &tpi, sizeof(tpi));
444 if (buf_used <= 0) {
445 if ((errno == ESRCH) || (errno == EINVAL)) {
446 // if the process or thread is gone
447 continue;
448 }
449 } else if (buf_used < sizeof(tai)) {
450 // if we didn't get enough information
451 return -1;
452 }
453
454 status = check_file(info, &tpi.pvip.vip_vi.vi_stat);
455 if (status != 0) {
456 // if error or match
457 return status;
458 }
459 }
460 }
461
462 return 0;
463}
464
465
466/*
fe8ab488
A
467 * check_process_phase1
468 * check [process] process-wide current working and root directories
39236c6e 469 * check [process] open file descriptors
fe8ab488 470 * check [process] per-thread current working and root directories
39236c6e
A
471 *
472 * in : pid
473 * out : -1 if error
474 * 0 if no match
475 * 1 if match
476 */
477static int
fe8ab488 478check_process_phase1(fdOpenInfoRef info, int pid)
39236c6e 479{
0a7de745 480 int status;
39236c6e
A
481
482 // check root and current working directory
483 status = check_process_vnodes(info, pid);
484 if (status != 0) {
485 // if error or match
486 return status;
487 }
488
39236c6e
A
489 // check open file descriptors
490 status = check_process_fds(info, pid);
491 if (status != 0) {
492 // if error or match
493 return status;
494 }
495
496 // check per-thread working directories
497 status = check_process_threads(info, pid);
498 if (status != 0) {
499 // if error or match
500 return status;
501 }
502
503 return 0;
504}
505
fe8ab488
A
506/*
507 * check_process_phase2
508 * check [process] text (memory)
509 *
510 * in : pid
511 * out : -1 if error
512 * 0 if no match
513 * 1 if match
514 */
515static int
516check_process_phase2(fdOpenInfoRef info, int pid)
517{
0a7de745 518 int status;
fe8ab488
A
519
520 // check process text (memory)
521 status = check_process_text(info, pid);
522 if (status != 0) {
523 // if error or match
524 return status;
525 }
526
527 return 0;
528}
39236c6e
A
529
530/*
531 * proc_listpidspath
532 *
533 * in : type
534 * : typeinfo
535 * : path
536 * : pathflags
537 * : buffer
538 * : buffersize
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.
543 */
544int
0a7de745
A
545proc_listpidspath(uint32_t type,
546 uint32_t typeinfo,
547 const char *path,
548 uint32_t pathflags,
549 void *buffer,
550 int buffersize)
39236c6e 551{
0a7de745
A
552 int buf_used;
553 int *buf_next = (int *)buffer;
554 int i;
555 fdOpenInfoRef info;
556 int status = -1;
39236c6e
A
557
558 if (buffer == NULL) {
559 // if this is a sizing request
560 return proc_listpids(type, typeinfo, NULL, 0);
561 }
562
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
566 errno = ENOMEM;
567 return -1;
568 }
569
570 // init
571 info = check_init(path, pathflags);
572 if (info == NULL) {
573 return -1;
574 }
575
576 // get list of processes
577 buf_used = proc_listpids(type, typeinfo, NULL, 0);
578 if (buf_used <= 0) {
579 goto done;
580 }
581
582 while (1) {
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);
587 }
588
589 if (info->pids == NULL) {
590 info->pids = malloc(info->pids_size);
591 } else {
592 info->pids = reallocf(info->pids, info->pids_size);
593 }
594 if (info->pids == NULL) {
595 goto done;
596 }
597 }
598
fe8ab488 599 buf_used = proc_listpids(type, typeinfo, info->pids, (int)info->pids_size);
39236c6e
A
600 if (buf_used <= 0) {
601 goto done;
602 }
603
604 if ((buf_used + sizeof(int)) >= info->pids_size) {
605 // if not enough room in the buffer for an extra pid
fe8ab488 606 buf_used = (int)(info->pids_size + sizeof(int));
39236c6e
A
607 continue;
608 }
609
610 info->pids_count = buf_used / sizeof(int);
611 break;
612 }
613
614 // iterate through each process
615 buf_used = 0;
616 for (i = info->pids_count - 1; i >= 0; i--) {
0a7de745
A
617 int pid;
618 int pstatus;
fe8ab488
A
619
620 pid = info->pids[i];
621 if (pid == 0) {
622 continue;
623 }
624
625 pstatus = check_process_phase1(info, pid);
626 if (pstatus != 1) {
627 // if not a match
628 continue;
629 }
630
631 *buf_next++ = pid;
632 buf_used += sizeof(int);
633
634 if (buf_used >= buffersize) {
635 // if we have filled the buffer
636 break;
637 }
638 }
639
640 if (buf_used >= buffersize) {
641 // if we have filled the buffer
642 status = buf_used;
643 goto done;
644 }
645
646 // do a more expensive search if we still have buffer space
647 for (i = info->pids_count - 1; i >= 0; i--) {
0a7de745
A
648 int pid;
649 int pstatus;
39236c6e
A
650
651 pid = info->pids[i];
652 if (pid == 0) {
653 continue;
654 }
655
fe8ab488
A
656 pstatus = check_process_phase2(info, pid);
657 if (pstatus != 1) {
39236c6e
A
658 // if not a match
659 continue;
660 }
661
662 *buf_next++ = pid;
663 buf_used += sizeof(int);
664
665 if (buf_used >= buffersize) {
666 // if we have filled the buffer
667 break;
668 }
669 }
670
671 status = buf_used;
672
0a7de745 673done:
39236c6e
A
674
675 // cleanup
676 check_free(info);
677
678 return status;
679}