towerops/c_src/towerops_nif.c
Graham McIntire 522223c57c fix: define _DEFAULT_SOURCE and _BSD_SOURCE for BSD types
On newer glibc versions, BSD types like u_char are only available
when _DEFAULT_SOURCE or _BSD_SOURCE feature test macros are defined.

Define these macros before including any headers to ensure BSD types
are available for net-snmp headers.
2026-01-30 13:17:17 -06:00

150 lines
4.7 KiB
C

// Pure C NIF for net-snmp MIB resolution
// Directly links to libnetsnmp without Rust layer
// Enable BSD types (u_char, u_short, etc.) on newer glibc
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#include <erl_nif.h>
#include <sys/types.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// Initialization state
static int snmp_initialized = 0;
// Helper function to create Elixir string (binary) from C string
static ERL_NIF_TERM make_binary_string(ErlNifEnv* env, const char* str) {
size_t len = strlen(str);
ErlNifBinary bin;
if (!enif_alloc_binary(len, &bin)) {
return enif_make_badarg(env);
}
memcpy(bin.data, str, len);
return enif_make_binary(env, &bin);
}
// Initialize net-snmp library
static ERL_NIF_TERM init_mib_library(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
if (snmp_initialized) {
return make_binary_string(env, "already_initialized");
}
// Initialize SNMP library
// net-snmp reads MIB directories from MIBDIRS and MIBS environment variables
// These must be set BEFORE calling this function
init_snmp("towerops");
// Initialize MIB subsystem
netsnmp_init_mib();
// Load all MIBs into memory
// This takes ~1-2 seconds but only happens once
read_all_mibs();
snmp_initialized = 1;
return make_binary_string(env, "initialized");
}
// Set MIB directory - should be called BEFORE init_mib_library
static ERL_NIF_TERM load_mib_directory(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifBinary bin;
char mib_dir[2048];
// Get binary (Elixir strings are binaries)
if (!enif_inspect_binary(env, argv[0], &bin)) {
return enif_make_badarg(env);
}
// Copy to null-terminated C string
if (bin.size >= sizeof(mib_dir)) {
return enif_make_badarg(env);
}
memcpy(mib_dir, bin.data, bin.size);
mib_dir[bin.size] = '\0';
// Add MIB directory to search path (can be called before or after init)
add_mibdir(mib_dir);
return enif_make_atom(env, "ok");
}
// Resolve MIB name to numeric OID
static ERL_NIF_TERM resolve_oid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifBinary bin;
char mib_name[256];
oid objid[MAX_OID_LEN];
size_t objid_len = MAX_OID_LEN;
char oid_str[512];
// Get binary (Elixir strings are binaries)
if (!enif_inspect_binary(env, argv[0], &bin)) {
return enif_make_badarg(env);
}
// Copy to null-terminated C string
if (bin.size >= sizeof(mib_name)) {
return enif_make_badarg(env);
}
memcpy(mib_name, bin.data, bin.size);
mib_name[bin.size] = '\0';
// Ensure library is initialized
if (!snmp_initialized) {
// Auto-initialize on first call
// net-snmp reads MIB directories from environment variables during init
// MIBDIRS and MIBS must be set BEFORE this code runs
init_snmp("towerops");
netsnmp_init_mib();
read_all_mibs();
snmp_initialized = 1;
}
// Use snmp_parse_oid which automatically searches all loaded MIBs
// and handles both "sysDescr" and "SNMPv2-MIB::sysDescr" formats
if (!snmp_parse_oid(mib_name, objid, &objid_len)) {
return enif_make_tuple2(env,
enif_make_atom(env, "error"),
make_binary_string(env, "Failed to resolve MIB name"));
}
// Convert OID array to dotted decimal string (numeric format)
// Use snprint_objid with -On flag equivalent (numeric only)
int save_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT);
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC);
if (snprint_objid(oid_str, sizeof(oid_str), objid, objid_len) < 0) {
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, save_format);
return enif_make_tuple2(env,
enif_make_atom(env, "error"),
make_binary_string(env, "Failed to format OID"));
}
// Restore original format setting
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, save_format);
// Strip leading dot if present
char* result = oid_str;
if (result[0] == '.') {
result++;
}
// Return OID string as binary (Elixir string)
return make_binary_string(env, result);
}
// NIF function table
static ErlNifFunc nif_funcs[] = {
{"load_mib_directory", 1, load_mib_directory, 0},
{"init_mib_library", 0, init_mib_library, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"resolve_oid", 1, resolve_oid, ERL_NIF_DIRTY_JOB_CPU_BOUND}
};
// NIF initialization
ERL_NIF_INIT(Elixir.ToweropsNative, nif_funcs, NULL, NULL, NULL, NULL)