//
// 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;
}
原文地址:https://blog.51cto.com/haidragon/2407067