Skip to content

Instantly share code, notes, and snippets.

@leiless
Created November 29, 2018 02:11
Show Gist options
  • Save leiless/6e19c99448a417c8a95e9e22e1b9e036 to your computer and use it in GitHub Desktop.
Save leiless/6e19c99448a417c8a95e9e22e1b9e036 to your computer and use it in GitHub Desktop.
(macOS) Fetch all BSD procs into a proc list
/*
* Created 181128 lynnl
*/
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#ifndef __nullable
#define __nullable /* fake nullable annotation */
#endif
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))
/**
* Fetch all BSD procs into a proc list
* @proclist array list to store all bsd procs
* you must free(3) it if success
* pass NULL if you only care count of procs
* @cnt proc count of above list
* @return 0 if success errno o.w.
*
* NOTE:
* first sysctl(3) tends to return a larger buffer length
* as i investigated in such case it's 5
* the gap is likely used to fix the size-TOCTTOU bug
*
* see:
* https://developer.apple.com/library/archive/qa/qa2001/qa1123.html
* https://stackoverflow.com/q/32134935
*/
static int fetch_all_bsd_proc(
struct kinfo_proc ** __nullable proclist,
size_t *cnt)
{
int e;
static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
struct kinfo_proc *list = NULL;
size_t len;
if (proclist != NULL) assert(*proclist == NULL);
assert(cnt != NULL);
while (1) {
assert(list == NULL);
e = sysctl((int *) name, ARRAY_SIZE(name), NULL, &len, NULL, 0);
if (e == -1) {
e = errno;
break;
} else if (proclist == NULL) {
#ifdef DEBUG
printf("length #0: %zu\n", len / sizeof(struct kinfo_proc));
#endif
/* Only care proc count */
break;
}
#ifdef DEBUG
printf("length #1: %zu\n", len / sizeof(struct kinfo_proc));
#endif
list = malloc(len);
if (list == NULL) {
e = ENOMEM;
break;
}
/*
* Call sysctl again with the new buffer
* If we get an ENOMEM error toss away the buffer and start again
*/
e = sysctl((int *) name, ARRAY_SIZE(name), list, &len, NULL, 0);
if (e == -1) e = errno;
if (e == 0) {
#ifdef DEBUG
printf("length #2: %zu\n", len / sizeof(struct kinfo_proc));
#endif
break;
} else {
assert(list != NULL);
free(list);
list = NULL;
/*
* Abort if met unexpected errno
* Retry if count of proc. larger than last snapshot(got ENOMEM)
*/
if (e != ENOMEM) break;
}
}
if (e == 0) {
if (proclist != NULL) *proclist = list;
*cnt = len / sizeof(struct kinfo_proc);
} else {
assert(list == NULL);
}
return e;
}
int main(void)
{
int e;
size_t cnt;
struct kinfo_proc *list = NULL;
struct kinfo_proc *p;
e = fetch_all_bsd_proc(NULL, &cnt);
if (e == 0) {
printf("#proc: %zu\n", cnt);
} else {
printf("fetch_all_bsd_proc() fail errno: %d\n", e);
}
e = fetch_all_bsd_proc(&list, &cnt);
if (e == 0) {
printf("#proc: %zu\n", cnt);
for (p = list; p - list < cnt; p++) {
printf("pid: %5d %16s uid: %3u prio: %2u\n",
p->kp_proc.p_pid, p->kp_proc.p_comm,
p->kp_eproc.e_ucred.cr_uid,
p->kp_proc.p_priority);
}
free(list);
} else {
printf("fetch_all_bsd_proc() fail errno: %d\n", e);
}
return 0;
}
@leiless
Copy link
Author

leiless commented Nov 29, 2018

Sample output: http://ix.io/1uN8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment