1074 lines
21 KiB
C
1074 lines
21 KiB
C
/* upsstats - cgi program to generate the main ups info page
|
|
|
|
Copyright (C) 1998 Russell Kroll <rkroll@exploits.org>
|
|
Copyright (C) 2005 Arnaud Quette <http://arnaud.quette.free.fr/contact.html>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "nut_stdint.h"
|
|
#include "timehead.h"
|
|
#include "upsclient.h"
|
|
#include "status.h"
|
|
#include "cgilib.h"
|
|
#include "parseconf.h"
|
|
#include "upsstats.h"
|
|
#include "upsimagearg.h"
|
|
|
|
#define MAX_CGI_STRLEN 128
|
|
#define MAX_PARSE_ARGS 16
|
|
|
|
static char *monhost = NULL;
|
|
static int use_celsius = 1, refreshdelay = -1, treemode = 0;
|
|
|
|
/* from cgilib's checkhost() */
|
|
static char *monhostdesc = NULL;
|
|
|
|
static uint16_t port;
|
|
static char *upsname, *hostname;
|
|
static char *upsimgpath="upsimage.cgi", *upsstatpath="upsstats.cgi";
|
|
static UPSCONN_t ups;
|
|
|
|
static FILE *tf;
|
|
static long forofs = 0;
|
|
|
|
static ulist_t *ulhead = NULL, *currups = NULL;
|
|
|
|
static int skip_clause = 0, skip_block = 0;
|
|
|
|
void parsearg(char *var, char *value)
|
|
{
|
|
/* avoid bogus junk from evil people */
|
|
if ((strlen(var) > MAX_CGI_STRLEN) || (strlen(value) > MAX_CGI_STRLEN))
|
|
return;
|
|
|
|
if (!strcmp(var, "host")) {
|
|
free(monhost);
|
|
monhost = xstrdup(value);
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(var, "refresh"))
|
|
refreshdelay = (int) strtol(value, (char **) NULL, 10);
|
|
|
|
if (!strcmp(var, "treemode")) {
|
|
/* FIXME: Validate that treemode is allowed */
|
|
treemode = 1;
|
|
}
|
|
}
|
|
|
|
static void report_error(void)
|
|
{
|
|
if (upscli_upserror(&ups) == UPSCLI_ERR_VARNOTSUPP)
|
|
printf("Not supported\n");
|
|
else
|
|
printf("[error: %s]\n", upscli_strerror(&ups));
|
|
}
|
|
|
|
/* make sure we're actually connected to upsd */
|
|
static int check_ups_fd(int do_report)
|
|
{
|
|
if (upscli_fd(&ups) == -1) {
|
|
if (do_report)
|
|
report_error();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* also check for insanity in currups */
|
|
|
|
if (!currups) {
|
|
if (do_report)
|
|
printf("No UPS specified for monitoring\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* must be OK */
|
|
return 1;
|
|
}
|
|
|
|
static int get_var(const char *var, char *buf, size_t buflen, int verbose)
|
|
{
|
|
int ret;
|
|
size_t numq, numa;
|
|
const char *query[4];
|
|
char **answer;
|
|
|
|
if (!check_ups_fd(1))
|
|
return 0;
|
|
|
|
if (!upsname) {
|
|
if (verbose)
|
|
printf("[No UPS name specified]\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
query[0] = "VAR";
|
|
query[1] = upsname;
|
|
query[2] = var;
|
|
|
|
numq = 3;
|
|
|
|
ret = upscli_get(&ups, numq, query, &numa, &answer);
|
|
|
|
if (ret < 0) {
|
|
if (verbose)
|
|
report_error();
|
|
return 0;
|
|
}
|
|
|
|
if (numa < numq) {
|
|
if (verbose)
|
|
printf("[Invalid response]\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
snprintf(buf, buflen, "%s", answer[3]);
|
|
return 1;
|
|
}
|
|
|
|
static void parse_var(const char *var)
|
|
{
|
|
char answer[SMALLBUF];
|
|
|
|
if (!get_var(var, answer, sizeof(answer), 1))
|
|
return;
|
|
|
|
printf("%s", answer);
|
|
}
|
|
|
|
static void do_status(void)
|
|
{
|
|
int i;
|
|
char status[SMALLBUF], *ptr, *last = NULL;
|
|
|
|
if (!get_var("ups.status", status, sizeof(status), 1)) {
|
|
return;
|
|
}
|
|
|
|
for (ptr = strtok_r(status, " \n", &last); ptr != NULL; ptr = strtok_r(NULL, " \n", &last)) {
|
|
|
|
/* expand from table in status.h */
|
|
for (i = 0; stattab[i].name != NULL; i++) {
|
|
|
|
if (!strcasecmp(ptr, stattab[i].name)) {
|
|
printf("%s<br>", stattab[i].desc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void do_runtime(void)
|
|
{
|
|
int total, hours, minutes, seconds;
|
|
char runtime[SMALLBUF];
|
|
|
|
if (!get_var("battery.runtime", runtime, sizeof(runtime), 1))
|
|
return;
|
|
|
|
total = (int) strtol(runtime, (char **) NULL, 10);
|
|
|
|
hours = total / 3600;
|
|
minutes = (total - (hours * 3600)) / 60;
|
|
seconds = total % 60;
|
|
|
|
printf("%02d:%02d:%02d", hours, minutes, seconds);
|
|
|
|
}
|
|
|
|
static int do_date(const char *buf)
|
|
{
|
|
char datebuf[SMALLBUF];
|
|
time_t tod;
|
|
struct tm tmbuf;
|
|
|
|
time(&tod);
|
|
if (strftime(datebuf, sizeof(datebuf), buf, localtime_r(&tod, &tmbuf))) {
|
|
printf("%s", datebuf);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_img_val(const char *var, const char *desc, const char *imgargs)
|
|
{
|
|
char answer[SMALLBUF];
|
|
|
|
if (!get_var(var, answer, sizeof(answer), 1))
|
|
return 1;
|
|
|
|
printf("<IMG SRC=\"%s?host=%s&display=%s",
|
|
upsimgpath, currups->sys, var);
|
|
|
|
if ((imgargs) && (strlen(imgargs) > 0))
|
|
printf("&%s", imgargs);
|
|
|
|
printf("\" ALT=\"%s: %s\">", desc, answer);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* see if <arg> is valid - table from upsimagearg.h */
|
|
static void check_imgarg(char *arg, char *out, size_t outlen)
|
|
{
|
|
int i;
|
|
char *ep;
|
|
|
|
ep = strchr(arg, '=');
|
|
|
|
if (!ep)
|
|
return;
|
|
|
|
*ep++= '\0';
|
|
|
|
/* if it's allowed, append it so it can become part of the URL */
|
|
for (i = 0; imgarg[i].name != NULL; i++) {
|
|
if (!strcmp(imgarg[i].name, arg)) {
|
|
|
|
if (strlen(out) == 0)
|
|
snprintf(out, outlen, "%s=%s", arg, ep);
|
|
else
|
|
snprintfcat(out, outlen, "&%s=%s", arg, ep);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* split out the var=val commands from the IMG line */
|
|
static void split_imgarg(char *in, char *out, size_t outlen)
|
|
{
|
|
char *ptr, *sp;
|
|
|
|
if (strlen(in) < 3)
|
|
return;
|
|
|
|
ptr = in;
|
|
|
|
sp = strchr(ptr, ' ');
|
|
|
|
/* split by spaces, then check each one (can't use parseconf...) */
|
|
while (sp) {
|
|
*sp++ = '\0';
|
|
check_imgarg(ptr, out, outlen);
|
|
|
|
ptr = sp;
|
|
sp = strchr(ptr, ' ');
|
|
}
|
|
|
|
check_imgarg(ptr, out, outlen);
|
|
}
|
|
|
|
/* IMG <type> [<var>=<val] [<var>=<val>] ... */
|
|
static int do_img(char *buf)
|
|
{
|
|
char *type, *ptr, imgargs[SMALLBUF];
|
|
|
|
memset(imgargs, '\0', sizeof(imgargs));
|
|
|
|
type = buf;
|
|
|
|
ptr = strchr(buf, ' ');
|
|
|
|
if (ptr) {
|
|
*ptr++ = '\0';
|
|
split_imgarg(ptr, imgargs, sizeof(imgargs));
|
|
}
|
|
|
|
/* only allow known types through */
|
|
|
|
if (!strcmp(type, "input.voltage")
|
|
|| !strcmp(type, "input.L1-N.voltage")
|
|
|| !strcmp(type, "input.L2-N.voltage")
|
|
|| !strcmp(type, "input.L3-N.voltage")
|
|
|| !strcmp(type, "input.L1-L2.voltage")
|
|
|| !strcmp(type, "input.L2-L3.voltage")
|
|
|| !strcmp(type, "input.L3-L1.voltage")) {
|
|
return get_img_val(type, "Input voltage", imgargs);
|
|
}
|
|
|
|
if (!strcmp(type, "battery.voltage"))
|
|
return get_img_val(type, "Battery voltage", imgargs);
|
|
|
|
if (!strcmp(type, "battery.charge"))
|
|
return get_img_val(type, "Battery charge", imgargs);
|
|
|
|
if (!strcmp(type, "output.voltage")
|
|
|| !strcmp(type, "output.L1-N.voltage")
|
|
|| !strcmp(type, "output.L2-N.voltage")
|
|
|| !strcmp(type, "output.L3-N.voltage")
|
|
|| !strcmp(type, "output.L1-L2.voltage")
|
|
|| !strcmp(type, "output.L2-L3.voltage")
|
|
|| !strcmp(type, "output.L3-L1.voltage")) {
|
|
return get_img_val(type, "Output voltage", imgargs);
|
|
}
|
|
|
|
if (!strcmp(type, "ups.load")
|
|
|| !strcmp(type, "output.L1.power.percent")
|
|
|| !strcmp(type, "output.L2.power.percent")
|
|
|| !strcmp(type, "output.L3.power.percent")
|
|
|| !strcmp(type, "output.L1.realpower.percent")
|
|
|| !strcmp(type, "output.L2.realpower.percent")
|
|
|| !strcmp(type, "output.L3.realpower.percent")) {
|
|
return get_img_val(type, "UPS load", imgargs);
|
|
}
|
|
|
|
if (!strcmp(type, "input.frequency"))
|
|
return get_img_val(type, "Input frequency", imgargs);
|
|
|
|
if (!strcmp(type, "output.frequency"))
|
|
return get_img_val(type, "Output frequency", imgargs);
|
|
|
|
if (!strcmp(type, "ups.temperature"))
|
|
return get_img_val(type, "UPS temperature", imgargs);
|
|
|
|
if (!strcmp(type, "ambient.temperature"))
|
|
return get_img_val(type, "Ambient temperature", imgargs);
|
|
|
|
if (!strcmp(type, "ambient.humidity"))
|
|
return get_img_val(type, "Ambient humidity", imgargs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ups_connect(void)
|
|
{
|
|
static ulist_t *lastups = NULL;
|
|
char *newups, *newhost;
|
|
uint16_t newport;
|
|
|
|
/* try to minimize reconnects */
|
|
if (lastups) {
|
|
|
|
/* don't reconnect if these are both the same UPS */
|
|
if (!strcmp(lastups->sys, currups->sys)) {
|
|
lastups = currups;
|
|
return;
|
|
}
|
|
|
|
/* see if it's just on the same host */
|
|
newups = newhost = NULL;
|
|
|
|
if (upscli_splitname(currups->sys, &newups, &newhost,
|
|
&newport) != 0) {
|
|
printf("Unusable UPS definition [%s]\n", currups->sys);
|
|
fprintf(stderr, "Unusable UPS definition [%s]\n",
|
|
currups->sys);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if ((!strcmp(newhost, hostname)) && (port == newport)) {
|
|
free(upsname);
|
|
upsname = newups;
|
|
|
|
free(newhost);
|
|
lastups = currups;
|
|
return;
|
|
}
|
|
|
|
/* not the same upsd, so disconnect */
|
|
free(newups);
|
|
free(newhost);
|
|
}
|
|
|
|
upscli_disconnect(&ups);
|
|
|
|
free(upsname);
|
|
free(hostname);
|
|
|
|
if (upscli_splitname(currups->sys, &upsname, &hostname, &port) != 0) {
|
|
printf("Unusable UPS definition [%s]\n", currups->sys);
|
|
fprintf(stderr, "Unusable UPS definition [%s]\n", currups->sys);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (upscli_connect(&ups, hostname, port, 0) < 0)
|
|
fprintf(stderr, "UPS [%s]: can't connect to server: %s\n", currups->sys, upscli_strerror(&ups));
|
|
|
|
lastups = currups;
|
|
}
|
|
|
|
static void do_hostlink(void)
|
|
{
|
|
if (!currups) {
|
|
return;
|
|
}
|
|
|
|
printf("<a href=\"%s?host=%s", upsstatpath, currups->sys);
|
|
|
|
if (refreshdelay > 0) {
|
|
printf("&refresh=%d", refreshdelay);
|
|
}
|
|
|
|
printf("\">%s</a>", currups->desc);
|
|
}
|
|
|
|
static void do_treelink(void)
|
|
{
|
|
if (!currups) {
|
|
return;
|
|
}
|
|
|
|
printf("<a href=\"%s?host=%s&treemode\">All data</a>", upsstatpath, currups->sys);
|
|
}
|
|
|
|
/* see if the UPS supports this variable - skip to the next ENDIF if not */
|
|
/* if val is not null, value returned by var must be equal to val to match */
|
|
static void do_ifsupp(const char *var, const char *val)
|
|
{
|
|
char dummy[SMALLBUF];
|
|
|
|
/* if not connected, act like it's not supported and skip the rest */
|
|
if (!check_ups_fd(0)) {
|
|
skip_clause = 1;
|
|
return;
|
|
}
|
|
|
|
if (!get_var(var, dummy, sizeof(dummy), 0)) {
|
|
skip_clause = 1;
|
|
return;
|
|
}
|
|
|
|
if(!val) {
|
|
return;
|
|
}
|
|
|
|
if(strcmp(dummy, val)) {
|
|
skip_clause = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int breakargs(char *s, char **aargs)
|
|
{
|
|
char *p;
|
|
int i=0;
|
|
|
|
aargs[i]=NULL;
|
|
|
|
for(p=s; *p && i<(MAX_PARSE_ARGS-1); p++) {
|
|
if(aargs[i] == NULL) {
|
|
aargs[i] = p;
|
|
aargs[i+1] = NULL;
|
|
}
|
|
if(*p==' ') {
|
|
*p='\0';
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/* Check how many valid args we got */
|
|
for(i=0; aargs[i]; i++);
|
|
|
|
return i;
|
|
}
|
|
|
|
static void do_ifeq(const char *s)
|
|
{
|
|
char var[SMALLBUF];
|
|
char *aa[MAX_PARSE_ARGS];
|
|
int nargs;
|
|
|
|
strcpy(var, s);
|
|
|
|
nargs = breakargs(var, aa);
|
|
if(nargs != 2) {
|
|
printf("upsstats: IFEQ: Argument error!\n");
|
|
return;
|
|
}
|
|
|
|
do_ifsupp(aa[0], aa[1]);
|
|
}
|
|
|
|
/* IFBETWEEN var1 var2 var3. Skip if var3 not between var1
|
|
* and var2 */
|
|
static void do_ifbetween(const char *s)
|
|
{
|
|
char var[SMALLBUF];
|
|
char *aa[MAX_PARSE_ARGS];
|
|
char tmp[SMALLBUF];
|
|
int nargs;
|
|
long v1, v2, v3;
|
|
char *isvalid=NULL;
|
|
|
|
strcpy(var, s);
|
|
|
|
nargs = breakargs(var, aa);
|
|
if(nargs != 3) {
|
|
printf("upsstats: IFBETWEEN: Argument error!\n");
|
|
return;
|
|
}
|
|
|
|
if (!check_ups_fd(0)) {
|
|
return;
|
|
}
|
|
|
|
if (!get_var(aa[0], tmp, sizeof(tmp), 0)) {
|
|
return;
|
|
}
|
|
v1 = strtol(tmp, &isvalid, 10);
|
|
if(tmp == isvalid) {
|
|
return;
|
|
}
|
|
|
|
if (!get_var(aa[1], tmp, sizeof(tmp), 0)) {
|
|
return;
|
|
}
|
|
v2 = strtol(tmp, &isvalid, 10);
|
|
if(tmp == isvalid) {
|
|
return;
|
|
}
|
|
|
|
if (!get_var(aa[2], tmp, sizeof(tmp), 0)) {
|
|
return;
|
|
}
|
|
v3 = strtol(tmp, &isvalid, 10);
|
|
if(tmp == isvalid) {
|
|
return;
|
|
}
|
|
|
|
if(v1 > v3 || v2 < v3) {
|
|
skip_clause = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void do_upsstatpath(const char *s) {
|
|
|
|
if(strlen(s)) {
|
|
upsstatpath = strdup(s);
|
|
}
|
|
}
|
|
|
|
static void do_upsimgpath(const char *s) {
|
|
|
|
if(strlen(s)) {
|
|
upsimgpath = strdup(s);
|
|
}
|
|
}
|
|
|
|
static void do_temp(const char *var)
|
|
{
|
|
char tempc[SMALLBUF];
|
|
double tempf;
|
|
|
|
if (!get_var(var, tempc, sizeof(tempc), 1))
|
|
return;
|
|
|
|
if (use_celsius) {
|
|
printf("%s", tempc);
|
|
return;
|
|
}
|
|
|
|
tempf = (strtod(tempc, (char **) NULL) * 1.8) + 32;
|
|
printf("%.1f", tempf);
|
|
}
|
|
|
|
static void do_degrees(void)
|
|
{
|
|
printf("°");
|
|
|
|
if (use_celsius)
|
|
printf("C");
|
|
else
|
|
printf("F");
|
|
}
|
|
|
|
/* plug in the right color string (like #FF0000) for the UPS status */
|
|
static void do_statuscolor(void)
|
|
{
|
|
int severity, i;
|
|
char stat[SMALLBUF], *sp, *ptr;
|
|
|
|
if (!check_ups_fd(0)) {
|
|
|
|
/* can't print the warning here - give a red error condition */
|
|
printf("#FF0000");
|
|
return;
|
|
}
|
|
|
|
if (!get_var("ups.status", stat, sizeof(stat), 0)) {
|
|
/* status not available - give yellow as a warning */
|
|
printf("#FFFF00");
|
|
return;
|
|
}
|
|
|
|
severity = 0;
|
|
sp = stat;
|
|
|
|
while (sp) {
|
|
ptr = strchr(sp, ' ');
|
|
if (ptr)
|
|
*ptr++ = '\0';
|
|
|
|
/* expand from table in status.h */
|
|
for (i = 0; stattab[i].name != NULL; i++)
|
|
if (!strcmp(stattab[i].name, sp))
|
|
if (stattab[i].severity > severity)
|
|
severity = stattab[i].severity;
|
|
|
|
sp = ptr;
|
|
}
|
|
|
|
switch(severity) {
|
|
case 0: printf("#00FF00"); break; /* green : OK */
|
|
case 1: printf("#FFFF00"); break; /* yellow : warning */
|
|
|
|
default: printf("#FF0000"); break; /* red : error */
|
|
}
|
|
}
|
|
|
|
static int do_command(char *cmd)
|
|
{
|
|
/* ending an if block? */
|
|
if (!strcmp(cmd, "ENDIF")) {
|
|
skip_clause = 0;
|
|
skip_block = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Skipping a block means skip until ENDIF, so... */
|
|
if (skip_block) {
|
|
return 1;
|
|
}
|
|
|
|
/* Toggle state when we run across ELSE */
|
|
if (!strcmp(cmd, "ELSE")) {
|
|
if (skip_clause) {
|
|
skip_clause = 0;
|
|
} else {
|
|
skip_block = 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* don't do any commands if skipping a section */
|
|
if (skip_clause == 1) {
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(cmd, "VAR ", 4)) {
|
|
parse_var(&cmd[4]);
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "HOST")) {
|
|
printf("%s", currups->sys);
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "HOSTDESC")) {
|
|
printf("%s", currups->desc);
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "RUNTIME")) {
|
|
do_runtime();
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "STATUS")) {
|
|
do_status();
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "STATUSCOLOR")) {
|
|
do_statuscolor();
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "TEMPF")) {
|
|
use_celsius = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "TEMPC")) {
|
|
use_celsius = 1;
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(cmd, "DATE ", 5)) {
|
|
return do_date(&cmd[5]);
|
|
}
|
|
|
|
if (!strncmp(cmd, "IMG ", 4)) {
|
|
return do_img(&cmd[4]);
|
|
}
|
|
|
|
if (!strcmp(cmd, "VERSION")) {
|
|
printf("%s", UPS_VERSION);
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "REFRESH")) {
|
|
if (refreshdelay > 0) {
|
|
printf("<META HTTP-EQUIV=\"Refresh\" CONTENT=\"%d\">", refreshdelay);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "FOREACHUPS")) {
|
|
forofs = ftell(tf);
|
|
|
|
currups = ulhead;
|
|
ups_connect();
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "ENDFOR")) {
|
|
|
|
/* if not in a for, ignore this */
|
|
if (forofs == 0) {
|
|
return 1;
|
|
}
|
|
|
|
currups = currups->next;
|
|
|
|
if (currups) {
|
|
fseek(tf, forofs, SEEK_SET);
|
|
ups_connect();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "HOSTLINK")) {
|
|
do_hostlink();
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "TREELINK")) {
|
|
do_treelink();
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(cmd, "IFSUPP ", 7)) {
|
|
do_ifsupp(&cmd[7], NULL);
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "UPSTEMP")) {
|
|
do_temp("ups.temperature");
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "BATTTEMP")) {
|
|
do_temp("battery.temperature");
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "AMBTEMP")) {
|
|
do_temp("ambient.temperature");
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(cmd, "DEGREES")) {
|
|
do_degrees();
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(cmd, "IFEQ ", 5)) {
|
|
do_ifeq(&cmd[5]);
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(cmd, "IFBETWEEN ", 10)) {
|
|
do_ifbetween(&cmd[10]);
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(cmd, "UPSSTATSPATH ", 13)) {
|
|
do_upsstatpath(&cmd[13]);
|
|
return 1;
|
|
}
|
|
|
|
if (!strncmp(cmd, "UPSIMAGEPATH ", 13)) {
|
|
do_upsimgpath(&cmd[13]);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void parse_line(const char *buf)
|
|
{
|
|
char cmd[SMALLBUF];
|
|
size_t i, len;
|
|
char do_cmd = 0;
|
|
|
|
for (i = 0; buf[i]; i += len) {
|
|
|
|
len = strcspn(&buf[i], "@");
|
|
|
|
if (len == 0) {
|
|
if (do_cmd) {
|
|
do_command(cmd);
|
|
do_cmd = 0;
|
|
} else {
|
|
cmd[0] = '\0';
|
|
do_cmd = 1;
|
|
}
|
|
i++; /* skip over the '@' character */
|
|
continue;
|
|
}
|
|
assert (len < INT_MAX);
|
|
|
|
if (do_cmd) {
|
|
snprintf(cmd, sizeof(cmd), "%.*s", (int)len, &buf[i]);
|
|
continue;
|
|
}
|
|
|
|
if (skip_clause || skip_block) {
|
|
/* ignore this */
|
|
continue;
|
|
}
|
|
|
|
/* pass it trough */
|
|
printf("%.*s", (int)len, &buf[i]);
|
|
}
|
|
}
|
|
|
|
static void display_template(const char *tfn)
|
|
{
|
|
char fn[SMALLBUF], buf[LARGEBUF];
|
|
|
|
snprintf(fn, sizeof(fn), "%s/%s", confpath(), tfn);
|
|
|
|
tf = fopen(fn, "r");
|
|
|
|
if (!tf) {
|
|
fprintf(stderr, "upsstats: Can't open %s: %s\n", fn, strerror(errno));
|
|
|
|
printf("Error: can't open template file (%s)\n", tfn);
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), tf)) {
|
|
parse_line(buf);
|
|
}
|
|
|
|
fclose(tf);
|
|
}
|
|
|
|
static void display_tree(int verbose)
|
|
{
|
|
size_t numq, numa;
|
|
const char *query[4];
|
|
char **answer;
|
|
|
|
if (!upsname) {
|
|
if (verbose)
|
|
printf("[No UPS name specified]\n");
|
|
return;
|
|
}
|
|
|
|
query[0] = "VAR";
|
|
query[1] = upsname;
|
|
numq = 2;
|
|
|
|
if (upscli_list_start(&ups, numq, query) < 0) {
|
|
if (verbose)
|
|
report_error();
|
|
return;
|
|
}
|
|
|
|
printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n");
|
|
printf(" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n");
|
|
printf("<HTML>\n");
|
|
printf("<HEAD><TITLE>upsstat: data tree of %s</TITLE></HEAD>\n", currups->desc);
|
|
|
|
printf("<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000EE\" VLINK=\"#551A8B\">\n");
|
|
|
|
printf("<TABLE BGCOLOR=\"#50A0A0\" ALIGN=\"CENTER\">\n");
|
|
printf("<TR><TD>\n");
|
|
|
|
printf("<TABLE CELLPADDING=\"5\" CELLSPACING=\"0\" ALIGN=\"CENTER\" WIDTH=\"100%%\">\n");
|
|
|
|
/* include the description from checkhost() if present */
|
|
printf("<TR><TH COLSPAN=3 BGCOLOR=\"#50A0A0\">\n");
|
|
printf("<FONT SIZE=\"+2\">%s</FONT>\n", currups->desc);
|
|
printf("</TH></TR>\n");
|
|
|
|
printf("<TR><TH COLSPAN=3 BGCOLOR=\"#60B0B0\"></TH></TR>\n");
|
|
|
|
while (upscli_list_next(&ups, numq, query, &numa, &answer) == 1) {
|
|
|
|
/* VAR <upsname> <varname> <val> */
|
|
if (numa < 4) {
|
|
if (verbose)
|
|
printf("[Invalid response]\n");
|
|
|
|
return;
|
|
}
|
|
|
|
printf("<TR BGCOLOR=\"#60B0B0\" ALIGN=\"LEFT\">\n");
|
|
|
|
printf("<TD>%s</TD>\n", answer[2]);
|
|
printf("<TD>:</TD>\n");
|
|
printf("<TD>%s<br></TD>\n", answer[3]);
|
|
|
|
printf("</TR>\n");
|
|
}
|
|
|
|
printf("</TABLE>\n");
|
|
printf("</TD></TR></TABLE>\n");
|
|
|
|
/* FIXME (AQ): add a save button (?), and a checkbt for showing var.desc */
|
|
printf("</BODY></HTML>\n");
|
|
}
|
|
|
|
static void add_ups(char *sys, char *desc)
|
|
{
|
|
ulist_t *tmp, *last;
|
|
|
|
tmp = last = ulhead;
|
|
|
|
while (tmp) {
|
|
last = tmp;
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
tmp = xmalloc(sizeof(ulist_t));
|
|
|
|
tmp->sys = xstrdup(sys);
|
|
tmp->desc = xstrdup(desc);
|
|
tmp->next = NULL;
|
|
|
|
if (last)
|
|
last->next = tmp;
|
|
else
|
|
ulhead = tmp;
|
|
}
|
|
|
|
/* called for fatal errors in parseconf like malloc failures */
|
|
static void upsstats_hosts_err(const char *errmsg)
|
|
{
|
|
upslogx(LOG_ERR, "Fatal error in parseconf(hosts.conf): %s", errmsg);
|
|
}
|
|
|
|
static void load_hosts_conf(void)
|
|
{
|
|
char fn[SMALLBUF];
|
|
PCONF_CTX_t ctx;
|
|
|
|
snprintf(fn, sizeof(fn), "%s/hosts.conf", CONFPATH);
|
|
|
|
pconf_init(&ctx, upsstats_hosts_err);
|
|
|
|
if (!pconf_file_begin(&ctx, fn)) {
|
|
pconf_finish(&ctx);
|
|
|
|
printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n");
|
|
printf(" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n");
|
|
printf("<HTML><HEAD>\n");
|
|
printf("<TITLE>Error: can't open hosts.conf</TITLE>\n");
|
|
printf("</HEAD><BODY>\n");
|
|
printf("Error: can't open hosts.conf\n");
|
|
printf("</BODY></HTML>\n");
|
|
|
|
/* leave something for the admin */
|
|
fprintf(stderr, "upsstats: %s\n", ctx.errmsg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
while (pconf_file_next(&ctx)) {
|
|
if (pconf_parse_error(&ctx)) {
|
|
upslogx(LOG_ERR, "Parse error: %s:%d: %s",
|
|
fn, ctx.linenum, ctx.errmsg);
|
|
continue;
|
|
}
|
|
|
|
if (ctx.numargs < 3)
|
|
continue;
|
|
|
|
/* MONITOR <host> <desc> */
|
|
if (!strcmp(ctx.arglist[0], "MONITOR"))
|
|
add_ups(ctx.arglist[1], ctx.arglist[2]);
|
|
|
|
}
|
|
|
|
pconf_finish(&ctx);
|
|
|
|
if (!ulhead) {
|
|
printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n");
|
|
printf(" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n");
|
|
printf("<HTML><HEAD>\n");
|
|
printf("<TITLE>Error: no hosts to monitor</TITLE>\n");
|
|
printf("</HEAD><BODY>\n");
|
|
printf("Error: no hosts to monitor (check <CODE>hosts.conf</CODE>)\n");
|
|
printf("</BODY></HTML>\n");
|
|
|
|
/* leave something for the admin */
|
|
fprintf(stderr, "upsstats: no hosts to monitor\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void display_single(void)
|
|
{
|
|
if (!checkhost(monhost, &monhostdesc)) {
|
|
printf("Access to that host [%s] is not authorized.\n",
|
|
monhost);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
add_ups(monhost, monhostdesc);
|
|
|
|
currups = ulhead;
|
|
ups_connect();
|
|
|
|
/* switch between data tree view and standard single view */
|
|
if (treemode)
|
|
display_tree(1);
|
|
else
|
|
display_template("upsstats-single.html");
|
|
|
|
upscli_disconnect(&ups);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
NUT_UNUSED_VARIABLE(argc);
|
|
NUT_UNUSED_VARIABLE(argv);
|
|
|
|
extractcgiargs();
|
|
|
|
printf("Content-type: text/html\n");
|
|
printf("Pragma: no-cache\n");
|
|
printf("\n");
|
|
|
|
/* if a host is specified, use upsstats-single.html instead */
|
|
if (monhost) {
|
|
display_single();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
/* default: multimon replacement mode */
|
|
|
|
load_hosts_conf();
|
|
|
|
currups = ulhead;
|
|
|
|
display_template("upsstats.html");
|
|
|
|
upscli_disconnect(&ups);
|
|
|
|
return 0;
|
|
}
|