]> git.saurik.com Git - apple/xnu.git/blob - libsyscall/wrappers/libproc/proc_listpidspath.c
xnu-2422.115.4.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
55 } fdOpenInfo, *fdOpenInfoRef;
56
57
58 /*
59 * check_init
60 */
61 static fdOpenInfoRef
62 check_init(const char *path, uint32_t flags)
63 {
64 fdOpenInfoRef info;
65 int status;
66
67 info = malloc(sizeof(*info));
68 if (!info)
69 return NULL;
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 uint64_t a = 0;
215 int status;
216
217 while (1) { // for all memory regions
218 int buf_used;
219 struct proc_regionwithpathinfo rwpi;
220
221 // processing next address
222 buf_used = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, a, &rwpi, sizeof(rwpi));
223 if (buf_used <= 0) {
224 if ((errno == ESRCH) || (errno == EINVAL)) {
225 // if no more text information is available for this process.
226 break;
227 }
228 return -1;
229 } else if (buf_used < sizeof(rwpi)) {
230 // if we didn't get enough information
231 return -1;
232 }
233
234 status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
235 if (status != 0) {
236 // if error or match
237 return status;
238 }
239
240 a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
241 }
242
243 return 0;
244 }
245
246
247 /*
248 * check_process_fds
249 * check [process] open file descriptors
250 *
251 * in : pid
252 * out : -1 if error
253 * 0 if no match
254 * 1 if match
255 */
256 static int
257 check_process_fds(fdOpenInfoRef info, int pid)
258 {
259 int buf_used;
260 int i;
261 int status;
262
263 // get list of open file descriptors
264 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
265 if (buf_used <= 0) {
266 return -1;
267 }
268
269 while (1) {
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);
274 }
275
276 if (info->fds == NULL) {
277 info->fds = malloc(info->fds_size);
278 } else {
279 info->fds = reallocf(info->fds, info->fds_size);
280 }
281 if (info->fds == NULL) {
282 return -1;
283 }
284 }
285
286 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info->fds, info->fds_size);
287 if (buf_used <= 0) {
288 return -1;
289 }
290
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);
294 continue;
295 }
296
297 info->fds_count = buf_used / sizeof(struct proc_fdinfo);
298 break;
299 }
300
301 // iterate through each file descriptor
302 for (i = 0; i < info->fds_count; i++) {
303 struct proc_fdinfo *fdp;
304
305 fdp = &info->fds[i];
306 switch (fdp->proc_fdtype) {
307 case PROX_FDTYPE_VNODE : {
308 int buf_used;
309 struct vnode_fdinfo vi;
310
311 buf_used = proc_pidfdinfo(pid, fdp->proc_fd, PROC_PIDFDVNODEINFO, &vi, sizeof(vi));
312 if (buf_used <= 0) {
313 if (errno == ENOENT) {
314 /*
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.
319 */
320 continue;
321 }
322 return -1;
323 } else if (buf_used < sizeof(vi)) {
324 // if we didn't get enough information
325 return -1;
326 }
327
328 if ((info->flags & PROC_LISTPIDSPATH_EXCLUDE_EVTONLY) &&
329 (vi.pfi.fi_openflags & O_EVTONLY)) {
330 // if this file should be excluded
331 continue;
332 }
333
334 status = check_file(info, &vi.pvi.vi_stat);
335 if (status != 0) {
336 // if error or match
337 return status;
338 }
339 break;
340 }
341 default :
342 break;
343 }
344 }
345
346 return 0;
347 }
348
349
350 /*
351 * check_process_threads
352 * check [process] thread working directories
353 *
354 * in : pid
355 * out : -1 if error
356 * 0 if no match
357 * 1 if match
358 */
359 static int
360 check_process_threads(fdOpenInfoRef info, int pid)
361 {
362 int buf_used;
363 int status;
364 struct proc_taskallinfo tai;
365
366 buf_used = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &tai, sizeof(tai));
367 if (buf_used <= 0) {
368 if (errno == ESRCH) {
369 // if the process is gone
370 return 0;
371 }
372 return -1;
373 } else if (buf_used < sizeof(tai)) {
374 // if we didn't get enough information
375 return -1;
376 }
377
378 // check thread info
379 if (tai.pbsd.pbi_flags & PROC_FLAG_THCWD) {
380 int i;
381
382 // get list of threads
383 buf_used = tai.ptinfo.pti_threadnum * sizeof(uint64_t);
384
385 while (1) {
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);
390 }
391
392 if (info->threads == NULL) {
393 info->threads = malloc(info->thr_size);
394 } else {
395 info->threads = reallocf(info->threads, info->thr_size);
396 }
397 if (info->threads == NULL) {
398 return -1;
399 }
400 }
401
402 buf_used = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, info->threads, info->thr_size);
403 if (buf_used <= 0) {
404 return -1;
405 }
406
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);
410 continue;
411 }
412
413 info->thr_count = buf_used / sizeof(uint64_t);
414 break;
415 }
416
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;
421
422 buf_used = proc_pidinfo(pid, PROC_PIDTHREADPATHINFO, thr, &tpi, sizeof(tpi));
423 if (buf_used <= 0) {
424 if ((errno == ESRCH) || (errno == EINVAL)) {
425 // if the process or thread is gone
426 continue;
427 }
428 } else if (buf_used < sizeof(tai)) {
429 // if we didn't get enough information
430 return -1;
431 }
432
433 status = check_file(info, &tpi.pvip.vip_vi.vi_stat);
434 if (status != 0) {
435 // if error or match
436 return status;
437 }
438 }
439 }
440
441 return 0;
442 }
443
444
445 /*
446 * check_process
447 * check [process] current working and root directories
448 * check [process] text (memory)
449 * check [process] open file descriptors
450 *
451 * in : pid
452 * out : -1 if error
453 * 0 if no match
454 * 1 if match
455 */
456 static int
457 check_process(fdOpenInfoRef info, int pid)
458 {
459 int status;
460
461 // check root and current working directory
462 status = check_process_vnodes(info, pid);
463 if (status != 0) {
464 // if error or match
465 return status;
466 }
467
468 // check process text (memory)
469 status = check_process_text(info, pid);
470 if (status != 0) {
471 // if error or match
472 return status;
473 }
474
475 // check open file descriptors
476 status = check_process_fds(info, pid);
477 if (status != 0) {
478 // if error or match
479 return status;
480 }
481
482 // check per-thread working directories
483 status = check_process_threads(info, pid);
484 if (status != 0) {
485 // if error or match
486 return status;
487 }
488
489 return 0;
490 }
491
492
493 /*
494 * proc_listpidspath
495 *
496 * in : type
497 * : typeinfo
498 * : path
499 * : pathflags
500 * : buffer
501 * : buffersize
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.
506 */
507 int
508 proc_listpidspath(uint32_t type,
509 uint32_t typeinfo,
510 const char *path,
511 uint32_t pathflags,
512 void *buffer,
513 int buffersize)
514 {
515 int buf_used;
516 int *buf_next = (int *)buffer;
517 int i;
518 fdOpenInfoRef info;
519 int status = -1;
520
521 if (buffer == NULL) {
522 // if this is a sizing request
523 return proc_listpids(type, typeinfo, NULL, 0);
524 }
525
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
529 errno = ENOMEM;
530 return -1;
531 }
532
533 // init
534 info = check_init(path, pathflags);
535 if (info == NULL) {
536 return -1;
537 }
538
539 // get list of processes
540 buf_used = proc_listpids(type, typeinfo, NULL, 0);
541 if (buf_used <= 0) {
542 goto done;
543 }
544
545 while (1) {
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);
550 }
551
552 if (info->pids == NULL) {
553 info->pids = malloc(info->pids_size);
554 } else {
555 info->pids = reallocf(info->pids, info->pids_size);
556 }
557 if (info->pids == NULL) {
558 goto done;
559 }
560 }
561
562 buf_used = proc_listpids(type, typeinfo, info->pids, info->pids_size);
563 if (buf_used <= 0) {
564 goto done;
565 }
566
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);
570 continue;
571 }
572
573 info->pids_count = buf_used / sizeof(int);
574 break;
575 }
576
577 // iterate through each process
578 buf_used = 0;
579 for (i = info->pids_count - 1; i >= 0; i--) {
580 int pid;
581 int status;
582
583 pid = info->pids[i];
584 if (pid == 0) {
585 continue;
586 }
587
588 status = check_process(info, pid);
589 if (status != 1) {
590 // if not a match
591 continue;
592 }
593
594 *buf_next++ = pid;
595 buf_used += sizeof(int);
596
597 if (buf_used >= buffersize) {
598 // if we have filled the buffer
599 break;
600 }
601 }
602
603 status = buf_used;
604
605 done :
606
607 // cleanup
608 check_free(info);
609
610 return status;
611 }