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

1 by Laurent Bigonville
Import upstream version 2.7.2
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
}