]> git.saurik.com Git - apple/libc.git/blob - darwin/proc_listpidspath.c
Libc-583.tar.gz
[apple/libc.git] / darwin / 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 <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/fcntl.h>
28 #include <sys/errno.h>
29 #include <sys/param.h>
30 #include <sys/mount.h>
31 #include <libproc.h>
32
33
34 typedef struct {
35 // process IDs
36 int *pids;
37 int pids_count;
38 size_t pids_size;
39
40 // threads
41 uint64_t *threads;
42 int thr_count;
43 size_t thr_size;
44
45 // open file descriptors
46 struct proc_fdinfo *fds;
47 int fds_count;
48 size_t fds_size;
49
50 // file/volume of interest
51 struct stat match_stat;
52
53 // flags
54 uint32_t flags;
55
56 } fdOpenInfo, *fdOpenInfoRef;
57
58
59 /*
60 * check_init
61 */
62 static fdOpenInfoRef
63 check_init(const char *path, uint32_t flags)
64 {
65 fdOpenInfoRef info;
66 int status;
67
68 info = malloc(sizeof(*info));
69 if (!info)
70 return NULL;
71
72 info->pids = NULL;
73 info->pids_count = 0;
74 info->pids_size = 0;
75
76 info->threads = NULL;
77 info->thr_count = 0;
78 info->thr_size = 0;
79
80 info->fds = NULL;
81 info->fds_count = 0;
82 info->fds_size = 0;
83
84 status = stat(path, &info->match_stat);
85 if (status == -1) {
86 goto fail;
87 }
88
89 info->flags = flags;
90
91 return info;
92
93 fail :
94
95 free(info);
96 return NULL;
97 }
98
99
100 /*
101 * check_free
102 */
103 static void
104 check_free(fdOpenInfoRef info)
105 {
106 if (info->pids != NULL) {
107 free(info->pids);
108 }
109
110 if (info->threads != NULL) {
111 free(info->threads);
112 }
113
114 if (info->fds != NULL) {
115 free(info->fds);
116 }
117
118 free(info);
119
120 return;
121 }
122
123
124 /*
125 * check_file
126 * check if a process vnode is of interest
127 *
128 * in : vnode stat(2)
129 * out : -1 if error
130 * 0 if no match
131 * 1 if match
132 */
133 static int
134 check_file(fdOpenInfoRef info, struct vinfo_stat *sb)
135 {
136 if (sb->vst_dev == 0) {
137 // if no info
138 return 0;
139 }
140
141 if (sb->vst_dev != info->match_stat.st_dev) {
142 // if not the requested filesystem
143 return 0;
144 }
145
146 if (!(info->flags & PROC_LISTPIDSPATH_PATH_IS_VOLUME) &&
147 (sb->vst_ino != info->match_stat.st_ino)) {
148 // if not the requested file
149 return 0;
150 }
151
152 return 1;
153 }
154
155
156 /*
157 * check_process_vnodes
158 * check [process] current working directory
159 * check [process] root directory
160 *
161 * in : pid
162 * out : -1 if error
163 * 0 if no match
164 * 1 if match
165 */
166 static int
167 check_process_vnodes(fdOpenInfoRef info, int pid)
168 {
169 int buf_used;
170 int status;
171 struct proc_vnodepathinfo vpi;
172
173 buf_used = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
174 if (buf_used <= 0) {
175 if (errno == ESRCH) {
176 // if the process is gone
177 return 0;
178 }
179 return -1;
180 } else if (buf_used < sizeof(vpi)) {
181 // if we didn't get enough information
182 return -1;
183 }
184
185 // processing current working directory
186 status = check_file(info, &vpi.pvi_cdir.vip_vi.vi_stat);
187 if (status != 0) {
188 // if error or match
189 return status;
190 }
191
192 // processing root directory
193 status = check_file(info, &vpi.pvi_rdir.vip_vi.vi_stat);
194 if (status != 0) {
195 // if error or match
196 return status;
197 }
198
199 return 0;
200 }
201
202
203 /*
204 * check_process_text
205 * check [process] text (memory)
206 *
207 * in : pid
208 * out : -1 if error
209 * 0 if no match
210 * 1 if match
211 */
212 static int
213 check_process_text(fdOpenInfoRef info, int pid)
214 {
215 uint64_t a = 0;
216 int status;
217
218 while (1) { // for all memory regions
219 int buf_used;
220 struct proc_regionwithpathinfo rwpi;
221
222 // processing next address
223 buf_used = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, a, &rwpi, sizeof(rwpi));
224 if (buf_used <= 0) {
225 if ((errno == ESRCH) || (errno == EINVAL)) {
226 // if no more text information is available for this process.
227 break;
228 }
229 return -1;
230 } else if (buf_used < sizeof(rwpi)) {
231 // if we didn't get enough information
232 return -1;
233 }
234
235 status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
236 if (status != 0) {
237 // if error or match
238 return status;
239 }
240
241 a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
242 }
243
244 return 0;
245 }
246
247
248 /*
249 * check_process_fds
250 * check [process] open file descriptors
251 *
252 * in : pid
253 * out : -1 if error
254 * 0 if no match
255 * 1 if match
256 */
257 static int
258 check_process_fds(fdOpenInfoRef info, int pid)
259 {
260 int buf_used;
261 int i;
262 int status;
263
264 // get list of open file descriptors
265 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
266 if (buf_used <= 0) {
267 return -1;
268 }
269
270 while (1) {
271 if (buf_used > info->fds_size) {
272 // if we need to allocate [more] space
273 while (buf_used > info->fds_size) {
274 info->fds_size += (sizeof(struct proc_fdinfo) * 32);
275 }
276
277 if (info->fds == NULL) {
278 info->fds = malloc(info->fds_size);
279 } else {
280 info->fds = reallocf(info->fds, info->fds_size);
281 }
282 if (info->fds == NULL) {
283 return -1;
284 }
285 }
286
287 buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info->fds, info->fds_size);
288 if (buf_used <= 0) {
289 return -1;
290 }
291
292 if ((buf_used + sizeof(struct proc_fdinfo)) >= info->fds_size) {
293 // if not enough room in the buffer for an extra fd
294 buf_used = info->fds_size + sizeof(struct proc_fdinfo);
295 continue;
296 }
297
298 info->fds_count = buf_used / sizeof(struct proc_fdinfo);
299 break;
300 }
301
302 // iterate through each file descriptor
303 for (i = 0; i < info->fds_count; i++) {
304 struct proc_fdinfo *fdp;
305
306 fdp = &info->fds[i];
307 switch (fdp->proc_fdtype) {
308 case PROX_FDTYPE_VNODE : {
309 int buf_used;
310 struct vnode_fdinfo vi;
311
312 buf_used = proc_pidfdinfo(pid, fdp->proc_fd, PROC_PIDFDVNODEINFO, &vi, sizeof(vi));
313 if (buf_used <= 0) {
314 if (errno == ENOENT) {
315 /*
316 * The file descriptor's vnode may have been revoked. This is a
317 * bit of a hack, since an ENOENT error might not always mean the
318 * descriptor's vnode has been revoked. As the libproc API
319 * matures, this code may need to be revisited.
320 */
321 continue;
322 }
323 return -1;
324 } else if (buf_used < sizeof(vi)) {
325 // if we didn't get enough information
326 return -1;
327 }
328
329 if ((info->flags & PROC_LISTPIDSPATH_EXCLUDE_EVTONLY) &&
330 (vi.pfi.fi_openflags & O_EVTONLY)) {
331 // if this file should be excluded
332 continue;
333 }
334
335 status = check_file(info, &vi.pvi.vi_stat);
336 if (status != 0) {
337 // if error or match
338 return status;
339 }
340 break;
341 }
342 default :
343 break;
344 }
345 }
346
347 return 0;
348 }
349
350
351 /*
352 * check_process_threads
353 * check [process] thread working directories
354 *
355 * in : pid
356 * out : -1 if error
357 * 0 if no match
358 * 1 if match
359 */
360 static int
361 check_process_threads(fdOpenInfoRef info, int pid)
362 {
363 int buf_used;
364 int status;
365 struct proc_taskallinfo tai;
366
367 buf_used = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &tai, sizeof(tai));
368 if (buf_used <= 0) {
369 if (errno == ESRCH) {
370 // if the process is gone
371 return 0;
372 }
373 return -1;
374 } else if (buf_used < sizeof(tai)) {
375 // if we didn't get enough information
376 return -1;
377 }
378
379 // check thread info
380 if (tai.pbsd.pbi_flags & PROC_FLAG_THCWD) {
381 int i;
382
383 // get list of threads
384 buf_used = tai.ptinfo.pti_threadnum * sizeof(uint64_t);
385
386 while (1) {
387 if (buf_used > info->thr_size) {
388 // if we need to allocate [more] space
389 while (buf_used > info->thr_size) {
390 info->thr_size += (sizeof(uint64_t) * 32);
391 }
392
393 if (info->threads == NULL) {
394 info->threads = malloc(info->thr_size);
395 } else {
396 info->threads = reallocf(info->threads, info->thr_size);
397 }
398 if (info->threads == NULL) {
399 return -1;
400 }
401 }
402
403 buf_used = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, info->threads, info->thr_size);
404 if (buf_used <= 0) {
405 return -1;
406 }
407
408 if ((buf_used + sizeof(uint64_t)) >= info->thr_size) {
409 // if not enough room in the buffer for an extra thread
410 buf_used = info->thr_size + sizeof(uint64_t);
411 continue;
412 }
413
414 info->thr_count = buf_used / sizeof(uint64_t);
415 break;
416 }
417
418 // iterate through each thread
419 for (i = 0; i < info->thr_count; i++) {
420 uint64_t thr = info->threads[i];
421 struct proc_threadwithpathinfo tpi;
422
423 buf_used = proc_pidinfo(pid, PROC_PIDTHREADPATHINFO, thr, &tpi, sizeof(tpi));
424 if (buf_used <= 0) {
425 if ((errno == ESRCH) || (errno == EINVAL)) {
426 // if the process or thread is gone
427 continue;
428 }
429 } else if (buf_used < sizeof(tai)) {
430 // if we didn't get enough information
431 return -1;
432 }
433
434 status = check_file(info, &tpi.pvip.vip_vi.vi_stat);
435 if (status != 0) {
436 // if error or match
437 return status;
438 }
439 }
440 }
441
442 return 0;
443 }
444
445
446 /*
447 * check_process
448 * check [process] current working and root directories
449 * check [process] text (memory)
450 * check [process] open file descriptors
451 *
452 * in : pid
453 * out : -1 if error
454 * 0 if no match
455 * 1 if match
456 */
457 static int
458 check_process(fdOpenInfoRef info, int pid)
459 {
460 int status;
461
462 // check root and current working directory
463 status = check_process_vnodes(info, pid);
464 if (status != 0) {
465 // if error or match
466 return status;
467 }
468
469 // check process text (memory)
470 status = check_process_text(info, pid);
471 if (status != 0) {
472 // if error or match
473 return status;
474 }
475
476 // check open file descriptors
477 status = check_process_fds(info, pid);
478 if (status != 0) {
479 // if error or match
480 return status;
481 }
482
483 // check per-thread working directories
484 status = check_process_threads(info, pid);
485 if (status != 0) {
486 // if error or match
487 return status;
488 }
489
490 return 0;
491 }
492
493
494 /*
495 * proc_listpidspath
496 *
497 * in : type
498 * : typeinfo
499 * : path
500 * : pathflags
501 * : buffer
502 * : buffersize
503 * out : buffer filled with process IDs that have open file
504 * references that match the specified path or volume;
505 * return value is the bytes of the returned buffer
506 * that contains valid information.
507 */
508 int
509 proc_listpidspath(uint32_t type,
510 uint32_t typeinfo,
511 const char *path,
512 uint32_t pathflags,
513 void *buffer,
514 int buffersize)
515 {
516 int buf_used;
517 int *buf_next = (int *)buffer;
518 int i;
519 fdOpenInfoRef info;
520 int status = -1;
521
522 if (buffer == NULL) {
523 // if this is a sizing request
524 return proc_listpids(type, typeinfo, NULL, 0);
525 }
526
527 buffersize -= (buffersize % sizeof(int)); // make whole number of ints
528 if (buffersize < sizeof(int)) {
529 // if we can't even return a single PID
530 errno = ENOMEM;
531 return -1;
532 }
533
534 // init
535 info = check_init(path, pathflags);
536 if (info == NULL) {
537 return -1;
538 }
539
540 // get list of processes
541 buf_used = proc_listpids(type, typeinfo, NULL, 0);
542 if (buf_used <= 0) {
543 goto done;
544 }
545
546 while (1) {
547 if (buf_used > info->pids_size) {
548 // if we need to allocate [more] space
549 while (buf_used > info->pids_size) {
550 info->pids_size += (sizeof(int) * 32);
551 }
552
553 if (info->pids == NULL) {
554 info->pids = malloc(info->pids_size);
555 } else {
556 info->pids = reallocf(info->pids, info->pids_size);
557 }
558 if (info->pids == NULL) {
559 goto done;
560 }
561 }
562
563 buf_used = proc_listpids(type, typeinfo, info->pids, info->pids_size);
564 if (buf_used <= 0) {
565 goto done;
566 }
567
568 if ((buf_used + sizeof(int)) >= info->pids_size) {
569 // if not enough room in the buffer for an extra pid
570 buf_used = info->pids_size + sizeof(int);
571 continue;
572 }
573
574 info->pids_count = buf_used / sizeof(int);
575 break;
576 }
577
578 // iterate through each process
579 buf_used = 0;
580 for (i = info->pids_count - 1; i >= 0; i--) {
581 int pid;
582 int status;
583
584 pid = info->pids[i];
585 if (pid == 0) {
586 continue;
587 }
588
589 status = check_process(info, pid);
590 if (status != 1) {
591 // if not a match
592 continue;
593 }
594
595 *buf_next++ = pid;
596 buf_used += sizeof(int);
597
598 if (buf_used >= buffersize) {
599 // if we have filled the buffer
600 break;
601 }
602 }
603
604 status = buf_used;
605
606 done :
607
608 // cleanup
609 check_free(info);
610
611 return status;
612 }