+++ /dev/null
-/*
- * Copyright (c) 2007, 2008 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/fcntl.h>
-#include <sys/errno.h>
-#include <sys/param.h>
-#include <sys/mount.h>
-#include <libproc.h>
-
-
-typedef struct {
- // process IDs
- int *pids;
- int pids_count;
- size_t pids_size;
-
- // threads
- uint64_t *threads;
- int thr_count;
- size_t thr_size;
-
- // open file descriptors
- struct proc_fdinfo *fds;
- int fds_count;
- size_t fds_size;
-
- // file/volume of interest
- struct stat match_stat;
-
- // flags
- uint32_t flags;
-
-} fdOpenInfo, *fdOpenInfoRef;
-
-
-/*
- * check_init
- */
-static fdOpenInfoRef
-check_init(const char *path, uint32_t flags)
-{
- fdOpenInfoRef info;
- int status;
-
- info = malloc(sizeof(*info));
- if (!info)
- return NULL;
-
- info->pids = NULL;
- info->pids_count = 0;
- info->pids_size = 0;
-
- info->threads = NULL;
- info->thr_count = 0;
- info->thr_size = 0;
-
- info->fds = NULL;
- info->fds_count = 0;
- info->fds_size = 0;
-
- status = stat(path, &info->match_stat);
- if (status == -1) {
- goto fail;
- }
-
- info->flags = flags;
-
- return info;
-
- fail :
-
- free(info);
- return NULL;
-}
-
-
-/*
- * check_free
- */
-static void
-check_free(fdOpenInfoRef info)
-{
- if (info->pids != NULL) {
- free(info->pids);
- }
-
- if (info->threads != NULL) {
- free(info->threads);
- }
-
- if (info->fds != NULL) {
- free(info->fds);
- }
-
- free(info);
-
- return;
-}
-
-
-/*
- * check_file
- * check if a process vnode is of interest
- *
- * in : vnode stat(2)
- * out : -1 if error
- * 0 if no match
- * 1 if match
- */
-static int
-check_file(fdOpenInfoRef info, struct vinfo_stat *sb)
-{
- if (sb->vst_dev == 0) {
- // if no info
- return 0;
- }
-
- if (sb->vst_dev != info->match_stat.st_dev) {
- // if not the requested filesystem
- return 0;
- }
-
- if (!(info->flags & PROC_LISTPIDSPATH_PATH_IS_VOLUME) &&
- (sb->vst_ino != info->match_stat.st_ino)) {
- // if not the requested file
- return 0;
- }
-
- return 1;
-}
-
-
-/*
- * check_process_vnodes
- * check [process] current working directory
- * check [process] root directory
- *
- * in : pid
- * out : -1 if error
- * 0 if no match
- * 1 if match
- */
-static int
-check_process_vnodes(fdOpenInfoRef info, int pid)
-{
- int buf_used;
- int status;
- struct proc_vnodepathinfo vpi;
-
- buf_used = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
- if (buf_used <= 0) {
- if (errno == ESRCH) {
- // if the process is gone
- return 0;
- }
- return -1;
- } else if (buf_used < sizeof(vpi)) {
- // if we didn't get enough information
- return -1;
- }
-
- // processing current working directory
- status = check_file(info, &vpi.pvi_cdir.vip_vi.vi_stat);
- if (status != 0) {
- // if error or match
- return status;
- }
-
- // processing root directory
- status = check_file(info, &vpi.pvi_rdir.vip_vi.vi_stat);
- if (status != 0) {
- // if error or match
- return status;
- }
-
- return 0;
-}
-
-
-/*
- * check_process_text
- * check [process] text (memory)
- *
- * in : pid
- * out : -1 if error
- * 0 if no match
- * 1 if match
- */
-static int
-check_process_text(fdOpenInfoRef info, int pid)
-{
- uint64_t a = 0;
- int status;
-
- while (1) { // for all memory regions
- int buf_used;
- struct proc_regionwithpathinfo rwpi;
-
- // processing next address
- buf_used = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, a, &rwpi, sizeof(rwpi));
- if (buf_used <= 0) {
- if ((errno == ESRCH) || (errno == EINVAL)) {
- // if no more text information is available for this process.
- break;
- }
- return -1;
- } else if (buf_used < sizeof(rwpi)) {
- // if we didn't get enough information
- return -1;
- }
-
- status = check_file(info, &rwpi.prp_vip.vip_vi.vi_stat);
- if (status != 0) {
- // if error or match
- return status;
- }
-
- a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
- }
-
- return 0;
-}
-
-
-/*
- * check_process_fds
- * check [process] open file descriptors
- *
- * in : pid
- * out : -1 if error
- * 0 if no match
- * 1 if match
- */
-static int
-check_process_fds(fdOpenInfoRef info, int pid)
-{
- int buf_used;
- int i;
- int status;
-
- // get list of open file descriptors
- buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
- if (buf_used <= 0) {
- return -1;
- }
-
- while (1) {
- if (buf_used > info->fds_size) {
- // if we need to allocate [more] space
- while (buf_used > info->fds_size) {
- info->fds_size += (sizeof(struct proc_fdinfo) * 32);
- }
-
- if (info->fds == NULL) {
- info->fds = malloc(info->fds_size);
- } else {
- info->fds = reallocf(info->fds, info->fds_size);
- }
- if (info->fds == NULL) {
- return -1;
- }
- }
-
- buf_used = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info->fds, info->fds_size);
- if (buf_used <= 0) {
- return -1;
- }
-
- if ((buf_used + sizeof(struct proc_fdinfo)) >= info->fds_size) {
- // if not enough room in the buffer for an extra fd
- buf_used = info->fds_size + sizeof(struct proc_fdinfo);
- continue;
- }
-
- info->fds_count = buf_used / sizeof(struct proc_fdinfo);
- break;
- }
-
- // iterate through each file descriptor
- for (i = 0; i < info->fds_count; i++) {
- struct proc_fdinfo *fdp;
-
- fdp = &info->fds[i];
- switch (fdp->proc_fdtype) {
- case PROX_FDTYPE_VNODE : {
- int buf_used;
- struct vnode_fdinfo vi;
-
- buf_used = proc_pidfdinfo(pid, fdp->proc_fd, PROC_PIDFDVNODEINFO, &vi, sizeof(vi));
- if (buf_used <= 0) {
- if (errno == ENOENT) {
- /*
- * The file descriptor's vnode may have been revoked. This is a
- * bit of a hack, since an ENOENT error might not always mean the
- * descriptor's vnode has been revoked. As the libproc API
- * matures, this code may need to be revisited.
- */
- continue;
- }
- return -1;
- } else if (buf_used < sizeof(vi)) {
- // if we didn't get enough information
- return -1;
- }
-
- if ((info->flags & PROC_LISTPIDSPATH_EXCLUDE_EVTONLY) &&
- (vi.pfi.fi_openflags & O_EVTONLY)) {
- // if this file should be excluded
- continue;
- }
-
- status = check_file(info, &vi.pvi.vi_stat);
- if (status != 0) {
- // if error or match
- return status;
- }
- break;
- }
- default :
- break;
- }
- }
-
- return 0;
-}
-
-
-/*
- * check_process_threads
- * check [process] thread working directories
- *
- * in : pid
- * out : -1 if error
- * 0 if no match
- * 1 if match
- */
-static int
-check_process_threads(fdOpenInfoRef info, int pid)
-{
- int buf_used;
- int status;
- struct proc_taskallinfo tai;
-
- buf_used = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &tai, sizeof(tai));
- if (buf_used <= 0) {
- if (errno == ESRCH) {
- // if the process is gone
- return 0;
- }
- return -1;
- } else if (buf_used < sizeof(tai)) {
- // if we didn't get enough information
- return -1;
- }
-
- // check thread info
- if (tai.pbsd.pbi_flags & PROC_FLAG_THCWD) {
- int i;
-
- // get list of threads
- buf_used = tai.ptinfo.pti_threadnum * sizeof(uint64_t);
-
- while (1) {
- if (buf_used > info->thr_size) {
- // if we need to allocate [more] space
- while (buf_used > info->thr_size) {
- info->thr_size += (sizeof(uint64_t) * 32);
- }
-
- if (info->threads == NULL) {
- info->threads = malloc(info->thr_size);
- } else {
- info->threads = reallocf(info->threads, info->thr_size);
- }
- if (info->threads == NULL) {
- return -1;
- }
- }
-
- buf_used = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, info->threads, info->thr_size);
- if (buf_used <= 0) {
- return -1;
- }
-
- if ((buf_used + sizeof(uint64_t)) >= info->thr_size) {
- // if not enough room in the buffer for an extra thread
- buf_used = info->thr_size + sizeof(uint64_t);
- continue;
- }
-
- info->thr_count = buf_used / sizeof(uint64_t);
- break;
- }
-
- // iterate through each thread
- for (i = 0; i < info->thr_count; i++) {
- uint64_t thr = info->threads[i];
- struct proc_threadwithpathinfo tpi;
-
- buf_used = proc_pidinfo(pid, PROC_PIDTHREADPATHINFO, thr, &tpi, sizeof(tpi));
- if (buf_used <= 0) {
- if ((errno == ESRCH) || (errno == EINVAL)) {
- // if the process or thread is gone
- continue;
- }
- } else if (buf_used < sizeof(tai)) {
- // if we didn't get enough information
- return -1;
- }
-
- status = check_file(info, &tpi.pvip.vip_vi.vi_stat);
- if (status != 0) {
- // if error or match
- return status;
- }
- }
- }
-
- return 0;
-}
-
-
-/*
- * check_process
- * check [process] current working and root directories
- * check [process] text (memory)
- * check [process] open file descriptors
- *
- * in : pid
- * out : -1 if error
- * 0 if no match
- * 1 if match
- */
-static int
-check_process(fdOpenInfoRef info, int pid)
-{
- int status;
-
- // check root and current working directory
- status = check_process_vnodes(info, pid);
- if (status != 0) {
- // if error or match
- return status;
- }
-
- // check process text (memory)
- status = check_process_text(info, pid);
- if (status != 0) {
- // if error or match
- return status;
- }
-
- // check open file descriptors
- status = check_process_fds(info, pid);
- if (status != 0) {
- // if error or match
- return status;
- }
-
- // check per-thread working directories
- status = check_process_threads(info, pid);
- if (status != 0) {
- // if error or match
- return status;
- }
-
- return 0;
-}
-
-
-/*
- * proc_listpidspath
- *
- * in : type
- * : typeinfo
- * : path
- * : pathflags
- * : buffer
- * : buffersize
- * out : buffer filled with process IDs that have open file
- * references that match the specified path or volume;
- * return value is the bytes of the returned buffer
- * that contains valid information.
- */
-int
-proc_listpidspath(uint32_t type,
- uint32_t typeinfo,
- const char *path,
- uint32_t pathflags,
- void *buffer,
- int buffersize)
-{
- int buf_used;
- int *buf_next = (int *)buffer;
- int i;
- fdOpenInfoRef info;
- int status = -1;
-
- if (buffer == NULL) {
- // if this is a sizing request
- return proc_listpids(type, typeinfo, NULL, 0);
- }
-
- buffersize -= (buffersize % sizeof(int)); // make whole number of ints
- if (buffersize < sizeof(int)) {
- // if we can't even return a single PID
- errno = ENOMEM;
- return -1;
- }
-
- // init
- info = check_init(path, pathflags);
- if (info == NULL) {
- return -1;
- }
-
- // get list of processes
- buf_used = proc_listpids(type, typeinfo, NULL, 0);
- if (buf_used <= 0) {
- goto done;
- }
-
- while (1) {
- if (buf_used > info->pids_size) {
- // if we need to allocate [more] space
- while (buf_used > info->pids_size) {
- info->pids_size += (sizeof(int) * 32);
- }
-
- if (info->pids == NULL) {
- info->pids = malloc(info->pids_size);
- } else {
- info->pids = reallocf(info->pids, info->pids_size);
- }
- if (info->pids == NULL) {
- goto done;
- }
- }
-
- buf_used = proc_listpids(type, typeinfo, info->pids, info->pids_size);
- if (buf_used <= 0) {
- goto done;
- }
-
- if ((buf_used + sizeof(int)) >= info->pids_size) {
- // if not enough room in the buffer for an extra pid
- buf_used = info->pids_size + sizeof(int);
- continue;
- }
-
- info->pids_count = buf_used / sizeof(int);
- break;
- }
-
- // iterate through each process
- buf_used = 0;
- for (i = info->pids_count - 1; i >= 0; i--) {
- int pid;
- int status;
-
- pid = info->pids[i];
- if (pid == 0) {
- continue;
- }
-
- status = check_process(info, pid);
- if (status != 1) {
- // if not a match
- continue;
- }
-
- *buf_next++ = pid;
- buf_used += sizeof(int);
-
- if (buf_used >= buffersize) {
- // if we have filled the buffer
- break;
- }
- }
-
- status = buf_used;
-
- done :
-
- // cleanup
- check_free(info);
-
- return status;
-}