#include #include #include #include #include <9p.h> #include #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); }