In investigating how to dynamically produce data that I could plumb via snmp, it became quickly apparent that the snmpd shipped on Intel macs in 10.4.* is pretty well busted. It only supports a very small subset of the functionality provided by the net-snmp daemon in general (i.e. on other platforms). This can be observed by running snmpd with the -H switch. Notable omissions include 'exec' and 'extend', for example, which are typically used to easily fork off arbitrary sub processes tasked with collecting data for the targeted OID or range of OIDs.
It was quickly obvious that we'd have to use the macports snmpd. That version does have support for more functionality than the one shipped with Mac OS X, but it's *missing* exec and extend! But wait! What's this about 'pass' and 'pass_persist'? They are similar to 'exec' except that pass and pass_persist allow the script to dynamically define the OID hierarchy. This is different from exec; with exec you call out the specific OID to script mappings in the config file. With pass_persist, the script takes control of an entire 'tree', and is responsible for responding for any OIDs under that tree.
To use pass_persist, we need a script that can speak the net-snmp pass_persist protocol, which is described in the snmpd.conf(5) man page.
pass_persist [-p priority] MIBOID PROG will also pass control of the subtree rooted at MIBOID to the specified PROG command. However this command will continue to run after the initial request has been answered, so subsequent requests can be processed without the startup overheads. Upon initialization, PROG will be passed the string "PING\n" on stdin, and should respond by printing "PONG\n" to stdout. For GET and GETNEXT requests, PROG will be passed two lines on stdin, the command (get or getnext) and the requested OID. It should respond by printing three lines to stdout - the OID for the result varbind, the TYPE and the VALUE itself - exactly as for the pass directive above. If the command cannot return an appropriate varbind, it should print print "NONE\n" to stdout (but continue running).
One bit of information that is missing from the man page is the shutdown sequence employed. For example, when snmpd is stopping, how does it inform the pass-persist agents of that? Well, it sends a blank line to the agent, this signals that the agent should shut down. A sample script might look something like this - note, this is just proof of concept (does not implement getnext, for example):
#!/usr/bin/python -u '''This is a python backend for snmp's "pass_persist" function. It is critical that the python interpreter be invoked with unbuffered STDIN and STDOUT by use of the -u switch in the shebang line.''' import sys from select import select def readStdin () : '''Read from standard input. Use "select" to wait for new data''' (rr, wr, er) = select([sys.stdin], , ) for fd in rr: line = fd.readline() processInput(line) def processInput (line) : '''Examine input, call subroutines''' if line.strip() == '': sys.exit(0) if 'PING' in line : playPingPong() if 'get' in line : target = sys.stdin.readline() doGet(target) def playPingPong () : '''Perform the snmpd secret handshake''' print 'PONG' def doGet(target) : '''Process a "get" request''' print target, print 'integer' print '42' # loop while 1 : readStdin()
Next, we add an entry in the snmpd config file, associating an SNMP OID tree with our script:
pass_persist .188.8.131.52.4.1.2021.255 /path/to/persist_test.py
Now let's start snmpd in a debug mode, asking for details about the pass_persist backend:
snmpd -c /etc/snmpd.conf --logTimeStamp=true -f -Lf /tmp/snmpd.log \ -Ducd-snmp/pass_persist,ucd-snmp/pass,snmpd,output,helper:debug
Tail that log file in one window, and make an snmp request in another.
snmpget -v 1 -c <community> localhost .184.108.40.206.4.1.2021.255.2
In the snmpd debug log, we see:
2007-06-29 16:06:09 ucd-snmp/pass_persist: open_persist_pipe(1,'/path/to/persist_test.py') 2007-06-29 16:06:09 ucd-snmp/pass_persist: persistpass-sending: get .220.127.116.11.4.1.2021.255.2
... and then a response to our snmp query:
UCD-SNMP-MIB::ucdavis.255.2 = INTEGER: 42