--- /dev/null
+/* apcupsd.c: conky module for APC UPS daemon monitoring
+ *
+ * Copyright (C) 2009 Jaromir Smrcek <jaromir.smrcek@zoner.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA. */
+
+#include "conky.h"
+#include "apcupsd.h"
+
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+//
+// encapsulated recv()
+//
+static int net_recv_ex(int sock, void *buf, int size, struct timeval *tv)
+{
+
+ fd_set fds;
+ int res;
+
+ // wait for some data to be read
+ do {
+ errno = 0;
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ res = select(sock + 1, &fds, NULL, NULL, tv);
+ } while (res < 0 && errno == EINTR);
+ if (res < 0) return 0;
+ if (res == 0) {
+ // timeout
+ errno = ETIMEDOUT; // select was succesfull, errno is now 0
+ return 0;
+ }
+
+ // socket ready, read the data
+ do {
+ errno = 0;
+ res = recv(sock, (char*)buf, size, 0);
+ } while (res < 0 && errno == EINTR);
+ if (res < 0) return 0;
+ if (res == 0) {
+ // orderly shutdown
+ errno = ENOTCONN;
+ return 0;
+ }
+
+ return res;
+}
+
+//
+// read whole buffer or fail
+//
+static int net_recv(int sock, void* buf, int size) {
+
+ int todo = size;
+ int off = 0;
+ int len;
+ struct timeval tv = { 0, 250000 };
+
+ while (todo) {
+ len = net_recv_ex(sock, (char*)buf + off, todo, &tv);
+ if (!len) return 0;
+ todo -= len;
+ off += len;
+ }
+ return 1;
+}
+
+//
+// get one response line
+//
+static int get_line(int sock, char line[], short linesize) {
+
+ // get the line length
+ short sz;
+ if (!net_recv(sock, &sz, sizeof(sz))) return -1;
+ sz = ntohs(sz);
+ if (!sz) return 0;
+
+ // get the line
+ while (sz > linesize) {
+ // this is just a hack (being lazy), this should not happen anyway
+ net_recv(sock, line, linesize);
+ sz -= sizeof(line);
+ }
+ if (!net_recv(sock, line, sz)) return 0;
+ line[sz] = 0;
+ return sz;
+}
+
+#define FILL(NAME,FIELD,FIRST) \
+ if (!strncmp(NAME, line, sizeof(NAME)-1)) { \
+ strncpy(apc->items[FIELD], line+11, APCUPSD_MAXSTR); \
+ /* remove trailing newline and assure termination */ \
+ apc->items[FIELD][len-11 > APCUPSD_MAXSTR ? APCUPSD_MAXSTR : len-12] = 0; \
+ if (FIRST) { \
+ char* c; \
+ for (c = apc->items[FIELD]; *c; ++c) \
+ if (*c == ' ' && c > apc->items[FIELD]+2) { \
+ *c = 0; \
+ break; \
+ } \
+ } \
+ }
+
+//
+// fills in the data received from a socket
+//
+static int fill_items(int sock, PAPCUPSD_S apc) {
+
+ char line[512];
+ int len;
+ while ((len = get_line(sock, line, sizeof(line)))) {
+ // fill the right types in
+ FILL("UPSNAME", APCUPSD_NAME, FALSE);
+ FILL("MODEL", APCUPSD_MODEL, FALSE);
+ FILL("UPSMODE", APCUPSD_UPSMODE, FALSE);
+ FILL("CABLE", APCUPSD_CABLE, FALSE);
+ FILL("STATUS", APCUPSD_STATUS, FALSE);
+ FILL("LINEV", APCUPSD_LINEV, TRUE);
+ FILL("LOADPCT", APCUPSD_LOAD, TRUE);
+ FILL("BCHARGE", APCUPSD_CHARGE, TRUE);
+ FILL("TIMELEFT", APCUPSD_TIMELEFT, TRUE);
+ FILL("ITEMP", APCUPSD_TEMP, TRUE);
+ FILL("LASTXFER", APCUPSD_LASTXFER, FALSE);
+ }
+
+ return len == 0;
+}
+
+//
+// Conky update function for apcupsd data
+//
+void update_apcupsd(void) {
+
+ int i;
+ APCUPSD_S apc;
+ int sock;
+
+ for (i = 0; i < _APCUPSD_COUNT; ++i)
+ memcpy(apc.items[i], "N/A", 4); // including \0
+
+ do {
+ struct hostent* he = 0;
+ struct sockaddr_in addr;
+ short sz = 0;
+ //
+ // connect to apcupsd daemon
+ //
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ break;
+ }
+ he = gethostbyname(info.apcupsd.host);
+ if (!he) {
+ herror("gethostbyname");
+ break;
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = info.apcupsd.port;
+ memcpy(&addr.sin_addr, he->h_addr, he->h_length);
+ if (connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0) {
+ // no error reporting, the daemon is probably not running
+ break;
+ }
+
+ //
+ // send status request - "status" - 6B
+ //
+ sz = htons(6);
+ // no waiting to become writeable is really needed
+ if (send(sock, &sz, sizeof(sz), 0) != sizeof(sz) || send(sock, "status", 6, 0) != 6) {
+ perror("send");
+ break;
+ }
+
+ //
+ // read the lines of output and put them into the info structure
+ //
+ if (!fill_items(sock, &apc)) break;
+
+ } while (0);
+
+ close(sock);
+
+ //
+ // "atomically" copy the data into working set
+ //
+ memcpy(info.apcupsd.items, apc.items, sizeof(info.apcupsd.items));
+ return;
+}
+
+//
+// fills in the N/A strings and default host:port
+//
+void init_apcupsd(void) {
+
+ int i;
+ for (i = 0; i < _APCUPSD_COUNT; ++i)
+ memcpy(info.apcupsd.items[i], "N/A", 4); // including \0
+ memcpy(info.apcupsd.host, "localhost", 10);
+ info.apcupsd.port = htons(3551);
+}
--- /dev/null
+/* apcupsd.h: conky module for APC UPS daemon monitoring
+ *
+ * Copyright (C) 2009 Jaromir Smrcek <jaromir.smrcek@zoner.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA. */
+
+#ifndef APCUPSD_H_
+#define APCUPSD_H_
+
+enum _apcupsd_items {
+ APCUPSD_NAME,
+ APCUPSD_MODEL,
+ APCUPSD_UPSMODE,
+ APCUPSD_CABLE,
+ APCUPSD_STATUS,
+ APCUPSD_LINEV,
+ APCUPSD_LOAD,
+ APCUPSD_CHARGE,
+ APCUPSD_TIMELEFT,
+ APCUPSD_TEMP,
+ APCUPSD_LASTXFER,
+ _APCUPSD_COUNT,
+};
+
+/* type for data exchange with main thread */
+#define APCUPSD_MAXSTR 32
+typedef struct apcupsd_s {
+ char items[_APCUPSD_COUNT][APCUPSD_MAXSTR+1]; /* e.g. items[APCUPSD_STATUS] */
+ char host[64];
+ int port;
+} APCUPSD_S, *PAPCUPSD_S;
+
+/* Service routine for the conky main thread */
+void update_apcupsd(void);
+
+/* fill in the default values */
+void init_apcupsd(void);
+
+#endif /*APCUPSD_H_*/
case OBJ_apcupsd_load:
case OBJ_apcupsd_loadbar:
case OBJ_apcupsd_loadgraph:
+ case OBJ_apcupsd_loadgauge:
case OBJ_apcupsd_charge:
case OBJ_apcupsd_timeleft:
case OBJ_apcupsd_temp:
END OBJ(apcupsd_loadgraph, INFO_APCUPSD)
char* buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d, &obj->e, &obj->showaslog);
if (buf) free(buf);
+ END OBJ(apcupsd_loadgauge, INFO_APCUPSD)
+ scan_gauge(arg, &obj->a, &obj->b);
END OBJ(apcupsd_charge, INFO_APCUPSD)
END OBJ(apcupsd_timeleft, INFO_APCUPSD)
END OBJ(apcupsd_temp, INFO_APCUPSD)
new_graph(p, obj->a, obj->b, obj->c, obj->d,
(int)progress, 100, 1, obj->showaslog);
}
+ OBJ(apcupsd_loadgauge) {
+ double progress;
+ progress = atof(cur->apcupsd.items[APCUPSD_LOAD]);
+ new_gauge(p, obj->a, obj->b,
+ (int)progress);
+ }
OBJ(apcupsd_charge) {
snprintf(p, p_max_size, "%s",
cur->apcupsd.items[APCUPSD_CHARGE]);