]> git.saurik.com Git - apple/xnu.git/blob - libsyscall/wrappers/libproc/proc_listpidspath.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / libsyscall / wrappers / libproc / proc_listpidspath.c
1 /*
2 * Copyright (c) 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
12 *
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.
20 *
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
33 typedef struct {
34 // process IDs
35 int *pids;
36 int pids_count;
37 size_t pids_size;
38
39 // threads
40 uint64_t *threads;
41 int thr_count;
42 size_t thr_size;
43
44 // open file descriptors
45 struct proc_fdinfo *fds;
46 int fds_count;
47 size_t fds_size;
48
49 // file/volume of interest
50 struct stat match_stat;
51
52 // flags
53 uint32_t flags;
54 } fdOpenInfo, *fdOpenInfoRef;
55
56
57 /*
58 * check_init
59 */
60 static fdOpenInfoRef
61 check_init(const char *path, uint32_t flags)
62 {
63 fdOpenInfoRef info;
64 int status;
65
66 info = malloc(sizeof(*info));
67 if (!info) {
68 return NULL;
69 }
70
71 info->pids = NULL;
72 info->pids_count = 0;
73 info->pids_size = 0;
74
75 info->threads = NULL;
76 info->thr_count = 0;
77 info->thr_size = 0;
78
79 info->fds = NULL;
80 info->fds_count = 0;
81 info->fds_size = 0;
82
83 status = stat(path, &info->match_stat);
84 if (status == -1) {
85 goto fail;
86 }
87
88 info->flags = flags;
89
90 return info;
91
92 fail:
93
94 free(info);
95 return NULL;
96 }
97
98
99 /*
100 * check_free
101 */
102 static void
103 check_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 */
132 static int
133 check_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 */
165 static int
166 check_process_vnodes(fdOpenInfoRef info, int pid)
167 {
168 int buf_used;
169 int status;
170 struct proc_vnodepathinfo vpi;
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 */
211 static int
212 check_process_text(fdOpenInfoRef info, int pid)
213 {
214 int status;
215 int buf_used;
216 struct proc_regionwithpathinfo rwpi;
217
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));
221 if (buf_used <= 0) {
222 if ((errno == ESRCH) || (errno == EINVAL)) {
223 // if no more text information is available for this process.
224 return 0;
225 }
226 return -1;
227 } else if (buf_used < sizeof(rwpi)) {
228 // if we didn't get enough information
229 return -1;
230 }
231
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 }
237 } else {
238 uint64_t a = 0;
239
240 while (1) { // for all memory regions
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 }
253
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 }
259
260 a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
261 }
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 */
277 static int
278 check_process_fds(fdOpenInfoRef info, int pid)
279 {
280 int buf_used;
281 int i;
282 int status;
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
307 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info->fds, (int)info->fds_size);
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
314 buf_used = (int)(info->fds_size + sizeof(struct proc_fdinfo));
315 continue;
316 }
317
318 info->fds_count = (int)(buf_used / sizeof(struct proc_fdinfo));
319 break;
320 }
321
322 // iterate through each file descriptor
323 for (i = 0; i < info->fds_count; i++) {
324 struct proc_fdinfo *fdp;
325
326 fdp = &info->fds[i];
327 switch (fdp->proc_fdtype) {
328 case PROX_FDTYPE_VNODE: {
329 int buf_used;
330 struct vnode_fdinfo vi;
331
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 */
341 continue;
342 }
343 return -1;
344 } else if (buf_used < sizeof(vi)) {
345 // if we didn't get enough information
346 return -1;
347 }
348
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;
359 }
360 break;
361 }
362 default:
363 break;
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 */
380 static int
381 check_process_threads(fdOpenInfoRef info, int pid)
382 {
383 int buf_used;
384 int status;
385 struct proc_taskallinfo tai;
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) {
401 int i;
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
423 buf_used = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, info->threads, (int)info->thr_size);
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
430 buf_used = (int)(info->thr_size + sizeof(uint64_t));
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++) {
440 uint64_t thr = info->threads[i];
441 struct proc_threadwithpathinfo tpi;
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 /*
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
471 *
472 * in : pid
473 * out : -1 if error
474 * 0 if no match
475 * 1 if match
476 */
477 static int
478 check_process_phase1(fdOpenInfoRef info, int pid)
479 {
480 int status;
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
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
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 */
515 static int
516 check_process_phase2(fdOpenInfoRef info, int pid)
517 {
518 int status;
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 }
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 */
544 int
545 proc_listpidspath(uint32_t type,
546 uint32_t typeinfo,
547 const char *path,
548 uint32_t pathflags,
549 void *buffer,
550 int buffersize)
551 {
552 int buf_used;
553 int *buf_next = (int *)buffer;
554 int i;
555 fdOpenInfoRef info;
556 int status = -1;
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
599 buf_used = proc_listpids(type, typeinfo, info->pids, (int)info->pids_size);
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
606 buf_used = (int)(info->pids_size + sizeof(int));
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--) {
617 int pid;
618 int pstatus;
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--) {
648 int pid;
649 int pstatus;
650
651 pid = info->pids[i];
652 if (pid == 0) {
653 continue;
654 }
655
656 pstatus = check_process_phase2(info, pid);
657 if (pstatus != 1) {
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
673 done:
674
675 // cleanup
676 check_free(info);
677
678 return status;
679 }