ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Dump进程(任务)中的所有通信端口

2019-06-10 23:55:05  阅读:319  来源: 互联网

标签:Dump err stdout 端口 通信 fprintf stderr port mach


//
//  main.m
//  MachPortDump
//
//  Created by haidragon on 2019/6/10.
//  Copyright © 2019 haidragon. All rights reserved.
//
//
//#import <Foundation/Foundation.h>
//
//int main(int argc, const char * argv[]) {
//    @autoreleasepool {
//        // insert code here...
//        NSLog(@"Hello, World!");
//    }
//    return 0;
//}
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/sysctl.h>
#include <servers/bootstrap.h>
#include <mach/mach.h>
#pragma mark ***** Compatibility Note
#pragma mark ***** The Code

static const char *gProgramName;

static void PrintPortSetMembers(mach_port_t taskSendRight, mach_port_name_t portSetName)
// For a given Mach port set within a given task, print the members
// of the port set.
{
    kern_return_t           err;
    kern_return_t           junk;
    mach_port_name_array_t  memberNames;
    mach_msg_type_number_t  memberNamesCount;
    mach_msg_type_number_t  memberIndex;

    memberNames = NULL;

    // Get an array of members.

    err = mach_port_get_set_status(
                                   taskSendRight,
                                   portSetName,
                                   &memberNames,
                                   &memberNamesCount
                                   );

    // Iterate over the array, printing each one.  Note that we print 6 members to
    // a line and we start every line except the second with enough spaces to
    // account for the information that we print that's common to each type
    // of output.

    if (err == KERN_SUCCESS) {
        fprintf(stdout, "    ");
        for (memberIndex = 0; memberIndex < memberNamesCount; memberIndex++) {
            if ( (memberIndex != 0) && (memberIndex % 6) == 0) {
                // 6 columns of (8 characters plus space)
                // plus DNR column (3 chars) plus space
                fprintf(stdout, "\n%*s    ", (6 * (8 + 1)) + 3 + 1, "");
            }
            fprintf(stdout, "%#8x ", memberNames[memberIndex]);
        }
    } else {
        fprintf(stdout, "??? ");
    }

    // Clean up.

    if (memberNames != NULL) {
        junk = vm_deallocate(mach_task_self(), (vm_address_t) memberNames, memberNamesCount * sizeof(*memberNames));
        assert(junk == KERN_SUCCESS);
    }
}

static void PrintPortReceiveStatus(mach_port_t taskSendRight, mach_port_name_t receiveRight)
// Print information about the Mach receive right in the specified
// task.
{
    kern_return_t           err;
    mach_port_status_t      status;
    mach_msg_type_number_t  statusCount;

    // Get information about the the right.

    statusCount = MACH_PORT_RECEIVE_STATUS_COUNT;
    err = mach_port_get_attributes(
                                   taskSendRight,
                                   receiveRight,
                                   MACH_PORT_RECEIVE_STATUS,
                                   (mach_port_info_t) &status,
                                   &statusCount
                                   );
    assert( (err != KERN_SUCCESS) || (statusCount == MACH_PORT_RECEIVE_STATUS_COUNT) );

    // Print it, as a group of flags followed by 6 columns of numbers,
    // which are basically all counters.

    if (err == KERN_SUCCESS) {
        fprintf(
                stdout,
                "%c%c%c ",
                (status.mps_nsrequest ? 'N' : '-'),
                (status.mps_pdrequest ? 'P' : '-'),
                (status.mps_srights   ? 'S' : '-')
                );

        fprintf(
                stdout,
                "%8u %8u %8u %8u %8u %8u",
                status.mps_seqno,
                status.mps_mscount,
                status.mps_qlimit,
                status.mps_msgcount,
                status.mps_sorights,
                status.mps_pset
                );
        // The kernel always sets mps_flags to 0, so we don't both printing it.
        assert(status.mps_flags == 0);
    } else {
        fprintf(
                stdout,
                "??? %8s %8s %8s %8s %8s %8s",
                "???", "???", "???", "???", "???", "???"
                );
    }
}

static kern_return_t PrintProcessPortSpace(pid_t pid, bool verbose)
// Prints port rights owned by the specified process.
{
    kern_return_t           err;
    kern_return_t           junk;
    mach_port_t             taskSendRight;
    mach_port_name_array_t    rightNames;
    mach_msg_type_number_t    rightNamesCount = 0;
    mach_port_type_array_t    rightTypes;
    mach_msg_type_number_t    rightTypesCount = 0;
    unsigned int            i;

    taskSendRight = MACH_PORT_NULL;
    rightNames    = NULL;
    rightTypes    = NULL;

    // Get the task control port for the process.

    err = task_for_pid(mach_task_self(), pid, &taskSendRight);
    if (err != KERN_SUCCESS) {
        fprintf(stderr, "%s: Could not attach to process %lld (%#08x).\n", gProgramName, (long long) pid, err);
    }

    // Get a snapshot of the port name space for the task.

    if (err == KERN_SUCCESS) {
        err = mach_port_names(taskSendRight, &rightNames, &rightNamesCount, &rightTypes, &rightTypesCount);
    }
    if (err == KERN_SUCCESS) {
        if ( rightNamesCount != rightTypesCount ) {
            fprintf(stderr, "%s: Count mismatch (%u/%u)\n", gProgramName, rightNamesCount, rightTypesCount);
            err = KERN_FAILURE;
        }
    }

    // Print that snapshot.

    if (err == KERN_SUCCESS) {
        fprintf(stdout, "    Name     Send  Receive SendOnce  PortSet DeadName DNR");
        if (verbose) {
            fprintf(stdout, " flg    seqno  mscount   qlimit msgcount sorights     pset");
        }
        fprintf(stdout, "\n");
        fprintf(stdout, "    ----     ----  ------- --------  ------- -------- ---");
        if (verbose) {
            fprintf(stdout, " ---    -----  -------   ------ -------- --------     ----");
        }
        fprintf(stdout, "\n");

        // For each name, print a reference count of each type of right.  If running
        // verbose, print other information as well.

        for (i = 0; i < rightNamesCount; i++) {
            mach_port_right_t     right;

            // We print the right name in hex because it makes it easier to
            // see the index and generation fields.  See <mach/port.h> for
            // information about this.

            fprintf(stdout, "%#8x ", rightNames[i]);

            for (right = MACH_PORT_RIGHT_SEND; right <= MACH_PORT_RIGHT_DEAD_NAME; right++) {
                mach_port_urefs_t     refCount;

                // If the rightTypes for this name has the bit associated
                // with this type of right set (that is, if the name
                // references this type of right), get the name's reference
                // for this right and print it.  Otherwise just print an
                // empty string to keep the columns lined up.

                if (rightTypes[i] & MACH_PORT_TYPE(right)) {

                    err = mach_port_get_refs(taskSendRight, rightNames[i], right, &refCount);
                    if (err == KERN_SUCCESS) {
                        fprintf(stdout, "%8d ", refCount);
                    } else {
                        fprintf(stdout, "%8s ", "???");
                    }
                } else {
                    fprintf(stdout, "%8s ", "");
                }
            }
            if ( rightTypes[i] & MACH_PORT_TYPE_DNREQUEST ) {
                fprintf(stdout, "yes ");
            } else {
                fprintf(stdout, "    ");
            }

            if (verbose) {
                if (rightTypes[i] & MACH_PORT_TYPE_PORT_SET) {
                    PrintPortSetMembers(taskSendRight, rightNames[i]);
                } else if (rightTypes[i] & MACH_PORT_TYPE_RECEIVE) {
                    PrintPortReceiveStatus(taskSendRight, rightNames[i]);
                }
            }
            fprintf(stdout, "\n");
        }
    }

    // Clean up.

    if (rightNames != NULL) {
        junk = vm_deallocate(mach_task_self(), (vm_address_t) rightNames, rightNamesCount * sizeof(*rightNames));
        assert(junk == KERN_SUCCESS);
    }
    if (rightTypes != NULL) {
        junk = vm_deallocate(mach_task_self(), (vm_address_t) rightTypes, rightTypesCount * sizeof(*rightTypes));
        assert(junk == KERN_SUCCESS);
    }
    if (taskSendRight != MACH_PORT_NULL) {
        junk = mach_port_deallocate(mach_task_self(), taskSendRight);
        assert(junk == KERN_SUCCESS);
    }

    return err;
}

typedef struct kinfo_proc kinfo_proc;

static int GetBSDProcessList(kinfo_proc **procList, size_t *procCount)
// Returns a list of all BSD processes on the system.  This routine
// allocates the list and puts it in *procList and a count of the
// number of entries in *procCount.  You are responsible for freeing
// this list (use "free" from System framework).
// On success, the function returns 0.
// On error, the function returns a BSD errno value.
{
    int                 err;
    kinfo_proc *        result;
    bool                done;
    static const int    name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
    // Declaring name as const requires us to cast it when passing it to
    // sysctl because the prototype doesn't include the const modifier.
    size_t              length;

    assert( procList != NULL);
    assert(*procList == NULL);
    assert(procCount != NULL);

    *procCount = 0;

    // We start by calling sysctl with result == NULL and length == 0.
    // That will succeed, and set length to the appropriate length.
    // We then allocate a buffer of that size and call sysctl again
    // with that buffer.  If that succeeds, we're done.  If that fails
    // with ENOMEM, we have to throw away our buffer and loop.  Note
    // that the loop causes use to call sysctl with NULL again; this
    // is necessary because the ENOMEM failure case sets length to
    // the amount of data returned, not the amount of data that
    // could have been returned.

    result = NULL;
    done = false;
    do {
        assert(result == NULL);

        // Call sysctl with a NULL buffer.

        length = 0;
        err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
                     NULL, &length,
                     NULL, 0);
        if (err == -1) {
            err = errno;
        }

        // Allocate an appropriately sized buffer based on the results
        // from the previous call.

        if (err == 0) {
            result = malloc(length);
            if (result == NULL) {
                err = ENOMEM;
            }
        }

        // Call sysctl again with the new buffer.  If we get an ENOMEM
        // error, toss away our buffer and start again.

        if (err == 0) {
            err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
                         result, &length,
                         NULL, 0);
            if (err == -1) {
                err = errno;
            }
            if (err == 0) {
                done = true;
            } else if (err == ENOMEM) {
                assert(result != NULL);
                free(result);
                result = NULL;
                err = 0;
            }
        }
    } while (err == 0 && ! done);

    // Clean up and establish post conditions.

    if (err != 0 && result != NULL) {
        free(result);
        result = NULL;
    }
    *procList = result;
    if (err == 0) {
        *procCount = length / sizeof(kinfo_proc);
    }

    assert( (err == 0) == (*procList != NULL) );

    return err;
}

static int FindProcessByName(const char *processName, pid_t *pid)
// Find the process that best matches processName and return
// its PID.  It first tries to find an exact match; if that fails
// it tries to find a substring match; if that fails it checks
// whether processName is a number and returns that as the PID.
//
// On entry, processName must not be NULL, and it must not be the
// empty string.  pid must not be NULL.
// On success, *pid will be the process ID of the found process.
// On error, *pid is undefined.
{
    int             err;
    int             foundCount;
    kinfo_proc *    processList;
    size_t          processCount;
    size_t          processIndex;

    assert(processName != NULL);
    assert(processName[0] != 0);            // needed for strstr to behave
    assert(pid != NULL);

    processList = NULL;

    foundCount = 0;

    // Get the list of all processes.

    err = GetBSDProcessList(&processList, &processCount);

    if (err == 0) {

        // Search for an exact match.

        for (processIndex = 0; processIndex < processCount; processIndex++) {
            if ( strcmp(processList[processIndex].kp_proc.p_comm, processName) == 0 ) {
                *pid = processList[processIndex].kp_proc.p_pid;
                foundCount = 1;
                break;
            }
        }

        // If that failed, search for a substring match.

        if (foundCount == 0) {
            for (processIndex = 0; processIndex < processCount; processIndex++) {
                if ( strstr(processList[processIndex].kp_proc.p_comm, processName) != NULL ) {
                    *pid = processList[processIndex].kp_proc.p_pid;
                    foundCount += 1;
                }
            }
        }

        // If we found more than 1, that's ambiguous and we error out.

        if (foundCount > 1) {
            fprintf(stderr, "%s: '%s' does not denote a unique process.\n", gProgramName, processName);
            err = EINVAL;
        }
    }

    // If still not found, try processName as a PID.

    if ( (err == 0) && (foundCount == 0) ) {
        char *    firstInvalid;

        *pid = (pid_t) strtol(processName, &firstInvalid, 10);
        if ( (processName[0] == 0) || (*firstInvalid != 0) ) {
            err = EINVAL;
        }
    }

    free(processList);

    return err;
}

static void PrintUsage(void)
{
    fprintf(stderr, "usage: %s [options] [ [ pid | name ]... ]\n", gProgramName);
    fprintf(stderr, "       Send, Receive, SendOnce, PortSet, DeadName = right reference counts\n");
    fprintf(stderr, "       DNR = dead name request\n");
    fprintf(stderr, "       -w wide output, with lots of extra info\n");
    fprintf(stderr, "          flg      = N (no senders) P (port dead) S (send rights)\n");
    fprintf(stderr, "          seqno    = sequence number\n");
    fprintf(stderr, "          mscount  = make-send count\n");
    fprintf(stderr, "          qlimit   = queue limit\n");
    fprintf(stderr, "          msgcount = message count\n");
    fprintf(stderr, "          sorights = send-once right count\n");
    fprintf(stderr, "          pset     = port set count\n");
}

int main(int argc, char * argv[])
{
    kern_return_t         err = 0;
    bool                verbose;
    int                 ch;
    int                 argIndex;

    // Set gProgramName to the last path component of argv[0]

    gProgramName = strrchr(argv[0], '/');
    if (gProgramName == NULL) {
        gProgramName = argv[0];
    } else {
        gProgramName += 1;
    }

    // Parse our options.

    verbose = false;
    do {
        ch = getopt(argc, argv, "w");
        if (ch != -1) {
            switch (ch) {
                case 'w':
                verbose = true;
                break;
                case '?':
                default:
                PrintUsage();
                exit(EXIT_FAILURE);
                break;
            }
        }
    } while (ch != -1);

    // Handle the remaining arguments.  If none, we work against ourselves.
    // Otherwise each string is treated as a process name, and we look that
    // up using FindProcessByName.

    if (argv[optind] == NULL) {
        err = PrintProcessPortSpace(getpid(), verbose);
    } else {
        for (argIndex = optind; argIndex < argc; argIndex++) {
            pid_t   pid = 0;

            if (argIndex > optind) {
                fprintf(stdout, "\n");
            }
            if (argv[argIndex][0] == 0) {
                err = EINVAL;
            } else {
                err = FindProcessByName(argv[argIndex], &pid);
            }
            if (err == 0) {
                fprintf(stdout, "Mach ports for '%s' (%lld):\n", argv[argIndex], (long long) pid);
                fprintf(stdout, "\n");

                err = PrintProcessPortSpace(pid, verbose);
            }

            if (err != 0) {
                break;
            }
        }
    }

    if (err != 0) {
        fprintf(stderr, "%s: Failed with error %d.\n", gProgramName, err);
    }
    return (err == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Dump进程(任务)中的所有通信端口
Dump进程(任务)中的所有通信端口

标签:Dump,err,stdout,端口,通信,fprintf,stderr,port,mach
来源: https://blog.51cto.com/haidragon/2407067

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有