Plan 9 from Bell Labs’s /usr/web/sources/contrib/stallion/src/collectd/df.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <bio.h>
#include <disk.h>
#include "collectd.h"

#define	MAXFS	8
#define NSEC	300	/* 5 minutes */

typedef	struct Fs Fs;
struct Fs {
	char	*name;
	char	*device;
	double	total;
	double	used;
	long	lastread;
};

static char *
getaddr(char *addr)
{
	char *f[3];
	int nf;

	nf = gettokens(addr, f, nelem(f), "!");
	return nf > 1 ? f[1] : f[0];
}

static char *
getvalue(char *s)
{
	char *p;

	if(p = strchr(s, '='))
		return p+1;
	return s;
}

static double
getvalued(char *s)
{
	char *p;
	static char buf[32];

	s = getvalue(s);
	p = buf;
	do{
		if(*s != ',')
			*p++ = *s;
	}while(*s++);

	return strtod(buf, nil);
}

static ulong
loadfossil(Fs *fs, ulong size)
{
	int fd, nf, nfs;
	char *bootdisk;
	char buf[1024], *f[4];
	char *p, *e, *q;
	long n;
	Fs *fp;

	nfs = 0;
	bootdisk = getenv("bootdisk");
	if(bootdisk == nil)
		return 0;
	fd = open(bootdisk, OREAD);
	if(fd < 0)
		sysfatal("couldn't open %s: %r", bootdisk);
	n = pread(fd, buf, sizeof buf, 127*1024);
	if(n < 0)
		sysfatal("couldn't read %s: %r", bootdisk);
	if(memcmp(buf, "fossil config\n", 14) != 0){
		close(fd);
		return 0; /* kfs? */
	}
	buf[n] = '\0';

	p = buf;
	e = buf + n;
	while(p && p < e){
		q = strchr(p, '\n');
		if(q)
			*q++ = '\0';
		nf = tokenize(p, f, nelem(f));
		if(strcmp(f[0], "fsys") == 0)
		if(strcmp(f[2], "config") == 0){
			fp = &fs[nfs++];
			if(fp == fs+size)
				break;
			fp->name = smprint("fossil-%s", f[1]);
			if(nf < nelem(f))
				fp->device = estrdup(bootdisk);
			else
				fp->device = estrdup(f[3]);
			fp->lastread = 0;
		}
		p = q;
	}
	close(fd);

	return nfs;
}

static int
loadventi(Fs *fp)
{
	char *p;

	if(p = getenv("venti")){
		fp->device = estrdup(netmkaddr(getaddr(p), "tcp", "8000"));
		free(p);
	}
	return !!fp->device;
}

static void
submit(Channel *c, Fs *fp)
{
	Packet *pp;

	pp = palloc();
	pp->host = estrdup(hostname);
	pp->interval = interval;
	pp->time = time(nil);
	pp->plugin = estrdup("df");
	pp->pinst = estrdup(fp->name);
	pp->type = estrdup("df");
	addgauge(pp, fp->used);
	addgauge(pp, fp->total - fp->used);
	if(nbsendp(c, pp) < 1)
		pfree(pp);
}

static void
readfossil(Fs *fp)
{
	long now;
	int pfd[2];
	char *p, *f[2];
	int nf;
	static Biobuf bin;

	now = time(nil);
	if(now - fp->lastread < NSEC)
		return;
	fp->lastread = now;

	if(pipe(pfd) < 0)
		exits(nil);

	switch(fork()){
	case -1:
		sysfatal("fork failed: %r");
	case 0:
		dup(pfd[0], 1);
		close(pfd[0]);
		close(pfd[1]);
		execl("/bin/fossil/df", "fossil/df", fp->device, nil);
		exits("nodf");
	default:
		close(pfd[0]);
		Binit(&bin, pfd[1], OREAD);
		if(p = Brdline(&bin, '\n')){
			p[Blinelen(&bin)-1] = '\0';

			nf = tokenize(p, f, nelem(f));
			if(nf < nelem(f))
				sysfatal("unexpected df output");

			fp->total = getvalued(f[0]);
			fp->used = getvalued(f[1]);
		}
		Bterm(&bin);
		close(pfd[1]);
		waitpid();
	}
}

static void
readventi(Fs *fp)
{
	long now;
	int fd;
	char *p, *f[3];
	int nf;
	static Biobuf bin;

	now = time(nil);
	if(now - fp->lastread < NSEC)
		return;
	fp->lastread = now;

	fd = dial(fp->device, nil, nil, nil);
	if(fd < 0)
		sysfatal("couldn't dial %s: %r", fp->device);

	fprint(fd, "GET /storage\n");

	Binit(&bin, fd, OREAD);
	while(p = Brdline(&bin, '\n')){
		p[Blinelen(&bin)-1] = '\0';

		if(fp->name == nil)
		if(strncmp(p, "index=", 6) == 0)
			fp->name = smprint("venti-%s", getvalue(p));

		if(strncmp(p, "total space=", 12) == 0){
			nf = tokenize(p, f, nelem(f));
			if(nf < nelem(f))
				sysfatal("unexpected venti output");

			fp->total = getvalued(f[1]);
			fp->used = getvalued(f[2]);
			break;
		}
	}
	Bterm(&bin);
	close(fd);
}

void
dfproc(void *arg)
{
	Fs fs[MAXFS], venti;
	long nfs;
	Channel *c;
	int hasventi, i;

	/*
	 * Currently, only fossil and venti are supported.  Venti must
	 * be configured in plan9.ini; ndb(6) and fossilcons(8) provide
	 * less interesting methods of configuration.
	 */
	nfs = loadfossil(fs, nelem(fs));
	if(nfs == 0)
		exits("nodf");

	memset(&venti, 0, sizeof venti);
	hasventi = loadventi(&venti);

	c = arg;
	for(;;){

		/*
		 * Checking used disk space is surprisingly expensive.
		 * To avoid colliding with fossilcons(8), fossil/df is
		 * called, which must recalculate used blocks for each
		 * invocation.  Values are cached and refreshed
		 * periodically independant of the configured interval.
		 */
		for(i = 0; i < nfs; ++i){
			readfossil(&fs[i]);
			submit(c, &fs[i]);
		}
		if(hasventi){
			readventi(&venti);
			submit(c, &venti);
		}
		
		snooze();
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.