kaashif's blog

Programming, with some mathematics on the side

How to get a list of processes on OpenBSD (in C)

2015-06-18

Is it portable?

First off, the information in this post definitely doesn't apply to Linux (as it has a completely different way of doing things) and may or may not apply to other BSDs (I see that NetBSD and FreeBSD both have similar, maybe identical, kvm(3) interfaces). There certainly isn't anything in POSIX to make this standard. The only real reason

any UNIX-like OSes have this particular interface comes from the kvm(3) man page in OpenBSD:

The kvm interface was first introduced in SunOS. A considerable number of programs have been developed that use this interface, making backward compatibility highly desirable. In most respects, the Sun kvm interface is consistent and clean. Accordingly, the generic portion of the interface (i.e., kvm_open(), kvm_close(), kvm_read(), kvm_write(), and kvm_nlist()) has been incorporated into the BSD interface. Indeed, many kvm applications (i.e., debuggers and statistical monitors) use only this subset of the interface.

So even with that, only the "generic portion" of the interface is "standardised" (although it's not really standardised, it's just de facto). Hence, using kvm_openfiles(3) and the like has no reason to work on any other OS the same way it does on OpenBSD.

Actually writing some code

The information about running processes is stored somewhere: the kernel. Even in Linux, with procfs (/proc), the info all really comes from the kernel.

On OpenBSD, you want to access the running system's kernel image. This is done using kvm_openfiles(3), which takes a host of parameters detailing the file you want to load the kernel image from. Obviously, you don't want to load a file, but the running kernel. To do this, just pass in NULL as the parameters that have anything to do with files: the function will know this means you want the running system:

#include <stdio.h>
#include <kvm.h>
#include <limits.h>
#include <sys/param.h>
#include <sys/sysctl.h>

int
main(void)
{
    char errbuf[_POSIX2_LINE_MAX];
    kvm_t *kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);

You should really check if it's null or whatever, but for didactic purposes, I'll just leave out all of the boring error handling - it's obvious where the error string would be stored (hint - errbuf), so you can handle that.

Next, you want to get a list of processes from that kernel image using kvm_getprocs(3):

    int nentries = 0;
    struct kinfo_proc *kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &nentries);

Again, check if null, handle errors. The number of processes obtained is stored in nentries. If you're wondering, the "0" in the arguments to kvm_getprocs(3) actually doesn't matter - KERN_PROC_ALL is an operation that doesn't take an argument. There are other useful operations which do take an argument, so see kvm_getprocs(3) for info on those.

Now, you obviously want to go through the processes and do something with them: you know how many processes there are and you have the pointer to the first one, so a simple for loop with a counter will do. Why don't we just print the binary name for every process?

    int i;
    for (i = 0; i < nentries; ++i) {
        printf("%s\n", kinfo[i].p_comm);
    }

Now that was easy. There are a lot of fields in the kinfo_proc struct, and there actually isn't a man page for them, since the full definition is available in <sys/sysctl.h>. Look there (/usr/include/sys/sysctl.h) for info on information you can get.

Almost forgot: this is supposed to be a valid program, so return something:

    return 0;
}

The final code

#include <stdio.h>
#include <kvm.h>
#include <limits.h>
#include <sys/param.h>
#include <sys/sysctl.h>

int
main(void)
{
    char errbuf[_POSIX2_LINE_MAX];
    kvm_t *kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
    int nentries = 0;
    struct kinfo_proc *kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &nentries);
    int i;
    for (i = 0; i < nentries; ++i) {
        printf("%s\n", kinfo[i].p_comm);
    }
    return 0;
}

Compile that with cc -lkvm main.c and run it to get some output (hopefully).

See Also

This short tutorial is basically just a gateway into the hundreds of possible things you can do with processes in OpenBSD. The documentation in the man pages is excellent, so don't hesitate to use apropos(1) in your search for more knowledge. Also, you can just go to the man page for kvm(3) and just work your way through the "SEE ALSO" sections, playing with functions you find as you go.

Have fun!