nut-debian/clients/upsset.c
2022-07-10 09:23:45 +02:00

1120 lines
24 KiB
C

/* upsset - CGI program to manage read/write variables
Copyright (C) 1999 Russell Kroll <rkroll@exploits.org>
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 <netdb.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "nut_stdint.h"
#include "upsclient.h"
#include "cgilib.h"
#include "parseconf.h"
struct list_t {
char *name;
struct list_t *next;
};
/* see the stock upsset.conf for the whole rant on what this is */
#define MAGIC_ENABLE_STRING "I_HAVE_SECURED_MY_CGI_DIRECTORY"
#define HARD_UPSVAR_LIMIT_NUM 64
#define HARD_UPSVAR_LIMIT_LEN 256
static char *monups, *username, *password, *function, *upscommand;
/* set once the MAGIC_ENABLE_STRING is found in the upsset.conf */
static int magic_string_set = 0;
static uint16_t port;
static char *upsname, *hostname;
static UPSCONN_t ups;
typedef struct {
char *var;
char *value;
void *next;
} uvtype_t;
static uvtype_t *firstuv = NULL;
void parsearg(char *var, char *value)
{
char *ptr;
uvtype_t *last, *tmp = NULL;
static int upsvc = 0;
/* store variables from a SET command for the later commit */
if (!strncmp(var, "UPSVAR_", 7)) {
/* if someone bombs us with variables, stop at some point */
if (upsvc > HARD_UPSVAR_LIMIT_NUM)
return;
/* same idea: throw out anything that's much too long */
if (strlen(value) > HARD_UPSVAR_LIMIT_LEN)
return;
ptr = strchr(var, '_');
if (!ptr) /* sanity check */
return;
ptr++;
tmp = last = firstuv;
while (tmp) {
last = tmp;
tmp = tmp->next;
}
tmp = xmalloc(sizeof(uvtype_t));
tmp->var = xstrdup(ptr);
tmp->value = xstrdup(value);
tmp->next = NULL;
if (last)
last->next = tmp;
else
firstuv = tmp;
upsvc++;
return;
}
if (!strcmp(var, "username")) {
free(username);
username = xstrdup(value);
}
if (!strcmp(var, "password")) {
free(password);
password = xstrdup(value);
}
if (!strcmp(var, "function")) {
free(function);
function = xstrdup(value);
}
if (!strcmp(var, "monups")) {
free(monups);
monups = xstrdup(value);
}
if (!strcmp(var, "upscommand")) {
free(upscommand);
upscommand = xstrdup(value);
}
}
static void do_header(const char *title)
{
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>upsset: %s</TITLE></HEAD>\n", title);
printf("<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000EE\" VLINK=\"#551A8B\">\n");
printf("<TABLE BGCOLOR=\"#50A0A0\" ALIGN=\"CENTER\">\n");
printf("<TR><TD>\n");
}
static void start_table(void)
{
printf("<TABLE CELLPADDING=\"5\" CELLSPACING=\"0\" ALIGN=\"CENTER\" WIDTH=\"100%%\">\n");
printf("<TR><TH COLSPAN=2 BGCOLOR=\"#60B0B0\">\n");
printf("<FONT SIZE=\"+2\">Network UPS Tools upsset %s</FONT>\n",
UPS_VERSION);
printf("</TH></TR>\n");
}
/* propagate login details across pages - no cookies here! */
static void do_hidden(const char *next)
{
printf("<INPUT TYPE=\"HIDDEN\" NAME=\"username\" VALUE=\"%s\">\n",
username);
printf("<INPUT TYPE=\"HIDDEN\" NAME=\"password\" VALUE=\"%s\">\n",
password);
if (next)
printf("<INPUT TYPE=\"HIDDEN\" NAME=\"function\" VALUE=\"%s\">\n",
next);
}
/* generate SELECT chooser from hosts.conf entries */
static void upslist_arg(size_t numargs, char **arg)
{
if (numargs < 3)
return;
/* MONITOR <ups> <description> */
if (!strcmp(arg[0], "MONITOR")) {
printf("<OPTION VALUE=\"%s\"", arg[1]);
if (monups)
if (!strcmp(monups, arg[1]))
printf("SELECTED");
printf(">%s</OPTION>\n", arg[2]);
}
}
/* called for fatal errors in parseconf like malloc failures */
static void upsset_hosts_err(const char *errmsg)
{
upslogx(LOG_ERR, "Fatal error in parseconf(hosts.conf): %s", errmsg);
}
/* this defaults to wherever we are now, ups and function-wise */
static void do_pickups(const char *currfunc)
{
char hostfn[SMALLBUF];
PCONF_CTX_t ctx;
snprintf(hostfn, sizeof(hostfn), "%s/hosts.conf", confpath());
printf("<FORM METHOD=\"POST\" ACTION=\"upsset.cgi\">\n");
printf("Select UPS and function:\n<BR>\n");
pconf_init(&ctx, upsset_hosts_err);
if (!pconf_file_begin(&ctx, hostfn)) {
pconf_finish(&ctx);
printf("Error: hosts.conf unavailable\n");
printf("</FORM>\n");
/* stderr is for the admin - should wind up in error.log */
fprintf(stderr, "upsset: %s\n", ctx.errmsg);
return;
}
printf("<SELECT NAME=\"monups\">\n");
while (pconf_file_next(&ctx)) {
if (pconf_parse_error(&ctx)) {
upslogx(LOG_ERR, "Parse error: %s:%d: %s",
hostfn, ctx.linenum, ctx.errmsg);
continue;
}
upslist_arg(ctx.numargs, ctx.arglist);
}
pconf_finish(&ctx);
printf("</SELECT>\n");
printf("<SELECT NAME=\"function\">\n");
/* FUTURE */
/* printf("<OPTION VALUE=\"showstatus\">Status</OPTION>\n"); */
/* TODO: clean this up */
if (!strcmp(currfunc, "showsettings"))
printf("<OPTION VALUE=\"showsettings\" SELECTED>Settings</OPTION>\n");
else
printf("<OPTION VALUE=\"showsettings\">Settings</OPTION>\n");
if (!strcmp(currfunc, "showcmds"))
printf("<OPTION VALUE=\"showcmds\" SELECTED>Commands</OPTION>\n");
else
printf("<OPTION VALUE=\"showcmds\">Commands</OPTION>\n");
printf("</SELECT>\n");
do_hidden(NULL);
printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"View\">\n");
printf("</FORM>\n");
}
static void error_page(const char *next, const char *title,
const char *fmt, ...)
__attribute__((noreturn));
static void error_page(const char *next, const char *title,
const char *fmt, ...)
{
char msg[SMALLBUF];
va_list ap;
va_start(ap, fmt);
#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
#pragma GCC diagnostic push
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
vsnprintf(msg, sizeof(msg), fmt, ap);
#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
#pragma GCC diagnostic pop
#endif
va_end(ap);
do_header(title);
start_table();
printf("<TR><TH COLSPAN=2 BGCOLOR=\"#60B0B0\">\n");
printf("Error: %s\n", msg);
printf("</TH></TR>\n");
printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
do_pickups(next);
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</TD></TR></TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
static void loginscreen(void)
__attribute__((noreturn));
static void loginscreen(void)
{
do_header("Login");
printf("<FORM METHOD=\"POST\" ACTION=\"upsset.cgi\">\n");
start_table();
printf("<TR BGCOLOR=\"#60B0B0\">\n");
printf("<TH>Username</TH>\n");
printf("<TD><INPUT TYPE=\"TEXT\" NAME=\"username\" VALUE=\"\"></TD>\n");
printf("</TR>\n");
printf("<TR BGCOLOR=\"#60B0B0\">\n");
printf("<TH>Password</TH>\n");
printf("<TD><INPUT TYPE=\"PASSWORD\" NAME=\"password\" VALUE=\"\"></TD>\n");
printf("</TR>\n");
printf("<TR><TD COLSPAN=2 ALIGN=\"CENTER\">\n");
printf("<INPUT TYPE=\"HIDDEN\" NAME=\"function\" VALUE=\"pickups\">\n");
printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"Login\">\n");
printf("<INPUT TYPE=\"RESET\" VALUE=\"Reset fields\">\n");
printf("</TD></TR></TABLE>\n");
printf("</FORM>\n");
printf("</TD></TR></TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
/* try to connect to upsd - generate an error page if it fails */
static void upsd_connect(void)
{
if (upscli_splitname(monups, &upsname, &hostname, &port) != 0) {
error_page("showsettings", "UPS name is unusable",
"Unable to split UPS name [%s]", monups);
/* NOTREACHED */
}
if (upscli_connect(&ups, hostname, port, 0) < 0) {
error_page("showsettings", "Connect failure",
"Unable to connect to %s: %s",
monups, upscli_strerror(&ups));
/* NOTREACHED */
}
}
static void print_cmd(const char *cmd)
{
int ret;
size_t numq, numa;
char **answer;
const char *query[4];
query[0] = "CMDDESC";
query[1] = upsname;
query[2] = cmd;
numq = 3;
ret = upscli_get(&ups, numq, query, &numa, &answer);
if ((ret < 0) || (numa < numq))
return;
/* CMDDESC <upsname> <cmdname> <desc> */
printf("<OPTION VALUE=\"%s\">%s</OPTION>\n", cmd, answer[3]);
}
/* generate a list of instant commands */
static void showcmds(void)
{
int ret;
size_t numq, numa;
const char *query[2];
char **answer;
struct list_t *lhead, *llast, *ltmp, *lnext;
char *desc;
if (!checkhost(monups, &desc))
error_page("showsettings", "Access denied",
"Access to that host is not authorized");
upsd_connect();
llast = lhead = NULL;
query[0] = "CMD";
query[1] = upsname;
numq = 2;
ret = upscli_list_start(&ups, numq, query);
if (ret < 0) {
fprintf(stderr, "LIST CMD %s failed: %s\n",
upsname, upscli_strerror(&ups));
error_page("showcmds", "Server protocol error",
"LIST CMD command failed");
/* NOTREACHED */
}
ret = upscli_list_next(&ups, numq, query, &numa, &answer);
while (ret == 1) {
/* CMD upsname cmdname */
if (numa < 3) {
fprintf(stderr, "Error: insufficient data "
"(got %zu args, need at least 3)\n", numa);
return;
}
ltmp = xmalloc(sizeof(struct list_t));
ltmp->name = xstrdup(answer[2]);
ltmp->next = NULL;
if (llast)
llast->next = ltmp;
else
lhead = ltmp;
llast = ltmp;
ret = upscli_list_next(&ups, numq, query, &numa, &answer);
}
if (!lhead)
error_page("showcmds", "No instant commands supported",
"This UPS doesn't support any instant commands.");
do_header("Instant commands");
printf("<FORM ACTION=\"upsset.cgi\" METHOD=\"POST\">\n");
start_table();
/* include the description from checkhost() if present */
if (desc)
printf("<TR><TH BGCOLOR=\"#60B0B0\"COLSPAN=2>%s</TH></TR>\n",
desc);
printf("<TR BGCOLOR=\"#60B0B0\" ALIGN=\"CENTER\">\n");
printf("<TD>Instant commands</TD>\n");
printf("<TD>\n");
printf("<SELECT NAME=\"upscommand\">\n");
/* provide a dummy do-nothing default choice */
printf("<OPTION VALUE=\"\" SELECTED></OPTION>\n");
ltmp = lhead;
while (ltmp) {
lnext = ltmp->next;
print_cmd(ltmp->name);
free(ltmp->name);
free(ltmp);
ltmp = lnext;
}
printf("</SELECT>\n");
printf("</TD></TR>\n");
printf("<TR BGCOLOR=\"#60B0B0\">\n");
printf("<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n");
do_hidden("docmd");
printf("<INPUT TYPE=\"HIDDEN\" NAME=\"monups\" VALUE=\"%s\">\n", monups);
printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"Issue command\">\n");
printf("<INPUT TYPE=\"RESET\" VALUE=\"Reset\">\n");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</FORM>\n");
printf("<TR><TD ALIGN=\"CENTER\">\n");
do_pickups("showcmds");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
/* handle setting authentication data in the server */
static void send_auth(const char *next)
{
char buf[SMALLBUF];
snprintf(buf, sizeof(buf), "USERNAME %s\n", username);
if (upscli_sendline(&ups, buf, strlen(buf)) < 0) {
fprintf(stderr, "Can't set username: %s\n",
upscli_strerror(&ups));
error_page(next, "Can't set username",
"Set username failed: %s", upscli_strerror(&ups));
}
if (upscli_readline(&ups, buf, sizeof(buf)) < 0) {
/* test for old upsd that doesn't do USERNAME */
if (upscli_upserror(&ups) == UPSCLI_ERR_UNKCOMMAND) {
error_page(next, "Protocol mismatch",
"upsd version too old - USERNAME not supported");
}
error_page(next, "Can't set user name",
"Set user name failed: %s", upscli_strerror(&ups));
}
snprintf(buf, sizeof(buf), "PASSWORD %s\n", password);
if (upscli_sendline(&ups, buf, strlen(buf)) < 0)
error_page(next, "Can't set password",
"Password set failed: %s", upscli_strerror(&ups));
if (upscli_readline(&ups, buf, sizeof(buf)) < 0)
error_page(next, "Can't set password",
"Password set failed: %s", upscli_strerror(&ups));
}
static void docmd(void)
__attribute__((noreturn));
static void docmd(void)
{
char buf[SMALLBUF], *desc;
if (!checkhost(monups, &desc))
error_page("showsettings", "Access denied",
"Access to that host is not authorized");
/* the user is messing with us */
if (!upscommand)
error_page("showcmds", "Form error",
"No instant command selected");
/* (l)user took the default blank option */
if (strlen(upscommand) == 0)
error_page("showcmds", "Form error",
"No instant command selected");
upsd_connect();
send_auth("showcmds");
snprintf(buf, sizeof(buf), "INSTCMD %s %s\n", upsname, upscommand);
if (upscli_sendline(&ups, buf, strlen(buf)) < 0) {
do_header("Error while issuing command");
start_table();
printf("<TR><TD>Error sending command: %s\n</TD></TR>",
upscli_strerror(&ups));
printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
do_pickups("showcmds");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</TD></TR></TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
if (upscli_readline(&ups, buf, sizeof(buf)) < 0) {
do_header("Error while reading command response");
start_table();
printf("<TR><TD>Error reading command response: %s\n</TD></TR>",
upscli_strerror(&ups));
printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
do_pickups("showcmds");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</TD></TR></TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
do_header("Issuing command");
start_table();
printf("<TR><TD><PRE>\n");
printf("Sending command: %s\n", upscommand);
printf("Response: %s\n", buf);
printf("</PRE></TD></TR>\n");
printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
do_pickups("showcmds");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</TD></TR></TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
static const char *get_data(const char *type, const char *varname)
{
int ret;
size_t numq, numa;
char **answer;
const char *query[4];
query[0] = type;
query[1] = upsname;
query[2] = varname;
numq = 3;
ret = upscli_get(&ups, numq, query, &numa, &answer);
if ((ret < 0) || (numa < numq))
return NULL;
/* <type> <upsname> <varname> <desc> */
return answer[3];
}
static void do_string(const char *varname, int maxlen)
{
const char *val;
val = get_data("VAR", varname);
if (!val) {
printf("Unavailable\n");
fprintf(stderr, "do_string: can't get current value of %s\n",
varname);
return;
}
printf("<INPUT TYPE=\"TEXT\" NAME=\"UPSVAR_%s\" VALUE=\"%s\" "
"SIZE=\"%d\">\n", varname, val, maxlen);
}
static void do_enum(const char *varname)
{
int ret;
size_t numq, numa;
char **answer, *val;
const char *query[4], *tmp;
/* get current value */
tmp = get_data("VAR", varname);
if (!tmp) {
printf("Unavailable\n");
fprintf(stderr, "do_enum: can't get current value of %s\n",
varname);
return;
}
/* tmp is a pointer into answer - have to save it somewhere else */
val = xstrdup(tmp);
query[0] = "ENUM";
query[1] = upsname;
query[2] = varname;
numq = 3;
ret = upscli_list_start(&ups, numq, query);
if (ret < 0) {
printf("Unavailable\n");
fprintf(stderr, "Error doing ENUM %s %s: %s\n",
upsname, varname, upscli_strerror(&ups));
free(val);
return;
}
ret = upscli_list_next(&ups, numq, query, &numa, &answer);
printf("<SELECT NAME=\"UPSVAR_%s\">\n", varname);
while (ret == 1) {
/* ENUM <upsname> <varname> <value> */
if (numa < 4) {
fprintf(stderr, "Error: insufficient data "
"(got %zu args, need at least 4)\n", numa);
free(val);
return;
}
printf("<OPTION VALUE=\"%s\" ", answer[3]);
if (!strcmp(answer[3], val))
printf(" SELECTED");
printf(">%s</OPTION>\n", answer[3]);
ret = upscli_list_next(&ups, numq, query, &numa, &answer);
}
free(val);
printf("</SELECT>\n");
}
static void do_type(const char *varname)
{
int ret;
size_t i, numq, numa;
char **answer;
const char *query[4];
query[0] = "TYPE";
query[1] = upsname;
query[2] = varname;
numq = 3;
ret = upscli_get(&ups, numq, query, &numa, &answer);
if ((ret < 0) || (numa < numq)) {
printf("Unknown type\n");
return;
}
/* TYPE <upsname> <varname> <type>... */
for (i = 3; i < numa; i++) {
if (!strcasecmp(answer[i], "ENUM")) {
do_enum(varname);
return;
}
if (!strncasecmp(answer[i], "STRING:", 7)) {
char *ptr, len;
/* split out the :<len> data */
ptr = strchr(answer[i], ':');
*ptr++ = '\0';
long l = strtol(ptr, (char **) NULL, 10);
assert(l <= 127); /* FIXME: Loophole about longer numbers? Why are we limited to char at all here? */
len = (char)l;
do_string(varname, len);
return;
}
/* ignore this one */
if (!strcasecmp(answer[i], "RW"))
continue;
printf("Unrecognized\n");
}
}
static void print_rw(const char *arg_upsname, const char *varname)
{
const char *tmp;
printf("<!-- <TR><TD>Device</TD><TD>%s</TD></TR> -->\n", arg_upsname);
printf("<TR BGCOLOR=\"#60B0B0\" ALIGN=\"CENTER\">\n");
printf("<TD>");
tmp = get_data("DESC", varname);
if ((tmp) && (strcmp(tmp, "Unavailable") != 0))
printf("%s", tmp);
else
printf("%s", varname);
printf("</TD>\n");
printf("<TD>\n");
do_type(varname);
printf("</TD>\n");
printf("</TR>\n");
}
static void showsettings(void)
__attribute__((noreturn));
static void showsettings(void)
{
int ret;
size_t numq, numa;
const char *query[2];
char **answer, *desc = NULL;
struct list_t *lhead, *llast, *ltmp, *lnext;
if (!checkhost(monups, &desc))
error_page("showsettings", "Access denied",
"Access to that host is not authorized");
upsd_connect();
query[0] = "RW";
query[1] = upsname;
numq = 2;
ret = upscli_list_start(&ups, numq, query);
if (ret < 0) {
fprintf(stderr, "LIST RW %s failed: %s\n",
upsname, upscli_strerror(&ups));
error_page("showsettings", "Server protocol error",
"LIST RW command failed");
/* NOTREACHED */
}
llast = lhead = NULL;
ret = upscli_list_next(&ups, numq, query, &numa, &answer);
while (ret == 1) {
/* sock this entry away for later */
ltmp = xmalloc(sizeof(struct list_t));
ltmp->name = xstrdup(answer[2]);
ltmp->next = NULL;
if (llast)
llast->next = ltmp;
else
lhead = ltmp;
llast = ltmp;
ret = upscli_list_next(&ups, numq, query, &numa, &answer);
}
do_header("Current settings");
printf("<FORM ACTION=\"upsset.cgi\" METHOD=\"POST\">\n");
start_table();
/* include the description from checkhost() if present */
if (desc)
printf("<TR><TH BGCOLOR=\"#60B0B0\"COLSPAN=2>%s</TH></TR>\n",
desc);
printf("<TR BGCOLOR=\"#60B0B0\">\n");
printf("<TH>Setting</TH>\n");
printf("<TH>Value</TH></TR>\n");
/* use the list to get descriptions and types */
ltmp = lhead;
while (ltmp) {
lnext = ltmp->next;
print_rw(upsname, ltmp->name);
free(ltmp->name);
free(ltmp);
ltmp = lnext;
}
printf("<TR BGCOLOR=\"#60B0B0\">\n");
printf("<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n");
do_hidden("savesettings");
printf("<INPUT TYPE=\"HIDDEN\" NAME=\"monups\" VALUE=\"%s\">\n", monups);
printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"Save changes\">\n");
printf("<INPUT TYPE=\"RESET\" VALUE=\"Reset\">\n");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</FORM>\n");
printf("<TR><TD ALIGN=\"CENTER\">\n");
do_pickups("showsettings");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
static int setvar(const char *var, const char *val)
{
char buf[SMALLBUF], enc[SMALLBUF];
const char *tmp;
/* get old value */
tmp = get_data("VAR", var);
if (!tmp) {
printf("Can't get old value for %s, aborting SET\n", var);
return 0;
}
/* don't send a SET if it hasn't chnaged */
if (!strcmp(tmp, val))
return 0;
printf("set %s to %s (was %s)\n", var, val, tmp);
snprintf(buf, sizeof(buf), "SET VAR %s %s \"%s\"\n",
upsname, var, pconf_encode(val, enc, sizeof(enc)));
if (upscli_sendline(&ups, buf, strlen(buf)) < 0) {
printf("Error: SET failed: %s\n", upscli_strerror(&ups));
return 0;
}
if (upscli_readline(&ups, buf, sizeof(buf)) < 0) {
printf("Error: SET failed: %s\n", upscli_strerror(&ups));
return 0;
}
if (strncmp(buf, "OK", 2) != 0) {
printf("Unexpected response: %s\n", buf);
return 0;
}
printf("OK\n");
return 1;
}
/* turn a form submission of settings into SET commands for upsd */
static void savesettings(void)
__attribute__((noreturn));
static void savesettings(void)
{
int changed = 0;
char *desc;
uvtype_t *upsvar;
if (!checkhost(monups, &desc))
error_page("showsettings", "Access denied",
"Access to that host is not authorized");
upsd_connect();
upsvar = firstuv;
send_auth("showsettings");
do_header("Saving settings");
start_table();
printf("<TR><TD><PRE>\n");
while (upsvar) {
changed += setvar(upsvar->var, upsvar->value);
upsvar = upsvar->next;
}
if (changed == 0)
printf("No settings changed.\n");
else
printf("Updated %d setting%s.\n",
changed, changed == 1 ? "" : "s");
printf("</PRE></TD></TR>\n");
printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=\"2\">\n");
do_pickups("showsettings");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</TD></TR></TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
static void initial_pickups(void)
__attribute__((noreturn));
static void initial_pickups(void)
{
do_header("Select a UPS");
start_table();
printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=\"2\">\n");
do_pickups("");
printf("</TD></TR>\n");
printf("</TABLE>\n");
printf("</TD></TR></TABLE>\n");
printf("</BODY></HTML>\n");
upscli_disconnect(&ups);
exit(EXIT_SUCCESS);
}
static void upsset_conf_err(const char *errmsg)
{
upslogx(LOG_ERR, "Fatal error in parseconf(upsset.conf): %s", errmsg);
}
/* see if the user has confirmed their cgi directory's secure state */
static void check_conf(void)
{
char fn[SMALLBUF];
PCONF_CTX_t ctx;
snprintf(fn, sizeof(fn), "%s/upsset.conf", confpath());
pconf_init(&ctx, upsset_conf_err);
if (!pconf_file_begin(&ctx, fn)) {
pconf_finish(&ctx);
printf("<PRE>\n");
printf("Error: Can't open upsset.conf to verify security settings.\n");
printf("Refusing to start until this is fixed.\n");
printf("</PRE>\n");
/* leave something in the httpd log for the admin */
fprintf(stderr, "upsset.conf does not exist to permit execution\n");
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 < 1)
continue;
if (!strcmp(ctx.arglist[0], MAGIC_ENABLE_STRING))
magic_string_set = 1;
}
pconf_finish(&ctx);
/* if we've been enabled, jump out of here and go to work */
if (magic_string_set == 1)
return;
printf("<PRE>\n");
printf("Error: Secure mode has not been enabled in upsset.conf.\n");
printf("Refusing to start until this is fixed.\n");
printf("</PRE>\n");
/* leave something in the httpd log for the admin */
fprintf(stderr, "upsset.conf does not permit execution\n");
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
NUT_UNUSED_VARIABLE(argc);
NUT_UNUSED_VARIABLE(argv);
username = password = function = monups = NULL;
printf("Content-type: text/html\n\n");
/* see if the magic string is present in the config file */
check_conf();
/* see if there's anything waiting .. the server my not close STDIN properly */
if (1) {
fd_set fds;
struct timeval tv;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
tv.tv_sec = 0;
tv.tv_usec = 250000; /* wait for up to 250ms for a POST response */
if ((select(STDIN_FILENO+1, &fds, 0, 0, &tv)) > 0)
extractpostargs();
}
if ((!username) || (!password) || (!function))
loginscreen();
if ((!strcmp(function, "pickups")) || (!monups))
initial_pickups();
if (!strcmp(function, "showsettings"))
showsettings();
if (!strcmp(function, "savesettings"))
savesettings();
#if 0 /* FUTURE */
if (!strcmp(function, "showstatus"))
showstatus();
#endif
if (!strcmp(function, "showcmds"))
showcmds();
if (!strcmp(function, "docmd"))
docmd();
printf("Error: Unhandled function name [%s]\n", function);
return 0;
}