mirror of
https://github.com/eddyem/small_tel.git
synced 2026-05-07 05:17:00 +03:00
217 lines
7.5 KiB
C
217 lines
7.5 KiB
C
/*
|
|
* This file is part of the weatherdaemon project.
|
|
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
// set flag FORCE_OFF only within FORCEOFF_PAUSE seconds after power loss
|
|
#define FORCEOFF_PAUSE 30
|
|
|
|
#include "weathlib.h"
|
|
#define SENSOR_NAME "SNMP UPS monitor"
|
|
|
|
// https://mibs.observium.org/mib/XUPS-MIB/
|
|
|
|
// gcc $(net-snmp-config --cflags --libs) snmp.c -o snmptest
|
|
|
|
enum{
|
|
NBATSTAT,
|
|
NTONBAT,
|
|
NTREMAIN,
|
|
NBATCAP,
|
|
NSOURCE,
|
|
NONBAT,
|
|
NAMOUNT
|
|
};
|
|
|
|
enum{
|
|
BATT_STAT_UNKN = 1,
|
|
BATT_STAT_NORMAL,
|
|
BATT_STAT_LOW,
|
|
BATT_STAT_DEPLETED,
|
|
BATT_STAT_AMOUNT
|
|
};
|
|
|
|
const char* batt_stat[BATT_STAT_AMOUNT]= {
|
|
"--",
|
|
[BATT_STAT_UNKN] = "Unknown",
|
|
[BATT_STAT_NORMAL] = "Normal",
|
|
[BATT_STAT_LOW] = "Low",
|
|
[BATT_STAT_DEPLETED] = "Depleted"
|
|
};
|
|
|
|
enum{
|
|
SOURCE_OTHER = 1,
|
|
SOURCE_NONE,
|
|
SOURCE_NORMAL,
|
|
SOURCE_BYPASS,
|
|
SOURCE_BATTERY,
|
|
SOURCE_BOOSTER,
|
|
SOURCE_REDUCER,
|
|
SOURCE_AMOUNT
|
|
};
|
|
|
|
const char *sources[SOURCE_AMOUNT] = {
|
|
"--",
|
|
[SOURCE_OTHER] = "Other",
|
|
[SOURCE_NONE] = "None",
|
|
[SOURCE_NORMAL] = "Normal",
|
|
[SOURCE_BYPASS] = "Bypass",
|
|
[SOURCE_BATTERY] = "Battery",
|
|
[SOURCE_BOOSTER] = "Booster",
|
|
[SOURCE_REDUCER] = "Reducer"
|
|
};
|
|
|
|
enum{
|
|
OID_BATT_STATUS, // batt status: 1 - unkn, 2 - normal, 3 - low, 4 - depleted
|
|
OID_BATT_SECONDS_ONBAT, // seconds from ONBAT starts
|
|
OID_BATT_EST_MINUTES, // estimated minutes of work
|
|
OID_BATT_CAPACITY, // capacity of battery
|
|
OID_OUTPUT_SOURCE, // input source: 1 - other, 2 - none, 3 - normal, 4 - bypass, 5 - battery, 6 - booster, 7 - reducer
|
|
OID_AMOUNT
|
|
};
|
|
|
|
static netsnmp_session *snmp_session;
|
|
static oid anOID[OID_AMOUNT][MAX_OID_LEN];
|
|
static size_t anOID_len[OID_AMOUNT];
|
|
|
|
const char *oids[OID_AMOUNT] = {
|
|
[OID_BATT_STATUS] = ".1.3.6.1.2.1.33.1.2.1.0",
|
|
[OID_BATT_SECONDS_ONBAT] = ".1.3.6.1.2.1.33.1.2.2.0",
|
|
[OID_BATT_EST_MINUTES] = ".1.3.6.1.2.1.33.1.2.3.0",
|
|
[OID_BATT_CAPACITY] = ".1.3.6.1.2.1.33.1.2.4.0",
|
|
[OID_OUTPUT_SOURCE] = ".1.3.6.1.2.1.33.1.4.1.0",
|
|
};
|
|
|
|
static const val_t values[NAMOUNT] = {
|
|
[NBATSTAT] = {.sense = VAL_RECOMMENDED, .type = VALT_STRING, .meaning = IS_OTHER, .name = "UPSBTST", .comment = "UPS battery status"},
|
|
[NTONBAT] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "UPSTONBT", .comment = "UPS worked on battery time (s)"},
|
|
[NTREMAIN] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "UPSTREM", .comment = "UPS estimated time on battery (s)"},
|
|
[NBATCAP] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "UPSBATCP", .comment = "UPS battery capacity, percents"},
|
|
[NSOURCE] = {.sense = VAL_RECOMMENDED, .type = VALT_STRING, .meaning = IS_OTHER, .name = "UPSSRC", .comment = "UPS power source"},
|
|
[NONBAT] = {.sense = VAL_FORCEDSHTDN, .type = VALT_UINT, .meaning = IS_FORCEDSHTDN, .name = "UPSONBAT", .comment = "AC power lost, works on battery"},
|
|
};
|
|
|
|
static void *mainthread(void *s){
|
|
double t0 = sl_dtime();
|
|
sensordata_t *sensor = (sensordata_t *)s;
|
|
netsnmp_pdu *pdu, *response;
|
|
while(sensor->fdes > -1){
|
|
//DBG("run");
|
|
pdu = snmp_pdu_create(SNMP_MSG_GET);
|
|
for(int i = 0; i < OID_AMOUNT; ++i)
|
|
snmp_add_null_var(pdu, anOID[i], anOID_len[i]);
|
|
int status = snmp_synch_response(snmp_session, pdu, &response);
|
|
//DBG("status = %d", status);
|
|
if(status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR){
|
|
time_t curt = time(NULL);
|
|
netsnmp_variable_list *vars = response->variables; // OID_BATT_STATUS
|
|
pthread_mutex_lock(&sensor->valmutex);
|
|
int ival = *vars->val.integer;
|
|
if(ival > 0 && ival < BATT_STAT_AMOUNT){
|
|
snprintf(sensor->values[NBATSTAT].value.str, STRT_LEN+1, "%s", batt_stat[ival]);
|
|
}
|
|
vars = vars->next_variable; // OID_BATT_SECONDS_ONBAT
|
|
uint32_t tonbat = (uint32_t) *vars->val.integer;
|
|
sensor->values[NTONBAT].value.u = tonbat;
|
|
vars = vars->next_variable; // OID_BATT_EST_MINUTES
|
|
sensor->values[NTREMAIN].value.u = 60 * (uint32_t) *vars->val.integer;
|
|
vars = vars->next_variable; // OID_BATT_CAPACITY
|
|
sensor->values[NBATCAP].value.u = (uint32_t) *vars->val.integer;
|
|
vars = vars->next_variable; // OID_OUTPUT_SOURCE
|
|
ival = *vars->val.integer;
|
|
if(ival > 0 && ival < SOURCE_AMOUNT)
|
|
snprintf(sensor->values[NSOURCE].value.str, STRT_LEN+1, "%s", sources[ival]);
|
|
if(ival == SOURCE_BATTERY && tonbat > FORCEOFF_PAUSE){
|
|
sensor->values[NONBAT].value.u = 1;
|
|
}else sensor->values[NONBAT].value.u = 0;
|
|
for(int i = 0; i < NAMOUNT; ++i)
|
|
sensor->values[i].time = curt;
|
|
//DBG("times updated to %zd", curt);
|
|
pthread_mutex_unlock(&sensor->valmutex);
|
|
if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
|
|
}else DBG("Error in packet");
|
|
if(response) snmp_free_pdu(response);
|
|
//DBG("sleep");
|
|
while(sl_dtime() - t0 < sensor->tpoll) usleep(500);
|
|
t0 = sl_dtime();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void snmp_kill(sensordata_t *s){
|
|
s->fdes = -1;
|
|
pthread_join(s->thread, NULL);
|
|
snmp_close(snmp_session);
|
|
common_kill(s);
|
|
}
|
|
|
|
int sensor_init(sensordata_t *s){
|
|
FNAME();
|
|
if(!s || !s->path[0]) return FALSE;
|
|
|
|
netsnmp_session session;
|
|
init_snmp("snmpapp");
|
|
|
|
snmp_sess_init(&session);
|
|
session.version = SNMP_VERSION_1;
|
|
session.community = (u_char *)"public";
|
|
session.community_len = strlen((const char *)session.community);
|
|
|
|
DBG("PATH: %s", s->path);
|
|
|
|
const char *colon = strchr(s->path, ':');
|
|
if(colon) ++colon; // omit "N:" in field "N:host"
|
|
session.peername = strdup(colon);
|
|
|
|
snmp_session = snmp_open(&session);
|
|
if(!snmp_session){
|
|
snmp_sess_perror("snmp_open", &session);
|
|
FREE(session.peername);
|
|
return FALSE;
|
|
}
|
|
|
|
s->kill = snmp_kill;
|
|
|
|
snprintf(s->name, NAME_LEN, "%s", SENSOR_NAME);
|
|
s->fdes = 0;
|
|
s->Nvalues = NAMOUNT;
|
|
s->values = MALLOC(val_t, NAMOUNT);
|
|
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
|
|
|
|
DBG("init OIDs");
|
|
for(int i = 0; i < OID_AMOUNT; ++i){
|
|
anOID_len[i] = MAX_OID_LEN;
|
|
if(!read_objid(oids[i], anOID[i], &anOID_len[i])){
|
|
snmp_perror(oids[i]);
|
|
continue;
|
|
}
|
|
DBG("Got OID %s", oids[i]);
|
|
//snmp_add_null_var(snmp_pdu, anOID, anOID_len);
|
|
}
|
|
DBG("Start main thread");
|
|
if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
|
|
pthread_create(&s->thread, NULL, mainthread, (void*)s)){
|
|
s->kill(s);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|