Loading CMakeLists.txt +6 −2 Changes for CMakeLists.txt: 6 added lines, 2 removed lines. Original line number Diff line number Diff line cmake_minimum_required(VERSION 3.16) project(snmpdd VERSION 0.1.0 LANGUAGES CXX) # snmpdd: sends the state of the system as SNMP notifications (traps, # SNMPv2c or v1) to network management stations, every interval and on # snmpdd: sends the state of the system as SNMP notifications (traps: # SNMPv3 with authentication and encryption (USM), SNMPv2c or v1) to # network management stations, every interval and on # events. It collects over sysbus what storaged knows (RAID arrays, LVM # volume groups, SMART) and syslite's services and mount units, and how # full the mounted file systems are; mibs/SNMPDD-MIB.txt describes the Loading @@ -29,6 +30,7 @@ find_package(libconfplus REQUIRED) find_package(tinyxml2 REQUIRED) set(SNMPDD_SYSCONFDIR "/etc/snmpdd" CACHE PATH "configuration directory") set(SNMPDD_STATEDIR "/var/lib/snmpdd" CACHE PATH "SNMPv3 engine ID and boots") set(SNMPDD_MIBDIR "${CMAKE_INSTALL_FULL_DATADIR}/snmp/mibs" CACHE PATH "MIB directory") # needs storaged/api.h (storaged-dev) Loading @@ -38,11 +40,13 @@ add_executable(snmpdd src/collect.cpp src/sender.cpp src/snmp.cpp src/usm.cpp ) target_include_directories(snmpdd PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src) target_compile_definitions(snmpdd PRIVATE SNMPDD_CONFIG="${SNMPDD_SYSCONFDIR}/snmpdd.yaml" SNMPDD_SETTINGS="${SNMPDD_SYSCONFDIR}/settings.xml" SNMPDD_STATE="${SNMPDD_STATEDIR}/engine" ) target_link_libraries(snmpdd PRIVATE sysbus::sysbus netplus::netplus confplus::confplus tinyxml2::tinyxml2) Loading dist/snmpdd.yaml +5 −0 Changes for dist/snmpdd.yaml: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -14,3 +14,8 @@ snmpdd: # seconds statvfs() of all mounted file systems may take together (a # dead NFS server doesn't answer) statvfs_timeout: 5 # SNMPv3: the engine ID of snmpdd, in hex (the NMS knows its users # under it). Empty: made up at the first start and kept in the state # file, which also counts the starts (engineBoots). engine_id: "" state: /var/lib/snmpdd/engine include/snmpdd/api.h +13 −4 Changes for include/snmpdd/api.h: 13 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -34,7 +34,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * snmpdd collects, every interval, what storaged knows about RAID * arrays, LVM volume groups and SMART, the state of syslite's services * and mount units and how full the mounted file systems are, and sends * it as SNMP notifications (traps, SNMPv2c or v1) to every target. Event * it as SNMP notifications (traps: SNMPv3 with authentication and * encryption, SNMPv2c or v1) to every target. Event * notifications go out at once: a new SMART warning (storaged), a failed * self-test, a unit that fails or comes back (syslite), an item whose * status changed since the last report (a disk getting too warm, an Loading @@ -47,14 +48,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * report_smart_attributes (ATA attributes of every disk), * fs_warning, fs_critical (% used), temp_warning, temp_critical * (°C of a disk), service (repeated: the services * reported; none: all), enterprise (read only); then per target: * target (host), port, version (2c|1), community * SetSettings {the same, without enterprise} replaces all of them * reported; none: all), enterprise, engine_id (hex; read only); * then per target: target (host), port, version (3|2c|1), * version 1/2c: community; version 3: user, level (noAuthNoPriv| * authNoPriv|authPriv), auth (SHA|SHA-256|SHA-384), * auth_password_set, priv (AES|AES-256), priv_password_set -- * passwords are never sent back * SetSettings {the same, without enterprise and engine_id; version 3: * auth_password, priv_password instead of *_set: empty keeps the * one the target (host, port, user) has} * -- replaces all of them * * Status: * GetStatus -> enabled, interval, last_report (unix time, 0: never), * next_report (0: off), sent (notifications since start), status * (ok|warning|critical|unknown: the worst item of the last report), * engine_id, engine_boots (SNMPv3), * problem? (repeated: what couldn't be collected); then per * target: target (host:port), sent, error? * SendReport -> sent (notifications) -- collects and sends now; an Loading src/config.cpp +91 −2 Changes for src/config.cpp: 91 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <tinyxml2.h> #include "lang.h" #include "usm.h" namespace snmpdd { Loading Loading @@ -116,6 +117,12 @@ Config loadConfig(const std::string &path) { } if (readValue(conf, "/snmpdd/statvfs_timeout", v) && toInt(v, n) && n > 0 && n <= 300) cfg.statvfsTimeout = (int)n; if (readValue(conf, "/snmpdd/engine_id", v) && !v.empty()) { std::string id; if (!usm::fromHex(v, id)) throw std::runtime_error(path + ": engine_id: 5 to 32 bytes in hex"); cfg.engineId = id; } if (readValue(conf, "/snmpdd/state", v) && !v.empty()) cfg.statePath = v; } catch (const confplus::ConfException &e) { throw std::runtime_error(path + ": " + e.what()); } Loading Loading @@ -165,6 +172,12 @@ bool loadSettings(const std::string &path, Settings &out, std::string *error) { e->QueryIntAttribute("port", &t.port); if (e->Attribute("version")) t.version = e->Attribute("version"); if (e->Attribute("community")) t.community = e->Attribute("community"); if (e->Attribute("user")) t.user = e->Attribute("user"); if (e->Attribute("level")) t.level = e->Attribute("level"); if (e->Attribute("auth")) t.auth = e->Attribute("auth"); if (e->Attribute("auth_password")) t.authPassword = e->Attribute("auth_password"); if (e->Attribute("priv")) t.priv = e->Attribute("priv"); if (e->Attribute("priv_password")) t.privPassword = e->Attribute("priv_password"); out.targets.push_back(t); } std::string bad = validate(out); Loading Loading @@ -205,7 +218,18 @@ bool saveSettings(const std::string &path, const Settings &s, std::string *error e->SetAttribute("host", t.host.c_str()); e->SetAttribute("port", t.port); e->SetAttribute("version", t.version.c_str()); if (t.version != "3") { e->SetAttribute("community", t.community.c_str()); continue; } e->SetAttribute("user", t.user.c_str()); e->SetAttribute("level", t.level.c_str()); if (t.level == "noAuthNoPriv") continue; e->SetAttribute("auth", t.auth.c_str()); e->SetAttribute("auth_password", t.authPassword.c_str()); if (t.level != "authPriv") continue; e->SetAttribute("priv", t.priv.c_str()); e->SetAttribute("priv_password", t.privPassword.c_str()); } tinyxml2::XMLPrinter printer; Loading Loading @@ -258,12 +282,77 @@ std::string validate(const Settings &s) { for (auto &t : s.targets) { if (!validHost(t.host)) return tr("invalid target host \"%1\"", "ungültiger Zielhost \"%1\"", {t.host}); if (t.port < 1 || t.port > 65535) return tr("invalid port for %1", "ungültiger Port für %1", {t.host}); if (t.version != "2c" && t.version != "1") return tr("SNMP version for %1: 2c or 1", "SNMP-Version für %1: 2c oder 1", {t.host}); if (t.version != "2c" && t.version != "1" && t.version != "3") return tr("SNMP version for %1: 3, 2c or 1", "SNMP-Version für %1: 3, 2c oder 1", {t.host}); if (t.version != "3") { if (t.community.empty() || t.community.size() > 255) return tr("community for %1: 1 to 255 characters", "Community für %1: 1 bis 255 Zeichen", {t.host}); continue; } if (t.user.empty() || t.user.size() > 32) return tr("user for %1: 1 to 32 characters", "Benutzer für %1: 1 bis 32 Zeichen", {t.host}); if (t.level != "noAuthNoPriv" && t.level != "authNoPriv" && t.level != "authPriv") return tr("security level for %1: noAuthNoPriv, authNoPriv or authPriv", "Sicherheitsstufe für %1: noAuthNoPriv, authNoPriv oder authPriv", {t.host}); if (t.level == "noAuthNoPriv") continue; usm::Auth a; if (!usm::parseAuth(t.auth, a)) return tr("authentication for %1: SHA, SHA-256 or SHA-384", "Authentifizierung für %1: SHA, SHA-256 oder SHA-384", {t.host}); // RFC 3414 11.2: at least 8 characters if (t.authPassword.size() < 8 || t.authPassword.size() > 255) return tr("authentication password for %1: 8 to 255 characters", "Authentifizierungs-Passwort für %1: 8 bis 255 Zeichen", {t.host}); if (t.level == "authNoPriv") continue; usm::Priv p; if (!usm::parsePriv(t.priv, p)) return tr("encryption for %1: AES or AES-256", "Verschlüsselung für %1: AES oder AES-256", {t.host}); if (p == usm::Priv::Aes256 && a == usm::Auth::Sha1) return tr("AES-256 for %1 needs SHA-256 or SHA-384", "AES-256 für %1 braucht SHA-256 oder SHA-384", {t.host}); if (t.privPassword.size() < 8 || t.privPassword.size() > 255) return tr("encryption password for %1: 8 to 255 characters", "Verschlüsselungs-Passwort für %1: 8 bis 255 Zeichen", {t.host}); } return ""; } std::string Target::key() const { bool v6 = host.find(':') != std::string::npos; return (v6 ? "[" + host + "]" : host) + ":" + std::to_string(port); } bool startEngine(const Config &c, std::string &id, uint32_t &boots, std::string *error) { // the state file: "engine_id <hex>" and "boots <n>" std::string storedId; unsigned long long storedBoots = 0; if (FILE *f = fopen(c.statePath.c_str(), "re")) { char key[32], value[128]; while (fscanf(f, "%31s %127s", key, value) == 2) { if (!strcmp(key, "engine_id")) usm::fromHex(value, storedId); else if (!strcmp(key, "boots")) storedBoots = std::strtoull(value, nullptr, 10); } fclose(f); } uint32_t pen = c.enterprise.arcs.size() > 6 && c.enterprise.arcs[4] == 4 && c.enterprise.arcs[5] == 1 ? c.enterprise.arcs[6] : 0; id = !c.engineId.empty() ? c.engineId : !storedId.empty() ? storedId : usm::newEngineId(pen); // RFC 3414 2.2.2: a new engine ID starts at 1; 2^31-1 stays boots = id != storedId ? 1 : storedBoots >= 2147483647ull ? 2147483647u : (uint32_t)storedBoots + 1; std::string dir = c.statePath.substr(0, c.statePath.rfind('/') == std::string::npos ? 0 : c.statePath.rfind('/')); if (!dir.empty()) mkdir(dir.c_str(), 0700); std::string tmp = c.statePath + ".new"; FILE *f = fopen(tmp.c_str(), "we"); bool ok = f && fprintf(f, "engine_id %s\nboots %u\n", usm::toHex(id).c_str(), boots) > 0; if (f) { ok = ok && fflush(f) == 0 && fsync(fileno(f)) == 0; fclose(f); } if (!ok || rename(tmp.c_str(), c.statePath.c_str()) != 0) { if (error) *error = c.statePath + ": " + strerror(errno); unlink(tmp.c_str()); return false; } return true; } } // namespace snmpdd src/config.h +26 −3 Changes for src/config.h: 26 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -43,7 +43,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * <filesystems warning="85" critical="95"/> * <disks temp_warning="50" temp_critical="60" attributes="yes"/> * <services><service>sshd</service></services> (none: all) * <targets><target host="nms" port="162" version="2c" community="public"/></targets> * <targets> * <target host="nms" port="162" version="2c" community="public"/> * <target host="nms2" port="162" version="3" user="snmpdd" level="authPriv" * auth="SHA-256" auth_password="..." priv="AES" priv_password="..."/> * </targets> * </snmpdd> */ #pragma once Loading @@ -60,13 +64,27 @@ struct Config { snmp::Oid enterprise{1, 3, 6, 1, 4, 1, 99999}; size_t maxMessage = 1472; // bytes of one notification (UDP payload) int statvfsTimeout = 5; // seconds for all file systems together // SNMPv3: the engine ID ("" : made up once and kept in the state // file) and where engineBoots is counted std::string engineId; std::string statePath = SNMPDD_STATE; }; struct Target { std::string host; int port = 162; std::string version = "2c"; // 2c or 1 std::string community = "public"; std::string version = "2c"; // 2c, 1 or 3 std::string community = "public"; // 1, 2c // 3 (USM): level noAuthNoPriv, authNoPriv or authPriv; auth SHA, // SHA-256 or SHA-384; priv AES or AES-256 (this one with SHA-256/384) std::string user; std::string level = "authPriv"; std::string auth = "SHA-256"; std::string authPassword; std::string priv = "AES"; std::string privPassword; std::string key() const; // host:port, as in the status }; struct Settings { Loading @@ -93,4 +111,9 @@ bool saveSettings(const std::string &path, const Settings &s, std::string *error // "" or what is wrong (in the language of the call, see tr()) std::string validate(const Settings &s); // The SNMPv3 engine: the ID (c.engineId, else the one in the state file, // else a new one) and engineBoots, one more than last time; both written // back to the state file. bool startEngine(const Config &c, std::string &id, uint32_t &boots, std::string *error); } // namespace snmpdd Loading
CMakeLists.txt +6 −2 Changes for CMakeLists.txt: 6 added lines, 2 removed lines. Original line number Diff line number Diff line cmake_minimum_required(VERSION 3.16) project(snmpdd VERSION 0.1.0 LANGUAGES CXX) # snmpdd: sends the state of the system as SNMP notifications (traps, # SNMPv2c or v1) to network management stations, every interval and on # snmpdd: sends the state of the system as SNMP notifications (traps: # SNMPv3 with authentication and encryption (USM), SNMPv2c or v1) to # network management stations, every interval and on # events. It collects over sysbus what storaged knows (RAID arrays, LVM # volume groups, SMART) and syslite's services and mount units, and how # full the mounted file systems are; mibs/SNMPDD-MIB.txt describes the Loading @@ -29,6 +30,7 @@ find_package(libconfplus REQUIRED) find_package(tinyxml2 REQUIRED) set(SNMPDD_SYSCONFDIR "/etc/snmpdd" CACHE PATH "configuration directory") set(SNMPDD_STATEDIR "/var/lib/snmpdd" CACHE PATH "SNMPv3 engine ID and boots") set(SNMPDD_MIBDIR "${CMAKE_INSTALL_FULL_DATADIR}/snmp/mibs" CACHE PATH "MIB directory") # needs storaged/api.h (storaged-dev) Loading @@ -38,11 +40,13 @@ add_executable(snmpdd src/collect.cpp src/sender.cpp src/snmp.cpp src/usm.cpp ) target_include_directories(snmpdd PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src) target_compile_definitions(snmpdd PRIVATE SNMPDD_CONFIG="${SNMPDD_SYSCONFDIR}/snmpdd.yaml" SNMPDD_SETTINGS="${SNMPDD_SYSCONFDIR}/settings.xml" SNMPDD_STATE="${SNMPDD_STATEDIR}/engine" ) target_link_libraries(snmpdd PRIVATE sysbus::sysbus netplus::netplus confplus::confplus tinyxml2::tinyxml2) Loading
dist/snmpdd.yaml +5 −0 Changes for dist/snmpdd.yaml: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -14,3 +14,8 @@ snmpdd: # seconds statvfs() of all mounted file systems may take together (a # dead NFS server doesn't answer) statvfs_timeout: 5 # SNMPv3: the engine ID of snmpdd, in hex (the NMS knows its users # under it). Empty: made up at the first start and kept in the state # file, which also counts the starts (engineBoots). engine_id: "" state: /var/lib/snmpdd/engine
include/snmpdd/api.h +13 −4 Changes for include/snmpdd/api.h: 13 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -34,7 +34,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * snmpdd collects, every interval, what storaged knows about RAID * arrays, LVM volume groups and SMART, the state of syslite's services * and mount units and how full the mounted file systems are, and sends * it as SNMP notifications (traps, SNMPv2c or v1) to every target. Event * it as SNMP notifications (traps: SNMPv3 with authentication and * encryption, SNMPv2c or v1) to every target. Event * notifications go out at once: a new SMART warning (storaged), a failed * self-test, a unit that fails or comes back (syslite), an item whose * status changed since the last report (a disk getting too warm, an Loading @@ -47,14 +48,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * report_smart_attributes (ATA attributes of every disk), * fs_warning, fs_critical (% used), temp_warning, temp_critical * (°C of a disk), service (repeated: the services * reported; none: all), enterprise (read only); then per target: * target (host), port, version (2c|1), community * SetSettings {the same, without enterprise} replaces all of them * reported; none: all), enterprise, engine_id (hex; read only); * then per target: target (host), port, version (3|2c|1), * version 1/2c: community; version 3: user, level (noAuthNoPriv| * authNoPriv|authPriv), auth (SHA|SHA-256|SHA-384), * auth_password_set, priv (AES|AES-256), priv_password_set -- * passwords are never sent back * SetSettings {the same, without enterprise and engine_id; version 3: * auth_password, priv_password instead of *_set: empty keeps the * one the target (host, port, user) has} * -- replaces all of them * * Status: * GetStatus -> enabled, interval, last_report (unix time, 0: never), * next_report (0: off), sent (notifications since start), status * (ok|warning|critical|unknown: the worst item of the last report), * engine_id, engine_boots (SNMPv3), * problem? (repeated: what couldn't be collected); then per * target: target (host:port), sent, error? * SendReport -> sent (notifications) -- collects and sends now; an Loading
src/config.cpp +91 −2 Changes for src/config.cpp: 91 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <tinyxml2.h> #include "lang.h" #include "usm.h" namespace snmpdd { Loading Loading @@ -116,6 +117,12 @@ Config loadConfig(const std::string &path) { } if (readValue(conf, "/snmpdd/statvfs_timeout", v) && toInt(v, n) && n > 0 && n <= 300) cfg.statvfsTimeout = (int)n; if (readValue(conf, "/snmpdd/engine_id", v) && !v.empty()) { std::string id; if (!usm::fromHex(v, id)) throw std::runtime_error(path + ": engine_id: 5 to 32 bytes in hex"); cfg.engineId = id; } if (readValue(conf, "/snmpdd/state", v) && !v.empty()) cfg.statePath = v; } catch (const confplus::ConfException &e) { throw std::runtime_error(path + ": " + e.what()); } Loading Loading @@ -165,6 +172,12 @@ bool loadSettings(const std::string &path, Settings &out, std::string *error) { e->QueryIntAttribute("port", &t.port); if (e->Attribute("version")) t.version = e->Attribute("version"); if (e->Attribute("community")) t.community = e->Attribute("community"); if (e->Attribute("user")) t.user = e->Attribute("user"); if (e->Attribute("level")) t.level = e->Attribute("level"); if (e->Attribute("auth")) t.auth = e->Attribute("auth"); if (e->Attribute("auth_password")) t.authPassword = e->Attribute("auth_password"); if (e->Attribute("priv")) t.priv = e->Attribute("priv"); if (e->Attribute("priv_password")) t.privPassword = e->Attribute("priv_password"); out.targets.push_back(t); } std::string bad = validate(out); Loading Loading @@ -205,7 +218,18 @@ bool saveSettings(const std::string &path, const Settings &s, std::string *error e->SetAttribute("host", t.host.c_str()); e->SetAttribute("port", t.port); e->SetAttribute("version", t.version.c_str()); if (t.version != "3") { e->SetAttribute("community", t.community.c_str()); continue; } e->SetAttribute("user", t.user.c_str()); e->SetAttribute("level", t.level.c_str()); if (t.level == "noAuthNoPriv") continue; e->SetAttribute("auth", t.auth.c_str()); e->SetAttribute("auth_password", t.authPassword.c_str()); if (t.level != "authPriv") continue; e->SetAttribute("priv", t.priv.c_str()); e->SetAttribute("priv_password", t.privPassword.c_str()); } tinyxml2::XMLPrinter printer; Loading Loading @@ -258,12 +282,77 @@ std::string validate(const Settings &s) { for (auto &t : s.targets) { if (!validHost(t.host)) return tr("invalid target host \"%1\"", "ungültiger Zielhost \"%1\"", {t.host}); if (t.port < 1 || t.port > 65535) return tr("invalid port for %1", "ungültiger Port für %1", {t.host}); if (t.version != "2c" && t.version != "1") return tr("SNMP version for %1: 2c or 1", "SNMP-Version für %1: 2c oder 1", {t.host}); if (t.version != "2c" && t.version != "1" && t.version != "3") return tr("SNMP version for %1: 3, 2c or 1", "SNMP-Version für %1: 3, 2c oder 1", {t.host}); if (t.version != "3") { if (t.community.empty() || t.community.size() > 255) return tr("community for %1: 1 to 255 characters", "Community für %1: 1 bis 255 Zeichen", {t.host}); continue; } if (t.user.empty() || t.user.size() > 32) return tr("user for %1: 1 to 32 characters", "Benutzer für %1: 1 bis 32 Zeichen", {t.host}); if (t.level != "noAuthNoPriv" && t.level != "authNoPriv" && t.level != "authPriv") return tr("security level for %1: noAuthNoPriv, authNoPriv or authPriv", "Sicherheitsstufe für %1: noAuthNoPriv, authNoPriv oder authPriv", {t.host}); if (t.level == "noAuthNoPriv") continue; usm::Auth a; if (!usm::parseAuth(t.auth, a)) return tr("authentication for %1: SHA, SHA-256 or SHA-384", "Authentifizierung für %1: SHA, SHA-256 oder SHA-384", {t.host}); // RFC 3414 11.2: at least 8 characters if (t.authPassword.size() < 8 || t.authPassword.size() > 255) return tr("authentication password for %1: 8 to 255 characters", "Authentifizierungs-Passwort für %1: 8 bis 255 Zeichen", {t.host}); if (t.level == "authNoPriv") continue; usm::Priv p; if (!usm::parsePriv(t.priv, p)) return tr("encryption for %1: AES or AES-256", "Verschlüsselung für %1: AES oder AES-256", {t.host}); if (p == usm::Priv::Aes256 && a == usm::Auth::Sha1) return tr("AES-256 for %1 needs SHA-256 or SHA-384", "AES-256 für %1 braucht SHA-256 oder SHA-384", {t.host}); if (t.privPassword.size() < 8 || t.privPassword.size() > 255) return tr("encryption password for %1: 8 to 255 characters", "Verschlüsselungs-Passwort für %1: 8 bis 255 Zeichen", {t.host}); } return ""; } std::string Target::key() const { bool v6 = host.find(':') != std::string::npos; return (v6 ? "[" + host + "]" : host) + ":" + std::to_string(port); } bool startEngine(const Config &c, std::string &id, uint32_t &boots, std::string *error) { // the state file: "engine_id <hex>" and "boots <n>" std::string storedId; unsigned long long storedBoots = 0; if (FILE *f = fopen(c.statePath.c_str(), "re")) { char key[32], value[128]; while (fscanf(f, "%31s %127s", key, value) == 2) { if (!strcmp(key, "engine_id")) usm::fromHex(value, storedId); else if (!strcmp(key, "boots")) storedBoots = std::strtoull(value, nullptr, 10); } fclose(f); } uint32_t pen = c.enterprise.arcs.size() > 6 && c.enterprise.arcs[4] == 4 && c.enterprise.arcs[5] == 1 ? c.enterprise.arcs[6] : 0; id = !c.engineId.empty() ? c.engineId : !storedId.empty() ? storedId : usm::newEngineId(pen); // RFC 3414 2.2.2: a new engine ID starts at 1; 2^31-1 stays boots = id != storedId ? 1 : storedBoots >= 2147483647ull ? 2147483647u : (uint32_t)storedBoots + 1; std::string dir = c.statePath.substr(0, c.statePath.rfind('/') == std::string::npos ? 0 : c.statePath.rfind('/')); if (!dir.empty()) mkdir(dir.c_str(), 0700); std::string tmp = c.statePath + ".new"; FILE *f = fopen(tmp.c_str(), "we"); bool ok = f && fprintf(f, "engine_id %s\nboots %u\n", usm::toHex(id).c_str(), boots) > 0; if (f) { ok = ok && fflush(f) == 0 && fsync(fileno(f)) == 0; fclose(f); } if (!ok || rename(tmp.c_str(), c.statePath.c_str()) != 0) { if (error) *error = c.statePath + ": " + strerror(errno); unlink(tmp.c_str()); return false; } return true; } } // namespace snmpdd
src/config.h +26 −3 Changes for src/config.h: 26 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -43,7 +43,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * <filesystems warning="85" critical="95"/> * <disks temp_warning="50" temp_critical="60" attributes="yes"/> * <services><service>sshd</service></services> (none: all) * <targets><target host="nms" port="162" version="2c" community="public"/></targets> * <targets> * <target host="nms" port="162" version="2c" community="public"/> * <target host="nms2" port="162" version="3" user="snmpdd" level="authPriv" * auth="SHA-256" auth_password="..." priv="AES" priv_password="..."/> * </targets> * </snmpdd> */ #pragma once Loading @@ -60,13 +64,27 @@ struct Config { snmp::Oid enterprise{1, 3, 6, 1, 4, 1, 99999}; size_t maxMessage = 1472; // bytes of one notification (UDP payload) int statvfsTimeout = 5; // seconds for all file systems together // SNMPv3: the engine ID ("" : made up once and kept in the state // file) and where engineBoots is counted std::string engineId; std::string statePath = SNMPDD_STATE; }; struct Target { std::string host; int port = 162; std::string version = "2c"; // 2c or 1 std::string community = "public"; std::string version = "2c"; // 2c, 1 or 3 std::string community = "public"; // 1, 2c // 3 (USM): level noAuthNoPriv, authNoPriv or authPriv; auth SHA, // SHA-256 or SHA-384; priv AES or AES-256 (this one with SHA-256/384) std::string user; std::string level = "authPriv"; std::string auth = "SHA-256"; std::string authPassword; std::string priv = "AES"; std::string privPassword; std::string key() const; // host:port, as in the status }; struct Settings { Loading @@ -93,4 +111,9 @@ bool saveSettings(const std::string &path, const Settings &s, std::string *error // "" or what is wrong (in the language of the call, see tr()) std::string validate(const Settings &s); // The SNMPv3 engine: the ID (c.engineId, else the one in the state file, // else a new one) and engineBoots, one more than last time; both written // back to the state file. bool startEngine(const Config &c, std::string &id, uint32_t &boots, std::string *error); } // namespace snmpdd