Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/snmpfs/main.c

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


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].