~iheino+ub/+junk/nut-upsconf-docfix

« back to all changes in this revision

Viewing changes to drivers/snmp-ups.c

  • Committer: Tuomas Heino
  • Author(s): Laurent Bigonville
  • Date: 2014-04-22 20:46:12 UTC
  • Revision ID: iheino+ub@cc.hut.fi-20140422204612-1x2gh3nkezfsdao4
Tags: upstream-2.7.2
ImportĀ upstreamĀ versionĀ 2.7.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  snmp-ups.c - NUT Meta SNMP driver (support different MIBS)
 
2
 *
 
3
 *  Based on NetSNMP API (Simple Network Management Protocol V1-2)
 
4
 *
 
5
 *  Copyright (C)
 
6
 *      2002 - 2014     Arnaud Quette <arnaud.quette@free.fr>
 
7
 *      2002 - 2006     Dmitry Frolov <frolov@riss-telecom.ru>
 
8
 *                      J.W. Hoogervorst <jeroen@hoogervorst.net>
 
9
 *                      Niels Baggesen <niels@baggesen.net>
 
10
 *      2009 - 2010     Arjen de Korte <adkorte-guest@alioth.debian.org>
 
11
 *
 
12
 *  Sponsored by Eaton <http://www.eaton.com>
 
13
 *   and originally by MGE UPS SYSTEMS <http://www.mgeups.com/>
 
14
 *
 
15
 *  This program is free software; you can redistribute it and/or modify
 
16
 *  it under the terms of the GNU General Public License as published by
 
17
 *  the Free Software Foundation; either version 2 of the License, or
 
18
 *  (at your option) any later version.
 
19
 *
 
20
 *  This program is distributed in the hope that it will be useful,
 
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
23
 *  GNU General Public License for more details.
 
24
 *
 
25
 *
 
26
 *  You should have received a copy of the GNU General Public License
 
27
 *  along with this program; if not, write to the Free Software
 
28
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
29
 *
 
30
 */
 
31
 
 
32
#include <limits.h>
 
33
 
 
34
/* NUT SNMP common functions */
 
35
#include "main.h"
 
36
#include "snmp-ups.h"
 
37
#include "parseconf.h"
 
38
 
 
39
/* include all known mib2nut lookup tables */
 
40
#include "apc-mib.h"
 
41
#include "mge-mib.h"
 
42
#include "netvision-mib.h"
 
43
#include "powerware-mib.h"
 
44
#include "eaton-mib.h"
 
45
#include "raritan-pdu-mib.h"
 
46
#include "baytech-mib.h"
 
47
#include "compaq-mib.h"
 
48
#include "bestpower-mib.h"
 
49
#include "cyberpower-mib.h"
 
50
#include "delta_ups-mib.h"
 
51
#include "ietf-mib.h"
 
52
#include "xppc-mib.h"
 
53
 
 
54
/* Address API change */
 
55
#ifndef usmAESPrivProtocol
 
56
#define usmAESPrivProtocol usmAES128PrivProtocol
 
57
#endif
 
58
 
 
59
static mib2nut_info_t *mib2nut[] = {
 
60
        &apc,
 
61
        &mge,
 
62
        &netvision,
 
63
        &powerware,
 
64
        &aphel_genesisII,
 
65
        &aphel_revelation,
 
66
        &eaton_marlin,
 
67
        &pulizzi_switched1,
 
68
        &pulizzi_switched2,
 
69
        &raritan,
 
70
        &baytech,
 
71
        &compaq,
 
72
        &bestpower,
 
73
        &cyberpower,
 
74
        &delta_ups,
 
75
        &xppc,
 
76
        /*
 
77
         * Prepend vendor specific MIB mappings before IETF, so that
 
78
         * if a device supports both IETF and vendor specific MIB,
 
79
         * the vendor specific one takes precedence (when mib=auto)
 
80
         */
 
81
        &ietf,
 
82
        /* end of structure. */
 
83
        NULL
 
84
};
 
85
 
 
86
struct snmp_session g_snmp_sess, *g_snmp_sess_p;
 
87
const char *OID_pwr_status;
 
88
int g_pwr_battery;
 
89
int pollfreq; /* polling frequency */
 
90
int input_phases, output_phases, bypass_phases;
 
91
 
 
92
/* pointer to the Snmp2Nut lookup table */
 
93
mib2nut_info_t *mib2nut_info;
 
94
/* FIXME: to be trashed */
 
95
snmp_info_t *snmp_info;
 
96
alarms_info_t *alarms_info;
 
97
const char *mibname;
 
98
const char *mibvers;
 
99
 
 
100
static void disable_transfer_oids(void);
 
101
 
 
102
#define DRIVER_NAME     "Generic SNMP UPS driver"
 
103
#define DRIVER_VERSION          "0.72"
 
104
 
 
105
/* driver description structure */
 
106
upsdrv_info_t   upsdrv_info = {
 
107
        DRIVER_NAME,
 
108
        DRIVER_VERSION,
 
109
        "Arnaud Quette <arnaud.quette@free.fr>\n" \
 
110
        "Dmitry Frolov <frolov@riss-telecom.ru>\n" \
 
111
        "J.W. Hoogervorst <jeroen@hoogervorst.net>\n" \
 
112
        "Niels Baggesen <niels@baggesen.net>\n" \
 
113
        "Arjen de Korte <adkorte-guest@alioth.debian.org>",
 
114
        DRV_STABLE,
 
115
        { NULL }
 
116
};
 
117
/* FIXME: integrate MIBs info? do the same as for usbhid-ups! */
 
118
 
 
119
time_t lastpoll = 0;
 
120
 
 
121
/* outlet OID index start with 0 or 1,
 
122
 * automatically guessed at the first pass */
 
123
int outlet_index_base = -1;
 
124
 
 
125
/* sysOID location */
 
126
#define SYSOID_OID      ".1.3.6.1.2.1.1.2.0"
 
127
 
 
128
/* ---------------------------------------------
 
129
 * driver functions implementations
 
130
 * --------------------------------------------- */
 
131
void upsdrv_initinfo(void)
 
132
{
 
133
        snmp_info_t *su_info_p;
 
134
 
 
135
        upsdebugx(1, "SNMP UPS driver : entering upsdrv_initinfo()");
 
136
 
 
137
        dstate_setinfo("driver.version.data", "%s MIB %s", mibname, mibvers);
 
138
 
 
139
        /* add instant commands to the info database.
 
140
         * outlet commands are processed later, during initial walk */
 
141
        for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++)
 
142
        {
 
143
                su_info_p->flags |= SU_FLAG_OK;
 
144
                if ((SU_TYPE(su_info_p) == SU_TYPE_CMD)
 
145
                        && !(su_info_p->flags & SU_OUTLET)) {
 
146
                        /* first check that this OID actually exists */
 
147
                        if (nut_snmp_get(su_info_p->OID) != NULL) {
 
148
                                dstate_addcmd(su_info_p->info_type);
 
149
                                upsdebugx(1, "upsdrv_initinfo(): adding command '%s'", su_info_p->info_type);
 
150
                        }
 
151
                }
 
152
        }
 
153
 
 
154
        if (testvar("notransferoids"))
 
155
                disable_transfer_oids();
 
156
 
 
157
        /* initialize all other INFO_ fields from list */
 
158
        if (snmp_ups_walk(SU_WALKMODE_INIT))
 
159
                dstate_dataok();
 
160
        else
 
161
                dstate_datastale();
 
162
 
 
163
        /* setup handlers for instcmd and setvar functions */
 
164
        upsh.setvar = su_setvar;
 
165
        upsh.instcmd = su_instcmd;
 
166
}
 
167
 
 
168
void upsdrv_updateinfo(void)
 
169
{
 
170
        upsdebugx(1,"SNMP UPS driver : entering upsdrv_updateinfo()");
 
171
 
 
172
        /* only update every pollfreq */
 
173
        /* FIXME: only update status (SU_STATUS_*), Ć  la usbhid-ups, in between */
 
174
        if (time(NULL) > (lastpoll + pollfreq)) {
 
175
 
 
176
                status_init();
 
177
 
 
178
                /* update all dynamic info fields */
 
179
                if (snmp_ups_walk(SU_WALKMODE_UPDATE))
 
180
                        dstate_dataok();
 
181
                else
 
182
                        dstate_datastale();
 
183
 
 
184
                status_commit();
 
185
 
 
186
                /* store timestamp */
 
187
                lastpoll = time(NULL);
 
188
        }
 
189
}
 
190
 
 
191
void upsdrv_shutdown(void)
 
192
{
 
193
        /*
 
194
        This driver will probably never support this. In order to
 
195
        be any use, the driver should be called near the end of
 
196
        the system halt script. By that time we in all likelyhood
 
197
        we won't have network capabilities anymore, so we could
 
198
        never send this command to the UPS. This is not an error,
 
199
        but a limitation of the interface used.
 
200
        */
 
201
 
 
202
        upsdebugx(1, "upsdrv_shutdown...");
 
203
 
 
204
        /* Try to shutdown with delay */
 
205
        if (su_instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) {
 
206
                /* Shutdown successful */
 
207
                return;
 
208
        }
 
209
 
 
210
        /* If the above doesn't work, try shutdown.reboot */
 
211
        if (su_instcmd("shutdown.reboot", NULL) == STAT_INSTCMD_HANDLED) {
 
212
                /* Shutdown successful */
 
213
                return;
 
214
        }
 
215
 
 
216
        /* If the above doesn't work, try load.off.delay */
 
217
        if (su_instcmd("load.off.delay", NULL) == STAT_INSTCMD_HANDLED) {
 
218
                /* Shutdown successful */
 
219
                return;
 
220
        }
 
221
 
 
222
        fatalx(EXIT_FAILURE, "Shutdown failed!");
 
223
}
 
224
 
 
225
void upsdrv_help(void)
 
226
{
 
227
        upsdebugx(1, "entering upsdrv_help");
 
228
}
 
229
 
 
230
/* list flags and values that you want to receive via -x */
 
231
void upsdrv_makevartable(void)
 
232
{
 
233
        upsdebugx(1, "entering upsdrv_makevartable()");
 
234
 
 
235
        addvar(VAR_VALUE, SU_VAR_MIBS,
 
236
                "Set MIB compliance (default=ietf, allowed: mge,apcc,netvision,pw,cpqpower,...)");
 
237
        addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_COMMUNITY,
 
238
                "Set community name (default=public)");
 
239
        addvar(VAR_VALUE, SU_VAR_VERSION,
 
240
                "Set SNMP version (default=v1, allowed v2c)");
 
241
        addvar(VAR_VALUE, SU_VAR_POLLFREQ,
 
242
                "Set polling frequency in seconds, to reduce network flow (default=30)");
 
243
        addvar(VAR_FLAG, "notransferoids",
 
244
                "Disable transfer OIDs (use on APCC Symmetras)");
 
245
        addvar(VAR_VALUE, SU_VAR_SECLEVEL,
 
246
                "Set the securityLevel used for SNMPv3 messages (default=noAuthNoPriv, allowed: authNoPriv,authPriv)");
 
247
        addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_SECNAME,
 
248
                "Set the securityName used for authenticated SNMPv3 messages (no default)");
 
249
        addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_AUTHPASSWD,
 
250
                "Set the authentication pass phrase used for authenticated SNMPv3 messages (no default)");
 
251
        addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_PRIVPASSWD,
 
252
                "Set  the privacy pass phrase used for encrypted SNMPv3 messages (no default)");
 
253
        addvar(VAR_VALUE, SU_VAR_AUTHPROT,
 
254
                "Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages (default=MD5)");
 
255
        addvar(VAR_VALUE, SU_VAR_PRIVPROT,
 
256
                "Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages (default=DES)");
 
257
}
 
258
 
 
259
void upsdrv_initups(void)
 
260
{
 
261
        snmp_info_t *su_info_p;
 
262
        char model[SU_INFOSIZE];
 
263
        bool_t status;
 
264
        const char *mibs;
 
265
 
 
266
        upsdebugx(1, "SNMP UPS driver : entering upsdrv_initups()");
 
267
 
 
268
        /* Retrieve user's parameters */
 
269
        mibs = testvar(SU_VAR_MIBS) ? getval(SU_VAR_MIBS) : "auto";
 
270
 
 
271
        /* init SNMP library, etc... */
 
272
        nut_snmp_init(progname, device_path);
 
273
 
 
274
        /* FIXME: first test if the device is reachable to avoid timeouts! */
 
275
 
 
276
        /* Load the SNMP to NUT translation data */
 
277
        load_mib2nut(mibs);
 
278
 
 
279
        /* init polling frequency */
 
280
        if (getval(SU_VAR_POLLFREQ))
 
281
                pollfreq = atoi(getval(SU_VAR_POLLFREQ));
 
282
        else
 
283
                pollfreq = DEFAULT_POLLFREQ;
 
284
 
 
285
        /* Get UPS Model node to see if there's a MIB */
 
286
        su_info_p = su_find_info("ups.model");
 
287
        status = nut_snmp_get_str(su_info_p->OID, model, sizeof(model), NULL);
 
288
 
 
289
        if (status == TRUE)
 
290
                upslogx(0, "Detected %s on host %s (mib: %s %s)",
 
291
                         model, device_path, mibname, mibvers);
 
292
        else
 
293
                fatalx(EXIT_FAILURE, "%s MIB wasn't found on %s", mibs, g_snmp_sess.peername);
 
294
                /* FIXME: "No supported device detected" */
 
295
 
 
296
        if (su_find_info("load.off.delay")) {
 
297
                /* Adds default with a delay value of '0' (= immediate) */
 
298
                dstate_addcmd("load.off");
 
299
        }
 
300
 
 
301
        if (su_find_info("load.on.delay")) {
 
302
                /* Adds default with a delay value of '0' (= immediate) */
 
303
                dstate_addcmd("load.on");
 
304
        }
 
305
 
 
306
        if (su_find_info("load.off.delay") && su_find_info("load.on.delay")) {
 
307
                /* Add composite instcmds (require setting multiple OID values) */
 
308
                dstate_addcmd("shutdown.return");
 
309
                dstate_addcmd("shutdown.stayoff");
 
310
        }
 
311
}
 
312
 
 
313
void upsdrv_cleanup(void)
 
314
{
 
315
        nut_snmp_cleanup();
 
316
}
 
317
 
 
318
/* -----------------------------------------------------------
 
319
 * SNMP functions.
 
320
 * ----------------------------------------------------------- */
 
321
 
 
322
void nut_snmp_init(const char *type, const char *hostname)
 
323
{
 
324
        char *ns_options = NULL;
 
325
        const char *community, *version;
 
326
        const char *secLevel = NULL, *authPassword, *privPassword;
 
327
        const char *authProtocol, *privProtocol;
 
328
 
 
329
        upsdebugx(2, "SNMP UPS driver : entering nut_snmp_init(%s)", type);
 
330
 
 
331
        /* Force numeric OIDs resolution (ie, do not resolve to textual names)
 
332
         * This is mostly for the convenience of debug output */
 
333
        ns_options = snmp_out_toggle_options("n");
 
334
        if (ns_options != NULL) {
 
335
                upsdebugx(2, "Failed to enable numeric OIDs resolution");
 
336
        }
 
337
 
 
338
        /* Initialize the SNMP library */
 
339
        init_snmp(type);
 
340
 
 
341
        /* Initialize session */
 
342
        snmp_sess_init(&g_snmp_sess);
 
343
 
 
344
        g_snmp_sess.peername = xstrdup(hostname);
 
345
 
 
346
        /* Retrieve user parameters */
 
347
        version = testvar(SU_VAR_VERSION) ? getval(SU_VAR_VERSION) : "v1";
 
348
        
 
349
        if ((strcmp(version, "v1") == 0) || (strcmp(version, "v2c") == 0)) {
 
350
                g_snmp_sess.version = (strcmp(version, "v1") == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
 
351
                community = testvar(SU_VAR_COMMUNITY) ? getval(SU_VAR_COMMUNITY) : "public";
 
352
                g_snmp_sess.community = (unsigned char *)xstrdup(community);
 
353
                g_snmp_sess.community_len = strlen(community);
 
354
        }
 
355
        else if (strcmp(version, "v3") == 0) {
 
356
                /* SNMP v3 related init */
 
357
                g_snmp_sess.version = SNMP_VERSION_3;
 
358
 
 
359
                /* Security level */
 
360
                if (testvar(SU_VAR_SECLEVEL)) {
 
361
                        secLevel = getval(SU_VAR_SECLEVEL);
 
362
 
 
363
                        if (strcmp(secLevel, "noAuthNoPriv") == 0)
 
364
                                g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
 
365
                        else if (strcmp(secLevel, "authNoPriv") == 0)
 
366
                                g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
 
367
                        else if (strcmp(secLevel, "authPriv") == 0)
 
368
                                g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
 
369
                        else
 
370
                                fatalx(EXIT_FAILURE, "Bad SNMPv3 securityLevel: %s", secLevel);
 
371
                }
 
372
                else
 
373
                        g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
 
374
 
 
375
                /* Security name */
 
376
                if (testvar(SU_VAR_SECNAME)) {
 
377
                        g_snmp_sess.securityName = xstrdup(getval(SU_VAR_SECNAME));
 
378
                        g_snmp_sess.securityNameLen = strlen(g_snmp_sess.securityName);
 
379
                }
 
380
                else
 
381
                        fatalx(EXIT_FAILURE, "securityName is required for SNMPv3");
 
382
 
 
383
                /* Process mandatory fields, based on the security level */
 
384
                authPassword = testvar(SU_VAR_AUTHPASSWD) ? getval(SU_VAR_AUTHPASSWD) : NULL;
 
385
                privPassword = testvar(SU_VAR_PRIVPASSWD) ? getval(SU_VAR_PRIVPASSWD) : NULL;
 
386
 
 
387
                switch (g_snmp_sess.securityLevel) {
 
388
                        case SNMP_SEC_LEVEL_AUTHNOPRIV:
 
389
                                if (authPassword == NULL)
 
390
                                        fatalx(EXIT_FAILURE, "authPassword is required for SNMPv3 in %s mode", secLevel);
 
391
                                break;
 
392
                        case SNMP_SEC_LEVEL_AUTHPRIV:
 
393
                                if ((authPassword == NULL) || (privPassword == NULL))
 
394
                                        fatalx(EXIT_FAILURE, "authPassword and privPassword are required for SNMPv3 in %s mode", secLevel);
 
395
                                break;
 
396
                        default:
 
397
                        case SNMP_SEC_LEVEL_NOAUTH:
 
398
                                /* nothing else needed */
 
399
                                break;
 
400
                }
 
401
 
 
402
                /* Process authentication protocol and key */
 
403
                g_snmp_sess.securityAuthKeyLen = USM_AUTH_KU_LEN;
 
404
                authProtocol = testvar(SU_VAR_AUTHPROT) ? getval(SU_VAR_AUTHPROT) : "MD5";
 
405
 
 
406
                if (strcmp(authProtocol, "MD5") == 0) {
 
407
                        g_snmp_sess.securityAuthProto = usmHMACMD5AuthProtocol;
 
408
                        g_snmp_sess.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
 
409
                }
 
410
                else if (strcmp(authProtocol, "SHA") == 0) {
 
411
                        g_snmp_sess.securityAuthProto = usmHMACSHA1AuthProtocol;
 
412
                        g_snmp_sess.securityAuthProtoLen = sizeof(usmHMACSHA1AuthProtocol)/sizeof(oid);
 
413
                }
 
414
                else
 
415
                        fatalx(EXIT_FAILURE, "Bad SNMPv3 authProtocol: %s", authProtocol);
 
416
 
 
417
                /* set the authentication key to a MD5/SHA1 hashed version of our
 
418
                 * passphrase (must be at least 8 characters long) */
 
419
                if(g_snmp_sess.securityLevel != SNMP_SEC_LEVEL_NOAUTH) {
 
420
                        if (generate_Ku(g_snmp_sess.securityAuthProto,
 
421
                                g_snmp_sess.securityAuthProtoLen,
 
422
                                (u_char *) authPassword, strlen(authPassword),
 
423
                                g_snmp_sess.securityAuthKey,
 
424
                                &g_snmp_sess.securityAuthKeyLen) !=
 
425
                                SNMPERR_SUCCESS) {
 
426
                                fatalx(EXIT_FAILURE, "Error generating Ku from authentication pass phrase");
 
427
                        }
 
428
                }
 
429
 
 
430
                privProtocol = testvar(SU_VAR_PRIVPROT) ? getval(SU_VAR_PRIVPROT) : "DES";
 
431
 
 
432
                if (strcmp(privProtocol, "DES") == 0) {
 
433
                        g_snmp_sess.securityPrivProto = usmDESPrivProtocol;
 
434
                        g_snmp_sess.securityPrivProtoLen =  sizeof(usmDESPrivProtocol)/sizeof(oid);
 
435
                }
 
436
                else if (strcmp(privProtocol, "AES") == 0) {
 
437
                        g_snmp_sess.securityPrivProto = usmAESPrivProtocol;
 
438
                        g_snmp_sess.securityPrivProtoLen =  sizeof(usmAESPrivProtocol)/sizeof(oid);
 
439
                }
 
440
                else
 
441
                        fatalx(EXIT_FAILURE, "Bad SNMPv3 authProtocol: %s", authProtocol);
 
442
 
 
443
                /* set the privacy key to a MD5/SHA1 hashed version of our
 
444
                 * passphrase (must be at least 8 characters long) */
 
445
                if(g_snmp_sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
 
446
                        g_snmp_sess.securityPrivKeyLen = USM_PRIV_KU_LEN;
 
447
                        if (generate_Ku(g_snmp_sess.securityAuthProto,
 
448
                                g_snmp_sess.securityAuthProtoLen,
 
449
                                (u_char *) privPassword, strlen(privPassword),
 
450
                                g_snmp_sess.securityPrivKey,
 
451
                                &g_snmp_sess.securityPrivKeyLen) !=
 
452
                                SNMPERR_SUCCESS) {
 
453
                                fatalx(EXIT_FAILURE, "Error generating Ku from privacy pass phrase");
 
454
                        }
 
455
                }
 
456
        }
 
457
        else
 
458
                fatalx(EXIT_FAILURE, "Bad SNMP version: %s", version);
 
459
 
 
460
        /* Open the session */
 
461
        SOCK_STARTUP; /* MS Windows wrapper, not really needed on Unix! */
 
462
        g_snmp_sess_p = snmp_open(&g_snmp_sess);        /* establish the session */
 
463
        if (g_snmp_sess_p == NULL) {
 
464
                nut_snmp_perror(&g_snmp_sess, 0, NULL, "nut_snmp_init: snmp_open");
 
465
                fatalx(EXIT_FAILURE, "Unable to establish communication");
 
466
        }
 
467
}
 
468
 
 
469
void nut_snmp_cleanup(void)
 
470
{
 
471
        /* close snmp session. */
 
472
        if (g_snmp_sess_p) {
 
473
                snmp_close(g_snmp_sess_p);
 
474
                g_snmp_sess_p = NULL;
 
475
        }
 
476
        SOCK_CLEANUP; /* wrapper not needed on Unix! */
 
477
}
 
478
 
 
479
/* Free a struct snmp_pdu * returned by nut_snmp_walk */
 
480
void nut_snmp_free(struct snmp_pdu ** array_to_free)
 
481
{
 
482
        struct snmp_pdu ** current_element;
 
483
 
 
484
        current_element = array_to_free;
 
485
 
 
486
        while (*current_element != NULL) {
 
487
                snmp_free_pdu(*current_element);
 
488
                current_element++;
 
489
        }
 
490
 
 
491
        free( array_to_free );
 
492
}
 
493
 
 
494
/* Return a NULL terminated array of snmp_pdu * */
 
495
struct snmp_pdu **nut_snmp_walk(const char *OID, int max_iteration)
 
496
{
 
497
        int status;
 
498
        struct snmp_pdu *pdu, *response = NULL;
 
499
        oid name[MAX_OID_LEN];
 
500
        size_t name_len = MAX_OID_LEN;
 
501
        oid * current_name;
 
502
        size_t current_name_len;
 
503
        static unsigned int numerr = 0;
 
504
        int nb_iteration = 0;
 
505
        struct snmp_pdu ** ret_array = NULL;
 
506
        int type = SNMP_MSG_GET;
 
507
 
 
508
        upsdebugx(3, "nut_snmp_walk(%s)", OID);
 
509
 
 
510
        /* create and send request. */
 
511
        if (!snmp_parse_oid(OID, name, &name_len)) {
 
512
                upsdebugx(2, "[%s] nut_snmp_walk: %s: %s",
 
513
                        upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno));
 
514
                return NULL;
 
515
        }
 
516
 
 
517
        current_name = name;
 
518
        current_name_len = name_len;
 
519
 
 
520
        while( nb_iteration < max_iteration ) {
 
521
                /* Going to a shorter OID means we are outside our sub-tree */
 
522
                if( current_name_len < name_len ) {
 
523
                        break;
 
524
                }
 
525
 
 
526
                pdu = snmp_pdu_create(type);
 
527
 
 
528
                if (pdu == NULL) {
 
529
                        fatalx(EXIT_FAILURE, "Not enough memory");
 
530
                }
 
531
 
 
532
                snmp_add_null_var(pdu, current_name, current_name_len);
 
533
 
 
534
                status = snmp_synch_response(g_snmp_sess_p, pdu, &response);
 
535
 
 
536
                if (!response) {
 
537
                        break;
 
538
                }
 
539
 
 
540
                if (!((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR))) {
 
541
                        if (mibname == NULL) {
 
542
                                /* We are probing for proper mib - ignore errors */
 
543
                                snmp_free_pdu(response);
 
544
                                return NULL;
 
545
                        }
 
546
 
 
547
                        numerr++;
 
548
 
 
549
                        if ((numerr == SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) {
 
550
                                upslogx(LOG_WARNING, "[%s] Warning: excessive poll "
 
551
                                                "failures, limiting error reporting",
 
552
                                                upsname?upsname:device_name);
 
553
                        }
 
554
 
 
555
                        if ((numerr < SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) {
 
556
                                if (type == SNMP_MSG_GETNEXT) {
 
557
                                        upsdebugx(2, "=> No more OID, walk complete");
 
558
                                }
 
559
                                else {
 
560
                                        nut_snmp_perror(g_snmp_sess_p, status, response,
 
561
                                                        "nut_snmp_walk: %s", OID);
 
562
                                }
 
563
                        }
 
564
 
 
565
                        snmp_free_pdu(response);
 
566
                        break;
 
567
                } else {
 
568
                        numerr = 0;
 
569
                }
 
570
 
 
571
                nb_iteration++;
 
572
                /* +1 is for the terminating NULL */
 
573
                ret_array = realloc(ret_array,sizeof(struct snmp_pdu*)*(nb_iteration+1));
 
574
                ret_array[nb_iteration-1] = response;
 
575
                ret_array[nb_iteration]=NULL;
 
576
 
 
577
                current_name = response->variables->name;
 
578
                current_name_len = response->variables->name_length;
 
579
 
 
580
                type = SNMP_MSG_GETNEXT;
 
581
        }
 
582
 
 
583
        return ret_array;
 
584
}
 
585
 
 
586
struct snmp_pdu *nut_snmp_get(const char *OID)
 
587
{
 
588
        struct snmp_pdu ** pdu_array;
 
589
        struct snmp_pdu * ret_pdu;
 
590
 
 
591
        if (OID == NULL)
 
592
                return NULL;
 
593
 
 
594
        upsdebugx(3, "nut_snmp_get(%s)", OID);
 
595
 
 
596
        pdu_array = nut_snmp_walk(OID,1);
 
597
 
 
598
        if(pdu_array == NULL) {
 
599
                return NULL;
 
600
        }
 
601
 
 
602
        ret_pdu = snmp_clone_pdu(*pdu_array);
 
603
 
 
604
        nut_snmp_free(pdu_array);
 
605
 
 
606
        return ret_pdu;
 
607
}
 
608
 
 
609
static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_lkp_t *oid2info) {
 
610
        size_t len = 0;
 
611
 
 
612
        /* zero out buffer. */
 
613
        memset(buf, 0, buf_len);
 
614
 
 
615
        switch (pdu->variables->type) {
 
616
        case ASN_OCTET_STR:
 
617
        case ASN_OPAQUE:
 
618
                len = pdu->variables->val_len > buf_len - 1 ?
 
619
                        buf_len - 1 : pdu->variables->val_len;
 
620
                memcpy(buf, pdu->variables->val.string, len);
 
621
                buf[len] = '\0';
 
622
                break;
 
623
        case ASN_INTEGER:
 
624
        case ASN_COUNTER:
 
625
        case ASN_GAUGE:
 
626
                if(oid2info) {
 
627
                        const char *str;
 
628
                        if((str=su_find_infoval(oid2info, *pdu->variables->val.integer))) {
 
629
                                strncpy(buf, str, buf_len-1);
 
630
                        }
 
631
                        else {
 
632
                                strncpy(buf, "UNKNOWN", buf_len-1);
 
633
                        }
 
634
                        buf[buf_len-1]='\0';
 
635
                }
 
636
                else {
 
637
                        len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer);
 
638
                }
 
639
                break;
 
640
        case ASN_TIMETICKS:
 
641
                /* convert timeticks to seconds */
 
642
                len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer / 100);
 
643
                break;
 
644
        case ASN_OBJECT_ID:
 
645
                len = snprint_objid (buf, buf_len, pdu->variables->val.objid, pdu->variables->val_len / sizeof(oid));
 
646
                break;
 
647
        default:
 
648
                return FALSE;
 
649
        }
 
650
 
 
651
        return TRUE;
 
652
}
 
653
 
 
654
bool_t nut_snmp_get_str(const char *OID, char *buf, size_t buf_len, info_lkp_t *oid2info)
 
655
{
 
656
        struct snmp_pdu *pdu;
 
657
        bool_t ret;
 
658
 
 
659
        upsdebugx(3, "Entering nut_snmp_get_str()");
 
660
 
 
661
        pdu = nut_snmp_get(OID);
 
662
        if (pdu == NULL)
 
663
                return FALSE;
 
664
 
 
665
        ret = decode_str(pdu,buf,buf_len,oid2info);
 
666
 
 
667
        if(ret == FALSE) {
 
668
                upsdebugx(2, "[%s] unhandled ASN 0x%x received from %s",
 
669
                        upsname?upsname:device_name, pdu->variables->type, OID);
 
670
        }
 
671
 
 
672
        snmp_free_pdu(pdu);
 
673
 
 
674
        return ret;
 
675
}
 
676
 
 
677
bool_t nut_snmp_get_int(const char *OID, long *pval)
 
678
{
 
679
        struct snmp_pdu *pdu;
 
680
        long value;
 
681
        char *buf;
 
682
 
 
683
        pdu = nut_snmp_get(OID);
 
684
        if (pdu == NULL)
 
685
                return FALSE;
 
686
 
 
687
        switch (pdu->variables->type) {
 
688
        case ASN_OCTET_STR:
 
689
        case ASN_OPAQUE:
 
690
                buf = xmalloc(pdu->variables->val_len + 1);
 
691
                memcpy(buf, pdu->variables->val.string, pdu->variables->val_len);
 
692
                buf[pdu->variables->val_len] = '\0';
 
693
                value = strtol(buf, NULL, 0);
 
694
                free(buf);
 
695
                break;
 
696
        case ASN_INTEGER:
 
697
        case ASN_COUNTER:
 
698
        case ASN_GAUGE:
 
699
                value = *pdu->variables->val.integer;
 
700
                break;
 
701
        case ASN_TIMETICKS:
 
702
                /* convert timeticks to seconds */
 
703
                value = *pdu->variables->val.integer / 100;
 
704
                break;
 
705
        default:
 
706
                upslogx(LOG_ERR, "[%s] unhandled ASN 0x%x received from %s",
 
707
                        upsname?upsname:device_name, pdu->variables->type, OID);
 
708
                return FALSE;
 
709
                break;
 
710
        }
 
711
 
 
712
        snmp_free_pdu(pdu);
 
713
 
 
714
        if (pval != NULL)
 
715
                *pval = value;
 
716
 
 
717
        return TRUE;
 
718
}
 
719
 
 
720
bool_t nut_snmp_set(const char *OID, char type, const char *value)
 
721
{
 
722
        int status;
 
723
        bool_t ret = FALSE;
 
724
        struct snmp_pdu *pdu, *response = NULL;
 
725
        oid name[MAX_OID_LEN];
 
726
        size_t name_len = MAX_OID_LEN;
 
727
 
 
728
        upsdebugx(1, "entering nut_snmp_set (%s, %c, %s)", OID, type, value);
 
729
 
 
730
        if (!snmp_parse_oid(OID, name, &name_len)) {
 
731
                upslogx(LOG_ERR, "[%s] nut_snmp_set: %s: %s",
 
732
                        upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno));
 
733
                return FALSE;
 
734
        }
 
735
 
 
736
        pdu = snmp_pdu_create(SNMP_MSG_SET);
 
737
        if (pdu == NULL)
 
738
                fatalx(EXIT_FAILURE, "Not enough memory");
 
739
 
 
740
        if (snmp_add_var(pdu, name, name_len, type, value)) {
 
741
                upslogx(LOG_ERR, "[%s] nut_snmp_set: %s: %s",
 
742
                        upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno));
 
743
 
 
744
                return FALSE;
 
745
        }
 
746
 
 
747
        status = snmp_synch_response(g_snmp_sess_p, pdu, &response);
 
748
 
 
749
        if ((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR))
 
750
                ret = TRUE;
 
751
        else
 
752
                nut_snmp_perror(g_snmp_sess_p, status, response,
 
753
                        "nut_snmp_set: can't set %s", OID);
 
754
 
 
755
        snmp_free_pdu(response);
 
756
        return ret;
 
757
}
 
758
 
 
759
bool_t nut_snmp_set_str(const char *OID, const char *value)
 
760
{
 
761
        return nut_snmp_set(OID, 's', value);
 
762
}
 
763
 
 
764
bool_t nut_snmp_set_int(const char *OID, long value)
 
765
{
 
766
        char buf[SU_BUFSIZE];
 
767
 
 
768
        snprintf(buf, sizeof(buf), "%ld", value);
 
769
        return nut_snmp_set(OID, 'i', buf);
 
770
}
 
771
 
 
772
bool_t nut_snmp_set_time(const char *OID, long value)
 
773
{
 
774
        char buf[SU_BUFSIZE];
 
775
 
 
776
        snprintf(buf, SU_BUFSIZE, "%ld", value * 100);
 
777
        return nut_snmp_set(OID, 't', buf);
 
778
}
 
779
 
 
780
/* log descriptive SNMP error message. */
 
781
void nut_snmp_perror(struct snmp_session *sess, int status,
 
782
        struct snmp_pdu *response, const char *fmt, ...)
 
783
{
 
784
        va_list va;
 
785
        int cliberr, snmperr;
 
786
        char *snmperrstr;
 
787
        char buf[SU_LARGEBUF];
 
788
 
 
789
        va_start(va, fmt);
 
790
        vsnprintf(buf, sizeof(buf), fmt, va);
 
791
        va_end(va);
 
792
 
 
793
        if (response == NULL) {
 
794
                snmp_error(sess, &cliberr, &snmperr, &snmperrstr);
 
795
                upslogx(LOG_ERR, "[%s] %s: %s",
 
796
                        upsname?upsname:device_name, buf, snmperrstr);
 
797
                free(snmperrstr);
 
798
        } else if (status == STAT_SUCCESS) {
 
799
                switch (response->errstat)
 
800
                {
 
801
                case SNMP_ERR_NOERROR:
 
802
                        break;
 
803
                case SNMP_ERR_NOSUCHNAME:       /* harmless */
 
804
                        upsdebugx(2, "[%s] %s: %s",
 
805
                                         upsname?upsname:device_name, buf, snmp_errstring(response->errstat));
 
806
                        break;
 
807
                default:
 
808
                        upslogx(LOG_ERR, "[%s] %s: Error in packet: %s",
 
809
                                upsname?upsname:device_name, buf, snmp_errstring(response->errstat));
 
810
                        break;
 
811
                }
 
812
        } else if (status == STAT_TIMEOUT) {
 
813
                upslogx(LOG_ERR, "[%s] %s: Timeout: no response from %s",
 
814
                        upsname?upsname:device_name, buf, sess->peername);
 
815
        } else {
 
816
                snmp_sess_error(sess, &cliberr, &snmperr, &snmperrstr);
 
817
                upslogx(LOG_ERR, "[%s] %s: %s",
 
818
                        upsname?upsname:device_name, buf, snmperrstr);
 
819
                free(snmperrstr);
 
820
        }
 
821
}
 
822
 
 
823
/* -----------------------------------------------------------
 
824
 * utility functions.
 
825
 * ----------------------------------------------------------- */
 
826
 
 
827
/* deal with APCC weirdness on Symmetras */
 
828
static void disable_transfer_oids(void)
 
829
{
 
830
        snmp_info_t *su_info_p;
 
831
 
 
832
        upslogx(LOG_INFO, "Disabling transfer OIDs");
 
833
 
 
834
        for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) {
 
835
                if (!strcasecmp(su_info_p->info_type, "input.transfer.low")) {
 
836
                        su_info_p->flags &= ~SU_FLAG_OK;
 
837
                        continue;
 
838
                }
 
839
 
 
840
                if (!strcasecmp(su_info_p->info_type, "input.transfer.high")) {
 
841
                        su_info_p->flags &= ~SU_FLAG_OK;
 
842
                        continue;
 
843
                }
 
844
        }
 
845
}
 
846
 
 
847
/* universal function to add or update info element. */
 
848
void su_setinfo(snmp_info_t *su_info_p, const char *value)
 
849
{
 
850
        upsdebugx(1, "entering su_setinfo(%s)", su_info_p->info_type);
 
851
 
 
852
        if (SU_TYPE(su_info_p) == SU_TYPE_CMD)
 
853
                return;
 
854
 
 
855
        if (strcasecmp(su_info_p->info_type, "ups.status"))
 
856
        {
 
857
                if (value != NULL)
 
858
                        dstate_setinfo(su_info_p->info_type, "%s", value);
 
859
                else
 
860
                        dstate_setinfo(su_info_p->info_type, "%s", su_info_p->dfl);
 
861
 
 
862
                dstate_setflags(su_info_p->info_type, su_info_p->info_flags);
 
863
                dstate_setaux(su_info_p->info_type, su_info_p->info_len);
 
864
 
 
865
                /* Commit the current value, to avoid staleness with huge
 
866
                 * data collections on slow devices */
 
867
                 dstate_dataok();
 
868
        }
 
869
}
 
870
 
 
871
void su_status_set(snmp_info_t *su_info_p, long value)
 
872
{
 
873
        const char *info_value = NULL;
 
874
 
 
875
        upsdebugx(2, "SNMP UPS driver : entering su_status_set()");
 
876
 
 
877
        if ((info_value = su_find_infoval(su_info_p->oid2info, value)) != NULL)
 
878
        {
 
879
                if (strcmp(info_value, "")) {
 
880
                        status_set(info_value);
 
881
                }
 
882
        }
 
883
        /* TODO: else */
 
884
}
 
885
 
 
886
/* find info element definition in my info array. */
 
887
snmp_info_t *su_find_info(const char *type)
 
888
{
 
889
        snmp_info_t *su_info_p;
 
890
 
 
891
        for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++)
 
892
                if (!strcasecmp(su_info_p->info_type, type)) {
 
893
                        upsdebugx(3, "su_find_info: \"%s\" found", type);
 
894
                        return su_info_p;
 
895
                }
 
896
 
 
897
        upsdebugx(3, "su_find_info: unknown info type (%s)", type);
 
898
        return NULL;
 
899
}
 
900
 
 
901
/* Try to find the MIB using sysOID matching.
 
902
 * Return a pointer to a mib2nut definition if found, NULL otherwise */
 
903
mib2nut_info_t *match_sysoid()
 
904
{
 
905
        char sysOID_buf[LARGEBUF];
 
906
        oid device_sysOID[MAX_OID_LEN];
 
907
        size_t device_sysOID_len = MAX_OID_LEN;
 
908
        oid mib2nut_sysOID[MAX_OID_LEN];
 
909
        size_t mib2nut_sysOID_len = MAX_OID_LEN;
 
910
        int i;
 
911
 
 
912
        /* Retrieve sysOID value of this device */
 
913
        if (nut_snmp_get_str(SYSOID_OID, sysOID_buf, sizeof(sysOID_buf), NULL))
 
914
        {
 
915
                upsdebugx(1, "match_sysoid: device sysOID value = %s", sysOID_buf);
 
916
 
 
917
                /* Build OIDs for comparison */
 
918
                if (!read_objid(sysOID_buf, device_sysOID, &device_sysOID_len))
 
919
                {
 
920
                        upsdebugx(2, "match_sysoid: can't build device_sysOID %s: %s",
 
921
                                sysOID_buf, snmp_api_errstring(snmp_errno));
 
922
 
 
923
                        return FALSE;
 
924
                }
 
925
 
 
926
                /* Now, iterate on mib2nut definitions */
 
927
                for (i = 0; mib2nut[i] != NULL; i++)
 
928
                {
 
929
                        upsdebugx(1, "match_sysoid: checking MIB %s", mib2nut[i]->mib_name);
 
930
 
 
931
                        if (mib2nut[i]->sysOID == NULL)
 
932
                                continue;
 
933
 
 
934
                        /* Clear variables */
 
935
                        memset(mib2nut_sysOID, 0, MAX_OID_LEN);
 
936
                        mib2nut_sysOID_len = MAX_OID_LEN;
 
937
 
 
938
                        if (!read_objid(mib2nut[i]->sysOID, mib2nut_sysOID, &mib2nut_sysOID_len))
 
939
                        {
 
940
                                upsdebugx(2, "match_sysoid: can't build OID %s: %s",
 
941
                                        sysOID_buf, snmp_api_errstring(snmp_errno));
 
942
 
 
943
                                /* Try to continue anyway! */
 
944
                                continue;
 
945
                        }
 
946
                        /* Now compare these */
 
947
                        upsdebugx(1, "match_sysoid: comparing %s with %s", sysOID_buf, mib2nut[i]->sysOID);
 
948
                        if (!netsnmp_oid_equals(device_sysOID, device_sysOID_len, mib2nut_sysOID, mib2nut_sysOID_len))
 
949
                        {
 
950
                                upsdebugx(2, "match_sysoid: sysOID matches MIB '%s'!", mib2nut[i]->mib_name);
 
951
                                return mib2nut[i];
 
952
                        }
 
953
                }
 
954
                /* Yell all to call for user report */
 
955
                upslogx(LOG_ERR, "No matching MIB found for sysOID '%s'!\n" \
 
956
                        "Please report it to NUT developers, with an 'upsc' output for your device.\n" \
 
957
                        "Going back to the classic MIB detection method.",
 
958
                        sysOID_buf);
 
959
        }
 
960
        else
 
961
                upsdebugx(2, "Can't get sysOID value");
 
962
 
 
963
        return NULL;
 
964
}
 
965
 
 
966
/* Load the right snmp_info_t structure matching mib parameter */
 
967
bool_t load_mib2nut(const char *mib)
 
968
{
 
969
        int     i;
 
970
        char    buf[LARGEBUF];
 
971
        mib2nut_info_t *m2n = NULL;
 
972
 
 
973
        upsdebugx(2, "SNMP UPS driver : entering load_mib2nut(%s)", mib);
 
974
 
 
975
        /* First, try to match against sysOID, if no MIB was provided.
 
976
         * This should speed up init stage
 
977
         * (Note: sysOID points the device main MIB entry point) */
 
978
        if (!strcmp(mib, "auto"))
 
979
        {
 
980
                upsdebugx(1, "trying the new match_sysoid() method");
 
981
                m2n = match_sysoid();
 
982
        }
 
983
 
 
984
        /* Otherwise, revert to the classic method */
 
985
        if (m2n == NULL)
 
986
        {
 
987
                for (i = 0; mib2nut[i] != NULL; i++) {
 
988
                        /* Is there already a MIB name provided? */
 
989
                        if (strcmp(mib, "auto") && strcmp(mib, mib2nut[i]->mib_name)) {
 
990
                                continue;
 
991
                        }
 
992
                        upsdebugx(1, "load_mib2nut: trying classic method with '%s' mib", mib2nut[i]->mib_name);
 
993
 
 
994
                        /* Classic method: test an OID specific to this MIB */
 
995
                        if (!nut_snmp_get_str(mib2nut[i]->oid_auto_check, buf, sizeof(buf), NULL)) {
 
996
                                continue;
 
997
                        }
 
998
                        /* MIB found */
 
999
                        m2n = mib2nut[i];
 
1000
                        break;
 
1001
                }
 
1002
        }
 
1003
 
 
1004
        /* Store the result, if any */
 
1005
        if (m2n != NULL)
 
1006
        {
 
1007
                snmp_info = m2n->snmp_info;
 
1008
                OID_pwr_status = m2n->oid_pwr_status;
 
1009
                mibname = m2n->mib_name;
 
1010
                mibvers = m2n->mib_version;
 
1011
                alarms_info = m2n->alarms_info;
 
1012
                upsdebugx(1, "load_mib2nut: using %s mib", mibname);
 
1013
                return TRUE;
 
1014
        }
 
1015
 
 
1016
        /* Did we find something or is it really an unknown mib */
 
1017
        if (strcmp(mib, "auto") != 0) {
 
1018
                fatalx(EXIT_FAILURE, "Unknown mibs value: %s", mib);
 
1019
        } else {
 
1020
                fatalx(EXIT_FAILURE, "No supported device detected");
 
1021
        }
 
1022
}
 
1023
 
 
1024
/* find the OID value matching that INFO_* value */
 
1025
long su_find_valinfo(info_lkp_t *oid2info, const char* value)
 
1026
{
 
1027
        info_lkp_t *info_lkp;
 
1028
 
 
1029
        for (info_lkp = oid2info; (info_lkp != NULL) &&
 
1030
                (strcmp(info_lkp->info_value, "NULL")); info_lkp++) {
 
1031
 
 
1032
                if (!(strcmp(info_lkp->info_value, value))) {
 
1033
                        upsdebugx(1, "su_find_valinfo: found %s (value: %s)",
 
1034
                                        info_lkp->info_value, value);
 
1035
 
 
1036
                        return info_lkp->oid_value;
 
1037
                }
 
1038
        }
 
1039
        upsdebugx(1, "su_find_valinfo: no matching INFO_* value for this OID value (%s)", value);
 
1040
        return -1;
 
1041
}
 
1042
 
 
1043
/* find the INFO_* value matching that OID value */
 
1044
const char *su_find_infoval(info_lkp_t *oid2info, long value)
 
1045
{
 
1046
        info_lkp_t *info_lkp;
 
1047
 
 
1048
        for (info_lkp = oid2info; (info_lkp != NULL) &&
 
1049
                (strcmp(info_lkp->info_value, "NULL")) && (info_lkp->info_value != NULL); info_lkp++) {
 
1050
 
 
1051
                if (info_lkp->oid_value == value) {
 
1052
                        upsdebugx(1, "su_find_infoval: found %s (value: %ld)",
 
1053
                                        info_lkp->info_value, value);
 
1054
 
 
1055
                        return info_lkp->info_value;
 
1056
                }
 
1057
        }
 
1058
        upsdebugx(1, "su_find_infoval: no matching INFO_* value for this OID value (%ld)", value);
 
1059
        return NULL;
 
1060
}
 
1061
 
 
1062
static void disable_competition(snmp_info_t *entry)
 
1063
{
 
1064
        snmp_info_t     *p;
 
1065
 
 
1066
        for(p=snmp_info; p->info_type!=NULL; p++) {
 
1067
                if(p!=entry && !strcmp(p->info_type, entry->info_type)) {
 
1068
                        upsdebugx(2, "disable_competition: disabling %s %s",
 
1069
                                        p->info_type, p->OID);
 
1070
                        p->flags &= ~SU_FLAG_OK;
 
1071
                }
 
1072
        }
 
1073
}
 
1074
 
 
1075
/* instantiate an snmp_info_t from a template.
 
1076
 * mostly (only?) useful for outlet templates.
 
1077
 * Note: remember to adapt info_type, OID and optionaly dfl */
 
1078
snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance)
 
1079
{
 
1080
        /* sanity check */
 
1081
        if (info_template == NULL)
 
1082
                return NULL;
 
1083
 
 
1084
        if (new_instance == NULL)
 
1085
                new_instance = (snmp_info_t *)xmalloc(sizeof(snmp_info_t));
 
1086
 
 
1087
        new_instance->info_type = (char *)xmalloc(SU_INFOSIZE);
 
1088
        if (info_template->OID != NULL)
 
1089
                new_instance->OID = (char *)xmalloc(SU_INFOSIZE);
 
1090
        else
 
1091
                new_instance->OID = NULL;
 
1092
        new_instance->info_flags = info_template->info_flags;
 
1093
        new_instance->info_len = info_template->info_len;
 
1094
        /* FIXME: check if we need to adapt this one... */
 
1095
        new_instance->dfl = info_template->dfl;
 
1096
        new_instance->flags = info_template->flags;
 
1097
        new_instance->oid2info = info_template->oid2info;
 
1098
        new_instance->setvar = info_template->setvar;
 
1099
 
 
1100
        return new_instance;
 
1101
}
 
1102
 
 
1103
/* free a dynamically allocated snmp_info_t.
 
1104
 * mostly (only?) useful for outlet templates */
 
1105
void free_info(snmp_info_t *su_info_p)
 
1106
{
 
1107
        /* sanity check */
 
1108
        if (su_info_p == NULL)
 
1109
                return;
 
1110
 
 
1111
        if (su_info_p->info_type != NULL)
 
1112
                free ((char *)su_info_p->info_type);
 
1113
 
 
1114
        if (su_info_p->OID != NULL)
 
1115
                free ((char *)su_info_p->OID);
 
1116
 
 
1117
        free (su_info_p);
 
1118
}
 
1119
 
 
1120
/* return the base SNMP index (0 or 1) to start outlet iteration on the MIB,
 
1121
 * based on a test using a template OID */
 
1122
int base_snmp_outlet_index(const char *OID_template)
 
1123
{
 
1124
        int base_index = outlet_index_base;
 
1125
        char test_OID[SU_INFOSIZE];
 
1126
 
 
1127
        if (outlet_index_base == -1)
 
1128
        {
 
1129
                /* not initialised yet */
 
1130
                for (base_index = 0 ; base_index < 2 ; base_index++) {
 
1131
                        sprintf(test_OID, OID_template, base_index);
 
1132
                        if (nut_snmp_get(test_OID) != NULL)
 
1133
                                break;
 
1134
                }
 
1135
                outlet_index_base = base_index;
 
1136
        }
 
1137
        upsdebugx(3, "base_snmp_outlet_index: %i", outlet_index_base);
 
1138
        return base_index;
 
1139
}
 
1140
 
 
1141
/* return the NUT offset (increment) based on outlet_index_base
 
1142
 * ie (outlet_index_base == 0) => increment +1
 
1143
 *    (outlet_index_base == 1) => increment +0 */
 
1144
int base_nut_outlet_offset(void)
 
1145
{
 
1146
        return (outlet_index_base==0)?1:0;
 
1147
}
 
1148
 
 
1149
/* try to determine the number of outlets, using a template definition,
 
1150
 * that we walk, until we can't get anymore values */
 
1151
static int guestimate_outlet_count(const char *OID_template)
 
1152
{
 
1153
        int base_index = 0;
 
1154
        char test_OID[SU_INFOSIZE];
 
1155
        int base_count;
 
1156
 
 
1157
        upsdebugx(1, "guestimate_outlet_count(%s)", OID_template);
 
1158
 
 
1159
        /* Determine if OID index starts from 0 or 1? */
 
1160
        sprintf(test_OID, OID_template, base_index);
 
1161
        if (nut_snmp_get(test_OID) == NULL)
 
1162
                base_index++;
 
1163
 
 
1164
        /* Now, actually iterate */
 
1165
        for (base_count = 0 ;  ; base_count++) {
 
1166
                sprintf(test_OID, OID_template, base_index + base_count);
 
1167
                if (nut_snmp_get(test_OID) == NULL)
 
1168
                        break;
 
1169
        }
 
1170
 
 
1171
        upsdebugx(3, "guestimate_outlet_count: %i", base_count);
 
1172
        return base_count;
 
1173
}
 
1174
 
 
1175
/* process a single data from a walk */
 
1176
bool_t get_and_process_data(int mode, snmp_info_t *su_info_p)
 
1177
{
 
1178
        bool_t status = FALSE;
 
1179
 
 
1180
        upsdebugx(1, "getting data: %s (%s)", su_info_p->info_type, su_info_p->OID);
 
1181
 
 
1182
        /* ok, update this element. */
 
1183
        status = su_ups_get(su_info_p);
 
1184
 
 
1185
        /* set stale flag if data is stale, clear if not. */
 
1186
        if (status == TRUE) {
 
1187
                if (su_info_p->flags & SU_FLAG_STALE) {
 
1188
                        upslogx(LOG_INFO, "[%s] snmp_ups_walk: data resumed for %s",
 
1189
                                upsname?upsname:device_name, su_info_p->info_type);
 
1190
                        su_info_p->flags &= ~SU_FLAG_STALE;
 
1191
                }
 
1192
                if(su_info_p->flags & SU_FLAG_UNIQUE) {
 
1193
                        /* We should be the only provider of this */
 
1194
                        disable_competition(su_info_p);
 
1195
                        su_info_p->flags &= ~SU_FLAG_UNIQUE;
 
1196
                }
 
1197
                dstate_dataok();
 
1198
        } else {
 
1199
                if (mode == SU_WALKMODE_INIT) {
 
1200
                        /* handle unsupported vars */
 
1201
                        su_info_p->flags &= ~SU_FLAG_OK;
 
1202
                } else  {
 
1203
                        if (!(su_info_p->flags & SU_FLAG_STALE)) {
 
1204
                                upslogx(LOG_INFO, "[%s] snmp_ups_walk: data stale for %s",
 
1205
                                        upsname?upsname:device_name, su_info_p->info_type);
 
1206
                                su_info_p->flags |= SU_FLAG_STALE;
 
1207
                        }
 
1208
                        dstate_datastale();
 
1209
                }
 
1210
        }
 
1211
        return status;
 
1212
}
 
1213
 
 
1214
/* walk ups variables and set elements of the info array. */
 
1215
bool_t snmp_ups_walk(int mode)
 
1216
{
 
1217
        static unsigned long iterations = 0;
 
1218
        snmp_info_t *su_info_p;
 
1219
        bool_t status = FALSE;
 
1220
 
 
1221
        for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) {
 
1222
 
 
1223
                /* Check if we are asked to stop (reactivity++) */
 
1224
                if (exit_flag != 0)
 
1225
                        return TRUE;
 
1226
 
 
1227
                /* skip instcmd, not linked to outlets */
 
1228
                if ((SU_TYPE(su_info_p) == SU_TYPE_CMD)
 
1229
                        && !(su_info_p->flags & SU_OUTLET)) {
 
1230
                        upsdebugx(1, "SU_CMD_MASK => %s", su_info_p->OID);
 
1231
                        continue;
 
1232
                }
 
1233
                /* skip elements we shouldn't show */
 
1234
                if (!(su_info_p->flags & SU_FLAG_OK))
 
1235
                        continue;
 
1236
 
 
1237
                /* skip static elements in update mode */
 
1238
                if (mode == SU_WALKMODE_UPDATE &&
 
1239
                                su_info_p->flags & SU_FLAG_STATIC)
 
1240
                        continue;
 
1241
 
 
1242
                /* set default value if we cannot fetch it */
 
1243
                /* and set static flag on this element.
 
1244
                 * not applicable to outlets (need SU_FLAG_STATIC tagging) */
 
1245
                if ((su_info_p->flags & SU_FLAG_ABSENT)
 
1246
                        && !(su_info_p->flags & SU_OUTLET)) {
 
1247
                        if (mode == SU_WALKMODE_INIT) {
 
1248
                                if (su_info_p->dfl) {
 
1249
                                        /* Set default value if we cannot fetch it from ups. */
 
1250
                                        su_setinfo(su_info_p, NULL);
 
1251
                                }
 
1252
                                su_info_p->flags |= SU_FLAG_STATIC;
 
1253
                        }
 
1254
                        continue;
 
1255
                }
 
1256
 
 
1257
                /* check stale elements only on each PN_STALE_RETRY iteration. */
 
1258
                if ((su_info_p->flags & SU_FLAG_STALE) &&
 
1259
                                (iterations % SU_STALE_RETRY) != 0)
 
1260
                        continue;
 
1261
 
 
1262
                if (su_info_p->flags & SU_INPHASES) {
 
1263
                        upsdebugx(1, "Check input_phases");
 
1264
                        if (input_phases == 0) {
 
1265
                                continue;
 
1266
                        }
 
1267
                        if (su_info_p->flags & SU_INPUT_1) {
 
1268
                                if (input_phases == 1) {
 
1269
                                        upsdebugx(1, "input_phases is 1");
 
1270
                                        su_info_p->flags &= ~SU_INPHASES;
 
1271
                                } else {
 
1272
                                        upsdebugx(1, "input_phases is not 1");
 
1273
                                        su_info_p->flags &= ~SU_FLAG_OK;
 
1274
                                        continue;
 
1275
                                }
 
1276
                        } else if (su_info_p->flags & SU_INPUT_3) {
 
1277
                            if (input_phases == 3) {
 
1278
                                        upsdebugx(1, "input_phases is 3");
 
1279
                                        su_info_p->flags &= ~SU_INPHASES;
 
1280
                                } else {
 
1281
                                        upsdebugx(1, "input_phases is not 3");
 
1282
                                        su_info_p->flags &= ~SU_FLAG_OK;
 
1283
                                        continue;
 
1284
                                }
 
1285
                        } else {
 
1286
                                upsdebugx(1, "input_phases is %d", input_phases);
 
1287
                        }
 
1288
                }
 
1289
 
 
1290
                if (su_info_p->flags & SU_OUTPHASES) {
 
1291
                        upsdebugx(1, "Check output_phases");
 
1292
                        if (output_phases == 0) {
 
1293
                                continue;
 
1294
                        }
 
1295
                        if (su_info_p->flags & SU_OUTPUT_1) {
 
1296
                                if (output_phases == 1) {
 
1297
                                        upsdebugx(1, "output_phases is 1");
 
1298
                                        su_info_p->flags &= ~SU_OUTPHASES;
 
1299
                                } else {
 
1300
                                        upsdebugx(1, "output_phases is not 1");
 
1301
                                        su_info_p->flags &= ~SU_FLAG_OK;
 
1302
                                        continue;
 
1303
                                }
 
1304
                        } else if (su_info_p->flags & SU_OUTPUT_3) {
 
1305
                                if (output_phases == 3) {
 
1306
                                        upsdebugx(1, "output_phases is 3");
 
1307
                                        su_info_p->flags &= ~SU_OUTPHASES;
 
1308
                                } else {
 
1309
                                        upsdebugx(1, "output_phases is not 3");
 
1310
                                        su_info_p->flags &= ~SU_FLAG_OK;
 
1311
                                        continue;
 
1312
                                }
 
1313
                        } else {
 
1314
                                upsdebugx(1, "output_phases is %d", output_phases);
 
1315
                        }
 
1316
                }
 
1317
 
 
1318
                if (su_info_p->flags & SU_BYPPHASES) {
 
1319
                        upsdebugx(1, "Check bypass_phases");
 
1320
                        if (bypass_phases == 0) {
 
1321
                                continue;
 
1322
                        }
 
1323
                        if (su_info_p->flags & SU_BYPASS_1) {
 
1324
                                if (bypass_phases == 1) {
 
1325
                                        upsdebugx(1, "bypass_phases is 1");
 
1326
                                        su_info_p->flags &= ~SU_BYPPHASES;
 
1327
                                } else {
 
1328
                                        upsdebugx(1, "bypass_phases is not 1");
 
1329
                                        su_info_p->flags &= ~SU_FLAG_OK;
 
1330
                                        continue;
 
1331
                                }
 
1332
                        } else if (su_info_p->flags & SU_BYPASS_3) {
 
1333
                                if (input_phases == 3) {
 
1334
                                        upsdebugx(1, "bypass_phases is 3");
 
1335
                                        su_info_p->flags &= ~SU_BYPPHASES;
 
1336
                                } else {
 
1337
                                        upsdebugx(1, "bypass_phases is not 3");
 
1338
                                        su_info_p->flags &= ~SU_FLAG_OK;
 
1339
                                        continue;
 
1340
                                }
 
1341
                        } else {
 
1342
                                upsdebugx(1, "bypass_phases is %d", bypass_phases);
 
1343
                        }
 
1344
                }
 
1345
 
 
1346
                /* process outlet template definition */
 
1347
                if (su_info_p->flags & SU_OUTLET) {
 
1348
                        upsdebugx(1, "outlet template definition found (%s)...", su_info_p->info_type);
 
1349
                        int cur_outlet_number = 1;
 
1350
                        int cur_nut_index = 0;
 
1351
                        int outlet_count = 0;
 
1352
                        snmp_info_t cur_info_p;
 
1353
 
 
1354
                        if(dstate_getinfo("outlet.count") == NULL) {
 
1355
                                /* FIXME: should we disable it?
 
1356
                                 * su_info_p->flags &= ~SU_FLAG_OK;
 
1357
                                 * or rely on guestimation? */
 
1358
                                if ((outlet_count = guestimate_outlet_count(su_info_p->OID)) == -1) {
 
1359
                                        /* Failed */
 
1360
                                        continue;
 
1361
                                }
 
1362
                                else {
 
1363
                                        /* Publish the count estimation */
 
1364
                                        dstate_setinfo("outlet.count", "%i", outlet_count);
 
1365
                                }
 
1366
                        }
 
1367
                        else {
 
1368
                                outlet_count = atoi(dstate_getinfo("outlet.count"));
 
1369
                        }
 
1370
 
 
1371
                        /* Only instantiate outlets if needed! */
 
1372
                        if (outlet_count > 0) {
 
1373
                                /* general init of data using the template */
 
1374
                                instantiate_info(su_info_p, &cur_info_p);
 
1375
 
 
1376
                                for (cur_outlet_number = base_snmp_outlet_index(su_info_p->OID) ;
 
1377
                                                cur_outlet_number < (outlet_count + base_snmp_outlet_index(su_info_p->OID)) ;
 
1378
                                                cur_outlet_number++)
 
1379
                                {
 
1380
                                        cur_nut_index = cur_outlet_number + base_nut_outlet_offset();
 
1381
                                        sprintf((char*)cur_info_p.info_type, su_info_p->info_type,
 
1382
                                                        cur_nut_index);
 
1383
 
 
1384
                                        /* check if default value is also a template */
 
1385
                                        if ((cur_info_p.dfl != NULL) &&
 
1386
                                                (strstr(su_info_p->dfl, "%i") != NULL)) {
 
1387
                                                cur_info_p.dfl = (char *)xmalloc(SU_INFOSIZE);
 
1388
                                                sprintf((char *)cur_info_p.dfl, su_info_p->dfl, cur_nut_index);
 
1389
                                        }
 
1390
 
 
1391
                                        if (cur_info_p.OID != NULL) {
 
1392
                                                sprintf((char *)cur_info_p.OID, su_info_p->OID, cur_outlet_number);
 
1393
 
 
1394
                                                /* add outlet instant commands to the info database. */
 
1395
                                                if (SU_TYPE(su_info_p) == SU_TYPE_CMD) {
 
1396
                                                        /* FIXME: only add if "su_ups_get(cur_info_p) == TRUE" */
 
1397
                                                        if (mode == SU_WALKMODE_INIT)
 
1398
                                                                dstate_addcmd(cur_info_p.info_type);
 
1399
                                                }
 
1400
                                                else /* get and process this data */
 
1401
                                                        status = get_and_process_data(mode, &cur_info_p);
 
1402
                                        } else {
 
1403
                                                /* server side (ABSENT) data */
 
1404
                                                su_setinfo(&cur_info_p, NULL);
 
1405
                                        }
 
1406
                                        /* set back the flag */
 
1407
                                        su_info_p->flags = cur_info_p.flags;
 
1408
                                }
 
1409
                                free((char*)cur_info_p.info_type);
 
1410
                                if (cur_info_p.OID != NULL)
 
1411
                                        free((char*)cur_info_p.OID);
 
1412
                                if ((cur_info_p.dfl != NULL) &&
 
1413
                                        (strstr(su_info_p->dfl, "%i") != NULL))
 
1414
                                        free((char*)cur_info_p.dfl);
 
1415
                        }
 
1416
                        else {
 
1417
                                upsdebugx(1, "No outlet present, discarding template definition...");
 
1418
                        }
 
1419
                }
 
1420
                else {
 
1421
                        /* get and process this data */
 
1422
                        status = get_and_process_data(mode, su_info_p);
 
1423
                }
 
1424
        }       /* for (su_info_p... */
 
1425
 
 
1426
        iterations++;
 
1427
 
 
1428
        return status;
 
1429
}
 
1430
 
 
1431
bool_t su_ups_get(snmp_info_t *su_info_p)
 
1432
{
 
1433
        static char buf[SU_INFOSIZE];
 
1434
        bool_t status;
 
1435
        long value;
 
1436
        struct snmp_pdu ** pdu_array;
 
1437
        struct snmp_pdu * current_pdu;
 
1438
        alarms_info_t * alarms;
 
1439
        int index = 0;
 
1440
 
 
1441
        upsdebugx(2, "su_ups_get: %s %s", su_info_p->info_type, su_info_p->OID);
 
1442
 
 
1443
        if (!strcasecmp(su_info_p->info_type, "ups.status")) {
 
1444
 
 
1445
                status = nut_snmp_get_int(su_info_p->OID, &value);
 
1446
                if (status == TRUE)
 
1447
                {
 
1448
                        su_status_set(su_info_p, value);
 
1449
                        upsdebugx(2, "=> value: %ld", value);
 
1450
                }
 
1451
                else upsdebugx(2, "=> Failed");
 
1452
 
 
1453
                return status;
 
1454
        }
 
1455
 
 
1456
        if (!strcasecmp(su_info_p->info_type, "ups.alarms")) {
 
1457
                status = nut_snmp_get_int(su_info_p->OID, &value);
 
1458
                if (status == TRUE) {
 
1459
                        upsdebugx(2, "=> value: %ld", value);
 
1460
                        if( value > 0 ) {
 
1461
                                pdu_array = nut_snmp_walk(su_info_p->OID,INT_MAX);
 
1462
                                if(pdu_array == NULL) {
 
1463
                                        upsdebugx(2, "=> Walk failed");
 
1464
                                        return FALSE;
 
1465
                                }
 
1466
 
 
1467
                                current_pdu = pdu_array[index];
 
1468
                                while(current_pdu) {
 
1469
                                        decode_str(current_pdu,buf,sizeof(buf),NULL);
 
1470
                                        alarms = alarms_info;
 
1471
                                        while( alarms->OID ) {
 
1472
                                                if(!strcmp(buf+1,alarms_info->OID)) {
 
1473
                                                        upsdebugx(3, "Alarm OID %s found => %s", alarms->OID, alarms->info_value);
 
1474
                                                        status_set(alarms->info_value);
 
1475
                                                        break;
 
1476
                                                }
 
1477
                                                alarms++;
 
1478
                                        }
 
1479
                                        index++;
 
1480
                                        current_pdu = pdu_array[index];
 
1481
                                }
 
1482
                                nut_snmp_free(pdu_array);
 
1483
                        }
 
1484
                }
 
1485
 
 
1486
                else {
 
1487
                        upsdebugx(2, "=> Failed");
 
1488
                }
 
1489
 
 
1490
                return status;
 
1491
        }
 
1492
 
 
1493
        /* another special case */
 
1494
        if (!strcasecmp(su_info_p->info_type, "ambient.temperature")) {
 
1495
                float temp=0;
 
1496
 
 
1497
                status = nut_snmp_get_int(su_info_p->OID, &value);
 
1498
 
 
1499
                if(status != TRUE) {
 
1500
                        return status;
 
1501
                }
 
1502
 
 
1503
                /* only do this if using the IEM sensor */
 
1504
                if (!strcmp(su_info_p->OID, APCC_OID_IEM_TEMP)) {
 
1505
                        int     su;
 
1506
                        long    units;
 
1507
 
 
1508
                        su = nut_snmp_get_int(APCC_OID_IEM_TEMP_UNIT, &units);
 
1509
 
 
1510
                        /* no response, or units == F */
 
1511
                        if ((su == FALSE) || (units == APCC_IEM_FAHRENHEIT))
 
1512
                                temp = (value - 32) / 1.8;
 
1513
                        else
 
1514
                                temp = value;
 
1515
                }
 
1516
                else {
 
1517
                        temp = value * su_info_p->info_len;
 
1518
                }
 
1519
 
 
1520
                snprintf(buf, sizeof(buf), "%.1f", temp);
 
1521
                su_setinfo(su_info_p, buf);
 
1522
 
 
1523
                return TRUE;
 
1524
        }
 
1525
 
 
1526
        if (su_info_p->info_flags == 0) {
 
1527
                status = nut_snmp_get_int(su_info_p->OID, &value);
 
1528
                if (status == TRUE) {
 
1529
                        if (su_info_p->flags&SU_FLAG_NEGINVALID && value<0) {
 
1530
                                su_info_p->flags &= ~SU_FLAG_OK;
 
1531
                                if(su_info_p->flags&SU_FLAG_UNIQUE) {
 
1532
                                        disable_competition(su_info_p);
 
1533
                                        su_info_p->flags &= ~SU_FLAG_UNIQUE;
 
1534
                                }
 
1535
                                return FALSE;
 
1536
                        }
 
1537
                        if (su_info_p->flags & SU_FLAG_SETINT) {
 
1538
                                upsdebugx(1, "setvar %s", su_info_p->OID);
 
1539
                                *su_info_p->setvar = value;
 
1540
                        }
 
1541
                        snprintf(buf, sizeof(buf), "%.2f", value * su_info_p->info_len);
 
1542
                }
 
1543
        } else {
 
1544
                status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info);
 
1545
        }
 
1546
 
 
1547
        if (status == TRUE) {
 
1548
                su_setinfo(su_info_p, buf);
 
1549
                upsdebugx(2, "=> value: %s", buf);
 
1550
        }
 
1551
        else
 
1552
                upsdebugx(2, "=> Failed");
 
1553
 
 
1554
        return status;
 
1555
}
 
1556
 
 
1557
/* set r/w INFO_ element to a value. */
 
1558
int su_setvar(const char *varname, const char *val)
 
1559
{
 
1560
        snmp_info_t *su_info_p = NULL;
 
1561
        bool_t status;
 
1562
        int retval = STAT_SET_FAILED;
 
1563
        int value = -1;
 
1564
 
 
1565
        upsdebugx(2, "entering su_setvar(%s, %s)", varname, val);
 
1566
 
 
1567
        if (strncmp(varname, "outlet", 6))
 
1568
                su_info_p = su_find_info(varname);
 
1569
        else {
 
1570
                snmp_info_t *tmp_info_p;
 
1571
                char *outlet_number_ptr = strchr(varname, '.');
 
1572
                int outlet_number = atoi(++outlet_number_ptr);
 
1573
                if (dstate_getinfo("outlet.count") == NULL) {
 
1574
                        upsdebugx(2, "su_setvar: can't get outlet.count...");
 
1575
                        return STAT_SET_INVALID;
 
1576
                }
 
1577
 
 
1578
                upsdebugx(3, "su_setvar: outlet %i / %i", outlet_number,
 
1579
                        atoi(dstate_getinfo("outlet.count")));
 
1580
 
 
1581
                /* ensure the outlet number is supported (filtered upstream though)! */
 
1582
                if (outlet_number > atoi(dstate_getinfo("outlet.count"))) {
 
1583
                        /* out of bound outlet number */
 
1584
                        upsdebugx(2, "su_setvar: outlet is out of bound (%i / %s)",
 
1585
                                outlet_number, dstate_getinfo("outlet.count"));
 
1586
                        return STAT_SET_INVALID;
 
1587
                }
 
1588
                /* find back the outlet template */
 
1589
                char *outlet_varname = (char *)xmalloc(SU_INFOSIZE);
 
1590
                sprintf(outlet_varname, "outlet.%s%s", "%i", strchr(outlet_number_ptr++, '.'));
 
1591
                upsdebugx(3, "su_setvar: searching for template\"%s\"", outlet_varname);
 
1592
                tmp_info_p = su_find_info(outlet_varname);
 
1593
                free(outlet_varname);
 
1594
 
 
1595
                /* for an snmp_info_t instance */
 
1596
                su_info_p = instantiate_info(tmp_info_p, su_info_p);
 
1597
 
 
1598
                /* check if default value is also a template */
 
1599
                if ((su_info_p->dfl != NULL) &&
 
1600
                        (strstr(tmp_info_p->dfl, "%i") != NULL)) {
 
1601
                        su_info_p->dfl = (char *)xmalloc(SU_INFOSIZE);
 
1602
                        sprintf((char *)su_info_p->dfl, tmp_info_p->dfl,
 
1603
                                outlet_number - base_nut_outlet_offset());
 
1604
                }
 
1605
                /* adapt the OID */
 
1606
                if (su_info_p->OID != NULL) {
 
1607
                        sprintf((char *)su_info_p->OID, tmp_info_p->OID,
 
1608
                                outlet_number - base_nut_outlet_offset());
 
1609
                }
 
1610
                /* else, don't return STAT_SET_INVALID since we can be setting
 
1611
                 * a server side variable! */
 
1612
 
 
1613
                /* adapt info_type */
 
1614
                if (su_info_p->info_type != NULL)
 
1615
                        sprintf((char *)su_info_p->info_type, "%s", varname);
 
1616
        }
 
1617
 
 
1618
        if (!su_info_p || !su_info_p->info_type || !(su_info_p->flags & SU_FLAG_OK)) {
 
1619
                upsdebugx(2, "su_setvar: info element unavailable %s", varname);
 
1620
 
 
1621
                if (!strncmp(varname, "outlet", 6))
 
1622
                        free_info(su_info_p);
 
1623
 
 
1624
                return STAT_SET_UNKNOWN;
 
1625
        }
 
1626
 
 
1627
        if (!(su_info_p->info_flags & ST_FLAG_RW) || su_info_p->OID == NULL) {
 
1628
                upsdebugx(2, "su_setvar: not writable %s", varname);
 
1629
 
 
1630
                if (!strncmp(varname, "outlet", 6))
 
1631
                        free_info(su_info_p);
 
1632
 
 
1633
                return STAT_SET_INVALID;
 
1634
        }
 
1635
 
 
1636
        /* set value into the device */
 
1637
        if (su_info_p->info_flags & ST_FLAG_STRING) {
 
1638
                status = nut_snmp_set_str(su_info_p->OID, val);
 
1639
        } else {
 
1640
                /* non string data may imply a value lookup */
 
1641
                if (su_info_p->oid2info) {
 
1642
                        value = su_find_valinfo(su_info_p->oid2info, val);
 
1643
                }
 
1644
                else {
 
1645
                        value = strtol(val, NULL, 0);
 
1646
                }
 
1647
                /* Actually apply the new value */
 
1648
                status = nut_snmp_set_int(su_info_p->OID, value);
 
1649
        }
 
1650
 
 
1651
        if (status == FALSE)
 
1652
                upsdebugx(1, "su_setvar: cannot set value %s for %s", val, su_info_p->OID);
 
1653
        else {
 
1654
                retval = STAT_SET_HANDLED;
 
1655
                upsdebugx(1, "su_setvar: successfully set %s to \"%s\"", varname, val);
 
1656
 
 
1657
                /* update info array */
 
1658
                su_setinfo(su_info_p, val);
 
1659
        }
 
1660
        if (!strncmp(varname, "outlet", 6))
 
1661
                free_info(su_info_p);
 
1662
 
 
1663
        return retval;
 
1664
}
 
1665
 
 
1666
/* process instant command and take action. */
 
1667
int su_instcmd(const char *cmdname, const char *extradata)
 
1668
{
 
1669
        snmp_info_t *su_info_p = NULL;
 
1670
        int status;
 
1671
        int retval = STAT_INSTCMD_FAILED;
 
1672
        int cmd_offset = 0;
 
1673
 
 
1674
        upsdebugx(2, "entering su_instcmd(%s, %s)", cmdname, extradata);
 
1675
 
 
1676
        /* FIXME: this should only apply if strchr(%)! */
 
1677
        if (strncmp(cmdname, "outlet", 6)) {
 
1678
                su_info_p = su_find_info(cmdname);
 
1679
        }
 
1680
        else {
 
1681
                snmp_info_t *tmp_info_p;
 
1682
                char *outlet_number_ptr = strchr(cmdname, '.');
 
1683
                int outlet_number = atoi(++outlet_number_ptr);
 
1684
                if (dstate_getinfo("outlet.count") == NULL) {
 
1685
                        upsdebugx(2, "su_instcmd: can't get outlet.count...");
 
1686
                        return STAT_INSTCMD_UNKNOWN;
 
1687
                }
 
1688
 
 
1689
                upsdebugx(3, "su_instcmd: outlet %i / %i", outlet_number,
 
1690
                        atoi(dstate_getinfo("outlet.count")));
 
1691
 
 
1692
                /* ensure the outlet number is supported! */
 
1693
                if (outlet_number > atoi(dstate_getinfo("outlet.count"))) {
 
1694
                        /* out of bound outlet number */
 
1695
                        upsdebugx(2, "su_instcmd: outlet is out of bound (%i / %s)",
 
1696
                                outlet_number, dstate_getinfo("outlet.count"));
 
1697
                        return STAT_INSTCMD_INVALID;
 
1698
                }
 
1699
 
 
1700
                /* find back the outlet template */
 
1701
                char *outlet_cmdname = (char *)xmalloc(SU_INFOSIZE);
 
1702
                sprintf(outlet_cmdname, "outlet.%s%s", "%i", strchr(outlet_number_ptr++, '.'));
 
1703
                upsdebugx(3, "su_instcmd: searching for template\"%s\"", outlet_cmdname);
 
1704
                tmp_info_p = su_find_info(outlet_cmdname);
 
1705
                free(outlet_cmdname);
 
1706
 
 
1707
                /* for an snmp_info_t instance */
 
1708
                su_info_p = instantiate_info(tmp_info_p, su_info_p);
 
1709
 
 
1710
                /* check if default value is also a template */
 
1711
                if ((su_info_p->dfl != NULL) &&
 
1712
                        (strstr(tmp_info_p->dfl, "%i") != NULL)) {
 
1713
                        su_info_p->dfl = (char *)xmalloc(SU_INFOSIZE);
 
1714
                        sprintf((char *)su_info_p->dfl, tmp_info_p->dfl,
 
1715
                                outlet_number - base_nut_outlet_offset());
 
1716
                }
 
1717
                /* adapt the OID */
 
1718
                if (su_info_p->OID != NULL) {
 
1719
                        /* Workaround buggy Eaton Pulizzi implementation
 
1720
                         * which have different offsets index for data & commands! */
 
1721
                        if (su_info_p->flags & SU_CMD_OFFSET) {
 
1722
                                upsdebugx(3, "Adding command offset");
 
1723
                                cmd_offset++;
 
1724
                        }
 
1725
 
 
1726
                        sprintf((char *)su_info_p->OID, tmp_info_p->OID,
 
1727
                                outlet_number - base_nut_outlet_offset() + cmd_offset);
 
1728
                } else {
 
1729
                        free_info(su_info_p);
 
1730
                        return STAT_INSTCMD_UNKNOWN;
 
1731
                }
 
1732
        }
 
1733
 
 
1734
        /* Sanity check */
 
1735
        if (!su_info_p || !su_info_p->info_type || !(su_info_p->flags & SU_FLAG_OK)) {
 
1736
 
 
1737
                /* Check for composite commands */
 
1738
                if (!strcasecmp(cmdname, "load.on")) {
 
1739
                        return su_instcmd("load.on.delay", "0");
 
1740
                }
 
1741
 
 
1742
                if (!strcasecmp(cmdname, "load.off")) {
 
1743
                        return su_instcmd("load.off.delay", "0");
 
1744
                }
 
1745
 
 
1746
                if (!strcasecmp(cmdname, "shutdown.return")) {
 
1747
                        int     ret;
 
1748
 
 
1749
                        /* Ensure "ups.start.auto" is set to "yes", if supported */
 
1750
                        if (dstate_getinfo("ups.start.auto")) {
 
1751
                                su_setvar("ups.start.auto", "yes");
 
1752
                        }
 
1753
 
 
1754
                        ret = su_instcmd("load.on.delay", dstate_getinfo("ups.delay.start"));
 
1755
                        if (ret != STAT_INSTCMD_HANDLED) {
 
1756
                                return ret;
 
1757
                        }
 
1758
 
 
1759
                        return su_instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown"));
 
1760
                }
 
1761
 
 
1762
                if (!strcasecmp(cmdname, "shutdown.stayoff")) {
 
1763
                        int     ret;
 
1764
 
 
1765
                        /* Ensure "ups.start.auto" is set to "no", if supported */
 
1766
                        if (dstate_getinfo("ups.start.auto")) {
 
1767
                                su_setvar("ups.start.auto", "no");
 
1768
                        }
 
1769
 
 
1770
                        ret = su_instcmd("load.on.delay", "-1");
 
1771
                        if (ret != STAT_INSTCMD_HANDLED) {
 
1772
                                return ret;
 
1773
                        }
 
1774
 
 
1775
                        return su_instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown"));
 
1776
                }
 
1777
 
 
1778
                upsdebugx(2, "su_instcmd: %s unavailable", cmdname);
 
1779
 
 
1780
                if (!strncmp(cmdname, "outlet", 6))
 
1781
                        free_info(su_info_p);
 
1782
 
 
1783
                return STAT_INSTCMD_UNKNOWN;
 
1784
        }
 
1785
 
 
1786
        /* set value, using the provided one, or the default one otherwise */
 
1787
        if (su_info_p->info_flags & ST_FLAG_STRING) {
 
1788
                status = nut_snmp_set_str(su_info_p->OID, extradata ? extradata : su_info_p->dfl);
 
1789
        } else {
 
1790
                status = nut_snmp_set_int(su_info_p->OID, extradata ? atoi(extradata) : su_info_p->info_len);
 
1791
        }
 
1792
 
 
1793
        if (status == FALSE)
 
1794
                upsdebugx(1, "su_instcmd: cannot set value for %s", cmdname);
 
1795
        else {
 
1796
                retval = STAT_INSTCMD_HANDLED;
 
1797
                upsdebugx(1, "su_instcmd: successfully sent command %s", cmdname);
 
1798
        }
 
1799
 
 
1800
        if (!strncmp(cmdname, "outlet", 6))
 
1801
                free_info(su_info_p);
 
1802
 
 
1803
        return retval;
 
1804
}
 
1805
 
 
1806
/* FIXME: the below functions can be removed since these were for loading
 
1807
 * the mib2nut information from a file instead of the .h definitions... */
 
1808
/* return 1 if usable, 0 if not */
 
1809
static int parse_mibconf_args(int numargs, char **arg)
 
1810
{
 
1811
        bool_t ret;
 
1812
 
 
1813
        /* everything below here uses up through arg[1] */
 
1814
        if (numargs < 6)
 
1815
                return 0;
 
1816
 
 
1817
        /* <info type> <info flags> <info len> <OID name> <default value> <value lookup> */
 
1818
 
 
1819
        /* special case for setting some OIDs value at driver startup */
 
1820
        if (!strcmp(arg[0], "init")) {
 
1821
                /* set value. */
 
1822
                if (!strcmp(arg[1], "str")) {
 
1823
                        ret = nut_snmp_set_str(arg[3], arg[4]);
 
1824
                } else {
 
1825
                        ret = nut_snmp_set_int(arg[3], strtol(arg[4], NULL, 0));
 
1826
                }
 
1827
 
 
1828
                if (ret == FALSE)
 
1829
                        upslogx(LOG_ERR, "su_setvar: cannot set value %s for %s", arg[4], arg[3]);
 
1830
                else
 
1831
                        upsdebugx(1, "su_setvar: successfully set %s to \"%s\"", arg[0], arg[4]);
 
1832
 
 
1833
                return 1;
 
1834
        }
 
1835
 
 
1836
        /* TODO: create the lookup table */
 
1837
        upsdebugx(2, "%s, %s, %s, %s, %s, %s", arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]);
 
1838
 
 
1839
        return 1;
 
1840
}
 
1841
/* called for fatal errors in parseconf like malloc failures */
 
1842
static void mibconf_err(const char *errmsg)
 
1843
{
 
1844
        upslogx(LOG_ERR, "Fatal error in parseconf (*mib.conf): %s", errmsg);
 
1845
}
 
1846
/* load *mib.conf into an snmp_info_t structure */
 
1847
void read_mibconf(char *mib)
 
1848
{
 
1849
        char    fn[SMALLBUF];
 
1850
        PCONF_CTX_t     ctx;
 
1851
 
 
1852
        upsdebugx(2, "SNMP UPS driver : entering read_mibconf(%s)", mib);
 
1853
 
 
1854
        snprintf(fn, sizeof(fn), "%s/snmp/%s.conf", CONFPATH, mib);
 
1855
 
 
1856
        pconf_init(&ctx, mibconf_err);
 
1857
 
 
1858
        if (!pconf_file_begin(&ctx, fn))
 
1859
                fatalx(EXIT_FAILURE, "%s", ctx.errmsg);
 
1860
 
 
1861
        while (pconf_file_next(&ctx)) {
 
1862
                if (pconf_parse_error(&ctx)) {
 
1863
                        upslogx(LOG_ERR, "Parse error: %s:%d: %s",
 
1864
                                fn, ctx.linenum, ctx.errmsg);
 
1865
                        continue;
 
1866
                }
 
1867
 
 
1868
                if (ctx.numargs < 1)
 
1869
                        continue;
 
1870
 
 
1871
                if (!parse_mibconf_args(ctx.numargs, ctx.arglist)) {
 
1872
                        unsigned int    i;
 
1873
                        char    errmsg[SMALLBUF];
 
1874
 
 
1875
                        snprintf(errmsg, sizeof(errmsg),
 
1876
                                "mib.conf: invalid directive");
 
1877
 
 
1878
                        for (i = 0; i < ctx.numargs; i++)
 
1879
                                snprintfcat(errmsg, sizeof(errmsg), " %s",
 
1880
                                        ctx.arglist[i]);
 
1881
 
 
1882
                        upslogx(LOG_WARNING, "%s", errmsg);
 
1883
                }
 
1884
        }
 
1885
        pconf_finish(&ctx);
 
1886
}