#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <ip.h>
#include "a1.h"
enum {
Cachetime = 3 // secconds a value is cached locally for
};
typedef struct Cache Cache;
struct Cache {
char *oid; // oid to read/write
char *data; // data last read
int ndata; // length of data
long mtime; // tile data last fetched
};
extern void loadmap(void);
extern char *lookmap(char *);
static int Net;
static char *User;
static int Numeric;
static int Debug;
static void
responderrstr(Req *r)
{
char e[ERRMAX];
*e = 0;
rerrstr(e, sizeof e);
respond(r, e);
}
void
mkent(File *f, char *oid)
{
File *nf;
Cache *c;
int depth;
char *p, *name, *path;
c = emalloc9p(sizeof(Cache));
memset(c, 0, sizeof(Cache));
c->oid = estrdup9p(oid);
path = oid;
depth = 1;
incref(f);
while(f && (p = strchr(path, '.'))){
*p = '\0';
if((name = lookmap(oid)) == nil)
name = path;
incref(f);
if((nf = walkfile(f, name)) == nil)
if((nf = createfile(f, name, User, DMDIR|0755, nil)) == nil)
sysfatal("%s - cannot create directory\n", name);
decref(f);
f = nf;
*p = '.';
path = p+1;
depth++;
}
incref(f);
if((name = lookmap(oid)) == nil)
name = path;
if((nf = walkfile(f, name)) == nil){
if((nf = createfile(f, name, User, 0666, c)) == nil)
sysfatal("%s - cannot create file\n", name);
}
decref(f);
decref(nf);
}
static int
mktree(File *f, char *top)
{
Snmp s, r;
memset(&s, 0, sizeof(s));
memset(&r, 0, sizeof(r));
s.vers = 0;
s.private = 0;
s.type = Pgetn;
strcpy(s.pdu[0].objid, top);
s.pdu[0].type = Anull;
s.npdu = 1;
r.eindex = 0;
while(r.eindex == 0) {
if(dosnmp(Net, &s, &r) < 0)
return -1;
if(Debug)
fprint(2, "walk: %s\n", r.pdu[0].objid);
if(strncmp(r.pdu[0].objid, top, strlen(top)) != 0)
break;
if(strcmp(s.pdu[0].objid, top) != 0)
mkent(f, s.pdu[0].objid);
strcpy(s.pdu[0].objid, r.pdu[0].objid);
}
return 0;
}
int
flushcache(Cache *c)
{
Snmp s, r;
memset(&s, 0, sizeof(s));
memset(&r, 0, sizeof(r));
s.private = 1;
strcpy(s.pdu[0].objid, c->oid);
s.type = Pset;
s.npdu = 1;
c->data[c->ndata-1] = 0;
if(Sscan(&s.pdu[0], c->data) < 0)
return -1;
if(dosnmp(Net, &s, &r) < 0)
return -1;
return 0;
}
int
fillcache(Cache *c)
{
Snmp s, r;
memset(&s, 0, sizeof(s));
memset(&r, 0, sizeof(r));
s.type = Pget;
strcpy(s.pdu[0].objid, c->oid);
s.pdu[1].type = Anull;
s.npdu = 1;
if(dosnmp(Net, &s, &r) < 0)
return -1;
free(c->data);
c->data = smprint("%A", &r);
c->ndata = strlen(c->data);
c->mtime = time(nil);
return 0;
}
void
fsread(Req *r)
{
Cache *c;
vlong offset;
long count;
c = r->fid->file->aux;
offset = r->ifcall.offset;
count = r->ifcall.count;
r->ofcall.count = 0;
if(time(nil) - c->mtime > Cachetime)
if(fillcache(c) < 0){
responderrstr(r);
return;
}
if(offset >= c->ndata){
respond(r, nil);
return;
}
if(offset+count >= c->ndata)
count = c->ndata - offset;
memmove(r->ofcall.data, c->data+offset, count);
r->ofcall.count = count;
respond(r, nil);
}
void
fswrite(Req *r)
{
void *v;
Cache *c;
vlong offset;
long count;
c = r->fid->file->aux;
offset = r->ifcall.offset;
count = r->ifcall.count;
if(offset+count >= c->ndata){
v = realloc(c->data, offset+count);
if(v == nil){
responderrstr(r);
return;
}
c->data = v;
c->ndata = offset+count;
r->fid->file->length = c->ndata;
}
memmove(c->data+offset, r->ifcall.data, count);
r->ofcall.count = count;
respond(r, nil);
}
void
fsdestroyfid(Fid *fid)
{
Cache *c;
if(!fid->file || !fid->file->aux)
return;
c = fid->file->aux;
if((fid->omode &OWRITE) == OWRITE && c->data && c->ndata)
if(flushcache(c) < 0)
fprint(2, "%s set failed - %r\n", argv0);
free(c->data);
c->data = nil;
c->ndata = 0;
}
Srv fs = {
.read= fsread,
.write= fswrite,
.destroyfid= fsdestroyfid
};
void
usage(void)
{
fprint(2, "usage: %s [-n] [-s srvname] [-m mtpt] [-M snmp.oidmap] [-r root-oid] host\n", argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
char *top, *srv, *mnt;
quotefmtinstall();
fmtinstall('A', Sfmt);
fmtinstall('V', eipfmt);
if((User = getuser()) == nil)
sysfatal("$user not set\n");
srv = nil;
mnt = "/n/snmp";
top = "1.3.6.1.2";
ARGBEGIN{
case 'd':
Debug++;
break;
case 'D':
chatty9p++;
break;
case 's':
srv = EARGF(usage());
break;
case 'm':
mnt = EARGF(usage());
break;
case 'n':
Numeric++;
break;
case 'r':
top = EARGF(usage());
break;
default:
usage();
}ARGEND;
if(argc != 1)
usage();
if(! Numeric)
loadmap();
if((Net = dial(netmkaddr(argv[0], "udp", "snmp"), 0, 0, 0)) < 0)
sysfatal("dial: %s: %r", argv[0]);
fs.tree = alloctree(User, "snmp", DMDIR|0777, nil);
if(mktree(fs.tree->root, top) < 0)
sysfatal("traversal failed %r");
postmountsrv(&fs, srv, mnt, MREPL);
exits(0);
}
|