#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <ip.h>
#include "a1.h"
enum { // SNMP error codes
EnoError = 0, // no error
EnoSuchName = 2, // name of a requested object was not found.
};
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 char *Snmperr[] = {
"no error",
"reply too big",
"no such name",
"bad value or syntax",
"readonly",
"generic error",
"permission denied",
"bad type",
"bad length",
"bad encoding",
"bad value",
"cannot create",
"value inconsistant",
"resource not available",
"commit failed",
"set and backout both failed",
"authorisation failed",
"not writable",
"inconsistant name"
};
static void
responderrstr(Req *r)
{
char e[ERRMAX];
*e = 0;
rerrstr(e, sizeof e);
respond(r, e);
}
int
mkent(File *f, char *oid)
{
File *nf;
Cache *c;
int depth;
char *p, *name, *path, *root;
root = estrdup9p(oid);
if((p = strrchr(root, '.')) != nil && strcmp(p, ".0") == 0)
*p = 0;
path = root;
depth = 1;
incref(f);
while(f && (p = strchr(path, '.'))){
*p = '\0';
if((name = lookmap(root)) == 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(root)) == nil)
name = path;
if((nf = walkfile(f, name)) != nil){
fprint(2, "%s: warning: %s duplicated - Agent bug - walk aborted\n", argv0, oid);
decref(f);
decref(nf);
return -1;
}
c = emalloc9p(sizeof(Cache));
memset(c, 0, sizeof(Cache));
c->oid = estrdup9p(oid);
if((nf = createfile(f, name, User, 0666, c)) == nil)
print("%s - %s is duplicated\n", oid, name);
// sysfatal("%s %s - cannot create file\n", oid, name);
else
decref(nf);
decref(f);
return 0;
}
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;
s.pdu[0].objid = estrdup9p(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(r.estat != EnoError && r.estat != EnoSuchName){
fprint(2, "err %d\n", r.estat);
break;
}
if(Debug)
fprint(2, "mktree: %s %s\n", r.pdu[0].objid, top);
if(strcmp(s.pdu[0].objid, r.pdu[0].objid) == 0) // stuck
break;
if(strncmp(r.pdu[0].objid, top, strlen(top)) != 0) // walked out
break;
if(strcmp(s.pdu[0].objid, top) != 0)
if(mkent(f, s.pdu[0].objid) < 0)
break;
free(s.pdu[0].objid);
s.pdu[0].objid = r.pdu[0].objid;
}
free(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;
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;
free(r.pdu[0].objid);
return r.estat;
}
int
fillcache(Cache *c)
{
Snmp s, r;
memset(&s, 0, sizeof(s));
memset(&r, 0, sizeof(r));
s.type = Pget;
s.pdu[0].objid = c->oid;
s.pdu[1].type = Anull;
s.npdu = 1;
if(dosnmp(Net, &s, &r) < 0)
return -1;
if(r.estat != 0){
free(r.pdu[0].objid);
return r.estat;
}
free(c->data);
c->data = smprint("%A", &r);
c->ndata = strlen(c->data);
c->mtime = time(nil);
free(r.pdu[0].objid);
return r.estat;
}
void
fsread(Req *r)
{
int err;
Cache *c;
long count;
vlong offset;
c = r->fid->file->aux;
offset = r->ifcall.offset;
count = r->ifcall.count;
r->ofcall.count = 0;
if(time(nil) - c->mtime > Cachetime){
err = fillcache(c);
switch(err){
case 0:
break;
case -1:
responderrstr(r);
return;
default:
if(err > 0 && err < nelem(Snmperr))
respond(r, Snmperr[err]);
else
respond(r, "unknown error");
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)
{
int err;
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);
if(c->data == nil || memchr(c->data, '\n', c->ndata) == nil){
r->ofcall.count = count;
respond(r, nil);
return;
}
err = flushcache(c);
c->ndata = 0;
switch(err){
case 0:
break;
case -1:
responderrstr(r);
return;
default:
if(err > 0 && err < nelem(Snmperr))
respond(r, Snmperr[err]);
else
respond(r, "unknown error");
return;
}
r->ofcall.count = count;
respond(r, nil);
}
void
fsdestroyfid(Fid *fid)
{
Cache *c;
if(!fid->file || !fid->file->aux)
return;
c = fid->file->aux;
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('O', oidfmt);
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);
}
|