4 * Network client driver.
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,
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.
39 const char *upskeyword
;
42 {"battcap", "BCHARGE", 1},
43 {"battdate", "BATTDATE", 1},
44 {"battpct", "BCHARGE", 1},
45 {"battvolt", "BATTV", 1},
46 {"cable", "CABLE", 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},
85 /* Convert UPS response to enum */
86 static SelfTestResult
decode_testresult(char* str
)
88 if (!strncmp(str
, "OK", 2))
90 else if (!strncmp(str
, "NO", 2))
92 else if (!strncmp(str
, "BT", 2))
94 else if (!strncmp(str
, "NG", 2))
96 else if (!strncmp(str
, "WN", 2))
98 else if (!strncmp(str
, "IP", 2))
99 return TEST_INPROGRESS
;
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"))
111 else if (!strcmp(str
, "Automatic or explicit self test"))
112 return XFER_SELFTEST
;
113 else if (!strcmp(str
, "Forced by software"))
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"))
123 else if (!strcmp(str
, "Input frequency out of range"))
130 * The remote server DEVICE entry in apcupsd.conf is
133 * DEVICE hostname[:port]
136 static int initialize_device_data(UPSINFO
*ups
)
138 struct driver_data
*nid
= (struct driver_data
*)ups
->driver_internal_data
;
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
, ':');
152 nid
->port
= atoi(cp
);
154 /* use NIS port as default */
155 nid
->port
= ups
->statusport
;
161 Dmsg0(90, "Exit initialize_device_data\n");
166 * Returns 1 if var found
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
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
;
179 const char *stat_match
= NULL
;
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
;
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 */
198 find
+= 11; /* skip label */
200 while (*find
!= '\n' && i
< anslen
- 1)
201 answer
[i
++] = *find
++;
205 if (strcmp(answer
, "N/A") == 0) {
208 Dmsg2(100, "Return 1 for getupsvar %s %s\n", request
, answer
);
212 Dmsg1(100, "Hey!!! No match in getupsvar for %s!\n", request
);
215 astrncpy(answer
, "Not found", anslen
);
219 static int poll_ups(UPSINFO
*ups
)
221 struct driver_data
*nid
= (struct driver_data
*)ups
->driver_internal_data
;
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()) {
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");
244 Dmsg0(99, "===============\n");
245 while ((n
= net_recv(nid
->sockfd
, buf
, sizeof(buf
) - 1)) > 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");
254 Dmsg0(90, "Exit poll_ups 0 bad stat net_recv\n");
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
);
268 * Fill buffer with data from UPS network daemon
269 * Returns false on error
273 static bool fill_status_buffer(UPSINFO
*ups
)
275 struct driver_data
*nid
= (struct driver_data
*)ups
->driver_internal_data
;
277 static time_t tlog
= 0;
278 static bool comm_err
= false;
280 /* Poll or fill the buffer maximum one time per second */
282 if ((now
- nid
->last_fill_time
) < 2) {
283 Dmsg0(90, "Exit fill_status_buffer OK less than 2 sec\n");
287 if (!poll_ups(ups
)) {
288 /* generate event once */
290 execute_command(ups
, ups_event
[CMDCOMMFAILURE
]);
294 /* log every 10 minutes */
295 if (now
- tlog
>= 10 * 60) {
297 log_event(ups
, event_msg
[CMDCOMMFAILURE
].level
,
298 event_msg
[CMDCOMMFAILURE
].msg
);
302 generate_event(ups
, CMDCOMMOK
);
307 nid
->last_fill_time
= now
;
310 net_ups_get_capabilities(ups
);
312 if (nid
->got_caps
&& !nid
->got_static_data
)
313 net_ups_read_static_data(ups
);
319 static int get_ups_status_flag(UPSINFO
*ups
, int fill
)
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
);
334 if (!nid
->got_static_data
) {
335 net_ups_read_static_data(ups
);
336 if (!nid
->got_static_data
)
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
346 stat
= fill_status_buffer(ups
);
351 if (!getupsvar(ups
, "status", answer
, sizeof(answer
))) {
352 Dmsg0(100, "HEY!!! Couldn't get status flag.\n");
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");
409 int net_ups_open(UPSINFO
*ups
)
411 struct driver_data
*nid
;
413 nid
= (struct driver_data
*)malloc(sizeof(struct driver_data
));
416 log_event(ups
, LOG_ERR
, "Out of memory.");
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. */
431 int net_ups_close(UPSINFO
*ups
)
433 if (ups
->driver_internal_data
== NULL
)
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. */
445 int net_ups_setup(UPSINFO
*ups
)
447 /* Nothing to setup. */
451 int net_ups_get_capabilities(UPSINFO
*ups
)
453 struct driver_data
*nid
= (struct driver_data
*)ups
->driver_internal_data
;
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;
485 nid
->got_caps
= false;
492 int net_ups_program_eeprom(UPSINFO
*ups
, int command
, const char *data
)
497 int net_ups_kill_power(UPSINFO
*ups
)
503 int net_ups_check_state(UPSINFO
*ups
)
507 sleep_time
= ups
->wait_time
;
509 Dmsg1(100, "Sleep %d secs.\n", sleep_time
);
511 get_ups_status_flag(ups
, 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
)
523 if (!fill_status_buffer(ups
))
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
);
586 get_ups_status_flag(ups
, 0);
591 int net_ups_read_static_data(UPSINFO
*ups
)
593 struct driver_data
*nid
= (struct driver_data
*)ups
->driver_internal_data
;
600 ups
, "upsname", ups
->upsname
,
601 sizeof(ups
->upsname
))) {
602 log_event(ups
, LOG_ERR
, "getupsvar: failed for \"upsname\".");
605 ups
, "model", ups
->mode
.long_name
,
606 sizeof(ups
->mode
.long_name
))) {
607 log_event(ups
, LOG_ERR
, "getupsvar: failed for \"model\".");
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;
638 nid
->got_static_data
= false;
645 int net_ups_entry_point(UPSINFO
*ups
, int command
, void *data
)
650 case DEVICE_CMD_CHECK_SELFTEST
:
651 Dmsg0(80, "Checking self test.\n");
655 * One day we will do this test inside the driver and not as an
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
));
674 case DEVICE_CMD_GET_SELFTEST_MSG
:
675 if (!GETVAR(CI_ST_STAT
, "selftest"))
678 ups
->testresult
= decode_testresult(answer
);