Skip to content

Commit

Permalink
single pid + leak checks
Browse files Browse the repository at this point in the history
  • Loading branch information
hrbrmstr committed Mar 14, 2021
1 parent 2bf7ad5 commit 659982d
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 47 deletions.
13 changes: 11 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@ archinfo:
$(CC) archinfo.c -o arm_app -target arm64-apple-macos11
lipo -create -output archinfo x86_app arm_app && rm x86_app arm_app

leaks: archinfo
leaks --readonlyContent -atExit -- ./archinfo | grep LEAK: || true
leaks --readonlyContent -atExit -- ./archinfo --json | grep LEAK: || true
leaks --readonlyContent -atExit -- ./archinfo --json | grep LEAK: || true

sign: archinfo
codesign --force --verify --verbose --sign ${IDENTITY} archinfo

clean:
rm -f archinfo

test: archinfo
@./archinfo | grep -q tccd && echo "Columns: PASSED" || "Columns: FAILED"
@./archinfo --json | grep -q 'tccd"}' && echo " JSON: PASSED" || " JSON: FAILED"
@./archinfo | grep -q tccd && echo "Columns: PASSED (list)" || echo "Columns: FAILED (list)"
@./archinfo --columns | grep -q tccd && echo "Columns: PASSED (list, explicit)" || echo "Columns: FAILED (list, explicit)"
@./archinfo --json | grep -q 'tccd"}' && echo " JSON: PASSED (list)" || echo " JSON: FAILED (list)"
@(./archinfo --pid `pgrep keyboardservicesd` | grep -q '64') && echo "Columns: PASSED (single)" || echo "Columns: FAILED (single)"
@(./archinfo --columns --pid `pgrep keyboardservicesd` | grep -q '"}') && echo "Columns: PASSED (single, explicit)" || echo "Columns: FAILED (single, explicit)"
@(./archinfo --json --pid `pgrep keyboardservicesd` | grep -q '"}') && echo " JSON: PASSED (single)" || echo " JSON: FAILED (single)"
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.3.0 • 2021-03-14
- added option for retrieving process info for a single pid
- added more tests
- added leak check Makefile option
# 0.2.0 • 2021-03-14
- removed Xcode dependency
- added codesigning option
Expand Down
170 changes: 125 additions & 45 deletions archinfo.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <mach/machine.h>
#include <sys/types.h>
#include <sys/sysctl.h>

#define noErr 0
#define VERSION "0.2.0"
#define VERSION "0.3.0"
#define SYSCTL_ERROR 1

enum fmt { JSON=0, COLUMNS };

static int maxArgumentSize = 0;

typedef struct ProcInfo {
bool ok;
char *name;
char arch[10];
} procinfo;


// arch_info() is due to the spelunking work of Patrick Wardle <https://www.patreon.com/posts/45121749>
static char *arch_info(pid_t pid) {

Expand Down Expand Up @@ -51,37 +64,50 @@ static char *arch_info(pid_t pid) {

}

// The below is modified code from: <https://gist.github.com/s4y/1173880/9ea0ed9b8a55c23f10ecb67ce288e09f08d9d1e5>
// and is Copyright (c) 2020 DeepTech, Inc. (MIT License).
//
// The code below the standard way to do this and I originally only copied it due to some Objective-C components.
// While those components are no longer in use, the ACK stays b/c I'm thankful I didn't have to write Objective-C
// for the initial version :-)
procinfo proc_info(pid_t pid) {

size_t size = maxArgumentSize;
procinfo p;

int enumerate_processes(enum fmt output_type) {
char* buffer = (char *)malloc(4096);

static int maxArgumentSize = 0;

if (maxArgumentSize == 0) {
size_t size = sizeof(maxArgumentSize);
if (sysctl((int[]) { CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) {
perror("sysctl argument size");
maxArgumentSize = 4096; // Default
}
if (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) {

p.ok = TRUE;
p.name = buffer;
strncpy(p.arch, arch_info(pid), 10);

} else {
free(buffer);
p.ok = FALSE;
}

return(p);

}

void output_one(enum fmt output_type, pid_t pid, procinfo p) {
if (output_type == COLUMNS) {
printf("%7d %6s %s\n", pid, p.arch, p.name+sizeof(int));
} else if (output_type == JSON) {
printf("{\"pid\":%d,\"arch\":\"%s\",\"name\":\"%s\"}\n", pid, p.arch, p.name+sizeof(int));
}
}

int enumerate_processes(enum fmt output_type) {

int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
struct kinfo_proc *info;
size_t length;
int count;

if (sysctl(mib, 3, NULL, &length, NULL, 0) < 0) return(1);
if (sysctl(mib, 3, NULL, &length, NULL, 0) < 0) return(SYSCTL_ERROR);

if (!(info = malloc(length))) return(1);
if (!(info = malloc(length))) return(SYSCTL_ERROR);

if (sysctl(mib, 3, info, &length, NULL, 0) < 0) {
free(info);
return(1);
return(SYSCTL_ERROR);
}

count = (int)(length / sizeof(struct kinfo_proc));
Expand All @@ -92,20 +118,13 @@ int enumerate_processes(enum fmt output_type) {

if (pid == 0) continue;

size_t size = maxArgumentSize;

char* buffer = (char *)malloc(length);
procinfo p = proc_info(pid);

if (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) {
if (output_type == COLUMNS) {
printf("%7d %6s %s\n", pid, arch_info(pid), buffer+sizeof(int));
} else if (output_type == JSON) {
printf("{\"pid\":%d,\"arch\":\"%s\",\"name\":\"%s\"}\n", pid, arch_info(pid), buffer+sizeof(int));
}
if (p.ok) {
output_one(output_type, pid, p);
free(p.name);
}

free(buffer);


}

free(info);
Expand All @@ -119,31 +138,92 @@ void help() {
printf("archinfo %s\n", VERSION);
printf("boB Rudis <[email protected]>\n");
printf("\n");
printf("archinfo outputs a list of running processes with the architecture (x86_64/amd64)\n");
printf("archinfo outputs a list of running processes with architecture (x86_64/amd64) information\n");
printf("\n");
printf("USAGE:\n");
printf(" archinfo [FLAG]\n");
printf(" archinfo [--columns|--json] [--pid #]\n");
printf("\n");
printf("FLAG:\n");
printf("FLAGS/OPTIONS:\n");
printf(" --columns Output process list in columns (default)\n");
printf(" --json Output process list in ndjson\n");
printf(" --columns Output process list in columns\n");
printf(" --pid # Output process architecture info for the specified process\n");
printf(" --help Display this help text\n");

}

void init() {

if (maxArgumentSize == 0) {
size_t size = sizeof(maxArgumentSize);
if (sysctl((int[]) { CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) {
perror("sysctl argument size");
maxArgumentSize = 4096; // Default
}
}

}

int main(int argc, char** argv) {

if (argc >= 2) {
if (strcmp("--json", argv[1]) == 0) {
return(enumerate_processes(JSON));
} else if (strcmp("--columns", argv[1]) == 0) {
return(enumerate_processes(COLUMNS));
} else if (strcmp("--help", argv[1]) == 0) {
help();
return(0);
int c;
bool show_help = FALSE;
bool do_one = FALSE;
pid_t pid = -1;
enum fmt output_type = COLUMNS;

while(true) {

int this_option_optind = optind ? optind : 1;
int option_index = 0;

static struct option long_options[] = {
{ "json", no_argument, 0, 'j' },
{ "columns", no_argument, 0, 'c' },
{ "help", no_argument, 0, 'h' },
{ "pid", required_argument, 0, 'p' },
{ 0, 0, 0, 0 }
};

c = getopt_long(argc, argv, "jchp:", long_options, &option_index);

if (c == -1) break;

switch(c) {

case 'p': do_one = TRUE; pid = atoi(optarg); break;
case 'h': show_help = TRUE; break;
case 'j': output_type = JSON; break;
case 'c': output_type = COLUMNS; break;

}
} else {
return(enumerate_processes(COLUMNS));

}

init();

// only show help if --help is in the arg list

if (show_help) {
help();
return(0);
}

// only do one process if --pid is in the arg list

if (do_one) {
if (pid > 0) {
procinfo p = proc_info(pid);
if (p.ok) {
output_one(output_type, pid, p);
free(p.name);
return(0);
}
}
return(SYSCTL_ERROR);
}

// otherwise do them all

return(enumerate_processes(output_type));

}

0 comments on commit 659982d

Please sign in to comment.