Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagate IMMUTABLE flag on socket protocol (driver=>upsd); use it in netset; sanity-check if (defaulted) NUMBER values are not in fact STRINGs #2549

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ https://github.com/networkupstools/nut/milestone/11
* `upsd_cleanup()` is now traced, to more easily see that the daemon is
exiting (and/or start-up has aborted due to configuration or run-time
issues). Warning about "world readable" files clarified. [#2417]
* sanity-check `NUMBER` values that they actually resolve as a `double`
or `long int`; report an un-flagged type as `STRING` if not (previously
blindly defaulted to `NUMBER` always). Debug-print presence of the
`IMMUTABLE` flag in `netget.c::get_type()`, and actually consider it
in the `netset.c::set_var()` method to abort early. Socket protocol
used between a driver and data server to exchange device state was
updated to pass `IMMUTABLE` flag, but this change should not impact
the NUT network protocol as published in RFC 9271. [#266]

- nut-scanner:
* the tool relies on dynamic loading of shared objects (library files)
Expand Down
13 changes: 13 additions & 0 deletions UPGRADING.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ Changes from 2.8.2 to 2.8.3
and one structure (`nutscan_ipmi_t`) updated in a (hopefully) backwards
compatible manner. [PR #2523, issue #2244 and numerous PRs for it]

- The `netset.c::set_var()` was updated to consider the `IMMUTABLE` flag on
values to reject writing into them quickly. This is currently expected
to only impact `override.*` values in vanilla NUT codebase, and should
not impact the NUT network protocol as published in RFC 9271 (only the
socket protocol used between a driver and data server to exchange device
state). [#266]

- The `netset.c::get_type()` was updated to sanity-check `NUMBER`-flagged
values that the strings actually resolve into `double` or `long int`
values. Some codebase does not flag its values properly and warnings
can be emitted (to be reported for upstream NUT to fix these use-cases).
[#266]

- Internal API change for `sendsignalpid()` and `sendsignalfn()` methods,
which can impact NUT forks which build using `libcommon.la` and similar
libraries. Added new last argument with `const char *progname` (may be
Expand Down
5 changes: 5 additions & 0 deletions common/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,11 @@ void state_setflags(st_tree_t *root, const char *var, size_t numflags, char **fl

for (i = 0; i < numflags; i++) {

if (!strcasecmp(flag[i], "IMMUTABLE")) {
sttmp->flags |= ST_FLAG_IMMUTABLE;
continue;
}

if (!strcasecmp(flag[i], "RW")) {
sttmp->flags |= ST_FLAG_RW;
continue;
Expand Down
5 changes: 5 additions & 0 deletions docs/net-protocol.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ exponents, and comma for thousands separator are forbidden.
For example: "1200.20" is valid, while "1,200.20" and "1200,20" and "1.2e4"
are invalid.

Also note that internally NUT programs can flag variables as 'IMMUTABLE':
a value of such variable can not be changed after it was initially set
(this is used e.g. for "override" settings where the device is known to
provide bogus readings). The flag is not currently exposed with `GET`
commands, but can be a reason for a `SET` to fail.

This replaces the old "VARTYPE" command.

Expand Down
4 changes: 3 additions & 1 deletion docs/nut.dict
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
personal_ws-1.1 en 3197 utf-8
personal_ws-1.1 en 3199 utf-8
AAC
AAS
ABI
Expand Down Expand Up @@ -2380,8 +2380,10 @@ nd
nds
netcat
netclient
netget
netmask
netserver
netset
netsh
netsnmp
netvision
Expand Down
2 changes: 1 addition & 1 deletion docs/sock-protocol.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ flags are supported. Also note that they are not crammed together in

This also replaces any previous flags for a given variable.

Currently supported flags include `RW`, `STRING` and `NUMBER`
Currently supported flags include `IMMUTABLE`, `RW`, `STRING` and `NUMBER`
(detailed in the NUT Network Protocol documentation); unrecognized values
are quietly ignored.

Expand Down
3 changes: 3 additions & 0 deletions drivers/dstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,9 @@ static int st_tree_dump_conn(st_tree_t *node, conn_t *conn)
/* build the list */
snprintf(flist, sizeof(flist), "%s", node->var);

if (node->flags & ST_FLAG_IMMUTABLE) {
snprintfcat(flist, sizeof(flist), " IMMUTABLE");
}
if (node->flags & ST_FLAG_RW) {
snprintfcat(flist, sizeof(flist), " RW");
}
Expand Down
74 changes: 74 additions & 0 deletions server/netget.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "state.h"
#include "desc.h"
#include "neterr.h"
#include "nut_stdint.h"

#include "netget.h"

Expand Down Expand Up @@ -138,6 +139,19 @@ static void get_type(nut_ctype_t *client, const char *upsname, const char *var)

snprintf(buf, sizeof(buf), "TYPE %s %s", upsname, var);

if (node->flags & ST_FLAG_IMMUTABLE) {
#if defined DEBUG && DEBUG
/* Properly exposing this needs also an update to
* docs/net-protocol.txt (promote the paragraph
* provided as a note currently) and to the NUT RFC
* https://www.rfc-editor.org/info/rfc9271
*/
snprintfcat(buf, sizeof(buf), " IMMUTABLE");
#endif
upsdebugx(3, "%s: UPS[%s] variable %s is IMMUTABLE",
__func__, upsname, var);
}

if (node->flags & ST_FLAG_RW)
snprintfcat(buf, sizeof(buf), " RW");

Expand All @@ -162,6 +176,66 @@ static void get_type(nut_ctype_t *client, const char *upsname, const char *var)
__func__, upsname, var);
}

/* Sanity-check current contents */
if (node->val && *(node->val)) {
double d;
long l;
int ok = 1;
size_t len = strlen(node->val);

errno = 0;
if (!str_to_double_strict(node->val, &d, 10)) {
upsdebugx(3, "%s: UPS[%s] variable %s is flagged a NUMBER but not (exclusively) a double: %s",
__func__, upsname, var, node->val);
upsdebug_with_errno(4, "%s: val=%f len=%" PRIuSIZE,
__func__, d, len);
ok = 0;
}

if (!ok) {
/* did not parse as a float... range issues or NaN? */
errno = 0;
ok = 1;
if (!str_to_long_strict(node->val, &l, 10)) {
upsdebugx(3, "%s: UPS[%s] variable %s is flagged a NUMBER but not (exclusively) a long int: %s",
__func__, upsname, var, node->val);
upsdebug_with_errno(4, "%s: val=%ld len=%" PRIuSIZE,
__func__, l, len);
ok = 0;
}
}

#if defined DEBUG && DEBUG
/* Need to figure out an "aux" value here (length of current
* string at least?) and propagate the flag into where netset
* would see it. Maybe this sanity-check should move into the
* core state.c logic, so dstate setting would already remember
* the defaulted flag (and maybe set another to clarify it is
* a guess). Currently that code does not concern itself with
* sanity-checks, it seems!
*/
if (!ok && !(node->flags & ST_FLAG_NUMBER)) {
upsdebugx(3, "%s: assuming UPS[%s] variable %s is a STRING after all, by contents; "
"value='%s' len='%" PRIuSIZE "' aux='%ld'",
__func__, upsname, var, node->val, len, node->aux);

sendback(client, "%s STRING:%ld\n", buf, node->aux);
return;
}
#endif

if (!ok) {
/* FIXME: Should this return an error?
* Value was explicitly flagged as a NUMBER but is not by content.
* Note that state_addinfo() does not sanity-check; but
* netset.c::set_var() does though (for protocol clients).
*/
upslogx(LOG_WARNING, "%s: UPS[%s] variable %s is flagged as a NUMBER but is not "
"by contents (please report as a bug to NUT project)): %s",
__func__, upsname, var, node->val);
}
}

sendback(client, "%s NUMBER\n", buf);
}

Expand Down
10 changes: 10 additions & 0 deletions server/netset.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var,
}
}

/* FIXME: Consider null/non-null values for strings
* See note on sstate_getnode() idea above.
*/
if (sstate_getflags(ups, var) & ST_FLAG_IMMUTABLE) {
upsdebugx(3, "%s: UPS [%s]: value of %s is already set and immutable",
__func__, ups->name, var);
send_err(client, NUT_ERR_READONLY);
return;
}

/* must be OK now */

snprintf(cmd, sizeof(cmd), "SET %s \"%s\"",
Expand Down
Loading