-
Notifications
You must be signed in to change notification settings - Fork 2
TUT:Writing_a_Dynamically_Loadable_Object
This page describes how to build extension for Net-SNMP as shared objects, binary files that can be loaded by the SNMPd daemon directly and are executed as part of the daemon. This differs from the concept introduced in Writing a MIB Module in that the extension resides in its own binary file and is loaded by the agent at runtime. This adds the flexibility to add new functionality / MIBs after the agent has been compiled, enabling you to extent the daemon provided by a binary package without the need to roll your own modified package.
Yet another possibility is offered by Subagents, see Writing a Subagent: Subagents are separate processes that do not share memory, file descriptors and so on with the daemon and must use interprocess communication (IPC) to communicate with the daemon.
Writing dynamically loadable objects is interesting because you will have the entire API provided by Net-SNMP at your disposal, for example a caching infrastructure. Shared objects loaded this way usually get their configuration from the same configuration file the core daemon uses, which often is a nice thing. Disadvantages are that a programming mistake in your custom extension may crash the entire daemon and that your code is by design always executed as the same user the core daemon is executed as, i. e. potentially with unnecessary privileges — or too little, depending on your setup.
While not strictly necessary, you'll probably have an easier time building a shared object if you start with an already functioning MIB module (extension) as described in Writing a MIB Module. This section of the tutorials will assume you have a working extension build directly into the daemon already and tells you how to move that out of the daemon and into a shared object.
For demonstration purposes, we'll refer to some example MIB objects and code: the NET-SNMP-TUTORIAL-MIB MIB, and the example MIB module and it's header file.
Note: The dlmod
code is based on the UCD-DLMOD-MIB
.
It resides in the ucdExperimental
name-space, but since nothing has
changed since 1999 it's pretty safe to assume this interface is stable.
You can find the MIB in mibs/UCD-DLMOD-MIB.txt
in the source-code
distribution.
The Net-SNMP package must have been built with dynamically loadable module support for the following to work. You should therefore enable the UCD MIB when configuring the sources:
$ ./configure --with-mib-modules="ucd_snmp $OTHER_MIBS" $OTHER_OPTIONS
Besides the right MIB, you need of course support for shared objects.
This is enabled with the --enable-shared
argument of configure
, but
should be automatically enabled if your system supports dynamically
loaded objects. You can check for support in your agent but looking at
the output of the snmpd -H
command for the "dlmod" token. If its
listed, the compiled agent supports it.
Note: All command line options below assume you have an
appropriately setup ~/.snmp/snmp.conf
file that allows you to not have
to specify a SNMP version number, community name, username, or whatever
else in order to talk to your agent. The agent, of course, must have a
matching /usr/local/share/snmp/snmpd.conf
file (or equivalent).
|- | nstAgentPluginObject.h || The MIB module's header file |- | nstAgentPluginObject.c || The MIB module's C source code |}
After loading the shared object to memory, the object loader
will look for an initialization function inside the shared object. The
name of this function is init_
name
, where "name" is the
name of your extension. The name of the example module is
"nstAgentPluginObject", so the loader will look for a function with the
following prototype:
void init_nstAgentPluginObject (void);
There's a complementary function, called deinit_
name
, which
is called when the module is unloaded, for example when the daemon is
shutting down. Unsurprisingly, the prototype of the example
deinit-function is:
void deinit_nstAgentPluginObject (void);
These are the only two functions called by the object loaded. All other
functions (and module-global variables) therefore should be declared
static
.
It is important to note that nothing prevents the agent from a second "load" of the same shared object. The module isn't actually loaded a second time, but the initialization function is run again. Modules should be able to handle that, for example by checking if they were already initialized when returning without further actions if so. The same applies to the de-initialization functions.
Further when the last unload happens the module is unloaded and so the code disappears. When this happens everything registered with the agent from the module must be unregistered since not doing so is an open invitation to core dumps.
You can check the names of currently loaded modules using the
UCD-DLMOD-MIB::dlmodName
OID.
As mentioned in the introduction, you can register own configuration options which extend the daemon's configuration. To do so, you need to provide two callback functions which are called when appropriate: One that handles a configuration statement, and one that cleans up when necessary. A short example is given below and registers the foobar option:
static const char *global_foobar = NULL;
…
static void config_handle (const char *key, char *value)
{
if (strcasecmp ("foobar", key) == 0)
{
char *tmp = strdup (value);
if (tmp != NULL)
{
free (global_foobar);
global_foobar = tmp;
}
}
else if …
} /* void config_handle */
static void config_free (void)
{
free (global_foobar);
global_foobar = NULL;
}
…
snmpd_register_config_handler ("foobar",
config_handle, config_free,
"The foobar option does something incredible!");
Do not just assign the value
pointer to a global
variable or dreadful things may happen!
The next steps are basically the same as those steps outlined in Writing a MIB Module, i. e. register one or more data sets at some OIDs, for example using the netsnmp_register_table_data_set, see the data_set.c example. Please see the introductory documentation.
Building shared objects for Net-SNMP works just like building any other
shared object. If you have not done that so far, you should probably
read your compilers documentation on the subject. When using the GNU
Compiler Collection (GCC), you will most likely need the -fPIC -shared
flags.
You should use the net-snmp-config
script to determine additional
compiler and linker flags. For
example:
$ cc `net-snmp-config --cflags` -fPIC -shared -g -O0 -o nstAgentPluginObject.so nstAgentPluginObject.c `net-snmp-config --libs`
You can find example Makefiles at
agent/mibgroup/examples/Makefile.dlmod
in the source-code distribution
and at
1.
Hopefully this'll get you started.
-
Start the snmpd and watch the dlmod and nstAgentPluginObject modules interact using the debugging flag (this assumes you already have access control set up properly for your agent):
% **snmpd -f -L -DnstAgentPluginObject,dlmod**
-
In another window, test to make sure that the agent doesn't currently support the nstAgentPluginObject (if you get different results running this command you need to recompile the net-snmp agent without the nstAgentPluginObject mib module compiled in directly):
% **snmpget localhost NET-SNMP-TUTORIAL-MIB::nstAgentPluginObject.0** nstAgentPluginObject.0 = No Such Object available on this agent at this OID
-
% **snmpset localhost UCD-DLMOD-MIB::dlmodStatus.1 i create** dlmodStatus.1 = create(6)
-
% '''snmptable localhost UCD-DLMOD-MIB::dlmodTable ''' SNMP table: dlmodTable dlmodName dlmodPath dlmodError dlmodStatus unloaded
-
% '''snmpset localhost UCD-DLMOD-MIB::dlmodName.1 s "nstAgentPluginObject" UCD-DLMOD-MIB::dlmodPath.1 s "/path/to/nstAgentPluginObject.so" ''' dlmodName.1 = "nstAgentPluginObject" dlmodName.1 = "/path/to/nstAgentPluginObject.so" % '''snmptable localhost UCD-DLMOD-MIB::dlmodTable ''' SNMP table: dlmodTable dlmodName dlmodPath dlmodError dlmodStatus nstAgentPluginObject /path/to/nstAgentPluginObject.so unloaded
-
% '''snmpset localhost UCD-DLMOD-MIB::dlmodStatus.1 i load ''' dlmodStatus.1 = loaded(1) % snmptable localhost UCD-DLMOD-MIB::dlmodTable SNMP table: dlmodTable dlmodName dlmodPath dlmodError dlmodStatus nstAgentPluginObject /path/to/nstAgentPluginObject.so loaded
-
If everything above was done correctly, then the following command should work and will access the shared object's data:
% '''snmpget localhost NET-SNMP-TUTORIAL-MIB::nstAgentPluginObject.0 ''' nstAgentPluginObject.0 = INTEGER: 3
A more common method of loading the shared object is to instruct the
agent to load the extension at startup in the snmpd.conf
configuration
file. This can be achieved using the dlmod
configuration option:
dlmod nstAgentPluginObject /path/to/nstAgentPluginObject.so
The first argument specifies the shared object's module name, the second argument specifies the full pathname of the shared object file. The module name used here must match the name of the initialization function, see #Initialization.
All custom configuration options, as described in
#Configuration, must succeed the
dlmod
statement.
- TUT:Writing a MIB Module
- Net-snmp extensions List of extensions, some of which are built as dynamically loaded objects.