From 72241ad10c6d0856bef499ab3946f2a8005fe9a2 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 1 Feb 2019 14:52:25 +0100 Subject: [PATCH 01/45] INSTCMD and SET VAR status tracking implementation This commit implements the instant commands (instcmd) and variables settings (setvar) status tracking, to get the actual execution status from the driver Signed-off-by: Arnaud Quette --- common/Makefile.am | 2 +- common/uuid4.c | 91 +++++++++++++++++++ conf/upsd.conf.sample | 8 ++ docs/man/upsd.conf.txt | 6 ++ docs/net-protocol.txt | 57 ++++++++++-- docs/nut.dict | 8 +- docs/sock-protocol.txt | 30 ++++++- drivers/dstate.c | 61 +++++++++++-- drivers/dstate.h | 1 + drivers/upshandler.h | 8 +- include/Makefile.am | 2 +- include/uuid4.h | 23 +++++ server/conf.c | 6 ++ server/netget.c | 14 +++ server/netinstcmd.c | 71 +++++++++++---- server/netset.c | 83 ++++++++++++++++-- server/nut_ctype.h | 3 + server/sstate.c | 11 ++- server/upsd.c | 194 +++++++++++++++++++++++++++++++++++++++++ server/upsd.h | 33 ++++++- 20 files changed, 660 insertions(+), 52 deletions(-) create mode 100644 common/uuid4.c create mode 100644 include/uuid4.h diff --git a/common/Makefile.am b/common/Makefile.am index e2a4acb697..e6953cfc03 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -9,7 +9,7 @@ libparseconf_la_SOURCES = parseconf.c # 'dist', and is only required for actual build, in which case # BUILT_SOURCES (in ../include) will ensure nut_version.h will # be built before anything else -libcommon_la_SOURCES = common.c state.c str.c upsconf.c +libcommon_la_SOURCES = common.c state.c str.c upsconf.c uuid4.c libcommonclient_la_SOURCES = common.c state.c str.c # ensure inclusion of local implementation of missing systems functions # using LTLIBOBJS. Refer to configure.in/.ac -> AC_REPLACE_FUNCS diff --git a/common/uuid4.c b/common/uuid4.c new file mode 100644 index 0000000000..c285a29317 --- /dev/null +++ b/common/uuid4.c @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2018 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include + +#if defined(_WIN32) +#include +#include +#endif + +#include "uuid4.h" + + +static uint64_t seed[2]; + + +static uint64_t xorshift128plus(uint64_t *s) { + /* http://xorshift.di.unimi.it/xorshift128plus.c */ + uint64_t s1 = s[0]; + const uint64_t s0 = s[1]; + s[0] = s0; + s1 ^= s1 << 23; + s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); + return s[1] + s0; +} + + +int uuid4_init(void) { +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) + int res; + FILE *fp = fopen("/dev/urandom", "rb"); + if (!fp) { + return UUID4_EFAILURE; + } + res = fread(seed, 1, sizeof(seed), fp); + fclose(fp); + if ( res != sizeof(seed) ) { + return UUID4_EFAILURE; + } + +#elif defined(_WIN32) + int res; + HCRYPTPROV hCryptProv; + res = CryptAcquireContext( + &hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + if (!res) { + return UUID4_EFAILURE; + } + res = CryptGenRandom(hCryptProv, (DWORD) sizeof(seed), (PBYTE) seed); + CryptReleaseContext(hCryptProv, 0); + if (!res) { + return UUID4_EFAILURE; + } + +#else + #error "unsupported platform" + /* FIXME: use rand() */ +#endif + return UUID4_ESUCCESS; +} + + +void uuid4_generate(char *dst) { + static const char *template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"; + static const char *chars = "0123456789abcdef"; + union { unsigned char b[16]; uint64_t word[2]; } s; + const char *p; + int i, n; + /* get random */ + s.word[0] = xorshift128plus(seed); + s.word[1] = xorshift128plus(seed); + /* build string */ + p = template; + i = 0; + while (*p) { + n = s.b[i >> 1]; + n = (i & 1) ? (n >> 4) : (n & 0xf); + switch (*p) { + case 'x' : *dst = chars[n]; i++; break; + case 'y' : *dst = chars[(n & 0x3) + 8]; i++; break; + default : *dst = *p; + } + dst++, p++; + } + *dst = '\0'; +} diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index e7328f6865..dbf0ad6cb6 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -21,6 +21,14 @@ # the data fresh within the normal 15 second interval. Watch the syslog # for notifications from upsd about staleness. +# ======================================================================= +# CMDSETSTATUSDELAY +# CMDSETSTATUSDELAY 3600 +# +# This defaults to 1 hour. When instant commands and variables setting status +# tracking is enabled, status execution information are kept during this +# amount of time, and then cleaned up. + # ======================================================================= # STATEPATH # STATEPATH /var/run/nut diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index ebdfef6024..7af58b0c79 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -26,6 +26,12 @@ to make upsd wait longer. + Most users should leave this at the default value. +"CMDSETSTATUSDELAY 'seconds'":: + +When instant commands and variables setting status tracking is enabled, status +execution information are kept during this amount of time, and then cleaned up. +This defaults to 3600 (1 hour). + "STATEPATH 'path'":: Tell upsd to look for the driver state sockets in 'path' rather diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 4f9a7fa34d..aa8bd2152f 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -44,6 +44,7 @@ NUT network protocol, over the time: |1.1 |>= 1.5.0 |Original protocol (without old commands) .2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands |Add ranges of values for writable variables +|1.3 |>= 2.7.5 |Add "cmdparam" to "INSTCMD" |=============================================================================== NOTE: any new version of the protocol implies an update of NUT_NETVERSION @@ -189,6 +190,24 @@ This is like DESC above, but it applies to the instant commands. This replaces the old "INSTCMDDESC" command. +CMDSET_STATUS +~~~~~~~~~~~~~ + +Form: + + GET CMDSET_STATUS (activation status of CMDSET_STATUS) + GET CMDSET_STATUS [] (execution status of a command / setvar) + GET CMDSET_STATUS 1bd31808-cb49-4aec-9d75-d056e6f018d2 + +Response: + + PENDING (command execution is pending) + SUCCESS (command was successfully executed) + ERR UNKNOWN (command execution failed with unknown error) + ERR INVALID-ARGUMENT (command execution failed due to missing or invalid argument) + ERR FAILED (command execution failed) + + LIST ---- @@ -377,11 +396,36 @@ Response: SET --- +VAR +~~~ + Form: SET VAR "" SET VAR su700 ups.id "My UPS" +Response: + + OK + ERR [...] + + +CMDSET_STATUS +~~~~~~~~~~~~~ + +Form: + + SET CMDSET_STATUS "" + SET CMDSET_STATUS ON + SET CMDSET_STATUS OFF + +Response: + + OK + ERR INVALID-ARGUMENT (if "value" is not "ON" or "OFF") + ERR USERNAME-REQUIRED (if not yet authenticated) + ERR PASSWORD-REQUIRED (if not yet authenticated) + INSTCMD ------- @@ -394,6 +438,12 @@ Form: NOTE: cmdparam is an additional and optional parameter for the command. +Response: + + OK (if CMDSET_STATUS is not enabled) + OK (if CMDSET_STATUS is enabled) + ERR ... (see Error responses) + LOGOUT ------ @@ -699,13 +749,6 @@ For example, "LIST VARS +DESC" would return the current value like now, but it would also append the description of that variable. -Command status -~~~~~~~~~~~~~~ - -After sending an INSTCMD or SET, a client will eventually be able to -poll to see whether it was completed successfully by the driver. - - Get collection ~~~~~~~~~~~~~~ diff --git a/docs/nut.dict b/docs/nut.dict index 014497060d..c8a6660e22 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2433 utf-8 +personal_ws-1.1 en 2439 utf-8 AAS ACFAIL ACFREQ @@ -12,6 +12,7 @@ ADDRINFO ADK ADKK AEC +aec AEG AES AGM @@ -136,6 +137,7 @@ ByPass CA's CABAC CAs +cb CBLimit CCC CCCC @@ -149,6 +151,8 @@ CLI CLOCAL CMDDESC CMDSCRIPT +CMDSET +CMDSETSTATUSDELAY CN COMLI COMMBAD @@ -203,6 +207,7 @@ DDDDD DDF DEADTIME DEBUGOUT +dedb DELCMD DELENUM DELINFO @@ -2316,6 +2321,7 @@ upserror upsfetch upsgone upsh +upshandler upsidentmodel upsimage upsload diff --git a/docs/sock-protocol.txt b/docs/sock-protocol.txt index 944d3ef7fd..0961b614e7 100644 --- a/docs/sock-protocol.txt +++ b/docs/sock-protocol.txt @@ -146,6 +146,18 @@ status information once this has been sent. This will be sent in the beginning of a dump if the data is stale, and may be repeated. It is cleared by DATAOK. +CMDSET_STATUS +~~~~~~~~~~~~~ + + CMDSET_STATUS + +This is sent in response to an INSTCMD or SET VAR that includes a STATUS_ID, +upon completion of request execution by the driver. is the integer +return value from the driver handlers instcmd and setvar (see +drivers/upshandler.h). The server is in charge of translating these codes into +strings, as per docs/net-protocol.txt GET CMDSET_STATUS. + + Commands sent by the server --------------------------- @@ -166,16 +178,30 @@ server must not be passed on to the clients when this happens. INSTCMD ~~~~~~~ - INSTCMD + INSTCMD [cmdparam] [STATUS_ID ] INSTCMD panel.test.start + INSTCMD load.off 10 + INSTCMD load.on 10 STATUS_ID 1bd31808-cb49-4aec-9d75-d056e6f018d2 + +NOTE: +* cmdparam is an additional and optional parameter for the command, +* "STATUS_ID " can be provided to track commands execution status, if +CMDSET_STATUS was set to ON on upsd. In this case, driver will later return +the execution status, using CMDSET_STATUS. SET ~~~ - SET "" + SET "" [STATUS_ID ] SET ups.id "Data room" + SET ups.id "Data room" STATUS_ID 2dedb58a-3b91-4fab-831f-c8af4b90760a + +NOTE: +* "STATUS_ID " can be provided to track commands execution status, if +CMDSET_STATUS was set to ON on upsd. In this case, driver will later return +the execution status, using CMDSET_STATUS. DUMPALL ~~~~~~~ diff --git a/drivers/dstate.c b/drivers/dstate.c index 276da18b63..05c3cfa2da 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -201,6 +201,7 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) ret = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); + upsdebugx(2, "%s: sending %s", __func__, buf); if (ret < 1) { upsdebugx(2, "%s: nothing to write", __func__); return 1; @@ -394,20 +395,36 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* INSTCMD []*/ + /* INSTCMD [cmdparam] [STATUS_ID ] */ if (!strcasecmp(arg[0], "INSTCMD")) { + int ret; + char *cmdname = arg[1]; + char *cmdparam = NULL; + char *cmdid = NULL; + + /* Check if STATUS_ID was provided */ + if (numarg >= 4) { + if (!strcasecmp(arg[2], "STATUS_ID")) { + cmdid = strdup(arg[3]); + } + else { + cmdparam = arg[2]; + cmdid = arg[4]; + } + upsdebugx(3, "STATUS_ID = %s", cmdid); + } /* try the new handler first if present */ if (upsh.instcmd) { - if (numarg > 2) { - upsh.instcmd(arg[1], arg[2]); - return 1; - } + ret = upsh.instcmd(cmdname, cmdparam); - upsh.instcmd(arg[1], NULL); + /* send back execution result */ + if (cmdid) + dstate_send_cmdset_status(conn, cmdid, ret); + + /* The command was handled, status is a separate consideration */ return 1; } - upslogx(LOG_NOTICE, "Got INSTCMD, but driver lacks a handler"); return 1; } @@ -416,12 +433,32 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* SET */ + /* SET [STATUS_ID ] */ if (!strcasecmp(arg[0], "SET")) { + int ret; + char *cmdid = NULL; + + /* Check if STATUS_ID was provided */ + if (numarg == 5) { + if (!strcasecmp(arg[3], "STATUS_ID")) { + cmdid = arg[4]; + } + else { + upslogx(LOG_NOTICE, "Got INSTCMD with unsupported parameters (%s/%s)", + arg[3], arg[4]); + } + upsdebugx(3, "STATUS_ID = %s", cmdid); + } /* try the new handler first if present */ if (upsh.setvar) { - upsh.setvar(arg[1], arg[2]); + ret = upsh.setvar(arg[1], arg[2]); + + /* send back execution result */ + if (cmdid) + dstate_send_cmdset_status(conn, cmdid, ret); + + /* The command was handled, status is a separate consideration */ return 1; } @@ -879,6 +916,12 @@ int dstate_is_stale(void) return stale; } + +void dstate_send_cmdset_status(conn_t *conn, const char *id, int value) +{ + send_to_one(conn, "CMDSET_STATUS %s %i\n", id, value); +} + /* ups.status management functions - reducing duplication in the drivers */ /* clean out the temp space for a new pass */ diff --git a/drivers/dstate.h b/drivers/dstate.h index dd1b920122..4022c88a10 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -70,6 +70,7 @@ int dstate_delcmd(const char *cmd); void dstate_free(void); const st_tree_t *dstate_getroot(void); const cmdlist_t *dstate_getcmdlist(void); +void dstate_send_cmdset_status(conn_t *conn, const char *id, int value); void dstate_dataok(void); void dstate_datastale(void); diff --git a/drivers/upshandler.h b/drivers/upshandler.h index ca9a349594..fea10bc20c 100644 --- a/drivers/upshandler.h +++ b/drivers/upshandler.h @@ -25,15 +25,15 @@ enum { STAT_INSTCMD_HANDLED = 0, /* completed successfully */ STAT_INSTCMD_UNKNOWN, /* unspecified error */ STAT_INSTCMD_INVALID, /* invalid command */ - STAT_INSTCMD_FAILED /* command failed */ + STAT_INSTCMD_FAILED /* command failed */ }; /* return values for setvar */ enum { STAT_SET_HANDLED = 0, /* completed successfully */ - STAT_SET_UNKNOWN, /* unspecified error */ - STAT_SET_INVALID, /* not writeable */ - STAT_SET_FAILED /* writing failed */ + STAT_SET_UNKNOWN, /* unspecified error */ + STAT_SET_INVALID, /* not writeable */ + STAT_SET_FAILED /* writing failed */ }; /* structure for funcs that get called by msg parse routine */ diff --git a/include/Makefile.am b/include/Makefile.am index 527d0995b3..4c812da397 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,5 @@ dist_noinst_HEADERS = attribute.h common.h extstate.h parseconf.h proto.h \ - state.h str.h timehead.h upsconf.h nut_stdint.h nut_platform.h + state.h str.h timehead.h upsconf.h nut_stdint.h nut_platform.h uuid4.h # http://www.gnu.org/software/automake/manual/automake.html#Clean BUILT_SOURCES = nut_version.h diff --git a/include/uuid4.h b/include/uuid4.h new file mode 100644 index 0000000000..7fbc7864aa --- /dev/null +++ b/include/uuid4.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2018 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef UUID4_H +#define UUID4_H + +#define UUID4_VERSION "1.0.0" +#define UUID4_LEN 37 + +enum { + UUID4_ESUCCESS = 0, + UUID4_EFAILURE = -1 +}; + +int uuid4_init(void); +void uuid4_generate(char *dst); + +#endif + diff --git a/server/conf.c b/server/conf.c index aea8220cd7..c62a372517 100644 --- a/server/conf.c +++ b/server/conf.c @@ -125,6 +125,12 @@ static int parse_upsd_conf_args(int numargs, char **arg) return 1; } + /* CMDSETSTATUSDELAY */ + if (!strcmp(arg[0], "CMDSETSTATUSDELAY")) { + cmdset_status_delay = atoi(arg[1]); + return 1; + } + /* MAXCONN */ if (!strcmp(arg[0], "MAXCONN")) { maxconn = atoi(arg[1]); diff --git a/server/netget.c b/server/netget.c index 5ab41c3f3a..8769bfa72a 100644 --- a/server/netget.c +++ b/server/netget.c @@ -216,6 +216,20 @@ static void get_var(nut_ctype_t *client, const char *upsname, const char *var) void net_get(nut_ctype_t *client, int numarg, const char **arg) { + /* GET CMDSET_STATUS [STATUS_ID] */ + if (!strcasecmp(arg[0], "CMDSET_STATUS")) { + if (numarg < 2) { + sendback(client, "%s\n", (client->cmdset_status_enabled)?"ON":"OFF"); + } + else { + if (client->cmdset_status_enabled) + sendback(client, "%s\n", cmdset_status_get(arg[1])); + else + send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); + } + return; + } + if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; diff --git a/server/netinstcmd.c b/server/netinstcmd.c index c21d7ec1d7..f79a6bb4d0 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -27,9 +27,10 @@ #include "neterr.h" #include "netinstcmd.h" +#include "uuid4.h" static void send_instcmd(nut_ctype_t *client, const char *upsname, - const char *cmdname, const char *value) + const char *cmdname, const char *value, const char *status_id) { int found; upstype_t *ups; @@ -70,20 +71,29 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, return; } - /* see if the user has also passed a value for this command */ - if (value != NULL) { - upslogx(LOG_INFO, "Instant command: %s@%s did %s with value \"%s\" on %s", - client->username, client->addr, cmdname, value, ups->name); + /* Format the base command */ + snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s", cmdname); - snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s %s\n", - cmdname, pconf_encode(value, esc, sizeof(esc))); + /* see if the user has also passed a value for this command */ + if (value != NULL) + snprintfcat(sockcmd, sizeof(sockcmd), " %s", pconf_encode(value, esc, sizeof(esc))); + + /* see if the user want execution tracking for this command */ + if (status_id != NULL) { + snprintfcat(sockcmd, sizeof(sockcmd), " STATUS_ID %s", status_id); + /* Add an entry in the tracking structure */ + cmdset_status_add(status_id); } - else { - upslogx(LOG_INFO, "Instant command: %s@%s did %s on %s", - client->username, client->addr, cmdname, ups->name); - snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s\n", cmdname); - } + /* add EOL */ + snprintfcat(sockcmd, sizeof(sockcmd), "\n"); + + upslogx(LOG_INFO, "Instant command: %s@%s did %s%s%s on %s (tracking ID: %s)", + client->username, client->addr, cmdname, + (value != NULL)?" with value ":"", + (value != NULL)?value:"", + ups->name, + (status_id != NULL)?status_id:"disabled"); if (!sstate_sendline(ups, sockcmd)) { upslogx(LOG_INFO, "Set command send failed"); @@ -91,18 +101,45 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, return; } - /* FIXME: need to retrieve the cookie number */ - sendback(client, "OK\n"); + /* return the result, possibly including status_id */ + if (status_id != NULL) + sendback(client, "OK %s\n", status_id); + else + sendback(client, "OK\n"); } void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) { + const char *devname = NULL; + const char *cmdname = NULL; + const char *cmdparam = NULL; + char *status_id = NULL; + if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } - - /* INSTCMD []*/ - send_instcmd(client, arg[0], arg[1], (numarg == 3)?arg[2]:NULL); + + /* INSTCMD [cmdparam] */ + /* Check arguments */ + devname = arg[0]; + cmdname = arg[1]; + if (numarg == 3) + cmdparam = arg[2]; + + if (client->cmdset_status_enabled) { + /* Generate a tracking ID, if client requested status tracking */ + /* FIXME: for now, use a very basic and straightforward approach! */ + status_id = xcalloc(1, UUID4_LEN); + uuid4_init(); + uuid4_generate(status_id); + } + + send_instcmd(client, devname, cmdname, cmdparam, status_id); + + /* free the generated uuid if needed */ + if (status_id) + free(status_id); + return; } diff --git a/server/netset.c b/server/netset.c index b88bb40f7d..05fc4c711e 100644 --- a/server/netset.c +++ b/server/netset.c @@ -26,9 +26,10 @@ #include "neterr.h" #include "netset.h" +#include "uuid4.h" static void set_var(nut_ctype_t *client, const char *upsname, const char *var, - const char *newval) + const char *newval, const char *status_id) { upstype_t *ups; const char *val; @@ -134,11 +135,21 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, /* must be OK now */ - upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s", - client->username, client->addr, var, ups->name, newval); + upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s (tracking ID: %s)", + client->username, client->addr, var, ups->name, newval, + (status_id != NULL)?status_id:"disabled"); - snprintf(cmd, sizeof(cmd), "SET %s \"%s\"\n", - var, pconf_encode(newval, esc, sizeof(esc))); + /* see if the user want execution tracking for this command */ + if (status_id != NULL) { + snprintf(cmd, sizeof(cmd), "SET %s \"%s\" STATUS_ID %s\n", + var, pconf_encode(newval, esc, sizeof(esc)), status_id); + /* Add an entry in the tracking structure */ + cmdset_status_add(status_id); + } + else { + snprintf(cmd, sizeof(cmd), "SET %s \"%s\"\n", + var, pconf_encode(newval, esc, sizeof(esc))); + } if (!sstate_sendline(ups, cmd)) { upslogx(LOG_INFO, "Set command send failed"); @@ -146,19 +157,75 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, return; } - sendback(client, "OK\n"); + /* return the result, possibly including status_id */ + if (status_id != NULL) + sendback(client, "OK %s\n", status_id); + else + sendback(client, "OK\n"); } void net_set(nut_ctype_t *client, int numarg, const char **arg) { - if (numarg < 4) { + char *status_id = NULL; + + /* Base verification, to ensure that we have at least the SET parameter */ + if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* SET VAR UPS VARNAME VALUE */ if (!strcasecmp(arg[0], "VAR")) { - set_var(client, arg[1], arg[2], arg[3]); + if (numarg < 4) { + send_err(client, NUT_ERR_INVALID_ARGUMENT); + return; + } + + if (client->cmdset_status_enabled) { + /* Generate a tracking ID, if client requested status tracking */ + /* FIXME: for now, use a very basic and straightforward approach! */ + status_id = xcalloc(1, UUID4_LEN); + uuid4_init(); + uuid4_generate(status_id); + } + + set_var(client, arg[1], arg[2], arg[3], status_id); + + /* free the generated uuid if needed */ + if (status_id) + free(status_id); + + return; + } + + /* SET CMDSET_STATUS VALUE */ + if (!strcasecmp(arg[0], "CMDSET_STATUS")) { + /* Note: for now, limit status tracking to some OS. + * Check for library or homebrew implementation to support + * all platforms */ +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(_WIN32) + if (!strcasecmp(arg[1], "ON")) { + /* general enablement along with for this client */ + cmdset_status_enabled = 1; + client->cmdset_status_enabled = 1; + } + else if (!strcasecmp(arg[1], "OFF")) { + /* disable status tracking for this client first */ + client->cmdset_status_enabled = 0; + /* then only disable the general one if no other clients use it! */ + cmdset_status_enabled = cmdset_status_disable(); + } + else { + send_err(client, NUT_ERR_INVALID_ARGUMENT); + return; + } + upsdebugx(1, "%s: CMDSET_STATUS %s", __func__, + (cmdset_status_enabled == 1)?"enabled":"disabled"); + + sendback(client, "OK\n"); +#else + send_err(client, FEATURE-NOT-SUPPORTED); +#endif return; } diff --git a/server/nut_ctype.h b/server/nut_ctype.h index c2302aadb8..5e39a5f2be 100644 --- a/server/nut_ctype.h +++ b/server/nut_ctype.h @@ -45,6 +45,9 @@ typedef struct nut_ctype_s { char *loginups; char *password; char *username; + /* per client status info for commands and settings + * (disabled by default) */ + int cmdset_status_enabled; #ifdef WITH_OPENSSL SSL *ssl; diff --git a/server/sstate.c b/server/sstate.c index 34e1b2de9a..c4c64820bb 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -37,6 +37,7 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) { + upsdebugx(1, "%s: got %i args (%s)", __func__, numargs, arg[0]); if (numargs < 1) return 0; @@ -116,6 +117,14 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) return 1; } + /* CMDSET_STATUS */ + /* FIXME: condition on cmdset_status_enabled ? */ + if (!strcasecmp(arg[0], "CMDSET_STATUS")) { + cmdset_status_set(arg[1], arg[2]); + upsdebugx(1, "CMDSET_STATUS: ID %s status %s", arg[1], arg[2]); + return 1; + } + if (numargs < 4) return 0; @@ -388,7 +397,7 @@ int sstate_sendline(upstype_t *ups, const char *buf) ret = write(ups->sock_fd, buf, strlen(buf)); if (ret == (int)strlen(buf)) { - return 1; + return 1; } upslog_with_errno(LOG_NOTICE, "Send to UPS [%s] failed", ups->name); diff --git a/server/upsd.c b/server/upsd.c index a490608b2d..477f7c1171 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -53,6 +53,9 @@ int deny_severity = LOG_WARNING; /* default 15 seconds before data is marked stale */ int maxage = 15; + /* default to 1h before cleaning up status tracking entries */ + int cmdset_status_delay = 3600; + /* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */ int maxconn = 0; @@ -84,6 +87,15 @@ typedef struct { void *data; } handler_t; +/* general enable/disable status info for commands and settings + * (disabled by default) + * Note that only client that requested it will have it enabled + * (see nut_ctype.h) */ +int cmdset_status_enabled = 0; + +/* Commands and settings status tracking */ +cmdset_status_t *cmdset_status_list = NULL; + /* pollfd */ static struct pollfd *fds = NULL; static handler_t *handler = NULL; @@ -480,6 +492,8 @@ static void client_connect(stype_t *server) client->addr = xstrdup(inet_ntopW(&csock)); + client->cmdset_status_enabled = 0; + pconf_init(&client->ctx, NULL); if (firstclient) { @@ -644,6 +658,7 @@ static void upsd_cleanup(void) server_free(); client_free(); driver_free(); + cmdset_status_free(); free(statepath); free(datapath); @@ -672,6 +687,181 @@ void poll_reload(void) handler = xrealloc(handler, maxconn * sizeof(*handler)); } +/* instant command and setvar status tracking */ + +/* allocate a new status tracking entry */ +int cmdset_status_add(const char *id) +{ + if (!cmdset_status_enabled) + return 0; + + cmdset_status_t *cmdset_status; + + cmdset_status = xcalloc(1, sizeof(*cmdset_status)); + + cmdset_status->id = xstrdup(id); + cmdset_status->status = STAT_PENDING; + time(&cmdset_status->request_time); + + if (cmdset_status_list) { + cmdset_status_list->prev = cmdset_status; + cmdset_status->next = cmdset_status_list; + } + + cmdset_status_list = cmdset_status; + + return 1; +} + +/* set status of a specific tracking entry */ +int cmdset_status_set(const char *id, const char *value) +{ + /* sanity check */ + if (!cmdset_status_list) + return 0; + + cmdset_status_t *cmdset_status, *cmdset_status_next; + + for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + + cmdset_status_next = cmdset_status->next; + + if (!strcmp(cmdset_status->id, id)) { + cmdset_status->status = atoi(value); + return 1; + } + } + + return 0; /* id not found! */ +} + +/* free a specific tracking entry */ +int cmdset_status_del(const char *id) +{ + /* sanity check */ + if (!cmdset_status_list) + return 0; + + cmdset_status_t *cmdset_status, *cmdset_status_next; + + upsdebugx(3, "%s: deleting id %s", __func__, id); + + for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + + cmdset_status_next = cmdset_status->next; + + if (!strcmp(cmdset_status->id, id)) { + + if (cmdset_status->prev) { + cmdset_status->prev->next = cmdset_status->next; + } else { + /* deleting first entry */ + cmdset_status_list = cmdset_status->next; + } + + if (cmdset_status->next) { + cmdset_status->next->prev = cmdset_status->prev; + } + + free(cmdset_status->id); + free(cmdset_status); + + return 1; + } + } + + return 0; /* id not found! */ +} + +/* free all status tracking entries */ +void cmdset_status_free() +{ + /* sanity check */ + if (!cmdset_status_list) + return; + + cmdset_status_t *cmdset_status, *cmdset_status_next; + + upsdebugx(3, "%s", __func__); + + for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + cmdset_status_next = cmdset_status->next; + cmdset_status_del(cmdset_status->id); + } +} + +/* cleanup status tracking entries according to their age and cmdset_status_delay */ +void cmdset_status_cleanup() +{ + /* sanity check */ + if (!cmdset_status_list) + return; + + cmdset_status_t *cmdset_status, *cmdset_status_next; + time_t now; + time(&now); + + upsdebugx(3, "%s", __func__); + + for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + + cmdset_status_next = cmdset_status->next; + + if (difftime(now, cmdset_status->request_time) > cmdset_status_delay) { + cmdset_status_del(cmdset_status->id); + } + } +} + +/* get status of a specific tracking entry */ +char *cmdset_status_get(const char *id) +{ + /* sanity check */ + if (!cmdset_status_list) + return "ERR UNKNOWN"; + + cmdset_status_t *cmdset_status, *cmdset_status_next; + + for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + + cmdset_status_next = cmdset_status->next; + + if (!strcmp(cmdset_status->id, id)) { + switch (cmdset_status->status) { + case STAT_PENDING: + return "PENDING"; + case STAT_HANDLED: + return "SUCCESS"; + case STAT_UNKNOWN: + return "ERR UNKNOWN"; + case STAT_INVALID: + return "ERR INVALID-ARGUMENT"; + case STAT_FAILED: + return "ERR FAILED"; + } + } + } + + return "ERR UNKNOWN"; /* id not found! */ +} + +/* disable general status tracking only if no client still use it. + * return the new value for cmdset_status_enabled (0 if we can disable, 1 + * otherwise) */ +int cmdset_status_disable() +{ + int ret = 0; + nut_ctype_t *client, *cnext; + + /* cleanup client fds */ + for (client = firstclient; client; client = cnext) { + cnext = client->next; + if (client->cmdset_status_enabled == 1) + ret = 1; + } + return ret; +} + /* service requests and check on new data */ static void mainloop(void) { @@ -690,6 +880,9 @@ static void mainloop(void) reload_flag = 0; } + /* cleanup instcmd/setvar status tracking entries if needed */ + cmdset_status_cleanup(); + /* scan through driver sockets */ for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { @@ -722,6 +915,7 @@ static void mainloop(void) if (difftime(now, client->last_heard) > 60) { /* shed clients after 1 minute of inactivity */ + /* FIXME: create an upsd.conf parameter (CLIENT_INACTIVITY_DELAY) */ client_disconnect(client); continue; } diff --git a/server/upsd.h b/server/upsd.h index 2b3a3a74d1..174da73e13 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -68,12 +68,43 @@ void server_free(void); void check_perms(const char *fn); +/* return values for instcmd / setvar status tracking, + * mapped on drivers/upshandler.h, apart from STAT_PENDING (initial state) */ +enum { + STAT_PENDING = -1, /* not yet completed */ + STAT_HANDLED = 0, /* completed successfully (NUT_SUCCESS or "OK") */ + STAT_UNKNOWN, /* unspecified error (NUT_ERR_UNKNOWN) */ + STAT_INVALID, /* invalid command/setvar (NUT_ERR_INVALID_ARGUMENT) */ + STAT_FAILED /* command/setvar failed (NUT_ERR_INSTCMD_FAILED / NUT_ERR_SET_FAILED) */ +}; + +/* Commands and settings status tracking functions */ +int cmdset_status_add(const char *id); +int cmdset_status_set(const char *id, const char *value); +int cmdset_status_del(const char *id); +void cmdset_status_free(); +void cmdset_status_cleanup(); +char *cmdset_status_get(const char *id); +int cmdset_status_disable(); + +/* Commands and settings status tracking structure */ +typedef struct cmdset_status_s { + char *id; + int status; + time_t request_time; /* for cleanup */ + /* doubly linked list */ + struct cmdset_status_s *prev; + struct cmdset_status_s *next; +} cmdset_status_t; + /* declarations from upsd.c */ -extern int maxage, maxconn; +extern int maxage, maxconn, cmdset_status_delay; extern char *statepath, *datapath; extern upstype_t *firstups; extern nut_ctype_t *firstclient; +extern int cmdset_status_enabled; +extern cmdset_status_t *cmdset_status_list; /* map commands onto signals */ From b7c0a9acecf7aaa7bfcdf5f44fdcd46514a41e31 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 1 Feb 2019 15:10:48 +0100 Subject: [PATCH 02/45] Missing Revision history Signed-off-by: Arnaud Quette --- docs/net-protocol.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index aa8bd2152f..9cd310e90b 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -44,7 +44,8 @@ NUT network protocol, over the time: |1.1 |>= 1.5.0 |Original protocol (without old commands) .2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands |Add ranges of values for writable variables -|1.3 |>= 2.7.5 |Add "cmdparam" to "INSTCMD" +.2+|1.3 .2+|>= 2.7.5 |Add "cmdparam" to "INSTCMD" + |Add "CMDSET_STATUS" commands (GET, SET) |=============================================================================== NOTE: any new version of the protocol implies an update of NUT_NETVERSION From 97c51c9157ae52ba21cfe7155af4eb10ba8ad70e Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 1 Feb 2019 15:16:27 +0100 Subject: [PATCH 03/45] Fix nut-names.txt modification that should not be here Signed-off-by: Arnaud Quette --- docs/nut-names.txt | 81 ++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/docs/nut-names.txt b/docs/nut-names.txt index bdc30b8d06..e7f4d86a1b 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -621,46 +621,43 @@ Instant commands ---------------- [options="header"] -|=============================================================================== -| Name | Description -| load.off | Turn off the load immediately -| load.on | Turn on the load immediately -| load.off.delay | Turn off the load possibly after a delay -| load.on.delay | Turn on the load possibly after a delay -| shutdown.return | Turn off the load possibly after a delay +|======================================================================== +| Name | Description +| load.off | Turn off the load immediately +| load.on | Turn on the load immediately +| load.off.delay | Turn off the load possibly after a delay +| load.on.delay | Turn on the load possibly after a delay +| shutdown.return | Turn off the load possibly after a delay and return when power is back -| shutdown.stayoff | Turn off the load possibly after a delay - and remain off even if power returns -| shutdown.stop | Stop a shutdown in progress -| shutdown.reboot | Shut down the load briefly while rebooting the UPS -| shutdown.reboot.graceful | After a delay, shut down the load briefly - while rebooting the UPS -| test.panel.start | Start testing the UPS panel -| test.panel.stop | Stop a UPS panel test -| test.failure.start | Start a simulated power failure -| test.failure.stop | Stop simulating a power failure -| test.battery.start | Start a battery test -| test.battery.start.quick | Start a "quick" battery test -| test.battery.start.deep | Start a "deep" battery test -| test.battery.stop | Stop the battery test -| test.system.start | Start a system test -| calibrate.start | Start runtime calibration -| calibrate.stop | Stop runtime calibration -| bypass.start | Put the UPS in bypass mode -| bypass.stop | Take the UPS out of bypass mode -| reset.input.minmax | Reset minimum and maximum input voltage status -| reset.watchdog | Reset watchdog timer (forced reboot of load) -| beeper.enable | Enable UPS beeper/buzzer -| beeper.disable | Disable UPS beeper/buzzer -| beeper.mute | Temporarily mute UPS beeper/buzzer -| beeper.toggle | Toggle UPS beeper/buzzer -| outlet.n.shutdown.return | Turn off the outlet possibly after a delay - and return when power is back -| outlet.n.load.off | Turn off the outlet immediately -| outlet.n.load.on | Turn on the outlet immediately -| outlet.n.load.cycle | Power cycle the outlet immediately -| outlet.n.load.off.delay | Turn off the outlet possibly after a delay -| outlet.n.load.on.delay | Turn on the outlet possibly after a delay -| outlet.n.load.cycle.delay | Power cycle the outlet possibly after a delay -| outlet.n.shutdown.return | Turn off the outlet and return when power is back -|=============================================================================== +| shutdown.stayoff | Turn off the load possibly after a delay + and remain off even if power returns +| shutdown.stop | Stop a shutdown in progress +| shutdown.reboot | Shut down the load briefly while rebooting the UPS +| shutdown.reboot.graceful | After a delay, shut down the load briefly + while rebooting the UPS +| test.panel.start | Start testing the UPS panel +| test.panel.stop | Stop a UPS panel test +| test.failure.start | Start a simulated power failure +| test.failure.stop | Stop simulating a power failure +| test.battery.start | Start a battery test +| test.battery.start.quick | Start a "quick" battery test +| test.battery.start.deep | Start a "deep" battery test +| test.battery.stop | Stop the battery test +| test.system.start | Start a system test +| calibrate.start | Start runtime calibration +| calibrate.stop | Stop runtime calibration +| bypass.start | Put the UPS in bypass mode +| bypass.stop | Take the UPS out of bypass mode +| reset.input.minmax | Reset minimum and maximum input voltage status +| reset.watchdog | Reset watchdog timer (forced reboot of load) +| beeper.enable | Enable UPS beeper/buzzer +| beeper.disable | Disable UPS beeper/buzzer +| beeper.mute | Temporarily mute UPS beeper/buzzer +| beeper.toggle | Toggle UPS beeper/buzzer +| outlet.n.shutdown.return | Turn off the outlet possibly after a delay + and return when power is back +| outlet.n.load.off | Turn off the outlet immediately +| outlet.n.load.on | Turn on the outlet immediately +| outlet.n.load.cycle | Power cycle the outlet immediately +| outlet.n.shutdown.return | Turn off the outlet and return when power is back +|======================================================================== From bf46107dc0d2361aa493706aad43f1f9f4a72e26 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 1 Feb 2019 17:25:46 +0100 Subject: [PATCH 04/45] Augeas support: add CMDSETSTATUSDELAY for upsd.conf Signed-off-by: Arnaud Quette --- scripts/augeas/nutupsdconf.aug.in | 4 +++- scripts/augeas/tests/test_nut.aug | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/augeas/nutupsdconf.aug.in b/scripts/augeas/nutupsdconf.aug.in index d3aef1d300..4a5c2ff65d 100644 --- a/scripts/augeas/nutupsdconf.aug.in +++ b/scripts/augeas/nutupsdconf.aug.in @@ -39,6 +39,7 @@ let comment = Util.comment let path = word let upsd_maxage = [ opt_spc . key "MAXAGE" . sep_spc . store num . eol ] +let upsd_cmdsetstatusdelay = [ opt_spc . key "CMDSETSTATUSDELAY" . sep_spc . store num . eol ] let upsd_statepath = [ opt_spc . key "STATEPATH" . sep_spc . store path . eol ] let upsd_listen = [ opt_spc . key "LISTEN" . sep_spc . [ label "interface" . store ip ] @@ -49,13 +50,14 @@ let upsd_certfile = [ opt_spc . key "CERTFILE" . sep_spc . store path . eol ] (************************************************************************ * MAXAGE seconds + * CMDSETSTATUSDELAY seconds * STATEPATH path * LISTEN interface port * Multiple LISTEN addresses may be specified. The default is to bind to 0.0.0.0 if no LISTEN addresses are specified. * LISTEN 127.0.0.1 LISTEN 192.168.50.1 LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 * *************************************************************************) -let upsd_other = upsd_maxage | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile +let upsd_other = upsd_maxage | upsd_cmdsetstatusdelay | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile let upsd_lns = (upsd_other|comment|empty)* diff --git a/scripts/augeas/tests/test_nut.aug b/scripts/augeas/tests/test_nut.aug index 4153ed7cf5..f5ad7d3fc1 100644 --- a/scripts/augeas/tests/test_nut.aug +++ b/scripts/augeas/tests/test_nut.aug @@ -27,6 +27,7 @@ test NutUpsConf.ups_lns get ups_conf = let upsd_conf = " MAXAGE 30 +CMDSETSTATUSDELAY 600 LISTEN 0.0.0.0 3493 MAXCONN 1024 " @@ -34,6 +35,7 @@ MAXCONN 1024 test NutUpsdConf.upsd_lns get upsd_conf = { } { "MAXAGE" = "30" } + { "CMDSETSTATUSDELAY" = "600" } { "LISTEN" { "interface" = "0.0.0.0" } { "port" = "3493" } } From a3489952152a9378b210cb18d3802e7b51539540 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 4 Feb 2019 14:15:03 +0100 Subject: [PATCH 05/45] INSTCMD and SET VAR status tracking completion Following Daniele Pezzini thorough review, sanitize and improve the code, and also complete documentation Signed-off-by: Arnaud Quette --- docs/net-protocol.txt | 15 ++++++++------ drivers/dstate.c | 27 ++++++++++++++---------- drivers/dstate.h | 1 - server/netget.c | 5 +++++ server/sstate.c | 2 +- server/upsd.c | 48 ++++++++++++++++++++++--------------------- server/upsd.h | 6 +++--- 7 files changed, 59 insertions(+), 45 deletions(-) diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 9cd310e90b..48542e72eb 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -202,6 +202,8 @@ Form: Response: + ON (CMDSET_STATUS feature is enabled) + OFF (CMDSET_STATUS feature is disabled) PENDING (command execution is pending) SUCCESS (command was successfully executed) ERR UNKNOWN (command execution failed with unknown error) @@ -407,8 +409,9 @@ Form: Response: - OK - ERR [...] + OK (if CMDSET_STATUS is not enabled) + OK (if CMDSET_STATUS is enabled) + ERR [...] (see Error responses) CMDSET_STATUS @@ -423,7 +426,7 @@ Form: Response: OK - ERR INVALID-ARGUMENT (if "value" is not "ON" or "OFF") + ERR INVALID-ARGUMENT (if "value" is not "ON" or "OFF") ERR USERNAME-REQUIRED (if not yet authenticated) ERR PASSWORD-REQUIRED (if not yet authenticated) @@ -441,9 +444,9 @@ NOTE: cmdparam is an additional and optional parameter for the command. Response: - OK (if CMDSET_STATUS is not enabled) - OK (if CMDSET_STATUS is enabled) - ERR ... (see Error responses) + OK (if CMDSET_STATUS is not enabled) + OK (if CMDSET_STATUS is enabled) + ERR [...] (see Error responses) LOGOUT diff --git a/drivers/dstate.c b/drivers/dstate.c index 05c3cfa2da..8a0430aeef 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -357,6 +357,12 @@ static int cmd_dump_conn(conn_t *conn) return 1; } + +static void send_cmdset_status(conn_t *conn, const char *id, int value) +{ + send_to_one(conn, "CMDSET_STATUS %s %i\n", id, value); +} + static int sock_arg(conn_t *conn, int numarg, char **arg) { if (numarg < 1) { @@ -405,12 +411,16 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) /* Check if STATUS_ID was provided */ if (numarg >= 4) { if (!strcasecmp(arg[2], "STATUS_ID")) { - cmdid = strdup(arg[3]); + cmdid = arg[3]; } - else { + else if (!strcasecmp(arg[3], "STATUS_ID")) { cmdparam = arg[2]; cmdid = arg[4]; } + else { + upslogx(LOG_NOTICE, "Malformed INSTCMD request"); + return 0; + } upsdebugx(3, "STATUS_ID = %s", cmdid); } @@ -420,7 +430,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) /* send back execution result */ if (cmdid) - dstate_send_cmdset_status(conn, cmdid, ret); + send_cmdset_status(conn, cmdid, ret); /* The command was handled, status is a separate consideration */ return 1; @@ -444,8 +454,9 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) cmdid = arg[4]; } else { - upslogx(LOG_NOTICE, "Got INSTCMD with unsupported parameters (%s/%s)", + upslogx(LOG_NOTICE, "Got SET with unsupported parameters (%s/%s)", arg[3], arg[4]); + return 0; } upsdebugx(3, "STATUS_ID = %s", cmdid); } @@ -456,7 +467,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) /* send back execution result */ if (cmdid) - dstate_send_cmdset_status(conn, cmdid, ret); + send_cmdset_status(conn, cmdid, ret); /* The command was handled, status is a separate consideration */ return 1; @@ -916,12 +927,6 @@ int dstate_is_stale(void) return stale; } - -void dstate_send_cmdset_status(conn_t *conn, const char *id, int value) -{ - send_to_one(conn, "CMDSET_STATUS %s %i\n", id, value); -} - /* ups.status management functions - reducing duplication in the drivers */ /* clean out the temp space for a new pass */ diff --git a/drivers/dstate.h b/drivers/dstate.h index 4022c88a10..dd1b920122 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -70,7 +70,6 @@ int dstate_delcmd(const char *cmd); void dstate_free(void); const st_tree_t *dstate_getroot(void); const cmdlist_t *dstate_getcmdlist(void); -void dstate_send_cmdset_status(conn_t *conn, const char *id, int value); void dstate_dataok(void); void dstate_datastale(void); diff --git a/server/netget.c b/server/netget.c index 8769bfa72a..cccbb1fdcb 100644 --- a/server/netget.c +++ b/server/netget.c @@ -216,6 +216,11 @@ static void get_var(nut_ctype_t *client, const char *upsname, const char *var) void net_get(nut_ctype_t *client, int numarg, const char **arg) { + if (numarg < 1) { + send_err(client, NUT_ERR_INVALID_ARGUMENT); + return; + } + /* GET CMDSET_STATUS [STATUS_ID] */ if (!strcasecmp(arg[0], "CMDSET_STATUS")) { if (numarg < 2) { diff --git a/server/sstate.c b/server/sstate.c index c4c64820bb..e715bbf41a 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -25,6 +25,7 @@ #include "timehead.h" #include "sstate.h" +#include "upsd.h" #include "upstype.h" #include @@ -37,7 +38,6 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) { - upsdebugx(1, "%s: got %i args (%s)", __func__, numargs, arg[0]); if (numargs < 1) return 0; diff --git a/server/upsd.c b/server/upsd.c index 477f7c1171..7c7c158f02 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -692,13 +692,16 @@ void poll_reload(void) /* allocate a new status tracking entry */ int cmdset_status_add(const char *id) { + cmdset_status_t *cmdset_status; + if (!cmdset_status_enabled) return 0; - cmdset_status_t *cmdset_status; - cmdset_status = xcalloc(1, sizeof(*cmdset_status)); + if (!id) + return 0; + cmdset_status->id = xstrdup(id); cmdset_status->status = STAT_PENDING; time(&cmdset_status->request_time); @@ -716,12 +719,12 @@ int cmdset_status_add(const char *id) /* set status of a specific tracking entry */ int cmdset_status_set(const char *id, const char *value) { - /* sanity check */ - if (!cmdset_status_list) - return 0; - cmdset_status_t *cmdset_status, *cmdset_status_next; + /* sanity checks */ + if ((!cmdset_status_list) || (!id) || (!value)) + return 0; + for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { cmdset_status_next = cmdset_status->next; @@ -738,12 +741,12 @@ int cmdset_status_set(const char *id, const char *value) /* free a specific tracking entry */ int cmdset_status_del(const char *id) { + cmdset_status_t *cmdset_status, *cmdset_status_next; + /* sanity check */ - if (!cmdset_status_list) + if ((!cmdset_status_list) || (!id)) return 0; - cmdset_status_t *cmdset_status, *cmdset_status_next; - upsdebugx(3, "%s: deleting id %s", __func__, id); for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { @@ -774,14 +777,14 @@ int cmdset_status_del(const char *id) } /* free all status tracking entries */ -void cmdset_status_free() +void cmdset_status_free(void) { + cmdset_status_t *cmdset_status, *cmdset_status_next; + /* sanity check */ if (!cmdset_status_list) return; - cmdset_status_t *cmdset_status, *cmdset_status_next; - upsdebugx(3, "%s", __func__); for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { @@ -791,14 +794,15 @@ void cmdset_status_free() } /* cleanup status tracking entries according to their age and cmdset_status_delay */ -void cmdset_status_cleanup() +void cmdset_status_cleanup(void) { + cmdset_status_t *cmdset_status, *cmdset_status_next; + time_t now; + /* sanity check */ if (!cmdset_status_list) return; - cmdset_status_t *cmdset_status, *cmdset_status_next; - time_t now; time(&now); upsdebugx(3, "%s", __func__); @@ -816,8 +820,8 @@ void cmdset_status_cleanup() /* get status of a specific tracking entry */ char *cmdset_status_get(const char *id) { - /* sanity check */ - if (!cmdset_status_list) + /* sanity checks */ + if ((!cmdset_status_list) || (!id)) return "ERR UNKNOWN"; cmdset_status_t *cmdset_status, *cmdset_status_next; @@ -845,21 +849,19 @@ char *cmdset_status_get(const char *id) return "ERR UNKNOWN"; /* id not found! */ } -/* disable general status tracking only if no client still use it. +/* disable general status tracking only if no client use it anymore. * return the new value for cmdset_status_enabled (0 if we can disable, 1 * otherwise) */ -int cmdset_status_disable() +int cmdset_status_disable(void) { - int ret = 0; nut_ctype_t *client, *cnext; - /* cleanup client fds */ for (client = firstclient; client; client = cnext) { cnext = client->next; if (client->cmdset_status_enabled == 1) - ret = 1; + return 1; } - return ret; + return 0; } /* service requests and check on new data */ diff --git a/server/upsd.h b/server/upsd.h index 174da73e13..f6abd15612 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -82,10 +82,10 @@ enum { int cmdset_status_add(const char *id); int cmdset_status_set(const char *id, const char *value); int cmdset_status_del(const char *id); -void cmdset_status_free(); -void cmdset_status_cleanup(); +void cmdset_status_free(void); +void cmdset_status_cleanup(void); char *cmdset_status_get(const char *id); -int cmdset_status_disable(); +int cmdset_status_disable(void); /* Commands and settings status tracking structure */ typedef struct cmdset_status_s { From 2a171dd2136740043ef8210775565935574ae127 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 4 Feb 2019 17:27:21 +0100 Subject: [PATCH 06/45] INSTCMD and SET VAR status tracking completion implement support for status tracking in upscmd and upsrw Signed-off-by: Arnaud Quette --- clients/upscmd.c | 65 +++++++++++++++++++++++++++++++++++++++++---- clients/upsrw.c | 60 ++++++++++++++++++++++++++++++++++++++--- docs/man/upscmd.txt | 9 ++++--- docs/man/upsrw.txt | 6 ++++- 4 files changed, 128 insertions(+), 12 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index f4990a872c..50bfe2310f 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -1,6 +1,8 @@ /* upscmd - simple "client" to test instant commands via upsd - Copyright (C) 2000 Russell Kroll + Copyright (C) + 2000 Russell Kroll + 2019 EATON (author: Arnaud Quette ) 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 @@ -28,8 +30,9 @@ #include "upsclient.h" -static char *upsname = NULL, *hostname = NULL; +static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; +int status_info = 0; struct list_t { char *name; @@ -41,13 +44,15 @@ static void usage(const char *prog) printf("Network UPS Tools upscmd %s\n\n", UPS_VERSION); printf("usage: %s [-h]\n", prog); printf(" %s [-l ]\n", prog); - printf(" %s [-u ] [-p ] []\n\n", prog); + printf(" %s [-u ] [-p ] [-w] []\n\n", prog); printf("Administration program to initiate instant commands on UPS hardware.\n"); printf("\n"); printf(" -h display this help text\n"); printf(" -l show available commands on UPS \n"); printf(" -u set username for command authentication\n"); printf(" -p set password for command authentication\n"); + printf(" -w wait for the completion of command by the driver\n"); + printf(" and return its actual result from the device\n"); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf(" Valid instant command - test.panel.start, etc.\n"); @@ -138,7 +143,9 @@ static void listcmds(void) static void do_cmd(char **argv, const int argc) { + int cmd_complete = 0; char buf[SMALLBUF]; + char status_id[37]; /* UUID4_LEN */ if (argc > 1) { snprintf(buf, sizeof(buf), "INSTCMD %s %s %s\n", upsname, argv[0], argv[1]); @@ -154,11 +161,38 @@ static void do_cmd(char **argv, const int argc) fatalx(EXIT_FAILURE, "Instant command failed: %s", upscli_strerror(ups)); } - /* FUTURE: status cookies will tie in here */ + /* verify answer */ if (strncmp(buf, "OK", 2) != 0) { fatalx(EXIT_FAILURE, "Unexpected response from upsd: %s", buf); } + /* check for status tracking id */ + if (status_info) { + /* sanity check on the size: "OK " + UUID4_LEN */ + if (strlen(buf) == 39) { + snprintf(status_id, sizeof(status_id), "%s", buf+3); + + /* send status tracking request, looping if status is PENDING */ + /* FIXME: consider adding a timeout! */ + while (!cmd_complete) { + + snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); + + if (upscli_sendline(ups, buf, strlen(buf)) < 0) { + fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); + } + + /* and get status tracking reply */ + if (upscli_readline(ups, buf, sizeof(buf)) < 0) { + fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); + } + + if (strncmp(buf, "PENDING", 7) != 0) + cmd_complete = 1; + } + } + } + /* reply as usual */ fprintf(stderr, "%s\n", buf); } @@ -180,7 +214,7 @@ int main(int argc, char **argv) char buf[SMALLBUF], username[SMALLBUF], password[SMALLBUF]; const char *prog = xbasename(argv[0]); - while ((i = getopt(argc, argv, "+lhu:p:V")) != -1) { + while ((i = getopt(argc, argv, "+lhu:p:wV")) != -1) { switch (i) { @@ -198,6 +232,10 @@ int main(int argc, char **argv) have_pw = 1; break; + case 'w': + status_info = 1; + break; + case 'V': fatalx(EXIT_SUCCESS, "Network UPS Tools upscmd %s", UPS_VERSION); @@ -313,6 +351,23 @@ int main(int argc, char **argv) fatalx(EXIT_FAILURE, "Set password failed: %s", upscli_strerror(ups)); } + /* enable status tracking ID */ + if (status_info) { + + snprintf(buf, sizeof(buf), "SET CMDSET_STATUS ON\n"); + + if (upscli_sendline(ups, buf, strlen(buf)) < 0) { + fatalx(EXIT_FAILURE, "Can't enable command status tracking: %s", upscli_strerror(ups)); + } + + if (upscli_readline(ups, buf, sizeof(buf)) < 0) { + fatalx(EXIT_FAILURE, "Enabling command status tracking failed: %s", upscli_strerror(ups)); + } + else if (strncmp(buf, "OK", 2) != 0) { /* Verify the result */ + fatalx(EXIT_FAILURE, "Enabling command status tracking failed. upsd answered: %s", buf); + } + } + do_cmd(&argv[1], argc - 1); exit(EXIT_SUCCESS); diff --git a/clients/upsrw.c b/clients/upsrw.c index dd388e1702..47f86d2d75 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -1,6 +1,8 @@ /* upsrw - simple client for read/write variable access (formerly upsct2) - Copyright (C) 1999 Russell Kroll + Copyright (C) + 1999 Russell Kroll + 2019 EATON (author: Arnaud Quette ) 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 @@ -30,6 +32,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; +int status_info = 0; struct list_t { char *name; @@ -48,6 +51,8 @@ static void usage(const char *prog) printf(" use -s VAR=VALUE to avoid prompting for value\n"); printf(" -u set username for command authentication\n"); printf(" -p set password for command authentication\n"); + printf(" -w wait for the completion of setting by the driver\n"); + printf(" and return its actual result from the device\n"); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf("\n"); @@ -67,7 +72,9 @@ static void clean_exit(void) static void do_set(const char *varname, const char *newval) { + int cmd_complete = 0; char buf[SMALLBUF], enc[SMALLBUF]; + char status_id[37]; /* UUID4_LEN */ snprintf(buf, sizeof(buf), "SET VAR %s %s \"%s\"\n", upsname, varname, pconf_encode(newval, enc, sizeof(enc))); @@ -79,11 +86,38 @@ static void do_set(const char *varname, const char *newval) fatalx(EXIT_FAILURE, "Set variable failed: %s", upscli_strerror(ups)); } - /* FUTURE: status cookies will tie in here */ + /* verify answer */ if (strncmp(buf, "OK", 2) != 0) { fatalx(EXIT_FAILURE, "Unexpected response from upsd: %s", buf); } + /* check for status tracking id */ + if (status_info) { + /* sanity check on the size: "OK " + UUID4_LEN */ + if (strlen(buf) == 39) { + snprintf(status_id, sizeof(status_id), "%s", buf+3); + + /* send status tracking request, looping if status is PENDING */ + /* FIXME: consider adding a timeout! */ + while (!cmd_complete) { + + snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); + + if (upscli_sendline(ups, buf, strlen(buf)) < 0) { + fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); + } + + /* and get status tracking reply */ + if (upscli_readline(ups, buf, sizeof(buf)) < 0) { + fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); + } + + if (strncmp(buf, "PENDING", 7) != 0) + cmd_complete = 1; + } + } + } + /* reply as usual */ fprintf(stderr, "%s\n", buf); } @@ -178,6 +212,23 @@ static void do_setvar(const char *varname, char *uin, const char *pass) fatalx(EXIT_FAILURE, "Error: old variable names are not supported"); } + /* enable status tracking ID */ + if (status_info) { + + snprintf(temp, sizeof(temp), "SET CMDSET_STATUS ON\n"); + + if (upscli_sendline(ups, temp, strlen(temp)) < 0) { + fatalx(EXIT_FAILURE, "Can't enable set variable status tracking: %s", upscli_strerror(ups)); + } + + if (upscli_readline(ups, temp, sizeof(temp)) < 0) { + fatalx(EXIT_FAILURE, "Enabling set variable status tracking failed: %s", upscli_strerror(ups)); + } + else if (strncmp(temp, "OK", 2) != 0) { /* Verify the result */ + fatalx(EXIT_FAILURE, "Enabling set variable status tracking failed. upsd answered: %s", temp); + } + } + do_set(varname, newval); } @@ -519,7 +570,7 @@ int main(int argc, char **argv) const char *prog = xbasename(argv[0]); char *password = NULL, *username = NULL, *setvar = NULL; - while ((i = getopt(argc, argv, "+hs:p:u:V")) != -1) { + while ((i = getopt(argc, argv, "+hs:p:u:wV")) != -1) { switch (i) { case 's': @@ -531,6 +582,9 @@ int main(int argc, char **argv) case 'u': username = optarg; break; + case 'w': + status_info = 1; + break; case 'V': printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); exit(EXIT_SUCCESS); diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index 8083fece02..d101f0e19a 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -11,7 +11,7 @@ SYNOPSIS *upscmd* -l 'ups' -*upscmd* [-u 'username'] [-p 'password'] 'ups' 'command' +*upscmd* [-u 'username'] [-p 'password'] [-w] 'ups' 'command' DESCRIPTION ----------- @@ -42,6 +42,10 @@ you will be prompted for this when invoking a command if -u is not used. Set the password to authenticate to the server. This is also optional like -u, and you will be prompted for it if necessary. +*-w*:: +Wait for the completion of command execution by the driver and return its +actual result from the device. + 'ups':: Connect to this UPS. The format is `upsname[@hostname[:port]]`. The default hostname is "localhost". @@ -84,8 +88,7 @@ BUGS ---- There is currently no way to tell the user when the driver requires -confirmation to invoke a command such as `load.off`. Similarly, there is -not yet a way to tell the user if a command succeeds or fails. +confirmation to invoke a command such as `load.off`. This is on the list of things to fix in the future, so don't despair. It involves magic cookies. diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 8053364104..54be6f86bc 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -13,7 +13,7 @@ SYNOPSIS *upsrw* -h -*upsrw* -s 'variable' [-u 'username'] [-p 'password'] 'ups' +*upsrw* -s 'variable' [-u 'username'] [-p 'password'] [-w] 'ups' DESCRIPTION ----------- @@ -58,6 +58,10 @@ linkman:upsd.users[5], and are not linked to system usernames. Set the password to authenticate to the server. This is also optional like -u, and you will be prompted for it if necessary. +*-w*:: +Wait for the completion of setting execution by the driver and return its +actual result from the device. + 'ups':: View or change the settings on this UPS. The format for this option is `upsname[@hostname[:port]]`. The default hostname is "localhost". From f802054227fb2094a35a041122c8d78eb64b3175 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 4 Feb 2019 18:00:56 +0100 Subject: [PATCH 07/45] upscmd/upsrw: delay retries for status tracking Signed-off-by: Arnaud Quette --- clients/upscmd.c | 3 +++ clients/upsrw.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/clients/upscmd.c b/clients/upscmd.c index 50bfe2310f..2a9a8817a8 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -189,6 +189,9 @@ static void do_cmd(char **argv, const int argc) if (strncmp(buf, "PENDING", 7) != 0) cmd_complete = 1; + + /* wait a second before retrying */ + sleep (1); } } } diff --git a/clients/upsrw.c b/clients/upsrw.c index 47f86d2d75..45fa168377 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -114,6 +114,9 @@ static void do_set(const char *varname, const char *newval) if (strncmp(buf, "PENDING", 7) != 0) cmd_complete = 1; + + /* wait a second before retrying */ + sleep (1); } } } From 0f3a01b6e09d924ca79cf426de92da3b6b309d39 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 4 Feb 2019 21:08:38 +0100 Subject: [PATCH 08/45] Remove comment There is no need to condition CMDSET_STATUS commands received by upsd from the driver, on cmdset_status_enabled, since what matters is the presence of a list and id Signed-off-by: Arnaud Quette --- server/sstate.c | 1 - 1 file changed, 1 deletion(-) diff --git a/server/sstate.c b/server/sstate.c index e715bbf41a..9ba413fc05 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -118,7 +118,6 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) } /* CMDSET_STATUS */ - /* FIXME: condition on cmdset_status_enabled ? */ if (!strcasecmp(arg[0], "CMDSET_STATUS")) { cmdset_status_set(arg[1], arg[2]); upsdebugx(1, "CMDSET_STATUS: ID %s status %s", arg[1], arg[2]); From f67ab8dfa3336e7f8ec988fe29a03f3e7067f3fa Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 4 Feb 2019 21:08:56 +0100 Subject: [PATCH 09/45] Complete comment Signed-off-by: Arnaud Quette --- server/netset.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/netset.c b/server/netset.c index 05fc4c711e..f4fac0ee8b 100644 --- a/server/netset.c +++ b/server/netset.c @@ -212,7 +212,9 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) else if (!strcasecmp(arg[1], "OFF")) { /* disable status tracking for this client first */ client->cmdset_status_enabled = 0; - /* then only disable the general one if no other clients use it! */ + /* then only disable the general one if no other clients use it! + * Note: don't call cmdset_status_free() since we want info to + * persist, and cmdset_status_cleanup() takes care of cleaning */ cmdset_status_enabled = cmdset_status_disable(); } else { From 31043437f9ae10230178a1c1e6ccfd007aa5745c Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 4 Feb 2019 22:29:00 +0100 Subject: [PATCH 10/45] UUID v4 implementation replaced the initial rxi/uuid4 library used by a cross platform version. Though more basic, it is enough for our needs, at least for now Signed-off-by: Arnaud Quette --- common/Makefile.am | 2 +- common/uuid4.c | 91 --------------------------------------------- include/Makefile.am | 2 +- include/uuid4.h | 23 ------------ server/netinstcmd.c | 5 +-- server/netset.c | 13 +------ server/upsd.c | 27 ++++++++++++++ server/upsd.h | 35 +++++++++++++++++ 8 files changed, 67 insertions(+), 131 deletions(-) delete mode 100644 common/uuid4.c delete mode 100644 include/uuid4.h diff --git a/common/Makefile.am b/common/Makefile.am index e6953cfc03..e2a4acb697 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -9,7 +9,7 @@ libparseconf_la_SOURCES = parseconf.c # 'dist', and is only required for actual build, in which case # BUILT_SOURCES (in ../include) will ensure nut_version.h will # be built before anything else -libcommon_la_SOURCES = common.c state.c str.c upsconf.c uuid4.c +libcommon_la_SOURCES = common.c state.c str.c upsconf.c libcommonclient_la_SOURCES = common.c state.c str.c # ensure inclusion of local implementation of missing systems functions # using LTLIBOBJS. Refer to configure.in/.ac -> AC_REPLACE_FUNCS diff --git a/common/uuid4.c b/common/uuid4.c deleted file mode 100644 index c285a29317..0000000000 --- a/common/uuid4.c +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2018 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include - -#if defined(_WIN32) -#include -#include -#endif - -#include "uuid4.h" - - -static uint64_t seed[2]; - - -static uint64_t xorshift128plus(uint64_t *s) { - /* http://xorshift.di.unimi.it/xorshift128plus.c */ - uint64_t s1 = s[0]; - const uint64_t s0 = s[1]; - s[0] = s0; - s1 ^= s1 << 23; - s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); - return s[1] + s0; -} - - -int uuid4_init(void) { -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) - int res; - FILE *fp = fopen("/dev/urandom", "rb"); - if (!fp) { - return UUID4_EFAILURE; - } - res = fread(seed, 1, sizeof(seed), fp); - fclose(fp); - if ( res != sizeof(seed) ) { - return UUID4_EFAILURE; - } - -#elif defined(_WIN32) - int res; - HCRYPTPROV hCryptProv; - res = CryptAcquireContext( - &hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); - if (!res) { - return UUID4_EFAILURE; - } - res = CryptGenRandom(hCryptProv, (DWORD) sizeof(seed), (PBYTE) seed); - CryptReleaseContext(hCryptProv, 0); - if (!res) { - return UUID4_EFAILURE; - } - -#else - #error "unsupported platform" - /* FIXME: use rand() */ -#endif - return UUID4_ESUCCESS; -} - - -void uuid4_generate(char *dst) { - static const char *template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"; - static const char *chars = "0123456789abcdef"; - union { unsigned char b[16]; uint64_t word[2]; } s; - const char *p; - int i, n; - /* get random */ - s.word[0] = xorshift128plus(seed); - s.word[1] = xorshift128plus(seed); - /* build string */ - p = template; - i = 0; - while (*p) { - n = s.b[i >> 1]; - n = (i & 1) ? (n >> 4) : (n & 0xf); - switch (*p) { - case 'x' : *dst = chars[n]; i++; break; - case 'y' : *dst = chars[(n & 0x3) + 8]; i++; break; - default : *dst = *p; - } - dst++, p++; - } - *dst = '\0'; -} diff --git a/include/Makefile.am b/include/Makefile.am index 4c812da397..527d0995b3 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,5 @@ dist_noinst_HEADERS = attribute.h common.h extstate.h parseconf.h proto.h \ - state.h str.h timehead.h upsconf.h nut_stdint.h nut_platform.h uuid4.h + state.h str.h timehead.h upsconf.h nut_stdint.h nut_platform.h # http://www.gnu.org/software/automake/manual/automake.html#Clean BUILT_SOURCES = nut_version.h diff --git a/include/uuid4.h b/include/uuid4.h deleted file mode 100644 index 7fbc7864aa..0000000000 --- a/include/uuid4.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2018 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef UUID4_H -#define UUID4_H - -#define UUID4_VERSION "1.0.0" -#define UUID4_LEN 37 - -enum { - UUID4_ESUCCESS = 0, - UUID4_EFAILURE = -1 -}; - -int uuid4_init(void); -void uuid4_generate(char *dst); - -#endif - diff --git a/server/netinstcmd.c b/server/netinstcmd.c index f79a6bb4d0..b2a2b83ad0 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -27,7 +27,6 @@ #include "neterr.h" #include "netinstcmd.h" -#include "uuid4.h" static void send_instcmd(nut_ctype_t *client, const char *upsname, const char *cmdname, const char *value, const char *status_id) @@ -129,10 +128,8 @@ void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) if (client->cmdset_status_enabled) { /* Generate a tracking ID, if client requested status tracking */ - /* FIXME: for now, use a very basic and straightforward approach! */ status_id = xcalloc(1, UUID4_LEN); - uuid4_init(); - uuid4_generate(status_id); + nut_uuid_v4(status_id); } send_instcmd(client, devname, cmdname, cmdparam, status_id); diff --git a/server/netset.c b/server/netset.c index f4fac0ee8b..48e98f5b8e 100644 --- a/server/netset.c +++ b/server/netset.c @@ -26,7 +26,6 @@ #include "neterr.h" #include "netset.h" -#include "uuid4.h" static void set_var(nut_ctype_t *client, const char *upsname, const char *var, const char *newval, const char *status_id) @@ -183,10 +182,8 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) if (client->cmdset_status_enabled) { /* Generate a tracking ID, if client requested status tracking */ - /* FIXME: for now, use a very basic and straightforward approach! */ status_id = xcalloc(1, UUID4_LEN); - uuid4_init(); - uuid4_generate(status_id); + nut_uuid_v4(status_id); } set_var(client, arg[1], arg[2], arg[3], status_id); @@ -200,10 +197,6 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) /* SET CMDSET_STATUS VALUE */ if (!strcasecmp(arg[0], "CMDSET_STATUS")) { - /* Note: for now, limit status tracking to some OS. - * Check for library or homebrew implementation to support - * all platforms */ -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(_WIN32) if (!strcasecmp(arg[1], "ON")) { /* general enablement along with for this client */ cmdset_status_enabled = 1; @@ -225,9 +218,7 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) (cmdset_status_enabled == 1)?"enabled":"disabled"); sendback(client, "OK\n"); -#else - send_err(client, FEATURE-NOT-SUPPORTED); -#endif + return; } diff --git a/server/upsd.c b/server/upsd.c index 7c7c158f02..0a3e3ec722 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -864,6 +864,33 @@ int cmdset_status_disable(void) return 0; } +/* UUID v4 implementation, heavilly inspired from + * https://github.com/spc476/SPCUUID/blob/master/src/uuidlib_v4.c + * https://github.com/spc476/SPCUUID/blob/master/src/uuidlib_toa.c + * Copyright 2013 by Sean Conner. All Rights Reserved. + * Licensed under GPLv3+ */ +int nut_uuid_v4(char *dest) +{ + nut_uuid__t *uuid = xcalloc(1, sizeof(nut_uuid__t)); + + if (dest == NULL) + return 0; + + for (size_t i = 0 ; i < (sizeof(struct uuid) / sizeof(rand__t)) ; i++) + uuid->rnd[i] = (unsigned)rand() + (unsigned)rand(); + + /* set variant and version */ + uuid->flat[6] = (uuid->flat[6] & 0x0F) | 0x40; + uuid->flat[8] = (uuid->flat[8] & 0x3F) | 0x80; + + return snprintf(dest, UUID4_LEN + 1, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + uuid->flat[0], uuid->flat[1], uuid->flat[2], uuid->flat[3], + uuid->flat[4], uuid->flat[5], uuid->flat[6], uuid->flat[7], + uuid->flat[8], uuid->flat[9], uuid->flat[10], uuid->flat[11], + uuid->flat[12], uuid->flat[13], uuid->flat[14], uuid->flat[15]); +} + /* service requests and check on new data */ static void mainloop(void) { diff --git a/server/upsd.h b/server/upsd.h index f6abd15612..651053c621 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -122,6 +122,41 @@ extern cmdset_status_t *cmdset_status_list; #define shutdown_how 2 #endif +/* Minimalistic support for UUID v4 */ +#define UUID4_LEN 36 + +/* adapt to the system */ +#if RAND_MAX == SHRT_MAX + typedef unsigned short rand__t; +#else + typedef unsigned int rand__t; +#endif + +/* From RFC 4122: https://tools.ietf.org/html/rfc4122#section-4.1.2 */ +struct uuid +{ + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} __attribute__((packed)); + +typedef union +{ + struct uuid uuid; + uint8_t flat[sizeof(struct uuid)]; + rand__t rnd [sizeof(struct uuid) / sizeof(rand__t)]; +} __attribute__((packed)) nut_uuid__t; + +#ifdef __SunOS +#pragma pack() +#endif + +/* UUID v4 generation function */ +int nut_uuid_v4(char *dest); + #ifdef __cplusplus /* *INDENT-OFF* */ } From f3ac85257938e7c396008f1bd1a1143bc1813e50 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 08:32:16 +0100 Subject: [PATCH 11/45] status_info should be static Signed-off-by: Arnaud Quette --- clients/upscmd.c | 2 +- clients/upsrw.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 2a9a8817a8..3e17df501e 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -32,7 +32,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; -int status_info = 0; +static int status_info = 0; struct list_t { char *name; diff --git a/clients/upsrw.c b/clients/upsrw.c index 45fa168377..146db727bc 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -30,9 +30,9 @@ #include "upsclient.h" #include "extstate.h" -static char *upsname = NULL, *hostname = NULL; +static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; -int status_info = 0; +static int status_info = 0; struct list_t { char *name; From fe6c478e19d1fac7927713fa6feb93a2e7a4e321 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 08:36:23 +0100 Subject: [PATCH 12/45] No need for else, since fatalx is called before Signed-off-by: Arnaud Quette --- clients/upscmd.c | 4 +++- clients/upsrw.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 3e17df501e..71349fbbbf 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -366,7 +366,9 @@ int main(int argc, char **argv) if (upscli_readline(ups, buf, sizeof(buf)) < 0) { fatalx(EXIT_FAILURE, "Enabling command status tracking failed: %s", upscli_strerror(ups)); } - else if (strncmp(buf, "OK", 2) != 0) { /* Verify the result */ + + /* Verify the result */ + if (strncmp(buf, "OK", 2) != 0) { fatalx(EXIT_FAILURE, "Enabling command status tracking failed. upsd answered: %s", buf); } } diff --git a/clients/upsrw.c b/clients/upsrw.c index 146db727bc..0470a44442 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -227,7 +227,9 @@ static void do_setvar(const char *varname, char *uin, const char *pass) if (upscli_readline(ups, temp, sizeof(temp)) < 0) { fatalx(EXIT_FAILURE, "Enabling set variable status tracking failed: %s", upscli_strerror(ups)); } - else if (strncmp(temp, "OK", 2) != 0) { /* Verify the result */ + + /* Verify the result */ + if (strncmp(temp, "OK", 2) != 0) { fatalx(EXIT_FAILURE, "Enabling set variable status tracking failed. upsd answered: %s", temp); } } From d30e0cbc25819880c891b35c68ac8b2acb146265 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 08:50:23 +0100 Subject: [PATCH 13/45] Suppress \n from debug output Signed-off-by: Arnaud Quette --- drivers/dstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 8a0430aeef..49e4509f88 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -201,7 +201,7 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) ret = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - upsdebugx(2, "%s: sending %s", __func__, buf); + upsdebugx(2, "%s: sending %.*s", __func__, (int)strcspn(buf, "\n"), buf); if (ret < 1) { upsdebugx(2, "%s: nothing to write", __func__); return 1; From 6cdbe9369af5d63c15ea0abf903a8755232e89d8 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 08:53:12 +0100 Subject: [PATCH 14/45] Group sanity checks Signed-off-by: Arnaud Quette --- server/upsd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index 0a3e3ec722..472100a7a3 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -694,14 +694,11 @@ int cmdset_status_add(const char *id) { cmdset_status_t *cmdset_status; - if (!cmdset_status_enabled) + if ((!cmdset_status_enabled) || (!id)) return 0; cmdset_status = xcalloc(1, sizeof(*cmdset_status)); - if (!id) - return 0; - cmdset_status->id = xstrdup(id); cmdset_status->status = STAT_PENDING; time(&cmdset_status->request_time); From 823e968a295ed72136f86565466b589c8d36ff49 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 08:59:53 +0100 Subject: [PATCH 15/45] Get rid of dynamic memory allocation Signed-off-by: Arnaud Quette --- server/upsd.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index 472100a7a3..3e25cd5ba6 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -868,24 +868,29 @@ int cmdset_status_disable(void) * Licensed under GPLv3+ */ int nut_uuid_v4(char *dest) { - nut_uuid__t *uuid = xcalloc(1, sizeof(nut_uuid__t)); + nut_uuid__t uuid; + size_t i; - if (dest == NULL) + if (!dest) return 0; - for (size_t i = 0 ; i < (sizeof(struct uuid) / sizeof(rand__t)) ; i++) - uuid->rnd[i] = (unsigned)rand() + (unsigned)rand(); + for (i = 0; i < (sizeof(struct uuid) / sizeof(rand__t)); i++) + uuid.rnd[i] = (unsigned)rand() + (unsigned)rand(); /* set variant and version */ - uuid->flat[6] = (uuid->flat[6] & 0x0F) | 0x40; - uuid->flat[8] = (uuid->flat[8] & 0x3F) | 0x80; + uuid.flat[6] = (uuid.flat[6] & 0x0F) | 0x40; + uuid.flat[8] = (uuid.flat[8] & 0x3F) | 0x80; - return snprintf(dest, UUID4_LEN + 1, + return snprintf( + dest, + UUID4_LEN, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", - uuid->flat[0], uuid->flat[1], uuid->flat[2], uuid->flat[3], - uuid->flat[4], uuid->flat[5], uuid->flat[6], uuid->flat[7], - uuid->flat[8], uuid->flat[9], uuid->flat[10], uuid->flat[11], - uuid->flat[12], uuid->flat[13], uuid->flat[14], uuid->flat[15]); + uuid.flat[0], uuid.flat[1], uuid.flat[2], uuid.flat[3], + uuid.flat[4], uuid.flat[5], + uuid.flat[6], uuid.flat[7], + uuid.flat[8], uuid.flat[9], + uuid.flat[10], uuid.flat[11], uuid.flat[12], uuid.flat[13], uuid.flat[14], uuid.flat[15] + ); } /* service requests and check on new data */ From 4d36b831f710032b83f9bbd5b822c100c5f9d684 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 09:04:26 +0100 Subject: [PATCH 16/45] Improve and enforce the use of UUID4_LEN Signed-off-by: Arnaud Quette --- clients/upscmd.c | 4 ++-- clients/upsrw.c | 4 ++-- server/upsd.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 71349fbbbf..77d0c1fca4 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -145,7 +145,7 @@ static void do_cmd(char **argv, const int argc) { int cmd_complete = 0; char buf[SMALLBUF]; - char status_id[37]; /* UUID4_LEN */ + char status_id[UUID4_LEN]; if (argc > 1) { snprintf(buf, sizeof(buf), "INSTCMD %s %s %s\n", upsname, argv[0], argv[1]); @@ -169,7 +169,7 @@ static void do_cmd(char **argv, const int argc) /* check for status tracking id */ if (status_info) { /* sanity check on the size: "OK " + UUID4_LEN */ - if (strlen(buf) == 39) { + if (strlen(buf) == UUID4_LEN + 2) { snprintf(status_id, sizeof(status_id), "%s", buf+3); /* send status tracking request, looping if status is PENDING */ diff --git a/clients/upsrw.c b/clients/upsrw.c index 0470a44442..a42be1348e 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -74,7 +74,7 @@ static void do_set(const char *varname, const char *newval) { int cmd_complete = 0; char buf[SMALLBUF], enc[SMALLBUF]; - char status_id[37]; /* UUID4_LEN */ + char status_id[UUID4_LEN]; snprintf(buf, sizeof(buf), "SET VAR %s %s \"%s\"\n", upsname, varname, pconf_encode(newval, enc, sizeof(enc))); @@ -94,7 +94,7 @@ static void do_set(const char *varname, const char *newval) /* check for status tracking id */ if (status_info) { /* sanity check on the size: "OK " + UUID4_LEN */ - if (strlen(buf) == 39) { + if (strlen(buf) == UUID4_LEN + 2) { snprintf(status_id, sizeof(status_id), "%s", buf+3); /* send status tracking request, looping if status is PENDING */ diff --git a/server/upsd.h b/server/upsd.h index 651053c621..3689b6d0dd 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -123,7 +123,7 @@ extern cmdset_status_t *cmdset_status_list; #endif /* Minimalistic support for UUID v4 */ -#define UUID4_LEN 36 +#define UUID4_LEN 37 /* adapt to the system */ #if RAND_MAX == SHRT_MAX From aef9ea79171850253cf416f3ae8955208e355572 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 09:08:16 +0100 Subject: [PATCH 17/45] Comment on the size of dest for nut_uuid_v4() Signed-off-by: Arnaud Quette --- server/upsd.c | 3 ++- server/upsd.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index 3e25cd5ba6..c484d87751 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -865,7 +865,8 @@ int cmdset_status_disable(void) * https://github.com/spc476/SPCUUID/blob/master/src/uuidlib_v4.c * https://github.com/spc476/SPCUUID/blob/master/src/uuidlib_toa.c * Copyright 2013 by Sean Conner. All Rights Reserved. - * Licensed under GPLv3+ */ + * Licensed under GPLv3+ + * Note: 'dest' must be at least `UUID4_LEN` long */ int nut_uuid_v4(char *dest) { nut_uuid__t uuid; diff --git a/server/upsd.h b/server/upsd.h index 3689b6d0dd..0c887eb9fa 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -154,7 +154,8 @@ typedef union #pragma pack() #endif -/* UUID v4 generation function */ +/* UUID v4 generation function + * Note: 'dest' must be at least `UUID4_LEN` long */ int nut_uuid_v4(char *dest); #ifdef __cplusplus From 25eca74a13e2b19260c8b7c32b34f8233b3ff046 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 09:16:22 +0100 Subject: [PATCH 18/45] Move structures and defines to more appropriate places Signed-off-by: Arnaud Quette --- include/common.h | 2 ++ server/upsd.c | 31 +++++++++++++++++++++++++++++++ server/upsd.h | 32 -------------------------------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/include/common.h b/include/common.h index b1fb53ed2d..331a6d136c 100644 --- a/include/common.h +++ b/include/common.h @@ -128,6 +128,8 @@ char * get_libname(const char* base_libname); #define SMALLBUF 512 #define LARGEBUF 1024 +#define UUID4_LEN 37 + /* Provide declarations for getopt() global variables */ #ifdef NEED_GETOPT_H diff --git a/server/upsd.c b/server/upsd.c index c484d87751..ca0b5d49e4 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -106,6 +106,37 @@ static char pidfn[SMALLBUF]; /* set by signal handlers */ static int reload_flag = 0, exit_flag = 0; +/* Minimalistic support for UUID v4 */ +/* adapt to the system */ +#if RAND_MAX == SHRT_MAX + typedef unsigned short rand__t; +#else + typedef unsigned int rand__t; +#endif + +/* From RFC 4122: https://tools.ietf.org/html/rfc4122#section-4.1.2 */ +struct uuid +{ + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} __attribute__((packed)); + +typedef union +{ + struct uuid uuid; + uint8_t flat[sizeof(struct uuid)]; + rand__t rnd [sizeof(struct uuid) / sizeof(rand__t)]; +} __attribute__((packed)) nut_uuid__t; + +#ifdef __SunOS +#pragma pack() +#endif + + static const char *inet_ntopW (struct sockaddr_storage *s) { static char str[40]; diff --git a/server/upsd.h b/server/upsd.h index 0c887eb9fa..9d27483e0b 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -122,38 +122,6 @@ extern cmdset_status_t *cmdset_status_list; #define shutdown_how 2 #endif -/* Minimalistic support for UUID v4 */ -#define UUID4_LEN 37 - -/* adapt to the system */ -#if RAND_MAX == SHRT_MAX - typedef unsigned short rand__t; -#else - typedef unsigned int rand__t; -#endif - -/* From RFC 4122: https://tools.ietf.org/html/rfc4122#section-4.1.2 */ -struct uuid -{ - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint8_t clock_seq_hi_and_reserved; - uint8_t clock_seq_low; - uint8_t node[6]; -} __attribute__((packed)); - -typedef union -{ - struct uuid uuid; - uint8_t flat[sizeof(struct uuid)]; - rand__t rnd [sizeof(struct uuid) / sizeof(rand__t)]; -} __attribute__((packed)) nut_uuid__t; - -#ifdef __SunOS -#pragma pack() -#endif - /* UUID v4 generation function * Note: 'dest' must be at least `UUID4_LEN` long */ int nut_uuid_v4(char *dest); From 2e9c27ce4af4fa593f9ae79cf107fe6bcba95457 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 13:06:57 +0100 Subject: [PATCH 19/45] Add functions with timeout Add upscli_sendline_timeout and upscli_readline_timeout, beside from the classic blocking versions. Also make a common define for timeout, and use it in upsclient and nut-scanner Signed-off-by: Arnaud Quette --- clients/upsclient.c | 28 +++++++++++++++++++--------- clients/upsclient.h | 2 ++ include/common.h | 3 +++ tools/nut-scanner/nut-scanner.c | 2 -- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/clients/upsclient.c b/clients/upsclient.c index b90587b001..e026fcc636 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -567,7 +567,7 @@ static int upscli_select_read(const int fd, void *buf, const size_t buflen, cons } /* internal: abstract the SSL calls for the other functions */ -static int net_read(UPSCONN_t *ups, char *buf, size_t buflen) +static int net_read(UPSCONN_t *ups, char *buf, size_t buflen, int timeout) { int ret = -1; @@ -587,7 +587,7 @@ static int net_read(UPSCONN_t *ups, char *buf, size_t buflen) } #endif - ret = upscli_select_read(ups->fd, buf, buflen, 5, 0); + ret = upscli_select_read(ups->fd, buf, buflen, timeout, 0); /* error reading data, server disconnected? */ if (ret < 0) { @@ -628,7 +628,7 @@ static int upscli_select_write(const int fd, const void *buf, const size_t bufle } /* internal: abstract the SSL calls for the other functions */ -static int net_write(UPSCONN_t *ups, const char *buf, size_t buflen) +static int net_write(UPSCONN_t *ups, const char *buf, size_t buflen, int timeout) { int ret = -1; @@ -648,7 +648,7 @@ static int net_write(UPSCONN_t *ups, const char *buf, size_t buflen) } #endif - ret = upscli_select_write(ups->fd, buf, buflen, 0, 0); + ret = upscli_select_write(ups->fd, buf, buflen, timeout, 0); /* error writing data, server disconnected? */ if (ret < 0) { @@ -1326,7 +1326,7 @@ int upscli_list_next(UPSCONN_t *ups, unsigned int numq, const char **query, return 1; } -int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen) +int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, int timeout) { int ret; @@ -1349,7 +1349,7 @@ int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen) return -1; } - ret = net_write(ups, buf, buflen); + ret = net_write(ups, buf, buflen, timeout); if (ret < 1) { upscli_disconnect(ups); @@ -1359,7 +1359,12 @@ int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen) return 0; } -int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen) +int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen) +{ + return upscli_sendline_timeout(ups, buf, buflen, 0); +} + +int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, int timeout) { int ret; size_t recv; @@ -1387,7 +1392,7 @@ int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen) if (ups->readidx == ups->readlen) { - ret = net_read(ups, ups->readbuf, sizeof(ups->readbuf)); + ret = net_read(ups, ups->readbuf, sizeof(ups->readbuf), timeout); if (ret < 1) { upscli_disconnect(ups); @@ -1409,6 +1414,11 @@ int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen) return 0; } +int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen) +{ + return upscli_readline_timeout(ups, buf, buflen, DEFAULT_TIMEOUT); +} + /* split upsname[@hostname[:port]] into separate components */ int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port) { @@ -1518,7 +1528,7 @@ int upscli_disconnect(UPSCONN_t *ups) return 0; } - net_write(ups, "LOGOUT\n", 7); + net_write(ups, "LOGOUT\n", 7, 0); #ifdef WITH_OPENSSL if (ups->ssl) { diff --git a/clients/upsclient.h b/clients/upsclient.h index ac5fb5d567..7836a14457 100644 --- a/clients/upsclient.h +++ b/clients/upsclient.h @@ -86,8 +86,10 @@ int upscli_list_start(UPSCONN_t *ups, unsigned int numq, const char **query); int upscli_list_next(UPSCONN_t *ups, unsigned int numq, const char **query, unsigned int *numa, char ***answer); +int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, int timeout); int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); +int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, int timeout); int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); int upscli_splitname(const char *buf, char **upsname, char **hostname, diff --git a/include/common.h b/include/common.h index 331a6d136c..c7db6ac011 100644 --- a/include/common.h +++ b/include/common.h @@ -53,6 +53,9 @@ extern "C" { extern const char *UPS_VERSION; +/* Used by upsclient and nut-scanner */ +#define DEFAULT_TIMEOUT 5 + /* get the syslog ready for us */ void open_syslog(const char *progname); diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 8b41a3dbf9..9f622f67ba 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -38,8 +38,6 @@ #include "nut-scan.h" -#define DEFAULT_TIMEOUT 5 - #define ERR_BAD_OPTION (-1) const char optstring[] = "?ht:s:e:E:c:l:u:W:X:w:x:p:b:B:d:L:CUSMOAm:NPqIVaD"; From f91255db61a6c2ae98f327ece42aff6bf485f4e8 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 13:26:01 +0100 Subject: [PATCH 20/45] upscmd/upsrw: add a timeout option Signed-off-by: Arnaud Quette --- clients/upscmd.c | 22 ++++++++++++++++++---- clients/upsrw.c | 23 ++++++++++++++++++----- docs/man/upscmd.txt | 10 ++++++++-- docs/man/upsrw.txt | 10 ++++++++-- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 77d0c1fca4..f5fbb341aa 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -33,6 +33,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; static int status_info = 0; +static int timeout = 10; struct list_t { char *name; @@ -44,7 +45,7 @@ static void usage(const char *prog) printf("Network UPS Tools upscmd %s\n\n", UPS_VERSION); printf("usage: %s [-h]\n", prog); printf(" %s [-l ]\n", prog); - printf(" %s [-u ] [-p ] [-w] []\n\n", prog); + printf(" %s [-u ] [-p ] [-w] [-t ] []\n\n", prog); printf("Administration program to initiate instant commands on UPS hardware.\n"); printf("\n"); printf(" -h display this help text\n"); @@ -53,6 +54,7 @@ static void usage(const char *prog) printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of command by the driver\n"); printf(" and return its actual result from the device\n"); + printf(" -t set a timeout when using -w (default \"10\" seconds)\n"); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf(" Valid instant command - test.panel.start, etc.\n"); @@ -146,6 +148,7 @@ static void do_cmd(char **argv, const int argc) int cmd_complete = 0; char buf[SMALLBUF]; char status_id[UUID4_LEN]; + time_t start, now; if (argc > 1) { snprintf(buf, sizeof(buf), "INSTCMD %s %s %s\n", upsname, argv[0], argv[1]); @@ -166,6 +169,8 @@ static void do_cmd(char **argv, const int argc) fatalx(EXIT_FAILURE, "Unexpected response from upsd: %s", buf); } + time(&start); + /* check for status tracking id */ if (status_info) { /* sanity check on the size: "OK " + UUID4_LEN */ @@ -173,9 +178,14 @@ static void do_cmd(char **argv, const int argc) snprintf(status_id, sizeof(status_id), "%s", buf+3); /* send status tracking request, looping if status is PENDING */ - /* FIXME: consider adding a timeout! */ while (!cmd_complete) { + /* check for timeout */ + time(&now); + if (difftime(now, start) >= timeout) { + fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); + } + snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); if (upscli_sendline(ups, buf, strlen(buf)) < 0) { @@ -183,7 +193,7 @@ static void do_cmd(char **argv, const int argc) } /* and get status tracking reply */ - if (upscli_readline(ups, buf, sizeof(buf)) < 0) { + if (upscli_readline_timeout(ups, buf, sizeof(buf), timeout) < 0) { fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); } @@ -217,7 +227,7 @@ int main(int argc, char **argv) char buf[SMALLBUF], username[SMALLBUF], password[SMALLBUF]; const char *prog = xbasename(argv[0]); - while ((i = getopt(argc, argv, "+lhu:p:wV")) != -1) { + while ((i = getopt(argc, argv, "+lhu:p:t:wV")) != -1) { switch (i) { @@ -235,6 +245,10 @@ int main(int argc, char **argv) have_pw = 1; break; + case 't': + timeout = atoi(optarg); + break; + case 'w': status_info = 1; break; diff --git a/clients/upsrw.c b/clients/upsrw.c index a42be1348e..e2f9433124 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -33,6 +33,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; static int status_info = 0; +static int timeout = 10; struct list_t { char *name; @@ -43,7 +44,7 @@ static void usage(const char *prog) { printf("Network UPS Tools %s %s\n\n", prog, UPS_VERSION); printf("usage: %s [-h]\n", prog); - printf(" %s [-s ] [-u ] [-p ] \n\n", prog); + printf(" %s [-s ] [-u ] [-p ] [-w] [-t ] \n\n", prog); printf("Demo program to set variables within UPS hardware.\n"); printf("\n"); printf(" -h display this help text\n"); @@ -53,6 +54,7 @@ static void usage(const char *prog) printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of setting by the driver\n"); printf(" and return its actual result from the device\n"); + printf(" -t set a timeout when using -w (default \"10\" seconds)\n"); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf("\n"); @@ -75,6 +77,7 @@ static void do_set(const char *varname, const char *newval) int cmd_complete = 0; char buf[SMALLBUF], enc[SMALLBUF]; char status_id[UUID4_LEN]; + time_t start, now; snprintf(buf, sizeof(buf), "SET VAR %s %s \"%s\"\n", upsname, varname, pconf_encode(newval, enc, sizeof(enc))); @@ -91,6 +94,8 @@ static void do_set(const char *varname, const char *newval) fatalx(EXIT_FAILURE, "Unexpected response from upsd: %s", buf); } + time(&start); + /* check for status tracking id */ if (status_info) { /* sanity check on the size: "OK " + UUID4_LEN */ @@ -98,9 +103,14 @@ static void do_set(const char *varname, const char *newval) snprintf(status_id, sizeof(status_id), "%s", buf+3); /* send status tracking request, looping if status is PENDING */ - /* FIXME: consider adding a timeout! */ while (!cmd_complete) { + /* check for timeout */ + time(&now); + if (difftime(now, start) >= timeout) { + fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); + } + snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); if (upscli_sendline(ups, buf, strlen(buf)) < 0) { @@ -108,7 +118,7 @@ static void do_set(const char *varname, const char *newval) } /* and get status tracking reply */ - if (upscli_readline(ups, buf, sizeof(buf)) < 0) { + if (upscli_readline_timeout(ups, buf, sizeof(buf), timeout) < 0) { fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); } @@ -425,7 +435,7 @@ static void do_type(const char *varname) ret = upscli_get(ups, numq, query, &numa, &answer); if ((ret < 0) || (numa < numq)) { - printf("Unknown type\n"); + printf("Unknown type\n"); return; } @@ -575,7 +585,7 @@ int main(int argc, char **argv) const char *prog = xbasename(argv[0]); char *password = NULL, *username = NULL, *setvar = NULL; - while ((i = getopt(argc, argv, "+hs:p:u:wV")) != -1) { + while ((i = getopt(argc, argv, "+hs:p:t:u:wV")) != -1) { switch (i) { case 's': @@ -584,6 +594,9 @@ int main(int argc, char **argv) case 'p': password = optarg; break; + case 't': + timeout = atoi(optarg); + break; case 'u': username = optarg; break; diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index d101f0e19a..7bf2aee76d 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -11,7 +11,7 @@ SYNOPSIS *upscmd* -l 'ups' -*upscmd* [-u 'username'] [-p 'password'] [-w] 'ups' 'command' +*upscmd* [-u 'username'] [-p 'password'] [-w] [-t ] 'ups' 'command' DESCRIPTION ----------- @@ -44,7 +44,13 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of command execution by the driver and return its -actual result from the device. +actual result from the device. Note that this feature requires that upsd +supports CMDSET_STATUS (NUT version 2.7.5 or higher) or it will otherwise fail. +The command will also block until an actual result is provided from the driver, +or the timeout is reached (see *-t*). + +*-t* 'seconds':: +Set a timeout when using *-w*. Default to 10 seconds. 'ups':: Connect to this UPS. The format is `upsname[@hostname[:port]]`. The default diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 54be6f86bc..cb9c3c9c4d 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -13,7 +13,7 @@ SYNOPSIS *upsrw* -h -*upsrw* -s 'variable' [-u 'username'] [-p 'password'] [-w] 'ups' +*upsrw* -s 'variable' [-u 'username'] [-p 'password'] [-w] [-t ] 'ups' DESCRIPTION ----------- @@ -60,7 +60,13 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of setting execution by the driver and return its -actual result from the device. +actual result from the device. Note that this feature requires that upsd +supports CMDSET_STATUS (NUT version 2.7.5 or higher) or it will otherwise fail. +The command will also block until an actual result is provided from the driver, +or the timeout is reached (see *-t*). + +*-t* 'seconds':: +Set a timeout when using *-w*. Default to 10 seconds. 'ups':: View or change the settings on this UPS. The format for this option is From 14734965410e87405fb0c2a4082bf228baa5f49a Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 16:58:16 +0100 Subject: [PATCH 21/45] Basic homebrew UUID v4 implementation Signed-off-by: Arnaud Quette --- server/upsd.c | 66 +++++++++++++-------------------------------------- server/upsd.h | 2 +- 2 files changed, 18 insertions(+), 50 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index ca0b5d49e4..e7181fa940 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -4,6 +4,7 @@ 1999 Russell Kroll 2008 Arjen de Korte 2011 - 2012 Arnaud Quette + 2019 Eaton (author: Arnaud Quette ) 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 @@ -107,34 +108,9 @@ static char pidfn[SMALLBUF]; static int reload_flag = 0, exit_flag = 0; /* Minimalistic support for UUID v4 */ -/* adapt to the system */ -#if RAND_MAX == SHRT_MAX - typedef unsigned short rand__t; -#else - typedef unsigned int rand__t; -#endif - -/* From RFC 4122: https://tools.ietf.org/html/rfc4122#section-4.1.2 */ -struct uuid -{ - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint8_t clock_seq_hi_and_reserved; - uint8_t clock_seq_low; - uint8_t node[6]; -} __attribute__((packed)); - -typedef union -{ - struct uuid uuid; - uint8_t flat[sizeof(struct uuid)]; - rand__t rnd [sizeof(struct uuid) / sizeof(rand__t)]; -} __attribute__((packed)) nut_uuid__t; - -#ifdef __SunOS -#pragma pack() -#endif +/* Ref: RFC 4122 https://tools.ietf.org/html/rfc4122#section-4.1.2 */ +#define UUID4_BYTESIZE 16 +uint8_t nut_uuid[UUID4_BYTESIZE]; static const char *inet_ntopW (struct sockaddr_storage *s) @@ -892,37 +868,29 @@ int cmdset_status_disable(void) return 0; } -/* UUID v4 implementation, heavilly inspired from - * https://github.com/spc476/SPCUUID/blob/master/src/uuidlib_v4.c - * https://github.com/spc476/SPCUUID/blob/master/src/uuidlib_toa.c - * Copyright 2013 by Sean Conner. All Rights Reserved. - * Licensed under GPLv3+ +/* UUID v4 basic implementation * Note: 'dest' must be at least `UUID4_LEN` long */ -int nut_uuid_v4(char *dest) +int nut_uuid_v4(char *uuid_str) { - nut_uuid__t uuid; size_t i; + uint8_t nut_uuid[UUID4_BYTESIZE]; - if (!dest) + if (!uuid_str) return 0; - for (i = 0; i < (sizeof(struct uuid) / sizeof(rand__t)); i++) - uuid.rnd[i] = (unsigned)rand() + (unsigned)rand(); + for (i = 0; i < UUID4_BYTESIZE; i++) + nut_uuid[i] = (unsigned)rand() + (unsigned)rand(); /* set variant and version */ - uuid.flat[6] = (uuid.flat[6] & 0x0F) | 0x40; - uuid.flat[8] = (uuid.flat[8] & 0x3F) | 0x80; + nut_uuid[6] = (nut_uuid[6] & 0x0F) | 0x40; + nut_uuid[8] = (nut_uuid[8] & 0x3F) | 0x80; - return snprintf( - dest, - UUID4_LEN, + return snprintf(uuid_str, UUID4_LEN, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", - uuid.flat[0], uuid.flat[1], uuid.flat[2], uuid.flat[3], - uuid.flat[4], uuid.flat[5], - uuid.flat[6], uuid.flat[7], - uuid.flat[8], uuid.flat[9], - uuid.flat[10], uuid.flat[11], uuid.flat[12], uuid.flat[13], uuid.flat[14], uuid.flat[15] - ); + nut_uuid[0], nut_uuid[1], nut_uuid[2], nut_uuid[3], + nut_uuid[4], nut_uuid[5], nut_uuid[6], nut_uuid[7], + nut_uuid[8], nut_uuid[9], nut_uuid[10], nut_uuid[11], + nut_uuid[12], nut_uuid[13], nut_uuid[14], nut_uuid[15]); } /* service requests and check on new data */ diff --git a/server/upsd.h b/server/upsd.h index 9d27483e0b..c08f00d851 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -124,7 +124,7 @@ extern cmdset_status_t *cmdset_status_list; /* UUID v4 generation function * Note: 'dest' must be at least `UUID4_LEN` long */ -int nut_uuid_v4(char *dest); +int nut_uuid_v4(char *uuid_str); #ifdef __cplusplus /* *INDENT-OFF* */ From f91c893a3e50ff22370ada88e69b72873772107e Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 19:47:30 +0100 Subject: [PATCH 22/45] Prefer to use static buffer for UUID Signed-off-by: Arnaud Quette --- server/netinstcmd.c | 7 +------ server/netset.c | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/server/netinstcmd.c b/server/netinstcmd.c index b2a2b83ad0..cf53f082ad 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -112,7 +112,7 @@ void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) const char *devname = NULL; const char *cmdname = NULL; const char *cmdparam = NULL; - char *status_id = NULL; + char status_id[UUID4_LEN] = ""; if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); @@ -128,15 +128,10 @@ void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) if (client->cmdset_status_enabled) { /* Generate a tracking ID, if client requested status tracking */ - status_id = xcalloc(1, UUID4_LEN); nut_uuid_v4(status_id); } send_instcmd(client, devname, cmdname, cmdparam, status_id); - /* free the generated uuid if needed */ - if (status_id) - free(status_id); - return; } diff --git a/server/netset.c b/server/netset.c index 48e98f5b8e..85af7b34c2 100644 --- a/server/netset.c +++ b/server/netset.c @@ -165,7 +165,7 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, void net_set(nut_ctype_t *client, int numarg, const char **arg) { - char *status_id = NULL; + char status_id[UUID4_LEN] = ""; /* Base verification, to ensure that we have at least the SET parameter */ if (numarg < 2) { @@ -182,16 +182,11 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) if (client->cmdset_status_enabled) { /* Generate a tracking ID, if client requested status tracking */ - status_id = xcalloc(1, UUID4_LEN); nut_uuid_v4(status_id); } set_var(client, arg[1], arg[2], arg[3], status_id); - /* free the generated uuid if needed */ - if (status_id) - free(status_id); - return; } From b7d278d30539f0064e58fbb600fc31fd89f97ff1 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 6 Feb 2019 20:03:20 +0100 Subject: [PATCH 23/45] log actual result of instcmd / setvar Signed-off-by: Arnaud Quette --- server/sstate.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/sstate.c b/server/sstate.c index 9ba413fc05..215f7c6313 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -121,6 +121,11 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) if (!strcasecmp(arg[0], "CMDSET_STATUS")) { cmdset_status_set(arg[1], arg[2]); upsdebugx(1, "CMDSET_STATUS: ID %s status %s", arg[1], arg[2]); + + /* log actual result of instcmd / setvar */ + if (strncmp(arg[2], "PENDING", 7) != 0) { + upslogx(LOG_INFO, "tracking ID: %s\tresult: %s", arg[1], cmdset_status_get(arg[1])); + } return 1; } From 9989123451a3db7d6bdb889fca3a865bbd79a9a8 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 7 Feb 2019 15:02:07 +0100 Subject: [PATCH 24/45] Fix tracking ID reporting due to static memory changes Signed-off-by: Arnaud Quette --- server/netinstcmd.c | 9 +++++---- server/netset.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/server/netinstcmd.c b/server/netinstcmd.c index cf53f082ad..73254e15a6 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -31,7 +31,7 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, const char *cmdname, const char *value, const char *status_id) { - int found; + int found, have_status_id = 0; upstype_t *ups; const cmdlist_t *ctmp; char sockcmd[SMALLBUF], esc[SMALLBUF]; @@ -78,10 +78,11 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, snprintfcat(sockcmd, sizeof(sockcmd), " %s", pconf_encode(value, esc, sizeof(esc))); /* see if the user want execution tracking for this command */ - if (status_id != NULL) { + if (strcmp(status_id, "") != 0) { snprintfcat(sockcmd, sizeof(sockcmd), " STATUS_ID %s", status_id); /* Add an entry in the tracking structure */ cmdset_status_add(status_id); + have_status_id = 1; } /* add EOL */ @@ -92,7 +93,7 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, (value != NULL)?" with value ":"", (value != NULL)?value:"", ups->name, - (status_id != NULL)?status_id:"disabled"); + (have_status_id)?status_id:"disabled"); if (!sstate_sendline(ups, sockcmd)) { upslogx(LOG_INFO, "Set command send failed"); @@ -101,7 +102,7 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, } /* return the result, possibly including status_id */ - if (status_id != NULL) + if (have_status_id) sendback(client, "OK %s\n", status_id); else sendback(client, "OK\n"); diff --git a/server/netset.c b/server/netset.c index 85af7b34c2..fcc969dc33 100644 --- a/server/netset.c +++ b/server/netset.c @@ -35,6 +35,7 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, const enum_t *etmp; const range_t *rtmp; char cmd[SMALLBUF], esc[SMALLBUF]; + int have_status_id = 0; ups = get_ups_ptr(upsname); @@ -134,22 +135,23 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, /* must be OK now */ - upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s (tracking ID: %s)", - client->username, client->addr, var, ups->name, newval, - (status_id != NULL)?status_id:"disabled"); - /* see if the user want execution tracking for this command */ - if (status_id != NULL) { + if (strcmp(status_id, "") != 0) { snprintf(cmd, sizeof(cmd), "SET %s \"%s\" STATUS_ID %s\n", var, pconf_encode(newval, esc, sizeof(esc)), status_id); /* Add an entry in the tracking structure */ cmdset_status_add(status_id); + have_status_id = 1; } else { snprintf(cmd, sizeof(cmd), "SET %s \"%s\"\n", var, pconf_encode(newval, esc, sizeof(esc))); } + upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s (tracking ID: %s)", + client->username, client->addr, var, ups->name, newval, + (have_status_id)?status_id:"disabled"); + if (!sstate_sendline(ups, cmd)) { upslogx(LOG_INFO, "Set command send failed"); send_err(client, NUT_ERR_SET_FAILED); @@ -157,7 +159,7 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, } /* return the result, possibly including status_id */ - if (status_id != NULL) + if (have_status_id) sendback(client, "OK %s\n", status_id); else sendback(client, "OK\n"); From 7fda5a7dd0837bb8739903235f20a955f18ae5db Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 00:35:45 +0100 Subject: [PATCH 25/45] upsclient: use unsigned int for timeouts Also, explicit that upscli_cleanup() takes no argument. --- clients/upsclient.c | 10 +++++----- clients/upsclient.h | 6 +++--- docs/man/upscli_cleanup.txt | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clients/upsclient.c b/clients/upsclient.c index e026fcc636..bbd7f47c6e 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -442,7 +442,7 @@ static HOST_CERT_t* upscli_find_host_cert(const char* hostname) return NULL; } -int upscli_cleanup() +int upscli_cleanup(void) { #ifdef WITH_OPENSSL if (ssl_ctx) { @@ -567,7 +567,7 @@ static int upscli_select_read(const int fd, void *buf, const size_t buflen, cons } /* internal: abstract the SSL calls for the other functions */ -static int net_read(UPSCONN_t *ups, char *buf, size_t buflen, int timeout) +static int net_read(UPSCONN_t *ups, char *buf, size_t buflen, unsigned int timeout) { int ret = -1; @@ -628,7 +628,7 @@ static int upscli_select_write(const int fd, const void *buf, const size_t bufle } /* internal: abstract the SSL calls for the other functions */ -static int net_write(UPSCONN_t *ups, const char *buf, size_t buflen, int timeout) +static int net_write(UPSCONN_t *ups, const char *buf, size_t buflen, unsigned int timeout) { int ret = -1; @@ -1326,7 +1326,7 @@ int upscli_list_next(UPSCONN_t *ups, unsigned int numq, const char **query, return 1; } -int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, int timeout) +int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, unsigned int timeout) { int ret; @@ -1364,7 +1364,7 @@ int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen) return upscli_sendline_timeout(ups, buf, buflen, 0); } -int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, int timeout) +int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, unsigned int timeout) { int ret; size_t recv; diff --git a/clients/upsclient.h b/clients/upsclient.h index 7836a14457..d3da201bf1 100644 --- a/clients/upsclient.h +++ b/clients/upsclient.h @@ -69,7 +69,7 @@ typedef struct { const char *upscli_strerror(UPSCONN_t *ups); int upscli_init(int certverify, const char *certpath, const char *certname, const char *certpasswd); -int upscli_cleanup(); +int upscli_cleanup(void); int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags, struct timeval *tv); int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags); @@ -86,10 +86,10 @@ int upscli_list_start(UPSCONN_t *ups, unsigned int numq, const char **query); int upscli_list_next(UPSCONN_t *ups, unsigned int numq, const char **query, unsigned int *numa, char ***answer); -int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, int timeout); +int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, unsigned int timeout); int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); -int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, int timeout); +int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, unsigned int timeout); int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); int upscli_splitname(const char *buf, char **upsname, char **hostname, diff --git a/docs/man/upscli_cleanup.txt b/docs/man/upscli_cleanup.txt index cd88a518e1..611c3de051 100644 --- a/docs/man/upscli_cleanup.txt +++ b/docs/man/upscli_cleanup.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - int upscli_cleanup(); + int upscli_cleanup(void); DESCRIPTION ----------- From deb2279603f4226a454e08a381959287a5c0388b Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 00:49:52 +0100 Subject: [PATCH 26/45] upscmd/upsrw: use unsigned int for timeout + our str_to_uint() for it Also, slightly reword the help message for `-t` (timeout) option, in order to clarify which is the unit (seconds) used for the provided value. --- clients/upscmd.c | 7 ++++--- clients/upsrw.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index f5fbb341aa..144a23bdd8 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -33,7 +33,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; static int status_info = 0; -static int timeout = 10; +static unsigned int timeout = 10; struct list_t { char *name; @@ -54,7 +54,7 @@ static void usage(const char *prog) printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of command by the driver\n"); printf(" and return its actual result from the device\n"); - printf(" -t set a timeout when using -w (default \"10\" seconds)\n"); + printf(" -t set a timeout when using -w (in seconds, default \"10\")\n"); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf(" Valid instant command - test.panel.start, etc.\n"); @@ -246,7 +246,8 @@ int main(int argc, char **argv) break; case 't': - timeout = atoi(optarg); + if (!str_to_uint(optarg, &timeout, 10)) + fatal_with_errno(EXIT_FAILURE, "Could not convert the provided value for timeout ('-t' option) to unsigned int"); break; case 'w': diff --git a/clients/upsrw.c b/clients/upsrw.c index e2f9433124..aed13788ef 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -33,7 +33,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; static int status_info = 0; -static int timeout = 10; +static unsigned int timeout = 10; struct list_t { char *name; @@ -54,7 +54,7 @@ static void usage(const char *prog) printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of setting by the driver\n"); printf(" and return its actual result from the device\n"); - printf(" -t set a timeout when using -w (default \"10\" seconds)\n"); + printf(" -t set a timeout when using -w (in seconds, default \"10\")\n"); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf("\n"); @@ -595,7 +595,8 @@ int main(int argc, char **argv) password = optarg; break; case 't': - timeout = atoi(optarg); + if (!str_to_uint(optarg, &timeout, 10)) + fatal_with_errno(EXIT_FAILURE, "Could not convert the provided value for timeout ('-t' option) to unsigned int"); break; case 'u': username = optarg; From f688d2c3b09e13dfa31780a5d8137df10cd18d23 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 00:55:33 +0100 Subject: [PATCH 27/45] upscmd/upsrw: don't sleep after receiving a non-PENDING CMDSET_STATUS Also, remove some nesting in do_cmd() and do_set(). --- clients/upscmd.c | 55 ++++++++++++++++++++++++------------------------ clients/upsrw.c | 55 ++++++++++++++++++++++++------------------------ 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 144a23bdd8..cc500884cb 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -169,43 +169,44 @@ static void do_cmd(char **argv, const int argc) fatalx(EXIT_FAILURE, "Unexpected response from upsd: %s", buf); } - time(&start); - /* check for status tracking id */ - if (status_info) { + if ( + !status_info || /* sanity check on the size: "OK " + UUID4_LEN */ - if (strlen(buf) == UUID4_LEN + 2) { - snprintf(status_id, sizeof(status_id), "%s", buf+3); + strlen(buf) != UUID4_LEN + 2 + ) { + /* reply as usual */ + fprintf(stderr, "%s\n", buf); + return; + } - /* send status tracking request, looping if status is PENDING */ - while (!cmd_complete) { + snprintf(status_id, sizeof(status_id), "%s", buf + 3); + time(&start); - /* check for timeout */ - time(&now); - if (difftime(now, start) >= timeout) { - fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); - } + /* send status tracking request, looping if status is PENDING */ + while (!cmd_complete) { - snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); + /* check for timeout */ + time(&now); + if (difftime(now, start) >= timeout) + fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); - if (upscli_sendline(ups, buf, strlen(buf)) < 0) { - fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); - } + snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); - /* and get status tracking reply */ - if (upscli_readline_timeout(ups, buf, sizeof(buf), timeout) < 0) { - fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); - } + if (upscli_sendline(ups, buf, strlen(buf)) < 0) + fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); - if (strncmp(buf, "PENDING", 7) != 0) - cmd_complete = 1; + /* and get status tracking reply */ + if (upscli_readline_timeout(ups, buf, sizeof(buf), timeout) < 0) + fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); - /* wait a second before retrying */ - sleep (1); - } - } + if (strncmp(buf, "PENDING", 7)) + cmd_complete = 1; + else + /* wait a second before retrying */ + sleep(1); } - /* reply as usual */ + fprintf(stderr, "%s\n", buf); } diff --git a/clients/upsrw.c b/clients/upsrw.c index aed13788ef..018ff00e64 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -94,43 +94,44 @@ static void do_set(const char *varname, const char *newval) fatalx(EXIT_FAILURE, "Unexpected response from upsd: %s", buf); } - time(&start); - /* check for status tracking id */ - if (status_info) { + if ( + !status_info || /* sanity check on the size: "OK " + UUID4_LEN */ - if (strlen(buf) == UUID4_LEN + 2) { - snprintf(status_id, sizeof(status_id), "%s", buf+3); + strlen(buf) != UUID4_LEN + 2 + ) { + /* reply as usual */ + fprintf(stderr, "%s\n", buf); + return; + } - /* send status tracking request, looping if status is PENDING */ - while (!cmd_complete) { + snprintf(status_id, sizeof(status_id), "%s", buf + 3); + time(&start); - /* check for timeout */ - time(&now); - if (difftime(now, start) >= timeout) { - fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); - } + /* send status tracking request, looping if status is PENDING */ + while (!cmd_complete) { - snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); + /* check for timeout */ + time(&now); + if (difftime(now, start) >= timeout) + fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); - if (upscli_sendline(ups, buf, strlen(buf)) < 0) { - fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); - } + snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); - /* and get status tracking reply */ - if (upscli_readline_timeout(ups, buf, sizeof(buf), timeout) < 0) { - fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); - } + if (upscli_sendline(ups, buf, strlen(buf)) < 0) + fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); - if (strncmp(buf, "PENDING", 7) != 0) - cmd_complete = 1; + /* and get status tracking reply */ + if (upscli_readline_timeout(ups, buf, sizeof(buf), timeout) < 0) + fatalx(EXIT_FAILURE, "Can't receive status tracking information: %s", upscli_strerror(ups)); - /* wait a second before retrying */ - sleep (1); - } - } + if (strncmp(buf, "PENDING", 7)) + cmd_complete = 1; + else + /* wait a second before retrying */ + sleep(1); } - /* reply as usual */ + fprintf(stderr, "%s\n", buf); } From 5da7391a816ddaa9dcbb845c82ab4593864cec52 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 00:58:00 +0100 Subject: [PATCH 28/45] net-protocol: clarify the format of GET CMDSET_STATUS + is not optional to get the status of a command/setvar with CMDSET_STATUS, so drop the [square brackets] from it. Also drop "quotes" in SET CMDSET_STATUS's , since it's expected to be a single word. Plus, fix markup of INSTMCD's parameter. --- docs/net-protocol.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 48542e72eb..148ebc9561 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -196,8 +196,8 @@ CMDSET_STATUS Form: - GET CMDSET_STATUS (activation status of CMDSET_STATUS) - GET CMDSET_STATUS [] (execution status of a command / setvar) + GET CMDSET_STATUS (activation status of CMDSET_STATUS) + GET CMDSET_STATUS (execution status of a command / setvar) GET CMDSET_STATUS 1bd31808-cb49-4aec-9d75-d056e6f018d2 Response: @@ -419,14 +419,14 @@ CMDSET_STATUS Form: - SET CMDSET_STATUS "" + SET CMDSET_STATUS SET CMDSET_STATUS ON SET CMDSET_STATUS OFF Response: OK - ERR INVALID-ARGUMENT (if "value" is not "ON" or "OFF") + ERR INVALID-ARGUMENT (if is not "ON" or "OFF") ERR USERNAME-REQUIRED (if not yet authenticated) ERR PASSWORD-REQUIRED (if not yet authenticated) @@ -436,11 +436,11 @@ INSTCMD Form: - INSTCMD [cmdparam] + INSTCMD [] INSTCMD su700 test.panel.start INSTCMD su700 load.off.delay 120 -NOTE: cmdparam is an additional and optional parameter for the command. +NOTE: is an additional and optional parameter for the command. Response: From 8d5a0359c3c7c1f6e3e4f2308e69c3cdfbf708a0 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 01:00:17 +0100 Subject: [PATCH 29/45] sock-protocol: align case and markup of command parameters --- docs/sock-protocol.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/sock-protocol.txt b/docs/sock-protocol.txt index 0961b614e7..b56a9a4955 100644 --- a/docs/sock-protocol.txt +++ b/docs/sock-protocol.txt @@ -149,7 +149,7 @@ may be repeated. It is cleared by DATAOK. CMDSET_STATUS ~~~~~~~~~~~~~ - CMDSET_STATUS + CMDSET_STATUS This is sent in response to an INSTCMD or SET VAR that includes a STATUS_ID, upon completion of request execution by the driver. is the integer @@ -178,28 +178,28 @@ server must not be passed on to the clients when this happens. INSTCMD ~~~~~~~ - INSTCMD [cmdparam] [STATUS_ID ] + INSTCMD [] [STATUS_ID ] INSTCMD panel.test.start INSTCMD load.off 10 INSTCMD load.on 10 STATUS_ID 1bd31808-cb49-4aec-9d75-d056e6f018d2 NOTE: -* cmdparam is an additional and optional parameter for the command, -* "STATUS_ID " can be provided to track commands execution status, if +* is an additional and optional parameter for the command, +* "STATUS_ID " can be provided to track commands execution status, if CMDSET_STATUS was set to ON on upsd. In this case, driver will later return the execution status, using CMDSET_STATUS. SET ~~~ - SET "" [STATUS_ID ] + SET "" [STATUS_ID ] SET ups.id "Data room" SET ups.id "Data room" STATUS_ID 2dedb58a-3b91-4fab-831f-c8af4b90760a NOTE: -* "STATUS_ID " can be provided to track commands execution status, if +* "STATUS_ID " can be provided to track commands execution status, if CMDSET_STATUS was set to ON on upsd. In this case, driver will later return the execution status, using CMDSET_STATUS. From 87f59e7c009efe0fc2da9fba33d11ab31a61ffa8 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 01:06:51 +0100 Subject: [PATCH 30/45] dstate: fix handling of INSTCMD's optional parameters We should still support the old `INSTCMD []` format, and not only consider when also `STATUS_ID ` is provided. Also, fix the format of our sock-protocol commands mentioned in comments, and add function names to debug info. --- drivers/dstate.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 49e4509f88..7980eabd50 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -401,29 +401,29 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* INSTCMD [cmdparam] [STATUS_ID ] */ + /* INSTCMD [] [STATUS_ID ] */ if (!strcasecmp(arg[0], "INSTCMD")) { int ret; char *cmdname = arg[1]; char *cmdparam = NULL; char *cmdid = NULL; - /* Check if STATUS_ID was provided */ - if (numarg >= 4) { - if (!strcasecmp(arg[2], "STATUS_ID")) { - cmdid = arg[3]; - } - else if (!strcasecmp(arg[3], "STATUS_ID")) { - cmdparam = arg[2]; - cmdid = arg[4]; - } - else { - upslogx(LOG_NOTICE, "Malformed INSTCMD request"); - return 0; - } - upsdebugx(3, "STATUS_ID = %s", cmdid); + /* Check if and STATUS_ID were provided */ + if (numarg == 3) { + cmdparam = arg[2]; + } else if (numarg == 4 && !strcasecmp(arg[2], "STATUS_ID")) { + cmdid = arg[3]; + } else if (numarg == 5 && !strcasecmp(arg[3], "STATUS_ID")) { + cmdparam = arg[2]; + cmdid = arg[4]; + } else { + upslogx(LOG_NOTICE, "Malformed INSTCMD request"); + return 0; } + if (cmdid) + upsdebugx(3, "%s: STATUS_ID = %s", __func__, cmdid); + /* try the new handler first if present */ if (upsh.instcmd) { ret = upsh.instcmd(cmdname, cmdparam); @@ -443,7 +443,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* SET [STATUS_ID ] */ + /* SET [STATUS_ID ] */ if (!strcasecmp(arg[0], "SET")) { int ret; char *cmdid = NULL; @@ -458,7 +458,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) arg[3], arg[4]); return 0; } - upsdebugx(3, "STATUS_ID = %s", cmdid); + upsdebugx(3, "%s: STATUS_ID = %s", __func__, cmdid); } /* try the new handler first if present */ From a619455687f98d3dc2b2c83628ca4fa14cf39076 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 01:08:16 +0100 Subject: [PATCH 31/45] common: document the recently added things --- include/common.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/common.h b/include/common.h index c7db6ac011..43aaf40879 100644 --- a/include/common.h +++ b/include/common.h @@ -53,7 +53,7 @@ extern "C" { extern const char *UPS_VERSION; -/* Used by upsclient and nut-scanner */ +/** @brief Default timeout (in seconds), as used by upsclient and nut-scanner. */ #define DEFAULT_TIMEOUT 5 /* get the syslog ready for us */ @@ -131,6 +131,7 @@ char * get_libname(const char* base_libname); #define SMALLBUF 512 #define LARGEBUF 1024 +/** @brief (Minimum) Size that a string must have to hold a UUID4 (i.e. UUID4 length + the terminating null character). */ #define UUID4_LEN 37 /* Provide declarations for getopt() global variables */ From 69b66e67b22aaa9e66bd12948b7fa8a2b3ca5c7d Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 01:11:00 +0100 Subject: [PATCH 32/45] upsd: in INSTCMD/SET handlers, also accept NULL for status_id Plus: - use a simpler approach to test if status_id is not empty, - align the way the SET command is built to the one used for the INSTCMD command. --- server/netinstcmd.c | 2 +- server/netset.c | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/server/netinstcmd.c b/server/netinstcmd.c index 73254e15a6..f32d9c58e0 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -78,7 +78,7 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, snprintfcat(sockcmd, sizeof(sockcmd), " %s", pconf_encode(value, esc, sizeof(esc))); /* see if the user want execution tracking for this command */ - if (strcmp(status_id, "") != 0) { + if (status_id && *status_id) { snprintfcat(sockcmd, sizeof(sockcmd), " STATUS_ID %s", status_id); /* Add an entry in the tracking structure */ cmdset_status_add(status_id); diff --git a/server/netset.c b/server/netset.c index fcc969dc33..3e5eba7834 100644 --- a/server/netset.c +++ b/server/netset.c @@ -135,18 +135,19 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, /* must be OK now */ + snprintf(cmd, sizeof(cmd), "SET %s \"%s\"", + var, pconf_encode(newval, esc, sizeof(esc))); + /* see if the user want execution tracking for this command */ - if (strcmp(status_id, "") != 0) { - snprintf(cmd, sizeof(cmd), "SET %s \"%s\" STATUS_ID %s\n", - var, pconf_encode(newval, esc, sizeof(esc)), status_id); + if (status_id && *status_id) { + snprintfcat(cmd, sizeof(cmd), " STATUS_ID %s", status_id); /* Add an entry in the tracking structure */ cmdset_status_add(status_id); have_status_id = 1; } - else { - snprintf(cmd, sizeof(cmd), "SET %s \"%s\"\n", - var, pconf_encode(newval, esc, sizeof(esc))); - } + + /* add EOL */ + snprintfcat(cmd, sizeof(cmd), "\n"); upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s (tracking ID: %s)", client->username, client->addr, var, ups->name, newval, From 57de34fdeb17a0ac45e468db874db99cdd527d55 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 01:13:41 +0100 Subject: [PATCH 33/45] upsd: drop unnecessary/unused global --- server/upsd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/server/upsd.c b/server/upsd.c index e7181fa940..48d15ae2ae 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -110,7 +110,6 @@ static int reload_flag = 0, exit_flag = 0; /* Minimalistic support for UUID v4 */ /* Ref: RFC 4122 https://tools.ietf.org/html/rfc4122#section-4.1.2 */ #define UUID4_BYTESIZE 16 -uint8_t nut_uuid[UUID4_BYTESIZE]; static const char *inet_ntopW (struct sockaddr_storage *s) From e14c7398199b1b15da58b9359ce611abf1e7ce0a Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 01:14:28 +0100 Subject: [PATCH 34/45] upsd: move sanity checks of cmdset_status_get() after declaration of vars Also, drop some nesting in that function. --- server/upsd.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index 48d15ae2ae..b8e55902fb 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -823,29 +823,31 @@ void cmdset_status_cleanup(void) /* get status of a specific tracking entry */ char *cmdset_status_get(const char *id) { + cmdset_status_t *cmdset_status, *cmdset_status_next; + /* sanity checks */ if ((!cmdset_status_list) || (!id)) return "ERR UNKNOWN"; - cmdset_status_t *cmdset_status, *cmdset_status_next; - for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { cmdset_status_next = cmdset_status->next; - if (!strcmp(cmdset_status->id, id)) { - switch (cmdset_status->status) { - case STAT_PENDING: - return "PENDING"; - case STAT_HANDLED: - return "SUCCESS"; - case STAT_UNKNOWN: - return "ERR UNKNOWN"; - case STAT_INVALID: - return "ERR INVALID-ARGUMENT"; - case STAT_FAILED: - return "ERR FAILED"; - } + if (strcmp(cmdset_status->id, id)) + continue; + + switch (cmdset_status->status) + { + case STAT_PENDING: + return "PENDING"; + case STAT_HANDLED: + return "SUCCESS"; + case STAT_UNKNOWN: + return "ERR UNKNOWN"; + case STAT_INVALID: + return "ERR INVALID-ARGUMENT"; + case STAT_FAILED: + return "ERR FAILED"; } } From 0fdc984b503383f9224136da1a023ed4f14ad116 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Mon, 11 Feb 2019 05:45:18 +0100 Subject: [PATCH 35/45] dstate: *really* fix handling of INSTCMD's optional parameters Erroring out on `INSTCMD ` doesn't seem like a good idea... --- drivers/dstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 7980eabd50..6e89d3a69b 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -416,7 +416,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) } else if (numarg == 5 && !strcasecmp(arg[3], "STATUS_ID")) { cmdparam = arg[2]; cmdid = arg[4]; - } else { + } else if (numarg != 2) { upslogx(LOG_NOTICE, "Malformed INSTCMD request"); return 0; } From 496d2c66953becc0d4934e715e0624046adb3fa1 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 23:11:25 +0100 Subject: [PATCH 36/45] Move from CMDSET_STATUS / STATUS_ID to TRACKING This is just a big, big rename, no code changes. --- clients/upscmd.c | 16 ++-- clients/upsrw.c | 16 ++-- conf/upsd.conf.sample | 4 +- docs/man/upscmd.txt | 2 +- docs/man/upsd.conf.txt | 2 +- docs/man/upsrw.txt | 2 +- docs/net-protocol.txt | 34 ++++----- docs/nut.dict | 5 +- docs/sock-protocol.txt | 30 ++++---- drivers/dstate.c | 32 ++++---- scripts/augeas/nutupsdconf.aug.in | 6 +- scripts/augeas/tests/test_nut.aug | 4 +- server/conf.c | 6 +- server/netget.c | 10 +-- server/netinstcmd.c | 28 +++---- server/netset.c | 48 ++++++------ server/nut_ctype.h | 2 +- server/sstate.c | 10 +-- server/upsd.c | 122 +++++++++++++++--------------- server/upsd.h | 28 +++---- 20 files changed, 203 insertions(+), 204 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index cc500884cb..55fcea8c36 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -32,7 +32,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; -static int status_info = 0; +static int tracking_enabled = 0; static unsigned int timeout = 10; struct list_t { @@ -147,7 +147,7 @@ static void do_cmd(char **argv, const int argc) { int cmd_complete = 0; char buf[SMALLBUF]; - char status_id[UUID4_LEN]; + char tracking_id[UUID4_LEN]; time_t start, now; if (argc > 1) { @@ -171,7 +171,7 @@ static void do_cmd(char **argv, const int argc) /* check for status tracking id */ if ( - !status_info || + !tracking_enabled || /* sanity check on the size: "OK " + UUID4_LEN */ strlen(buf) != UUID4_LEN + 2 ) { @@ -180,7 +180,7 @@ static void do_cmd(char **argv, const int argc) return; } - snprintf(status_id, sizeof(status_id), "%s", buf + 3); + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + 3); time(&start); /* send status tracking request, looping if status is PENDING */ @@ -191,7 +191,7 @@ static void do_cmd(char **argv, const int argc) if (difftime(now, start) >= timeout) fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); - snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); + snprintf(buf, sizeof(buf), "GET TRACKING %s\n", tracking_id); if (upscli_sendline(ups, buf, strlen(buf)) < 0) fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); @@ -252,7 +252,7 @@ int main(int argc, char **argv) break; case 'w': - status_info = 1; + tracking_enabled = 1; break; case 'V': @@ -371,9 +371,9 @@ int main(int argc, char **argv) } /* enable status tracking ID */ - if (status_info) { + if (tracking_enabled) { - snprintf(buf, sizeof(buf), "SET CMDSET_STATUS ON\n"); + snprintf(buf, sizeof(buf), "SET TRACKING ON\n"); if (upscli_sendline(ups, buf, strlen(buf)) < 0) { fatalx(EXIT_FAILURE, "Can't enable command status tracking: %s", upscli_strerror(ups)); diff --git a/clients/upsrw.c b/clients/upsrw.c index 018ff00e64..63d70ef532 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -32,7 +32,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; -static int status_info = 0; +static int tracking_enabled = 0; static unsigned int timeout = 10; struct list_t { @@ -76,7 +76,7 @@ static void do_set(const char *varname, const char *newval) { int cmd_complete = 0; char buf[SMALLBUF], enc[SMALLBUF]; - char status_id[UUID4_LEN]; + char tracking_id[UUID4_LEN]; time_t start, now; snprintf(buf, sizeof(buf), "SET VAR %s %s \"%s\"\n", upsname, varname, pconf_encode(newval, enc, sizeof(enc))); @@ -96,7 +96,7 @@ static void do_set(const char *varname, const char *newval) /* check for status tracking id */ if ( - !status_info || + !tracking_enabled || /* sanity check on the size: "OK " + UUID4_LEN */ strlen(buf) != UUID4_LEN + 2 ) { @@ -105,7 +105,7 @@ static void do_set(const char *varname, const char *newval) return; } - snprintf(status_id, sizeof(status_id), "%s", buf + 3); + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + 3); time(&start); /* send status tracking request, looping if status is PENDING */ @@ -116,7 +116,7 @@ static void do_set(const char *varname, const char *newval) if (difftime(now, start) >= timeout) fatalx(EXIT_FAILURE, "Can't receive status tracking information: timeout"); - snprintf(buf, sizeof(buf), "GET CMDSET_STATUS %s\n", status_id); + snprintf(buf, sizeof(buf), "GET TRACKING %s\n", tracking_id); if (upscli_sendline(ups, buf, strlen(buf)) < 0) fatalx(EXIT_FAILURE, "Can't send status tracking request: %s", upscli_strerror(ups)); @@ -227,9 +227,9 @@ static void do_setvar(const char *varname, char *uin, const char *pass) } /* enable status tracking ID */ - if (status_info) { + if (tracking_enabled) { - snprintf(temp, sizeof(temp), "SET CMDSET_STATUS ON\n"); + snprintf(temp, sizeof(temp), "SET TRACKING ON\n"); if (upscli_sendline(ups, temp, strlen(temp)) < 0) { fatalx(EXIT_FAILURE, "Can't enable set variable status tracking: %s", upscli_strerror(ups)); @@ -603,7 +603,7 @@ int main(int argc, char **argv) username = optarg; break; case 'w': - status_info = 1; + tracking_enabled = 1; break; case 'V': printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index dbf0ad6cb6..5cb94062ce 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -22,8 +22,8 @@ # for notifications from upsd about staleness. # ======================================================================= -# CMDSETSTATUSDELAY -# CMDSETSTATUSDELAY 3600 +# TRACKINGDELAY +# TRACKINGDELAY 3600 # # This defaults to 1 hour. When instant commands and variables setting status # tracking is enabled, status execution information are kept during this diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index 7bf2aee76d..78c4d0e7f5 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -45,7 +45,7 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of command execution by the driver and return its actual result from the device. Note that this feature requires that upsd -supports CMDSET_STATUS (NUT version 2.7.5 or higher) or it will otherwise fail. +supports TRACKING (NUT version 2.7.5 or higher) or it will otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index 7af58b0c79..5947792f20 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -26,7 +26,7 @@ to make upsd wait longer. + Most users should leave this at the default value. -"CMDSETSTATUSDELAY 'seconds'":: +"TRACKINGDELAY 'seconds'":: When instant commands and variables setting status tracking is enabled, status execution information are kept during this amount of time, and then cleaned up. diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index cb9c3c9c4d..8271dad182 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -61,7 +61,7 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of setting execution by the driver and return its actual result from the device. Note that this feature requires that upsd -supports CMDSET_STATUS (NUT version 2.7.5 or higher) or it will otherwise fail. +supports TRACKING (NUT version 2.7.5 or higher) or it will otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 148ebc9561..bf8adf21ab 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -45,7 +45,7 @@ NUT network protocol, over the time: .2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands |Add ranges of values for writable variables .2+|1.3 .2+|>= 2.7.5 |Add "cmdparam" to "INSTCMD" - |Add "CMDSET_STATUS" commands (GET, SET) + |Add "TRACKING" commands (GET, SET) |=============================================================================== NOTE: any new version of the protocol implies an update of NUT_NETVERSION @@ -191,19 +191,19 @@ This is like DESC above, but it applies to the instant commands. This replaces the old "INSTCMDDESC" command. -CMDSET_STATUS -~~~~~~~~~~~~~ +TRACKING +~~~~~~~~ Form: - GET CMDSET_STATUS (activation status of CMDSET_STATUS) - GET CMDSET_STATUS (execution status of a command / setvar) - GET CMDSET_STATUS 1bd31808-cb49-4aec-9d75-d056e6f018d2 + GET TRACKING (activation status of TRACKING) + GET TRACKING (execution status of a command / setvar) + GET TRACKING 1bd31808-cb49-4aec-9d75-d056e6f018d2 Response: - ON (CMDSET_STATUS feature is enabled) - OFF (CMDSET_STATUS feature is disabled) + ON (TRACKING feature is enabled) + OFF (TRACKING feature is disabled) PENDING (command execution is pending) SUCCESS (command was successfully executed) ERR UNKNOWN (command execution failed with unknown error) @@ -409,19 +409,19 @@ Form: Response: - OK (if CMDSET_STATUS is not enabled) - OK (if CMDSET_STATUS is enabled) + OK (if TRACKING is not enabled) + OK (if TRACKING is enabled) ERR [...] (see Error responses) -CMDSET_STATUS -~~~~~~~~~~~~~ +TRACKING +~~~~~~~~ Form: - SET CMDSET_STATUS - SET CMDSET_STATUS ON - SET CMDSET_STATUS OFF + SET TRACKING + SET TRACKING ON + SET TRACKING OFF Response: @@ -444,8 +444,8 @@ NOTE: is an additional and optional parameter for the command. Response: - OK (if CMDSET_STATUS is not enabled) - OK (if CMDSET_STATUS is enabled) + OK (if TRACKING is not enabled) + OK (if TRACKING is enabled) ERR [...] (see Error responses) diff --git a/docs/nut.dict b/docs/nut.dict index c8a6660e22..e2d886829e 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2439 utf-8 +personal_ws-1.1 en 2438 utf-8 AAS ACFAIL ACFREQ @@ -151,8 +151,6 @@ CLI CLOCAL CMDDESC CMDSCRIPT -CMDSET -CMDSETSTATUSDELAY CN COMLI COMMBAD @@ -1004,6 +1002,7 @@ TIOCMBIC TIOCMBIS TLS TODO +TRACKINGDELAY TRYSSL TSR TST diff --git a/docs/sock-protocol.txt b/docs/sock-protocol.txt index b56a9a4955..f951aae6d0 100644 --- a/docs/sock-protocol.txt +++ b/docs/sock-protocol.txt @@ -146,16 +146,16 @@ status information once this has been sent. This will be sent in the beginning of a dump if the data is stale, and may be repeated. It is cleared by DATAOK. -CMDSET_STATUS -~~~~~~~~~~~~~ +TRACKING +~~~~~~~~ - CMDSET_STATUS + TRACKING -This is sent in response to an INSTCMD or SET VAR that includes a STATUS_ID, +This is sent in response to an INSTCMD or SET VAR that includes a TRACKING, upon completion of request execution by the driver. is the integer return value from the driver handlers instcmd and setvar (see drivers/upshandler.h). The server is in charge of translating these codes into -strings, as per docs/net-protocol.txt GET CMDSET_STATUS. +strings, as per docs/net-protocol.txt GET TRACKING. Commands sent by the server @@ -178,30 +178,30 @@ server must not be passed on to the clients when this happens. INSTCMD ~~~~~~~ - INSTCMD [] [STATUS_ID ] + INSTCMD [] [TRACKING ] INSTCMD panel.test.start INSTCMD load.off 10 - INSTCMD load.on 10 STATUS_ID 1bd31808-cb49-4aec-9d75-d056e6f018d2 + INSTCMD load.on 10 TRACKING 1bd31808-cb49-4aec-9d75-d056e6f018d2 NOTE: * is an additional and optional parameter for the command, -* "STATUS_ID " can be provided to track commands execution status, if -CMDSET_STATUS was set to ON on upsd. In this case, driver will later return -the execution status, using CMDSET_STATUS. +* "TRACKING " can be provided to track commands execution status, if +TRACKING was set to ON on upsd. In this case, driver will later return +the execution status, using TRACKING. SET ~~~ - SET "" [STATUS_ID ] + SET "" [TRACKING ] SET ups.id "Data room" - SET ups.id "Data room" STATUS_ID 2dedb58a-3b91-4fab-831f-c8af4b90760a + SET ups.id "Data room" TRACKING 2dedb58a-3b91-4fab-831f-c8af4b90760a NOTE: -* "STATUS_ID " can be provided to track commands execution status, if -CMDSET_STATUS was set to ON on upsd. In this case, driver will later return -the execution status, using CMDSET_STATUS. +* "TRACKING " can be provided to track commands execution status, if +TRACKING was set to ON on upsd. In this case, driver will later return +the execution status, using TRACKING. DUMPALL ~~~~~~~ diff --git a/drivers/dstate.c b/drivers/dstate.c index 6e89d3a69b..51e41a052a 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -358,9 +358,9 @@ static int cmd_dump_conn(conn_t *conn) } -static void send_cmdset_status(conn_t *conn, const char *id, int value) +static void send_tracking(conn_t *conn, const char *id, int value) { - send_to_one(conn, "CMDSET_STATUS %s %i\n", id, value); + send_to_one(conn, "TRACKING %s %i\n", id, value); } static int sock_arg(conn_t *conn, int numarg, char **arg) @@ -401,19 +401,19 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* INSTCMD [] [STATUS_ID ] */ + /* INSTCMD [] [TRACKING ] */ if (!strcasecmp(arg[0], "INSTCMD")) { int ret; char *cmdname = arg[1]; char *cmdparam = NULL; char *cmdid = NULL; - /* Check if and STATUS_ID were provided */ + /* Check if and TRACKING were provided */ if (numarg == 3) { cmdparam = arg[2]; - } else if (numarg == 4 && !strcasecmp(arg[2], "STATUS_ID")) { + } else if (numarg == 4 && !strcasecmp(arg[2], "TRACKING")) { cmdid = arg[3]; - } else if (numarg == 5 && !strcasecmp(arg[3], "STATUS_ID")) { + } else if (numarg == 5 && !strcasecmp(arg[3], "TRACKING")) { cmdparam = arg[2]; cmdid = arg[4]; } else if (numarg != 2) { @@ -422,7 +422,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) } if (cmdid) - upsdebugx(3, "%s: STATUS_ID = %s", __func__, cmdid); + upsdebugx(3, "%s: TRACKING = %s", __func__, cmdid); /* try the new handler first if present */ if (upsh.instcmd) { @@ -430,7 +430,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) /* send back execution result */ if (cmdid) - send_cmdset_status(conn, cmdid, ret); + send_tracking(conn, cmdid, ret); /* The command was handled, status is a separate consideration */ return 1; @@ -443,22 +443,22 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* SET [STATUS_ID ] */ + /* SET [TRACKING ] */ if (!strcasecmp(arg[0], "SET")) { int ret; - char *cmdid = NULL; + char *setid = NULL; - /* Check if STATUS_ID was provided */ + /* Check if TRACKING was provided */ if (numarg == 5) { - if (!strcasecmp(arg[3], "STATUS_ID")) { - cmdid = arg[4]; + if (!strcasecmp(arg[3], "TRACKING")) { + setid = arg[4]; } else { upslogx(LOG_NOTICE, "Got SET with unsupported parameters (%s/%s)", arg[3], arg[4]); return 0; } - upsdebugx(3, "%s: STATUS_ID = %s", __func__, cmdid); + upsdebugx(3, "%s: TRACKING = %s", __func__, setid); } /* try the new handler first if present */ @@ -466,8 +466,8 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) ret = upsh.setvar(arg[1], arg[2]); /* send back execution result */ - if (cmdid) - send_cmdset_status(conn, cmdid, ret); + if (setid) + send_tracking(conn, setid, ret); /* The command was handled, status is a separate consideration */ return 1; diff --git a/scripts/augeas/nutupsdconf.aug.in b/scripts/augeas/nutupsdconf.aug.in index 4a5c2ff65d..4c200039e8 100644 --- a/scripts/augeas/nutupsdconf.aug.in +++ b/scripts/augeas/nutupsdconf.aug.in @@ -39,7 +39,7 @@ let comment = Util.comment let path = word let upsd_maxage = [ opt_spc . key "MAXAGE" . sep_spc . store num . eol ] -let upsd_cmdsetstatusdelay = [ opt_spc . key "CMDSETSTATUSDELAY" . sep_spc . store num . eol ] +let upsd_trackingdelay = [ opt_spc . key "TRACKINGDELAY" . sep_spc . store num . eol ] let upsd_statepath = [ opt_spc . key "STATEPATH" . sep_spc . store path . eol ] let upsd_listen = [ opt_spc . key "LISTEN" . sep_spc . [ label "interface" . store ip ] @@ -50,14 +50,14 @@ let upsd_certfile = [ opt_spc . key "CERTFILE" . sep_spc . store path . eol ] (************************************************************************ * MAXAGE seconds - * CMDSETSTATUSDELAY seconds + * TRACKINGDELAY seconds * STATEPATH path * LISTEN interface port * Multiple LISTEN addresses may be specified. The default is to bind to 0.0.0.0 if no LISTEN addresses are specified. * LISTEN 127.0.0.1 LISTEN 192.168.50.1 LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 * *************************************************************************) -let upsd_other = upsd_maxage | upsd_cmdsetstatusdelay | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile +let upsd_other = upsd_maxage | upsd_trackingdelay | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile let upsd_lns = (upsd_other|comment|empty)* diff --git a/scripts/augeas/tests/test_nut.aug b/scripts/augeas/tests/test_nut.aug index f5ad7d3fc1..eb79b07bd3 100644 --- a/scripts/augeas/tests/test_nut.aug +++ b/scripts/augeas/tests/test_nut.aug @@ -27,7 +27,7 @@ test NutUpsConf.ups_lns get ups_conf = let upsd_conf = " MAXAGE 30 -CMDSETSTATUSDELAY 600 +TRACKINGDELAY 600 LISTEN 0.0.0.0 3493 MAXCONN 1024 " @@ -35,7 +35,7 @@ MAXCONN 1024 test NutUpsdConf.upsd_lns get upsd_conf = { } { "MAXAGE" = "30" } - { "CMDSETSTATUSDELAY" = "600" } + { "TRACKINGDELAY" = "600" } { "LISTEN" { "interface" = "0.0.0.0" } { "port" = "3493" } } diff --git a/server/conf.c b/server/conf.c index c62a372517..75723d141b 100644 --- a/server/conf.c +++ b/server/conf.c @@ -125,9 +125,9 @@ static int parse_upsd_conf_args(int numargs, char **arg) return 1; } - /* CMDSETSTATUSDELAY */ - if (!strcmp(arg[0], "CMDSETSTATUSDELAY")) { - cmdset_status_delay = atoi(arg[1]); + /* TRACKINGDELAY */ + if (!strcmp(arg[0], "TRACKINGDELAY")) { + tracking_delay = atoi(arg[1]); return 1; } diff --git a/server/netget.c b/server/netget.c index cccbb1fdcb..55a27a429c 100644 --- a/server/netget.c +++ b/server/netget.c @@ -221,14 +221,14 @@ void net_get(nut_ctype_t *client, int numarg, const char **arg) return; } - /* GET CMDSET_STATUS [STATUS_ID] */ - if (!strcasecmp(arg[0], "CMDSET_STATUS")) { + /* GET TRACKING [ID] */ + if (!strcasecmp(arg[0], "TRACKING")) { if (numarg < 2) { - sendback(client, "%s\n", (client->cmdset_status_enabled)?"ON":"OFF"); + sendback(client, "%s\n", (client->tracking) ? "ON" : "OFF"); } else { - if (client->cmdset_status_enabled) - sendback(client, "%s\n", cmdset_status_get(arg[1])); + if (client->tracking) + sendback(client, "%s\n", tracking_get(arg[1])); else send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); } diff --git a/server/netinstcmd.c b/server/netinstcmd.c index f32d9c58e0..27c77d1a46 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -29,9 +29,9 @@ #include "netinstcmd.h" static void send_instcmd(nut_ctype_t *client, const char *upsname, - const char *cmdname, const char *value, const char *status_id) + const char *cmdname, const char *value, const char *tracking_id) { - int found, have_status_id = 0; + int found, have_tracking_id = 0; upstype_t *ups; const cmdlist_t *ctmp; char sockcmd[SMALLBUF], esc[SMALLBUF]; @@ -78,11 +78,11 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, snprintfcat(sockcmd, sizeof(sockcmd), " %s", pconf_encode(value, esc, sizeof(esc))); /* see if the user want execution tracking for this command */ - if (status_id && *status_id) { - snprintfcat(sockcmd, sizeof(sockcmd), " STATUS_ID %s", status_id); + if (tracking_id && *tracking_id) { + snprintfcat(sockcmd, sizeof(sockcmd), " TRACKING %s", tracking_id); /* Add an entry in the tracking structure */ - cmdset_status_add(status_id); - have_status_id = 1; + tracking_add(tracking_id); + have_tracking_id = 1; } /* add EOL */ @@ -93,7 +93,7 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, (value != NULL)?" with value ":"", (value != NULL)?value:"", ups->name, - (have_status_id)?status_id:"disabled"); + (have_tracking_id) ? tracking_id : "disabled"); if (!sstate_sendline(ups, sockcmd)) { upslogx(LOG_INFO, "Set command send failed"); @@ -101,9 +101,9 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, return; } - /* return the result, possibly including status_id */ - if (have_status_id) - sendback(client, "OK %s\n", status_id); + /* return the result, possibly including tracking_id */ + if (have_tracking_id) + sendback(client, "OK %s\n", tracking_id); else sendback(client, "OK\n"); } @@ -113,7 +113,7 @@ void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) const char *devname = NULL; const char *cmdname = NULL; const char *cmdparam = NULL; - char status_id[UUID4_LEN] = ""; + char tracking_id[UUID4_LEN] = ""; if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); @@ -127,12 +127,12 @@ void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) if (numarg == 3) cmdparam = arg[2]; - if (client->cmdset_status_enabled) { + if (client->tracking) { /* Generate a tracking ID, if client requested status tracking */ - nut_uuid_v4(status_id); + nut_uuid_v4(tracking_id); } - send_instcmd(client, devname, cmdname, cmdparam, status_id); + send_instcmd(client, devname, cmdname, cmdparam, tracking_id); return; } diff --git a/server/netset.c b/server/netset.c index 3e5eba7834..05b65321af 100644 --- a/server/netset.c +++ b/server/netset.c @@ -28,14 +28,14 @@ #include "netset.h" static void set_var(nut_ctype_t *client, const char *upsname, const char *var, - const char *newval, const char *status_id) + const char *newval, const char *tracking_id) { upstype_t *ups; const char *val; const enum_t *etmp; const range_t *rtmp; char cmd[SMALLBUF], esc[SMALLBUF]; - int have_status_id = 0; + int have_tracking_id = 0; ups = get_ups_ptr(upsname); @@ -139,11 +139,11 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, var, pconf_encode(newval, esc, sizeof(esc))); /* see if the user want execution tracking for this command */ - if (status_id && *status_id) { - snprintfcat(cmd, sizeof(cmd), " STATUS_ID %s", status_id); + if (tracking_id && *tracking_id) { + snprintfcat(cmd, sizeof(cmd), " TRACKING %s", tracking_id); /* Add an entry in the tracking structure */ - cmdset_status_add(status_id); - have_status_id = 1; + tracking_add(tracking_id); + have_tracking_id = 1; } /* add EOL */ @@ -151,7 +151,7 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s (tracking ID: %s)", client->username, client->addr, var, ups->name, newval, - (have_status_id)?status_id:"disabled"); + (have_tracking_id) ? tracking_id : "disabled"); if (!sstate_sendline(ups, cmd)) { upslogx(LOG_INFO, "Set command send failed"); @@ -159,16 +159,16 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, return; } - /* return the result, possibly including status_id */ - if (have_status_id) - sendback(client, "OK %s\n", status_id); + /* return the result, possibly including tracking_id */ + if (have_tracking_id) + sendback(client, "OK %s\n", tracking_id); else sendback(client, "OK\n"); } void net_set(nut_ctype_t *client, int numarg, const char **arg) { - char status_id[UUID4_LEN] = ""; + char tracking_id[UUID4_LEN] = ""; /* Base verification, to ensure that we have at least the SET parameter */ if (numarg < 2) { @@ -183,37 +183,37 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) return; } - if (client->cmdset_status_enabled) { + if (client->tracking) { /* Generate a tracking ID, if client requested status tracking */ - nut_uuid_v4(status_id); + nut_uuid_v4(tracking_id); } - set_var(client, arg[1], arg[2], arg[3], status_id); + set_var(client, arg[1], arg[2], arg[3], tracking_id); return; } - /* SET CMDSET_STATUS VALUE */ - if (!strcasecmp(arg[0], "CMDSET_STATUS")) { + /* SET TRACKING VALUE */ + if (!strcasecmp(arg[0], "TRACKING")) { if (!strcasecmp(arg[1], "ON")) { /* general enablement along with for this client */ - cmdset_status_enabled = 1; - client->cmdset_status_enabled = 1; + tracking_enabled = 1; + client->tracking = 1; } else if (!strcasecmp(arg[1], "OFF")) { /* disable status tracking for this client first */ - client->cmdset_status_enabled = 0; + client->tracking = 0; /* then only disable the general one if no other clients use it! - * Note: don't call cmdset_status_free() since we want info to - * persist, and cmdset_status_cleanup() takes care of cleaning */ - cmdset_status_enabled = cmdset_status_disable(); + * Note: don't call tracking_free() since we want info to + * persist, and tracking_cleanup() takes care of cleaning */ + tracking_enabled = tracking_disable(); } else { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } - upsdebugx(1, "%s: CMDSET_STATUS %s", __func__, - (cmdset_status_enabled == 1)?"enabled":"disabled"); + upsdebugx(1, "%s: TRACKING %s", __func__, + (tracking_enabled == 1) ? "enabled" : "disabled"); sendback(client, "OK\n"); diff --git a/server/nut_ctype.h b/server/nut_ctype.h index 5e39a5f2be..54184d2baa 100644 --- a/server/nut_ctype.h +++ b/server/nut_ctype.h @@ -47,7 +47,7 @@ typedef struct nut_ctype_s { char *username; /* per client status info for commands and settings * (disabled by default) */ - int cmdset_status_enabled; + int tracking; #ifdef WITH_OPENSSL SSL *ssl; diff --git a/server/sstate.c b/server/sstate.c index 215f7c6313..0365cd30f6 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -117,14 +117,14 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) return 1; } - /* CMDSET_STATUS */ - if (!strcasecmp(arg[0], "CMDSET_STATUS")) { - cmdset_status_set(arg[1], arg[2]); - upsdebugx(1, "CMDSET_STATUS: ID %s status %s", arg[1], arg[2]); + /* TRACKING */ + if (!strcasecmp(arg[0], "TRACKING")) { + tracking_set(arg[1], arg[2]); + upsdebugx(1, "TRACKING: ID %s status %s", arg[1], arg[2]); /* log actual result of instcmd / setvar */ if (strncmp(arg[2], "PENDING", 7) != 0) { - upslogx(LOG_INFO, "tracking ID: %s\tresult: %s", arg[1], cmdset_status_get(arg[1])); + upslogx(LOG_INFO, "tracking ID: %s\tresult: %s", arg[1], tracking_get(arg[1])); } return 1; } diff --git a/server/upsd.c b/server/upsd.c index b8e55902fb..1d974e26db 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -55,7 +55,7 @@ int deny_severity = LOG_WARNING; int maxage = 15; /* default to 1h before cleaning up status tracking entries */ - int cmdset_status_delay = 3600; + int tracking_delay = 3600; /* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */ int maxconn = 0; @@ -92,10 +92,10 @@ typedef struct { * (disabled by default) * Note that only client that requested it will have it enabled * (see nut_ctype.h) */ -int cmdset_status_enabled = 0; +int tracking_enabled = 0; /* Commands and settings status tracking */ -cmdset_status_t *cmdset_status_list = NULL; +tracking_t *tracking_list = NULL; /* pollfd */ static struct pollfd *fds = NULL; @@ -498,7 +498,7 @@ static void client_connect(stype_t *server) client->addr = xstrdup(inet_ntopW(&csock)); - client->cmdset_status_enabled = 0; + client->tracking = 0; pconf_init(&client->ctx, NULL); @@ -664,7 +664,7 @@ static void upsd_cleanup(void) server_free(); client_free(); driver_free(); - cmdset_status_free(); + tracking_free(); free(statepath); free(datapath); @@ -696,44 +696,44 @@ void poll_reload(void) /* instant command and setvar status tracking */ /* allocate a new status tracking entry */ -int cmdset_status_add(const char *id) +int tracking_add(const char *id) { - cmdset_status_t *cmdset_status; + tracking_t *item; - if ((!cmdset_status_enabled) || (!id)) + if ((!tracking_enabled) || (!id)) return 0; - cmdset_status = xcalloc(1, sizeof(*cmdset_status)); + item = xcalloc(1, sizeof(*item)); - cmdset_status->id = xstrdup(id); - cmdset_status->status = STAT_PENDING; - time(&cmdset_status->request_time); + item->id = xstrdup(id); + item->status = STAT_PENDING; + time(&item->request_time); - if (cmdset_status_list) { - cmdset_status_list->prev = cmdset_status; - cmdset_status->next = cmdset_status_list; + if (tracking_list) { + tracking_list->prev = item; + item->next = tracking_list; } - cmdset_status_list = cmdset_status; + tracking_list = item; return 1; } /* set status of a specific tracking entry */ -int cmdset_status_set(const char *id, const char *value) +int tracking_set(const char *id, const char *value) { - cmdset_status_t *cmdset_status, *cmdset_status_next; + tracking_t *item, *next_item; /* sanity checks */ - if ((!cmdset_status_list) || (!id) || (!value)) + if ((!tracking_list) || (!id) || (!value)) return 0; - for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + for (item = tracking_list; item; item = next_item) { - cmdset_status_next = cmdset_status->next; + next_item = item->next; - if (!strcmp(cmdset_status->id, id)) { - cmdset_status->status = atoi(value); + if (!strcmp(item->id, id)) { + item->status = atoi(value); return 1; } } @@ -742,35 +742,35 @@ int cmdset_status_set(const char *id, const char *value) } /* free a specific tracking entry */ -int cmdset_status_del(const char *id) +int tracking_del(const char *id) { - cmdset_status_t *cmdset_status, *cmdset_status_next; + tracking_t *item, *next_item; /* sanity check */ - if ((!cmdset_status_list) || (!id)) + if ((!tracking_list) || (!id)) return 0; upsdebugx(3, "%s: deleting id %s", __func__, id); - for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + for (item = tracking_list; item; item = next_item) { - cmdset_status_next = cmdset_status->next; + next_item = item->next; - if (!strcmp(cmdset_status->id, id)) { + if (!strcmp(item->id, id)) { - if (cmdset_status->prev) { - cmdset_status->prev->next = cmdset_status->next; + if (item->prev) { + item->prev->next = item->next; } else { /* deleting first entry */ - cmdset_status_list = cmdset_status->next; + tracking_list = item->next; } - if (cmdset_status->next) { - cmdset_status->next->prev = cmdset_status->prev; + if (item->next) { + item->next->prev = item->prev; } - free(cmdset_status->id); - free(cmdset_status); + free(item->id); + free(item); return 1; } @@ -780,63 +780,63 @@ int cmdset_status_del(const char *id) } /* free all status tracking entries */ -void cmdset_status_free(void) +void tracking_free(void) { - cmdset_status_t *cmdset_status, *cmdset_status_next; + tracking_t *item, *next_item; /* sanity check */ - if (!cmdset_status_list) + if (!tracking_list) return; upsdebugx(3, "%s", __func__); - for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { - cmdset_status_next = cmdset_status->next; - cmdset_status_del(cmdset_status->id); + for (item = tracking_list; item; item = next_item) { + next_item = item->next; + tracking_del(item->id); } } -/* cleanup status tracking entries according to their age and cmdset_status_delay */ -void cmdset_status_cleanup(void) +/* cleanup status tracking entries according to their age and tracking_delay */ +void tracking_cleanup(void) { - cmdset_status_t *cmdset_status, *cmdset_status_next; + tracking_t *item, *next_item; time_t now; /* sanity check */ - if (!cmdset_status_list) + if (!tracking_list) return; time(&now); upsdebugx(3, "%s", __func__); - for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + for (item = tracking_list; item; item = next_item) { - cmdset_status_next = cmdset_status->next; + next_item = item->next; - if (difftime(now, cmdset_status->request_time) > cmdset_status_delay) { - cmdset_status_del(cmdset_status->id); + if (difftime(now, item->request_time) > tracking_delay) { + tracking_del(item->id); } } } /* get status of a specific tracking entry */ -char *cmdset_status_get(const char *id) +char *tracking_get(const char *id) { - cmdset_status_t *cmdset_status, *cmdset_status_next; + tracking_t *item, *next_item; /* sanity checks */ - if ((!cmdset_status_list) || (!id)) + if ((!tracking_list) || (!id)) return "ERR UNKNOWN"; - for (cmdset_status = cmdset_status_list; cmdset_status; cmdset_status = cmdset_status_next) { + for (item = tracking_list; item; item = next_item) { - cmdset_status_next = cmdset_status->next; + next_item = item->next; - if (strcmp(cmdset_status->id, id)) + if (strcmp(item->id, id)) continue; - switch (cmdset_status->status) + switch (item->status) { case STAT_PENDING: return "PENDING"; @@ -855,15 +855,15 @@ char *cmdset_status_get(const char *id) } /* disable general status tracking only if no client use it anymore. - * return the new value for cmdset_status_enabled (0 if we can disable, 1 + * return the new value for tracking_enabled (0 if we can disable, 1 * otherwise) */ -int cmdset_status_disable(void) +int tracking_disable(void) { nut_ctype_t *client, *cnext; for (client = firstclient; client; client = cnext) { cnext = client->next; - if (client->cmdset_status_enabled == 1) + if (client->tracking == 1) return 1; } return 0; @@ -913,7 +913,7 @@ static void mainloop(void) } /* cleanup instcmd/setvar status tracking entries if needed */ - cmdset_status_cleanup(); + tracking_cleanup(); /* scan through driver sockets */ for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { diff --git a/server/upsd.h b/server/upsd.h index c08f00d851..e5427906a7 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -79,32 +79,32 @@ enum { }; /* Commands and settings status tracking functions */ -int cmdset_status_add(const char *id); -int cmdset_status_set(const char *id, const char *value); -int cmdset_status_del(const char *id); -void cmdset_status_free(void); -void cmdset_status_cleanup(void); -char *cmdset_status_get(const char *id); -int cmdset_status_disable(void); +int tracking_add(const char *id); +int tracking_set(const char *id, const char *value); +int tracking_del(const char *id); +void tracking_free(void); +void tracking_cleanup(void); +char *tracking_get(const char *id); +int tracking_disable(void); /* Commands and settings status tracking structure */ -typedef struct cmdset_status_s { +typedef struct tracking_s { char *id; int status; time_t request_time; /* for cleanup */ /* doubly linked list */ - struct cmdset_status_s *prev; - struct cmdset_status_s *next; -} cmdset_status_t; + struct tracking_s *prev; + struct tracking_s *next; +} tracking_t; /* declarations from upsd.c */ -extern int maxage, maxconn, cmdset_status_delay; +extern int maxage, maxconn, tracking_delay; extern char *statepath, *datapath; extern upstype_t *firstups; extern nut_ctype_t *firstclient; -extern int cmdset_status_enabled; -extern cmdset_status_t *cmdset_status_list; +extern int tracking_enabled; +extern tracking_t *tracking_list; /* map commands onto signals */ From 5a5c8c821bbb520373c8adfd13e819c01c83a77d Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 23:40:18 +0100 Subject: [PATCH 37/45] upsd: refine the tracking API Add a couple of functions to change in a predictable way the value of the general enablement of tracking and make it visible only inside upsd.c. Also, move the tracking type (tracking_t) and the list of items inside upsd.c. --- server/netset.c | 10 +++++----- server/upsd.c | 36 +++++++++++++++++++++++++++++++----- server/upsd.h | 14 ++------------ 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/server/netset.c b/server/netset.c index 05b65321af..a19c63b02b 100644 --- a/server/netset.c +++ b/server/netset.c @@ -197,8 +197,7 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) if (!strcasecmp(arg[0], "TRACKING")) { if (!strcasecmp(arg[1], "ON")) { /* general enablement along with for this client */ - tracking_enabled = 1; - client->tracking = 1; + client->tracking = tracking_enable(); } else if (!strcasecmp(arg[1], "OFF")) { /* disable status tracking for this client first */ @@ -206,14 +205,15 @@ void net_set(nut_ctype_t *client, int numarg, const char **arg) /* then only disable the general one if no other clients use it! * Note: don't call tracking_free() since we want info to * persist, and tracking_cleanup() takes care of cleaning */ - tracking_enabled = tracking_disable(); + tracking_disable(); } else { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } - upsdebugx(1, "%s: TRACKING %s", __func__, - (tracking_enabled == 1) ? "enabled" : "disabled"); + upsdebugx(1, "%s: TRACKING general %s, client %s.", __func__, + tracking_is_enabled() ? "enabled" : "disabled", + client->tracking ? "enabled" : "disabled"); sendback(client, "OK\n"); diff --git a/server/upsd.c b/server/upsd.c index 1d974e26db..136f03db5c 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -88,14 +88,27 @@ typedef struct { void *data; } handler_t; + +/* Commands and settings status tracking */ + /* general enable/disable status info for commands and settings * (disabled by default) * Note that only client that requested it will have it enabled * (see nut_ctype.h) */ -int tracking_enabled = 0; +static int tracking_enabled = 0; + +/* Commands and settings status tracking structure */ +typedef struct tracking_s { + char *id; + int status; + time_t request_time; /* for cleanup */ + /* doubly linked list */ + struct tracking_s *prev; + struct tracking_s *next; +} tracking_t; + +static tracking_t *tracking_list = NULL; -/* Commands and settings status tracking */ -tracking_t *tracking_list = NULL; /* pollfd */ static struct pollfd *fds = NULL; @@ -854,9 +867,16 @@ char *tracking_get(const char *id) return "ERR UNKNOWN"; /* id not found! */ } +/* enable general status tracking (tracking_enabled) and return its value (1). */ +int tracking_enable(void) +{ + tracking_enabled = 1; + + return tracking_enabled; +} + /* disable general status tracking only if no client use it anymore. - * return the new value for tracking_enabled (0 if we can disable, 1 - * otherwise) */ + * return the new value for tracking_enabled */ int tracking_disable(void) { nut_ctype_t *client, *cnext; @@ -869,6 +889,12 @@ int tracking_disable(void) return 0; } +/* return current general status of tracking (tracking_enabled). */ +int tracking_is_enabled(void) +{ + return tracking_enabled; +} + /* UUID v4 basic implementation * Note: 'dest' must be at least `UUID4_LEN` long */ int nut_uuid_v4(char *uuid_str) diff --git a/server/upsd.h b/server/upsd.h index e5427906a7..2cd3eb399e 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -85,17 +85,9 @@ int tracking_del(const char *id); void tracking_free(void); void tracking_cleanup(void); char *tracking_get(const char *id); +int tracking_enable(void); int tracking_disable(void); - -/* Commands and settings status tracking structure */ -typedef struct tracking_s { - char *id; - int status; - time_t request_time; /* for cleanup */ - /* doubly linked list */ - struct tracking_s *prev; - struct tracking_s *next; -} tracking_t; +int tracking_is_enabled(void); /* declarations from upsd.c */ @@ -103,8 +95,6 @@ extern int maxage, maxconn, tracking_delay; extern char *statepath, *datapath; extern upstype_t *firstups; extern nut_ctype_t *firstclient; -extern int tracking_enabled; -extern tracking_t *tracking_list; /* map commands onto signals */ From 7c7bc138c26ec6b5d218a6a585cf4ef70adb1d0b Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Sun, 10 Feb 2019 23:48:37 +0100 Subject: [PATCH 38/45] net-protocol: also return TRACKING between OK and , for INSTCMD/SET VAR --- clients/upscmd.c | 6 +++--- clients/upsrw.c | 6 +++--- docs/net-protocol.txt | 4 ++-- server/netinstcmd.c | 2 +- server/netset.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 55fcea8c36..b0d3f21415 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -172,15 +172,15 @@ static void do_cmd(char **argv, const int argc) /* check for status tracking id */ if ( !tracking_enabled || - /* sanity check on the size: "OK " + UUID4_LEN */ - strlen(buf) != UUID4_LEN + 2 + /* sanity check on the size: "OK TRACKING " + UUID4_LEN */ + strlen(buf) != (UUID4_LEN - 1 + strlen("OK TRACKING ")) ) { /* reply as usual */ fprintf(stderr, "%s\n", buf); return; } - snprintf(tracking_id, sizeof(tracking_id), "%s", buf + 3); + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING ")); time(&start); /* send status tracking request, looping if status is PENDING */ diff --git a/clients/upsrw.c b/clients/upsrw.c index 63d70ef532..4d527d9d27 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -97,15 +97,15 @@ static void do_set(const char *varname, const char *newval) /* check for status tracking id */ if ( !tracking_enabled || - /* sanity check on the size: "OK " + UUID4_LEN */ - strlen(buf) != UUID4_LEN + 2 + /* sanity check on the size: "OK TRACKING " + UUID4_LEN */ + strlen(buf) != (UUID4_LEN - 1 + strlen("OK TRACKING ")) ) { /* reply as usual */ fprintf(stderr, "%s\n", buf); return; } - snprintf(tracking_id, sizeof(tracking_id), "%s", buf + 3); + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING ")); time(&start); /* send status tracking request, looping if status is PENDING */ diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index bf8adf21ab..91c62dbdc4 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -410,7 +410,7 @@ Form: Response: OK (if TRACKING is not enabled) - OK (if TRACKING is enabled) + OK TRACKING (if TRACKING is enabled) ERR [...] (see Error responses) @@ -445,7 +445,7 @@ NOTE: is an additional and optional parameter for the command. Response: OK (if TRACKING is not enabled) - OK (if TRACKING is enabled) + OK TRACKING (if TRACKING is enabled) ERR [...] (see Error responses) diff --git a/server/netinstcmd.c b/server/netinstcmd.c index 27c77d1a46..3c4ccc8212 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -103,7 +103,7 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, /* return the result, possibly including tracking_id */ if (have_tracking_id) - sendback(client, "OK %s\n", tracking_id); + sendback(client, "OK TRACKING %s\n", tracking_id); else sendback(client, "OK\n"); } diff --git a/server/netset.c b/server/netset.c index a19c63b02b..a69aa96ff4 100644 --- a/server/netset.c +++ b/server/netset.c @@ -161,7 +161,7 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, /* return the result, possibly including tracking_id */ if (have_tracking_id) - sendback(client, "OK %s\n", tracking_id); + sendback(client, "OK TRACKING %s\n", tracking_id); else sendback(client, "OK\n"); } From de0deb357c6e0ea7024f15122266e9518c2fa3bc Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Mon, 11 Feb 2019 23:23:36 +0100 Subject: [PATCH 39/45] common: massage default timeouts Rename the default timeout used in network operations by upsclient and nut-scanner to be more specific: from DEFAULT_TIMEOUT to DEFAULT_NETWORK_TIMEOUT. Plus, make the default timeout used when retrieving the result of an INSTCMD/SET VAR with TRACKING enabled a common #define'd value (DEFAULT_TRACKING_TIMEOUT), and use it also to publish the default value of the -t option in the help messages of upscmd and upsrw. As suggested by Charles Lepple. --- clients/upsclient.c | 2 +- clients/upscmd.c | 4 ++-- clients/upsrw.c | 4 ++-- include/common.h | 7 +++++-- tools/nut-scanner/nut-scanner.c | 8 ++++---- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/clients/upsclient.c b/clients/upsclient.c index bbd7f47c6e..dd4c9486ae 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -1416,7 +1416,7 @@ int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, unsigned i int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen) { - return upscli_readline_timeout(ups, buf, buflen, DEFAULT_TIMEOUT); + return upscli_readline_timeout(ups, buf, buflen, DEFAULT_NETWORK_TIMEOUT); } /* split upsname[@hostname[:port]] into separate components */ diff --git a/clients/upscmd.c b/clients/upscmd.c index b0d3f21415..f8e237d429 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -33,7 +33,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; static int tracking_enabled = 0; -static unsigned int timeout = 10; +static unsigned int timeout = DEFAULT_TRACKING_TIMEOUT; struct list_t { char *name; @@ -54,7 +54,7 @@ static void usage(const char *prog) printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of command by the driver\n"); printf(" and return its actual result from the device\n"); - printf(" -t set a timeout when using -w (in seconds, default \"10\")\n"); + printf(" -t set a timeout when using -w (in seconds, default: %u)\n", DEFAULT_TRACKING_TIMEOUT); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf(" Valid instant command - test.panel.start, etc.\n"); diff --git a/clients/upsrw.c b/clients/upsrw.c index 4d527d9d27..0d1906de51 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -33,7 +33,7 @@ static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; static int tracking_enabled = 0; -static unsigned int timeout = 10; +static unsigned int timeout = DEFAULT_TRACKING_TIMEOUT; struct list_t { char *name; @@ -54,7 +54,7 @@ static void usage(const char *prog) printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of setting by the driver\n"); printf(" and return its actual result from the device\n"); - printf(" -t set a timeout when using -w (in seconds, default \"10\")\n"); + printf(" -t set a timeout when using -w (in seconds, default: %u)\n", DEFAULT_TRACKING_TIMEOUT); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf("\n"); diff --git a/include/common.h b/include/common.h index 43aaf40879..4f99e13ec5 100644 --- a/include/common.h +++ b/include/common.h @@ -53,8 +53,11 @@ extern "C" { extern const char *UPS_VERSION; -/** @brief Default timeout (in seconds), as used by upsclient and nut-scanner. */ -#define DEFAULT_TIMEOUT 5 +/** @brief Default timeout (in seconds) for network opereations, as used by `upsclient` and `nut-scanner`. */ +#define DEFAULT_NETWORK_TIMEOUT 5 + +/** @brief Default timeout (in seconds) for retrieving the result of a `TRACKING`-enabled operation (e.g. `INSTCMD`, `SET VAR`). */ +#define DEFAULT_TRACKING_TIMEOUT 10 /* get the syslog ready for us */ void open_syslog(const char *progname); diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 9f622f67ba..25e39d9875 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -82,7 +82,7 @@ const struct option longopts[] = static nutscan_device_t *dev[TYPE_END]; -static long timeout = DEFAULT_TIMEOUT*1000*1000; /* in usec */ +static long timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; /* in usec */ static char * start_ip = NULL; static char * end_ip = NULL; static char * port = NULL; @@ -167,7 +167,7 @@ void show_usage() printf(" -E, --eaton_serial : Scan serial Eaton devices (XCP, SHUT and Q1).\n"); printf("\nNetwork specific options:\n"); - printf(" -t, --timeout : network operation timeout (default %d).\n",DEFAULT_TIMEOUT); + printf(" -t, --timeout : network operation timeout (default %d).\n", DEFAULT_NETWORK_TIMEOUT); printf(" -s, --start_ip : First IP address to scan.\n"); printf(" -e, --end_ip : Last IP address to scan.\n"); printf(" -m, --mask_cidr : Give a range of IP using CIDR notation.\n"); @@ -268,8 +268,8 @@ int main(int argc, char *argv[]) case 't': timeout = atol(optarg)*1000*1000; /*in usec*/ if( timeout == 0 ) { - fprintf(stderr,"Illegal timeout value, using default %ds\n", DEFAULT_TIMEOUT); - timeout = DEFAULT_TIMEOUT*1000*1000; + fprintf(stderr,"Illegal timeout value, using default %ds\n", DEFAULT_NETWORK_TIMEOUT); + timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; } break; case 's': From 787d5842150c0915bb219169281cbc89aebf6379 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Mon, 11 Feb 2019 23:53:19 +0100 Subject: [PATCH 40/45] upsd: ignore case of UUID4 in tracking API Also, remove some nesting in tracking_del(). --- server/upsd.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index 136f03db5c..a49b51110f 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -745,7 +745,7 @@ int tracking_set(const char *id, const char *value) next_item = item->next; - if (!strcmp(item->id, id)) { + if (!strcasecmp(item->id, id)) { item->status = atoi(value); return 1; } @@ -769,24 +769,23 @@ int tracking_del(const char *id) next_item = item->next; - if (!strcmp(item->id, id)) { + if (strcasecmp(item->id, id)) + continue; - if (item->prev) { - item->prev->next = item->next; - } else { - /* deleting first entry */ - tracking_list = item->next; - } + if (item->prev) + item->prev->next = item->next; + else + /* deleting first entry */ + tracking_list = item->next; - if (item->next) { - item->next->prev = item->prev; - } + if (item->next) + item->next->prev = item->prev; - free(item->id); - free(item); + free(item->id); + free(item); + + return 1; - return 1; - } } return 0; /* id not found! */ @@ -846,7 +845,7 @@ char *tracking_get(const char *id) next_item = item->next; - if (strcmp(item->id, id)) + if (strcasecmp(item->id, id)) continue; switch (item->status) From a31e5e46549b9165a7018b291b35af1ad98bac58 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Tue, 12 Feb 2019 00:16:15 +0100 Subject: [PATCH 41/45] libupsclient: generate manpages for upscli_{read,send}line_timeout() To keep things simple, at least for now, only generate manpages and not html pages (and, as such, don't even think of using our linkman AsciiDoc macro with those functions, to avoid dead links). --- docs/man/Makefile.am | 8 ++++++++ docs/man/upscli_readline.txt | 19 ++++++++++++------- docs/man/upscli_sendline.txt | 18 ++++++++++++------ 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 0c0646bcd6..fef33f6db0 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -252,7 +252,9 @@ MAN3_DEV_PAGES = \ upscli_list_next.3 \ upscli_list_start.3 \ upscli_readline.3 \ + upscli_readline_timeout.3 \ upscli_sendline.3 \ + upscli_sendline_timeout.3 \ upscli_splitaddr.3 \ upscli_splitname.3 \ upscli_ssl.3 \ @@ -289,6 +291,12 @@ MAN3_DEV_PAGES = \ nutscan_get_serial_ports_list.3 \ nutscan_init.3 +upscli_readline_timeout.3: upscli_readline.3 + touch $@ + +upscli_sendline_timeout.3: upscli_sendline.3 + touch $@ + MAN1_DEV_PAGES = \ libupsclient-config.1 endif diff --git a/docs/man/upscli_readline.txt b/docs/man/upscli_readline.txt index 52106ade43..2d96240614 100644 --- a/docs/man/upscli_readline.txt +++ b/docs/man/upscli_readline.txt @@ -4,7 +4,7 @@ UPSCLI_READLINE(3) NAME ---- -upscli_readline - read a single response from a UPS +upscli_readline, upscli_readline_timeout - read a single response from a UPS SYNOPSIS -------- @@ -12,23 +12,28 @@ SYNOPSIS #include int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); + int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, unsigned int timeout); DESCRIPTION ----------- -The *upscli_readline()* function takes the pointer 'ups' to a -`UPSCONN_t` state structure, receives a single line from the server, and -copies up to 'buflen' bytes of the response into the buffer -'buf'. +The *upscli_readline()* and *upscli_readline_timeout()* functions take the +pointer 'ups' to a `UPSCONN_t` state structure, receive a single line from the +server, and copy up to 'buflen' bytes of the response into the buffer 'buf'. Some parsing of the string occurs during reception. In particular, ERR messages from linkman:upsd[8] are detected and will cause this function to return -1. +The difference between the two functions is that *upscli_readline_timeout()* +let the caller decide the amount of time ('timeout' seconds) after which it +should give up and return, whereas *upscli_readline()* does not offer this +freedom. + RETURN VALUE ------------ -The *upscli_readline()* function returns 0 on success, or -1 if an -error occurs. +The *upscli_readline()* and *upscli_readline_timeout()* functions return 0 on +success, or -1 if an error occurs. SEE ALSO -------- diff --git a/docs/man/upscli_sendline.txt b/docs/man/upscli_sendline.txt index c449c6dd5d..e5ce332c25 100644 --- a/docs/man/upscli_sendline.txt +++ b/docs/man/upscli_sendline.txt @@ -4,7 +4,7 @@ UPSCLI_SENDLINE(3) NAME ---- -upscli_sendline - send a single command to a UPS +upscli_sendline, upscli_sendline_timeout - send a single command to a UPS SYNOPSIS -------- @@ -13,22 +13,28 @@ SYNOPSIS #include int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); + int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, unsigned int timeout); DESCRIPTION ----------- -The *upscli_sendline()* function takes the pointer 'ups' to a -`UPSCONN_t` state structure and transmits a buffer 'buf' of size -'buflen' to the server. +The *upscli_sendline()* and *upscli_sendline_timeout()* functions take the +pointer 'ups' to a `UPSCONN_t` state structure and transmit a buffer 'buf' of +size 'buflen' to the server. The data in 'buf' must be a fully formatted protocol command as no parsing of the buffer occurs within this function. +The difference between the two functions is that *upscli_sendline_timeout()* +let the caller decide the amount of time ('timeout' seconds) after which it +should give up and return, whereas *upscli_sendline()* does not offer this +freedom. + RETURN VALUE ------------ -The *upscli_sendline()* function returns 0 on success, or -1 if an -error occurs. +The *upscli_sendline()* and *upscli_sendline_timeout()* functions return 0 on +success, or -1 if an error occurs. SEE ALSO -------- From 663dd9fd878edd31207b90653744e3e27ab9cc61 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Tue, 12 Feb 2019 00:19:06 +0100 Subject: [PATCH 42/45] libupsclient: bump version as per recent changes - addition of upscli_{read,send}line_timeout(), - upscli_cleanup() -> upcli_cleanup(void) Note: only increase 'current' and not 'age', because the upscli_cleanup() change could (potentially) make it not compatible with previous versions. --- clients/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/Makefile.am b/clients/Makefile.am index 7f0c7d8363..53e1d55361 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -53,7 +53,7 @@ endif # libupsclient version information # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -libupsclient_la_LDFLAGS = -version-info 4:0:0 +libupsclient_la_LDFLAGS = -version-info 5:0:0 # libnutclient version information libnutclient_la_SOURCES = nutclient.h nutclient.cpp From c0c478ba6bf26fc1d9e2fabee1bb6ee28f8f7110 Mon Sep 17 00:00:00 2001 From: Daniele Pezzini Date: Wed, 13 Feb 2019 23:17:43 +0100 Subject: [PATCH 43/45] upscmd/upsrw: warn that also the drivers need to support TRACKING, for -w --- docs/man/upscmd.txt | 5 +++-- docs/man/upsrw.txt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index 78c4d0e7f5..b4278d0c28 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -44,8 +44,9 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of command execution by the driver and return its -actual result from the device. Note that this feature requires that upsd -supports TRACKING (NUT version 2.7.5 or higher) or it will otherwise fail. +actual result from the device. Note that this feature requires that both upsd +and the driver support TRACKING (NUT version 2.7.5 or higher) or it will +otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 8271dad182..8a183977eb 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -60,8 +60,9 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of setting execution by the driver and return its -actual result from the device. Note that this feature requires that upsd -supports TRACKING (NUT version 2.7.5 or higher) or it will otherwise fail. +actual result from the device. Note that this feature requires that both upsd +and the driver support TRACKING (NUT version 2.7.5 or higher) or it will +otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). From 155e795225ca51152672300d9d8fd4b3d39f7bca Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 26 Feb 2019 09:52:08 +0100 Subject: [PATCH 44/45] Fix typo and spelling Signed-off-by: Arnaud Quette --- docs/man/upscli_readline.txt | 4 ++-- docs/man/upscli_sendline.txt | 4 ++-- docs/man/upscmd.txt | 2 +- docs/man/upsrw.txt | 2 +- docs/nut-names.txt | 6 +++--- include/common.h | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/man/upscli_readline.txt b/docs/man/upscli_readline.txt index 2d96240614..06db9c62c3 100644 --- a/docs/man/upscli_readline.txt +++ b/docs/man/upscli_readline.txt @@ -25,9 +25,9 @@ ERR messages from linkman:upsd[8] are detected and will cause this function to return -1. The difference between the two functions is that *upscli_readline_timeout()* -let the caller decide the amount of time ('timeout' seconds) after which it +lets the caller decide the amount of time ('timeout' seconds) after which it should give up and return, whereas *upscli_readline()* does not offer this -freedom. +freedom, and uses NUT default network timeout (5 seconds). RETURN VALUE ------------ diff --git a/docs/man/upscli_sendline.txt b/docs/man/upscli_sendline.txt index e5ce332c25..cefa264538 100644 --- a/docs/man/upscli_sendline.txt +++ b/docs/man/upscli_sendline.txt @@ -26,9 +26,9 @@ The data in 'buf' must be a fully formatted protocol command as no parsing of the buffer occurs within this function. The difference between the two functions is that *upscli_sendline_timeout()* -let the caller decide the amount of time ('timeout' seconds) after which it +lets the caller decide the amount of time ('timeout' seconds) after which it should give up and return, whereas *upscli_sendline()* does not offer this -freedom. +freedom, and uses an immediate timeout (0 second). RETURN VALUE ------------ diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index b4278d0c28..a66d0f1399 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -51,7 +51,7 @@ The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). *-t* 'seconds':: -Set a timeout when using *-w*. Default to 10 seconds. +Set a timeout when using *-w*. Defaults to 10 seconds. 'ups':: Connect to this UPS. The format is `upsname[@hostname[:port]]`. The default diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 8a183977eb..eb4ed351f8 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -67,7 +67,7 @@ The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). *-t* 'seconds':: -Set a timeout when using *-w*. Default to 10 seconds. +Set a timeout when using *-w*. Defaults to 10 seconds. 'ups':: View or change the settings on this UPS. The format for this option is diff --git a/docs/nut-names.txt b/docs/nut-names.txt index e7f4d86a1b..c5eb47cb3d 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -625,9 +625,9 @@ Instant commands | Name | Description | load.off | Turn off the load immediately | load.on | Turn on the load immediately -| load.off.delay | Turn off the load possibly after a delay -| load.on.delay | Turn on the load possibly after a delay -| shutdown.return | Turn off the load possibly after a delay +| load.off.delay | Turn off the load possibly after a delay +| load.on.delay | Turn on the load possibly after a delay +| shutdown.return | Turn off the load possibly after a delay and return when power is back | shutdown.stayoff | Turn off the load possibly after a delay and remain off even if power returns diff --git a/include/common.h b/include/common.h index 4f99e13ec5..248dadd374 100644 --- a/include/common.h +++ b/include/common.h @@ -53,7 +53,7 @@ extern "C" { extern const char *UPS_VERSION; -/** @brief Default timeout (in seconds) for network opereations, as used by `upsclient` and `nut-scanner`. */ +/** @brief Default timeout (in seconds) for network operations, as used by `upsclient` and `nut-scanner`. */ #define DEFAULT_NETWORK_TIMEOUT 5 /** @brief Default timeout (in seconds) for retrieving the result of a `TRACKING`-enabled operation (e.g. `INSTCMD`, `SET VAR`). */ From 9a40084a5c756fa47c436819c2b15c41fa91d93a Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 26 Feb 2019 10:12:28 +0100 Subject: [PATCH 45/45] Add check around atoi() conversion Signed-off-by: Arnaud Quette --- common/snprintf.c | 2 +- server/conf.c | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/common/snprintf.c b/common/snprintf.c index 62c8c14182..be7785266f 100644 --- a/common/snprintf.c +++ b/common/snprintf.c @@ -54,7 +54,7 @@ #include "config.h" #include -# include +#include #include #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) diff --git a/server/conf.c b/server/conf.c index 75723d141b..e6046ed7c9 100644 --- a/server/conf.c +++ b/server/conf.c @@ -23,6 +23,7 @@ #include "sstate.h" #include "user.h" #include "netssl.h" +#include ups_t *upstable = NULL; int num_ups = 0; @@ -121,20 +122,38 @@ static int parse_upsd_conf_args(int numargs, char **arg) /* MAXAGE */ if (!strcmp(arg[0], "MAXAGE")) { - maxage = atoi(arg[1]); - return 1; + if (isdigit(arg[1])) { + maxage = atoi(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "MAXAGE has non numeric value (%s)!", arg[1]); + return 0; + } } /* TRACKINGDELAY */ if (!strcmp(arg[0], "TRACKINGDELAY")) { - tracking_delay = atoi(arg[1]); - return 1; + if (isdigit(arg[1])) { + tracking_delay = atoi(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "TRACKINGDELAY has non numeric value (%s)!", arg[1]); + return 0; + } } /* MAXCONN */ if (!strcmp(arg[0], "MAXCONN")) { - maxconn = atoi(arg[1]); - return 1; + if (isdigit(arg[1])) { + maxconn = atoi(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "MAXCONN has non numeric value (%s)!", arg[1]); + return 0; + } } /* STATEPATH */ @@ -168,8 +187,14 @@ static int parse_upsd_conf_args(int numargs, char **arg) #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION /* CERTREQUEST (0 | 1 | 2) */ if (!strcmp(arg[0], "CERTREQUEST")) { - certrequest = atoi(arg[1]); - return 1; + if (isdigit(arg[1])) { + certrequest = atoi(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "CERTREQUEST has non numeric value (%s)!", arg[1]); + return 0; + } } #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ #endif /* WITH_OPENSSL | WITH_NSS */