1119 lines
24 KiB
C
1119 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;
|
|
}
|