#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();
}
}
|