UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / drivers / net / net.c
blobe32904fd40f829fe7890857fa9b9b295bb2a13c8
1 /*
2 * net.c
4 * Network client driver.
5 */
7 /*
8 * Copyright (C) 2001-2006 Kern Sibbald
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General
12 * Public License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * MA 02111-1307, USA.
25 #include "apc.h"
26 #include "net.h"
29 * List of variables that can be read by getupsvar().
30 * First field is that name given to getupsvar(),
31 * Second field is our internal name as produced by the STATUS
32 * output from apcupsd.
33 * Third field, if 0 returns everything to the end of the
34 * line, and if 1 returns only to first space (e.g. integers,
35 * and floating point values.
37 static const struct {
38 const char *request;
39 const char *upskeyword;
40 int nfields;
41 } cmdtrans[] = {
42 {"battcap", "BCHARGE", 1},
43 {"battdate", "BATTDATE", 1},
44 {"battpct", "BCHARGE", 1},
45 {"battvolt", "BATTV", 1},
46 {"cable", "CABLE", 0},
47 {"date", "DATE", 0},
48 {"firmware", "FIRMWARE", 0},
49 {"highxfer", "HITRANS", 1},
50 {"hostname", "HOSTNAME", 1},
51 {"laststest", "LASTSTEST",0},
52 {"lastxfer", "LASTXFER", 0}, /* reason for last xfer to batteries */
53 {"linemax", "MAXLINEV", 1},
54 {"linemin", "MINLINEV", 1},
55 {"loadpct", "LOADPCT", 1},
56 {"lowbatt", "DLOWBATT", 1}, /* low battery power off delay */
57 {"lowxfer", "LOTRANS", 1},
58 {"maxtime", "MAXTIME", 1},
59 {"mbattchg", "MBATTCHG", 1},
60 {"mintimel", "MINTIMEL", 1},
61 {"model", "MODEL", 0},
62 {"nombattv", "NOMBATTV", 1},
63 {"nominv", "NOMINV", 1},
64 {"nomoutv", "NOMOUTV", 1},
65 {"nompower", "NOMPOWER", 1},
66 {"outputfreq", "LINEFREQ", 1},
67 {"outputv", "OUTPUTV", 1},
68 {"version", "VERSION", 1},
69 {"retpct", "RETPCT", 1}, /* min batt to turn on UPS */
70 {"runtime", "TIMELEFT", 1},
71 {"selftest", "SELFTEST", 1}, /* results of last self test */
72 {"sense", "SENSE", 1},
73 {"serialno", "SERIALNO", 1},
74 {"status", "STATFLAG", 1},
75 {"transhi", "HITRANS", 1},
76 {"translo", "LOTRANS", 1},
77 {"upsload", "LOADPCT", 1},
78 {"upsmode", "UPSMODE", 0},
79 {"upsname", "UPSNAME", 1},
80 {"upstemp", "ITEMP", 1},
81 {"utility", "LINEV", 1},
82 {NULL, NULL}
85 /* Convert UPS response to enum */
86 static SelfTestResult decode_testresult(char* str)
88 if (!strncmp(str, "OK", 2))
89 return TEST_PASSED;
90 else if (!strncmp(str, "NO", 2))
91 return TEST_NONE;
92 else if (!strncmp(str, "BT", 2))
93 return TEST_FAILCAP;
94 else if (!strncmp(str, "NG", 2))
95 return TEST_FAILED;
96 else if (!strncmp(str, "WN", 2))
97 return TEST_WARNING;
98 else if (!strncmp(str, "IP", 2))
99 return TEST_INPROGRESS;
100 else
101 return TEST_UNKNOWN;
104 /* Convert UPS response to enum */
105 static LastXferCause decode_lastxfer(char *str)
107 Dmsg1(80, "Transfer reason: %s\n", str);
109 if (!strcmp(str, "No transfers since turnon"))
110 return XFER_NONE;
111 else if (!strcmp(str, "Automatic or explicit self test"))
112 return XFER_SELFTEST;
113 else if (!strcmp(str, "Forced by software"))
114 return XFER_FORCED;
115 else if (!strcmp(str, "Low line voltage"))
116 return XFER_UNDERVOLT;
117 else if (!strcmp(str, "High line voltage"))
118 return XFER_OVERVOLT;
119 else if (!strcmp(str, "Line voltage notch or spike"))
120 return XFER_NOTCHSPIKE;
121 else if (!strcmp(str, "Unacceptable line voltage changes"))
122 return XFER_RIPPLE;
123 else if (!strcmp(str, "Input frequency out of range"))
124 return XFER_FREQ;
125 else
126 return XFER_UNKNOWN;
130 * The remote server DEVICE entry in apcupsd.conf is
131 * in the form:
133 * DEVICE hostname[:port]
136 static int initialize_device_data(UPSINFO *ups)
138 struct driver_data *nid = (struct driver_data *)ups->driver_internal_data;
139 char *cp;
141 astrncpy(nid->device, ups->device, sizeof(nid->device));
142 astrncpy(ups->master_name, ups->device, sizeof(ups->master_name));
143 astrncpy(ups->upsclass.long_name, "Net Slave", sizeof(ups->upsclass.long_name));
145 /* Now split the device. */
146 nid->hostname = nid->device;
148 cp = strchr(nid->device, ':');
149 if (cp) {
150 *cp = '\0';
151 cp++;
152 nid->port = atoi(cp);
153 } else {
154 /* use NIS port as default */
155 nid->port = ups->statusport;
158 nid->statbuf[0] = 0;
159 nid->statlen = 0;
161 Dmsg0(90, "Exit initialize_device_data\n");
162 return SUCCESS;
166 * Returns 1 if var found
167 * answer has var
168 * Returns 0 if variable name not found
169 * answer has "Not found" is variable name not found
170 * answer may have "N/A" if the UPS does not support this
171 * feature
172 * Returns -1 if network problem
173 * answer has "N/A" if host is not available or network error
175 static int getupsvar(UPSINFO *ups, const char *request, char *answer, int anslen)
177 struct driver_data *nid = (struct driver_data *)ups->driver_internal_data;
178 int i;
179 const char *stat_match = NULL;
180 char *find;
181 int nfields = 0;
182 char format[21];
184 for (i = 0; cmdtrans[i].request; i++) {
185 if (!(strcmp(cmdtrans[i].request, request))) {
186 stat_match = cmdtrans[i].upskeyword;
187 nfields = cmdtrans[i].nfields;
191 if (stat_match) {
192 if ((find = strstr(nid->statbuf, stat_match)) != NULL) {
193 if (nfields == 1) { /* get one field */
194 asnprintf(format, sizeof(format), "%%*s %%*s %%%ds", anslen);
195 sscanf(find, format, answer);
196 } else { /* get everything to eol */
197 i = 0;
198 find += 11; /* skip label */
200 while (*find != '\n' && i < anslen - 1)
201 answer[i++] = *find++;
203 answer[i] = 0;
205 if (strcmp(answer, "N/A") == 0) {
206 return 0;
208 Dmsg2(100, "Return 1 for getupsvar %s %s\n", request, answer);
209 return 1;
211 } else {
212 Dmsg1(100, "Hey!!! No match in getupsvar for %s!\n", request);
215 astrncpy(answer, "Not found", anslen);
216 return 0;
219 static int poll_ups(UPSINFO *ups)
221 struct driver_data *nid = (struct driver_data *)ups->driver_internal_data;
222 int n, stat = 1;
223 char buf[1000];
225 nid->statbuf[0] = 0;
226 nid->statlen = 0;
228 Dmsg2(20, "Opening connection to %s:%d\n", nid->hostname, nid->port);
229 if ((nid->sockfd = net_open(nid->hostname, NULL, nid->port)) < 0) {
230 Dmsg0(90, "Exit poll_ups 0 comm lost\n");
231 if (!ups->is_commlost()) {
232 ups->set_commlost();
234 return 0;
237 if (net_send(nid->sockfd, "status", 6) != 6) {
238 net_close(nid->sockfd);
239 Dmsg0(90, "Exit poll_ups 0 no status flag\n");
240 ups->set_commlost();
241 return 0;
244 Dmsg0(99, "===============\n");
245 while ((n = net_recv(nid->sockfd, buf, sizeof(buf) - 1)) > 0) {
246 buf[n] = 0;
247 astrncat(nid->statbuf, buf, sizeof(nid->statbuf));
248 Dmsg3(99, "Partial buf (%d, %d):\n%s", n, strlen(nid->statbuf), buf);
250 Dmsg0(99, "===============\n");
252 if (n < 0) {
253 stat = 0;
254 Dmsg0(90, "Exit poll_ups 0 bad stat net_recv\n");
255 ups->set_commlost();
256 } else {
257 ups->clear_commlost();
259 net_close(nid->sockfd);
261 Dmsg1(99, "Buffer:\n%s\n", nid->statbuf);
262 nid->statlen = strlen(nid->statbuf);
263 Dmsg1(90, "Exit poll_ups, stat=%d\n", stat);
264 return stat;
268 * Fill buffer with data from UPS network daemon
269 * Returns false on error
270 * Returns true if OK
272 #define SLEEP_TIME 2
273 static bool fill_status_buffer(UPSINFO *ups)
275 struct driver_data *nid = (struct driver_data *)ups->driver_internal_data;
276 time_t now;
277 static time_t tlog = 0;
278 static bool comm_err = false;
280 /* Poll or fill the buffer maximum one time per second */
281 now = time(NULL);
282 if ((now - nid->last_fill_time) < 2) {
283 Dmsg0(90, "Exit fill_status_buffer OK less than 2 sec\n");
284 return true;
287 if (!poll_ups(ups)) {
288 /* generate event once */
289 if (!comm_err) {
290 execute_command(ups, ups_event[CMDCOMMFAILURE]);
291 comm_err = true;
294 /* log every 10 minutes */
295 if (now - tlog >= 10 * 60) {
296 tlog = now;
297 log_event(ups, event_msg[CMDCOMMFAILURE].level,
298 event_msg[CMDCOMMFAILURE].msg);
300 } else {
301 if (comm_err) {
302 generate_event(ups, CMDCOMMOK);
303 tlog = 0;
304 comm_err = false;
307 nid->last_fill_time = now;
309 if (!nid->got_caps)
310 net_ups_get_capabilities(ups);
312 if (nid->got_caps && !nid->got_static_data)
313 net_ups_read_static_data(ups);
316 return !comm_err;
319 static int get_ups_status_flag(UPSINFO *ups, int fill)
321 char answer[200];
322 int stat = 1;
323 int32_t newStatus; /* this really should be uint32_t! */
324 int32_t masterStatus; /* status from master */
325 static bool comm_loss = false;
326 struct driver_data *nid = (struct driver_data *)ups->driver_internal_data;
328 if (!nid->got_caps) {
329 net_ups_get_capabilities(ups);
330 if (!nid->got_caps)
331 return 0;
334 if (!nid->got_static_data) {
335 net_ups_read_static_data(ups);
336 if (!nid->got_static_data)
337 return 0;
340 if (fill) {
342 * Not locked because no one else should be writing in the
343 * status buffer, and we don't want to lock across I/O
344 * operations.
346 stat = fill_status_buffer(ups);
349 write_lock(ups);
350 answer[0] = 0;
351 if (!getupsvar(ups, "status", answer, sizeof(answer))) {
352 Dmsg0(100, "HEY!!! Couldn't get status flag.\n");
353 stat = 0;
354 masterStatus = 0;
355 } else {
357 * Make sure we don't override local bits, and that
358 * all non-local bits are set/cleared correctly.
360 * local bits = UPS_commlost|UPS_shutdown|UPS_slave|UPS_slavedown|
361 * UPS_prev_onbatt|UPS_prev_battlow|UPS_onbatt_msg|
362 * UPS_fastpoll|UPS_plugged|UPS_dev_setup
365 /* First transfer set or not set all non-local bits */
366 masterStatus = strtol(answer, NULL, 0);
367 newStatus = masterStatus & ~UPS_LOCAL_BITS; /* clear local bits */
368 ups->Status &= UPS_LOCAL_BITS; /* clear non-local bits */
369 ups->Status |= newStatus; /* set new non-local bits */
372 * Now set any special bits, note this is set only, we do
373 * not clear these bits, but let our own core code clear them
375 newStatus = masterStatus & (UPS_commlost | UPS_fastpoll);
376 ups->Status |= newStatus;
379 Dmsg2(100, "Got Status = %s 0x%x\n", answer, ups->Status);
381 if (masterStatus & UPS_shutdown && !ups->is_shut_remote()) {
382 ups->set_shut_remote(); /* if master is shutting down so do we */
383 log_event(ups, LOG_ERR, "Shutdown because NIS master is shutting down.");
384 Dmsg0(100, "Set SHUT_REMOTE because of master status.\n");
388 * If we lost connection with master and we
389 * are running on batteries, shutdown on the fourth
390 * consequtive pass here. While on batteries, this code
391 * is called once per second.
393 if (stat == 0 && ups->is_onbatt()) {
394 if (comm_loss++ == 4 && !ups->is_shut_remote()) {
395 ups->set_shut_remote();
396 log_event(ups, LOG_ERR,
397 "Shutdown because loss of comm with NIS master while on batteries.");
398 Dmsg0(100, "Set SHUT_REMOTE because of loss of comm on batteries.\n");
400 } else {
401 comm_loss = 0;
404 write_unlock(ups);
405 return stat;
409 int net_ups_open(UPSINFO *ups)
411 struct driver_data *nid;
413 nid = (struct driver_data *)malloc(sizeof(struct driver_data));
415 if (nid == NULL) {
416 log_event(ups, LOG_ERR, "Out of memory.");
417 exit(1);
420 memset(nid, 0, sizeof(struct driver_data));
421 ups->driver_internal_data = nid;
423 initialize_device_data(ups);
425 /* Fake core code. Will go away when ups->fd is cleaned up. */
426 ups->fd = 1;
428 return 1;
431 int net_ups_close(UPSINFO *ups)
433 if (ups->driver_internal_data == NULL)
434 return 1;
436 free(ups->driver_internal_data);
437 ups->driver_internal_data = NULL;
439 /* Fake core code. Will go away when ups->fd will be cleaned up. */
440 ups->fd = -1;
442 return 1;
445 int net_ups_setup(UPSINFO *ups)
447 /* Nothing to setup. */
448 return 1;
451 int net_ups_get_capabilities(UPSINFO *ups)
453 struct driver_data *nid = (struct driver_data *)ups->driver_internal_data;
454 char answer[200];
456 write_lock(ups);
458 if (poll_ups(ups)) {
459 ups->UPS_Cap[CI_VLINE] = getupsvar(ups, "utility", answer, sizeof(answer));
460 ups->UPS_Cap[CI_LOAD] = getupsvar(ups, "loadpct", answer, sizeof(answer));
461 ups->UPS_Cap[CI_BATTLEV] = getupsvar(ups, "battcap", answer, sizeof(answer));
462 ups->UPS_Cap[CI_RUNTIM] = getupsvar(ups, "runtime", answer, sizeof(answer));
463 ups->UPS_Cap[CI_VMAX] = getupsvar(ups, "linemax", answer, sizeof(answer));
464 ups->UPS_Cap[CI_VMIN] = getupsvar(ups, "linemin", answer, sizeof(answer));
465 ups->UPS_Cap[CI_VOUT] = getupsvar(ups, "outputv", answer, sizeof(answer));
466 ups->UPS_Cap[CI_SENS] = getupsvar(ups, "sense", answer, sizeof(answer));
467 ups->UPS_Cap[CI_DLBATT] = getupsvar(ups, "lowbatt", answer, sizeof(answer));
468 ups->UPS_Cap[CI_LTRANS] = getupsvar(ups, "lowxfer", answer, sizeof(answer));
469 ups->UPS_Cap[CI_HTRANS] = getupsvar(ups, "highxfer", answer, sizeof(answer));
470 ups->UPS_Cap[CI_RETPCT] = getupsvar(ups, "retpct", answer, sizeof(answer));
471 ups->UPS_Cap[CI_ITEMP] = getupsvar(ups, "upstemp", answer, sizeof(answer));
472 ups->UPS_Cap[CI_VBATT] = getupsvar(ups, "battvolt", answer, sizeof(answer));
473 ups->UPS_Cap[CI_FREQ] = getupsvar(ups, "outputfreq", answer, sizeof(answer));
474 ups->UPS_Cap[CI_WHY_BATT] = getupsvar(ups, "lastxfer", answer, sizeof(answer));
475 ups->UPS_Cap[CI_ST_STAT] = getupsvar(ups, "selftest", answer, sizeof(answer));
476 ups->UPS_Cap[CI_SERNO] = getupsvar(ups, "serialno", answer, sizeof(answer));
477 ups->UPS_Cap[CI_BATTDAT] = getupsvar(ups, "battdate", answer, sizeof(answer));
478 ups->UPS_Cap[CI_NOMBATTV] = getupsvar(ups, "nombattv", answer, sizeof(answer));
479 ups->UPS_Cap[CI_NOMINV] = getupsvar(ups, "nominv", answer, sizeof(answer));
480 ups->UPS_Cap[CI_NOMOUTV] = getupsvar(ups, "nomoutv", answer, sizeof(answer));
481 ups->UPS_Cap[CI_NOMPOWER] = getupsvar(ups, "nompower", answer, sizeof(answer));
482 ups->UPS_Cap[CI_REVNO] = getupsvar(ups, "firmware", answer, sizeof(answer));
483 nid->got_caps = true;
484 } else {
485 nid->got_caps = false;
488 write_unlock(ups);
489 return 1;
492 int net_ups_program_eeprom(UPSINFO *ups, int command, const char *data)
494 return 0;
497 int net_ups_kill_power(UPSINFO *ups)
499 /* Not possible */
500 return 0;
503 int net_ups_check_state(UPSINFO *ups)
505 int sleep_time;
507 sleep_time = ups->wait_time;
509 Dmsg1(100, "Sleep %d secs.\n", sleep_time);
510 sleep(sleep_time);
511 get_ups_status_flag(ups, 1);
513 return 1;
516 #define GETVAR(ci,str) \
517 (ups->UPS_Cap[ci] && getupsvar(ups, str, answer, sizeof(answer)))
519 int net_ups_read_volatile_data(UPSINFO *ups)
521 char answer[200];
523 if (!fill_status_buffer(ups))
524 return 0;
526 write_lock(ups);
527 ups->set_slave();
529 /* ***FIXME**** poll time needs to be scanned */
530 ups->poll_time = time(NULL);
531 ups->last_master_connect_time = ups->poll_time;
533 if (GETVAR(CI_VLINE, "utility"))
534 ups->LineVoltage = atof(answer);
536 if (GETVAR(CI_LOAD, "loadpct"))
537 ups->UPSLoad = atof(answer);
539 if (GETVAR(CI_BATTLEV, "battcap"))
540 ups->BattChg = atof(answer);
542 if (GETVAR(CI_RUNTIM, "runtime"))
543 ups->TimeLeft = atof(answer);
545 if (GETVAR(CI_VMAX, "linemax"))
546 ups->LineMax = atof(answer);
548 if (GETVAR(CI_VMIN, "linemin"))
549 ups->LineMin = atof(answer);
551 if (GETVAR(CI_VOUT, "outputv"))
552 ups->OutputVoltage = atof(answer);
554 if (GETVAR(CI_SENS, "sense"))
555 ups->sensitivity[0] = answer[0];
557 if (GETVAR(CI_DLBATT, "lowbatt"))
558 ups->dlowbatt = (int)atof(answer);
560 if (GETVAR(CI_LTRANS, "lowxfer"))
561 ups->lotrans = (int)atof(answer);
563 if (GETVAR(CI_HTRANS, "highxfer"))
564 ups->hitrans = (int)atof(answer);
566 if (GETVAR(CI_RETPCT, "retpct"))
567 ups->rtnpct = (int)atof(answer);
569 if (GETVAR(CI_ITEMP, "upstemp"))
570 ups->UPSTemp = atof(answer);
572 if (GETVAR(CI_VBATT, "battvolt"))
573 ups->BattVoltage = atof(answer);
575 if (GETVAR(CI_FREQ, "outputfreq"))
576 ups->LineFreq = atof(answer);
578 if (GETVAR(CI_WHY_BATT, "lastxfer"))
579 ups->lastxfer = decode_lastxfer(answer);
581 if (GETVAR(CI_ST_STAT, "selftest"))
582 ups->testresult = decode_testresult(answer);
584 write_unlock(ups);
586 get_ups_status_flag(ups, 0);
588 return 1;
591 int net_ups_read_static_data(UPSINFO *ups)
593 struct driver_data *nid = (struct driver_data *)ups->driver_internal_data;
594 char answer[200];
596 write_lock(ups);
598 if (poll_ups(ups)) {
599 if (!getupsvar(
600 ups, "upsname", ups->upsname,
601 sizeof(ups->upsname))) {
602 log_event(ups, LOG_ERR, "getupsvar: failed for \"upsname\".");
604 if (!getupsvar(
605 ups, "model", ups->mode.long_name,
606 sizeof(ups->mode.long_name))) {
607 log_event(ups, LOG_ERR, "getupsvar: failed for \"model\".");
609 if (!getupsvar(
610 ups, "upsmode", ups->upsclass.long_name,
611 sizeof(ups->upsclass.long_name))) {
612 log_event(ups, LOG_ERR, "getupsvar: failed for \"upsmode\".");
615 if (GETVAR(CI_SERNO, "serialno"))
616 astrncpy(ups->serial, answer, sizeof(ups->serial));
618 if (GETVAR(CI_BATTDAT, "battdate"))
619 astrncpy(ups->battdat, answer, sizeof(ups->battdat));
621 if (GETVAR(CI_NOMBATTV, "nombattv"))
622 ups->nombattv = atof(answer);
624 if (GETVAR(CI_NOMINV, "nominv"))
625 ups->NomInputVoltage = (int)atof(answer);
627 if (GETVAR(CI_NOMOUTV, "nomoutv"))
628 ups->NomOutputVoltage = (int)atof(answer);
630 if (GETVAR(CI_NOMPOWER, "nompower"))
631 ups->NomPower = (int)atof(answer);
633 if (GETVAR(CI_REVNO, "firmware"))
634 astrncpy(ups->firmrev, answer, sizeof(ups->firmrev));
636 nid->got_static_data = true;
637 } else {
638 nid->got_static_data = false;
641 write_unlock(ups);
642 return 1;
645 int net_ups_entry_point(UPSINFO *ups, int command, void *data)
647 char answer[200];
649 switch (command) {
650 case DEVICE_CMD_CHECK_SELFTEST:
651 Dmsg0(80, "Checking self test.\n");
653 * XXX FIXME
655 * One day we will do this test inside the driver and not as an
656 * entry point.
658 /* Reason for last transfer to batteries */
659 if (GETVAR(CI_WHY_BATT, "lastxfer")) {
660 ups->lastxfer = decode_lastxfer(answer);
661 Dmsg1(80, "Transfer reason: %d\n", ups->lastxfer);
663 /* See if this is a self test rather than power failure */
664 if (ups->lastxfer == XFER_SELFTEST) {
666 * set Self Test start time
668 ups->SelfTest = time(NULL);
669 Dmsg1(80, "Self Test time: %s", ctime(&ups->SelfTest));
672 break;
674 case DEVICE_CMD_GET_SELFTEST_MSG:
675 if (!GETVAR(CI_ST_STAT, "selftest"))
676 return FAILURE;
678 ups->testresult = decode_testresult(answer);
679 break;
681 default:
682 return FAILURE;
685 return SUCCESS;