Subject: Re: building a userland interface to a kernel structure
To: David Maxwell <david@fundy.ca>
From: Michael L. Hitch <mhitch@lightning.oscs.montana.edu>
List: current-users
Date: 01/12/1999 11:31:00
On Tue, 12 Jan 1999, David Maxwell wrote:

> On Tue, Jan 12, 1999 at 12:08:25AM -0800, dustin sallings wrote:
> > 
> > 	For a kernel modification I'm doing, I need to be able to build
> > and maintain a simple array of (arrays|hashes|linked lists) from a
> > userland process.
> > 
> > 	I don't have the slightest idea where to put this in the kernel,
> > what to name it, or how to interface it.  Can someone give me an idea?
> 
> I'll tag onto this message and ask... I'd like to write a userland program
> which can read the kernel disklist linked list. I've looked into the 
> kernel memory syscalls in a fair amount of detail, and looked at some
> examples in the tree, but I can't find anything in the tree that
> reads _structures_ from the kernel, just simple variables. I haven't
> been able to make my code work.

  Here's a simple example.  I don't remember where I got this - it doesn't
look like I wrote it, although I may have modified it somewhat.  [The
program is almost 3 years old.]  I have written a number of little
kernel-groveling programs like this to make it easier to see what's going
on in a live kernel.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/dkstat.h>
#include <sys/time.h>
#include <sys/disk.h>

#include <err.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <nlist.h>

static struct nlist namelist[] = {
#define	X_TK_NIN	0
	{ "_tk_nin" },		/* tty characters in */
#define	X_TK_NOUT	1
	{ "_tk_nout" },		/* tty characters out */
#define	X_CP_TIME	2
	{ "_cp_time" },		/* system timer ticks */
#define	X_HZ		3
	{ "_hz" },		/* ticks per second */
#define	X_STATHZ	4
	{ "_stathz" },
#define X_DISK_COUNT	5
	{ "_disk_count" },	/* number of disks */
#define X_DISKLIST	6
	{ "_disklist" },	/* TAILQ of disks */
	{ NULL },
};

/* Kernel pointers: nlistf and memf defined in calling program. */
static kvm_t	*kd = NULL;
char	*nlistf;
char	*memf;

/* Pointer to list of disks. */
static struct disk	*dk_drivehead = NULL;

/* Backward compatibility references. */
int	  	dk_ndrive = 0;

#define	KVM_ERROR(_string) {						\
	warnx((_string));						\
	errx(1, kvm_geterr(kd));					\
}

/*
 * Dereference the namelist pointer `v' and fill in the local copy 
 * 'p' which is of size 's'.
 */
#define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));

static void deref_kptr __P((void *, void *, size_t));

main(int argc, char *argv[])
{
	int i;
	struct disklist_head disk_head;
	struct disk	cur_disk, *p;
        char		errbuf[_POSIX2_LINE_MAX];

        if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
		errx(1, "kvm_openfiles: %s", errbuf);

	/* Obtain the namelist symbols from the kernel. */
	if (kvm_nlist(kd, namelist))
		KVM_ERROR("kvm_nlist failed to read symbols.");

	/* Get the number of attached drives. */
	deref_nl(X_DISK_COUNT, &dk_ndrive, sizeof(dk_ndrive));

	if (dk_ndrive < 0)
		errx(1, "invalid _disk_count %d.", dk_ndrive);
	else if (dk_ndrive == 0) {
		warnx("No drives attached.");
	}
	else {
		/* Get a pointer to the first disk. */
		deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
		dk_drivehead = disk_head.tqh_first;
	}

	printf("%d drives, header %x\n", dk_ndrive, dk_drivehead);

	p = dk_drivehead;
	printf("Address  Name     Transfers   Seeks     Bytes\n");
	for (i = 0; i < dk_ndrive; ++i) {
		deref_kptr(p, &cur_disk, sizeof(cur_disk));
		deref_kptr(cur_disk.dk_name, errbuf, 10);
		printf("%08x %-10s%8lld%8lld%10lld\n", p, errbuf,
		    cur_disk.dk_xfer, cur_disk.dk_seek, cur_disk.dk_bytes);
		p = cur_disk.dk_link.tqe_next;
	}
}

/*
 * Dereference the kernel pointer `kptr' and fill in the local copy 
 * pointed to by `ptr'.  The storage space must be pre-allocated,
 * and the size of the copy passed in `len'.
 */
static void
deref_kptr(kptr, ptr, len)
	void *kptr, *ptr;
	size_t len;
{
	char buf[128];

	if (kvm_read(kd, (u_long)kptr, (char *)ptr, len) != len) {
		bzero(buf, sizeof(buf));
		snprintf(buf, (sizeof(buf) - 1),
		     "can't dereference kptr 0x%lx", (u_long)kptr);
		KVM_ERROR(buf);
	}
}

---
Michael L. Hitch			mhitch@montana.edu
Computer Consultant
Information Technology Center
Montana State University	Bozeman, MT	USA