apcupsd-ups: ignore generated files
[networkupstools/kirr.git] / drivers / clone-outlet.c
blob99d150cf120f22f47fc8f976264a3b18e8dee895
1 /*
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
21 #include "main.h"
22 #include "parseconf.h"
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/un.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 = {
33 DRIVER_NAME,
34 DRIVER_VERSION,
35 "Arjen de Korte <adkorte-guest@alioth.debian.org>",
36 DRV_EXPERIMENTAL,
37 { NULL }
40 static struct {
41 struct {
42 char *shutdown;
43 } delay;
44 struct {
45 char *shutdown;
46 } timer;
47 char *status;
48 } prefix = { { NULL }, { NULL }, NULL };
50 static struct {
51 struct {
52 long shutdown;
53 } delay;
54 struct {
55 long shutdown;
56 } timer;
57 int status;
58 } outlet = { { -1 }, { -1 }, 1 };
60 static struct {
61 char status[LARGEBUF];
62 } ups = { "WAIT" };
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)
71 if (numargs < 1) {
72 return 0;
75 if (!strcasecmp(arg[0], "PONG")) {
76 upsdebugx(3, "Got PONG from UPS");
77 return 1;
80 if (!strcasecmp(arg[0], "DUMPDONE")) {
81 upsdebugx(3, "UPS: dump is done");
82 dumpdone = 1;
83 return 1;
86 if (!strcasecmp(arg[0], "DATASTALE")) {
87 dstate_datastale();
88 return 1;
91 if (!strcasecmp(arg[0], "DATAOK")) {
92 dstate_dataok();
93 return 1;
96 if (numargs < 2) {
97 return 0;
100 /* DELINFO <var> */
101 if (!strcasecmp(arg[0], "DELINFO")) {
102 dstate_delinfo(arg[1]);
103 return 1;
106 if (numargs < 3) {
107 return 0;
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 */
115 return 1;
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]);
132 return 1;
135 dstate_setinfo(arg[1], "%s", arg[2]);
136 return 1;
139 return 0;
143 static int sstate_connect(void)
145 int ret, fd;
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);
155 if (fd < 0) {
156 upslog_with_errno(LOG_ERR, "Can't create socket for UPS [%s]", device_path);
157 return -1;
160 ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
162 if (ret < 0) {
163 time_t now;
165 close(fd);
167 /* rate-limit complaints - don't spam the syslog */
168 time(&now);
170 if (difftime(now, last_connfail) < 60) {
171 return -1;
174 last_connfail = now;
176 upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path);
177 return -1;
180 ret = fcntl(fd, F_GETFL, 0);
182 if (ret < 0) {
183 upslog_with_errno(LOG_ERR, "fcntl get on UPS [%s] failed", device_path);
184 close(fd);
185 return -1;
188 ret = fcntl(fd, F_SETFL, ret | O_NDELAY);
190 if (ret < 0) {
191 upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on UPS [%s] failed", device_path);
192 close(fd);
193 return -1;
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);
201 close(fd);
202 return -1;
205 pconf_init(&sock_ctx, NULL);
207 time(&last_heard);
209 dumpdone = 0;
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);
215 return fd;
219 static void sstate_disconnect(void)
221 if (upsfd < 0) {
222 return;
225 pconf_finish(&sock_ctx);
227 close(upsfd);
228 upsfd = -1;
232 static int sstate_sendline(const char *buf)
234 int ret;
236 if (upsfd < 0) {
237 return -1; /* failed */
240 ret = write(upsfd, buf, strlen(buf));
242 if (ret == (int)strlen(buf)) {
243 return 0;
246 upslog_with_errno(LOG_NOTICE, "Send to UPS [%s] failed", device_path);
247 return -1; /* failed */
251 static int sstate_readline(void)
253 int i, ret;
254 char buf[SMALLBUF];
256 if (upsfd < 0) {
257 return -1; /* failed */
260 ret = read(upsfd, buf, sizeof(buf));
262 if (ret < 0) {
263 switch(errno)
265 case EINTR:
266 case EAGAIN:
267 return 0;
269 default:
270 upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", device_path);
271 return -1;
275 for (i = 0; i < ret; i++) {
277 switch (pconf_char(&sock_ctx, buf[i]))
279 case 1:
280 if (parse_args(sock_ctx.numargs, sock_ctx.arglist)) {
281 time(&last_heard);
283 continue;
285 case 0:
286 continue; /* haven't gotten a line yet */
288 default:
289 /* parse error */
290 upslogx(LOG_NOTICE, "Parse error on sock: %s", sock_ctx.errmsg);
291 return -1;
295 return 0;
299 static int sstate_dead(int maxage)
301 time_t now;
302 double elapsed;
304 /* an unconnected ups is always dead */
305 if (upsfd < 0) {
306 upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", device_path);
307 return -1; /* dead */
310 time(&now);
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");
324 last_ping = now;
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 */
333 return 0;
337 void upsdrv_initinfo(void)
342 void upsdrv_updateinfo(void)
344 if (sstate_dead(15)) {
345 sstate_disconnect();
346 extrafd = upsfd = sstate_connect();
347 return;
350 if (sstate_readline()) {
351 sstate_disconnect();
352 return;
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);
358 return;
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);
364 return;
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)
390 char buf[SMALLBUF];
391 const char *val;
393 val = getval("prefix");
394 if (!val) {
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);
415 free(prefix.status);
417 sstate_disconnect();