diff --git a/.clang-format b/.clang-format index 19398b4..0ff6811 100644 --- a/.clang-format +++ b/.clang-format @@ -1,9 +1,12 @@ -BasedOnStyle: LLVM UseTab: ForContinuationAndIndentation IndentWidth: 4 TabWidth: 4 AllowShortIfStatementsOnASingleLine: false BreakBeforeBraces: Attach AlignConsecutiveMacros: true - -ColumnLimit: 130 +ColumnLimit: 100 +IndentPPDirectives: AfterHash +SortIncludes: Never +AllowShortEnumsOnASingleLine: false +BinPackArguments: true +InsertBraces: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8386cf1..7e778c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,14 +12,6 @@ jobs: steps: - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: '3.x' - cache: pip - - - name: Install pip packages - run: pip install -r .github/requirements.txt - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y libsnmp-dev @@ -27,20 +19,6 @@ jobs: - name: Make run: make - - name: Start simmulation - run: | - setup-snmpsim-data /tmp/data - snmpsim-command-responder --daemonize --pid-file /tmp/snmp --data-dir=/tmp/data/network/switch --agent-udpv4-endpoint=127.0.0.1:1611 - - - name: Sleep - run: sleep 60s - shell: bash - - - name: check_cisco_health - run: | - chmod 0755 check_cisco_health - ./check_cisco_health -h 127.0.0.1:1611 -c cisco-c3750 -t 30000 - clang-build: runs-on: ubuntu-latest env: @@ -50,30 +28,8 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.x' - cache: pip - - - name: Install pip packages - run: pip install -r .github/requirements.txt - - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y libsnmp-dev - name: Make run: make - - - name: Start simmulation - run: | - setup-snmpsim-data /tmp/data - snmpsim-command-responder --daemonize --pid-file /tmp/snmp --data-dir=/tmp/data/network/switch --agent-udpv4-endpoint=127.0.0.1:1611 - - - name: Sleep - run: sleep 60s - shell: bash - - - name: check_cisco_health - run: | - chmod 0755 check_cisco_health - ./check_cisco_health -h 127.0.0.1:1611 -c cisco-c3750 -t 30000 diff --git a/check_cisco_health.c b/check_cisco_health.c index fcb5e48..1bc58b9 100644 --- a/check_cisco_health.c +++ b/check_cisco_health.c @@ -2,7 +2,7 @@ * * COPYRIGHT: * - * This software is Copyright (c) 2011,2012 NETWAYS GmbH, William Preston + * This software is Copyright (c) 2011-2025 NETWAYS GmbH, William Preston * * * (Except where explicitly superseded by other copyright notices) @@ -90,87 +90,80 @@ struct cisco_env_table { int source; }; -netsnmp_session *start_session(netsnmp_session *, char *, char *); -netsnmp_session *start_session_v3(netsnmp_session *, char *, char *, char *, char *, char *, char *); -int usage(char *); -int addstr(char **, size_t *, const char *, ...); -int parseoids(int, char *, struct OIDStruct *); -void strcpy_nospaces(char *, char *); -int addval(int env, int index, int var, netsnmp_variable_list *); +netsnmp_session *start_session(char * /*community*/, char * /*hostname*/); +netsnmp_session *start_session_v3(char * /*user*/, char * /*auth_proto*/, char * /*auth_pass*/, + char * /*priv_proto*/, char * /*priv_pass*/, char * /*hostname*/); +int usage(char * /*progname*/); +int addstr(char ** /*strp*/, size_t * /*strs*/, const char * /*format*/, ...); +int parseoids(int /*i*/, char * /*oid_list*/, struct OIDStruct * /*query*/); +void strcpy_nospaces(char * /*dest*/, char * /*src*/); +int addval(int env, int index, int var, netsnmp_variable_list * /*result*/); + struct table_list *table_listp = 0; unsigned long timeout = DFLT_TIMEOUT; -int main(int argc, char *argv[]) { - netsnmp_session session, *ss; - netsnmp_pdu *pdu; - netsnmp_pdu *response; - netsnmp_variable_list *vars; - - int status; - int count = 0; - int index = 0; - int var = 0; - int errorflag = 0; - int warnflag = 0; - int endoftable = 0; - int opt; - char *hostname = 0, *community = 0; - char *user = 0, *auth_proto = 0, *auth_pass = 0, *priv_proto = 0, *priv_pass = 0; - - struct OIDStruct *OIDp, *OIDtable; - struct OIDStruct lastOid; /* save the last OID retrieved in case our bulk - get was insufficient */ - - struct table_list *ptr; - - static char *cisco_env[] = {".1.3.6.1.4.1.9.9.13.1"}; - static char *cisco_env_tables[] = {".1.3.6.1.4.1.9.9.13.1.2", ".1.3.6.1.4.1.9.9.13.1.3", ".1.3.6.1.4.1.9.9.13.1.4", - ".1.3.6.1.4.1.9.9.13.1.5"}; - static char *table_names[] = {"Voltage", "Temperature", "Fan", "PSU"}; - - char outstr[MAX_STRING]; - outstr[0] = 0; - char *outstrp = outstr; - size_t outstrsize = sizeof(outstr); +typedef struct { + char *hostname; + char *community; + char *user; + char *auth_proto; + char *auth_pass; + char *priv_proto; + char *priv_pass; +} check_cisco_health_config; + +check_cisco_health_config check_cisco_health_config_init() { + check_cisco_health_config result = { + .hostname = NULL, + .community = strdup("public"), + .user = NULL, + .auth_proto = NULL, + .auth_pass = NULL, + .priv_proto = NULL, + .priv_pass = NULL, + }; + return result; +} - char extstr[MAX_STRING]; - extstr[0] = 0; - char *extstrp = extstr; - size_t extstrsize = sizeof(extstr); +typedef struct { + int errorcode; + check_cisco_health_config config; +} opt_parse_wrapper; - char perfstr[MAX_STRING]; - perfstr[0] = 0; - char *perfstrp = perfstr; - size_t perfstrsize = sizeof(perfstr); +opt_parse_wrapper parse_commandline(int argc, char *argv[]) { + opt_parse_wrapper result = { + .config = check_cisco_health_config_init(), + .errorcode = 0, + }; /* parse options */ - + int opt; while ((opt = getopt(argc, argv, "c:h:j:J:k:K:t:u:?")) != -1) { switch (opt) { case 'c': - community = optarg; + result.config.community = optarg; break; case 'h': - hostname = optarg; + result.config.hostname = optarg; break; case 'j': - auth_proto = optarg; + result.config.auth_proto = optarg; break; case 'J': - auth_pass = optarg; + result.config.auth_pass = optarg; break; case 'k': - priv_proto = optarg; + result.config.priv_proto = optarg; break; case 'K': - priv_pass = optarg; + result.config.priv_pass = optarg; break; case 't': timeout = strtol(optarg, NULL, 10) * 1000UL; break; case 'u': - user = optarg; + result.config.user = optarg; break; case '?': default: @@ -178,23 +171,47 @@ int main(int argc, char *argv[]) { } } - if (!(hostname && (community || user))) { + if (!(result.config.hostname && (result.config.community || result.config.user))) { + result.errorcode = 1; + printf("Missing either hostname or community or user"); + } + + return result; +} + +int main(int argc, char *argv[]) { + opt_parse_wrapper conf_wrap = parse_commandline(argc, argv); + + if (conf_wrap.errorcode != 0) { exit(usage(argv[0])); } + const check_cisco_health_config config = conf_wrap.config; + /* set the MIB variable if it is unset to avoid net-snmp warnings */ if (getenv("MIBS") == NULL) { setenv("MIBS", "", 1); } - if (user) + + netsnmp_session *snmp_session; + + if (config.user) { /* use snmpv3 */ - ss = start_session_v3(&session, user, auth_proto, auth_pass, priv_proto, priv_pass, hostname); - else - ss = start_session(&session, community, hostname); + snmp_session = start_session_v3(config.user, config.auth_proto, config.auth_pass, + config.priv_proto, config.priv_pass, config.hostname); + } else { + snmp_session = start_session(config.community, config.hostname); + } + + static char *cisco_env[] = {".1.3.6.1.4.1.9.9.13.1"}; + static char *cisco_env_tables[] = {".1.3.6.1.4.1.9.9.13.1.2", ".1.3.6.1.4.1.9.9.13.1.3", + ".1.3.6.1.4.1.9.9.13.1.4", ".1.3.6.1.4.1.9.9.13.1.5"}; /* allocate the space for the OIDs */ - OIDp = (struct OIDStruct *)calloc((sizeof(cisco_env) / sizeof(char *)), sizeof(*OIDp)); - OIDtable = (struct OIDStruct *)calloc((sizeof(cisco_env_tables) / sizeof(char *)), sizeof(*OIDtable)); + struct OIDStruct *OIDp = + (struct OIDStruct *)calloc((sizeof(cisco_env) / sizeof(char *)), sizeof(*OIDp)); + struct OIDStruct *OIDtable = + (struct OIDStruct *)calloc((sizeof(cisco_env_tables) / sizeof(char *)), sizeof(*OIDtable)); /* parse the table oids for comparison later */ for (size_t i = 0; i < (sizeof(cisco_env_tables) / sizeof(char *)); i++) { @@ -206,7 +223,13 @@ int main(int argc, char *argv[]) { } } + int index = 0; + int count = 0; + int endoftable = 0; + struct OIDStruct lastOid; /* save the last OID retrieved in case our bulk + get was insufficient */ while (endoftable == 0) { + netsnmp_pdu *pdu; if (count == 0) { pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->non_repeaters = 0; @@ -231,11 +254,11 @@ int main(int argc, char *argv[]) { snmp_add_null_var(pdu, lastOid.name, lastOid.name_len); } - status = snmp_synch_response(ss, pdu, &response); + netsnmp_pdu *response; + int status = snmp_synch_response(snmp_session, pdu, &response); if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) { - - vars = response->variables; + netsnmp_variable_list *vars = response->variables; for (; vars; vars = vars->next_variable) { count++; @@ -250,7 +273,8 @@ int main(int argc, char *argv[]) { memcpy(lastOid.name, vars->name, (vars->name_length * sizeof(oid))); lastOid.name_len = vars->name_length; /* print_objid(lastOid.name, lastOid.name_len); */ - if (vars->name_length < OIDp[0].name_len || (memcmp(OIDp[0].name, vars->name, OIDp[0].name_len * sizeof(oid)))) { + if (vars->name_length < OIDp[0].name_len || + (memcmp(OIDp[0].name, vars->name, OIDp[0].name_len * sizeof(oid)))) { #ifdef DEBUG printf("reached end of table\n"); #endif @@ -261,7 +285,7 @@ int main(int argc, char *argv[]) { for (size_t i = 0; i < (sizeof(cisco_env_tables) / sizeof(char *)); i++) { if (!memcmp(OIDtable[i].name, vars->name, OIDtable[i].name_len * sizeof(oid))) { index = (int)vars->name[(vars->name_length - 1)]; - var = (int)vars->name[(vars->name_length - 2)]; + int var = (int)vars->name[(vars->name_length - 2)]; addval(i, index, var, vars); } } @@ -272,34 +296,49 @@ int main(int argc, char *argv[]) { * FAILURE: print what went wrong! */ - if (status == STAT_SUCCESS) + if (status == STAT_SUCCESS) { printf("Error in packet\nReason: %s\n", snmp_errstring(response->errstat)); - else if (status == STAT_TIMEOUT) - printf("Timeout: No response from %s.\n", session.peername); - else - snmp_sess_perror("snmp_bulkget", ss); + } else if (status == STAT_TIMEOUT) { + printf("Timeout: No response from %s.\n", config.hostname); + } else { + snmp_sess_perror("snmp_bulkget", snmp_session); + } exit(2); } /* * Clean up: * free the response. */ - if (response) + if (response) { snmp_free_pdu(response); + } } free(OIDp); - for (ptr = table_listp; ptr; ptr = ptr->next) { - + int warnflag = 0; + int errorflag = 0; + char outstr[MAX_STRING] = {}; + char *outstrp = outstr; + size_t outstrsize = sizeof(outstr); + char extstr[MAX_STRING] = {}; + char *extstrp = extstr; + size_t extstrsize = sizeof(extstr); + char perfstr[MAX_STRING]; + perfstr[0] = 0; + char *perfstrp = perfstr; + size_t perfstrsize = sizeof(perfstr); + static char *table_names[] = {"Voltage", "Temperature", "Fan", "PSU"}; + for (struct table_list *ptr = table_listp; ptr; ptr = ptr->next) { switch (ptr->table->state) { case 1: addstr(&extstrp, &extstrsize, "[OK] %s(%s)", table_names[ptr->type], ptr->table->descr); if ((ptr->type == 0 || ptr->type == 1) && (ptr->table->value > 0)) { - addstr(&extstrp, &extstrsize, " is %d%c\n", ptr->table->value, (ptr->type ? 'C' : 'V')); + addstr(&extstrp, &extstrsize, " is %d%c\n", ptr->table->value, + (ptr->type ? 'C' : 'V')); /* now add perfdata */ - addstr(&perfstrp, &perfstrsize, " %s_%d=%d%c", table_names[ptr->type], ptr->index, ptr->table->value, - ptr->type ? 'C' : 'V'); + addstr(&perfstrp, &perfstrsize, " %s_%d=%d%c", table_names[ptr->type], ptr->index, + ptr->table->value, ptr->type ? 'C' : 'V'); } else { addstr(&extstrp, &extstrsize, " is normal\n"); } @@ -307,9 +346,11 @@ int main(int argc, char *argv[]) { case 2: warnflag++; addstr(&outstrp, &outstrsize, " %s in state warning", ptr->table->descr); - addstr(&extstrp, &extstrsize, "[WARNING] %s(%s)", table_names[ptr->type], ptr->table->descr); + addstr(&extstrp, &extstrsize, "[WARNING] %s(%s)", table_names[ptr->type], + ptr->table->descr); if (ptr->type == 0 || ptr->type == 1) { - addstr(&extstrp, &extstrsize, " is %d%c\n", ptr->table->value, (ptr->type ? 'C' : 'V')); + addstr(&extstrp, &extstrsize, " is %d%c\n", ptr->table->value, + (ptr->type ? 'C' : 'V')); } else { addstr(&extstrp, &extstrsize, " in state warning\n"); } @@ -318,9 +359,11 @@ int main(int argc, char *argv[]) { case 4: errorflag++; addstr(&outstrp, &outstrsize, " %s in state critical", ptr->table->descr); - addstr(&extstrp, &extstrsize, "[CRITICAL] %s(%s)", table_names[ptr->type], ptr->table->descr); + addstr(&extstrp, &extstrsize, "[CRITICAL] %s(%s)", table_names[ptr->type], + ptr->table->descr); if (ptr->type == 0 || ptr->type == 1) { - addstr(&extstrp, &extstrsize, " is %d%c\n", ptr->table->value, (ptr->type ? 'C' : 'V')); + addstr(&extstrp, &extstrsize, " is %d%c\n", ptr->table->value, + (ptr->type ? 'C' : 'V')); } else { addstr(&extstrp, &extstrsize, " in state critical\n"); } @@ -329,7 +372,8 @@ int main(int argc, char *argv[]) { /* PSU not present */ break; default: - addstr(&extstrp, &extstrsize, "[OK] %s(%s) in state unknown\n", table_names[ptr->type], ptr->table->descr); + addstr(&extstrp, &extstrsize, "[OK] %s(%s) in state unknown\n", table_names[ptr->type], + ptr->table->descr); ptr->table->value = 0; break; } @@ -347,125 +391,131 @@ int main(int argc, char *argv[]) { printf("%s | %s\n%s", outstr, perfstr, extstr); - snmp_close(ss); + snmp_close(snmp_session); SOCK_CLEANUP; return ((errorflag) ? 2 : ((warnflag) ? 1 : 0)); } -netsnmp_session *start_session(netsnmp_session *session, char *community, char *hostname) { - - netsnmp_session *ss; - +netsnmp_session *start_session(char *community, char *hostname) { /* * Initialize the SNMP library */ init_snmp("snmp_bulkget"); /* setup session to hostname */ - snmp_sess_init(session); - session->peername = hostname; + netsnmp_session session = {}; + snmp_sess_init(&session); + session.peername = hostname; /* bulk gets require V2c or later */ - session->version = SNMP_VERSION_2c; + session.version = SNMP_VERSION_2c; - session->community = (u_char *)community; + session.community = (u_char *)community; /*session->community = "public"; */ - session->community_len = strlen(community); - session->timeout = timeout; + session.community_len = strlen(community); + session.timeout = timeout; /* * Open the session */ SOCK_STARTUP; - ss = snmp_open(session); /* establish the session */ + netsnmp_session *result_session = snmp_open(&session); /* establish the session */ - if (!ss) { - snmp_sess_perror("snmp_bulkget", session); + if (!result_session) { + snmp_sess_perror("snmp_bulkget", &session); SOCK_CLEANUP; exit(1); } - return (ss); + return (result_session); } -netsnmp_session *start_session_v3(netsnmp_session *session, char *user, char *auth_proto, char *auth_pass, char *priv_proto, +netsnmp_session *start_session_v3(char *user, char *auth_proto, char *auth_pass, char *priv_proto, char *priv_pass, char *hostname) { - netsnmp_session *ss; - init_snmp("snmp_bulkget"); - snmp_sess_init(session); - session->peername = hostname; + netsnmp_session session = {}; + snmp_sess_init(&session); + session.peername = hostname; - session->version = SNMP_VERSION_3; + session.version = SNMP_VERSION_3; - session->securityName = user; - session->securityModel = SNMP_SEC_MODEL_USM; - session->securityNameLen = strlen(user); + session.securityName = user; + session.securityModel = SNMP_SEC_MODEL_USM; + session.securityNameLen = strlen(user); if (priv_proto && priv_pass) { if (!strcmp(priv_proto, "AES")) { - session->securityPrivProto = snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN); - session->securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; + session.securityPrivProto = + snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN); + session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; } else if (!strcmp(priv_proto, "DES")) { - session->securityPrivProto = snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN); - session->securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; + session.securityPrivProto = + snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN); + session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; } else { printf("Unknown priv protocol %s\n", priv_proto); exit(3); } - session->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; - session->securityPrivKeyLen = USM_PRIV_KU_LEN; + session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; + session.securityPrivKeyLen = USM_PRIV_KU_LEN; } else { - session->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; - session->securityPrivKeyLen = 0; + session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + session.securityPrivKeyLen = 0; } if (auth_proto && auth_pass) { if (!strcmp(auth_proto, "SHA")) { - session->securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN); - session->securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; + session.securityAuthProto = + snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN); + session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; } else if (!strcmp(auth_proto, "MD5")) { - session->securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, USM_AUTH_PROTO_MD5_LEN); - session->securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; + session.securityAuthProto = + snmp_duplicate_objid(usmHMACMD5AuthProtocol, USM_AUTH_PROTO_MD5_LEN); + session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; } else { printf("Unknown auth protocol %s\n", auth_proto); exit(3); } - session->securityAuthKeyLen = USM_AUTH_KU_LEN; + session.securityAuthKeyLen = USM_AUTH_KU_LEN; } else { - session->securityLevel = SNMP_SEC_LEVEL_NOAUTH; - session->securityAuthKeyLen = 0; - session->securityPrivKeyLen = 0; + session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + session.securityAuthKeyLen = 0; + session.securityPrivKeyLen = 0; } - if ((session->securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) || (session->securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV)) { - if (generate_Ku(session->securityAuthProto, session->securityAuthProtoLen, (unsigned char *)auth_pass, strlen(auth_pass), - session->securityAuthKey, &session->securityAuthKeyLen) != SNMPERR_SUCCESS) + if ((session.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) || + (session.securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV)) { + if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, + (unsigned char *)auth_pass, strlen(auth_pass), session.securityAuthKey, + &session.securityAuthKeyLen) != SNMPERR_SUCCESS) { printf("Error generating AUTH sess\n"); - if (session->securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) { - if (generate_Ku(session->securityAuthProto, session->securityAuthProtoLen, (unsigned char *)priv_pass, - strlen(priv_pass), session->securityPrivKey, &session->securityPrivKeyLen) != SNMPERR_SUCCESS) + } + if (session.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) { + if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, + (unsigned char *)priv_pass, strlen(priv_pass), session.securityPrivKey, + &session.securityPrivKeyLen) != SNMPERR_SUCCESS) { printf("Error generating PRIV sess\n"); + } } } - session->timeout = timeout; + session.timeout = timeout; /* * Open the session */ SOCK_STARTUP; - ss = snmp_open(session); /* establish the session */ + netsnmp_session *result_session = snmp_open(&session); /* establish the session */ - if (!ss) { - snmp_sess_perror("snmp_bulkget", session); + if (!result_session) { + snmp_sess_perror("snmp_bulkget", &session); SOCK_CLEANUP; exit(1); } - return (ss); + return (result_session); } int usage(char *progname) { @@ -509,7 +559,6 @@ int addval(int env, int index, int var, netsnmp_variable_list *result) { TEMP = 1, FAN = 2, PSU = 3 - }; /* check if there is already a value */ @@ -615,10 +664,11 @@ int parseoids(int i, char *oid_list, struct OIDStruct *query) { /* only use for strings we already know the size of */ void strcpy_nospaces(char *dest, char *src) { - static unsigned char allowed[256] = "_________________________________!_#_%__()*+,-.-0123456789___=_?@" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^__abcdefghijklmnopqrstuvwxyz{_}________" - "______________________________________________________________________" - "____________________________________________________"; + static unsigned char allowed[256] = + "_________________________________!_#_%__()*+,-.-0123456789___=_?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^__abcdefghijklmnopqrstuvwxyz{_}________" + "______________________________________________________________________" + "____________________________________________________"; while (*src) { *(dest++) = allowed[(unsigned char)*(src++)];