kern.file weirdness

YONETANI Tomokazu qhwt at myrealbox.com
Sat Nov 22 16:01:51 PST 2003


On Sat, Nov 22, 2003 at 02:22:57PM +0100, Eirik Nygaard wrote:
> On Sat, Nov 22, 2003 at 10:08:57PM +0900, YONETANI Tomokazu wrote:
> > Hello.
> > 
> > On Sat, Nov 22, 2003 at 11:53:30AM +0100, Eirik Nygaard wrote:
> > > I am working on removing perlism in userland and have come over a little
> > > problem when I am rewriting sockstat. I try to get all the open fd's from
> > > the kern.file sysctl with this code:
> > > void
> > > get_files(void)
> > > {
> > > 	size_t size;
> > > 	
> > > 	if (sysctlbyname("kern.file", NULL, &size, NULL, 0) < 0)
> > > 		err(1, "sysctlbyname()");
> > > 	if ((files = malloc(size)) == NULL)
> > > 		err(1, "malloc()");
> > > 	if (sysctlbyname("kern.file", files, &size, NULL, 0) < 0) 
> > > 		err(1, "sysctlbyname()");
> > > 	
> > > 	nfiles = size / sizeof(struct file);
> > > 	{
> > > 		int i;
> > > 		for(i = 0; i < nfiles; i++)
> > > 			printf("Type: %d\n", files[i].f_type);
> > > 	}
> > > 	
> > > }
> > > 
> > > but it only gives me bogus results for some reason.
> > > 
> > > Output: 
> > > Type: -16333
> > > Type: -14424
> > > [...]
> > > Type: -14424
> > > 
> > > Would be grateful if someone knows what I am doing wrong here.
> > 
> > FreeBSD-current's version of sockstat has a block of code
> > which looks like this:
> > 
> > static void
> > getfiles(void)
> > {
> > 	size_t len;
> > 
> > 	if ((xfiles = malloc(len = sizeof *xfiles)) == NULL)
> > 		err(1, "malloc()");
> > 	while (sysctlbyname("kern.file", xfiles, &len, 0, 0) == -1) {
> > 		if (errno != ENOMEM)
> > 			err(1, "sysctlbyname()");
> > 		len *= 2;
> > 		if ((xfiles = realloc(xfiles, len)) == NULL)
> > 			err(1, "realloc()");
> > 	}
> > 	if (len > 0 && xfiles->xf_size != sizeof *xfiles)
> > 		errx(1, "struct xfile size mismatch");
> > 	nxfiles = len / sizeof *xfiles;
> > }
> > 
> > i.e., you must resize the buffer until sysctlbyname() fails with
> > ENOMEM.
> 
> If you check out the manpage for sysctlbyname it states:
>      The size of the available data can be determined by calling sysctl() with
>      the NULL argument for oldp.  The size of the available data will be
>      returned in the location pointed to by oldlenp.  For some operations, the
>      amount of space may change often.  For these operations, the system
>      attempts to round up so that the returned size is large enough for a call
>      to return the data shortly thereafter.
> 
> so I think it is something else, but thanks for the tip.

Ah, I didn't even understand your question, sorry. Please do

    less +/^sysctl_kern_file /sys/kern/kern_descrip.c

And you'll notice why the returned array has an extra garbage(filehead)
in front of it.

|	error = SYSCTL_OUT(req, (caddr_t)&filehead, sizeof(filehead));
|	if (error)
|		return (error);
|
|	/*
|	 * followed by an array of file structures
|	 */
|	LIST_FOREACH(fp, &filehead, f_list) {
|		error = SYSCTL_OUT(req, (caddr_t)fp, sizeof (struct file));
|		if (error)
|			return (error);
|	}
|	return (0);
|}

So you can rewrite your example code to something like this:

void
get_files(void)
{
	struct {
		struct filelist head;
		struct file array[1];
	} *files;
	struct file *f;
	size_t size;
	int i;
	
	if (sysctlbyname("kern.file", NULL, &size, NULL, 0) < 0)
		err(1, "sysctlbyname()");
	if ((files = malloc(size)) == NULL)
		err(1, "malloc()");
	if (sysctlbyname("kern.file", files, &size, NULL, 0) < 0) 
		err(1, "sysctlbyname()");
	nfiles = (size - sizeof(files->head)) / (sizeof files->array[0]);
	for(f = files->array; f < files->array + nfiles; ++f)
		printf("Type: %d\n", f->f_type);
}

FreeBSD-CURRENT's version of `sysctl -b kern.file' returns just an
array of struct xfile, so your code could be simpler(but making
sysctl_kern_file messy).





More information about the Submit mailing list