2 * clone-outlet.c: clone outlet UPS driver
4 * Copyright (C) 2009 - Arjen de Korte <adkorte-guest@alioth.debian.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "parseconf.h"
24 #include <sys/types.h>
25 #include <sys/socket.h>
28 #define DRIVER_NAME "clone outlet UPS Driver"
29 #define DRIVER_VERSION "0.01"
31 /* driver description structure */
32 upsdrv_info_t upsdrv_info
= {
35 "Arjen de Korte <adkorte-guest@alioth.debian.org>",
48 } prefix
= { { NULL
}, { NULL
}, NULL
};
58 } outlet
= { { -1 }, { -1 }, 1 };
61 char status
[LARGEBUF
];
64 static int dumpdone
= 0;
66 static PCONF_CTX_t sock_ctx
;
67 static time_t last_heard
= 0, last_ping
= 0, last_connfail
= 0;
69 static int parse_args(int numargs
, char **arg
)
75 if (!strcasecmp(arg
[0], "PONG")) {
76 upsdebugx(3, "Got PONG from UPS");
80 if (!strcasecmp(arg
[0], "DUMPDONE")) {
81 upsdebugx(3, "UPS: dump is done");
86 if (!strcasecmp(arg
[0], "DATASTALE")) {
91 if (!strcasecmp(arg
[0], "DATAOK")) {
101 if (!strcasecmp(arg
[0], "DELINFO")) {
102 dstate_delinfo(arg
[1]);
110 /* SETINFO <varname> <value> */
111 if (!strcasecmp(arg
[0], "SETINFO")) {
113 if (!strncasecmp(arg
[1], "driver.", 7)) {
114 /* don't pass on upstream driver settings */
118 if (!strcasecmp(arg
[1], prefix
.delay
.shutdown
)) {
119 outlet
.delay
.shutdown
= strtol(arg
[2], NULL
, 10);
122 if (!strcasecmp(arg
[1], prefix
.timer
.shutdown
)) {
123 outlet
.timer
.shutdown
= strtol(arg
[2], NULL
, 10);
126 if (!strcasecmp(arg
[1], prefix
.status
)) {
127 outlet
.status
= strcasecmp(arg
[2], "off");
130 if (!strcasecmp(arg
[1], "ups.status")) {
131 snprintf(ups
.status
, sizeof(ups
.status
), "%s", arg
[2]);
135 dstate_setinfo(arg
[1], "%s", arg
[2]);
143 static int sstate_connect(void)
146 const char *dumpcmd
= "DUMPALL\n";
147 struct sockaddr_un sa
;
149 memset(&sa
, '\0', sizeof(sa
));
150 sa
.sun_family
= AF_UNIX
;
151 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s", dflt_statepath(), device_path
);
153 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
156 upslog_with_errno(LOG_ERR
, "Can't create socket for UPS [%s]", device_path
);
160 ret
= connect(fd
, (struct sockaddr
*) &sa
, sizeof(sa
));
167 /* rate-limit complaints - don't spam the syslog */
170 if (difftime(now
, last_connfail
) < 60) {
176 upslog_with_errno(LOG_ERR
, "Can't connect to UPS [%s]", device_path
);
180 ret
= fcntl(fd
, F_GETFL
, 0);
183 upslog_with_errno(LOG_ERR
, "fcntl get on UPS [%s] failed", device_path
);
188 ret
= fcntl(fd
, F_SETFL
, ret
| O_NDELAY
);
191 upslog_with_errno(LOG_ERR
, "fcntl set O_NDELAY on UPS [%s] failed", device_path
);
196 /* get a dump started so we have a fresh set of data */
197 ret
= write(fd
, dumpcmd
, strlen(dumpcmd
));
199 if (ret
!= (int)strlen(dumpcmd
)) {
200 upslog_with_errno(LOG_ERR
, "Initial write to UPS [%s] failed", device_path
);
205 pconf_init(&sock_ctx
, NULL
);
211 /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */
212 dstate_setinfo("ups.status", "WAIT");
214 upslogx(LOG_INFO
, "Connected to UPS [%s]", device_path
);
219 static void sstate_disconnect(void)
225 pconf_finish(&sock_ctx
);
232 static int sstate_sendline(const char *buf
)
237 return -1; /* failed */
240 ret
= write(upsfd
, buf
, strlen(buf
));
242 if (ret
== (int)strlen(buf
)) {
246 upslog_with_errno(LOG_NOTICE
, "Send to UPS [%s] failed", device_path
);
247 return -1; /* failed */
251 static int sstate_readline(void)
257 return -1; /* failed */
260 ret
= read(upsfd
, buf
, sizeof(buf
));
270 upslog_with_errno(LOG_WARNING
, "Read from UPS [%s] failed", device_path
);
275 for (i
= 0; i
< ret
; i
++) {
277 switch (pconf_char(&sock_ctx
, buf
[i
]))
280 if (parse_args(sock_ctx
.numargs
, sock_ctx
.arglist
)) {
286 continue; /* haven't gotten a line yet */
290 upslogx(LOG_NOTICE
, "Parse error on sock: %s", sock_ctx
.errmsg
);
299 static int sstate_dead(int maxage
)
304 /* an unconnected ups is always dead */
306 upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", device_path
);
307 return -1; /* dead */
312 /* ignore DATAOK/DATASTALE unless the dump is done */
313 if (dumpdone
&& dstate_is_stale()) {
314 upsdebugx(3, "sstate_dead: driver for UPS [%s] says data is stale", device_path
);
315 return -1; /* dead */
318 elapsed
= difftime(now
, last_heard
);
320 /* somewhere beyond a third of the maximum time - prod it to make it talk */
321 if ((elapsed
> (maxage
/ 3)) && (difftime(now
, last_ping
) > (maxage
/ 3))) {
322 upsdebugx(3, "Send PING to UPS");
323 sstate_sendline("PING\n");
327 if (elapsed
> maxage
) {
328 upsdebugx(3, "sstate_dead: didn't hear from driver for UPS [%s] for %g seconds (max %d)",
329 device_path
, elapsed
, maxage
);
330 return -1; /* dead */
337 void upsdrv_initinfo(void)
342 void upsdrv_updateinfo(void)
344 if (sstate_dead(15)) {
346 extrafd
= upsfd
= sstate_connect();
350 if (sstate_readline()) {
355 if (outlet
.status
== 0) {
356 upsdebugx(2, "OFF flag set (%s: switched off)", prefix
.status
);
357 dstate_setinfo("ups.status", "%s OFF", ups
.status
);
361 if ((outlet
.timer
.shutdown
> -1) && (outlet
.timer
.shutdown
<= outlet
.delay
.shutdown
)) {
362 upsdebugx(2, "FSD flag set (%s: -1 < [%ld] <= %ld)", prefix
.timer
.shutdown
, outlet
.timer
.shutdown
, outlet
.delay
.shutdown
);
363 dstate_setinfo("ups.status", "FSD %s", ups
.status
);
367 upsdebugx(3, "%s: power state not critical", getval("prefix"));
368 dstate_setinfo("ups.status", "%s", ups
.status
);
372 void upsdrv_shutdown(void)
377 void upsdrv_help(void)
382 void upsdrv_makevartable(void)
384 addvar(VAR_VALUE
, "prefix", "Outlet prefix (mandatory)");
388 void upsdrv_initups(void)
393 val
= getval("prefix");
395 fatalx(EXIT_FAILURE
, "Outlet prefix is mandatory for this driver!");
398 snprintf(buf
, sizeof(buf
), "%s.delay.shutdown", val
);
399 prefix
.delay
.shutdown
= xstrdup(buf
);
401 snprintf(buf
, sizeof(buf
), "%s.timer.shutdown", val
);
402 prefix
.timer
.shutdown
= xstrdup(buf
);
404 snprintf(buf
, sizeof(buf
), "%s.status", val
);
405 prefix
.status
= xstrdup(buf
);
407 extrafd
= upsfd
= sstate_connect();
411 void upsdrv_cleanup(void)
413 free(prefix
.delay
.shutdown
);
414 free(prefix
.timer
.shutdown
);